Initial commit

This commit is contained in:
bilulib
2025-04-13 00:18:57 +02:00
parent cff009bb7c
commit d894249e61
18301 changed files with 2905442 additions and 3845 deletions

View File

@@ -0,0 +1,25 @@
import { middlewareResponse } from '../../client/components/react-dev-overlay/server/middleware-response';
import * as Log from '../../build/output/log';
import { devIndicatorServerState } from './dev-indicator-server-state';
const DISABLE_DEV_INDICATOR_PREFIX = '/__nextjs_disable_dev_indicator';
const COOLDOWN_TIME_MS = process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS ? parseInt(process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS) : 1000 * 60 * 60 * 24;
export function getDisableDevIndicatorMiddleware() {
return async function disableDevIndicatorMiddleware(req, res, next) {
try {
const { pathname } = new URL(`http://n${req.url}`);
if (!pathname.startsWith(DISABLE_DEV_INDICATOR_PREFIX)) {
return next();
}
if (req.method !== 'POST') {
return middlewareResponse.methodNotAllowed(res);
}
devIndicatorServerState.disabledUntil = Date.now() + COOLDOWN_TIME_MS;
return middlewareResponse.noContent(res);
} catch (err) {
Log.error('Failed to disable the dev indicator:', err instanceof Error ? err.message : err);
return middlewareResponse.internalServerError(res);
}
};
}
//# sourceMappingURL=dev-indicator-middleware.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/dev/dev-indicator-middleware.ts"],"sourcesContent":["import type { ServerResponse, IncomingMessage } from 'http'\nimport { middlewareResponse } from '../../client/components/react-dev-overlay/server/middleware-response'\nimport * as Log from '../../build/output/log'\nimport { devIndicatorServerState } from './dev-indicator-server-state'\n\nconst DISABLE_DEV_INDICATOR_PREFIX = '/__nextjs_disable_dev_indicator'\n\nconst COOLDOWN_TIME_MS = process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS\n ? parseInt(process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS)\n : // 1 day from now\n 1000 * 60 * 60 * 24\n\nexport function getDisableDevIndicatorMiddleware() {\n return async function disableDevIndicatorMiddleware(\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void\n ): Promise<void> {\n try {\n const { pathname } = new URL(`http://n${req.url}`)\n\n if (!pathname.startsWith(DISABLE_DEV_INDICATOR_PREFIX)) {\n return next()\n }\n\n if (req.method !== 'POST') {\n return middlewareResponse.methodNotAllowed(res)\n }\n\n devIndicatorServerState.disabledUntil = Date.now() + COOLDOWN_TIME_MS\n\n return middlewareResponse.noContent(res)\n } catch (err) {\n Log.error(\n 'Failed to disable the dev indicator:',\n err instanceof Error ? err.message : err\n )\n return middlewareResponse.internalServerError(res)\n }\n }\n}\n"],"names":["middlewareResponse","Log","devIndicatorServerState","DISABLE_DEV_INDICATOR_PREFIX","COOLDOWN_TIME_MS","process","env","__NEXT_DEV_INDICATOR_COOLDOWN_MS","parseInt","getDisableDevIndicatorMiddleware","disableDevIndicatorMiddleware","req","res","next","pathname","URL","url","startsWith","method","methodNotAllowed","disabledUntil","Date","now","noContent","err","error","Error","message","internalServerError"],"mappings":"AACA,SAASA,kBAAkB,QAAQ,uEAAsE;AACzG,YAAYC,SAAS,yBAAwB;AAC7C,SAASC,uBAAuB,QAAQ,+BAA8B;AAEtE,MAAMC,+BAA+B;AAErC,MAAMC,mBAAmBC,QAAQC,GAAG,CAACC,gCAAgC,GACjEC,SAASH,QAAQC,GAAG,CAACC,gCAAgC,IAErD,OAAO,KAAK,KAAK;AAErB,OAAO,SAASE;IACd,OAAO,eAAeC,8BACpBC,GAAoB,EACpBC,GAAmB,EACnBC,IAAgB;QAEhB,IAAI;YACF,MAAM,EAAEC,QAAQ,EAAE,GAAG,IAAIC,IAAI,CAAC,QAAQ,EAAEJ,IAAIK,GAAG,EAAE;YAEjD,IAAI,CAACF,SAASG,UAAU,CAACd,+BAA+B;gBACtD,OAAOU;YACT;YAEA,IAAIF,IAAIO,MAAM,KAAK,QAAQ;gBACzB,OAAOlB,mBAAmBmB,gBAAgB,CAACP;YAC7C;YAEAV,wBAAwBkB,aAAa,GAAGC,KAAKC,GAAG,KAAKlB;YAErD,OAAOJ,mBAAmBuB,SAAS,CAACX;QACtC,EAAE,OAAOY,KAAK;YACZvB,IAAIwB,KAAK,CACP,wCACAD,eAAeE,QAAQF,IAAIG,OAAO,GAAGH;YAEvC,OAAOxB,mBAAmB4B,mBAAmB,CAAChB;QAChD;IACF;AACF"}

View File

@@ -0,0 +1,5 @@
export const devIndicatorServerState = {
disabledUntil: 0
};
//# sourceMappingURL=dev-indicator-server-state.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/dev/dev-indicator-server-state.ts"],"sourcesContent":["export type DevIndicatorServerState = typeof devIndicatorServerState\n\nexport const devIndicatorServerState = {\n disabledUntil: 0,\n}\n"],"names":["devIndicatorServerState","disabledUntil"],"mappings":"AAEA,OAAO,MAAMA,0BAA0B;IACrCC,eAAe;AACjB,EAAC"}

View File

