This commit is contained in:
2026-04-12 01:02:14 +08:00
parent 509487f155
commit 9b053e302b
14085 changed files with 2680009 additions and 12 deletions

View File

@@ -0,0 +1,428 @@
import { cyan, dim, red, yellow } from '../../../lib/picocolors';
import util from 'util';
import { getConsoleLocation, getSourceMappedStackFrames, withLocation } from './source-map';
import { UNDEFINED_MARKER } from '../../../next-devtools/shared/forward-logs-shared';
export function restoreUndefined(x) {
if (x === UNDEFINED_MARKER) return undefined;
if (Array.isArray(x)) return x.map(restoreUndefined);
if (x && typeof x === 'object') {
for(let k in x){
x[k] = restoreUndefined(x[k]);
}
}
return x;
}
const methods = [
'log',
'info',
'warn',
'debug',
'table',
'error',
'assert',
'dir',
'dirxml',
'group',
'groupCollapsed',
'groupEnd'
];
const methodsToSkipInspect = new Set([
'table',
'dir',
'dirxml',
'group',
'groupCollapsed',
'groupEnd'
]);
// we aren't overriding console, we're just making a (slightly convoluted) helper for replaying user console methods
const forwardConsole = {
...console,
...Object.fromEntries(methods.map((method)=>[
method,
(...args)=>console[method](...args.map((arg)=>methodsToSkipInspect.has(method) || typeof arg !== 'object' || arg === null ? arg : util.inspect(arg, {
depth: Infinity,
colors: true
})))
]))
};
async function deserializeArgData(arg) {
try {
// we want undefined to be represented as it would be in the browser from the user's perspective (otherwise it would be stripped away/shown as null)
if (arg === UNDEFINED_MARKER) {
return restoreUndefined(arg);
}
return restoreUndefined(JSON.parse(arg));
} catch {
return arg;
}
}
const colorError = (mapped, config)=>{
const colorFn = (config == null ? void 0 : config.applyColor) === undefined || config.applyColor ? red : (x)=>x;
switch(mapped.kind){
case 'mapped-stack':
case 'stack':
{
return ((config == null ? void 0 : config.prefix) ? colorFn(config == null ? void 0 : config.prefix) : '') + `\n${colorFn(mapped.stack)}`;
}
case 'with-frame-code':
{
return ((config == null ? void 0 : config.prefix) ? colorFn(config == null ? void 0 : config.prefix) : '') + `\n${colorFn(mapped.stack)}\n${mapped.frameCode}`;
}
// a more sophisticated version of this allows the user to config if they want ignored frames (but we need to be sure to source map them)
case 'all-ignored':
{
return (config == null ? void 0 : config.prefix) ? colorFn(config == null ? void 0 : config.prefix) : '';
}
default:
{}
}
mapped;
};
function processConsoleFormatStrings(args) {
/**
* this handles the case formatting is applied to the console log
* otherwise we will see the format specifier directly in the terminal output
*/ if (args.length > 0 && typeof args[0] === 'string') {
const formatString = args[0];
if (formatString.includes('%s') || formatString.includes('%d') || formatString.includes('%i') || formatString.includes('%f') || formatString.includes('%o') || formatString.includes('%O') || formatString.includes('%c')) {
try {
const formatted = util.format(...args);
return [
formatted
];
} catch {
return args;
}
}
}
return args;
}
// in the case of logging errors, we want to strip formatting
// modifiers since we apply our own custom coloring to error
// stacks and code blocks, and otherwise it would conflict
// and cause awful output
export function stripFormatSpecifiers(args) {
if (args.length === 0 || typeof args[0] !== 'string') return args;
const fmtIn = String(args[0]);
const rest = args.slice(1);
if (!fmtIn.includes('%')) return args;
let fmtOut = '';
let argPtr = 0;
for(let i = 0; i < fmtIn.length; i++){
if (fmtIn[i] !== '%') {
fmtOut += fmtIn[i];
continue;
}
if (fmtIn[i + 1] === '%') {
fmtOut += '%';
i++;
continue;
}
const token = fmtIn[++i];
if (!token) {
fmtOut += '%';
continue;
}
if ('csdifoOj'.includes(token) || token === 'O') {
if (argPtr < rest.length) {
if (token === 'c') {
argPtr++;
} else if (token === 'o' || token === 'O' || token === 'j') {
const obj = rest[argPtr++];
fmtOut += util.inspect(obj, {
depth: 2,
colors: false
});
} else {
// string(...) is safe for remaining specifiers
fmtOut += String(rest[argPtr++]);
}
}
continue;
}
fmtOut += '%' + token;
}
const result = [
fmtOut
];
if (argPtr < rest.length) {
result.push(...rest.slice(argPtr));
}
return result;
}
async function prepareFormattedErrorArgs(entry, ctx, distDir) {
const mapped = await getSourceMappedStackFrames(entry.stack, ctx, distDir);
return [
colorError(mapped, {
prefix: entry.prefix
})
];
}
async function prepareConsoleArgs(entry, ctx, distDir) {
const deserialized = await Promise.all(entry.args.map(async (arg)=>{
if (arg.kind === 'arg') {
const data = await deserializeArgData(arg.data);
if (entry.method === 'warn' && typeof data === 'string') {
return yellow(data);
}
return data;
}
if (!arg.stack) return red(arg.prefix);
const mapped = await getSourceMappedStackFrames(arg.stack, ctx, distDir);
return colorError(mapped, {
prefix: arg.prefix,
applyColor: false
});
}));
return processConsoleFormatStrings(deserialized);
}
async function prepareConsoleErrorArgs(entry, ctx, distDir) {
const deserialized = await Promise.all(entry.args.map(async (arg)=>{
if (arg.kind === 'arg') {
if (arg.isRejectionMessage) return red(arg.data);
return deserializeArgData(arg.data);
}
if (!arg.stack) return red(arg.prefix);
const mapped = await getSourceMappedStackFrames(arg.stack, ctx, distDir);
return colorError(mapped, {
prefix: arg.prefix
});
}));
const mappedStack = await getSourceMappedStackFrames(entry.consoleErrorStack, ctx, distDir);
/**
* don't show the stack + codeblock when there are errors present, since:
* - it will look overwhelming to see 2 stacks and 2 code blocks
* - the user already knows where the console.error is at because we append the location
*/ const location = getConsoleLocation(mappedStack);
if (entry.args.some((a)=>a.kind === 'formatted-error-arg')) {
const result = stripFormatSpecifiers(deserialized);
if (location) {
result.push(dim(`(${location})`));
}
return result;
}
const result = [
...processConsoleFormatStrings(deserialized),
colorError(mappedStack)
];
if (location) {
result.push(dim(`(${location})`));
}
return result;
}
async function handleTable(entry, browserPrefix, ctx, distDir) {
const deserializedArgs = await Promise.all(entry.args.map(async (arg)=>{
if (arg.kind === 'formatted-error-arg') {
return {
stack: arg.stack
};
}
return deserializeArgData(arg.data);
}));
const location = await (async ()=>{
if (!entry.consoleMethodStack) {
return;
}
const frames = await getSourceMappedStackFrames(entry.consoleMethodStack, ctx, distDir);
return getConsoleLocation(frames);
})();
// we can't inline pass browser prefix, but it looks better multiline for table anyways
forwardConsole.log(browserPrefix);
forwardConsole.table(...deserializedArgs);
if (location) {
forwardConsole.log(dim(`(${location})`));
}
}
async function handleTrace(entry, browserPrefix, ctx, distDir) {
const deserializedArgs = await Promise.all(entry.args.map(async (arg)=>{
if (arg.kind === 'formatted-error-arg') {
if (!arg.stack) return red(arg.prefix);
const mapped = await getSourceMappedStackFrames(arg.stack, ctx, distDir);
return colorError(mapped, {
prefix: arg.prefix
});
}
return deserializeArgData(arg.data);
}));
if (!entry.consoleMethodStack) {
forwardConsole.log(browserPrefix, ...deserializedArgs, '[Trace unavailable]');
return;
}
// TODO(rob): refactor so we can re-use result and not re-run the entire source map to avoid trivial post processing
const [mapped, mappedIgnored] = await Promise.all([
getSourceMappedStackFrames(entry.consoleMethodStack, ctx, distDir, false),
getSourceMappedStackFrames(entry.consoleMethodStack, ctx, distDir)
]);
const location = getConsoleLocation(mappedIgnored);
forwardConsole.log(browserPrefix, ...deserializedArgs, `\n${mapped.stack}`, ...location ? [
`\n${dim(`(${location})`)}`
] : []);
}
async function handleDir(entry, browserPrefix, ctx, distDir) {
const loggableEntry = await prepareConsoleArgs(entry, ctx, distDir);
const consoleMethod = forwardConsole[entry.method] || forwardConsole.log;
if (entry.consoleMethodStack) {
const mapped = await getSourceMappedStackFrames(entry.consoleMethodStack, ctx, distDir);
const location = dim(`(${getConsoleLocation(mapped)})`);
const originalWrite = process.stdout.write.bind(process.stdout);
let captured = '';
process.stdout.write = (chunk)=>{
captured += chunk;
return true;
};
try {
consoleMethod(...loggableEntry);
} finally{
process.stdout.write = originalWrite;
}
const preserved = captured.replace(/\r?\n$/, '');
originalWrite(`${browserPrefix}${preserved} ${location}\n`);
return;
}
consoleMethod(browserPrefix, ...loggableEntry);
}
async function handleDefaultConsole(entry, browserPrefix, ctx, distDir, config) {
const loggableEntry = await prepareConsoleArgs(entry, ctx, distDir);
const withStackEntry = await withLocation({
original: loggableEntry,
stack: entry.consoleMethodStack || null
}, ctx, distDir, config);
const consoleMethod = forwardConsole[entry.method] || forwardConsole.log;
consoleMethod(browserPrefix, ...withStackEntry);
}
export async function handleLog(entries, ctx, distDir, config) {
const browserPrefix = cyan('[browser]');
for (const entry of entries){
try {
switch(entry.kind){
case 'console':
{
switch(entry.method){
case 'table':
{
// timeout based abort on source mapping result
await handleTable(entry, browserPrefix, ctx, distDir);
break;
}
// ignore frames
case 'trace':
{
await handleTrace(entry, browserPrefix, ctx, distDir);
break;
}
case 'dir':
{
await handleDir(entry, browserPrefix, ctx, distDir);
break;
}
case 'dirxml':
{
// xml log thing maybe needs an impl
// fallthrough
}
case 'group':
case 'groupCollapsed':
case 'groupEnd':
{
// [browser] undefined (app/page.tsx:8:11) console.group
// fallthrough
}
case 'assert':
{
// check console assert
// fallthrough
}
case 'log':
case 'info':
case 'debug':
case 'error':
case 'warn':
{
await handleDefaultConsole(entry, browserPrefix, ctx, distDir, config);
break;
}
default:
{
entry;
}
}
break;
}
// any logged errors are anything that are logged as "red" in the browser but aren't only an Error (console.error, Promise.reject(100))
case 'any-logged-error':
{
const consoleArgs = await prepareConsoleErrorArgs(entry, ctx, distDir);
forwardConsole.error(browserPrefix, ...consoleArgs);
break;
}
// formatted error is an explicit error event (rejections, uncaught errors)
case 'formatted-error':
{
const formattedArgs = await prepareFormattedErrorArgs(entry, ctx, distDir);
forwardConsole.error(browserPrefix, ...formattedArgs);
break;
}
default:
{}
}
} catch {
switch(entry.kind){
case 'any-logged-error':
{
const consoleArgs = await prepareConsoleErrorArgs(entry, ctx, distDir);
forwardConsole.error(browserPrefix, ...consoleArgs);
break;
}
case 'console':
{
const consoleMethod = forwardConsole[entry.method] || forwardConsole.log;
const consoleArgs = await prepareConsoleArgs(entry, ctx, distDir);
consoleMethod(browserPrefix, ...consoleArgs);
break;
}
case 'formatted-error':
{
forwardConsole.error(browserPrefix, `${entry.prefix}\n`, entry.stack);
break;
}
default:
{}
}
}
}
}
// the data is used later when we need to get sourcemaps for error stacks
export async function receiveBrowserLogsWebpack(opts) {
const { entries, router, sourceType, clientStats, serverStats, edgeServerStats, rootDirectory, distDir } = opts;
const isAppDirectory = router === 'app';
const isServer = sourceType === 'server';
const isEdgeServer = sourceType === 'edge-server';
const ctx = {
bundler: 'webpack',
isServer,
isEdgeServer,
isAppDirectory,
clientStats,
serverStats,
edgeServerStats,
rootDirectory
};
await handleLog(entries, ctx, distDir, opts.config);
}
export async function receiveBrowserLogsTurbopack(opts) {
const { entries, router, sourceType, project, projectPath, distDir } = opts;
const isAppDirectory = router === 'app';
const isServer = sourceType === 'server';
const isEdgeServer = sourceType === 'edge-server';
const ctx = {
bundler: 'turbopack',
project,
projectPath,
isServer,
isEdgeServer,
isAppDirectory
};
await handleLog(entries, ctx, distDir, opts.config);
}
//# sourceMappingURL=receive-logs.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,193 @@
import { getOriginalStackFrames as getOriginalStackFramesWebpack } from '../middleware-webpack';
import { getOriginalStackFrames as getOriginalStackFramesTurbopack } from '../middleware-turbopack';
import { dim } from '../../../lib/picocolors';
import { parseStack } from '../../lib/parse-stack';
import path from 'path';
import { LRUCache } from '../../lib/lru-cache';
// TODO: handle server vs browser error source mapping correctly
export async function mapFramesUsingBundler(frames, ctx) {
switch(ctx.bundler){
case 'webpack':
{
const { isServer, isEdgeServer, isAppDirectory, clientStats, serverStats, edgeServerStats, rootDirectory } = ctx;
const res = await getOriginalStackFramesWebpack({
isServer,
isEdgeServer,
isAppDirectory,
frames,
clientStats,
serverStats,
edgeServerStats,
rootDirectory
});
return res;
}
case 'turbopack':
{
const { project, projectPath, isServer, isEdgeServer, isAppDirectory } = ctx;
const res = await getOriginalStackFramesTurbopack({
project,
projectPath,
frames,
isServer,
isEdgeServer,
isAppDirectory
});
return res;
}
default:
{
return null;
}
}
}
// converts _next/static/chunks/... to file:///.next/static/chunks/... for parseStack
// todo: where does next dev overlay handle this case and re-use that logic
function preprocessStackTrace(stackTrace, distDir) {
return stackTrace.split('\n').map((line)=>{
const match = line.match(/^(\s*at\s+.*?)\s+\(([^)]+)\)$/);
if (match) {
const [, prefix, location] = match;
if (location.startsWith('_next/static/') && distDir) {
const normalizedDistDir = distDir.replace(/\\/g, '/').replace(/\/$/, '');
const absolutePath = normalizedDistDir + '/' + location.slice('_next/'.length);
const fileUrl = `file://${path.resolve(absolutePath)}`;
return `${prefix} (${fileUrl})`;
}
}
return line;
}).join('\n');
}
const cache = new LRUCache(25);
async function getSourceMappedStackFramesInternal(stackTrace, ctx, distDir, ignore = true) {
try {
var _filteredFrames_find;
const normalizedStack = preprocessStackTrace(stackTrace, distDir);
const frames = parseStack(normalizedStack, distDir);
if (frames.length === 0) {
return {
kind: 'stack',
stack: stackTrace
};
}
const mappingResults = await mapFramesUsingBundler(frames, ctx);
const processedFrames = mappingResults.map((result, index)=>({
result,
originalFrame: frames[index]
})).map(({ result, originalFrame })=>{
var _originalStackFrame_file;
if (result.status === 'rejected') {
return {
kind: 'rejected',
frameText: formatStackFrame(originalFrame),
codeFrame: null
};
}
const { originalStackFrame, originalCodeFrame } = result.value;
if ((originalStackFrame == null ? void 0 : originalStackFrame.ignored) && ignore) {
return {
kind: 'ignored'
};
}
// should we apply this generally to dev overlay (dev overlay does not ignore chrome-extension://)
if (originalStackFrame == null ? void 0 : (_originalStackFrame_file = originalStackFrame.file) == null ? void 0 : _originalStackFrame_file.startsWith('chrome-extension://')) {
return {
kind: 'ignored'
};
}
return {
kind: 'success',
// invariant: if result is not rejected and not ignored, then original stack frame exists
// verifiable by tracing `getOriginalStackFrame`. The invariant exists because of bad types
frameText: formatStackFrame(originalStackFrame),
codeFrame: originalCodeFrame
};
});
const allIgnored = processedFrames.every((frame)=>frame.kind === 'ignored');
// we want to handle **all** ignored vs all/some rejected differently
// if all are ignored we should show no frames
// if all are rejected, we want to fallback to showing original stack frames
if (allIgnored) {
return {
kind: 'all-ignored'
};
}
const filteredFrames = processedFrames.filter((frame)=>frame.kind !== 'ignored');
if (filteredFrames.length === 0) {
return {
kind: 'stack',
stack: stackTrace
};
}
const stackOutput = filteredFrames.map((frame)=>frame.frameText).join('\n');
const firstFrameCode = (_filteredFrames_find = filteredFrames.find((frame)=>frame.codeFrame)) == null ? void 0 : _filteredFrames_find.codeFrame;
if (firstFrameCode) {
return {
kind: 'with-frame-code',
frameCode: firstFrameCode,
stack: stackOutput,
frames: filteredFrames
};
}
// i don't think this a real case, but good for exhaustion
return {
kind: 'mapped-stack',
stack: stackOutput,
frames: filteredFrames
};
} catch (error) {
return {
kind: 'stack',
stack: stackTrace
};
}
}
// todo: cache the actual async call, not the wrapper with post processing
export async function getSourceMappedStackFrames(stackTrace, ctx, distDir, ignore = true) {
const cacheKey = `sm_${stackTrace}-${ctx.bundler}-${ctx.isAppDirectory}-${ctx.isEdgeServer}-${ctx.isServer}-${distDir}-${ignore}`;
const cacheItem = cache.get(cacheKey);
if (cacheItem) {
return cacheItem;
}
const result = await getSourceMappedStackFramesInternal(stackTrace, ctx, distDir, ignore);
cache.set(cacheKey, result);
return result;
}
function formatStackFrame(frame) {
const functionName = frame.methodName || '<anonymous>';
const location = frame.file && frame.line1 ? `${frame.file}:${frame.line1}${frame.column1 ? `:${frame.column1}` : ''}` : frame.file || '<unknown>';
return ` at ${functionName} (${location})`;
}
// appends the source mapped location of the console method
export const withLocation = async ({ original, stack }, ctx, distDir, config)=>{
if (typeof config === 'object' && config.showSourceLocation === false) {
return original;
}
if (!stack) {
return original;
}
const res = await getSourceMappedStackFrames(stack, ctx, distDir);
const location = getConsoleLocation(res);
if (!location) {
return original;
}
return [
...original,
dim(`(${location})`)
];
};
export const getConsoleLocation = (mapped)=>{
if (mapped.kind !== 'mapped-stack' && mapped.kind !== 'with-frame-code') {
return null;
}
const first = mapped.frames.at(0);
if (!first) {
return null;
}
// we don't want to show the name of parent function (at <fn> thing in stack), just source location for minimal noise
const match = first.frameText.match(/\(([^)]+)\)/);
const locationText = match ? match[1] : first.frameText;
return locationText;
};
//# sourceMappingURL=source-map.js.map

