next
Version:
The React Framework
487 lines (483 loc) • 23.8 kB
JavaScript
;
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