@@ -0,0 +1,210 @@
// Based on https://github.com/webpack-contrib/webpack-hot-middleware/blob/9708d781ae0e46179cf8ea1a94719de4679aaf53/middleware.js
// Included License below
// Copyright JS Foundation and other contributors
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// 'Software'), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import { isMiddlewareFilename } from '../../build/utils';
import { HMR_ACTIONS_SENT_TO_BROWSER } from './hot-reloader-types';
import { devIndicatorServerState } from './dev-indicator-server-state';
function isMiddlewareStats(stats) {
for (const key of stats.compilation.entrypoints.keys()){
if (isMiddlewareFilename(key)) {
return true;
}
}
return false;
}
function statsToJson(stats) {
if (!stats) return {};
return stats.toJson({
all: false,
errors: true,
hash: true,
warnings: true
});
}
function getStatsForSyncEvent(clientStats, serverStats) {
if (!clientStats) return serverStats == null ? void 0 : serverStats.stats;
if (!serverStats) return clientStats == null ? void 0 : clientStats.stats;
// Prefer the server compiler stats if it has errors.
// Otherwise we may end up in a state where the client compilation is the latest but without errors.
// This causes the error overlay to not display the build error.
if (serverStats.stats.hasErrors()) {
return serverStats.stats;
}
// Return the latest stats
return serverStats.ts > clientStats.ts ? serverStats.stats : clientStats.stats;
}
class EventStream {
constructor(){
this.clients = new Set();
}
close() {
for (const wsClient of this.clients){
// it's okay to not cleanly close these websocket connections, this is dev
wsClient.terminate();
}
this.clients.clear();
}
handler(client) {
this.clients.add(client);
client.addEventListener('close', ()=>{
this.clients.delete(client);
});
}
publish(payload) {
for (const wsClient of this.clients){
wsClient.send(JSON.stringify(payload));
}
}
}
export class WebpackHotMiddleware {
constructor(compilers, versionInfo, devtoolsFrontendUrl){
this.onClientInvalid = ()=>{
var _this_serverLatestStats;
if (this.closed || ((_this_serverLatestStats = this.serverLatestStats) == null ? void 0 : _this_serverLatestStats.stats.hasErrors())) return;
this.publish({
action: HMR_ACTIONS_SENT_TO_BROWSER.BUILDING
});
};
this.onClientDone = (statsResult)=>{
var _this_serverLatestStats;
this.clientLatestStats = {
ts: Date.now(),
stats: statsResult
};
if (this.closed || ((_this_serverLatestStats = this.serverLatestStats) == null ? void 0 : _this_serverLatestStats.stats.hasErrors())) return;
this.publishStats(statsResult);
};
this.onServerInvalid = ()=>{
var _this_serverLatestStats, _this_clientLatestStats;
if (!((_this_serverLatestStats = this.serverLatestStats) == null ? void 0 : _this_serverLatestStats.stats.hasErrors())) return;
this.serverLatestStats = null;
if ((_this_clientLatestStats = this.clientLatestStats) == null ? void 0 : _this_clientLatestStats.stats) {
this.publishStats(this.clientLatestStats.stats);
}
};
this.onServerDone = (statsResult)=>{
if (this.closed) return;
if (statsResult.hasErrors()) {
this.serverLatestStats = {
ts: Date.now(),
stats: statsResult
};
this.publishStats(statsResult);
}
};
this.onEdgeServerInvalid = ()=>{
var _this_middlewareLatestStats, _this_clientLatestStats;
if (!((_this_middlewareLatestStats = this.middlewareLatestStats) == null ? void 0 : _this_middlewareLatestStats.stats.hasErrors())) return;
this.middlewareLatestStats = null;
if ((_this_clientLatestStats = this.clientLatestStats) == null ? void 0 : _this_clientLatestStats.stats) {
this.publishStats(this.clientLatestStats.stats);
}
};
this.onEdgeServerDone = (statsResult)=>{
if (!isMiddlewareStats(statsResult)) {
this.onServerInvalid();
this.onServerDone(statsResult);
return;
}
if (statsResult.hasErrors()) {
this.middlewareLatestStats = {
ts: Date.now(),
stats: statsResult
};
this.publishStats(statsResult);
}
};
/**
* To sync we use the most recent stats but also we append middleware
* errors. This is because it is possible that middleware fails to compile
* and we still want to show the client overlay with the error while
* the error page should be rendered just fine.
*/ this.onHMR = (client)=>{
if (this.closed) return;
this.eventStream.handler(client);
const syncStats = getStatsForSyncEvent(this.clientLatestStats, this.serverLatestStats);
if (syncStats) {
var _this_middlewareLatestStats;
const stats = statsToJson(syncStats);
const middlewareStats = statsToJson((_this_middlewareLatestStats = this.middlewareLatestStats) == null ? void 0 : _this_middlewareLatestStats.stats);
if (devIndicatorServerState.disabledUntil < Date.now()) {
devIndicatorServerState.disabledUntil = 0;
}
this.publish({
action: HMR_ACTIONS_SENT_TO_BROWSER.SYNC,
hash: stats.hash,
errors: [
...stats.errors || [],
...middlewareStats.errors || []
],
warnings: [
...stats.warnings || [],
...middlewareStats.warnings || []
],
versionInfo: this.versionInfo,
debug: {
devtoolsFrontendUrl: this.devtoolsFrontendUrl
},
devIndicator: devIndicatorServerState
});
}
};
this.publishStats = (statsResult)=>{
const stats = statsResult.toJson({
all: false,
hash: true,
warnings: true,
errors: true,
moduleTrace: true
});
this.publish({
action: HMR_ACTIONS_SENT_TO_BROWSER.BUILT,
hash: stats.hash,
warnings: stats.warnings || [],
errors: stats.errors || []
});
};
this.publish = (payload)=>{
if (this.closed) return;
this.eventStream.publish(payload);
};
this.close = ()=>{
if (this.closed) return;
// Can't remove compiler plugins, so we just set a flag and noop if closed
// https://github.com/webpack/tapable/issues/32#issuecomment-350644466
this.closed = true;
this.eventStream.close();
};
this.eventStream = new EventStream();
this.clientLatestStats = null;
this.middlewareLatestStats = null;
this.serverLatestStats = null;
this.closed = false;
this.versionInfo = versionInfo;
this.devtoolsFrontendUrl = devtoolsFrontendUrl;
compilers[0].hooks.invalid.tap('webpack-hot-middleware', this.onClientInvalid);
compilers[0].hooks.done.tap('webpack-hot-middleware', this.onClientDone);
compilers[1].hooks.invalid.tap('webpack-hot-middleware', this.onServerInvalid);
compilers[1].hooks.done.tap('webpack-hot-middleware', this.onServerDone);
compilers[2].hooks.done.tap('webpack-hot-middleware', this.onEdgeServerDone);
compilers[2].hooks.invalid.tap('webpack-hot-middleware', this.onEdgeServerInvalid);
}
}
//# sourceMappingURL=hot-middleware.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,893 @@
import { mkdir, writeFile } from 'fs/promises';
import { join, extname } from 'path';
import { pathToFileURL } from 'url';
import ws from 'next/dist/compiled/ws';
import { store as consoleStore } from '../../build/output/store';
import { HMR_ACTIONS_SENT_TO_BROWSER } from './hot-reloader-types';
import { createDefineEnv } from '../../build/swc';
import * as Log from '../../build/output/log';
import { getVersionInfo, matchNextPageBundleRequest } from './hot-reloader-webpack';
import { BLOCKED_PAGES } from '../../shared/lib/constants';
import { getOverlayMiddleware, getSourceMapMiddleware } from '../../client/components/react-dev-overlay/server/middleware-turbopack';
import { PageNotFoundError } from '../../shared/lib/utils';
import { debounce } from '../utils';
import { deleteCache, deleteFromRequireCache } from './require-cache';
import { clearAllModuleContexts, clearModuleContext } from '../lib/render-server';
import { denormalizePagePath } from '../../shared/lib/page-path/denormalize-page-path';
import { trace } from '../../trace';
import { AssetMapper, handleEntrypoints, handlePagesErrorRoute, handleRouteType, hasEntrypointForKey, msToNs, processTopLevelIssues, printNonFatalIssue, normalizedPageToTurbopackStructureRoute } from './turbopack-utils';
import { propagateServerField } from '../lib/router-utils/setup-dev-bundler';
import { TurbopackManifestLoader } from '../../shared/lib/turbopack/manifest-loader';
import { findPagePathData } from './on-demand-entry-handler';
import { getEntryKey, splitEntryKey } from '../../shared/lib/turbopack/entry-key';
import { FAST_REFRESH_RUNTIME_RELOAD } from './messages';
import { generateEncryptionKeyBase64 } from '../app-render/encryption-utils-server';
import { isAppPageRouteDefinition } from '../route-definitions/app-page-route-definition';
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths';
import { getNodeDebugType } from '../lib/utils';
import { isMetadataRouteFile } from '../../lib/metadata/is-metadata-route';
import { setBundlerFindSourceMapImplementation } from '../patch-error-inspect';
import { getNextErrorFeedbackMiddleware } from '../../client/components/react-dev-overlay/server/get-next-error-feedback-middleware';
import { formatIssue, getTurbopackJsConfig, isPersistentCachingEnabled, isWellKnownError, processIssues, renderStyledStringToErrorAnsi } from '../../shared/lib/turbopack/utils';
import { getDevOverlayFontMiddleware } from '../../client/components/react-dev-overlay/font/get-dev-overlay-font-middleware';
import { devIndicatorServerState } from './dev-indicator-server-state';
import { getDisableDevIndicatorMiddleware } from './dev-indicator-middleware';
// import { getSupportedBrowsers } from '../../build/utils'
const wsServer = new ws.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());
/**
* Replaces turbopack:///[project] with the specified project in the `source` field.
*/ function rewriteTurbopackSources(projectRoot, sourceMap) {
if ('sections' in sourceMap) {
for (const section of sourceMap.sections){
rewriteTurbopackSources(projectRoot, section.map);
}
} else {
for(let i = 0; i < sourceMap.sources.length; i++){
sourceMap.sources[i] = pathToFileURL(join(projectRoot, sourceMap.sources[i].replace(/turbopack:\/\/\/\[project\]/, ''))).toString();
}
}
}
function getSourceMapFromTurbopack(project, projectRoot, sourceURL) {
let sourceMapJson = null;
try {
sourceMapJson = project.getSourceMapSync(sourceURL);
} catch (err) {}
if (sourceMapJson === null) {
return undefined;
} else {
const payload = JSON.parse(sourceMapJson);
// The sourcemap from Turbopack is not yet written to disk so its `sources`
// are not absolute paths yet. We need to rewrite them to be absolute paths.
rewriteTurbopackSources(projectRoot, payload);
return payload;
}
}
export async function createHotReloaderTurbopack(opts, serverFields, distDir, resetFetch) {
var _opts_nextConfig_turbopack, _nextConfig_watchOptions, _opts_nextConfig_experimental;
const dev = true;
const buildId = 'development';
const { nextConfig, dir: projectPath } = opts;
const { loadBindings } = require('../../build/swc');
let bindings = await loadBindings();
// 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 = trace('hot-reloader', undefined, {
version: "15.3.0"
});
// Ensure the hotReloaderSpan is flushed immediately as it's the parentSpan for all processing
// of the current `next dev` invocation.
hotReloaderSpan.stop();
const encryptionKey = await generateEncryptionKeyBase64({
isBuild: false,
distDir
});
// TODO: Implement
let clientRouterFilters;
if (nextConfig.experimental.clientRouterFilter) {
// TODO this need to be set correctly for persistent caching to work
}
// const supportedBrowsers = await getSupportedBrowsers(dir, dev)
const supportedBrowsers = [
'last 1 Chrome versions, last 1 Firefox versions, last 1 Safari versions, last 1 Edge versions'
];
const project = await bindings.turbo.createProject({
projectPath: projectPath,
rootPath: ((_opts_nextConfig_turbopack = opts.nextConfig.turbopack) == null ? void 0 : _opts_nextConfig_turbopack.root) || opts.nextConfig.outputFileTracingRoot || projectPath,
distDir,
nextConfig: opts.nextConfig,
jsConfig: await getTurbopackJsConfig(projectPath, nextConfig),
watch: {
enable: dev,
pollIntervalMs: (_nextConfig_watchOptions = nextConfig.watchOptions) == null ? void 0 : _nextConfig_watchOptions.pollIntervalMs
},
dev,
env: process.env,
defineEnv: createDefineEnv({
isTurbopack: true,
clientRouterFilters,
config: nextConfig,
dev,
distDir,
fetchCacheKeyPrefix: opts.nextConfig.experimental.fetchCacheKeyPrefix,
hasRewrites,
// TODO: Implement
middlewareMatchers: undefined
}),
buildId,
encryptionKey,
previewProps: opts.fsChecker.prerenderManifest.preview,
browserslistQuery: supportedBrowsers.join(', '),
noMangling: false
}, {
persistentCaching: isPersistentCachingEnabled(opts.nextConfig),
memoryLimit: (_opts_nextConfig_experimental = opts.nextConfig.experimental) == null ? void 0 : _opts_nextConfig_experimental.turbopackMemoryLimit
});
setBundlerFindSourceMapImplementation(getSourceMapFromTurbopack.bind(null, project, projectPath));
opts.onDevServerCleanup == null ? void 0 : opts.onDevServerCleanup.call(opts, async ()=>{
setBundlerFindSourceMapImplementation(()=>undefined);
await project.onExit();
});
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 TurbopackManifestLoader({
buildId,
distDir,
encryptionKey
});
// 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 AssetMapper();
function clearRequireCache(key, writtenEndpoint, { force } = {}) {
if (force) {
for (const { path, contentHash } of writtenEndpoint.serverPaths){
serverPathState.set(path, contentHash);
}
} else {
// Figure out if the server files have changed
let hasChange = false;
for (const { path, contentHash } of writtenEndpoint.serverPaths){
// We ignore source maps
if (path.endsWith('.map')) continue;
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(key, contentHash);
serverPathState.set(path, contentHash);
} else {
if (!localHash) {
serverPathState.set(key, contentHash);
}
if (!globalHash) {
serverPathState.set(path, contentHash);
}
}
}
if (!hasChange) {
return false;
}
}
resetFetch();
const hasAppPaths = writtenEndpoint.serverPaths.some(({ path: p })=>p.startsWith('server/app'));
if (hasAppPaths) {
deleteFromRequireCache(require.resolve('next/dist/compiled/next-server/app-page-turbo.runtime.dev.js'));
deleteFromRequireCache(require.resolve('next/dist/compiled/next-server/app-page-turbo-experimental.runtime.dev.js'));
}
const serverPaths = writtenEndpoint.serverPaths.map(({ path: p })=>join(distDir, p));
for (const file of serverPaths){
clearModuleContext(file);
deleteCache(file);
}
return true;
}
const buildingIds = new Set();
const startBuilding = (id, requestUrl, forceRebuild)=>{
if (!forceRebuild && readyIds.has(id)) {
return ()=>{};
}
if (buildingIds.size === 0) {
consoleStore.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;
consoleStore.setState({
loading: false
}, true);
}
};
};
let hmrEventHappened = false;
let hmrHash = 0;
const clients = new Set();
const clientStates = new WeakMap();
function sendToClient(client, payload) {
client.send(JSON.stringify(payload));
}
function sendEnqueuedMessages() {
for (const [, issueMap] of currentEntryIssues){
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 client of clients){
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 payload of state.hmrPayloads.values()){
sendToClient(client, payload);
}
state.hmrPayloads.clear();
if (state.turbopackUpdates.length > 0) {
sendToClient(client, {
action: HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_MESSAGE,
data: state.turbopackUpdates
});
state.turbopackUpdates.length = 0;
}
}
}
const sendEnqueuedMessagesDebounce = debounce(sendEnqueuedMessages, 2);
const sendHmr = (id, payload)=>{
for (const client of clients){
var _clientStates_get;
(_clientStates_get = clientStates.get(client)) == null ? void 0 : _clientStates_get.hmrPayloads.set(id, payload);
}
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 = [];
for (const client of clients){
var _clientStates_get;
(_clientStates_get = clientStates.get(client)) == null ? void 0 : _clientStates_get.turbopackUpdates.push(payload);
}
hmrEventHappened = true;
sendEnqueuedMessagesDebounce();
}
async function subscribeToChanges(key, includeIssues, endpoint, makePayload, onError) {
if (changeSubscriptions.has(key)) {
return;
}
const { side } = splitEntryKey(key);
const changedPromise = endpoint[`${side}Changed`](includeIssues);
changeSubscriptions.set(key, changedPromise);
try {
const changed = await changedPromise;
for await (const change of changed){
processIssues(currentEntryIssues, key, change, false, true);
// TODO: Get an actual content hash from Turbopack.
const payload = await makePayload(change, String(++hmrHash));
if (payload) {
sendHmr(key, payload);
}
}
} 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 unsubscribeFromChanges(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 subscribeToHmrEvents(client, id) {
const key = getEntryKey('assets', 'client', id);
if (!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);
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){
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 reloadAction = {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in HMR event subscription for ${id}: ${e}`
};
sendToClient(client, reloadAction);
client.close();
return;
}
}
function unsubscribeFromHmrEvents(client, id) {
const state = clientStates.get(client);
if (!state) {
return;
}
const subscription = state.subscriptions.get(id);
subscription == null ? void 0 : subscription.return();
const key = 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);
}
processTopLevelIssues(currentTopLevelIssues, entrypoints);
await handleEntrypoints({
entrypoints,
currentEntrypoints,
currentEntryIssues,
manifestLoader,
devRewrites: opts.fsChecker.rewrites,
productionRewrites: undefined,
logErrors: true,
dev: {
assetMapper,
changeSubscriptions,
clients,
clientStates,
serverFields,
hooks: {
handleWrittenEndpoint: (id, result, forceDeleteCache)=>{
currentWrittenEntrypoints.set(id, result);
return clearRequireCache(id, result, {
force: forceDeleteCache
});
},
propagateServerField: propagateServerField.bind(null, opts),
sendHmr,
startBuilding,
subscribeToChanges,
unsubscribeFromChanges,
unsubscribeFromHmrEvents
}
}
});
currentEntriesHandlingResolve();
currentEntriesHandlingResolve = undefined;
}
}
await mkdir(join(distDir, 'server'), {
recursive: true
});
await mkdir(join(distDir, 'static', buildId), {
recursive: true
});
await writeFile(join(distDir, 'package.json'), JSON.stringify({
type: 'commonjs'
}, null, 2));
const middlewares = [
getOverlayMiddleware(project, projectPath),
getSourceMapMiddleware(project),
getNextErrorFeedbackMiddleware(opts.telemetry),
getDevOverlayFontMiddleware(),
getDisableDevIndicatorMiddleware()
];
const versionInfoPromise = getVersionInfo();
let devtoolsFrontendUrl;
const nodeDebugType = getNodeDebugType();
if (nodeDebugType) {
const debugPort = process.debugPort;
let debugInfo;
try {
// It requires to use 127.0.0.1 instead of localhost for server-side fetching.
const debugInfoList = await fetch(`http://127.0.0.1:${debugPort}/json/list`).then((res)=>res.json());
debugInfo = debugInfoList[0];
} catch {}
if (debugInfo) {
devtoolsFrontendUrl = debugInfo.devtoolsFrontendUrl;
}
}
const hotReloader = {
turbopackProject: project,
activeWebpackConfigs: undefined,
serverStats: null,
edgeServerStats: null,
async run (req, res, _parsedUrl) {
var _req_url;
// intercept page chunks request and ensure them with turbopack
if ((_req_url = req.url) == null ? void 0 : _req_url.startsWith('/_next/static/chunks/pages/')) {
const params = matchNextPageBundleRequest(req.url);
if (params) {
const decodedPagePath = `/${params.path.map((param)=>decodeURIComponent(param)).join('/')}`;
const denormalizedPagePath = denormalizePagePath(decodedPagePath);
await hotReloader.ensurePage({
page: denormalizedPagePath,
clientOnly: false,
definition: undefined,
url: req.url
}).catch(console.error);
}
}
for (const middleware of middlewares){
let calledNext = false;
await middleware(req, res, ()=>{
calledNext = true;
});
if (!calledNext) {
return {
finished: true
};
}
}
// Request was not finished.
return {
finished: undefined
};
},
// TODO: Figure out if socket type can match the NextJsHotReloaderInterface
onHMR (req, socket, head, onUpgrade) {
wsServer.handleUpgrade(req, socket, head, (client)=>{
onUpgrade(client);
const clientIssues = new Map();
const subscriptions = new Map();
clients.add(client);
clientStates.set(client, {
clientIssues,
hmrPayloads: new Map(),
turbopackUpdates: [],
subscriptions
});
client.on('close', ()=>{
// Remove active subscriptions
for (const subscription of subscriptions.values()){
subscription.return == null ? void 0 : subscription.return.call(subscription);
}
clientStates.delete(client);
clients.delete(client);
});
client.addEventListener('message', ({ data })=>{
const parsedData = JSON.parse(typeof data !== 'string' ? data.toString() : data);
// Next.js messages
switch(parsedData.event){
case 'span-end':
{
hotReloaderSpan.manualTraceChild(parsedData.spanName, msToNs(parsedData.startTime), msToNs(parsedData.endTime), parsedData.attributes);
break;
}
case 'client-hmr-latency':
hotReloaderSpan.manualTraceChild(parsedData.event, msToNs(parsedData.startTime), msToNs(parsedData.endTime), {
updatedModules: parsedData.updatedModules,
page: parsedData.page,
isPageHidden: parsedData.isPageHidden
});
break;
case 'client-error':
case 'client-warning':
case 'client-success':
case 'server-component-reload-page':
case 'client-reload-page':
case 'client-removed-page':
case 'client-full-reload':
const { hadRuntimeError, dependencyChain } = parsedData;
if (hadRuntimeError) {
Log.warn(FAST_REFRESH_RUNTIME_RELOAD);
}
if (Array.isArray(dependencyChain) && typeof dependencyChain[0] === 'string') {
const cleanedModulePath = dependencyChain[0].replace(/^\[project\]/, '.').replace(/ \[.*\] \(.*\)$/, '');
Log.warn(`Fast Refresh had to perform a full reload when ${cleanedModulePath} changed. Read more: https://nextjs.org/docs/messages/fast-refresh-reload`);
}
break;
case 'client-added-page':
break;
default:
// Might be a Turbopack message...
if (!parsedData.type) {
throw Object.defineProperty(new Error(`unrecognized HMR message "${data}"`), "__NEXT_ERROR_CODE", {
value: "E155",
enumerable: false,
configurable: true
});
}
}
// Turbopack messages
switch(parsedData.type){
case 'turbopack-subscribe':
subscribeToHmrEvents(client, parsedData.path);
break;
case 'turbopack-unsubscribe':
unsubscribeFromHmrEvents(client, parsedData.path);
break;
default:
if (!parsedData.event) {
throw Object.defineProperty(new Error(`unrecognized Turbopack HMR message "${data}"`), "__NEXT_ERROR_CODE", {
value: "E492",
enumerable: false,
configurable: true
});
}
}
});
const turbopackConnected = {
action: HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_CONNECTED,
data: {
sessionId
}
};
sendToClient(client, turbopackConnected);
const errors = [];
for (const entryIssues of currentEntryIssues.values()){
for (const issue of entryIssues.values()){
if (issue.severity !== 'warning') {
errors.push({
message: formatIssue(issue)
});
} else {
printNonFatalIssue(issue);
}
}
}
if (devIndicatorServerState.disabledUntil < Date.now()) {
devIndicatorServerState.disabledUntil = 0;
}
;
(async function() {
const versionInfo = await versionInfoPromise;
const sync = {
action: HMR_ACTIONS_SENT_TO_BROWSER.SYNC,
errors,
warnings: [],
hash: '',
versionInfo,
debug: {
devtoolsFrontendUrl
},
devIndicator: devIndicatorServerState
};
sendToClient(client, sync);
})();
});
},
send (action) {
const payload = JSON.stringify(action);
for (const client of clients){
client.send(payload);
}
},
setHmrServerError (_error) {
// Not implemented yet.
},
clearHmrServerError () {
// Not implemented yet.
},
async start () {},
async getCompilationErrors (page) {
const appEntryKey = getEntryKey('app', 'server', page);
const pagesEntryKey = getEntryKey('pages', 'server', page);
const topLevelIssues = currentTopLevelIssues.values();
const thisEntryIssues = currentEntryIssues.get(appEntryKey) ?? currentEntryIssues.get(pagesEntryKey);
if (thisEntryIssues !== undefined && thisEntryIssues.size > 0) {
// If there is an error related to the requesting page we display it instead of the first error
return [
...topLevelIssues,
...thisEntryIssues.values()
].map((issue)=>{
const formattedIssue = formatIssue(issue);
if (issue.severity === 'warning') {
printNonFatalIssue(issue);
return null;
} else if (isWellKnownError(issue)) {
Log.error(formattedIssue);
}
return Object.defineProperty(new Error(formattedIssue), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}).filter((error)=>error !== null);
}
// Otherwise, return all errors across pages
const errors = [];
for (const issue of topLevelIssues){
if (issue.severity !== 'warning') {
errors.push(Object.defineProperty(new Error(formatIssue(issue)), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
}));
}
}
for (const entryIssues of currentEntryIssues.values()){
for (const issue of entryIssues.values()){
if (issue.severity !== 'warning') {
const message = formatIssue(issue);
errors.push(Object.defineProperty(new Error(message), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
}));
} else {
printNonFatalIssue(issue);
}
}
}
return errors;
},
async invalidate ({ // .env files or tsconfig/jsconfig change
reloadAfterInvalidation }) {
if (reloadAfterInvalidation) {
for (const [key, entrypoint] of currentWrittenEntrypoints){
clearRequireCache(key, entrypoint, {
force: true
});
}
await clearAllModuleContexts();
this.send({
action: HMR_ACTIONS_SENT_TO_BROWSER.SERVER_COMPONENT_CHANGES,
hash: String(++hmrHash)
});
}
},
async buildFallbackError () {
// Not implemented yet.
},
async ensurePage ({ page: inputPage, // Unused parameters
// clientOnly,
appPaths, definition, isApp, url: requestUrl }) {
// When there is no route definition this is an internal file not a route the user added.
// Middleware and instrumentation are handled in turbpack-utils.ts handleEntrypoints instead.
if (!definition) {
if (inputPage === '/middleware') return;
if (inputPage === '/src/middleware') return;
if (inputPage === '/instrumentation') return;
if (inputPage === '/src/instrumentation') return;
}
return hotReloaderSpan.traceChild('ensure-page', {
inputPage
}).traceAsyncFn(async ()=>{
if (BLOCKED_PAGES.includes(inputPage) && inputPage !== '/_error') {
return;
}
await currentEntriesHandling;
// TODO We shouldn't look into the filesystem again. This should use the information from entrypoints
let routeDef = definition ?? await findPagePathData(projectPath, inputPage, nextConfig.pageExtensions, opts.pagesDir, opts.appDir);
// If the route is actually an app page route, then we should have access
// to the app route definition, and therefore, the appPaths from it.
if (!appPaths && definition && isAppPageRouteDefinition(definition)) {
appPaths = definition.appPaths;
}
let page = routeDef.page;
if (appPaths) {
const normalizedPage = normalizeAppPath(page);
// filter out paths that are not exact matches (e.g. catchall)
const matchingAppPaths = appPaths.filter((path)=>normalizeAppPath(path) === normalizedPage);
// the last item in the array is the root page, if there are parallel routes
page = matchingAppPaths[matchingAppPaths.length - 1];
}
const pathname = (definition == null ? void 0 : definition.pathname) ?? inputPage;
if (page === '/_error') {
let finishBuilding = startBuilding(pathname, requestUrl, false);
try {
await handlePagesErrorRoute({
currentEntryIssues,
entrypoints: currentEntrypoints,
manifestLoader,
devRewrites: opts.fsChecker.rewrites,
productionRewrites: undefined,
logErrors: true,
hooks: {
subscribeToChanges,
handleWrittenEndpoint: (id, result, forceDeleteCache)=>{
currentWrittenEntrypoints.set(id, result);
assetMapper.setPathsForKey(id, result.clientPaths);
return clearRequireCache(id, result, {
force: forceDeleteCache
});
}
}
});
} finally{
finishBuilding();
}
return;
}
const isInsideAppDir = routeDef.bundlePath.startsWith('app/');
const isEntryMetadataRouteFile = isMetadataRouteFile(routeDef.filename.replace(opts.appDir || '', ''), nextConfig.pageExtensions, true);
const normalizedAppPage = isEntryMetadataRouteFile ? normalizedPageToTurbopackStructureRoute(page, extname(routeDef.filename)) : page;
const route = isInsideAppDir ? currentEntrypoints.app.get(normalizedAppPage) : currentEntrypoints.page.get(page);
if (!route) {
// TODO: why is this entry missing in turbopack?
if (page === '/middleware') return;
if (page === '/src/middleware') return;
if (page === '/instrumentation') return;
if (page === '/src/instrumentation') return;
throw new PageNotFoundError(`route not found ${page}`);
}
// We don't throw on ensureOpts.isApp === true for page-api
// since this can happen when app pages make
// api requests to page API routes.
if (isApp && route.type === 'page') {
throw Object.defineProperty(new Error(`mis-matched route type: isApp && page for ${page}`), "__NEXT_ERROR_CODE", {
value: "E373",
enumerable: false,
configurable: true
});
}
const finishBuilding = startBuilding(pathname, requestUrl, false);
try {
await handleRouteType({
dev,
page,
pathname,
route,
currentEntryIssues,
entrypoints: currentEntrypoints,
manifestLoader,
readyIds,
devRewrites: opts.fsChecker.rewrites,
productionRewrites: undefined,
logErrors: true,
hooks: {
subscribeToChanges,
handleWrittenEndpoint: (id, result, forceDeleteCache)=>{
currentWrittenEntrypoints.set(id, result);
assetMapper.setPathsForKey(id, result.clientPaths);
return clearRequireCache(id, result, {
force: forceDeleteCache
});
}
}
});
} finally{
finishBuilding();
}
});
},
close () {
for (const wsClient of clients){
// it's okay to not cleanly close these websocket connections, this is dev
wsClient.terminate();
}
clients.clear();
}
};
handleEntrypointsSubscription().catch((err)=>{
console.error(err);
process.exit(1);
});
// Write empty manifests
await currentEntriesHandling;
await manifestLoader.writeManifests({
devRewrites: opts.fsChecker.rewrites,
productionRewrites: undefined,
entrypoints: currentEntrypoints
});
async function handleProjectUpdates() {
for await (const updateMessage of project.updateInfoSubscribe(30)){
switch(updateMessage.updateType){
case 'start':
{
hotReloader.send({
action: HMR_ACTIONS_SENT_TO_BROWSER.BUILDING
});
break;
}
case 'end':
{
sendEnqueuedMessages();
function addErrors(errorsMap, issues) {
for (const issueMap of issues.values()){
for (const [key, issue] of issueMap){
if (issue.severity === 'warning') continue;
if (errorsMap.has(key)) continue;
const message = formatIssue(issue);
errorsMap.set(key, {
message,
details: issue.detail ? renderStyledStringToErrorAnsi(issue.detail) : undefined
});
}
}
}
const errors = new Map();
addErrors(errors, currentEntryIssues);
for (const client of clients){
const state = clientStates.get(client);
if (!state) {
continue;
}
const clientErrors = new Map(errors);
addErrors(clientErrors, state.clientIssues);
sendToClient(client, {
action: HMR_ACTIONS_SENT_TO_BROWSER.BUILT,
hash: String(++hmrHash),
errors: [
...clientErrors.values()
],
warnings: []
});
}
if (hmrEventHappened) {
const time = updateMessage.value.duration;
const timeMessage = time > 2000 ? `${Math.round(time / 100) / 10}s` : `${time}ms`;
Log.event(`Compiled in ${timeMessage}`);
hmrEventHappened = false;
}
break;
}
default:
}
}
}
handleProjectUpdates().catch((err)=>{
console.error(err);
process.exit(1);
});
return hotReloader;
}
//# sourceMappingURL=hot-reloader-turbopack.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
export var HMR_ACTIONS_SENT_TO_BROWSER = /*#__PURE__*/ function(HMR_ACTIONS_SENT_TO_BROWSER) {
HMR_ACTIONS_SENT_TO_BROWSER["ADDED_PAGE"] = "addedPage";
HMR_ACTIONS_SENT_TO_BROWSER["REMOVED_PAGE"] = "removedPage";
HMR_ACTIONS_SENT_TO_BROWSER["RELOAD_PAGE"] = "reloadPage";
HMR_ACTIONS_SENT_TO_BROWSER["SERVER_COMPONENT_CHANGES"] = "serverComponentChanges";
HMR_ACTIONS_SENT_TO_BROWSER["MIDDLEWARE_CHANGES"] = "middlewareChanges";
HMR_ACTIONS_SENT_TO_BROWSER["CLIENT_CHANGES"] = "clientChanges";
HMR_ACTIONS_SENT_TO_BROWSER["SERVER_ONLY_CHANGES"] = "serverOnlyChanges";
HMR_ACTIONS_SENT_TO_BROWSER["SYNC"] = "sync";
HMR_ACTIONS_SENT_TO_BROWSER["BUILT"] = "built";
HMR_ACTIONS_SENT_TO_BROWSER["BUILDING"] = "building";
HMR_ACTIONS_SENT_TO_BROWSER["DEV_PAGES_MANIFEST_UPDATE"] = "devPagesManifestUpdate";
HMR_ACTIONS_SENT_TO_BROWSER["TURBOPACK_MESSAGE"] = "turbopack-message";
HMR_ACTIONS_SENT_TO_BROWSER["SERVER_ERROR"] = "serverError";
HMR_ACTIONS_SENT_TO_BROWSER["TURBOPACK_CONNECTED"] = "turbopack-connected";
HMR_ACTIONS_SENT_TO_BROWSER["ISR_MANIFEST"] = "isrManifest";
HMR_ACTIONS_SENT_TO_BROWSER["DEV_INDICATOR"] = "devIndicator";
return HMR_ACTIONS_SENT_TO_BROWSER;
}({});
//# sourceMappingURL=hot-reloader-types.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,90 @@
import { blue, bold, gray, green, red, white, yellow } from '../../lib/picocolors';
import { stripNextRscUnionQuery } from '../../lib/url';
import { getRequestMeta } from '../request-meta';
/**
* Returns true if the incoming request should be ignored for logging.
*/ export function ignoreLoggingIncomingRequests(request, loggingConfig) {
var _loggingConfig_incomingRequests;
// If it's boolean use the boolean value
if (typeof (loggingConfig == null ? void 0 : loggingConfig.incomingRequests) === 'boolean') {
return !loggingConfig.incomingRequests;
}
// Any of the value on the chain is falsy, will not ignore the request.
const ignore = loggingConfig == null ? void 0 : (_loggingConfig_incomingRequests = loggingConfig.incomingRequests) == null ? void 0 : _loggingConfig_incomingRequests.ignore;
// If ignore is not set, don't ignore anything
if (!ignore) {
return false;
}
// If array of RegExp, ignore if any pattern matches
return ignore.some((pattern)=>pattern.test(request.url));
}
export function logRequests(options) {
const { request, response, loggingConfig, requestDurationInMs } = options;
if (!ignoreLoggingIncomingRequests(request, loggingConfig)) {
logIncomingRequests({
request,
requestDurationInMs,
statusCode: response.statusCode
});
}
if (request.fetchMetrics) {
for (const fetchMetric of request.fetchMetrics){
logFetchMetric(fetchMetric, loggingConfig);
}
}
}
function logIncomingRequests(options) {
const { request, requestDurationInMs, statusCode } = options;
const isRSC = getRequestMeta(request, 'isRSCRequest');
const url = isRSC ? stripNextRscUnionQuery(request.url) : request.url;
const statusCodeColor = statusCode < 200 ? white : statusCode < 300 ? green : statusCode < 400 ? blue : statusCode < 500 ? yellow : red;
const coloredStatus = statusCodeColor(statusCode.toString());
return writeLine(`${request.method} ${url} ${coloredStatus} in ${requestDurationInMs}ms`);
}
function logFetchMetric(fetchMetric, loggingConfig) {
var _loggingConfig_fetches;
let { cacheReason, cacheStatus, cacheWarning, end, method, start, status, url } = fetchMetric;
if (cacheStatus === 'hmr' && !(loggingConfig == null ? void 0 : (_loggingConfig_fetches = loggingConfig.fetches) == null ? void 0 : _loggingConfig_fetches.hmrRefreshes)) {
// Cache hits during HMR refreshes are intentionally not logged, unless
// explicitly enabled in the logging config.
return;
}
if (loggingConfig == null ? void 0 : loggingConfig.fetches) {
if (url.length > 48 && !loggingConfig.fetches.fullUrl) {
url = truncateUrl(url);
}
writeLine(white(`${method} ${url} ${status} in ${Math.round(end - start)}ms ${formatCacheStatus(cacheStatus)}`), 1);
if (cacheStatus === 'skip' || cacheStatus === 'miss') {
writeLine(gray(`Cache ${cacheStatus === 'skip' ? 'skipped' : 'missed'} reason: (${white(cacheReason)})`), 2);
}
} else if (cacheWarning) {
// When logging for fetches is not enabled, we still want to print any
// associated warnings, so we print the request first to provide context.
writeLine(white(`${method} ${url}`), 1);
}
if (cacheWarning) {
writeLine(`${yellow(bold('⚠'))} ${white(cacheWarning)}`, 2);
}
}
function writeLine(text, indentationLevel = 0) {
process.stdout.write(` ${'│ '.repeat(indentationLevel)}${text}\n`);
}
function truncate(text, maxLength) {
return maxLength !== undefined && text.length > maxLength ? text.substring(0, maxLength) + '..' : text;
}
function truncateUrl(url) {
const { protocol, host, pathname, search } = new URL(url);
return protocol + '//' + truncate(host, 16) + truncate(pathname, 24) + truncate(search, 16);
}
function formatCacheStatus(cacheStatus) {
switch(cacheStatus){
case 'hmr':
return green('(HMR cache)');
case 'hit':
return green('(cache hit)');
default:
return yellow(`(cache ${cacheStatus})`);
}
}
//# sourceMappingURL=log-requests.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export const FAST_REFRESH_RUNTIME_RELOAD = 'Fast Refresh had to perform a full reload due to a runtime error.';
//# sourceMappingURL=messages.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/dev/messages.ts"],"sourcesContent":["export const FAST_REFRESH_RUNTIME_RELOAD =\n 'Fast Refresh had to perform a full reload due to a runtime error.'\n"],"names":["FAST_REFRESH_RUNTIME_RELOAD"],"mappings":"AAAA,OAAO,MAAMA,8BACX,oEAAmE"}

