Files

90 lines
2.9 KiB
JavaScript

#!/usr/bin/env node
"use strict";
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { getUnsupportedNodeVersionMessage, isNodeVersionSupported } = require("./node-version");
if (!isNodeVersionSupported(process.versions.node)) {
console.error(getUnsupportedNodeVersionMessage(process.versions.node));
process.exit(1);
}
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { spawn } = require("child_process");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const path = require("path");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const fs = require("fs");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { parseLaunchOptions } = require("./pi-web-options");
const pkgDir = path.join(__dirname, "..");
const nextDir = path.join(pkgDir, ".next");
// Resolve next's CLI entry directly to avoid relying on .bin symlinks (which
// may not exist when installed via npx).
let nextBin;
try {
nextBin = require.resolve("next/dist/bin/next", { paths: [pkgDir] });
} catch {
// Fallback: locate next package root and derive the bin path manually.
try {
const nextPkg = require.resolve("next/package.json", { paths: [pkgDir] });
nextBin = path.join(path.dirname(nextPkg), "dist", "bin", "next");
} catch {
nextBin = path.join(pkgDir, "node_modules", "next", "dist", "bin", "next");
}
}
const { port, hostname, openBrowser } = parseLaunchOptions();
const loopbackHostnames = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
if (!fs.existsSync(nextDir)) {
console.error("Build artifacts not found. Please report this issue.");
process.exit(1);
}
if (!loopbackHostnames.has(hostname)) {
console.warn(
`Warning: pi-web is listening on ${hostname} without authentication. Only use this on a trusted network.`,
);
}
const nextArgs = ["start", "-p", port];
nextArgs.push("-H", hostname);
// Always run next's JS entry with node directly — avoids .bin symlink issues
// and path-with-spaces problems on Windows when shell: true is used.
const child = spawn(process.execPath, [nextBin, ...nextArgs], {
cwd: pkgDir,
stdio: ["inherit", "pipe", "inherit"],
env: { ...process.env, PI_WEB_HOSTNAME: hostname },
});
let browserOpened = false;
const url = `http://${hostname}:${port}`;
child.stdout.on("data", (chunk) => {
const text = chunk.toString();
process.stdout.write(text);
if (openBrowser && !browserOpened && text.includes("Ready")) {
browserOpened = true;
const isWindows = process.platform === "win32";
const isMac = process.platform === "darwin";
const openCmd = isWindows ? "start" : isMac ? "open" : "xdg-open";
const opener = spawn(openCmd, [url], {
shell: isWindows,
stdio: "ignore",
detached: true,
});
opener.on("error", (error) => {
console.warn(`Could not open browser automatically: ${error.message}`);
});
opener.unref();
}
});
child.on("exit", (code) => process.exit(code ?? 0));