UNPKG

next

Version:

The React Framework

487 lines (483 loc) • 23.8 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "nextDev", { enumerable: true, get: function() { return nextDev; } }); const _startserver = require("../server/lib/start-server"); const _utils = require("../server/lib/utils"); const _log = /*#__PURE__*/ _interop_require_wildcard(require("../build/output/log")); const _getprojectdir = require("../lib/get-project-dir"); const _constants = require("../shared/lib/constants"); const _path = /*#__PURE__*/ _interop_require_default(require("path")); const _configshared = require("../server/config-shared"); const _shared = require("../trace/shared"); const _storage = require("../telemetry/storage"); const _config = /*#__PURE__*/ _interop_require_default(require("../server/config")); const _findpagesdir = require("../lib/find-pages-dir"); const _findroot = require("../lib/find-root"); const _fileexists = require("../lib/file-exists"); const _getnpxcommand = require("../lib/helpers/get-npx-command"); const _watchpack = /*#__PURE__*/ _interop_require_default(require("watchpack")); const _stripansi = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/strip-ansi")); const _worker = require("../build/worker"); const _env = require("@next/env"); const _getvalidatedargs = require("../lib/get-validated-args"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } let dir; let config; let isTurboSession = false; let sessionStopHandled = false; let sessionStarted = Date.now(); const handleSessionStop = async ()=>{ if (sessionStopHandled) return; sessionStopHandled = true; try { const { eventCliSession } = require("../telemetry/events/session-stopped"); config = config || await (0, _config.default)(_constants.PHASE_DEVELOPMENT_SERVER, dir, undefined, undefined, true); let telemetry = _shared.traceGlobals.get("telemetry") || new _storage.Telemetry({ distDir: _path.default.join(dir, config.distDir) }); let pagesDir = !!_shared.traceGlobals.get("pagesDir"); let appDir = !!_shared.traceGlobals.get("appDir"); if (typeof _shared.traceGlobals.get("pagesDir") === "undefined" || typeof _shared.traceGlobals.get("appDir") === "undefined") { const pagesResult = (0, _findpagesdir.findPagesDir)(dir, !!config.experimental.appDir); appDir = !!pagesResult.appDir; pagesDir = !!pagesResult.pagesDir; } telemetry.record(eventCliSession({ cliCommand: "dev", turboFlag: isTurboSession, durationMilliseconds: Date.now() - sessionStarted, pagesDir, appDir }), true); telemetry.flushDetached("dev", dir); } catch (_) { // errors here aren't actionable so don't add // noise to the output } // ensure we re-enable the terminal cursor before exiting // the program, or the cursor could remain hidden process.stdout.write("\x1b[?25h"); process.stdout.write("\n"); process.exit(0); }; process.on("SIGINT", handleSessionStop); process.on("SIGTERM", handleSessionStop); let unwatchConfigFiles; function watchConfigFiles(dirToWatch, onChange = (filename)=>_log.warn(`\n> Found a change in ${_path.default.basename(filename)}. Restart the server to see the changes in effect.`)) { if (unwatchConfigFiles) { unwatchConfigFiles(); } const wp = new _watchpack.default(); wp.watch({ files: _constants.CONFIG_FILES.map((file)=>_path.default.join(dirToWatch, file)) }); wp.on("change", onChange); unwatchConfigFiles = ()=>wp.close(); } const nextDev = async (argv)=>{ const validArgs = { // Types "--help": Boolean, "--port": Number, "--hostname": String, "--turbo": Boolean, "--experimental-turbo": Boolean, // To align current messages with native binary. // Will need to adjust subcommand later. "--show-all": Boolean, "--root": String, // Aliases "-h": "--help", "-p": "--port", "-H": "--hostname" }; const args = (0, _getvalidatedargs.getValidatedArgs)(validArgs, argv); if (args["--help"]) { console.log(` Description Starts the application in development mode (hot-code reloading, error reporting, etc.) Usage $ next dev <dir> -p <port number> <dir> represents the directory of the Next.js application. If no directory is provided, the current directory will be used. Options --port, -p A port number on which to start the application --hostname, -H Hostname on which to start the application (default: 0.0.0.0) --help, -h Displays this message `); process.exit(0); } dir = (0, _getprojectdir.getProjectDir)(process.env.NEXT_PRIVATE_DEV_DIR || args._[0]); // Check if pages dir exists and warn if not if (!await (0, _fileexists.fileExists)(dir, _fileexists.FileType.Directory)) { (0, _utils.printAndExit)(`> No such directory exists as the project root: ${dir}`); } async function preflight() { const { getPackageVersion , getDependencies } = await Promise.resolve(require("../lib/get-package-version")); const [sassVersion, nodeSassVersion] = await Promise.all([ getPackageVersion({ cwd: dir, name: "sass" }), getPackageVersion({ cwd: dir, name: "node-sass" }) ]); if (sassVersion && nodeSassVersion) { _log.warn("Your project has both `sass` and `node-sass` installed as dependencies, but should only use one or the other. " + "Please remove the `node-sass` dependency from your project. " + " Read more: https://nextjs.org/docs/messages/duplicate-sass"); } const { dependencies , devDependencies } = await getDependencies({ cwd: dir }); // Warn if @next/font is installed as a dependency. Ignore `workspace:*` to not warn in the Next.js monorepo. if (dependencies["@next/font"] || devDependencies["@next/font"] && devDependencies["@next/font"] !== "workspace:*") { const command = (0, _getnpxcommand.getNpxCommand)(dir); _log.warn("Your project has `@next/font` installed as a dependency, please use the built-in `next/font` instead. " + "The `@next/font` package will be removed in Next.js 14. " + `You can migrate by running \`${command} @next/codemod@latest built-in-next-font .\`. Read more: https://nextjs.org/docs/messages/built-in-next-font`); } } const port = (0, _utils.getPort)(args); // If neither --port nor PORT were specified, it's okay to retry new ports. const allowRetry = args["--port"] === undefined && process.env.PORT === undefined; // We do not set a default host value here to prevent breaking // some set-ups that rely on listening on other interfaces const host = args["--hostname"]; config = await (0, _config.default)(_constants.PHASE_DEVELOPMENT_SERVER, dir); const devServerOptions = { dir, port, allowRetry, isDev: true, nextConfig: config, hostname: host, // This is required especially for app dir. useWorkers: true }; if (args["--turbo"]) { process.env.TURBOPACK = "1"; } if (args["--experimental-turbo"]) { process.env.EXPERIMENTAL_TURBOPACK = "1"; } if (process.env.TURBOPACK) { var _rawNextConfig_experimental, _defaultConfig_experimental, _rawNextConfig_experimental1; isTurboSession = true; const { validateTurboNextConfig } = require("../lib/turbopack-warning"); const { loadBindings , __isCustomTurbopackBinary , teardownHeapProfiler } = require("../build/swc"); const { eventCliSession } = require("../telemetry/events/version"); const { setGlobal } = require("../trace"); require("../telemetry/storage"); const findUp = require("next/dist/compiled/find-up"); const isCustomTurbopack = await __isCustomTurbopackBinary(); const rawNextConfig = await validateTurboNextConfig({ isCustomTurbopack, ...devServerOptions, isDev: true }); const distDir = _path.default.join(dir, rawNextConfig.distDir || ".next"); const { pagesDir , appDir } = (0, _findpagesdir.findPagesDir)(dir, typeof (rawNextConfig == null ? void 0 : (_rawNextConfig_experimental = rawNextConfig.experimental) == null ? void 0 : _rawNextConfig_experimental.appDir) === "undefined" ? !!((_defaultConfig_experimental = _configshared.defaultConfig.experimental) == null ? void 0 : _defaultConfig_experimental.appDir) : !!((_rawNextConfig_experimental1 = rawNextConfig.experimental) == null ? void 0 : _rawNextConfig_experimental1.appDir)); const telemetry = new _storage.Telemetry({ distDir }); setGlobal("appDir", appDir); setGlobal("pagesDir", pagesDir); setGlobal("telemetry", telemetry); if (!isCustomTurbopack) { telemetry.record(eventCliSession(distDir, rawNextConfig, { webpackVersion: 5, cliCommand: "dev", isSrcDir: _path.default.relative(dir, pagesDir || appDir || "").startsWith("src"), hasNowJson: !!await findUp("now.json", { cwd: dir }), isCustomServer: false, turboFlag: true, pagesDir: !!pagesDir, appDir: !!appDir })); } if (process.platform === "darwin") { // rust needs stdout to be blocking, otherwise it will throw an error (on macOS at least) when writing a lot of data (logs) to it // see https://github.com/napi-rs/napi-rs/issues/1630 // and https://github.com/nodejs/node/blob/main/doc/api/process.md#a-note-on-process-io if (process.stdout._handle != null) { // @ts-ignore process.stdout._handle.setBlocking(true); } if (process.stderr._handle != null) { // @ts-ignore process.stderr._handle.setBlocking(true); } } // Turbopack need to be in control over reading the .env files and watching them. // So we need to start with a initial env to know which env vars are coming from the user. (0, _env.resetEnv)(); let bindings = await loadBindings(); let server = bindings.turbo.startDev({ ...devServerOptions, showAll: args["--show-all"] ?? false, root: args["--root"] ?? (0, _findroot.findRootDir)(dir) }); // Start preflight after server is listening and ignore errors: preflight().catch(()=>{}); if (!isCustomTurbopack) { await telemetry.flush(); } [ "SIGTERM", "SIGINT", "beforeExit", "exit" ].forEach((event)=>process.on(event, ()=>teardownHeapProfiler())); return server; } else { let cleanupFns = []; const runDevServer = async ()=>{ const oldCleanupFns = cleanupFns; cleanupFns = []; await Promise.allSettled(oldCleanupFns.map((fn)=>fn())); try { var _config_experimental; let shouldFilter = false; let devServerTeardown; watchConfigFiles(devServerOptions.dir, (filename)=>{ _log.warn(`\n> Found a change in ${_path.default.basename(filename)}. Restarting the server to apply the changes...`); runDevServer(); }); cleanupFns.push(unwatchConfigFiles); const setupFork = async (newDir)=>{ // if we're using workers we can auto restart on config changes if (process.env.__NEXT_DISABLE_MEMORY_WATCHER && devServerTeardown) { _log.info(`Detected change, manual restart required due to '__NEXT_DISABLE_MEMORY_WATCHER' usage`); return; } if (devServerTeardown) { await devServerTeardown(); devServerTeardown = undefined; } const startDir = dir; if (newDir) { dir = newDir; process.env = Object.keys(process.env).reduce((newEnv, key)=>{ var _process_env_key; newEnv[key] = (_process_env_key = process.env[key]) == null ? void 0 : _process_env_key.replace(startDir, newDir); return newEnv; }, {}); process.chdir(newDir); devServerOptions.dir = newDir; devServerOptions.prevDir = startDir; } // since errors can start being logged from the fork // before we detect the project directory rename // attempt suppressing them long enough to check const filterForkErrors = (chunk, fd)=>{ const cleanChunk = (0, _stripansi.default)(chunk + ""); if (cleanChunk.match(/(ENOENT|Module build failed|Module not found|Cannot find module|Can't resolve)/)) { if (startDir === dir) { try { var _config_experimental; // check if start directory is still valid const result = (0, _findpagesdir.findPagesDir)(startDir, !!(config == null ? void 0 : (_config_experimental = config.experimental) == null ? void 0 : _config_experimental.appDir)); shouldFilter = !Boolean(result.pagesDir || result.appDir); } catch (_) { shouldFilter = true; } } if (shouldFilter || startDir !== dir) { shouldFilter = true; return; } } process[fd].write(chunk); }; let resolveCleanup; let cleanupPromise = new Promise((resolve)=>{ resolveCleanup = resolve; }); const cleanupWrapper = async ()=>{ const promise = cleanupPromise; cleanupPromise = Promise.resolve(async ()=>{}); const cleanup = await promise; await cleanup(); }; cleanupFns.push(cleanupWrapper); devServerTeardown = cleanupWrapper; try { devServerOptions.onStdout = (chunk)=>{ filterForkErrors(chunk, "stdout"); }; devServerOptions.onStderr = (chunk)=>{ filterForkErrors(chunk, "stderr"); }; shouldFilter = false; resolveCleanup(await (0, _startserver.startServer)(devServerOptions)); } finally{ // fallback to noop, if not provided resolveCleanup(async ()=>{}); } }; await setupFork(); await preflight(); const parentDir = _path.default.join("/", dir, ".."); const watchedEntryLength = parentDir.split("/").length + 1; const previousItems = new Set(); const instrumentationFilePaths = !!(config == null ? void 0 : (_config_experimental = config.experimental) == null ? void 0 : _config_experimental.instrumentationHook) ? (0, _worker.getPossibleInstrumentationHookFilenames)(dir, config.pageExtensions) : []; const instrumentationFileWatcher = new _watchpack.default({}); cleanupFns.push(()=>instrumentationFileWatcher.close()); instrumentationFileWatcher.watch({ files: instrumentationFilePaths, startTime: 0 }); let instrumentationFileLastHash = undefined; const previousInstrumentationFiles = new Set(); instrumentationFileWatcher.on("aggregated", async ()=>{ var _find; const knownFiles = instrumentationFileWatcher.getTimeInfoEntries(); const instrumentationFile = (_find = [ ...knownFiles.entries() ].find(([key, value])=>instrumentationFilePaths.includes(key) && value)) == null ? void 0 : _find[0]; if (instrumentationFile) { const fs = require("fs"); const instrumentationFileHash = require("crypto").createHash("sha1").update(await fs.promises.readFile(instrumentationFile, "utf8")).digest("hex"); if (instrumentationFileLastHash && instrumentationFileHash !== instrumentationFileLastHash) { _log.warn(`The instrumentation file has changed, restarting the server to apply changes.`); return setupFork(); } else { if (!instrumentationFileLastHash && previousInstrumentationFiles.size !== 0) { _log.warn("The instrumentation file was added, restarting the server to apply changes."); return setupFork(); } instrumentationFileLastHash = instrumentationFileHash; } } else if ([ ...previousInstrumentationFiles.keys() ].find((key)=>instrumentationFilePaths.includes(key))) { _log.warn(`The instrumentation file has been removed, restarting the server to apply changes.`); instrumentationFileLastHash = undefined; return setupFork(); } previousInstrumentationFiles.clear(); knownFiles.forEach((_, key)=>previousInstrumentationFiles.add(key)); }); const projectFolderWatcher = new _watchpack.default({ ignored: (entry)=>{ return !(entry.split("/").length <= watchedEntryLength); } }); cleanupFns.push(()=>projectFolderWatcher.close()); projectFolderWatcher.watch({ directories: [ parentDir ], startTime: 0 }); projectFolderWatcher.on("aggregated", async ()=>{ const knownFiles = projectFolderWatcher.getTimeInfoEntries(); const newFiles = []; let hasPagesApp = false; // if the dir still exists nothing to check try { var _config_experimental; const result = (0, _findpagesdir.findPagesDir)(dir, !!(config == null ? void 0 : (_config_experimental = config.experimental) == null ? void 0 : _config_experimental.appDir)); hasPagesApp = Boolean(result.pagesDir || result.appDir); } catch (err) { var _err_message; // if findPagesDir throws validation error let this be // handled in the dev-server itself in the fork if ((_err_message = err.message) == null ? void 0 : _err_message.includes("experimental")) { return; } } // try to find new dir introduced if (previousItems.size) { for (const key of knownFiles.keys()){ if (!previousItems.has(key)) { newFiles.push(key); } } previousItems.clear(); } for (const key of knownFiles.keys()){ previousItems.add(key); } if (hasPagesApp) { return; } // if we failed to find the new dir it may have been moved // to a new parent directory which we can't track as easily // so exit gracefully try { var _config_experimental1; const result = (0, _findpagesdir.findPagesDir)(newFiles[0], !!(config == null ? void 0 : (_config_experimental1 = config.experimental) == null ? void 0 : _config_experimental1.appDir)); hasPagesApp = Boolean(result.pagesDir || result.appDir); } catch (_) {} if (hasPagesApp && newFiles.length === 1) { _log.info(`Detected project directory rename, restarting in new location`); setupFork(newFiles[0]); watchConfigFiles(newFiles[0]); } else { _log.error(`Project directory could not be found, restart Next.js in your new directory`); process.exit(0); } }); } catch (err) { console.error(err); process.exit(1); } }; await runDevServer(); } }; //# sourceMappingURL=next-dev.js.map