File diff suppressed because one or more lines are too long

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","ignoreList":[0]}

View File

@@ -0,0 +1,85 @@
import fs from 'fs/promises';
import path from 'path';
import url from 'url';
import dataUriToBuffer from 'next/dist/compiled/data-uri-to-buffer';
function getSourceMapUrl(fileContents) {
const regex = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/gm;
let match = null;
for(;;){
let next = regex.exec(fileContents);
if (next == null) {
break;
}
match = next;
}
if (!(match && match[1])) {
return null;
}
return match[1].toString();
}
export async function getSourceMapFromFile(filename) {
filename = filename.startsWith('file://') ? url.fileURLToPath(filename) : filename;
let fileContents;
try {
fileContents = await fs.readFile(filename, 'utf-8');
} catch (error) {
throw Object.defineProperty(new Error(`Failed to read file contents of ${filename}.`, {
cause: error
}), "__NEXT_ERROR_CODE", {
value: "E466",
enumerable: false,
configurable: true
});
}
const sourceUrl = getSourceMapUrl(fileContents);
if (!sourceUrl) {
return undefined;
}
if (sourceUrl.startsWith('data:')) {
let buffer;
try {
buffer = dataUriToBuffer(sourceUrl);
} catch (error) {
throw Object.defineProperty(new Error(`Failed to parse source map URL for ${filename}.`, {
cause: error
}), "__NEXT_ERROR_CODE", {
value: "E199",
enumerable: false,
configurable: true
});
}
if (buffer.type !== 'application/json') {
throw Object.defineProperty(new Error(`Unknown source map type for ${filename}: ${buffer.typeFull}.`), "__NEXT_ERROR_CODE", {
value: "E113",
enumerable: false,
configurable: true
});
}
try {
return JSON.parse(buffer.toString());
} catch (error) {
throw Object.defineProperty(new Error(`Failed to parse source map for ${filename}.`, {
cause: error
}), "__NEXT_ERROR_CODE", {
value: "E318",
enumerable: false,
configurable: true
});
}
}
const sourceMapFilename = path.resolve(path.dirname(filename), decodeURIComponent(sourceUrl));
try {
const sourceMapContents = await fs.readFile(sourceMapFilename, 'utf-8');
return JSON.parse(sourceMapContents.toString());
} catch (error) {
throw Object.defineProperty(new Error(`Failed to parse source map ${sourceMapFilename}.`, {
cause: error
}), "__NEXT_ERROR_CODE", {
value: "E220",
enumerable: false,
configurable: true
});
}
}
//# sourceMappingURL=get-source-map-from-file.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/dev/get-source-map-from-file.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\nimport url from 'url'\nimport type { RawSourceMap } from 'next/dist/compiled/source-map08'\nimport dataUriToBuffer from 'next/dist/compiled/data-uri-to-buffer'\n\nfunction getSourceMapUrl(fileContents: string): string | null {\n const regex = /\\/\\/[#@] ?sourceMappingURL=([^\\s'\"]+)\\s*$/gm\n let match = null\n for (;;) {\n let next = regex.exec(fileContents)\n if (next == null) {\n break\n }\n match = next\n }\n if (!(match && match[1])) {\n return null\n }\n return match[1].toString()\n}\n\nexport async function getSourceMapFromFile(\n filename: string\n): Promise<RawSourceMap | undefined> {\n filename = filename.startsWith('file://')\n ? url.fileURLToPath(filename)\n : filename\n\n let fileContents: string\n\n try {\n fileContents = await fs.readFile(filename, 'utf-8')\n } catch (error) {\n throw new Error(`Failed to read file contents of ${filename}.`, {\n cause: error,\n })\n }\n\n const sourceUrl = getSourceMapUrl(fileContents)\n\n if (!sourceUrl) {\n return undefined\n }\n\n if (sourceUrl.startsWith('data:')) {\n let buffer: dataUriToBuffer.MimeBuffer\n\n try {\n buffer = dataUriToBuffer(sourceUrl)\n } catch (error) {\n throw new Error(`Failed to parse source map URL for ${filename}.`, {\n cause: error,\n })\n }\n\n if (buffer.type !== 'application/json') {\n throw new Error(\n `Unknown source map type for ${filename}: ${buffer.typeFull}.`\n )\n }\n\n try {\n return JSON.parse(buffer.toString())\n } catch (error) {\n throw new Error(`Failed to parse source map for ${filename}.`, {\n cause: error,\n })\n }\n }\n\n const sourceMapFilename = path.resolve(\n path.dirname(filename),\n decodeURIComponent(sourceUrl)\n )\n\n try {\n const sourceMapContents = await fs.readFile(sourceMapFilename, 'utf-8')\n\n return JSON.parse(sourceMapContents.toString())\n } catch (error) {\n throw new Error(`Failed to parse source map ${sourceMapFilename}.`, {\n cause: error,\n })\n }\n}\n"],"names":["fs","path","url","dataUriToBuffer","getSourceMapUrl","fileContents","regex","match","next","exec","toString","getSourceMapFromFile","filename","startsWith","fileURLToPath","readFile","error","Error","cause","sourceUrl","undefined","buffer","type","typeFull","JSON","parse","sourceMapFilename","resolve","dirname","decodeURIComponent","sourceMapContents"],"mappings":"AAAA,OAAOA,QAAQ,cAAa;AAC5B,OAAOC,UAAU,OAAM;AACvB,OAAOC,SAAS,MAAK;AAErB,OAAOC,qBAAqB,wCAAuC;AAEnE,SAASC,gBAAgBC,YAAoB;IAC3C,MAAMC,QAAQ;IACd,IAAIC,QAAQ;IACZ,OAAS;QACP,IAAIC,OAAOF,MAAMG,IAAI,CAACJ;QACtB,IAAIG,QAAQ,MAAM;YAChB;QACF;QACAD,QAAQC;IACV;IACA,IAAI,CAAED,CAAAA,SAASA,KAAK,CAAC,EAAE,AAAD,GAAI;QACxB,OAAO;IACT;IACA,OAAOA,KAAK,CAAC,EAAE,CAACG,QAAQ;AAC1B;AAEA,OAAO,eAAeC,qBACpBC,QAAgB;IAEhBA,WAAWA,SAASC,UAAU,CAAC,aAC3BX,IAAIY,aAAa,CAACF,YAClBA;IAEJ,IAAIP;IAEJ,IAAI;QACFA,eAAe,MAAML,GAAGe,QAAQ,CAACH,UAAU;IAC7C,EAAE,OAAOI,OAAO;QACd,MAAM,qBAEJ,CAFI,IAAIC,MAAM,CAAC,gCAAgC,EAAEL,SAAS,CAAC,CAAC,EAAE;YAC9DM,OAAOF;QACT,IAFM,qBAAA;mBAAA;wBAAA;0BAAA;QAEL;IACH;IAEA,MAAMG,YAAYf,gBAAgBC;IAElC,IAAI,CAACc,WAAW;QACd,OAAOC;IACT;IAEA,IAAID,UAAUN,UAAU,CAAC,UAAU;QACjC,IAAIQ;QAEJ,IAAI;YACFA,SAASlB,gBAAgBgB;QAC3B,EAAE,OAAOH,OAAO;YACd,MAAM,qBAEJ,CAFI,IAAIC,MAAM,CAAC,mCAAmC,EAAEL,SAAS,CAAC,CAAC,EAAE;gBACjEM,OAAOF;YACT,IAFM,qBAAA;uBAAA;4BAAA;8BAAA;YAEL;QACH;QAEA,IAAIK,OAAOC,IAAI,KAAK,oBAAoB;YACtC,MAAM,qBAEL,CAFK,IAAIL,MACR,CAAC,4BAA4B,EAAEL,SAAS,EAAE,EAAES,OAAOE,QAAQ,CAAC,CAAC,CAAC,GAD1D,qBAAA;uBAAA;4BAAA;8BAAA;YAEN;QACF;QAEA,IAAI;YACF,OAAOC,KAAKC,KAAK,CAACJ,OAAOX,QAAQ;QACnC,EAAE,OAAOM,OAAO;YACd,MAAM,qBAEJ,CAFI,IAAIC,MAAM,CAAC,+BAA+B,EAAEL,SAAS,CAAC,CAAC,EAAE;gBAC7DM,OAAOF;YACT,IAFM,qBAAA;uBAAA;4BAAA;8BAAA;YAEL;QACH;IACF;IAEA,MAAMU,oBAAoBzB,KAAK0B,OAAO,CACpC1B,KAAK2B,OAAO,CAAChB,WACbiB,mBAAmBV;IAGrB,IAAI;QACF,MAAMW,oBAAoB,MAAM9B,GAAGe,QAAQ,CAACW,mBAAmB;QAE/D,OAAOF,KAAKC,KAAK,CAACK,kBAAkBpB,QAAQ;IAC9C,EAAE,OAAOM,OAAO;QACd,MAAM,qBAEJ,CAFI,IAAIC,MAAM,CAAC,2BAA2B,EAAES,kBAAkB,CAAC,CAAC,EAAE;YAClER,OAAOF;QACT,IAFM,qBAAA;mBAAA;wBAAA;0BAAA;QAEL;IACH;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,215 @@
// 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, devToolsConfig){
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 (this.closed) return;
if (!isMiddlewareStats(statsResult)) {
this.onServerInvalid();
this.onServerDone(statsResult);
}
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,
devToolsConfig: this.devToolsConfig
});
}
};
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;
this.devToolsConfig = devToolsConfig || {};
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);
}
updateDevToolsConfig(newConfig) {
this.devToolsConfig = newConfig;
}
}
//# sourceMappingURL=hot-middleware.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,943 @@
import { mkdir, writeFile } from 'fs/promises';
import { join, extname, relative } 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 './middleware-turbopack';
import { PageNotFoundError } from '../../shared/lib/utils';
import { debounce } from '../utils';
import { deleteCache } 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 '../../next-devtools/server/get-next-error-feedback-middleware';
import { formatIssue, getTurbopackJsConfig, isPersistentCachingEnabled, isWellKnownError, processIssues, renderStyledStringToErrorAnsi } from '../../shared/lib/turbopack/utils';
import { getDevOverlayFontMiddleware } from '../../next-devtools/server/font/get-dev-overlay-font-middleware';
import { devIndicatorServerState } from './dev-indicator-server-state';
import { getDisableDevIndicatorMiddleware } from '../../next-devtools/server/dev-indicator-middleware';
import { getRestartDevServerMiddleware } from '../../next-devtools/server/restart-dev-server-middleware';
import { backgroundLogCompilationEvents } from '../../shared/lib/turbopack/compilation-events';
import { getSupportedBrowsers } from '../../build/utils';
import { receiveBrowserLogsTurbopack } from './browser-logs/receive-logs';
import { normalizePath } from '../../lib/normalize-path';
import { devToolsConfigMiddleware, getDevToolsConfig } from '../../next-devtools/server/devtools-config-middleware';
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.5.15"
});
// 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 = getSupportedBrowsers(projectPath, dev);
const currentNodeJsVersion = process.versions.node;
const rootPath = ((_opts_nextConfig_turbopack = opts.nextConfig.turbopack) == null ? void 0 : _opts_nextConfig_turbopack.root) || opts.nextConfig.outputFileTracingRoot || projectPath;
const project = await bindings.turbo.createProject({
rootPath,
projectPath: normalizePath(relative(rootPath, 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,
projectPath,
fetchCacheKeyPrefix: opts.nextConfig.experimental.fetchCacheKeyPrefix,
hasRewrites,
// TODO: Implement
middlewareMatchers: undefined,
rewrites: opts.fsChecker.rewrites
}),
buildId,
encryptionKey,
previewProps: opts.fsChecker.prerenderManifest.preview,
browserslistQuery: supportedBrowsers.join(', '),
noMangling: false,
currentNodeJsVersion
}, {
persistentCaching: isPersistentCachingEnabled(opts.nextConfig),
memoryLimit: (_opts_nextConfig_experimental = opts.nextConfig.experimental) == null ? void 0 : _opts_nextConfig_experimental.turbopackMemoryLimit,
isShortSession: false
});
backgroundLogCompilationEvents(project, {
eventTypes: [
'StartupCacheInvalidationEvent',
'TimingEvent'
]
});
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();
// Not available in:
// - Pages Router (no server-side HMR)
// - Edge Runtime (uses browser runtime which already disposes chunks individually)
if (typeof __next__clear_chunk_cache__ === 'function') {
__next__clear_chunk_cache__();
}
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,
isSrcDir: opts.isSrcDir
}),
getSourceMapMiddleware(project),
getNextErrorFeedbackMiddleware(opts.telemetry),
getDevOverlayFontMiddleware(),
getDisableDevIndicatorMiddleware(),
getRestartDevServerMiddleware({
telemetry: opts.telemetry,
turbopackProject: project
}),
devToolsConfigMiddleware({
distDir,
sendUpdateSignal: (data)=>{
hotReloader.send({
action: HMR_ACTIONS_SENT_TO_BROWSER.DEVTOOLS_CONFIG,
data
});
}
})
];
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', async ({ 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;
case 'browser-logs':
{
if (nextConfig.experimental.browserDebugInfoInTerminal) {
await receiveBrowserLogsTurbopack({
entries: parsedData.entries,
router: parsedData.router,
sourceType: parsedData.sourceType,
project,
projectPath,
distDir,
config: nextConfig.experimental.browserDebugInfoInTerminal
});
}
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 devToolsConfig = await getDevToolsConfig(distDir);
const sync = {
action: HMR_ACTIONS_SENT_TO_BROWSER.SYNC,
errors,
warnings: [],
hash: '',
versionInfo,
debug: {
devtoolsFrontendUrl
},
devIndicator: devIndicatorServerState,
devToolsConfig
};
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, !!nextConfig.experimental.globalNotFound);
// 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,22 @@
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";
HMR_ACTIONS_SENT_TO_BROWSER["DEVTOOLS_CONFIG"] = "devtoolsConfig";
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,93 @@
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)');
case 'miss':
case 'skip':
return yellow(`(cache ${cacheStatus})`);
default:
return 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","ignoreList":[0]}