View File

@@ -0,0 +1,596 @@
import { getRequestMeta } from '../request-meta';
import fs from 'fs';
import { Worker } from 'next/dist/compiled/jest-worker';
import { join as pathJoin } from 'path';
import { ampValidation } from '../../build/output';
import { INSTRUMENTATION_HOOK_FILENAME, PUBLIC_DIR_MIDDLEWARE_CONFLICT } from '../../lib/constants';
import { findPagesDir } from '../../lib/find-pages-dir';
import { PHASE_DEVELOPMENT_SERVER, PAGES_MANIFEST, APP_PATHS_MANIFEST, COMPILER_NAMES } from '../../shared/lib/constants';
import Server, { WrappedBuildError } from '../next-server';
import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-path';
import { pathHasPrefix } from '../../shared/lib/router/utils/path-has-prefix';
import { removePathPrefix } from '../../shared/lib/router/utils/remove-path-prefix';
import { Telemetry } from '../../telemetry/storage';
import { setGlobal, trace } from '../../trace';
import { findPageFile } from '../lib/find-page-file';
import { getFormattedNodeOptionsWithoutInspect } from '../lib/utils';
import { withCoalescedInvoke } from '../../lib/coalesced-function';
import { loadDefaultErrorComponents } from '../load-default-error-components';
import { DecodeError, MiddlewareNotFoundError } from '../../shared/lib/utils';
import * as Log from '../../build/output/log';
import isError, { getProperError } from '../../lib/is-error';
import { isMiddlewareFile } from '../../build/utils';
import { formatServerError } from '../../lib/format-server-error';
import { DevRouteMatcherManager } from '../route-matcher-managers/dev-route-matcher-manager';
import { DevPagesRouteMatcherProvider } from '../route-matcher-providers/dev/dev-pages-route-matcher-provider';
import { DevPagesAPIRouteMatcherProvider } from '../route-matcher-providers/dev/dev-pages-api-route-matcher-provider';
import { DevAppPageRouteMatcherProvider } from '../route-matcher-providers/dev/dev-app-page-route-matcher-provider';
import { DevAppRouteRouteMatcherProvider } from '../route-matcher-providers/dev/dev-app-route-route-matcher-provider';
import { NodeManifestLoader } from '../route-matcher-providers/helpers/manifest-loaders/node-manifest-loader';
import { BatchedFileReader } from '../route-matcher-providers/dev/helpers/file-reader/batched-file-reader';
import { DefaultFileReader } from '../route-matcher-providers/dev/helpers/file-reader/default-file-reader';
import { LRUCache } from '../lib/lru-cache';
import { getMiddlewareRouteMatcher } from '../../shared/lib/router/utils/middleware-route-matcher';
import { DetachedPromise } from '../../lib/detached-promise';
import { isPostpone } from '../lib/router-utils/is-postpone';
import { generateInterceptionRoutesRewrites } from '../../lib/generate-interception-routes-rewrites';
import { buildCustomRoute } from '../../lib/build-custom-route';
import { decorateServerError } from '../../shared/lib/error-source';
import { logRequests } from './log-requests';
import { FallbackMode } from '../../lib/fallback';
// Load ReactDevOverlay only when needed
let ReactDevOverlayImpl;
const ReactDevOverlay = (props)=>{
if (ReactDevOverlayImpl === undefined) {
ReactDevOverlayImpl = require('../../client/components/react-dev-overlay/pages/pages-dev-overlay').PagesDevOverlay;
}
return ReactDevOverlayImpl(props);
};
export default class DevServer extends Server {
getStaticPathsWorker() {
const worker = new Worker(require.resolve('./static-paths-worker'), {
maxRetries: 1,
// For dev server, it's not necessary to spin up too many workers as long as you are not doing a load test.
// This helps reusing the memory a lot.
numWorkers: 1,
enableWorkerThreads: this.nextConfig.experimental.workerThreads,
forkOptions: {
env: {
...process.env,
// discard --inspect/--inspect-brk flags from process.env.NODE_OPTIONS. Otherwise multiple Node.js debuggers
// would be started if user launch Next.js in debugging mode. The number of debuggers is linked to
// the number of workers Next.js tries to launch. The only worker users are interested in debugging
// is the main Next.js one
NODE_OPTIONS: getFormattedNodeOptionsWithoutInspect()
}
}
});
worker.getStdout().pipe(process.stdout);
worker.getStderr().pipe(process.stderr);
return worker;
}
constructor(options){
var _this_nextConfig_experimental_amp, _this_nextConfig_experimental;
try {
// Increase the number of stack frames on the server
Error.stackTraceLimit = 50;
} catch {}
super({
...options,
dev: true
}), /**
* The promise that resolves when the server is ready. When this is unset
* the server is ready.
*/ this.ready = new DetachedPromise();
this.bundlerService = options.bundlerService;
this.startServerSpan = options.startServerSpan ?? trace('start-next-dev-server');
this.renderOpts.dev = true;
this.renderOpts.ErrorDebug = ReactDevOverlay;
this.staticPathsCache = new LRUCache(// 5MB
5 * 1024 * 1024, function length(value) {
var _JSON_stringify;
return ((_JSON_stringify = JSON.stringify(value.staticPaths)) == null ? void 0 : _JSON_stringify.length) ?? 0;
});
this.renderOpts.ampSkipValidation = ((_this_nextConfig_experimental = this.nextConfig.experimental) == null ? void 0 : (_this_nextConfig_experimental_amp = _this_nextConfig_experimental.amp) == null ? void 0 : _this_nextConfig_experimental_amp.skipValidation) ?? false;
this.renderOpts.ampValidator = (html, pathname)=>{
const validatorPath = this.nextConfig.experimental && this.nextConfig.experimental.amp && this.nextConfig.experimental.amp.validator || require.resolve('next/dist/compiled/amphtml-validator/validator_wasm.js');
const AmpHtmlValidator = require('next/dist/compiled/amphtml-validator');
return AmpHtmlValidator.getInstance(validatorPath).then((validator)=>{
const result = validator.validateString(html);
ampValidation(pathname, result.errors.filter((e)=>e.severity === 'ERROR').filter((e)=>this._filterAmpDevelopmentScript(html, e)), result.errors.filter((e)=>e.severity !== 'ERROR'));
});
};
const { pagesDir, appDir } = findPagesDir(this.dir);
this.pagesDir = pagesDir;
this.appDir = appDir;
if (this.nextConfig.experimental.serverComponentsHmrCache) {
this.serverComponentsHmrCache = new LRUCache(this.nextConfig.cacheMaxMemorySize, function length(value) {
return JSON.stringify(value).length;
});
}
}
getServerComponentsHmrCache() {
return this.serverComponentsHmrCache;
}
getRouteMatchers() {
const { pagesDir, appDir } = findPagesDir(this.dir);
const ensurer = {
ensure: async (match, pathname)=>{
await this.ensurePage({
definition: match.definition,
page: match.definition.page,
clientOnly: false,
url: pathname
});
}
};
const matchers = new DevRouteMatcherManager(super.getRouteMatchers(), ensurer, this.dir);
const extensions = this.nextConfig.pageExtensions;
const extensionsExpression = new RegExp(`\\.(?:${extensions.join('|')})$`);
// If the pages directory is available, then configure those matchers.
if (pagesDir) {
const fileReader = new BatchedFileReader(new DefaultFileReader({
// Only allow files that have the correct extensions.
pathnameFilter: (pathname)=>extensionsExpression.test(pathname)
}));
matchers.push(new DevPagesRouteMatcherProvider(pagesDir, extensions, fileReader, this.localeNormalizer));
matchers.push(new DevPagesAPIRouteMatcherProvider(pagesDir, extensions, fileReader, this.localeNormalizer));
}
if (appDir) {
// We create a new file reader for the app directory because we don't want
// to include any folders or files starting with an underscore. This will
// prevent the reader from wasting time reading files that we know we
// don't care about.
const fileReader = new BatchedFileReader(new DefaultFileReader({
// Ignore any directory prefixed with an underscore.
ignorePartFilter: (part)=>part.startsWith('_')
}));
matchers.push(new DevAppPageRouteMatcherProvider(appDir, extensions, fileReader));
matchers.push(new DevAppRouteRouteMatcherProvider(appDir, extensions, fileReader));
}
return matchers;
}
getBuildId() {
return 'development';
}
async prepareImpl() {
var _this_ready;
setGlobal('distDir', this.distDir);
setGlobal('phase', PHASE_DEVELOPMENT_SERVER);
const telemetry = new Telemetry({
distDir: this.distDir
});
await super.prepareImpl();
await this.matchers.reload();
(_this_ready = this.ready) == null ? void 0 : _this_ready.resolve();
this.ready = undefined;
// In dev, this needs to be called after prepare because the build entries won't be known in the constructor
this.interceptionRoutePatterns = this.getinterceptionRoutePatterns();
// This is required by the tracing subsystem.
setGlobal('appDir', this.appDir);
setGlobal('pagesDir', this.pagesDir);
setGlobal('telemetry', telemetry);
process.on('unhandledRejection', (reason)=>{
if (isPostpone(reason)) {
// React postpones that are unhandled might end up logged here but they're
// not really errors. They're just part of rendering.
return;
}
this.logErrorWithOriginalStack(reason, 'unhandledRejection');
});
process.on('uncaughtException', (err)=>{
this.logErrorWithOriginalStack(err, 'uncaughtException');
});
}
async hasPage(pathname) {
let normalizedPath;
try {
normalizedPath = normalizePagePath(pathname);
} catch (err) {
console.error(err);
// if normalizing the page fails it means it isn't valid
// so it doesn't exist so don't throw and return false
// to ensure we return 404 instead of 500
return false;
}
if (isMiddlewareFile(normalizedPath)) {
return findPageFile(this.dir, normalizedPath, this.nextConfig.pageExtensions, false).then(Boolean);
}
let appFile = null;
let pagesFile = null;
if (this.appDir) {
appFile = await findPageFile(this.appDir, normalizedPath + '/page', this.nextConfig.pageExtensions, true);
}
if (this.pagesDir) {
pagesFile = await findPageFile(this.pagesDir, normalizedPath, this.nextConfig.pageExtensions, false);
}
if (appFile && pagesFile) {
return false;
}
return Boolean(appFile || pagesFile);
}
async runMiddleware(params) {
try {
const result = await super.runMiddleware({
...params,
onWarning: (warn)=>{
this.logErrorWithOriginalStack(warn, 'warning');
}
});
if ('finished' in result) {
return result;
}
result.waitUntil.catch((error)=>{
this.logErrorWithOriginalStack(error, 'unhandledRejection');
});
return result;
} catch (error) {
if (error instanceof DecodeError) {
throw error;
}
/**
* We only log the error when it is not a MiddlewareNotFound error as
* in that case we should be already displaying a compilation error
* which is what makes the module not found.
*/ if (!(error instanceof MiddlewareNotFoundError)) {
this.logErrorWithOriginalStack(error);
}
const err = getProperError(error);
decorateServerError(err, COMPILER_NAMES.edgeServer);
const { request, response, parsedUrl } = params;
/**
* When there is a failure for an internal Next.js request from
* middleware we bypass the error without finishing the request
* so we can serve the required chunks to render the error.
*/ if (request.url.includes('/_next/static') || request.url.includes('/__nextjs_original-stack-frame') || request.url.includes('/__nextjs_source-map') || request.url.includes('/__nextjs_error_feedback')) {
return {
finished: false
};
}
response.statusCode = 500;
await this.renderError(err, request, response, parsedUrl.pathname);
return {
finished: true
};
}
}
async runEdgeFunction(params) {
try {
return super.runEdgeFunction({
...params,
onError: (err)=>this.logErrorWithOriginalStack(err, 'app-dir'),
onWarning: (warn)=>{
this.logErrorWithOriginalStack(warn, 'warning');
}
});
} catch (error) {
if (error instanceof DecodeError) {
throw error;
}
this.logErrorWithOriginalStack(error, 'warning');
const err = getProperError(error);
const { req, res, page } = params;
res.statusCode = 500;
await this.renderError(err, req, res, page);
return null;
}
}
getRequestHandler() {
const handler = super.getRequestHandler();
return (req, res, parsedUrl)=>{
const request = this.normalizeReq(req);
const response = this.normalizeRes(res);
const loggingConfig = this.nextConfig.logging;
if (loggingConfig !== false) {
const start = Date.now();
const isMiddlewareRequest = getRequestMeta(req, 'middlewareInvoke');
if (!isMiddlewareRequest) {
response.originalResponse.once('close', ()=>{
// NOTE: The route match is only attached to the request's meta data
// after the request handler is created, so we need to check it in the
// close handler and not before.
const routeMatch = getRequestMeta(req).match;
if (!routeMatch) {
return;
}
logRequests({
request,
response,
loggingConfig,
requestDurationInMs: Date.now() - start
});
});
}
}
return handler(request, response, parsedUrl);
};
}
async handleRequest(req, res, parsedUrl) {
const span = trace('handle-request', undefined, {
url: req.url
});
const result = await span.traceAsyncFn(async ()=>{
var _this_ready;
await ((_this_ready = this.ready) == null ? void 0 : _this_ready.promise);
return await super.handleRequest(req, res, parsedUrl);
});
const memoryUsage = process.memoryUsage();
span.traceChild('memory-usage', {
url: req.url,
'memory.rss': String(memoryUsage.rss),
'memory.heapUsed': String(memoryUsage.heapUsed),
'memory.heapTotal': String(memoryUsage.heapTotal)
}).stop();
return result;
}
async run(req, res, parsedUrl) {
var _this_ready;
await ((_this_ready = this.ready) == null ? void 0 : _this_ready.promise);
const { basePath } = this.nextConfig;
let originalPathname = null;
// TODO: see if we can remove this in the future
if (basePath && pathHasPrefix(parsedUrl.pathname || '/', basePath)) {
// strip basePath before handling dev bundles
// If replace ends up replacing the full url it'll be `undefined`, meaning we have to default it to `/`
originalPathname = parsedUrl.pathname;
parsedUrl.pathname = removePathPrefix(parsedUrl.pathname || '/', basePath);
}
const { pathname } = parsedUrl;
if (pathname.startsWith('/_next')) {
if (fs.existsSync(pathJoin(this.publicDir, '_next'))) {
throw Object.defineProperty(new Error(PUBLIC_DIR_MIDDLEWARE_CONFLICT), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
}
if (originalPathname) {
// restore the path before continuing so that custom-routes can accurately determine
// if they should match against the basePath or not
parsedUrl.pathname = originalPathname;
}
try {
return await super.run(req, res, parsedUrl);
} catch (error) {
const err = getProperError(error);
formatServerError(err);
this.logErrorWithOriginalStack(err);
if (!res.sent) {
res.statusCode = 500;
try {
return await this.renderError(err, req, res, pathname, {
__NEXT_PAGE: isError(err) && err.page || pathname || ''
});
} catch (internalErr) {
console.error(internalErr);
res.body('Internal Server Error').send();
}
}
}
}
logErrorWithOriginalStack(err, type) {
this.bundlerService.logErrorWithOriginalStack(err, type);
}
getPagesManifest() {
return NodeManifestLoader.require(pathJoin(this.serverDistDir, PAGES_MANIFEST)) ?? undefined;
}
getAppPathsManifest() {
if (!this.enabledDirectories.app) return undefined;
return NodeManifestLoader.require(pathJoin(this.serverDistDir, APP_PATHS_MANIFEST)) ?? undefined;
}
getinterceptionRoutePatterns() {
const rewrites = generateInterceptionRoutesRewrites(Object.keys(this.appPathRoutes ?? {}), this.nextConfig.basePath).map((route)=>new RegExp(buildCustomRoute('rewrite', route).regex));
if (this.nextConfig.output === 'export' && rewrites.length > 0) {
Log.error('Intercepting routes are not supported with static export.\nRead more: https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features');
process.exit(1);
}
return rewrites ?? [];
}
async getMiddleware() {
var _this_middleware;
// We need to populate the match
// field as it isn't serializable
if (((_this_middleware = this.middleware) == null ? void 0 : _this_middleware.match) === null) {
this.middleware.match = getMiddlewareRouteMatcher(this.middleware.matchers || []);
}
return this.middleware;
}
getNextFontManifest() {
return undefined;
}
async hasMiddleware() {
return this.hasPage(this.actualMiddlewareFile);
}
async ensureMiddleware(url) {
return this.ensurePage({
page: this.actualMiddlewareFile,
clientOnly: false,
definition: undefined,
url
});
}
async loadInstrumentationModule() {
let instrumentationModule;
if (this.actualInstrumentationHookFile && await this.ensurePage({
page: this.actualInstrumentationHookFile,
clientOnly: false,
definition: undefined
}).then(()=>true).catch(()=>false)) {
try {
instrumentationModule = await require(pathJoin(this.distDir, 'server', INSTRUMENTATION_HOOK_FILENAME));
} catch (err) {
err.message = `An error occurred while loading instrumentation hook: ${err.message}`;
throw err;
}
}
return instrumentationModule;
}
async runInstrumentationHookIfAvailable() {
await this.startServerSpan.traceChild('run-instrumentation-hook').traceAsyncFn(()=>{
var _this_instrumentation_register, _this_instrumentation;
return (_this_instrumentation = this.instrumentation) == null ? void 0 : (_this_instrumentation_register = _this_instrumentation.register) == null ? void 0 : _this_instrumentation_register.call(_this_instrumentation);
});
}
async ensureEdgeFunction({ page, appPaths, url }) {
return this.ensurePage({
page,
appPaths,
clientOnly: false,
definition: undefined,
url
});
}
generateRoutes(_dev) {
// In development we expose all compiled files for react-error-overlay's line show feature
// We use unshift so that we're sure the routes is defined before Next's default routes
// routes.unshift({
// match: getPathMatch('/_next/development/:path*'),
// type: 'route',
// name: '_next/development catchall',
// fn: async (req, res, params) => {
// const p = pathJoin(this.distDir, ...(params.path || []))
// await this.serveStatic(req, res, p)
// return {
// finished: true,
// }
// },
// })
}
_filterAmpDevelopmentScript(html, event) {
if (event.code !== 'DISALLOWED_SCRIPT_TAG') {
return true;
}
const snippetChunks = html.split('\n');
let snippet;
if (!(snippet = html.split('\n')[event.line - 1]) || !(snippet = snippet.substring(event.col))) {
return true;
}
snippet = snippet + snippetChunks.slice(event.line).join('\n');
snippet = snippet.substring(0, snippet.indexOf('</script>'));
return !snippet.includes('data-amp-development-mode-only');
}
async getStaticPaths({ pathname, requestHeaders, page, isAppPath }) {
// we lazy load the staticPaths to prevent the user
// from waiting on them for the page to load in dev mode
const __getStaticPaths = async ()=>{
const { configFileName, publicRuntimeConfig, serverRuntimeConfig, httpAgentOptions } = this.nextConfig;
const { locales, defaultLocale } = this.nextConfig.i18n || {};
const staticPathsWorker = this.getStaticPathsWorker();
try {
var _this_nextConfig_experimental_sri;
const pathsResult = await staticPathsWorker.loadStaticPaths({
dir: this.dir,
distDir: this.distDir,
pathname,
config: {
pprConfig: this.nextConfig.experimental.ppr,
configFileName,
publicRuntimeConfig,
serverRuntimeConfig,
dynamicIO: Boolean(this.nextConfig.experimental.dynamicIO)
},
httpAgentOptions,
locales,
defaultLocale,
page,
isAppPath,
requestHeaders,
cacheHandler: this.nextConfig.cacheHandler,
cacheHandlers: this.nextConfig.experimental.cacheHandlers,
cacheLifeProfiles: this.nextConfig.experimental.cacheLife,
fetchCacheKeyPrefix: this.nextConfig.experimental.fetchCacheKeyPrefix,
isrFlushToDisk: this.nextConfig.experimental.isrFlushToDisk,
maxMemoryCacheSize: this.nextConfig.cacheMaxMemorySize,
nextConfigOutput: this.nextConfig.output,
buildId: this.buildId,
authInterrupts: Boolean(this.nextConfig.experimental.authInterrupts),
sriEnabled: Boolean((_this_nextConfig_experimental_sri = this.nextConfig.experimental.sri) == null ? void 0 : _this_nextConfig_experimental_sri.algorithm)
});
return pathsResult;
} finally{
// we don't re-use workers so destroy the used one
staticPathsWorker.end();
}
};
const result = this.staticPathsCache.get(pathname);
const nextInvoke = withCoalescedInvoke(__getStaticPaths)(`staticPaths-${pathname}`, []).then((res)=>{
const { prerenderedRoutes: staticPaths, fallbackMode: fallback } = res.value;
if (!isAppPath && this.nextConfig.output === 'export') {
if (fallback === FallbackMode.BLOCKING_STATIC_RENDER) {
throw Object.defineProperty(new Error('getStaticPaths with "fallback: blocking" cannot be used with "output: export". See more info here: https://nextjs.org/docs/advanced-features/static-html-export'), "__NEXT_ERROR_CODE", {
value: "E11",
enumerable: false,
configurable: true
});
} else if (fallback === FallbackMode.PRERENDER) {
throw Object.defineProperty(new Error('getStaticPaths with "fallback: true" cannot be used with "output: export". See more info here: https://nextjs.org/docs/advanced-features/static-html-export'), "__NEXT_ERROR_CODE", {
value: "E210",
enumerable: false,
configurable: true
});
}
}
const value = {
staticPaths: staticPaths == null ? void 0 : staticPaths.map((route)=>route.pathname),
fallbackMode: fallback
};
this.staticPathsCache.set(pathname, value);
return value;
}).catch((err)=>{
this.staticPathsCache.remove(pathname);
if (!result) throw err;
Log.error(`Failed to generate static paths for ${pathname}:`);
console.error(err);
});
if (result) {
return result;
}
return nextInvoke;
}
async ensurePage(opts) {
await this.bundlerService.ensurePage(opts);
}
async findPageComponents({ locale, page, query, params, isAppPath, appPaths = null, shouldEnsure, url }) {
var _this_ready;
await ((_this_ready = this.ready) == null ? void 0 : _this_ready.promise);
const compilationErr = await this.getCompilationError(page);
if (compilationErr) {
// Wrap build errors so that they don't get logged again
throw new WrappedBuildError(compilationErr);
}
if (shouldEnsure || this.serverOptions.customServer) {
await this.ensurePage({
page,
appPaths,
clientOnly: false,
definition: undefined,
url
});
}
this.nextFontManifest = super.getNextFontManifest();
return await super.findPageComponents({
page,
query,
params,
locale,
isAppPath,
shouldEnsure,
url
});
}
async getFallbackErrorComponents(url) {
await this.bundlerService.getFallbackErrorComponents(url);
return await loadDefaultErrorComponents(this.distDir);
}
async getCompilationError(page) {
return await this.bundlerService.getCompilationError(page);
}
async instrumentationOnRequestError(...args) {
await super.instrumentationOnRequestError(...args);
const err = args[0];
this.logErrorWithOriginalStack(err, 'app-dir');
}
}
//# sourceMappingURL=next-dev-server.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,641 @@
import createDebug from 'next/dist/compiled/debug';
import { EventEmitter } from 'events';
import { findPageFile } from '../lib/find-page-file';
import { getStaticInfoIncludingLayouts, runDependingOnPageType } from '../../build/entries';
import { join, posix } from 'path';
import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep';
import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-path';
import { ensureLeadingSlash } from '../../shared/lib/page-path/ensure-leading-slash';
import { removePagePathTail } from '../../shared/lib/page-path/remove-page-path-tail';
import { reportTrigger } from '../../build/output';
import getRouteFromEntrypoint from '../get-route-from-entrypoint';
import { isInstrumentationHookFile, isInstrumentationHookFilename, isMiddlewareFile, isMiddlewareFilename } from '../../build/utils';
import { PageNotFoundError, stringifyError } from '../../shared/lib/utils';
import { COMPILER_INDEXES, COMPILER_NAMES, RSC_MODULE_TYPES, UNDERSCORE_NOT_FOUND_ROUTE_ENTRY } from '../../shared/lib/constants';
import { PAGE_SEGMENT_KEY } from '../../shared/lib/segment';
import { HMR_ACTIONS_SENT_TO_BROWSER } from './hot-reloader-types';
import { isAppPageRouteDefinition } from '../route-definitions/app-page-route-definition';
import { scheduleOnNextTick } from '../../lib/scheduler';
import { Batcher } from '../../lib/batcher';
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths';
import { PAGE_TYPES } from '../../lib/page-types';
import { getNextFlightSegmentPath } from '../../client/flight-data-helpers';
const debug = createDebug('next:on-demand-entry-handler');
/**
* Returns object keys with type inferred from the object key
*/ const keys = Object.keys;
const COMPILER_KEYS = keys(COMPILER_INDEXES);
function treePathToEntrypoint(segmentPath, parentPath) {
const [parallelRouteKey, segment] = segmentPath;
// TODO-APP: modify this path to cover parallelRouteKey convention
const path = (parentPath ? parentPath + '/' : '') + (parallelRouteKey !== 'children' && !segment.startsWith('@') ? `@${parallelRouteKey}/` : '') + (segment === '' ? 'page' : segment);
// Last segment
if (segmentPath.length === 2) {
return path;
}
const childSegmentPath = getNextFlightSegmentPath(segmentPath);
return treePathToEntrypoint(childSegmentPath, path);
}
function convertDynamicParamTypeToSyntax(dynamicParamTypeShort, param) {
switch(dynamicParamTypeShort){
case 'c':
case 'ci':
return `[...${param}]`;
case 'oc':
return `[[...${param}]]`;
case 'd':
case 'di':
return `[${param}]`;
default:
throw Object.defineProperty(new Error('Unknown dynamic param type'), "__NEXT_ERROR_CODE", {
value: "E378",
enumerable: false,
configurable: true
});
}
}
/**
* format: {compiler type}@{page type}@{page path}
* e.g. client@pages@/index
* e.g. server@app@app/page
*
* This guarantees the uniqueness for each page, to avoid conflicts between app/ and pages/
*/ export function getEntryKey(compilerType, pageBundleType, page) {
// TODO: handle the /children slot better
// this is a quick hack to handle when children is provided as children/page instead of /page
const pageKey = page.replace(/(@[^/]+)\/children/g, '$1');
return `${compilerType}@${pageBundleType}@${pageKey}`;
}
function getPageBundleType(pageBundlePath) {
// Handle special case for /_error
if (pageBundlePath === '/_error') return PAGE_TYPES.PAGES;
if (isMiddlewareFilename(pageBundlePath)) return PAGE_TYPES.ROOT;
return pageBundlePath.startsWith('pages/') ? PAGE_TYPES.PAGES : pageBundlePath.startsWith('app/') ? PAGE_TYPES.APP : PAGE_TYPES.ROOT;
}
function getEntrypointsFromTree(tree, isFirst, parentPath = []) {
const [segment, parallelRoutes] = tree;
const currentSegment = Array.isArray(segment) ? convertDynamicParamTypeToSyntax(segment[2], segment[0]) : segment;
const isPageSegment = currentSegment.startsWith(PAGE_SEGMENT_KEY);
const currentPath = [
...parentPath,
isPageSegment ? '' : currentSegment
];
if (!isFirst && isPageSegment) {
// TODO get rid of '' at the start of tree
return [
treePathToEntrypoint(currentPath.slice(1))
];
}
return Object.keys(parallelRoutes).reduce((paths, key)=>{
const childTree = parallelRoutes[key];
const childPages = getEntrypointsFromTree(childTree, false, [
...currentPath,
key
]);
return [
...paths,
...childPages
];
}, []);
}
export const ADDED = Symbol('added');
export const BUILDING = Symbol('building');
export const BUILT = Symbol('built');
// Shadowing check in ESLint does not account for enum
// eslint-disable-next-line no-shadow
export var EntryTypes = /*#__PURE__*/ function(EntryTypes) {
EntryTypes[EntryTypes["ENTRY"] = 0] = "ENTRY";
EntryTypes[EntryTypes["CHILD_ENTRY"] = 1] = "CHILD_ENTRY";
return EntryTypes;
}({});
const entriesMap = new Map();
// remove /server from end of output for server compiler
const normalizeOutputPath = (dir)=>dir.replace(/[/\\]server$/, '');
export const getEntries = (dir)=>{
dir = normalizeOutputPath(dir);
const entries = entriesMap.get(dir) || {};
entriesMap.set(dir, entries);
return entries;
};
const invalidators = new Map();
export const getInvalidator = (dir)=>{
dir = normalizeOutputPath(dir);
return invalidators.get(dir);
};
const doneCallbacks = new EventEmitter();
const lastClientAccessPages = [
''
];
const lastServerAccessPagesForAppDir = [
''
];
// Make sure only one invalidation happens at a time
// Otherwise, webpack hash gets changed and it'll force the client to reload.
class Invalidator {
constructor(multiCompiler){
this.building = new Set();
this.rebuildAgain = new Set();
this.multiCompiler = multiCompiler;
}
shouldRebuildAll() {
return this.rebuildAgain.size > 0;
}
invalidate(compilerKeys = COMPILER_KEYS) {
for (const key of compilerKeys){
var _this_multiCompiler_compilers_COMPILER_INDEXES_key_watching;
// If there's a current build is processing, we won't abort it by invalidating.
// (If aborted, it'll cause a client side hard reload)
// But let it to invalidate just after the completion.
// So, it can re-build the queued pages at once.
if (this.building.has(key)) {
this.rebuildAgain.add(key);
continue;
}
this.building.add(key);
(_this_multiCompiler_compilers_COMPILER_INDEXES_key_watching = this.multiCompiler.compilers[COMPILER_INDEXES[key]].watching) == null ? void 0 : _this_multiCompiler_compilers_COMPILER_INDEXES_key_watching.invalidate();
}
}
startBuilding(compilerKey) {
this.building.add(compilerKey);
}
doneBuilding(compilerKeys = []) {
const rebuild = [];
for (const key of compilerKeys){
this.building.delete(key);
if (this.rebuildAgain.has(key)) {
rebuild.push(key);
this.rebuildAgain.delete(key);
}
}
if (rebuild.length > 0) {
this.invalidate(rebuild);
}
}
willRebuild(compilerKey) {
return this.rebuildAgain.has(compilerKey);
}
}
function disposeInactiveEntries(entries, maxInactiveAge) {
Object.keys(entries).forEach((entryKey)=>{
const entryData = entries[entryKey];
const { lastActiveTime, status, dispose, bundlePath } = entryData;
// TODO-APP: implement disposing of CHILD_ENTRY
if (entryData.type === 1) {
return;
}
// For the root middleware and the instrumentation hook files,
// we don't dispose them periodically as it's needed for every request.
if (isMiddlewareFilename(bundlePath) || isInstrumentationHookFilename(bundlePath)) {
return;
}
if (dispose) // Skip pages already scheduled for disposing
return;
// This means this entry is currently building or just added
// We don't need to dispose those entries.
if (status !== BUILT) return;
// We should not build the last accessed page even we didn't get any pings
// Sometimes, it's possible our XHR ping to wait before completing other requests.
// In that case, we should not dispose the current viewing page
if (lastClientAccessPages.includes(entryKey) || lastServerAccessPagesForAppDir.includes(entryKey)) return;
if (lastActiveTime && Date.now() - lastActiveTime > maxInactiveAge) {
entries[entryKey].dispose = true;
}
});
}
// Normalize both app paths and page paths
function tryToNormalizePagePath(page) {
try {
return normalizePagePath(page);
} catch (err) {
console.error(err);
throw new PageNotFoundError(page);
}
}
/**
* Attempts to find a page file path from the given pages absolute directory,
* a page and allowed extensions. If the page can't be found it will throw an
* error. It defaults the `/_error` page to Next.js internal error page.
*
* @param rootDir Absolute path to the project root.
* @param page The page normalized (it will be denormalized).
* @param extensions Array of page extensions.
* @param pagesDir Absolute path to the pages folder with trailing `/pages`.
* @param appDir Absolute path to the app folder with trailing `/app`.
*/ export async function findPagePathData(rootDir, page, extensions, pagesDir, appDir) {
const normalizedPagePath = tryToNormalizePagePath(page);
let pagePath = null;
const isInstrumentation = isInstrumentationHookFile(normalizedPagePath);
if (isMiddlewareFile(normalizedPagePath) || isInstrumentation) {
pagePath = await findPageFile(rootDir, normalizedPagePath, extensions, false);
if (!pagePath) {
throw new PageNotFoundError(normalizedPagePath);
}
const pageUrl = ensureLeadingSlash(removePagePathTail(normalizePathSep(pagePath), {
extensions
}));
let bundlePath = normalizedPagePath;
let pageKey = posix.normalize(pageUrl);
if (isInstrumentation || isMiddlewareFile(normalizedPagePath)) {
bundlePath = bundlePath.replace('/src', '');
pageKey = page.replace('/src', '');
}
return {
filename: join(rootDir, pagePath),
bundlePath: bundlePath.slice(1),
page: pageKey
};
}
// Check appDir first falling back to pagesDir
if (appDir) {
if (page === UNDERSCORE_NOT_FOUND_ROUTE_ENTRY) {
const notFoundPath = await findPageFile(appDir, 'not-found', extensions, true);
if (notFoundPath) {
return {
filename: join(appDir, notFoundPath),
bundlePath: `app${UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}`,
page: UNDERSCORE_NOT_FOUND_ROUTE_ENTRY
};
}
return {
filename: require.resolve('next/dist/client/components/not-found-error'),
bundlePath: `app${UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}`,
page: UNDERSCORE_NOT_FOUND_ROUTE_ENTRY
};
}
pagePath = await findPageFile(appDir, normalizedPagePath, extensions, true);
if (pagePath) {
const pageUrl = ensureLeadingSlash(removePagePathTail(normalizePathSep(pagePath), {
keepIndex: true,
extensions
}));
return {
filename: join(appDir, pagePath),
bundlePath: posix.join('app', pageUrl),
page: posix.normalize(pageUrl)
};
}
}
if (!pagePath && pagesDir) {
pagePath = await findPageFile(pagesDir, normalizedPagePath, extensions, false);
}
if (pagePath !== null && pagesDir) {
const pageUrl = ensureLeadingSlash(removePagePathTail(normalizePathSep(pagePath), {
extensions
}));
return {
filename: join(pagesDir, pagePath),
bundlePath: posix.join('pages', normalizePagePath(pageUrl)),
page: posix.normalize(pageUrl)
};
}
if (page === '/_error') {
return {
filename: require.resolve('next/dist/pages/_error'),
bundlePath: page,
page: normalizePathSep(page)
};
} else {
throw new PageNotFoundError(normalizedPagePath);
}
}
export function onDemandEntryHandler({ hotReloader, maxInactiveAge, multiCompiler, nextConfig, pagesBufferLength, pagesDir, rootDir, appDir }) {
const hasAppDir = !!appDir;
let curInvalidator = getInvalidator(multiCompiler.outputPath);
const curEntries = getEntries(multiCompiler.outputPath);
if (!curInvalidator) {
curInvalidator = new Invalidator(multiCompiler);
invalidators.set(multiCompiler.outputPath, curInvalidator);
}
const startBuilding = (compilation)=>{
const compilationName = compilation.name;
curInvalidator.startBuilding(compilationName);
};
for (const compiler of multiCompiler.compilers){
compiler.hooks.make.tap('NextJsOnDemandEntries', startBuilding);
}
function getPagePathsFromEntrypoints(type, entrypoints) {
const pagePaths = [];
for (const entrypoint of entrypoints.values()){
const page = getRouteFromEntrypoint(entrypoint.name, hasAppDir);
if (page) {
var _entrypoint_name;
const pageBundleType = ((_entrypoint_name = entrypoint.name) == null ? void 0 : _entrypoint_name.startsWith('app/')) ? PAGE_TYPES.APP : PAGE_TYPES.PAGES;
pagePaths.push(getEntryKey(type, pageBundleType, page));
} else if (isMiddlewareFilename(entrypoint.name) || isInstrumentationHookFilename(entrypoint.name)) {
pagePaths.push(getEntryKey(type, PAGE_TYPES.ROOT, `/${entrypoint.name}`));
}
}
return pagePaths;
}
for (const compiler of multiCompiler.compilers){
compiler.hooks.done.tap('NextJsOnDemandEntries', ()=>{
var _getInvalidator;
return (_getInvalidator = getInvalidator(compiler.outputPath)) == null ? void 0 : _getInvalidator.doneBuilding([
compiler.name
]);
});
}
multiCompiler.hooks.done.tap('NextJsOnDemandEntries', (multiStats)=>{
var _getInvalidator;
const [clientStats, serverStats, edgeServerStats] = multiStats.stats;
const entryNames = [
...getPagePathsFromEntrypoints(COMPILER_NAMES.client, clientStats.compilation.entrypoints),
...getPagePathsFromEntrypoints(COMPILER_NAMES.server, serverStats.compilation.entrypoints),
...edgeServerStats ? getPagePathsFromEntrypoints(COMPILER_NAMES.edgeServer, edgeServerStats.compilation.entrypoints) : []
];
for (const name of entryNames){
const entry = curEntries[name];
if (!entry) {
continue;
}
if (entry.status !== BUILDING) {
continue;
}
entry.status = BUILT;
doneCallbacks.emit(name);
}
(_getInvalidator = getInvalidator(multiCompiler.outputPath)) == null ? void 0 : _getInvalidator.doneBuilding([
...COMPILER_KEYS
]);
});
const pingIntervalTime = Math.max(1000, Math.min(5000, maxInactiveAge));
setInterval(function() {
disposeInactiveEntries(curEntries, maxInactiveAge);
}, pingIntervalTime + 1000).unref();
function handleAppDirPing(tree) {
const pages = getEntrypointsFromTree(tree, true);
for (const page of pages){
for (const compilerType of [
COMPILER_NAMES.client,
COMPILER_NAMES.server,
COMPILER_NAMES.edgeServer
]){
const entryKey = getEntryKey(compilerType, PAGE_TYPES.APP, `/${page}`);
const entryInfo = curEntries[entryKey];
// If there's no entry, it may have been invalidated and needs to be re-built.
if (!entryInfo) {
continue;
}
// We don't need to maintain active state of anything other than BUILT entries
if (entryInfo.status !== BUILT) continue;
// If there's an entryInfo
if (!lastServerAccessPagesForAppDir.includes(entryKey)) {
lastServerAccessPagesForAppDir.unshift(entryKey);
// Maintain the buffer max length
// TODO: verify that the current pageKey is not at the end of the array as multiple entrypoints can exist
if (lastServerAccessPagesForAppDir.length > pagesBufferLength) {
lastServerAccessPagesForAppDir.pop();
}
}
entryInfo.lastActiveTime = Date.now();
entryInfo.dispose = false;
}
}
}
function handlePing(pg) {
const page = normalizePathSep(pg);
for (const compilerType of [
COMPILER_NAMES.client,
COMPILER_NAMES.server,
COMPILER_NAMES.edgeServer
]){
const entryKey = getEntryKey(compilerType, PAGE_TYPES.PAGES, page);
const entryInfo = curEntries[entryKey];
// If there's no entry, it may have been invalidated and needs to be re-built.
if (!entryInfo) {
// if (page !== lastEntry) client pings, but there's no entry for page
if (compilerType === COMPILER_NAMES.client) {
return;
}
continue;
}
// We don't need to maintain active state of anything other than BUILT entries
if (entryInfo.status !== BUILT) continue;
// If there's an entryInfo
if (!lastClientAccessPages.includes(entryKey)) {
lastClientAccessPages.unshift(entryKey);
// Maintain the buffer max length
if (lastClientAccessPages.length > pagesBufferLength) {
lastClientAccessPages.pop();
}
}
entryInfo.lastActiveTime = Date.now();
entryInfo.dispose = false;
}
return;
}
async function ensurePageImpl({ page, appPaths, definition, isApp, url }) {
const stalledTime = 60;
const stalledEnsureTimeout = setTimeout(()=>{
debug(`Ensuring ${page} has taken longer than ${stalledTime}s, if this continues to stall this may be a bug`);
}, stalledTime * 1000);
try {
let route;
if (definition) {
route = definition;
} else {
route = await findPagePathData(rootDir, page, nextConfig.pageExtensions, pagesDir, appDir);
}
const isInsideAppDir = !!appDir && route.filename.startsWith(appDir);
if (typeof isApp === 'boolean' && isApp !== isInsideAppDir) {
Error.stackTraceLimit = 15;
throw Object.defineProperty(new Error(`Ensure bailed, found path "${route.page}" does not match ensure type (${isApp ? 'app' : 'pages'})`), "__NEXT_ERROR_CODE", {
value: "E419",
enumerable: false,
configurable: true
});
}
const pageBundleType = getPageBundleType(route.bundlePath);
const addEntry = (compilerType)=>{
const entryKey = getEntryKey(compilerType, pageBundleType, route.page);
if (curEntries[entryKey] && // there can be an overlap in the entryKey for the instrumentation hook file and a page named the same
// this is a quick fix to support this scenario by overwriting the instrumentation hook entry, since we only use it one time
// any changes to the instrumentation hook file will require a restart of the dev server anyway
!isInstrumentationHookFilename(curEntries[entryKey].bundlePath)) {
curEntries[entryKey].dispose = false;
curEntries[entryKey].lastActiveTime = Date.now();
if (curEntries[entryKey].status === BUILT) {
return {
entryKey,
newEntry: false,
shouldInvalidate: false
};
}
return {
entryKey,
newEntry: false,
shouldInvalidate: true
};
}
curEntries[entryKey] = {
type: 0,
appPaths,
absolutePagePath: route.filename,
request: route.filename,
bundlePath: route.bundlePath,
dispose: false,
lastActiveTime: Date.now(),
status: ADDED
};
return {
entryKey: entryKey,
newEntry: true,
shouldInvalidate: true
};
};
const staticInfo = await getStaticInfoIncludingLayouts({
page,
pageFilePath: route.filename,
isInsideAppDir,
pageExtensions: nextConfig.pageExtensions,
isDev: true,
config: nextConfig,
appDir
});
const added = new Map();
const isServerComponent = isInsideAppDir && staticInfo.rsc !== RSC_MODULE_TYPES.client;
let pageRuntime = staticInfo.runtime;
if (isMiddlewareFile(page) && !nextConfig.experimental.nodeMiddleware) {
pageRuntime = 'edge';
}
runDependingOnPageType({
page: route.page,
pageRuntime,
pageType: pageBundleType,
onClient: ()=>{
// Skip adding the client entry for app / Server Components.
if (isServerComponent || isInsideAppDir) {
return;
}
added.set(COMPILER_NAMES.client, addEntry(COMPILER_NAMES.client));
},
onServer: ()=>{
added.set(COMPILER_NAMES.server, addEntry(COMPILER_NAMES.server));
const edgeServerEntry = getEntryKey(COMPILER_NAMES.edgeServer, pageBundleType, route.page);
if (curEntries[edgeServerEntry] && !isInstrumentationHookFile(route.page)) {
// Runtime switched from edge to server
delete curEntries[edgeServerEntry];
}
},
onEdgeServer: ()=>{
added.set(COMPILER_NAMES.edgeServer, addEntry(COMPILER_NAMES.edgeServer));
const serverEntry = getEntryKey(COMPILER_NAMES.server, pageBundleType, route.page);
if (curEntries[serverEntry] && !isInstrumentationHookFile(route.page)) {
// Runtime switched from server to edge
delete curEntries[serverEntry];
}
}
});
const addedValues = [
...added.values()
];
const entriesThatShouldBeInvalidated = [
...added.entries()
].filter(([, entry])=>entry.shouldInvalidate);
const hasNewEntry = addedValues.some((entry)=>entry.newEntry);
if (hasNewEntry) {
const routePage = isApp ? route.page : normalizeAppPath(route.page);
reportTrigger(routePage, url);
}
if (entriesThatShouldBeInvalidated.length > 0) {
const invalidatePromise = Promise.all(entriesThatShouldBeInvalidated.map(([compilerKey, { entryKey }])=>{
return new Promise((resolve, reject)=>{
doneCallbacks.once(entryKey, (err)=>{
if (err) {
return reject(err);
}
// If the invalidation also triggers a rebuild, we need to
// wait for that additional build to prevent race conditions.
const needsRebuild = curInvalidator.willRebuild(compilerKey);
if (needsRebuild) {
doneCallbacks.once(entryKey, (rebuildErr)=>{
if (rebuildErr) {
return reject(rebuildErr);
}
resolve();
});
} else {
resolve();
}
});
});
}));
curInvalidator.invalidate([
...added.keys()
]);
await invalidatePromise;
}
} finally{
clearTimeout(stalledEnsureTimeout);
}
}
// Make sure that we won't have multiple invalidations ongoing concurrently.
const batcher = Batcher.create({
// The cache key here is composed of the elements that affect the
// compilation, namely, the page, whether it's client only, and whether
// it's an app page. This ensures that we don't have multiple compilations
// for the same page happening concurrently.
//
// We don't include the whole match because it contains match specific
// parameters (like route params) that would just bust this cache. Any
// details that would possibly bust the cache should be listed here.
cacheKeyFn: (options)=>JSON.stringify(options),
// Schedule the invocation of the ensurePageImpl function on the next tick.
schedulerFn: scheduleOnNextTick
});
return {
async ensurePage ({ page, appPaths = null, definition, isApp, url }) {
// If the route is actually an app page route, then we should have access
// to the app route definition, and therefore, the appPaths from it.
if (!appPaths && definition && isAppPageRouteDefinition(definition)) {
appPaths = definition.appPaths;
}
// Wrap the invocation of the ensurePageImpl function in the pending
// wrapper, which will ensure that we don't have multiple compilations
// for the same page happening concurrently.
return batcher.batch({
page,
appPaths,
definition,
isApp
}, async ()=>{
await ensurePageImpl({
page,
appPaths,
definition,
isApp,
url
});
});
},
onHMR (client, getHmrServerError) {
let bufferedHmrServerError = null;
client.addEventListener('close', ()=>{
bufferedHmrServerError = null;
});
client.addEventListener('message', ({ data })=>{
try {
const error = getHmrServerError();
// New error occurred: buffered error is flushed and new error occurred
if (!bufferedHmrServerError && error) {
hotReloader.send({
action: HMR_ACTIONS_SENT_TO_BROWSER.SERVER_ERROR,
errorJSON: stringifyError(error)
});
bufferedHmrServerError = null;
}
const parsedData = JSON.parse(typeof data !== 'string' ? data.toString() : data);
if (parsedData.event === 'ping') {
if (parsedData.appDirRoute) {
handleAppDirPing(parsedData.tree);
} else {
handlePing(parsedData.page);
}
}
} catch {}
});
}
};
}
//# sourceMappingURL=on-demand-entry-handler.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,57 @@
import * as semver from 'next/dist/compiled/semver';
export function parseVersionInfo(o) {
const latest = semver.parse(o.latest);
const canary = semver.parse(o.canary);
const installedParsed = semver.parse(o.installed);
const installed = o.installed;
if (installedParsed && latest && canary) {
if (installedParsed.major < latest.major) {
// Old major version
return {
staleness: 'stale-major',
expected: latest.raw,
installed
};
} else if (installedParsed.prerelease[0] === 'canary' && semver.lt(installedParsed, canary)) {
// Matching major, but old canary
return {
staleness: 'stale-prerelease',
expected: canary.raw,
installed
};
} else if (!installedParsed.prerelease.length && semver.lt(installedParsed, latest)) {
// Stable, but not the latest
if (installedParsed.minor === latest.minor) {
// Same major and minor, but not the latest patch
return {
staleness: 'stale-patch',
expected: latest.raw,
installed
};
}
return {
staleness: 'stale-minor',
expected: latest.raw,
installed
};
} else if (semver.gt(installedParsed, latest) && installedParsed.version !== canary.version) {
// Newer major version
return {
staleness: 'newer-than-npm',
installed
};
} else {
// Latest and greatest
return {
staleness: 'fresh',
installed
};
}
}
return {
installed: (installedParsed == null ? void 0 : installedParsed.raw) ?? '0.0.0',
staleness: 'unknown'
};
}
//# sourceMappingURL=parse-version-info.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/dev/parse-version-info.ts"],"sourcesContent":["import * as semver from 'next/dist/compiled/semver'\n\nexport interface VersionInfo {\n installed: string\n staleness:\n | 'fresh'\n | 'stale-patch'\n | 'stale-minor'\n | 'stale-major'\n | 'stale-prerelease'\n | 'newer-than-npm'\n | 'unknown'\n expected?: string\n}\n\nexport function parseVersionInfo(o: {\n installed: string\n latest: string\n canary: string\n}): VersionInfo {\n const latest = semver.parse(o.latest)\n const canary = semver.parse(o.canary)\n const installedParsed = semver.parse(o.installed)\n const installed = o.installed\n if (installedParsed && latest && canary) {\n if (installedParsed.major < latest.major) {\n // Old major version\n return { staleness: 'stale-major', expected: latest.raw, installed }\n } else if (\n installedParsed.prerelease[0] === 'canary' &&\n semver.lt(installedParsed, canary)\n ) {\n // Matching major, but old canary\n return {\n staleness: 'stale-prerelease',\n expected: canary.raw,\n installed,\n }\n } else if (\n !installedParsed.prerelease.length &&\n semver.lt(installedParsed, latest)\n ) {\n // Stable, but not the latest\n if (installedParsed.minor === latest.minor) {\n // Same major and minor, but not the latest patch\n return {\n staleness: 'stale-patch',\n expected: latest.raw,\n installed,\n }\n }\n return { staleness: 'stale-minor', expected: latest.raw, installed }\n } else if (\n semver.gt(installedParsed, latest) &&\n installedParsed.version !== canary.version\n ) {\n // Newer major version\n return { staleness: 'newer-than-npm', installed }\n } else {\n // Latest and greatest\n return { staleness: 'fresh', installed }\n }\n }\n\n return {\n installed: installedParsed?.raw ?? '0.0.0',\n staleness: 'unknown',\n }\n}\n"],"names":["semver","parseVersionInfo","o","latest","parse","canary","installedParsed","installed","major","staleness","expected","raw","prerelease","lt","length","minor","gt","version"],"mappings":"AAAA,YAAYA,YAAY,4BAA2B;AAenD,OAAO,SAASC,iBAAiBC,CAIhC;IACC,MAAMC,SAASH,OAAOI,KAAK,CAACF,EAAEC,MAAM;IACpC,MAAME,SAASL,OAAOI,KAAK,CAACF,EAAEG,MAAM;IACpC,MAAMC,kBAAkBN,OAAOI,KAAK,CAACF,EAAEK,SAAS;IAChD,MAAMA,YAAYL,EAAEK,SAAS;IAC7B,IAAID,mBAAmBH,UAAUE,QAAQ;QACvC,IAAIC,gBAAgBE,KAAK,GAAGL,OAAOK,KAAK,EAAE;YACxC,oBAAoB;YACpB,OAAO;gBAAEC,WAAW;gBAAeC,UAAUP,OAAOQ,GAAG;gBAAEJ;YAAU;QACrE,OAAO,IACLD,gBAAgBM,UAAU,CAAC,EAAE,KAAK,YAClCZ,OAAOa,EAAE,CAACP,iBAAiBD,SAC3B;YACA,iCAAiC;YACjC,OAAO;gBACLI,WAAW;gBACXC,UAAUL,OAAOM,GAAG;gBACpBJ;YACF;QACF,OAAO,IACL,CAACD,gBAAgBM,UAAU,CAACE,MAAM,IAClCd,OAAOa,EAAE,CAACP,iBAAiBH,SAC3B;YACA,6BAA6B;YAC7B,IAAIG,gBAAgBS,KAAK,KAAKZ,OAAOY,KAAK,EAAE;gBAC1C,iDAAiD;gBACjD,OAAO;oBACLN,WAAW;oBACXC,UAAUP,OAAOQ,GAAG;oBACpBJ;gBACF;YACF;YACA,OAAO;gBAAEE,WAAW;gBAAeC,UAAUP,OAAOQ,GAAG;gBAAEJ;YAAU;QACrE,OAAO,IACLP,OAAOgB,EAAE,CAACV,iBAAiBH,WAC3BG,gBAAgBW,OAAO,KAAKZ,OAAOY,OAAO,EAC1C;YACA,sBAAsB;YACtB,OAAO;gBAAER,WAAW;gBAAkBF;YAAU;QAClD,OAAO;YACL,sBAAsB;YACtB,OAAO;gBAAEE,WAAW;gBAASF;YAAU;QACzC;IACF;IAEA,OAAO;QACLA,WAAWD,CAAAA,mCAAAA,gBAAiBK,GAAG,KAAI;QACnCF,WAAW;IACb;AACF"}

View File

@@ -0,0 +1,34 @@
import isError from '../../lib/is-error';
import { realpathSync } from '../../lib/realpath';
import { clearManifestCache } from '../load-manifest';
export function deleteFromRequireCache(filePath) {
try {
filePath = realpathSync(filePath);
} catch (e) {
if (isError(e) && e.code !== 'ENOENT') throw e;
}
const mod = require.cache[filePath];
if (mod) {
// remove the child reference from all parent modules
for (const parent of Object.values(require.cache)){
if (parent == null ? void 0 : parent.children) {
const idx = parent.children.indexOf(mod);
if (idx >= 0) parent.children.splice(idx, 1);
}
}
// remove parent references from external modules
for (const child of mod.children){
child.parent = null;
}
delete require.cache[filePath];
return true;
}
return false;
}
export function deleteCache(filePath) {
// try to clear it from the fs cache
clearManifestCache(filePath);
deleteFromRequireCache(filePath);
}
//# sourceMappingURL=require-cache.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/dev/require-cache.ts"],"sourcesContent":["import isError from '../../lib/is-error'\nimport { realpathSync } from '../../lib/realpath'\nimport { clearManifestCache } from '../load-manifest'\n\nexport function deleteFromRequireCache(filePath: string) {\n try {\n filePath = realpathSync(filePath)\n } catch (e) {\n if (isError(e) && e.code !== 'ENOENT') throw e\n }\n const mod = require.cache[filePath]\n if (mod) {\n // remove the child reference from all parent modules\n for (const parent of Object.values(require.cache)) {\n if (parent?.children) {\n const idx = parent.children.indexOf(mod)\n if (idx >= 0) parent.children.splice(idx, 1)\n }\n }\n // remove parent references from external modules\n for (const child of mod.children) {\n child.parent = null\n }\n delete require.cache[filePath]\n return true\n }\n return false\n}\n\nexport function deleteCache(filePath: string) {\n // try to clear it from the fs cache\n clearManifestCache(filePath)\n\n deleteFromRequireCache(filePath)\n}\n"],"names":["isError","realpathSync","clearManifestCache","deleteFromRequireCache","filePath","e","code","mod","require","cache","parent","Object","values","children","idx","indexOf","splice","child","deleteCache"],"mappings":"AAAA,OAAOA,aAAa,qBAAoB;AACxC,SAASC,YAAY,QAAQ,qBAAoB;AACjD,SAASC,kBAAkB,QAAQ,mBAAkB;AAErD,OAAO,SAASC,uBAAuBC,QAAgB;IACrD,IAAI;QACFA,WAAWH,aAAaG;IAC1B,EAAE,OAAOC,GAAG;QACV,IAAIL,QAAQK,MAAMA,EAAEC,IAAI,KAAK,UAAU,MAAMD;IAC/C;IACA,MAAME,MAAMC,QAAQC,KAAK,CAACL,SAAS;IACnC,IAAIG,KAAK;QACP,qDAAqD;QACrD,KAAK,MAAMG,UAAUC,OAAOC,MAAM,CAACJ,QAAQC,KAAK,EAAG;YACjD,IAAIC,0BAAAA,OAAQG,QAAQ,EAAE;gBACpB,MAAMC,MAAMJ,OAAOG,QAAQ,CAACE,OAAO,CAACR;gBACpC,IAAIO,OAAO,GAAGJ,OAAOG,QAAQ,CAACG,MAAM,CAACF,KAAK;YAC5C;QACF;QACA,iDAAiD;QACjD,KAAK,MAAMG,SAASV,IAAIM,QAAQ,CAAE;YAChCI,MAAMP,MAAM,GAAG;QACjB;QACA,OAAOF,QAAQC,KAAK,CAACL,SAAS;QAC9B,OAAO;IACT;IACA,OAAO;AACT;AAEA,OAAO,SAASc,YAAYd,QAAgB;IAC1C,oCAAoC;IACpCF,mBAAmBE;IAEnBD,uBAAuBC;AACzB"}

View File

@@ -0,0 +1,84 @@
import '../require-hook';
import '../node-environment';
import { reduceAppConfig } from '../../build/utils';
import { collectSegments } from '../../build/segment-config/app/app-segments';
import { loadComponents } from '../load-components';
import { setHttpClientAndAgentOptions } from '../setup-http-agent-env';
import { isAppPageRouteModule } from '../route-modules/checks';
import { checkIsRoutePPREnabled } from '../lib/experimental/ppr';
import { InvariantError } from '../../shared/lib/invariant-error';
import { collectRootParamKeys } from '../../build/segment-config/app/collect-root-param-keys';
import { buildAppStaticPaths } from '../../build/static-paths/app';
import { buildPagesStaticPaths } from '../../build/static-paths/pages';
import { createIncrementalCache } from '../../export/helpers/create-incremental-cache';
// we call getStaticPaths in a separate process to ensure
// side-effects aren't relied on in dev that will break
// during a production build
export async function loadStaticPaths({ dir, distDir, pathname, config, httpAgentOptions, locales, defaultLocale, isAppPath, page, isrFlushToDisk, fetchCacheKeyPrefix, maxMemoryCacheSize, requestHeaders, cacheHandler, cacheHandlers, cacheLifeProfiles, nextConfigOutput, buildId, authInterrupts, sriEnabled }) {
// this needs to be initialized before loadComponents otherwise
// "use cache" could be missing it's cache handlers
await createIncrementalCache({
dir,
distDir,
cacheHandler,
cacheHandlers,
requestHeaders,
fetchCacheKeyPrefix,
flushToDisk: isrFlushToDisk,
cacheMaxMemorySize: maxMemoryCacheSize
});
// update work memory runtime-config
require('../../shared/lib/runtime-config.external').setConfig(config);
setHttpClientAndAgentOptions({
httpAgentOptions
});
const components = await loadComponents({
distDir,
// In `pages/`, the page is the same as the pathname.
page: page || pathname,
isAppPath,
isDev: true,
sriEnabled
});
if (isAppPath) {
const segments = await collectSegments(components);
const isRoutePPREnabled = isAppPageRouteModule(components.routeModule) && checkIsRoutePPREnabled(config.pprConfig, reduceAppConfig(segments));
const rootParamKeys = collectRootParamKeys(components);
return buildAppStaticPaths({
dir,
page: pathname,
dynamicIO: config.dynamicIO,
segments,
distDir,
requestHeaders,
cacheHandler,
cacheLifeProfiles,
isrFlushToDisk,
fetchCacheKeyPrefix,
maxMemoryCacheSize,
ComponentMod: components.ComponentMod,
nextConfigOutput,
isRoutePPREnabled,
buildId,
authInterrupts,
rootParamKeys
});
} else if (!components.getStaticPaths) {
// We shouldn't get to this point since the worker should only be called for
// SSG pages with getStaticPaths.
throw Object.defineProperty(new InvariantError(`Failed to load page with getStaticPaths for ${pathname}`), "__NEXT_ERROR_CODE", {
value: "E605",
enumerable: false,
configurable: true
});
}
return buildPagesStaticPaths({
page: pathname,
getStaticPaths: components.getStaticPaths,
configFileName: config.configFileName,
locales,
defaultLocale
});
}
//# sourceMappingURL=static-paths-worker.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,597 @@
import { HMR_ACTIONS_SENT_TO_BROWSER } from './hot-reloader-types';
import * as Log from '../../build/output/log';
import { getEntryKey, splitEntryKey } from '../../shared/lib/turbopack/entry-key';
import { isMetadataRoute } from '../../lib/metadata/is-metadata-route';
import { formatIssue, getIssueKey, isRelevantWarning, processIssues, renderStyledStringToErrorAnsi } from '../../shared/lib/turbopack/utils';
const onceErrorSet = new Set();
/**
* Check if given issue is a warning to be display only once.
* This mimics behavior of get-page-static-info's warnOnce.
* @param issue
* @returns
*/ function shouldEmitOnceWarning(issue) {
const { severity, title, stage } = issue;
if (severity === 'warning' && title.value === 'Invalid page configuration') {
if (onceErrorSet.has(issue)) {
return false;
}
onceErrorSet.add(issue);
}
if (severity === 'warning' && stage === 'config' && renderStyledStringToErrorAnsi(issue.title).includes("can't be external")) {
if (onceErrorSet.has(issue)) {
return false;
}
onceErrorSet.add(issue);
}
return true;
}
/// Print out an issue to the console which should not block
/// the build by throwing out or blocking error overlay.
export function printNonFatalIssue(issue) {
if (isRelevantWarning(issue) && shouldEmitOnceWarning(issue)) {
Log.warn(formatIssue(issue));
}
}
export function processTopLevelIssues(currentTopLevelIssues, result) {
currentTopLevelIssues.clear();
for (const issue of result.issues){
const issueKey = getIssueKey(issue);
currentTopLevelIssues.set(issueKey, issue);
}
}
const MILLISECONDS_IN_NANOSECOND = BigInt(1000000);
export function msToNs(ms) {
return BigInt(Math.floor(ms)) * MILLISECONDS_IN_NANOSECOND;
}
export async function handleRouteType({ dev, page, pathname, route, currentEntryIssues, entrypoints, manifestLoader, readyIds, devRewrites, productionRewrites, hooks, logErrors }) {
const shouldCreateWebpackStats = process.env.TURBOPACK_STATS != null;
switch(route.type){
case 'page':
{
const clientKey = getEntryKey('pages', 'client', page);
const serverKey = getEntryKey('pages', 'server', page);
try {
// In the best case scenario, Turbopack chunks document, app, page separately in that order,
// so it can happen that the chunks of document change, but the chunks of app and page
// don't. We still need to reload the page chunks in that case though, otherwise the version
// of the document or app component export from the pages template is stale.
let documentOrAppChanged = false;
if (entrypoints.global.app) {
const key = getEntryKey('pages', 'server', '_app');
const writtenEndpoint = await entrypoints.global.app.writeToDisk();
documentOrAppChanged ||= (hooks == null ? void 0 : hooks.handleWrittenEndpoint(key, writtenEndpoint, false)) ?? false;
processIssues(currentEntryIssues, key, writtenEndpoint, false, logErrors);
}
await manifestLoader.loadBuildManifest('_app');
await manifestLoader.loadPagesManifest('_app');
if (entrypoints.global.document) {
const key = getEntryKey('pages', 'server', '_document');
const writtenEndpoint = await entrypoints.global.document.writeToDisk();
documentOrAppChanged ||= (hooks == null ? void 0 : hooks.handleWrittenEndpoint(key, writtenEndpoint, false)) ?? false;
processIssues(currentEntryIssues, key, writtenEndpoint, false, logErrors);
}
await manifestLoader.loadPagesManifest('_document');
const writtenEndpoint = await route.htmlEndpoint.writeToDisk();
hooks == null ? void 0 : hooks.handleWrittenEndpoint(serverKey, writtenEndpoint, documentOrAppChanged);
const type = writtenEndpoint == null ? void 0 : writtenEndpoint.type;
await manifestLoader.loadBuildManifest(page);
await manifestLoader.loadPagesManifest(page);
if (type === 'edge') {
await manifestLoader.loadMiddlewareManifest(page, 'pages');
} else {
manifestLoader.deleteMiddlewareManifest(serverKey);
}
await manifestLoader.loadFontManifest('/_app', 'pages');
await manifestLoader.loadFontManifest(page, 'pages');
if (shouldCreateWebpackStats) {
await manifestLoader.loadWebpackStats(page, 'pages');
}
await manifestLoader.writeManifests({
devRewrites,
productionRewrites,
entrypoints
});
processIssues(currentEntryIssues, serverKey, writtenEndpoint, false, logErrors);
} finally{
if (dev) {
// TODO subscriptions should only be caused by the WebSocket connections
// otherwise we don't known when to unsubscribe and this leaking
hooks == null ? void 0 : hooks.subscribeToChanges(serverKey, false, route.dataEndpoint, ()=>{
// Report the next compilation again
readyIds == null ? void 0 : readyIds.delete(pathname);
return {
event: HMR_ACTIONS_SENT_TO_BROWSER.SERVER_ONLY_CHANGES,
pages: [
page
]
};
}, (e)=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in ${page} data subscription: ${e}`
};
});
hooks == null ? void 0 : hooks.subscribeToChanges(clientKey, false, route.htmlEndpoint, ()=>{
return {
event: HMR_ACTIONS_SENT_TO_BROWSER.CLIENT_CHANGES
};
}, (e)=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in ${page} html subscription: ${e}`
};
});
if (entrypoints.global.document) {
hooks == null ? void 0 : hooks.subscribeToChanges(getEntryKey('pages', 'server', '_document'), false, entrypoints.global.document, ()=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: '_document has changed (page route)'
};
}, (e)=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in _document subscription (page route): ${e}`
};
});
}
}
}
break;
}
case 'page-api':
{
const key = getEntryKey('pages', 'server', page);
const writtenEndpoint = await route.endpoint.writeToDisk();
hooks == null ? void 0 : hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
const type = writtenEndpoint.type;
await manifestLoader.loadPagesManifest(page);
if (type === 'edge') {
await manifestLoader.loadMiddlewareManifest(page, 'pages');
} else {
manifestLoader.deleteMiddlewareManifest(key);
}
await manifestLoader.writeManifests({
devRewrites,
productionRewrites,
entrypoints
});
processIssues(currentEntryIssues, key, writtenEndpoint, true, logErrors);
break;
}
case 'app-page':
{
const key = getEntryKey('app', 'server', page);
const writtenEndpoint = await route.htmlEndpoint.writeToDisk();
hooks == null ? void 0 : hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
if (dev) {
// TODO subscriptions should only be caused by the WebSocket connections
// otherwise we don't known when to unsubscribe and this leaking
hooks == null ? void 0 : hooks.subscribeToChanges(key, true, route.rscEndpoint, (change, hash)=>{
if (change.issues.some((issue)=>issue.severity === 'error')) {
// Ignore any updates that has errors
// There will be another update without errors eventually
return;
}
// Report the next compilation again
readyIds == null ? void 0 : readyIds.delete(pathname);
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.SERVER_COMPONENT_CHANGES,
hash
};
}, (e)=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in ${page} app-page subscription: ${e}`
};
});
}
const type = writtenEndpoint.type;
if (type === 'edge') {
await manifestLoader.loadMiddlewareManifest(page, 'app');
} else {
manifestLoader.deleteMiddlewareManifest(key);
}
await manifestLoader.loadAppBuildManifest(page);
await manifestLoader.loadBuildManifest(page, 'app');
await manifestLoader.loadAppPathsManifest(page);
await manifestLoader.loadActionManifest(page);
await manifestLoader.loadFontManifest(page, 'app');
if (shouldCreateWebpackStats) {
await manifestLoader.loadWebpackStats(page, 'app');
}
await manifestLoader.writeManifests({
devRewrites,
productionRewrites,
entrypoints
});
processIssues(currentEntryIssues, key, writtenEndpoint, dev, logErrors);
break;
}
case 'app-route':
{
const key = getEntryKey('app', 'server', page);
const writtenEndpoint = await route.endpoint.writeToDisk();
hooks == null ? void 0 : hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
const type = writtenEndpoint.type;
await manifestLoader.loadAppPathsManifest(page);
if (type === 'edge') {
await manifestLoader.loadMiddlewareManifest(page, 'app');
} else {
manifestLoader.deleteMiddlewareManifest(key);
}
await manifestLoader.writeManifests({
devRewrites,
productionRewrites,
entrypoints
});
processIssues(currentEntryIssues, key, writtenEndpoint, true, logErrors);
break;
}
default:
{
throw Object.defineProperty(new Error(`unknown route type ${route.type} for ${page}`), "__NEXT_ERROR_CODE", {
value: "E316",
enumerable: false,
configurable: true
});
}
}
}
/**
* Maintains a mapping between entrypoins and the corresponding client asset paths.
*/ export class AssetMapper {
/**
* Overrides asset paths for a key and updates the mapping from path to key.
*
* @param key
* @param assetPaths asset paths relative to the .next directory
*/ setPathsForKey(key, assetPaths) {
this.delete(key);
const newAssetPaths = new Set(assetPaths);
this.entryMap.set(key, newAssetPaths);
for (const assetPath of newAssetPaths){
let assetPathKeys = this.assetMap.get(assetPath);
if (!assetPathKeys) {
assetPathKeys = new Set();
this.assetMap.set(assetPath, assetPathKeys);
}
assetPathKeys.add(key);
}
}
/**
* Deletes the key and any asset only referenced by this key.
*
* @param key
*/ delete(key) {
for (const assetPath of this.getAssetPathsByKey(key)){
const assetPathKeys = this.assetMap.get(assetPath);
assetPathKeys == null ? void 0 : assetPathKeys.delete(key);
if (!(assetPathKeys == null ? void 0 : assetPathKeys.size)) {
this.assetMap.delete(assetPath);
}
}
this.entryMap.delete(key);
}
getAssetPathsByKey(key) {
return Array.from(this.entryMap.get(key) ?? []);
}
getKeysByAsset(path) {
return Array.from(this.assetMap.get(path) ?? []);
}
keys() {
return this.entryMap.keys();
}
constructor(){
this.entryMap = new Map();
this.assetMap = new Map();
}
}
export function hasEntrypointForKey(entrypoints, key, assetMapper) {
const { type, page } = splitEntryKey(key);
switch(type){
case 'app':
return entrypoints.app.has(page);
case 'pages':
switch(page){
case '_app':
return entrypoints.global.app != null;
case '_document':
return entrypoints.global.document != null;
case '_error':
return entrypoints.global.error != null;
default:
return entrypoints.page.has(page);
}
case 'root':
switch(page){
case 'middleware':
return entrypoints.global.middleware != null;
case 'instrumentation':
return entrypoints.global.instrumentation != null;
default:
return false;
}
case 'assets':
if (!assetMapper) {
return false;
}
return assetMapper.getKeysByAsset(page).some((pageKey)=>hasEntrypointForKey(entrypoints, pageKey, assetMapper));
default:
{
// validation that we covered all cases, this should never run.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _ = type;
return false;
}
}
}
export async function handleEntrypoints({ entrypoints, currentEntrypoints, currentEntryIssues, manifestLoader, devRewrites, logErrors, dev }) {
currentEntrypoints.global.app = entrypoints.pagesAppEndpoint;
currentEntrypoints.global.document = entrypoints.pagesDocumentEndpoint;
currentEntrypoints.global.error = entrypoints.pagesErrorEndpoint;
currentEntrypoints.global.instrumentation = entrypoints.instrumentation;
currentEntrypoints.page.clear();
currentEntrypoints.app.clear();
for (const [pathname, route] of entrypoints.routes){
switch(route.type){
case 'page':
case 'page-api':
currentEntrypoints.page.set(pathname, route);
break;
case 'app-page':
{
route.pages.forEach((page)=>{
currentEntrypoints.app.set(page.originalName, {
type: 'app-page',
...page
});
});
break;
}
case 'app-route':
{
currentEntrypoints.app.set(route.originalName, route);
break;
}
default:
Log.info(`skipping ${pathname} (${route.type})`);
break;
}
}
if (dev) {
await handleEntrypointsDevCleanup({
currentEntryIssues,
currentEntrypoints,
...dev
});
}
const { middleware, instrumentation } = entrypoints;
// We check for explicit true/false, since it's initialized to
// undefined during the first loop (middlewareChanges event is
// unnecessary during the first serve)
if (currentEntrypoints.global.middleware && !middleware) {
const key = getEntryKey('root', 'server', 'middleware');
// Went from middleware to no middleware
await (dev == null ? void 0 : dev.hooks.unsubscribeFromChanges(key));
currentEntryIssues.delete(key);
dev.hooks.sendHmr('middleware', {
event: HMR_ACTIONS_SENT_TO_BROWSER.MIDDLEWARE_CHANGES
});
} else if (!currentEntrypoints.global.middleware && middleware) {
// Went from no middleware to middleware
dev.hooks.sendHmr('middleware', {
event: HMR_ACTIONS_SENT_TO_BROWSER.MIDDLEWARE_CHANGES
});
}
currentEntrypoints.global.middleware = middleware;
if (instrumentation) {
const processInstrumentation = async (name, prop)=>{
const prettyName = {
nodeJs: 'Node.js',
edge: 'Edge'
};
const finishBuilding = dev.hooks.startBuilding(`instrumentation ${prettyName[prop]}`, undefined, true);
const key = getEntryKey('root', 'server', name);
const writtenEndpoint = await instrumentation[prop].writeToDisk();
dev.hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
processIssues(currentEntryIssues, key, writtenEndpoint, false, logErrors);
finishBuilding();
};
await processInstrumentation('instrumentation.nodeJs', 'nodeJs');
await processInstrumentation('instrumentation.edge', 'edge');
await manifestLoader.loadMiddlewareManifest('instrumentation', 'instrumentation');
await manifestLoader.writeManifests({
devRewrites,
productionRewrites: undefined,
entrypoints: currentEntrypoints
});
dev.serverFields.actualInstrumentationHookFile = '/instrumentation';
await dev.hooks.propagateServerField('actualInstrumentationHookFile', dev.serverFields.actualInstrumentationHookFile);
} else {
dev.serverFields.actualInstrumentationHookFile = undefined;
await dev.hooks.propagateServerField('actualInstrumentationHookFile', dev.serverFields.actualInstrumentationHookFile);
}
if (middleware) {
const key = getEntryKey('root', 'server', 'middleware');
const endpoint = middleware.endpoint;
async function processMiddleware() {
var _manifestLoader_getMiddlewareManifest;
const finishBuilding = dev.hooks.startBuilding('middleware', undefined, true);
const writtenEndpoint = await endpoint.writeToDisk();
dev.hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
processIssues(currentEntryIssues, key, writtenEndpoint, false, logErrors);
await manifestLoader.loadMiddlewareManifest('middleware', 'middleware');
const middlewareConfig = (_manifestLoader_getMiddlewareManifest = manifestLoader.getMiddlewareManifest(key)) == null ? void 0 : _manifestLoader_getMiddlewareManifest.middleware['/'];
if (dev && middlewareConfig) {
dev.serverFields.middleware = {
match: null,
page: '/',
matchers: middlewareConfig.matchers
};
}
finishBuilding();
}
await processMiddleware();
if (dev) {
dev == null ? void 0 : dev.hooks.subscribeToChanges(key, false, endpoint, async ()=>{
const finishBuilding = dev.hooks.startBuilding('middleware', undefined, true);
await processMiddleware();
await dev.hooks.propagateServerField('actualMiddlewareFile', dev.serverFields.actualMiddlewareFile);
await dev.hooks.propagateServerField('middleware', dev.serverFields.middleware);
await manifestLoader.writeManifests({
devRewrites,
productionRewrites: undefined,
entrypoints: currentEntrypoints
});
finishBuilding == null ? void 0 : finishBuilding();
return {
event: HMR_ACTIONS_SENT_TO_BROWSER.MIDDLEWARE_CHANGES
};
}, ()=>{
return {
event: HMR_ACTIONS_SENT_TO_BROWSER.MIDDLEWARE_CHANGES
};
});
}
} else {
manifestLoader.deleteMiddlewareManifest(getEntryKey('root', 'server', 'middleware'));
dev.serverFields.actualMiddlewareFile = undefined;
dev.serverFields.middleware = undefined;
}
await dev.hooks.propagateServerField('actualMiddlewareFile', dev.serverFields.actualMiddlewareFile);
await dev.hooks.propagateServerField('middleware', dev.serverFields.middleware);
}
async function handleEntrypointsDevCleanup({ currentEntryIssues, currentEntrypoints, assetMapper, changeSubscriptions, clients, clientStates, hooks }) {
// this needs to be first as `hasEntrypointForKey` uses the `assetMapper`
for (const key of assetMapper.keys()){
if (!hasEntrypointForKey(currentEntrypoints, key, assetMapper)) {
assetMapper.delete(key);
}
}
for (const key of changeSubscriptions.keys()){
// middleware is handled separately
if (!hasEntrypointForKey(currentEntrypoints, key, assetMapper)) {
await hooks.unsubscribeFromChanges(key);
}
}
for (const [key] of currentEntryIssues){
if (!hasEntrypointForKey(currentEntrypoints, key, assetMapper)) {
currentEntryIssues.delete(key);
}
}
for (const client of clients){
const state = clientStates.get(client);
if (!state) {
continue;
}
for (const key of state.clientIssues.keys()){
if (!hasEntrypointForKey(currentEntrypoints, key, assetMapper)) {
state.clientIssues.delete(key);
}
}
for (const id of state.subscriptions.keys()){
if (!hasEntrypointForKey(currentEntrypoints, getEntryKey('assets', 'client', id), assetMapper)) {
hooks.unsubscribeFromHmrEvents(client, id);
}
}
}
}
export async function handlePagesErrorRoute({ currentEntryIssues, entrypoints, manifestLoader, devRewrites, productionRewrites, logErrors, hooks }) {
if (entrypoints.global.app) {
const key = getEntryKey('pages', 'server', '_app');
const writtenEndpoint = await entrypoints.global.app.writeToDisk();
hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
hooks.subscribeToChanges(key, false, entrypoints.global.app, ()=>{
// There's a special case for this in `../client/page-bootstrap.ts`.
// https://github.com/vercel/next.js/blob/08d7a7e5189a835f5dcb82af026174e587575c0e/packages/next/src/client/page-bootstrap.ts#L69-L71
return {
event: HMR_ACTIONS_SENT_TO_BROWSER.CLIENT_CHANGES
};
}, ()=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: '_app has changed (error route)'
};
});
processIssues(currentEntryIssues, key, writtenEndpoint, false, logErrors);
}
await manifestLoader.loadBuildManifest('_app');
await manifestLoader.loadPagesManifest('_app');
await manifestLoader.loadFontManifest('_app');
if (entrypoints.global.document) {
const key = getEntryKey('pages', 'server', '_document');
const writtenEndpoint = await entrypoints.global.document.writeToDisk();
hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
hooks.subscribeToChanges(key, false, entrypoints.global.document, ()=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: '_document has changed (error route)'
};
}, (e)=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in _document subscription (error route): ${e}`
};
});
processIssues(currentEntryIssues, key, writtenEndpoint, false, logErrors);
}
await manifestLoader.loadPagesManifest('_document');
if (entrypoints.global.error) {
const key = getEntryKey('pages', 'server', '_error');
const writtenEndpoint = await entrypoints.global.error.writeToDisk();
hooks.handleWrittenEndpoint(key, writtenEndpoint, false);
hooks.subscribeToChanges(key, false, entrypoints.global.error, ()=>{
// There's a special case for this in `../client/page-bootstrap.ts`.
// https://github.com/vercel/next.js/blob/08d7a7e5189a835f5dcb82af026174e587575c0e/packages/next/src/client/page-bootstrap.ts#L69-L71
return {
event: HMR_ACTIONS_SENT_TO_BROWSER.CLIENT_CHANGES
};
}, (e)=>{
return {
action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD_PAGE,
data: `error in _error subscription: ${e}`
};
});
processIssues(currentEntryIssues, key, writtenEndpoint, false, logErrors);
}
await manifestLoader.loadBuildManifest('_error');
await manifestLoader.loadPagesManifest('_error');
await manifestLoader.loadFontManifest('_error');
await manifestLoader.writeManifests({
devRewrites,
productionRewrites,
entrypoints
});
}
export function removeRouteSuffix(route) {
return route.replace(/\/route$/, '');
}
export function addRouteSuffix(route) {
return route + '/route';
}
export function addMetadataIdToRoute(route) {
return route + '/[__metadata_id__]';
}
// Since turbopack will create app pages/route entries based on the structure,
// which means the entry keys are based on file names.
// But for special metadata conventions we'll change the page/pathname to a different path.
// So we need this helper to map the new path back to original turbopack entry key.
export function normalizedPageToTurbopackStructureRoute(route, ext) {
let entrypointKey = route;
if (isMetadataRoute(entrypointKey)) {
entrypointKey = entrypointKey.endsWith('/route') ? entrypointKey.slice(0, -'/route'.length) : entrypointKey;
if (ext) {
if (entrypointKey.endsWith('/[__metadata_id__]')) {
entrypointKey = entrypointKey.slice(0, -'/[__metadata_id__]'.length);
}
if (entrypointKey.endsWith('/sitemap.xml') && ext !== '.xml') {
// For dynamic sitemap route, remove the extension
entrypointKey = entrypointKey.slice(0, -'.xml'.length);
}
}
entrypointKey = entrypointKey + '/route';
}
return entrypointKey;
}
//# sourceMappingURL=turbopack-utils.js.map

File diff suppressed because one or more lines are too long