#!/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";
    }
    const experimentalTurbo = !!process.env.EXPERIMENTAL_TURBOPACK;
    if (experimentalTurbo) {
        const { loadBindings  } = require("../build/swc");
        (0, _env.resetEnv)();
        let bindings = await loadBindings();
        // Just testing code here:
        const options = {
            projectPath: dir,
            rootPath: args["--root"] ?? (0, _findroot.findRootDir)(dir) ?? dir,
            nextConfig: config,
            env: {
                NEXT_PUBLIC_ENV_VAR: "world"
            },
            watch: true
        };
        const project = await bindings.turbo.createProject(options);
        const iter = project.entrypointsSubscribe();
        try {
            for await (const entrypoints of iter){
                _log.info(entrypoints);
                _log.info(`writing _document to disk`);
                _log.info(await entrypoints.pagesDocumentEndpoint.writeToDisk());
                _log.info(`writing _app to disk`);
                _log.info(await entrypoints.pagesAppEndpoint.writeToDisk());
                _log.info(`writing _error to disk`);
                _log.info(await entrypoints.pagesErrorEndpoint.writeToDisk());
                for (const [pathname, route] of entrypoints.routes){
                    switch(route.type){
                        case "page":
                            {
                                _log.info(`writing ${pathname} to disk`);
                                const written = await route.htmlEndpoint.writeToDisk();
                                _log.info(written);
                                break;
                            }
                        case "page-api":
                            {
                                _log.info(`writing ${pathname} to disk`);
                                const written = await route.endpoint.writeToDisk();
                                _log.info(written);
                                break;
                            }
                        case "app-page":
                            {
                                _log.info(`writing ${pathname} to disk`);
                                const written = await route.rscEndpoint.writeToDisk();
                                _log.info(written);
                                break;
                            }
                        case "app-route":
                            {
                                _log.info(`writing ${pathname} to disk`);
                                const written = await route.endpoint.writeToDisk();
                                _log.info(written);
                                break;
                            }
                        default:
                            _log.info(`skipping ${pathname} (${route.type})`);
                            break;
                    }
                }
                _log.info("iteration done");
                await project.update({
                    ...options,
                    env: {
                        NEXT_PUBLIC_ENV_VAR: "hello"
                    }
                });
            }
        } catch (e) {
            console.dir(e);
        }
        _log.error("Not supported yet");
        process.exit(1);
    } else 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