View File

@@ -0,0 +1,362 @@
import { getOriginalCodeFrame, ignoreListAnonymousStackFramesIfSandwiched } from '../../next-devtools/server/shared';
import { middlewareResponse } from '../../next-devtools/server/middleware-response';
import path from 'path';
import { openFileInEditor } from '../../next-devtools/server/launch-editor';
import { SourceMapConsumer } from 'next/dist/compiled/source-map08';
import { devirtualizeReactServerURL, findApplicableSourceMapPayload } from '../lib/source-maps';
import { findSourceMap } from 'node:module';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { inspect } from 'node:util';
function shouldIgnorePath(modulePath) {
return modulePath.includes('node_modules') || // Only relevant for when Next.js is symlinked e.g. in the Next.js monorepo
modulePath.includes('next/dist') || modulePath.startsWith('node:');
}
const currentSourcesByFile = new Map();
/**
* @returns 1-based lines and 1-based columns
*/ async function batchedTraceSource(project, frame) {
const file = frame.file ? decodeURIComponent(frame.file) : undefined;
if (!file) return;
// For node internals they cannot traced the actual source code with project.traceSource,
// we need an early return to indicate it's ignored to avoid the unknown scheme error from `project.traceSource`.
if (file.startsWith('node:')) {
return {
frame: {
file,
line1: frame.line ?? null,
column1: frame.column ?? null,
methodName: frame.methodName ?? '<unknown>',
ignored: true,
arguments: []
},
source: null
};
}
const currentDirectoryFileUrl = pathToFileURL(process.cwd()).href;
const sourceFrame = await project.traceSource(frame, currentDirectoryFileUrl);
if (!sourceFrame) {
return {
frame: {
file,
line1: frame.line ?? null,
column1: frame.column ?? null,
methodName: frame.methodName ?? '<unknown>',
ignored: shouldIgnorePath(file),
arguments: []
},
source: null
};
}
let source = null;
const originalFile = sourceFrame.originalFile;
// Don't look up source for node_modules or internals. These can often be large bundled files.
const ignored = shouldIgnorePath(originalFile ?? sourceFrame.file) || // isInternal means resource starts with turbopack:///[turbopack]
!!sourceFrame.isInternal;
if (originalFile && !ignored) {
let sourcePromise = currentSourcesByFile.get(originalFile);
if (!sourcePromise) {
sourcePromise = project.getSourceForAsset(originalFile);
currentSourcesByFile.set(originalFile, sourcePromise);
setTimeout(()=>{
// Cache file reads for 100ms, as frames will often reference the same
// files and can be large.
currentSourcesByFile.delete(originalFile);
}, 100);
}
source = await sourcePromise;
}
// TODO: get ignoredList from turbopack source map
const ignorableFrame = {
file: sourceFrame.file,
line1: sourceFrame.line ?? null,
column1: sourceFrame.column ?? null,
methodName: // We ignore the sourcemapped name since it won't be the correct name.
// The callsite will point to the column of the variable name instead of the
// name of the enclosing function.
// TODO(NDX-531): Spy on prepareStackTrace to get the enclosing line number for method name mapping.
frame.methodName ?? '<unknown>',
ignored,
arguments: []
};
return {
frame: ignorableFrame,
source
};
}
function parseFile(fileParam) {
if (!fileParam) {
return undefined;
}
return devirtualizeReactServerURL(fileParam);
}
function createStackFrames(body) {
const { frames, isServer } = body;
return frames.map((frame)=>{
const file = parseFile(frame.file);
if (!file) {
return undefined;
}
return {
file,
methodName: frame.methodName ?? '<unknown>',
line: frame.line1 ?? undefined,
column: frame.column1 ?? undefined,
isServer
};
}).filter((f)=>f !== undefined);
}
function createStackFrame(searchParams) {
const file = parseFile(searchParams.get('file'));
if (!file) {
return undefined;
}
return {
file,
methodName: searchParams.get('methodName') ?? '<unknown>',
line: parseInt(searchParams.get('line1') ?? '0', 10) || undefined,
column: parseInt(searchParams.get('column1') ?? '0', 10) || undefined,
isServer: searchParams.get('isServer') === 'true'
};
}
/**
* @returns 1-based lines and 1-based columns
*/ async function nativeTraceSource(frame) {
const sourceURL = frame.file;
let sourceMapPayload;
try {
var _findSourceMap;
sourceMapPayload = (_findSourceMap = findSourceMap(sourceURL)) == null ? void 0 : _findSourceMap.payload;
} catch (cause) {
throw Object.defineProperty(new Error(`${sourceURL}: Invalid source map. Only conformant source maps can be used to find the original code.`, {
cause
}), "__NEXT_ERROR_CODE", {
value: "E635",
enumerable: false,
configurable: true
});
}
if (sourceMapPayload !== undefined) {
let consumer;
try {
consumer = await new SourceMapConsumer(sourceMapPayload);
} catch (cause) {
throw Object.defineProperty(new Error(`${sourceURL}: Invalid source map. Only conformant source maps can be used to find the original code.`, {
cause
}), "__NEXT_ERROR_CODE", {
value: "E635",
enumerable: false,
configurable: true
});
}
let traced;
try {
const originalPosition = consumer.originalPositionFor({
line: frame.line ?? 1,
// 0-based columns out requires 0-based columns in.
column: (frame.column ?? 1) - 1
});
if (originalPosition.source === null) {
traced = null;
} else {
const sourceContent = consumer.sourceContentFor(originalPosition.source, /* returnNullOnMissing */ true) ?? null;
traced = {
originalPosition,
sourceContent
};
}
} finally{
consumer.destroy();
}
if (traced !== null) {
var // We ignore the sourcemapped name since it won't be the correct name.
// The callsite will point to the column of the variable name instead of the
// name of the enclosing function.
// TODO(NDX-531): Spy on prepareStackTrace to get the enclosing line number for method name mapping.
_frame_methodName_replace, _frame_methodName;
const { originalPosition, sourceContent } = traced;
const applicableSourceMap = findApplicableSourceMapPayload((frame.line ?? 1) - 1, (frame.column ?? 1) - 1, sourceMapPayload);
// TODO(veil): Upstream a method to sourcemap consumer that immediately says if a frame is ignored or not.
let ignored = false;
if (applicableSourceMap === undefined) {
console.error('No applicable source map found in sections for frame', frame);
} else {
var _applicableSourceMap_ignoreList;
// TODO: O(n^2). Consider moving `ignoreList` into a Set
const sourceIndex = applicableSourceMap.sources.indexOf(originalPosition.source);
ignored = ((_applicableSourceMap_ignoreList = applicableSourceMap.ignoreList) == null ? void 0 : _applicableSourceMap_ignoreList.includes(sourceIndex)) ?? // When sourcemap is not available, fallback to checking `frame.file`.
// e.g. In pages router, nextjs server code is not bundled into the page.
shouldIgnorePath(frame.file);
}
const originalStackFrame = {
methodName: ((_frame_methodName = frame.methodName) == null ? void 0 : (_frame_methodName_replace = _frame_methodName.replace('__WEBPACK_DEFAULT_EXPORT__', 'default')) == null ? void 0 : _frame_methodName_replace.replace('__webpack_exports__.', '')) || '<unknown>',
file: originalPosition.source,
line1: originalPosition.line,
column1: originalPosition.column === null ? null : originalPosition.column + 1,
// TODO: c&p from async createOriginalStackFrame but why not frame.arguments?
arguments: [],
ignored
};
return {
frame: originalStackFrame,
source: sourceContent
};
}
}
return undefined;
}
async function createOriginalStackFrame(project, projectPath, frame) {
const traced = await nativeTraceSource(frame) ?? // TODO(veil): When would the bundler know more than native?
// If it's faster, try the bundler first and fall back to native later.
await batchedTraceSource(project, frame);
if (!traced) {
return null;
}
let normalizedStackFrameLocation = traced.frame.file;
if (normalizedStackFrameLocation !== null && normalizedStackFrameLocation.startsWith('file://')) {
normalizedStackFrameLocation = path.relative(projectPath, fileURLToPath(normalizedStackFrameLocation));
}
return {
originalStackFrame: {
arguments: traced.frame.arguments,
file: normalizedStackFrameLocation,
line1: traced.frame.line1,
column1: traced.frame.column1,
ignored: traced.frame.ignored,
methodName: traced.frame.methodName
},
originalCodeFrame: getOriginalCodeFrame(traced.frame, traced.source)
};
}
export function getOverlayMiddleware({ project, projectPath, isSrcDir }) {
return async function(req, res, next) {
const { pathname, searchParams } = new URL(req.url, 'http://n');
if (pathname === '/__nextjs_original-stack-frames') {
if (req.method !== 'POST') {
return middlewareResponse.badRequest(res);
}
const body = await new Promise((resolve, reject)=>{
let data = '';
req.on('data', (chunk)=>{
data += chunk;
});
req.on('end', ()=>resolve(data));
req.on('error', reject);
});
const request = JSON.parse(body);
const result = await getOriginalStackFrames({
project,
projectPath,
frames: request.frames,
isServer: request.isServer,
isEdgeServer: request.isEdgeServer,
isAppDirectory: request.isAppDirectory
});
ignoreListAnonymousStackFramesIfSandwiched(result);
return middlewareResponse.json(res, result);
} else if (pathname === '/__nextjs_launch-editor') {
const isAppRelativePath = searchParams.get('isAppRelativePath') === '1';
let openEditorResult;
if (isAppRelativePath) {
const relativeFilePath = searchParams.get('file') || '';
const appPath = path.join('app', isSrcDir ? 'src' : '', relativeFilePath);
openEditorResult = await openFileInEditor(appPath, 1, 1, projectPath);
} else {
const frame = createStackFrame(searchParams);
if (!frame) return middlewareResponse.badRequest(res);
openEditorResult = await openFileInEditor(frame.file, frame.line ?? 1, frame.column ?? 1, projectPath);
}
if (openEditorResult.error) {
return middlewareResponse.internalServerError(res, openEditorResult.error);
}
if (!openEditorResult.found) {
return middlewareResponse.notFound(res);
}
return middlewareResponse.noContent(res);
}
return next();
};
}
export function getSourceMapMiddleware(project) {
return async function(req, res, next) {
const { pathname, searchParams } = new URL(req.url, 'http://n');
if (pathname !== '/__nextjs_source-map') {
return next();
}
let filename = searchParams.get('filename');
if (!filename) {
return middlewareResponse.badRequest(res);
}
let nativeSourceMap;
try {
nativeSourceMap = findSourceMap(filename);
} catch (cause) {
return middlewareResponse.internalServerError(res, Object.defineProperty(new Error(`${filename}: Invalid source map. Only conformant source maps can be used to find the original code.`, {
cause
}), "__NEXT_ERROR_CODE", {
value: "E635",
enumerable: false,
configurable: true
}));
}
if (nativeSourceMap !== undefined) {
const sourceMapPayload = nativeSourceMap.payload;
return middlewareResponse.json(res, sourceMapPayload);
}
try {
// Turbopack chunk filenames might be URL-encoded.
filename = decodeURI(filename);
} catch {
return middlewareResponse.badRequest(res);
}
if (path.isAbsolute(filename)) {
filename = pathToFileURL(filename).href;
}
try {
const sourceMapString = await project.getSourceMap(filename);
if (sourceMapString) {
return middlewareResponse.jsonString(res, sourceMapString);
}
} catch (cause) {
return middlewareResponse.internalServerError(res, Object.defineProperty(new Error(`Failed to get source map for '${filename}'. This is a bug in Next.js`, {
cause
}), "__NEXT_ERROR_CODE", {
value: "E719",
enumerable: false,
configurable: true
}));
}
middlewareResponse.noContent(res);
};
}
export async function getOriginalStackFrames({ project, projectPath, frames, isServer, isEdgeServer, isAppDirectory }) {
const stackFrames = createStackFrames({
frames,
isServer,
isEdgeServer,
isAppDirectory
});
return Promise.all(stackFrames.map(async (frame)=>{
try {
const stackFrame = await createOriginalStackFrame(project, projectPath, frame);
if (stackFrame === null) {
return {
status: 'rejected',
reason: 'Failed to create original stack frame'
};
}
return {
status: 'fulfilled',
value: stackFrame
};
} catch (error) {
return {
status: 'rejected',
reason: inspect(error, {
colors: false
})
};
}
}));
}
//# sourceMappingURL=middleware-turbopack.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,434 @@
import { findSourceMap } from 'module';
import path from 'path';
import { fileURLToPath, pathToFileURL } from 'url';
import { SourceMapConsumer } from 'next/dist/compiled/source-map08';
import { getSourceMapFromFile } from './get-source-map-from-file';
import { devirtualizeReactServerURL, findApplicableSourceMapPayload, sourceMapIgnoreListsEverything } from '../lib/source-maps';
import { openFileInEditor } from '../../next-devtools/server/launch-editor';
import { getOriginalCodeFrame, ignoreListAnonymousStackFramesIfSandwiched } from '../../next-devtools/server/shared';
import { middlewareResponse } from '../../next-devtools/server/middleware-response';
import { formatFrameSourceFile } from '../../next-devtools/shared/webpack-module-path';
import { inspect } from 'util';
function shouldIgnoreSource(sourceURL) {
return sourceURL.includes('node_modules') || // Only relevant for when Next.js is symlinked e.g. in the Next.js monorepo
sourceURL.includes('next/dist') || sourceURL.startsWith('node:');
}
function getModuleById(id, compilation) {
const { chunkGraph, modules } = compilation;
return [
...modules
].find((module)=>chunkGraph.getModuleId(module) === id);
}
function findModuleNotFoundFromError(errorMessage) {
var _errorMessage_match;
return errorMessage == null ? void 0 : (_errorMessage_match = errorMessage.match(/'([^']+)' module/)) == null ? void 0 : _errorMessage_match[1];
}
function getSourcePath(source) {
if (source.startsWith('file://')) {
return fileURLToPath(source);
}
return source.replace(/^(webpack:\/\/\/|webpack:\/\/|webpack:\/\/_N_E\/)/, '');
}
/**
* @returns 1-based lines and 0-based columns
*/ async function findOriginalSourcePositionAndContent(sourceMap, position) {
let consumer;
try {
consumer = await new SourceMapConsumer(sourceMap);
} catch (cause) {
console.error(Object.defineProperty(new Error(`${sourceMap.file}: Invalid source map. Only conformant source maps can be used to find the original code.`, {
cause
}), "__NEXT_ERROR_CODE", {
value: "E635",
enumerable: false,
configurable: true
}));
return null;
}
try {
const sourcePosition = consumer.originalPositionFor({
line: position.line1 ?? 1,
// 0-based columns out requires 0-based columns in.
column: (position.column1 ?? 1) - 1
});
if (!sourcePosition.source) {
return null;
}
const sourceContent = consumer.sourceContentFor(sourcePosition.source, /* returnNullOnMissing */ true) ?? null;
return {
sourcePosition,
sourceContent
};
} finally{
consumer.destroy();
}
}
export function getIgnoredSources(sourceMap) {
const ignoreList = new Set(sourceMap.ignoreList ?? []);
const moduleFilenames = (sourceMap == null ? void 0 : sourceMap.sources) ?? [];
for(let index = 0; index < moduleFilenames.length; index++){
// bundlerFilePath case: webpack://./app/page.tsx
const webpackSourceURL = moduleFilenames[index];
// Format the path to the normal file path
const formattedFilePath = formatFrameSourceFile(webpackSourceURL);
if (shouldIgnoreSource(formattedFilePath)) {
ignoreList.add(index);
}
}
const ignoredSources = sourceMap.sources.map((source, index)=>{
var _sourceMap_sourcesContent;
return {
url: source,
ignored: ignoreList.has(sourceMap.sources.indexOf(source)),
content: ((_sourceMap_sourcesContent = sourceMap.sourcesContent) == null ? void 0 : _sourceMap_sourcesContent[index]) ?? null
};
});
return ignoredSources;
}
function isIgnoredSource(source, sourcePosition) {
if (sourcePosition.source == null) {
return true;
}
for (const ignoredSource of source.ignoredSources){
if (ignoredSource.ignored && ignoredSource.url === sourcePosition.source) {
return true;
}
}
return false;
}
function findOriginalSourcePositionAndContentFromCompilation(moduleId, importedModule, compilation) {
var _module_buildInfo_importLocByPath, _module_buildInfo;
const module = getModuleById(moduleId, compilation);
return (module == null ? void 0 : (_module_buildInfo = module.buildInfo) == null ? void 0 : (_module_buildInfo_importLocByPath = _module_buildInfo.importLocByPath) == null ? void 0 : _module_buildInfo_importLocByPath.get(importedModule)) ?? null;
}
export async function createOriginalStackFrame({ ignoredByDefault, source, rootDirectory, frame, errorMessage }) {
var // We ignore the sourcemapped name since it won't be the correct name.
// The callsite will point to the column of the variable name instead of the
// name of the enclosing function.
// TODO(NDX-531): Spy on prepareStackTrace to get the enclosing line number for method name mapping.
// default is not a valid identifier in JS so webpack uses a custom variable when it's an unnamed default export
// Resolve it back to `default` for the method name if the source position didn't have the method.
_frame_methodName_replace, _frame_methodName;
const moduleNotFound = findModuleNotFoundFromError(errorMessage);
const result = await (()=>{
if (moduleNotFound) {
if (source.type === 'file') {
return undefined;
}
return findOriginalSourcePositionAndContentFromCompilation(source.moduleId, moduleNotFound, source.compilation);
}
return findOriginalSourcePositionAndContent(source.sourceMap, frame);
})();
if (!result) {
return null;
}
const { sourcePosition, sourceContent } = result;
if (!sourcePosition.source) {
return null;
}
const ignored = ignoredByDefault || isIgnoredSource(source, sourcePosition) || // If the source file is externals, should be excluded even it's not ignored source.
// e.g. webpack://next/dist/.. needs to be ignored
shouldIgnoreSource(source.moduleURL);
const sourcePath = getSourcePath(// When sourcePosition.source is the loader path the modulePath is generally better.
(sourcePosition.source.includes('|') ? source.moduleURL : sourcePosition.source) || source.moduleURL);
const filePath = path.resolve(rootDirectory, sourcePath);
const resolvedFilePath = path.relative(rootDirectory, filePath);
const traced = {
file: resolvedFilePath,
line1: sourcePosition.line,
column1: sourcePosition.column === null ? null : sourcePosition.column + 1,
methodName: (_frame_methodName = frame.methodName) == null ? void 0 : (_frame_methodName_replace = _frame_methodName.replace('__WEBPACK_DEFAULT_EXPORT__', 'default')) == null ? void 0 : _frame_methodName_replace.replace('__webpack_exports__.', ''),
arguments: [],
ignored
};
return {
originalStackFrame: traced,
originalCodeFrame: getOriginalCodeFrame(traced, sourceContent)
};
}
async function getSourceMapFromCompilation(id, compilation) {
try {
const module = getModuleById(id, compilation);
if (!module) {
return undefined;
}
// @ts-expect-error The types for `CodeGenerationResults.get` require a
// runtime to be passed as second argument, but apparently it also works
// without it.
const codeGenerationResult = compilation.codeGenerationResults.get(module);
const source = codeGenerationResult == null ? void 0 : codeGenerationResult.sources.get('javascript');
return (source == null ? void 0 : source.map()) ?? undefined;
} catch (err) {
console.error(`Failed to lookup module by ID ("${id}"):`, err);
return undefined;
}
}
async function getSource(frame, options) {
let sourceURL = frame.file ?? '';
const { getCompilations } = options;
sourceURL = devirtualizeReactServerURL(sourceURL);
let nativeSourceMap;
try {
nativeSourceMap = findSourceMap(sourceURL);
} catch (cause) {
throw Object.defineProperty(new Error(`${sourceURL}: Invalid source map. Only conformant source maps can be used to find the original code.`, {
cause
}), "__NEXT_ERROR_CODE", {
value: "E635",
enumerable: false,
configurable: true
});
}
if (nativeSourceMap !== undefined) {
const sourceMapPayload = nativeSourceMap.payload;
return {
type: 'file',
sourceMap: findApplicableSourceMapPayload((frame.line1 ?? 1) - 1, (frame.column1 ?? 1) - 1, sourceMapPayload),
ignoredSources: getIgnoredSources(// @ts-expect-error -- TODO: Support IndexSourceMap
sourceMapPayload),
moduleURL: sourceURL
};
}
if (path.isAbsolute(sourceURL)) {
sourceURL = pathToFileURL(sourceURL).href;
}
if (sourceURL.startsWith('file:')) {
const sourceMap = await getSourceMapFromFile(sourceURL);
return sourceMap ? {
type: 'file',
sourceMap,
ignoredSources: getIgnoredSources(sourceMap),
moduleURL: sourceURL
} : undefined;
}
// webpack-internal:///./src/hello.tsx => ./src/hello.tsx
// webpack://_N_E/./src/hello.tsx => ./src/hello.tsx
const moduleId = sourceURL.replace(/^(webpack-internal:\/\/\/|webpack:\/\/(_N_E\/)?)/, '').replace(/\?\d+$/, '');
// (rsc)/./src/hello.tsx => ./src/hello.tsx
const moduleURL = moduleId.replace(/^(\(.*\)\/?)/, '');
for (const compilation of getCompilations()){
const sourceMap = await getSourceMapFromCompilation(moduleId, compilation);
if (sourceMap) {
const ignoredSources = getIgnoredSources(sourceMap);
return {
type: 'bundle',
sourceMap,
compilation,
moduleId,
moduleURL,
ignoredSources
};
}
}
return undefined;
}
export async function getOriginalStackFrames({ isServer, isEdgeServer, isAppDirectory, frames, clientStats, serverStats, edgeServerStats, rootDirectory }) {
const frameResponses = await Promise.all(frames.map((frame)=>getOriginalStackFrame({
isServer,
isEdgeServer,
isAppDirectory,
frame,
clientStats,
serverStats,
edgeServerStats,
rootDirectory
}).then((value)=>{
return {
status: 'fulfilled',
value
};
}, (reason)=>{
return {
status: 'rejected',
reason: inspect(reason, {
colors: false
})
};
})));
ignoreListAnonymousStackFramesIfSandwiched(frameResponses);
return frameResponses;
}
async function getOriginalStackFrame({ isServer, isEdgeServer, isAppDirectory, frame, clientStats, serverStats, edgeServerStats, rootDirectory }) {
const filename = frame.file ?? '';
const source = await getSource(frame, {
getCompilations: ()=>{
const compilations = [];
// Try Client Compilation first. In `pages` we leverage
// `isClientError` to check. In `app` it depends on if it's a server
// / client component and when the code throws. E.g. during HTML
// rendering it's the server/edge compilation.
if (!isEdgeServer && !isServer || isAppDirectory) {
var _clientStats;
const compilation = (_clientStats = clientStats()) == null ? void 0 : _clientStats.compilation;
if (compilation) {
compilations.push(compilation);
}
}
// Try Server Compilation. In `pages` this could be something
// imported in getServerSideProps/getStaticProps as the code for
// those is tree-shaken. In `app` this finds server components and
// code that was imported from a server component. It also covers
// when client component code throws during HTML rendering.
if (isServer || isAppDirectory) {
var _serverStats;
const compilation = (_serverStats = serverStats()) == null ? void 0 : _serverStats.compilation;
if (compilation) {
compilations.push(compilation);
}
}
// Try Edge Server Compilation. Both cases are the same as Server
// Compilation, main difference is that it covers `runtime: 'edge'`
// pages/app routes.
if (isEdgeServer || isAppDirectory) {
var _edgeServerStats;
const compilation = (_edgeServerStats = edgeServerStats()) == null ? void 0 : _edgeServerStats.compilation;
if (compilation) {
compilations.push(compilation);
}
}
return compilations;
}
});
let defaultNormalizedStackFrameLocation = frame.file;
if (defaultNormalizedStackFrameLocation !== null && defaultNormalizedStackFrameLocation.startsWith('file://')) {
defaultNormalizedStackFrameLocation = path.relative(rootDirectory, fileURLToPath(defaultNormalizedStackFrameLocation));
}
// This stack frame is used for the one that couldn't locate the source or source mapped frame
const defaultStackFrame = {
file: defaultNormalizedStackFrameLocation,
line1: frame.line1,
column1: frame.column1,
methodName: frame.methodName,
ignored: shouldIgnoreSource(filename),
arguments: []
};
if (!source) {
// return original stack frame with no source map
return {
originalStackFrame: defaultStackFrame,
originalCodeFrame: null
};
}
defaultStackFrame.ignored ||= sourceMapIgnoreListsEverything(source.sourceMap);
const originalStackFrameResponse = await createOriginalStackFrame({
ignoredByDefault: defaultStackFrame.ignored,
frame,
source,
rootDirectory
});
if (!originalStackFrameResponse) {
return {
originalStackFrame: defaultStackFrame,
originalCodeFrame: null
};
}
return originalStackFrameResponse;
}
export function getOverlayMiddleware(options) {
const { rootDirectory, isSrcDir, clientStats, serverStats, edgeServerStats } = options;
return async function(req, res, next) {
const { pathname, searchParams } = new URL(`http://n${req.url}`);
if (pathname === '/__nextjs_original-stack-frames') {
if (req.method !== 'POST') {
return middlewareResponse.badRequest(res);
}
const body = await new Promise((resolve, reject)=>{
let data = '';
req.on('data', (chunk)=>{
data += chunk;
});
req.on('end', ()=>resolve(data));
req.on('error', reject);
});
try {
const { frames, isServer, isEdgeServer, isAppDirectory } = JSON.parse(body);
return middlewareResponse.json(res, await getOriginalStackFrames({
isServer,
isEdgeServer,
isAppDirectory,
frames,
clientStats,
serverStats,
edgeServerStats,
rootDirectory
}));
} catch (err) {
return middlewareResponse.badRequest(res);
}
} else if (pathname === '/__nextjs_launch-editor') {
const frame = {
file: searchParams.get('file'),
methodName: searchParams.get('methodName'),
line1: parseInt(searchParams.get('line1') ?? '1', 10) || 1,
column1: parseInt(searchParams.get('column1') ?? '1', 10) || 1,
arguments: searchParams.getAll('arguments').filter(Boolean)
};
if (!frame.file) return middlewareResponse.badRequest(res);
let openEditorResult;
const isAppRelativePath = searchParams.get('isAppRelativePath') === '1';
if (isAppRelativePath) {
const relativeFilePath = searchParams.get('file') || '';
const appPath = path.join('app', isSrcDir ? 'src' : '', relativeFilePath);
openEditorResult = await openFileInEditor(appPath, 1, 1, rootDirectory);
} else {
// TODO: How do we differentiate layers and actual file paths with round brackets?
// frame files may start with their webpack layer, like (middleware)/middleware.js
const filePath = frame.file.replace(/^\([^)]+\)\//, '');
openEditorResult = await openFileInEditor(filePath, frame.line1, frame.column1 ?? 1, rootDirectory);
}
if (openEditorResult.error) {
console.error('Failed to launch editor:', openEditorResult.error);
return middlewareResponse.internalServerError(res, openEditorResult.error);
}
if (!openEditorResult.found) {
return middlewareResponse.notFound(res);
}
return middlewareResponse.noContent(res);
}
return next();
};
}
export function getSourceMapMiddleware(options) {
const { clientStats, serverStats, edgeServerStats } = options;
return async function(req, res, next) {
const { pathname, searchParams } = new URL(`http://n${req.url}`);
if (pathname !== '/__nextjs_source-map') {
return next();
}
const filename = searchParams.get('filename');
if (!filename) {
return middlewareResponse.badRequest(res);
}
let source;
try {
source = await getSource({
file: filename,
// Webpack doesn't use Index Source Maps
line1: null,
column1: null
}, {
getCompilations: ()=>{
const compilations = [];
for (const stats of [
clientStats(),
serverStats(),
edgeServerStats()
]){
if (stats == null ? void 0 : stats.compilation) {
compilations.push(stats.compilation);
}
}
return compilations;
}
});
} catch (error) {
return middlewareResponse.internalServerError(res, error);
}
if (!source) {
return middlewareResponse.noContent(res);
}
return middlewareResponse.json(res, source.sourceMap);
};
}
//# sourceMappingURL=middleware-webpack.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,674 @@
import { addRequestMeta, getRequestMeta } from '../request-meta';
import * as React from 'react';
import fs from 'fs';
import { Worker } from 'next/dist/compiled/jest-worker';
import { join as pathJoin } from 'path';
import { ampValidation } from '../../build/output';
import { 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, PRERENDER_MANIFEST } 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, fallbackModeToFallbackField } from '../../lib/fallback';
import { ensureInstrumentationRegistered, getInstrumentationModule } from '../lib/router-utils/instrumentation-globals.external';
import { getRouteRegex } from '../../shared/lib/router/utils/route-regex';
// Load ReactDevOverlay only when needed
let PagesDevOverlayBridgeImpl;
const ReactDevOverlay = (props)=>{
if (PagesDevOverlayBridgeImpl === undefined) {
PagesDevOverlayBridgeImpl = require('../../next-devtools/userspace/pages/pages-dev-overlay-setup').PagesDevOverlayBridge;
}
return React.createElement(PagesDevOverlayBridgeImpl, 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;
// Ensure minimum size of 1 for LRU eviction to work correctly
return ((_JSON_stringify = JSON.stringify(value.staticPaths)) == null ? void 0 : _JSON_stringify.length) || 1;
});
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 = async (html, pathname)=>{
var _this_nextConfig_experimental_amp, _this_nextConfig_experimental;
const { getAmpValidatorInstance, getBundledAmpValidatorFilepath } = require('../../export/helpers/get-amp-html-validator');
const validatorPath = ((_this_nextConfig_experimental = this.nextConfig.experimental) == null ? void 0 : (_this_nextConfig_experimental_amp = _this_nextConfig_experimental.amp) == null ? void 0 : _this_nextConfig_experimental_amp.validator) || getBundledAmpValidatorFilepath();
const validator = await getAmpValidatorInstance(validatorPath);
const result = validator.validateString(html);
ampValidation(pathname, result.errors.filter((error)=>{
if (error.severity === 'ERROR') {
// Unclear yet if these actually prevent the page from being indexed by the AMP cache.
// These are coming from React so all we can do is ignore them for now.
// <link rel="expect" blocking="render" />
// https://github.com/ampproject/amphtml/issues/40279
if (error.code === 'DISALLOWED_ATTR' && error.params[0] === 'blocking' && error.params[1] === 'link') {
return false;
}
// <template> without type
// https://github.com/ampproject/amphtml/issues/40280
if (error.code === 'MANDATORY_ATTR_MISSING' && error.params[0] === 'type' && error.params[1] === 'template') {
return false;
}
// <template> without type
// https://github.com/ampproject/amphtml/issues/40280
if (error.code === 'MISSING_REQUIRED_EXTENSION' && error.params[0] === 'template' && error.params[1] === 'amp-mustache') {
return false;
}
return true;
}
return false;
}).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('_')
}));
// TODO: Improve passing of "is running with Turbopack"
const isTurbopack = !!process.env.TURBOPACK;
matchers.push(new DevAppPageRouteMatcherProvider(appDir, extensions, fileReader, isTurbopack));
matchers.push(new DevAppRouteRouteMatcherProvider(appDir, extensions, fileReader, isTurbopack));
}
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);
addRequestMeta(req, 'PagesErrorDebug', this.renderOpts.ErrorDebug);
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 getInstrumentationModule(this.dir, this.nextConfig.distDir);
} catch (err) {
err.message = `An error occurred while loading instrumentation hook: ${err.message}`;
throw err;
}
}
return instrumentationModule;
}
async runInstrumentationHookIfAvailable() {
await ensureInstrumentationRegistered(this.dir, this.nextConfig.distDir);
}
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, urlPathname, 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,
cacheComponents: Boolean(this.nextConfig.experimental.cacheComponents)
},
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(async (res)=>{
var _res_value;
const { prerenderedRoutes, fallbackMode: fallback } = res.value;
if (isAppPath) {
if (this.nextConfig.output === 'export') {
if (!prerenderedRoutes) {
throw Object.defineProperty(new Error(`Page "${page}" is missing exported function "generateStaticParams()", which is required with "output: export" config.`), "__NEXT_ERROR_CODE", {
value: "E353",
enumerable: false,
configurable: true
});
}
if (!prerenderedRoutes.some((item)=>item.pathname === urlPathname)) {
throw Object.defineProperty(new Error(`Page "${page}" is missing param "${pathname}" in "generateStaticParams()", which is required with "output: export" config.`), "__NEXT_ERROR_CODE", {
value: "E443",
enumerable: false,
configurable: true
});
}
}
}
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: prerenderedRoutes == null ? void 0 : prerenderedRoutes.map((route)=>route.pathname),
prerenderedRoutes,
fallbackMode: fallback
};
if (((_res_value = res.value) == null ? void 0 : _res_value.fallbackMode) !== undefined && // This matches the hasGenerateStaticParams logic
// we do during build
(!isAppPath || prerenderedRoutes && prerenderedRoutes.length > 0)) {
// we write the static paths to partial manifest for
// fallback handling inside of entry handler's
const rawExistingManifest = await fs.promises.readFile(pathJoin(this.distDir, PRERENDER_MANIFEST), 'utf8');
const existingManifest = JSON.parse(rawExistingManifest);
for (const staticPath of value.staticPaths || []){
existingManifest.routes[staticPath] = {};
}
existingManifest.dynamicRoutes[pathname] = {
dataRoute: null,
dataRouteRegex: null,
fallback: fallbackModeToFallbackField(res.value.fallbackMode, page),
fallbackRevalidate: false,
fallbackExpire: undefined,
fallbackHeaders: undefined,
fallbackStatus: undefined,
fallbackRootParams: undefined,
fallbackSourceRoute: pathname,
prefetchDataRoute: undefined,
prefetchDataRouteRegex: undefined,
routeRegex: getRouteRegex(pathname).re.source,
experimentalPPR: undefined,
renderingMode: undefined,
allowHeader: []
};
const updatedManifest = JSON.stringify(existingManifest);
if (updatedManifest !== rawExistingManifest) {
await fs.promises.writeFile(pathJoin(this.distDir, PRERENDER_MANIFEST), updatedManifest);
}
}
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,62 @@
import { parse } from 'next/dist/compiled/stacktrace-parser';
import { decorateServerError } from '../../shared/lib/error-source';
function getFilesystemFrame(frame) {
const f = {
...frame
};
if (typeof f.file === 'string') {
if (// Posix:
f.file.startsWith('/') || // Win32:
/^[a-z]:\\/i.test(f.file) || // Win32 UNC:
f.file.startsWith('\\\\')) {
f.file = `file://${f.file}`;
}
}
return f;
}
export function getServerError(error, type) {
if (error.name === 'TurbopackInternalError') {
// If this is an internal Turbopack error we shouldn't show internal details
// to the user. These are written to a log file instead.
const turbopackInternalError = Object.defineProperty(new Error('An unexpected Turbopack error occurred. Please see the output of `next dev` for more details.'), "__NEXT_ERROR_CODE", {
value: "E167",
enumerable: false,
configurable: true
});
decorateServerError(turbopackInternalError, type);
return turbopackInternalError;
}
let n;
try {
throw Object.defineProperty(new Error(error.message), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
} catch (e) {
n = e;
}
n.name = error.name;
try {
n.stack = `${n.toString()}\n${parse(error.stack).map(getFilesystemFrame).map((f)=>{
let str = ` at ${f.methodName}`;
if (f.file) {
let loc = f.file;
if (f.lineNumber) {
loc += `:${f.lineNumber}`;
if (f.column) {
loc += `:${f.column}`;
}
}
str += ` (${loc})`;
}
return str;
}).join('\n')}`;
} catch {
n.stack = error.stack;
}
decorateServerError(n, type);
return n;
}
//# sourceMappingURL=node-stack-frames.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/dev/node-stack-frames.ts"],"sourcesContent":["import { parse } from 'next/dist/compiled/stacktrace-parser'\nimport type { StackFrame } from 'next/dist/compiled/stacktrace-parser'\nimport {\n decorateServerError,\n type ErrorSourceType,\n} from '../../shared/lib/error-source'\n\nfunction getFilesystemFrame(frame: StackFrame): StackFrame {\n const f: StackFrame = { ...frame }\n\n if (typeof f.file === 'string') {\n if (\n // Posix:\n f.file.startsWith('/') ||\n // Win32:\n /^[a-z]:\\\\/i.test(f.file) ||\n // Win32 UNC:\n f.file.startsWith('\\\\\\\\')\n ) {\n f.file = `file://${f.file}`\n }\n }\n\n return f\n}\n\nexport function getServerError(error: Error, type: ErrorSourceType): Error {\n if (error.name === 'TurbopackInternalError') {\n // If this is an internal Turbopack error we shouldn't show internal details\n // to the user. These are written to a log file instead.\n const turbopackInternalError = new Error(\n 'An unexpected Turbopack error occurred. Please see the output of `next dev` for more details.'\n )\n decorateServerError(turbopackInternalError, type)\n return turbopackInternalError\n }\n\n let n: Error\n try {\n throw new Error(error.message)\n } catch (e) {\n n = e as Error\n }\n\n n.name = error.name\n try {\n n.stack = `${n.toString()}\\n${parse(error.stack!)\n .map(getFilesystemFrame)\n .map((f) => {\n let str = ` at ${f.methodName}`\n if (f.file) {\n let loc = f.file\n if (f.lineNumber) {\n loc += `:${f.lineNumber}`\n if (f.column) {\n loc += `:${f.column}`\n }\n }\n str += ` (${loc})`\n }\n return str\n })\n .join('\\n')}`\n } catch {\n n.stack = error.stack\n }\n\n decorateServerError(n, type)\n return n\n}\n"],"names":["parse","decorateServerError","getFilesystemFrame","frame","f","file","startsWith","test","getServerError","error","type","name","turbopackInternalError","Error","n","message","e","stack","toString","map","str","methodName","loc","lineNumber","column","join"],"mappings":"AAAA,SAASA,KAAK,QAAQ,uCAAsC;AAE5D,SACEC,mBAAmB,QAEd,gCAA+B;AAEtC,SAASC,mBAAmBC,KAAiB;IAC3C,MAAMC,IAAgB;QAAE,GAAGD,KAAK;IAAC;IAEjC,IAAI,OAAOC,EAAEC,IAAI,KAAK,UAAU;QAC9B,IACE,SAAS;QACTD,EAAEC,IAAI,CAACC,UAAU,CAAC,QAClB,SAAS;QACT,aAAaC,IAAI,CAACH,EAAEC,IAAI,KACxB,aAAa;QACbD,EAAEC,IAAI,CAACC,UAAU,CAAC,SAClB;YACAF,EAAEC,IAAI,GAAG,CAAC,OAAO,EAAED,EAAEC,IAAI,EAAE;QAC7B;IACF;IAEA,OAAOD;AACT;AAEA,OAAO,SAASI,eAAeC,KAAY,EAAEC,IAAqB;IAChE,IAAID,MAAME,IAAI,KAAK,0BAA0B;QAC3C,4EAA4E;QAC5E,wDAAwD;QACxD,MAAMC,yBAAyB,qBAE9B,CAF8B,IAAIC,MACjC,kGAD6B,qBAAA;mBAAA;wBAAA;0BAAA;QAE/B;QACAZ,oBAAoBW,wBAAwBF;QAC5C,OAAOE;IACT;IAEA,IAAIE;IACJ,IAAI;QACF,MAAM,qBAAwB,CAAxB,IAAID,MAAMJ,MAAMM,OAAO,GAAvB,qBAAA;mBAAA;wBAAA;0BAAA;QAAuB;IAC/B,EAAE,OAAOC,GAAG;QACVF,IAAIE;IACN;IAEAF,EAAEH,IAAI,GAAGF,MAAME,IAAI;IACnB,IAAI;QACFG,EAAEG,KAAK,GAAG,GAAGH,EAAEI,QAAQ,GAAG,EAAE,EAAElB,MAAMS,MAAMQ,KAAK,EAC5CE,GAAG,CAACjB,oBACJiB,GAAG,CAAC,CAACf;YACJ,IAAIgB,MAAM,CAAC,OAAO,EAAEhB,EAAEiB,UAAU,EAAE;YAClC,IAAIjB,EAAEC,IAAI,EAAE;gBACV,IAAIiB,MAAMlB,EAAEC,IAAI;gBAChB,IAAID,EAAEmB,UAAU,EAAE;oBAChBD,OAAO,CAAC,CAAC,EAAElB,EAAEmB,UAAU,EAAE;oBACzB,IAAInB,EAAEoB,MAAM,EAAE;wBACZF,OAAO,CAAC,CAAC,EAAElB,EAAEoB,MAAM,EAAE;oBACvB;gBACF;gBACAJ,OAAO,CAAC,EAAE,EAAEE,IAAI,CAAC,CAAC;YACpB;YACA,OAAOF;QACT,GACCK,IAAI,CAAC,OAAO;IACjB,EAAE,OAAM;QACNX,EAAEG,KAAK,GAAGR,MAAMQ,KAAK;IACvB;IAEAhB,oBAAoBa,GAAGJ;IACvB,OAAOI;AACT","ignoreList":[0]}

View File

@@ -0,0 +1,653 @@
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, isGlobalNotFoundEnabled) {
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) {
// Load `global-not-found` when global-not-found is enabled.
// Prefer to load it when both `global-not-found` and root `not-found` present.
if (isGlobalNotFoundEnabled) {
const globalNotFoundPath = await findPageFile(appDir, 'global-not-found', extensions, true);
if (globalNotFoundPath) {
return {
filename: join(appDir, globalNotFoundPath),
bundlePath: `app${UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}`,
page: UNDERSCORE_NOT_FOUND_ROUTE_ENTRY
};
}
} else {
// Then if global-not-found.js doesn't exist then load not-found.js
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
};
}
}
// If they're not presented, then fallback to global-not-found
return {
filename: require.resolve('next/dist/client/components/builtin/global-not-found'),
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, !!nextConfig.experimental.globalNotFound);
}
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;
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","ignoreList":[0]}

View File

@@ -0,0 +1,34 @@
import isError from '../../lib/is-error';
import { realpathSync } from '../../lib/realpath';
import { clearManifestCache } from '../load-manifest.external';
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.external'\n\nfunction 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,4BAA2B;AAE9D,SAASC,uBAAuBC,QAAgB;IAC9C,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","ignoreList":[0]}

View File

@@ -0,0 +1,83 @@
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
});
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,
cacheComponents: config.cacheComponents,
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,601 @@
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.loadClientBuildManifest(page);
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;
}
case 'conflict':
Log.info(`skipping ${pathname} (${route.type})`);
break;
default:
route;
}
}
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.loadClientBuildManifest('_error');
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