penambahan web socket
This commit is contained in:
No files matched your search
+376
@@ -0,0 +1,376 @@
|
||||
import { fork } from 'node:child_process';
|
||||
import process from 'node:process';
|
||||
import { defineCommand } from 'citty';
|
||||
import { isSocketSupported } from 'get-port-please';
|
||||
import { createProxyServer } from 'httpxy';
|
||||
import { listen } from 'listhen';
|
||||
import { getArgs, parseArgs } from 'listhen/cli';
|
||||
import { resolve } from 'pathe';
|
||||
import { satisfies } from 'semver';
|
||||
import { isTest, isBun, isDeno } from 'std-env';
|
||||
import { i as initialize, r as resolveLoadingTemplate, a as renderError, b as isSocketURL, p as parseSocketURL } from './index.mjs';
|
||||
import { s as showVersions } from '../shared/cli.Dz2be-Ai.mjs';
|
||||
import { o as overrideEnv } from '../shared/cli.BEUGgaW4.mjs';
|
||||
import { l as loadKit } from '../shared/cli.qKvs7FJ2.mjs';
|
||||
import { l as logger } from '../shared/cli.B9AmABr3.mjs';
|
||||
import { e as extendsArgs, b as envNameArgs, l as legacyRootDirArgs, d as dotEnvArgs, a as logLevelArgs, c as cwdArgs } from '../shared/cli.CTXRG5Cu.mjs';
|
||||
import 'defu';
|
||||
import 'node:http';
|
||||
import 'node:events';
|
||||
import 'node:fs';
|
||||
import 'node:fs/promises';
|
||||
import 'node:url';
|
||||
import 'exsolve';
|
||||
import 'h3';
|
||||
import 'perfect-debounce';
|
||||
import 'ufo';
|
||||
import '../shared/cli.pLQ0oPGc.mjs';
|
||||
import '../shared/cli.At9IMXtr.mjs';
|
||||
import 'ohash';
|
||||
import 'youch';
|
||||
import 'consola/utils';
|
||||
import 'consola';
|
||||
import 'node:path';
|
||||
|
||||
const startTime = Date.now();
|
||||
const forkSupported = !isTest && (!isBun || isBunForkSupported());
|
||||
const listhenArgs = getArgs();
|
||||
const command = defineCommand({
|
||||
meta: {
|
||||
name: "dev",
|
||||
description: "Run Nuxt development server"
|
||||
},
|
||||
args: {
|
||||
...cwdArgs,
|
||||
...logLevelArgs,
|
||||
...dotEnvArgs,
|
||||
...legacyRootDirArgs,
|
||||
...envNameArgs,
|
||||
...extendsArgs,
|
||||
clear: {
|
||||
type: "boolean",
|
||||
description: "Clear console on restart",
|
||||
negativeDescription: "Disable clear console on restart"
|
||||
},
|
||||
fork: {
|
||||
type: "boolean",
|
||||
description: forkSupported ? "Disable forked mode" : "Enable forked mode",
|
||||
negativeDescription: "Disable forked mode",
|
||||
default: forkSupported,
|
||||
alias: ["f"]
|
||||
},
|
||||
...{
|
||||
...listhenArgs,
|
||||
"port": {
|
||||
...listhenArgs.port,
|
||||
description: "Port to listen on (default: `NUXT_PORT || NITRO_PORT || PORT || nuxtOptions.devServer.port`)",
|
||||
alias: ["p"]
|
||||
},
|
||||
"open": {
|
||||
...listhenArgs.open,
|
||||
alias: ["o"],
|
||||
default: false
|
||||
},
|
||||
"host": {
|
||||
...listhenArgs.host,
|
||||
alias: ["h"],
|
||||
description: "Host to listen on (default: `NUXT_HOST || NITRO_HOST || HOST || nuxtOptions.devServer?.host`)"
|
||||
},
|
||||
"clipboard": { ...listhenArgs.clipboard, default: false },
|
||||
"https.domains": {
|
||||
...listhenArgs["https.domains"],
|
||||
description: "Comma separated list of domains and IPs, the autogenerated certificate should be valid for (https: true)"
|
||||
}
|
||||
},
|
||||
sslCert: {
|
||||
type: "string",
|
||||
description: "(DEPRECATED) Use `--https.cert` instead."
|
||||
},
|
||||
sslKey: {
|
||||
type: "string",
|
||||
description: "(DEPRECATED) Use `--https.key` instead."
|
||||
}
|
||||
},
|
||||
async run(ctx) {
|
||||
overrideEnv("development");
|
||||
const cwd = resolve(ctx.args.cwd || ctx.args.rootDir);
|
||||
showVersions(cwd);
|
||||
const { loadNuxtConfig } = await loadKit(cwd);
|
||||
const nuxtOptions = await loadNuxtConfig({
|
||||
cwd,
|
||||
dotenv: { cwd, fileName: ctx.args.dotenv },
|
||||
envName: ctx.args.envName,
|
||||
// c12 will fall back to NODE_ENV
|
||||
overrides: {
|
||||
dev: true,
|
||||
logLevel: ctx.args.logLevel,
|
||||
...ctx.args.extends && { extends: ctx.args.extends },
|
||||
...ctx.data?.overrides
|
||||
}
|
||||
});
|
||||
const listenOptions = resolveListenOptions(nuxtOptions, ctx.args);
|
||||
if (!ctx.args.fork) {
|
||||
const { listener, close: close2 } = await initialize({
|
||||
cwd,
|
||||
args: ctx.args,
|
||||
hostname: listenOptions.hostname,
|
||||
public: listenOptions.public,
|
||||
publicURLs: void 0,
|
||||
proxy: {
|
||||
https: listenOptions.https
|
||||
}
|
||||
}, { data: ctx.data }, listenOptions);
|
||||
return {
|
||||
listener,
|
||||
async close() {
|
||||
await close2();
|
||||
await listener.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
const devProxy = await createDevProxy(cwd, nuxtOptions, listenOptions);
|
||||
const nuxtSocketEnv = process.env.NUXT_SOCKET ? process.env.NUXT_SOCKET === "1" : void 0;
|
||||
const useSocket = nuxtSocketEnv ?? (nuxtOptions._majorVersion === 4 && await isSocketSupported());
|
||||
const urls = await devProxy.listener.getURLs();
|
||||
const { onRestart, onReady, close } = await initialize({
|
||||
cwd,
|
||||
args: ctx.args,
|
||||
hostname: listenOptions.hostname,
|
||||
public: listenOptions.public,
|
||||
publicURLs: urls.map((r) => r.url),
|
||||
proxy: {
|
||||
url: devProxy.listener.url,
|
||||
urls,
|
||||
https: devProxy.listener.https,
|
||||
addr: devProxy.listener.address
|
||||
}
|
||||
// if running with nuxt v4 or `NUXT_SOCKET=1`, we use the socket listener
|
||||
// otherwise pass 'true' to listen on a random port instead
|
||||
}, {}, useSocket ? void 0 : true);
|
||||
onReady((address) => devProxy.setAddress(address));
|
||||
const fork2 = startSubprocess(cwd, ctx.args, ctx.rawArgs, listenOptions);
|
||||
onRestart(async (devServer) => {
|
||||
const [subprocess] = await Promise.all([
|
||||
fork2,
|
||||
devServer.close().catch(() => {
|
||||
})
|
||||
]);
|
||||
await subprocess.initialize(devProxy, useSocket);
|
||||
});
|
||||
return {
|
||||
listener: devProxy.listener,
|
||||
async close() {
|
||||
await close();
|
||||
const subprocess = await fork2;
|
||||
subprocess.kill(0);
|
||||
await devProxy.listener.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
async function createDevProxy(cwd, nuxtOptions, listenOptions) {
|
||||
let loadingMessage = "Nuxt dev server is starting...";
|
||||
let error;
|
||||
let address;
|
||||
let loadingTemplate = nuxtOptions.devServer.loadingTemplate;
|
||||
const proxy = createProxyServer({});
|
||||
proxy.on("proxyReq", (proxyReq, req) => {
|
||||
if (!proxyReq.hasHeader("x-forwarded-for")) {
|
||||
const address2 = req.socket.remoteAddress;
|
||||
if (address2) {
|
||||
proxyReq.appendHeader("x-forwarded-for", address2);
|
||||
}
|
||||
}
|
||||
if (!proxyReq.hasHeader("x-forwarded-port")) {
|
||||
const localPort = req?.socket?.localPort;
|
||||
if (localPort) {
|
||||
proxyReq.setHeader("x-forwarded-port", req.socket.localPort);
|
||||
}
|
||||
}
|
||||
if (!proxyReq.hasHeader("x-forwarded-Proto")) {
|
||||
const encrypted = req?.connection?.encrypted;
|
||||
proxyReq.setHeader("x-forwarded-proto", encrypted ? "https" : "http");
|
||||
}
|
||||
});
|
||||
const listener = await listen((req, res) => {
|
||||
if (error) {
|
||||
renderError(req, res, error);
|
||||
return;
|
||||
}
|
||||
if (!address) {
|
||||
res.statusCode = 503;
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
if (loadingTemplate) {
|
||||
res.end(loadingTemplate({ loading: loadingMessage }));
|
||||
return;
|
||||
}
|
||||
async function resolveLoadingMessage() {
|
||||
loadingTemplate = await resolveLoadingTemplate(cwd);
|
||||
res.end(loadingTemplate({ loading: loadingMessage }));
|
||||
}
|
||||
return resolveLoadingMessage();
|
||||
}
|
||||
const target = isSocketURL(address) ? parseSocketURL(address) : address;
|
||||
proxy.web(req, res, { target });
|
||||
}, listenOptions);
|
||||
listener.server.on("upgrade", (req, socket, head) => {
|
||||
if (!address) {
|
||||
if (!socket.destroyed) {
|
||||
socket.end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
const target = isSocketURL(address) ? parseSocketURL(address) : address;
|
||||
return proxy.ws(req, socket, { target, xfwd: true }, head).catch(() => {
|
||||
if (!socket.destroyed) {
|
||||
socket.end();
|
||||
}
|
||||
});
|
||||
});
|
||||
return {
|
||||
listener,
|
||||
setAddress: (_addr) => {
|
||||
address = _addr;
|
||||
},
|
||||
setLoadingMessage: (_msg) => {
|
||||
loadingMessage = _msg;
|
||||
},
|
||||
setError: (_error) => {
|
||||
error = _error;
|
||||
},
|
||||
clearError() {
|
||||
error = void 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
async function startSubprocess(cwd, args, rawArgs, listenOptions) {
|
||||
let childProc;
|
||||
let devProxy;
|
||||
let ready;
|
||||
const kill = (signal) => {
|
||||
if (childProc) {
|
||||
childProc.kill(signal === 0 && isDeno ? "SIGTERM" : signal);
|
||||
childProc = void 0;
|
||||
}
|
||||
};
|
||||
async function initialize2(proxy, socket) {
|
||||
devProxy = proxy;
|
||||
const urls = await devProxy.listener.getURLs();
|
||||
await ready;
|
||||
childProc.send({
|
||||
type: "nuxt:internal:dev:context",
|
||||
socket,
|
||||
context: {
|
||||
cwd,
|
||||
args,
|
||||
hostname: listenOptions.hostname,
|
||||
public: listenOptions.public,
|
||||
publicURLs: urls.map((r) => r.url),
|
||||
proxy: {
|
||||
url: devProxy.listener.url,
|
||||
urls,
|
||||
https: devProxy.listener.https
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
async function restart() {
|
||||
devProxy?.clearError();
|
||||
if (process.platform === "win32") {
|
||||
kill("SIGTERM");
|
||||
} else {
|
||||
kill("SIGHUP");
|
||||
}
|
||||
childProc = fork(globalThis.__nuxt_cli__.devEntry, rawArgs, {
|
||||
execArgv: ["--enable-source-maps", process.argv.find((a) => a.includes("--inspect"))].filter(Boolean),
|
||||
env: {
|
||||
...process.env,
|
||||
__NUXT__FORK: "true"
|
||||
}
|
||||
});
|
||||
childProc.on("close", (errorCode) => {
|
||||
if (errorCode) {
|
||||
process.exit(errorCode);
|
||||
}
|
||||
});
|
||||
ready = new Promise((resolve2, reject) => {
|
||||
childProc.on("error", reject);
|
||||
childProc.on("message", (message) => {
|
||||
if (message.type === "nuxt:internal:dev:fork-ready") {
|
||||
resolve2();
|
||||
} else if (message.type === "nuxt:internal:dev:ready") {
|
||||
devProxy.setAddress(message.address);
|
||||
if (startTime) {
|
||||
logger.debug(`Dev server ready for connections in ${Date.now() - startTime}ms`);
|
||||
}
|
||||
} else if (message.type === "nuxt:internal:dev:loading") {
|
||||
devProxy.setAddress(void 0);
|
||||
devProxy.setLoadingMessage(message.message);
|
||||
devProxy.clearError();
|
||||
} else if (message.type === "nuxt:internal:dev:loading:error") {
|
||||
devProxy.setAddress(void 0);
|
||||
devProxy.setError(message.error);
|
||||
} else if (message.type === "nuxt:internal:dev:restart") {
|
||||
restart();
|
||||
} else if (message.type === "nuxt:internal:dev:rejection") {
|
||||
logger.info(`Restarting Nuxt due to error: \`${message.message}\``);
|
||||
restart();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
for (const signal of [
|
||||
"exit",
|
||||
"SIGTERM",
|
||||
"SIGINT",
|
||||
"SIGQUIT"
|
||||
]) {
|
||||
process.once(signal, () => {
|
||||
kill(signal === "exit" ? 0 : signal);
|
||||
});
|
||||
}
|
||||
await restart();
|
||||
return {
|
||||
initialize: initialize2,
|
||||
restart,
|
||||
kill
|
||||
};
|
||||
}
|
||||
function resolveListenOptions(nuxtOptions, args) {
|
||||
const _port = args.port ?? args.p ?? process.env.NUXT_PORT ?? process.env.NITRO_PORT ?? process.env.PORT ?? nuxtOptions.devServer.port;
|
||||
const _hostname = typeof args.host === "string" ? args.host : args.host === true ? "" : process.env.NUXT_HOST ?? process.env.NITRO_HOST ?? process.env.HOST ?? (nuxtOptions.devServer?.host || void 0) ?? void 0;
|
||||
const _public = args.public ?? (_hostname && !["localhost", "127.0.0.1", "::1"].includes(_hostname)) ? true : void 0;
|
||||
const _httpsCert = args["https.cert"] || args.sslCert || process.env.NUXT_SSL_CERT || process.env.NITRO_SSL_CERT || typeof nuxtOptions.devServer.https !== "boolean" && nuxtOptions.devServer.https && "cert" in nuxtOptions.devServer.https && nuxtOptions.devServer.https.cert || "";
|
||||
const _httpsKey = args["https.key"] || args.sslKey || process.env.NUXT_SSL_KEY || process.env.NITRO_SSL_KEY || typeof nuxtOptions.devServer.https !== "boolean" && nuxtOptions.devServer.https && "key" in nuxtOptions.devServer.https && nuxtOptions.devServer.https.key || "";
|
||||
const _httpsPfx = args["https.pfx"] || typeof nuxtOptions.devServer.https !== "boolean" && nuxtOptions.devServer.https && "pfx" in nuxtOptions.devServer.https && nuxtOptions.devServer.https.pfx || "";
|
||||
const _httpsPassphrase = args["https.passphrase"] || typeof nuxtOptions.devServer.https !== "boolean" && nuxtOptions.devServer.https && "passphrase" in nuxtOptions.devServer.https && nuxtOptions.devServer.https.passphrase || "";
|
||||
const httpsEnabled = !!(args.https ?? nuxtOptions.devServer.https);
|
||||
const _listhenOptions = parseArgs({
|
||||
...args,
|
||||
"open": args.o || args.open,
|
||||
"https": httpsEnabled,
|
||||
"https.cert": _httpsCert,
|
||||
"https.key": _httpsKey,
|
||||
"https.pfx": _httpsPfx,
|
||||
"https.passphrase": _httpsPassphrase
|
||||
});
|
||||
const httpsOptions = httpsEnabled && {
|
||||
...nuxtOptions.devServer.https,
|
||||
..._listhenOptions.https
|
||||
};
|
||||
return {
|
||||
..._listhenOptions,
|
||||
port: _port,
|
||||
hostname: _hostname,
|
||||
public: _public,
|
||||
https: httpsOptions,
|
||||
baseURL: nuxtOptions.app.baseURL.startsWith("./") ? nuxtOptions.app.baseURL.slice(1) : nuxtOptions.app.baseURL
|
||||
};
|
||||
}
|
||||
function isBunForkSupported() {
|
||||
const bunVersion = globalThis.Bun.version;
|
||||
return satisfies(bunVersion, ">=1.2");
|
||||
}
|
||||
|
||||
export { command as default };
|
||||
Reference in New Issue
Block a user