next
Version:
The React Framework
1,047 lines • 86.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "createHotReloaderTurbopack", {
enumerable: true,
get: function() {
return createHotReloaderTurbopack;
}
});
const _promises = require("fs/promises");
const _fs = require("fs");
const _inspector = /*#__PURE__*/ _interop_require_wildcard(require("inspector"));
const _path = require("path");
const _url = require("url");
const _ws = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/ws"));
const _store = require("../../build/output/store");
const _hotreloadertypes = require("./hot-reloader-types");
const _swc = require("../../build/swc");
const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../build/output/log"));
const _constants = require("../../shared/lib/constants");
const _middlewareturbopack = require("./middleware-turbopack");
const _utils = require("../../shared/lib/utils");
const _utils1 = require("../utils");
const _loadmanifestexternal = require("../load-manifest.external");
const _requirecache = require("./require-cache");
const _renderserver = require("../lib/render-server");
const _denormalizepagepath = require("../../shared/lib/page-path/denormalize-page-path");
const _trace = require("../../trace");
const _turbopackutils = require("./turbopack-utils");
const _setupdevbundler = require("../lib/router-utils/setup-dev-bundler");
const _manifestloader = require("../../shared/lib/turbopack/manifest-loader");
const _ondemandentryhandler = require("./on-demand-entry-handler");
const _entrykey = require("../../shared/lib/turbopack/entry-key");
const _messages = require("./messages");
const _encryptionutilsserver = require("../app-render/encryption-utils-server");
const _apppageroutedefinition = require("../route-definitions/app-page-route-definition");
const _apppaths = require("../../shared/lib/router/utils/app-paths");
const _entries = require("../../build/entries");
const _ismetadataroute = require("../../lib/metadata/is-metadata-route");
const _patcherrorinspect = require("../patch-error-inspect");
const _sourcemaps = require("../lib/source-maps");
const _getnexterrorfeedbackmiddleware = require("../../next-devtools/server/get-next-error-feedback-middleware");
const _utils2 = require("../../shared/lib/turbopack/utils");
const _getdevoverlayfontmiddleware = require("../../next-devtools/server/font/get-dev-overlay-font-middleware");
const _devindicatorserverstate = require("./dev-indicator-server-state");
const _devindicatormiddleware = require("../../next-devtools/server/dev-indicator-middleware");
const _restartdevservermiddleware = require("../../next-devtools/server/restart-dev-server-middleware");
const _compilationevents = require("../../shared/lib/turbopack/compilation-events");
const _deferredemit = require("../../shared/lib/turbopack/deferred-emit");
const _getsupportedbrowsers = require("../../build/get-supported-browsers");
const _printbuilderrors = require("../../build/print-build-errors");
const _receivelogs = require("./browser-logs/receive-logs");
const _normalizepath = require("../../lib/normalize-path");
const _turbopackcacheseed = require("../../lib/turbopack-cache-seed");
const _devtoolsconfigmiddleware = require("../../next-devtools/server/devtools-config-middleware");
const _attachnodejsdebuggermiddleware = require("../../next-devtools/server/attach-nodejs-debugger-middleware");
const _debugchannel = require("./debug-channel");
const _hotreloadersharedutils = require("./hot-reloader-shared-utils");
const _getmcpmiddleware = require("../mcp/get-mcp-middleware");
const _formatcompilationissues = require("../mcp/tools/utils/format-compilation-issues");
const _requestinsights = require("../lib/trace/request-insights");
const _resolvepathtoroute = require("../mcp/tools/utils/resolve-path-to-route");
const _geterrors = require("../mcp/tools/get-errors");
const _getpagemetadata = require("../mcp/tools/get-page-metadata");
const _formaterrors = require("../mcp/tools/utils/format-errors");
const _mcptelemetrytracker = require("../mcp/mcp-telemetry-tracker");
const _filelogger = require("./browser-logs/file-logger");
const _serializederrors = require("./serialized-errors");
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 = {
__proto__: null
};
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;
}
const wsServer = new _ws.default.Server({
noServer: true
});
const isTestMode = !!(process.env.NEXT_TEST_MODE || process.env.__NEXT_TEST_MODE || process.env.DEBUG);
const sessionId = Math.floor(Number.MAX_SAFE_INTEGER * Math.random());
/** Output directory (relative to `distDir`) of server-HMR-managed chunks. */ const SERVER_HMR_CHUNKS_DIR = (0, _path.join)('server', 'chunks');
/**
* Collects the output chunk paths touched by a partial HMR update. Both
* single-chunk `EcmascriptMergedUpdate`s and `ChunkListUpdate`s (which nest
* per-chunk deltas inside `merged`) are flattened so the manifest cache can be
* invalidated for every affected chunk after a successful apply.
*/ function collectUpdatedChunkPaths(instruction) {
const paths = new Set();
if (instruction.type === 'EcmascriptMergedUpdate') {
for (const chunkPath of Object.keys(instruction.chunks ?? {})){
paths.add(chunkPath);
}
} else if (instruction.type === 'ChunkListUpdate') {
for (const chunkPath of Object.keys(instruction.chunks ?? {})){
paths.add(chunkPath);
}
for (const merged of instruction.merged ?? []){
for (const chunkPath of Object.keys(merged.chunks ?? {})){
paths.add(chunkPath);
}
}
} else {
throw Object.defineProperty(new Error(`[Server HMR] unreachable: unknown HMR instruction type ${instruction.type}`), "__NEXT_ERROR_CODE", {
value: "E1459",
enumerable: false,
configurable: true
});
}
return Array.from(paths);
}
function setupServerHmr(project, { reEvaluateAllModulesExpensive, onApplied }) {
async function runSubscription() {
const subscription = project.allHmrEvents(_swc.HmrTarget.Server);
// Subscribing immediately emits one event describing the current state.
// There's no previous state to diff it against, so it never carries anything
// to apply. Drop it; real updates start with the second event.
await subscription.next();
for await (const result of subscription){
const update = result;
// A 'restart' from the wire protocol means the update can't be applied
// incrementally, so we must fully re-evaluate all chunks from disk. This
// clears the module cache and notifies browsers to refetch RSC.
const requiresFullReEvaluation = update.type === 'restart';
if (requiresFullReEvaluation) {
await reEvaluateAllModulesExpensive();
continue;
}
if (update.type !== 'partial') {
continue;
}
// `EcmascriptMergedUpdate` is the only instruction the Node.js runtime
// knows how to apply; `ChunkListUpdate` is browser-only. Anything else is
// unknown to us, so ignore it rather than evicting the module cache.
const instruction = update.instruction;
if (!instruction || instruction.type !== 'EcmascriptMergedUpdate' && instruction.type !== 'ChunkListUpdate') {
throw Object.defineProperty(new Error(`[Server HMR] unreachable: unexpected update instruction type ${instruction.type}`), "__NEXT_ERROR_CODE", {
value: "E1460",
enumerable: false,
configurable: true
});
}
// No handler registered yet (before first request, or right after
// reEvaluateAllModulesExpensive()) — nothing live to update, so skip
// until the next request.
const handlers = globalThis.__turbopack_server_hmr_handlers__;
if (!handlers || handlers.size === 0) {
continue;
}
if (typeof __turbopack_server_hmr_apply__ === 'function') {
try {
__turbopack_server_hmr_apply__(update);
} catch {
// A matching runtime tried the apply and threw. Evict require.cache
// so the next request loads fresh, then skip onApplied. (A no-match
// update is a no-op and does not throw.)
await reEvaluateAllModulesExpensive();
continue;
}
const updatedChunkPaths = collectUpdatedChunkPaths(instruction);
// An empty partial only advances the version state (e.g. the seed
// transition or a new endpoint); nothing changed on disk, so don't
// invalidate manifests or ping browsers to refetch RSC.
if (updatedChunkPaths.length > 0) {
await onApplied(updatedChunkPaths);
}
} else {
await reEvaluateAllModulesExpensive();
}
}
}
// Start listening for changes in background. Re-subscribe on error so
// server Fast Refresh continues working for the rest of the dev session.
// The delay keeps a persistently-failing subscription (which throws on the
// initial read) from hot-looping through reEvaluateAllModulesExpensive().
;
(async ()=>{
for(;;){
try {
await runSubscription();
return;
} catch (err) {
console.error('[Server HMR] Subscription error, resubscribing:', err);
await reEvaluateAllModulesExpensive();
await new Promise((resolve)=>setTimeout(resolve, 1000));
}
}
})();
}
function getSourceMapFromTurbopack(project, sourceURL) {
let sourceMapJson = null;
try {
sourceMapJson = project.getSourceMapSync(sourceURL);
} catch (err) {}
if (sourceMapJson === null) {
return undefined;
} else {
return JSON.parse(sourceMapJson);
}
}
function getSourceMapURLFromTurbopack(distDir, scriptNameOrSourceURL) {
// React invokes this with the raw stack-frame filename, which arrives
// either as an absolute filesystem path or as a `file:` URL. Anything else
// (`file:` URLs with a query are eval'd server HMR modules carrying inline
// source maps, `webpack-internal://`, `node:internal/...`, `<anonymous>`,
// ...) is not something we have an emitted source map for.
let scriptPath = scriptNameOrSourceURL;
if (scriptNameOrSourceURL.startsWith('file://')) {
if (scriptNameOrSourceURL.includes('?')) {
return null;
}
try {
scriptPath = (0, _url.fileURLToPath)(scriptNameOrSourceURL);
} catch {
return null;
}
}
if (!(0, _path.isAbsolute)(scriptPath)) {
return null;
}
// Only chunks emitted into `distDir` have an on-disk source map to point at.
const relativePath = (0, _path.relative)(distDir, scriptPath);
if (relativePath.startsWith('..') || // On Windows an absolute path on a different drive is returned unchanged
// rather than as a `..`-prefixed relative path.
(0, _path.isAbsolute)(relativePath)) {
return null;
}
// The emitted source map lives next to its chunk with a `.map` suffix (see
// `SourceMapAsset::path`). Encode through `pathToFileURL` so any special
// characters in the path are escaped into a well-formed `file:` URL.
return (0, _url.pathToFileURL)(scriptPath + '.map').href;
}
async function createHotReloaderTurbopack(opts, serverFields, distDir, resetFetch, lockfile, serverFastRefresh) {
var _opts_nextConfig_turbopack, _nextConfig_watchOptions;
const dev = true;
const buildId = 'development';
const { nextConfig, dir: projectPath } = opts;
const bindings = (0, _swc.getBindingsSync)();
// Turbopack requires native bindings and cannot run with WASM bindings.
// Detect this early and give a clear, actionable error message.
if (bindings.isWasm) {
throw Object.defineProperty(new Error(`Turbopack is not supported on this platform (${process.platform}/${process.arch}) because native bindings are not available. ` + `Only WebAssembly (WASM) bindings were loaded, and Turbopack requires native bindings.\n\n` + `To use Next.js on this platform, use Webpack instead:\n` + ` next dev --webpack\n\n` + `For more information, see: https://nextjs.org/docs/app/api-reference/turbopack#supported-platforms`), "__NEXT_ERROR_CODE", {
value: "E1049",
enumerable: false,
configurable: true
});
}
// For the debugging purpose, check if createNext or equivalent next instance setup in test cases
// works correctly. Normally `run-test` hides output so only will be visible when `--debug` flag is used.
if (isTestMode) {
;
require('console').log('Creating turbopack project', {
dir: projectPath,
testMode: isTestMode
});
}
const hasRewrites = opts.fsChecker.rewrites.afterFiles.length > 0 || opts.fsChecker.rewrites.beforeFiles.length > 0 || opts.fsChecker.rewrites.fallback.length > 0;
const hotReloaderSpan = (0, _trace.trace)('hot-reloader', undefined, {
version: "16.3.0"
});
// Ensure the hotReloaderSpan is flushed immediately as it's the parentSpan for all processing
// of the current `next dev` invocation.
hotReloaderSpan.stop();
// Initialize log monitor for file logging
// Enable logging by default in development mode
const mcpServerEnabled = !!nextConfig.experimental.mcpServer;
const fileLogger = (0, _filelogger.getFileLogger)();
fileLogger.initialize(distDir, mcpServerEnabled);
const encryptionKey = await (0, _encryptionutilsserver.generateEncryptionKeyBase64)({
isBuild: false,
distDir
});
// TODO: Implement
let clientRouterFilters;
if (nextConfig.experimental.clientRouterFilter) {
// TODO this need to be set correctly for filesystem cache to work
}
const supportedBrowsers = (0, _getsupportedbrowsers.getSupportedBrowsers)(projectPath, dev);
const currentNodeJsVersion = process.versions.node;
const rootPath = ((_opts_nextConfig_turbopack = opts.nextConfig.turbopack) == null ? void 0 : _opts_nextConfig_turbopack.root) || opts.nextConfig.outputFileTracingRoot || projectPath;
if (nextConfig.experimental.turbopackSeedCacheFromWorktree) {
(0, _turbopackcacheseed.seedTurbopackCacheIfNeeded)({
projectDir: projectPath,
distDir
});
}
const project = await bindings.turbo.createProject({
rootPath,
projectPath: (0, _normalizepath.normalizePath)((0, _path.relative)(rootPath, projectPath) || '.'),
distDir,
nextConfig: opts.nextConfig,
watch: {
enable: dev,
pollIntervalMs: (_nextConfig_watchOptions = nextConfig.watchOptions) == null ? void 0 : _nextConfig_watchOptions.pollIntervalMs
},
dev,
env: process.env,
defineEnv: (0, _swc.createDefineEnv)({
isTurbopack: true,
clientRouterFilters,
config: nextConfig,
dev,
distDir,
projectPath,
fetchCacheKeyPrefix: opts.nextConfig.experimental.fetchCacheKeyPrefix,
hasRewrites,
// TODO: Implement
middlewareMatchers: undefined,
rewrites: opts.fsChecker.rewrites
}),
buildId,
encryptionKey,
previewProps: opts.fsChecker.previewProps,
browserslistQuery: supportedBrowsers.join(', '),
noMangling: false,
writeRoutesHashesManifest: false,
currentNodeJsVersion,
isPersistentCachingEnabled: (0, _utils2.isFileSystemCacheEnabledForDev)(opts.nextConfig),
nextVersion: "16.3.0",
serverHmr: serverFastRefresh
}, {
turbopackMemoryEviction: opts.nextConfig.experimental.turbopackMemoryEvictionMode,
isShortSession: false
});
(0, _compilationevents.backgroundLogCompilationEvents)(project, {
eventTypes: [
'StartupCacheInvalidationEvent',
'TimingEvent',
'SlowFilesystemEvent',
'TraceEvent'
],
parentSpan: hotReloaderSpan
});
(0, _patcherrorinspect.setBundlerFindSourceMapImplementation)(getSourceMapFromTurbopack.bind(null, project));
let canonicalDistDir = distDir;
try {
canonicalDistDir = (0, _fs.realpathSync)(distDir);
} catch {}
(0, _sourcemaps.setBundlerFindSourceMapURLImplementation)(getSourceMapURLFromTurbopack.bind(null, canonicalDistDir));
// Set up code frame renderer using native bindings
const { installCodeFrameSupport } = require('../lib/install-code-frame');
installCodeFrameSupport();
opts.onDevServerCleanup == null ? void 0 : opts.onDevServerCleanup.call(opts, async ()=>{
(0, _patcherrorinspect.setBundlerFindSourceMapImplementation)(()=>undefined);
(0, _sourcemaps.setBundlerFindSourceMapURLImplementation)(()=>null);
await project.onExit();
await (lockfile == null ? void 0 : lockfile.unlock());
});
const entrypointsSubscription = project.entrypointsSubscribe();
const currentWrittenEntrypoints = new Map();
const currentEntrypoints = {
global: {
app: undefined,
document: undefined,
error: undefined,
middleware: undefined,
instrumentation: undefined
},
page: new Map(),
app: new Map()
};
const currentTopLevelIssues = new Map();
const currentEntryIssues = new Map();
const manifestLoader = new _manifestloader.TurbopackManifestLoader({
buildId,
distDir,
encryptionKey,
dev: true,
sriEnabled: false
});
// Dev specific
const changeSubscriptions = new Map();
const serverPathState = new Map();
const readyIds = new Set();
let currentEntriesHandlingResolve;
let currentEntriesHandling = new Promise((resolve)=>currentEntriesHandlingResolve = resolve);
const assetMapper = new _turbopackutils.AssetMapper();
// Deferred entries state management
const deferredEntriesConfig = nextConfig.experimental.deferredEntries;
const hasDeferredEntriesConfig = deferredEntriesConfig && deferredEntriesConfig.length > 0;
let onBeforeDeferredEntriesCalled = false;
let onBeforeDeferredEntriesPromise = null;
// Track non-deferred entries that are currently being built
const nonDeferredBuildingEntries = new Set();
// Function to wait for all non-deferred entries to be built
async function waitForNonDeferredEntries() {
return new Promise((resolve)=>{
const checkEntries = ()=>{
// Check if there are any non-deferred entries that are still building
if (nonDeferredBuildingEntries.size === 0) {
resolve();
} else {
// Check again after a short delay
setTimeout(checkEntries, 100);
}
};
checkEntries();
});
}
// Function to handle deferred entry processing
async function processDeferredEntry() {
if (!hasDeferredEntriesConfig) return;
// Wait for all non-deferred entries to be built
await waitForNonDeferredEntries();
// Call the onBeforeDeferredEntries callback once
if (!onBeforeDeferredEntriesCalled) {
onBeforeDeferredEntriesCalled = true;
if (nextConfig.experimental.onBeforeDeferredEntries) {
if (!onBeforeDeferredEntriesPromise) {
onBeforeDeferredEntriesPromise = nextConfig.experimental.onBeforeDeferredEntries();
}
await onBeforeDeferredEntriesPromise;
}
} else if (onBeforeDeferredEntriesPromise) {
// Wait for any in-progress callback
await onBeforeDeferredEntriesPromise;
}
}
// Track whether HMR is pending - used to call callback once after HMR settles
let hmrPendingDeferredCallback = false;
// Debounced function to call onBeforeDeferredEntries after HMR
// This prevents rapid-fire calls when turbopack fires many update events
// Use 500ms debounce to ensure all rapid updates are batched together
const callOnBeforeDeferredEntriesAfterHMR = (0, _utils1.debounce)(()=>{
// Only call if HMR triggered a need for the callback
if (hasDeferredEntriesConfig && hmrPendingDeferredCallback) {
hmrPendingDeferredCallback = false;
onBeforeDeferredEntriesCalled = true;
if (nextConfig.experimental.onBeforeDeferredEntries) {
onBeforeDeferredEntriesPromise = nextConfig.experimental.onBeforeDeferredEntries();
}
}
}, 500);
function clearRequireCache(key, writtenEndpoint, { force } = {}) {
if (force) {
for (const { path, contentHash } of writtenEndpoint.serverPaths){
// We ignore source maps
if (path.endsWith('.map')) continue;
const localKey = `${key}:${path}`;
serverPathState.set(localKey, contentHash);
serverPathState.set(path, contentHash);
}
} else {
// Figure out if the server files have changed
let hasChange = false;
const currentPaths = new Set();
for (const { path, contentHash } of writtenEndpoint.serverPaths){
// We ignore source maps
if (path.endsWith('.map')) continue;
currentPaths.add(path);
const localKey = `${key}:${path}`;
const localHash = serverPathState.get(localKey);
const globalHash = serverPathState.get(path);
if (localHash && localHash !== contentHash || globalHash && globalHash !== contentHash) {
hasChange = true;
serverPathState.set(localKey, contentHash);
serverPathState.set(path, contentHash);
} else {
if (!localHash) {
serverPathState.set(localKey, contentHash);
}
if (!globalHash) {
serverPathState.set(path, contentHash);
}
}
}
const localKeyPrefix = `${key}:`;
for (const pathKey of serverPathState.keys()){
if (pathKey.startsWith(localKeyPrefix) && !currentPaths.has(pathKey.slice(localKeyPrefix.length))) {
serverPathState.delete(pathKey);
hasChange = true;
}
}
if (!hasChange) {
return false;
}
}
// Edge does not participate in server HMR.
if (writtenEndpoint.type === 'edge') {
void (0, _renderserver.clearAllModuleContexts)();
}
const serverPaths = writtenEndpoint.serverPaths.map(({ path: p })=>(0, _path.join)(distDir, p));
const { type: entryType } = (0, _entrykey.splitEntryKey)(key);
// Server HMR applies to App Router entries built with the Turbopack Node.js
// runtime: app pages and route handlers (including metadata routes). Edge
// routes, Pages Router pages, and middleware/instrumentation are excluded.
const usesServerHmr = serverFastRefresh && entryType === 'app' && writtenEndpoint.type !== 'edge';
const serverChunksPrefix = SERVER_HMR_CHUNKS_DIR + _path.sep;
const filesToDelete = [];
for (const file of serverPaths){
(0, _renderserver.clearModuleContext)(file);
const relativePath = (0, _path.relative)(distDir, file);
if (// For Pages Router, edge routes, middleware, and any entry not
// participating in server HMR: clear the sharedCache in
// evalManifest(), Node.js require.cache, and edge runtime module
// contexts.
force || !usesServerHmr || !relativePath.startsWith(serverChunksPrefix)) {
filesToDelete.push(file);
}
}
(0, _requirecache.deleteCache)(filesToDelete);
// Reset the fetch patch so patchFetch() can re-wrap on the next request.
if (serverPaths.length > 0) {
resetFetch();
}
// Clear Turbopack's chunk-loading cache so chunks are re-required from disk on
// the next request.
//
// For App Router with server HMR, this is normally skipped as server HMR
// manages module updates in-place. However, it *is* required when force is `true`
// (like for .env file or tsconfig changes).
if ((!usesServerHmr || force) && typeof __next__clear_chunk_cache__ === 'function') {
__next__clear_chunk_cache__();
}
return true;
}
const buildingIds = new Set();
const startBuilding = (id, requestUrl, forceRebuild)=>{
if (!forceRebuild && readyIds.has(id)) {
return ()=>{};
}
if (buildingIds.size === 0) {
_store.store.setState({
loading: true,
trigger: id,
url: requestUrl
}, true);
}
buildingIds.add(id);
return function finishBuilding() {
if (buildingIds.size === 0) {
return;
}
readyIds.add(id);
buildingIds.delete(id);
if (buildingIds.size === 0) {
hmrEventHappened = false;
_store.store.setState({
loading: false
}, true);
}
};
};
let hmrEventHappened = false;
// A counter identifying the current version of the compiled output, included
// by `"use cache"` in dev cache keys so that cached entries revalidate after
// an edit. It advances once per HMR change event (for App Router pages that
// is an RSC change, which is what a cached render depends on), independent of
// how many clients are connected. It deliberately does not advance on `BUILT`
// messages: those are sent per connected client on every compilation, so
// advancing there would both churn the hash without an edit and fail to
// advance it at all when no client is connected.
let hmrHash = 0;
// HACK: Defer sending `building` messages. Turbopack emits a compile pass for every
// foreground-job cycle, including empty no-op recompiles scheduled by
// request/render activity that changed no files. This allows us to prevent
// sending them if we quickly get a `built` message after a `building` message.
const pendingBuilding = new _deferredemit.DeferredEmit();
const clientsWithoutHtmlRequestId = new Set();
const clientsByHtmlRequestId = new Map();
const cacheStatusesByHtmlRequestId = new Map();
const clientStates = new WeakMap();
function sendToClient(client, message) {
const data = typeof message.type === 'number' ? (0, _messages.createBinaryHmrMessageData)(message) : JSON.stringify(message);
client.send(data);
}
let updateInProgress = false;
let pendingServerComponentChanges = false;
function sendServerComponentChanges() {
sendHmr('server-component-changes', {
type: _hotreloadertypes.HMR_MESSAGE_SENT_TO_BROWSER.SERVER_COMPONENT_CHANGES
});
}
// Each announcement makes every client refetch its page, so an update's
// changes are announced once, on the update's end.
function handleServerComponentChanges() {
if (updateInProgress) {
pendingServerComponentChanges = true;
} else {
sendServerComponentChanges();
}
}
function hasCompilationErrors() {
for (const [, issueMap] of currentEntryIssues){
if ([
...issueMap.values()
].filter((i)=>i.severity !== 'warning').length > 0) {
return true;
}
}
return false;
}
function sendEnqueuedMessages() {
if (hasCompilationErrors()) {
// During compilation errors we want to delay the HMR events until errors are fixed
return;
}
for (const client of [
...clientsWithoutHtmlRequestId,
...clientsByHtmlRequestId.values()
]){
const state = clientStates.get(client);
if (!state) {
continue;
}
for (const [, issueMap] of state.clientIssues){
if ([
...issueMap.values()
].filter((i)=>i.severity !== 'warning').length > 0) {
// During compilation errors we want to delay the HMR events until errors are fixed
return;
}
}
for (const message of state.messages.values()){
sendToClient(client, message);
}
state.messages.clear();
if (state.turbopackUpdates.length > 0) {
sendToClient(client, {
type: _hotreloadertypes.HMR_MESSAGE_SENT_TO_BROWSER.TURBOPACK_MESSAGE,
data: state.turbopackUpdates
});
state.turbopackUpdates.length = 0;
}
}
}
const sendEnqueuedMessagesDebounce = (0, _utils1.debounce)(sendEnqueuedMessages, 2);
const sendHmr = (id, message)=>{
pendingBuilding.flush();
for (const client of [
...clientsWithoutHtmlRequestId,
...clientsByHtmlRequestId.values()
]){
var _clientStates_get;
(_clientStates_get = clientStates.get(client)) == null ? void 0 : _clientStates_get.messages.set(id, message);
}
hmrEventHappened = true;
sendEnqueuedMessagesDebounce();
};
function sendTurbopackMessage(payload) {
// TODO(PACK-2049): For some reason we end up emitting hundreds of issues messages on bigger apps,
// a lot of which are duplicates.
// They are currently not handled on the client at all, so might as well not send them for now.
payload.diagnostics = [];
payload.issues = [];
pendingBuilding.flush();
for (const client of [
...clientsWithoutHtmlRequestId,
...clientsByHtmlRequestId.values()
]){
var _clientStates_get;
(_clientStates_get = clientStates.get(client)) == null ? void 0 : _clientStates_get.turbopackUpdates.push(payload);
}
hmrEventHappened = true;
sendEnqueuedMessagesDebounce();
}
async function subscribeToClientChanges(key, includeIssues, endpoint, createMessage, onError) {
if (changeSubscriptions.has(key)) {
return;
}
const { side } = (0, _entrykey.splitEntryKey)(key);
const changedPromise = endpoint[`${side}Changed`](includeIssues);
changeSubscriptions.set(key, changedPromise);
try {
const changed = await changedPromise;
for await (const change of changed){
(0, _utils2.processIssues)(currentEntryIssues, key, change, false, true);
// TODO: Get an actual content hash from Turbopack.
const message = await createMessage(change, String(++hmrHash));
if (message) {
sendHmr(key, message);
}
}
} catch (e) {
changeSubscriptions.delete(key);
const payload = await (onError == null ? void 0 : onError(e));
if (payload) {
sendHmr(key, payload);
}
return;
}
changeSubscriptions.delete(key);
}
async function unsubscribeFromClientChanges(key) {
const subscription = await changeSubscriptions.get(key);
if (subscription) {
await (subscription.return == null ? void 0 : subscription.return.call(subscription));
changeSubscriptions.delete(key);
}
currentEntryIssues.delete(key);
}
async function subscribeToClientHmrEvents(client, id) {
const key = (0, _entrykey.getEntryKey)('assets', 'client', id);
if (!(0, _turbopackutils.hasEntrypointForKey)(currentEntrypoints, key, assetMapper)) {
// maybe throw an error / force the client to reload?
return;
}
const state = clientStates.get(client);
if (!state || state.subscriptions.has(id)) {
return;
}
const subscription = project.hmrEvents(id, _swc.HmrTarget.Client);
state.subscriptions.set(id, subscription);
// The subscription will always emit once, which is the initial
// computation. This is not a change, so swallow it.
try {
await subscription.next();
for await (const data of subscription){
(0, _utils2.processIssues)(state.clientIssues, key, data, false, true);
if (data.type !== 'issues') {
sendTurbopackMessage(data);
}
}
} catch (e) {
// The client might be using an HMR session from a previous server, tell them
// to fully reload the page to resolve the issue. We can't use
// `hotReloader.send` since that would force every connected client to
// reload, only this client is out of date.
const reloadMessage = {
type: _hotreloadertypes.HMR_MESSAGE_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in HMR event subscription for ${id}: ${e}`
};
sendToClient(client, reloadMessage);
client.close();
return;
}
}
function unsubscribeFromClientHmrEvents(client, id) {
const state = clientStates.get(client);
if (!state) {
return;
}
const subscription = state.subscriptions.get(id);
subscription == null ? void 0 : subscription.return();
const key = (0, _entrykey.getEntryKey)('assets', 'client', id);
state.clientIssues.delete(key);
}
async function handleEntrypointsSubscription() {
for await (const entrypoints of entrypointsSubscription){
if (!currentEntriesHandlingResolve) {
currentEntriesHandling = new Promise(// eslint-disable-next-line no-loop-func
(resolve)=>currentEntriesHandlingResolve = resolve);
}
// Always process issues/diagnostics, even if there are no entrypoints yet
(0, _turbopackutils.processTopLevelIssues)(currentTopLevelIssues, entrypoints);
// Certain crtical issues prevent any entrypoints from being constructed so return early
if (!('routes' in entrypoints)) {
(0, _printbuilderrors.printBuildErrors)(entrypoints, true);
currentEntriesHandlingResolve();
currentEntriesHandlingResolve = undefined;
continue;
}
const routes = entrypoints.routes;
const existingRoutes = [
...currentEntrypoints.app.keys(),
...currentEntrypoints.page.keys()
];
const newRoutes = [
...routes.keys()
];
const addedRoutes = newRoutes.filter((route)=>!currentEntrypoints.app.has(route) && !currentEntrypoints.page.has(route));
const removedRoutes = existingRoutes.filter((route)=>!routes.has(route));
await (0, _turbopackutils.handleEntrypoints)({
entrypoints: entrypoints,
currentEntrypoints,
currentEntryIssues,
manifestLoader,
devRewrites: opts.fsChecker.rewrites,
productionRewrites: undefined,
logErrors: true,
dev: {
assetMapper,
changeSubscriptions,
clients: [
...clientsWithoutHtmlRequestId,
...clientsByHtmlRequestId.values()
],
clientStates,
serverFields,
hooks: {
handleWrittenEndpoint: (id, result, forceDeleteCache)=>{
currentWrittenEntrypoints.set(id, result);
return clearRequireCache(id, result, {
force: forceDeleteCache
});
},
propagateServerField: _setupdevbundler.propagateServerField.bind(null, opts),
sendHmr,
startBuilding,
subscribeToChanges: subscribeToClientChanges,
unsubscribeFromChanges: unsubscribeFromClientChanges,
unsubscribeFromHmrEvents: unsubscribeFromClientHmrEvents
}
}
});
// Reload matchers when the files have been compiled
await (0, _setupdevbundler.propagateServerField)(opts, 'reloadMatchers', undefined);
if (addedRoutes.length > 0 || removedRoutes.length > 0) {
// When the list of routes changes a new manifest should be fetched for Pages Router.
hotReloader.send({
type: _hotreloadertypes.HMR_MESSAGE_SENT_TO_BROWSER.DEV_PAGES_MANIFEST_UPDATE,
data: [
{
devPagesManifest: true
}
]
});
}
for (const route of addedRoutes){
hotReloader.send({
type: _hotreloadertypes.HMR_MESSAGE_SENT_TO_BROWSER.ADDED_PAGE,
data: [
route
]
});
}
for (const route of removedRoutes){
hotReloader.send({
type: _hotreloadertypes.HMR_MESSAGE_SENT_TO_BROWSER.REMOVED_PAGE,
data: [
route
]
});
}
currentEntriesHandlingResolve();
currentEntriesHandlingResolve = undefined;
}
}
await (0, _promises.mkdir)((0, _path.join)(distDir, 'server'), {
recursive: true
});
await (0, _promises.mkdir)((0, _path.join)(distDir, 'static', buildId), {
recursive: true
});
await (0, _promises.writeFile)((0, _path.join)(distDir, 'package.json'), JSON.stringify({
type: 'commonjs'
}, null, 2));
const middlewares = [
(0, _middlewareturbopack.getOverlayMiddleware)({
project,
projectPath,
isSrcDir: opts.isSrcDir
}),
(0, _middlewareturbopack.getSourceMapMiddleware)(project),
(0, _getnexterrorfeedbackmiddleware.getNextErrorFeedbackMiddleware)(opts.telemetry),
(0, _getdevoverlayfontmiddleware.getDevOverlayFontMiddleware)(),
(0, _devindicatormiddleware.getDisableDevIndicatorMiddleware)(),
(0, _restartdevservermiddleware.getRestartDevServerMiddleware)({
telemetry: opts.telemetry,
turbopackProject: project
}),
(0, _devtoolsconfigmiddleware.devToolsConfigMiddleware)({
distDir,
sendUpdateSignal: (data)=>{
hotReloader.send({
type: _hotreloadertypes.HMR_MESSAGE_SENT_TO_BROWSER.DEVTOOLS_CONFIG,
data
});
}
}),
(0, _attachnodejsdebuggermiddleware.getAttachNodejsDebuggerMiddleware)(),
...nextConfig.experimental.mcpServer ? [
(0, _getmcpmiddleware.getMcpMiddleware)({
projectPath,
distDir,
nextConfig,
pagesDir: opts.pagesDir,
appDir: opts.appDir,
sendHmrMessage: (message)=>hotReloader.send(message),
getActiveConnectionCount: ()=>clientsWithoutHtmlRequestId.size + clientsByHtmlRequestId.size,
getDevServerUrl: ()=>process.env.__NEXT_PRIVATE_ORIGIN,
getTurbopackProject: ()=>project,
compileRoute: async ({ routeSpecifier, path })=>{
// Resolve the caller's input to a concrete route specifier. The
// path-mode branch reuses the dev router's own live route table
// (opts.fsChecker) — the same one resolve-routes.ts consults on
// every incoming HTTP request — so first-match ordering and live
// route updates are inherited for free.
let page;
if (routeSpecifier != null) {
page = routeSpecifier;
} else if (path != null) {
const resolved = (0, _resolvepathtoroute.resolvePathToRoute)(path, {
appFiles: opts.fsChecker.appFiles,
pageFiles: opts.fsChecker.pageFiles,
dynamicRoutes: opts.fsChecker.getDynamicRoutes()
});
if ('notFound' in resolved) {
const err = Object.defineProperty(new Error(`no route matched for path "${resolved.pathname}"`), "__NEXT_ERROR_CODE", {
value: "E1277",
enumerable: false,
configurable: true
});
err.code = 'ENOENT';
throw err;
}
page = resolved.routeSpecifier;
} else {
// Tool handler rejects the empty case; defend the boundary.
throw Object.defineProperty(new Error('compileRoute: either routeSpecifier or path is required'), "__NEXT_ERROR_CODE", {
value: "E1278",
enumerable: false,
configurable: true
});
}
// ensurePage uses findPagePathData when no definition is provided,
// which calls normalizePagePath("/") → "/index" then findPageFile
// looking for "index.tsx" — neither of which matches "page.tsx" in
// the app dir. Pass a synthetic definition instead.
//
// currentEntrypoints.app is keyed by originalName which includes the
// trailing /page or /route segment (e.g. "/page" for the root route,
// "/blog/[slug]/page" for a dynamic page). Use normalizeAppPath to
// strip that suffix and find the entry matching the user-facing route.
let extraOptions = undefined;
for (const [name] of currentEntrypoints.app){
if ((0, _apppaths.normalizeAppPath)(name) === page) {
extraOptions = {
// Synthesize a definition so ensurePage bypasses findPagePathData.
// Only page and bundlePath are used from the definition:
// - page: the originalName used as the route key for currentEntrypoints lookup
// - bundlePath: must start with "app/" to set isInsideAppDir=true
definition: {
page: name,
bundlePath: `app${name}`,
filename: ''
}
};
break;
}
}
const ensureOpts = {
page,
// Compile both server and client bundles, matching what happens
// on a real page navigation. Client-only compilation isn't a
// meaningful MCP use case so we don't expose it as a knob.
clientOnly: false,
// Skip wiring HMR subscriptions: there is no client to receive
// updates for routes compiled this way, and these subscriptions
// are never unsubscribed (see TODOs in handleRouteType).
subscribeToChanges: false,
...extraOptions
};
// Snapshot the current issue maps before compilation so we can
// identify which entry keys were added or updated by this call.
// processIssues always creates a new Map() reference, so identity
// comparison detects changes even for re-compilations.
const snapshotBefore = new Map(currentEntryIssues);
// For app-page routes, processIssues is called with throwIssue=true,
// meaning it throws ModuleBuildError when there are compile errors—but
// it still writes the issues into currentEntryIssues before throwing.
// Catch ModuleBuildError so we can read those issues and return them
// as structured output rather than propagating the throw.
let moduleBuildError;
try {
await hotReloader.ensurePage(ensureOpts);
} catch (err) {
if (err instanceof _utils2.ModuleBuildError) {
moduleBuildError = err;
} else {
throw err;
}
}
const rawIssues = [];
for (const [key, issueMap] of currentEntryIssues){
if (snapshotBefore.get(key) !== issueMap) {
rawIssues.push(...issueMap.values());
}
}
// If ensurePage threw ModuleBuildError but we found no new issues in
// the map (shouldn't happen, but be safe), re-surface the original
// error so its message and stack are preserved.
if (moduleBuildError && rawIssues.length === 0) {
throw moduleBuildError;
}
return {
routeSpecifier: page,
issues: (0, _formatcompilationissues.formatCompilationIssues)(rawIssues)
};
}
})
] : []
];
(0, _formaterrors.setStackFrameResolver)(async (request)=>{
return (0, _middlewareturbopack.getOriginalStackFrames)({
project,
projectPath,
isServer: request.isServer,
isEdgeServer: request.isEdgeServer,
isAppDirectory: request.isAppDirectory,
frames: request.frames
});
});
let versionInfoCached;
// This fetch, even though not awaited, is not kicked off eagerly because the first `fetch()` in
// Node.js adds roughly 20ms main-thread blocking to load the SSL certificate cache
// We don't want that blocking time to be in the hot path for the `ready in` logging.
// Instead, the fetch is kicked off lazily when the first `getVersionInfoCached()` is called.
const getVersionInfoCached = ()=>{
if (!versionInfoCached) {
versionInfoCached = (0, _hotreloadersharedutils.getVersionInfo)();
}
return versionInfoCached;
};
let devtoolsFrontendUrl;
const inspectorURLRaw = _inspector.url();
if (inspectorURLRaw !== undefined) {
const inspectorURL =