create project
This commit is contained in:
475
kitabcitab/node_modules/next/dist/esm/server/dev/on-demand-entry-handler.js
generated
vendored
Normal file
475
kitabcitab/node_modules/next/dist/esm/server/dev/on-demand-entry-handler.js
generated
vendored
Normal file
@@ -0,0 +1,475 @@
|
||||
import origDebug from "next/dist/compiled/debug";
|
||||
import { EventEmitter } from "events";
|
||||
import { findPageFile } from "../lib/find-page-file";
|
||||
import { 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 { getPageStaticInfo } from "../../build/analysis/get-page-static-info";
|
||||
import { isMiddlewareFile, isMiddlewareFilename } from "../../build/utils";
|
||||
import { PageNotFoundError } from "../../shared/lib/utils";
|
||||
import { COMPILER_INDEXES, COMPILER_NAMES, RSC_MODULE_TYPES } from "../../shared/lib/constants";
|
||||
const debug = origDebug("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 = segmentPath.slice(2);
|
||||
return treePathToEntrypoint(childSegmentPath, path);
|
||||
}
|
||||
function convertDynamicParamTypeToSyntax(dynamicParamTypeShort, param) {
|
||||
switch(dynamicParamTypeShort){
|
||||
case "c":
|
||||
return `[...${param}]`;
|
||||
case "oc":
|
||||
return `[[...${param}]]`;
|
||||
case "d":
|
||||
return `[${param}]`;
|
||||
default:
|
||||
throw new Error("Unknown dynamic param type");
|
||||
}
|
||||
}
|
||||
function getEntrypointsFromTree(tree, isFirst, parentPath = []) {
|
||||
const [segment, parallelRoutes] = tree;
|
||||
const currentSegment = Array.isArray(segment) ? convertDynamicParamTypeToSyntax(segment[2], segment[0]) : segment;
|
||||
const currentPath = [
|
||||
...parentPath,
|
||||
currentSegment
|
||||
];
|
||||
if (!isFirst && currentSegment === "") {
|
||||
// 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");
|
||||
export var EntryTypes;
|
||||
(function(EntryTypes) {
|
||||
EntryTypes[EntryTypes["ENTRY"] = 0] = "ENTRY";
|
||||
EntryTypes[EntryTypes["CHILD_ENTRY"] = 1] = "CHILD_ENTRY";
|
||||
})(EntryTypes || (EntryTypes = {}));
|
||||
export const entries = {};
|
||||
let invalidator;
|
||||
export const getInvalidator = ()=>invalidator;
|
||||
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 {
|
||||
building = new Set();
|
||||
rebuildAgain = new Set();
|
||||
constructor(multiCompiler){
|
||||
this.multiCompiler = multiCompiler;
|
||||
}
|
||||
shouldRebuildAll() {
|
||||
return this.rebuildAgain.size > 0;
|
||||
}
|
||||
invalidate(compilerKeys = COMPILER_KEYS) {
|
||||
for (const key of compilerKeys){
|
||||
var ref;
|
||||
// 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;
|
||||
}
|
||||
(ref = this.multiCompiler.compilers[COMPILER_INDEXES[key]].watching) == null ? void 0 : ref.invalidate();
|
||||
this.building.add(key);
|
||||
}
|
||||
}
|
||||
startBuilding(compilerKey) {
|
||||
this.building.add(compilerKey);
|
||||
}
|
||||
doneBuilding() {
|
||||
const rebuild = [];
|
||||
for (const key of COMPILER_KEYS){
|
||||
this.building.delete(key);
|
||||
if (this.rebuildAgain.has(key)) {
|
||||
rebuild.push(key);
|
||||
this.rebuildAgain.delete(key);
|
||||
}
|
||||
}
|
||||
this.invalidate(rebuild);
|
||||
}
|
||||
}
|
||||
function disposeInactiveEntries(maxInactiveAge) {
|
||||
Object.keys(entries).forEach((entryKey)=>{
|
||||
const entryData = entries[entryKey];
|
||||
const { lastActiveTime , status , dispose } = entryData;
|
||||
// TODO-APP: implement disposing of CHILD_ENTRY
|
||||
if (entryData.type === 1) {
|
||||
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 pagesDir Absolute path to the pages folder with trailing `/pages`.
|
||||
* @param normalizedPagePath The page normalized (it will be denormalized).
|
||||
* @param pageExtensions Array of page extensions.
|
||||
*/ async function findPagePathData(rootDir, page, extensions, pagesDir, appDir) {
|
||||
const normalizedPagePath = tryToNormalizePagePath(page);
|
||||
let pagePath = null;
|
||||
if (isMiddlewareFile(normalizedPagePath)) {
|
||||
pagePath = await findPageFile(rootDir, normalizedPagePath, extensions, false);
|
||||
if (!pagePath) {
|
||||
throw new PageNotFoundError(normalizedPagePath);
|
||||
}
|
||||
const pageUrl = ensureLeadingSlash(removePagePathTail(normalizePathSep(pagePath), {
|
||||
extensions
|
||||
}));
|
||||
return {
|
||||
absolutePagePath: join(rootDir, pagePath),
|
||||
bundlePath: normalizedPagePath.slice(1),
|
||||
page: posix.normalize(pageUrl)
|
||||
};
|
||||
}
|
||||
// Check appDir first falling back to pagesDir
|
||||
if (appDir) {
|
||||
pagePath = await findPageFile(appDir, normalizedPagePath, extensions, true);
|
||||
if (pagePath) {
|
||||
const pageUrl = ensureLeadingSlash(removePagePathTail(normalizePathSep(pagePath), {
|
||||
keepIndex: true,
|
||||
extensions
|
||||
}));
|
||||
return {
|
||||
absolutePagePath: join(appDir, pagePath),
|
||||
bundlePath: posix.join("app", normalizePagePath(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 {
|
||||
absolutePagePath: join(pagesDir, pagePath),
|
||||
bundlePath: posix.join("pages", normalizePagePath(pageUrl)),
|
||||
page: posix.normalize(pageUrl)
|
||||
};
|
||||
}
|
||||
if (page === "/_error") {
|
||||
return {
|
||||
absolutePagePath: require.resolve("next/dist/pages/_error"),
|
||||
bundlePath: page,
|
||||
page: normalizePathSep(page)
|
||||
};
|
||||
} else {
|
||||
throw new PageNotFoundError(normalizedPagePath);
|
||||
}
|
||||
}
|
||||
export function onDemandEntryHandler({ maxInactiveAge , multiCompiler , nextConfig , pagesBufferLength , pagesDir , rootDir , appDir }) {
|
||||
invalidator = new Invalidator(multiCompiler);
|
||||
const startBuilding = (compilation)=>{
|
||||
const compilationName = compilation.name;
|
||||
invalidator.startBuilding(compilationName);
|
||||
};
|
||||
for (const compiler of multiCompiler.compilers){
|
||||
compiler.hooks.make.tap("NextJsOnDemandEntries", startBuilding);
|
||||
}
|
||||
function getPagePathsFromEntrypoints(type, entrypoints, root) {
|
||||
const pagePaths = [];
|
||||
for (const entrypoint of entrypoints.values()){
|
||||
const page = getRouteFromEntrypoint(entrypoint.name, root);
|
||||
if (page) {
|
||||
pagePaths.push(`${type}${page}`);
|
||||
} else if (root && entrypoint.name === "root" || isMiddlewareFilename(entrypoint.name)) {
|
||||
pagePaths.push(`${type}/${entrypoint.name}`);
|
||||
}
|
||||
}
|
||||
return pagePaths;
|
||||
}
|
||||
multiCompiler.hooks.done.tap("NextJsOnDemandEntries", (multiStats)=>{
|
||||
if (invalidator.shouldRebuildAll()) {
|
||||
return invalidator.doneBuilding();
|
||||
}
|
||||
const [clientStats, serverStats, edgeServerStats] = multiStats.stats;
|
||||
const root = !!appDir;
|
||||
const pagePaths = [
|
||||
...getPagePathsFromEntrypoints(COMPILER_NAMES.client, clientStats.compilation.entrypoints, root),
|
||||
...getPagePathsFromEntrypoints(COMPILER_NAMES.server, serverStats.compilation.entrypoints, root),
|
||||
...edgeServerStats ? getPagePathsFromEntrypoints(COMPILER_NAMES.edgeServer, edgeServerStats.compilation.entrypoints, root) : [],
|
||||
];
|
||||
for (const page of pagePaths){
|
||||
const entry = entries[page];
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
if (entry.status !== BUILDING) {
|
||||
continue;
|
||||
}
|
||||
entry.status = BUILT;
|
||||
doneCallbacks.emit(page);
|
||||
}
|
||||
invalidator.doneBuilding();
|
||||
});
|
||||
const pingIntervalTime = Math.max(1000, Math.min(5000, maxInactiveAge));
|
||||
setInterval(function() {
|
||||
disposeInactiveEntries(maxInactiveAge);
|
||||
}, pingIntervalTime + 1000).unref();
|
||||
function handleAppDirPing(tree) {
|
||||
const pages = getEntrypointsFromTree(tree, true);
|
||||
let toSend = {
|
||||
invalid: true
|
||||
};
|
||||
for (const page of pages){
|
||||
for (const compilerType of [
|
||||
COMPILER_NAMES.client,
|
||||
COMPILER_NAMES.server,
|
||||
COMPILER_NAMES.edgeServer,
|
||||
]){
|
||||
const pageKey = `${compilerType}/${page}`;
|
||||
const entryInfo = entries[pageKey];
|
||||
// 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(pageKey)) {
|
||||
lastServerAccessPagesForAppDir.unshift(pageKey);
|
||||
// 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;
|
||||
toSend = {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
}
|
||||
return toSend;
|
||||
}
|
||||
function handlePing(pg) {
|
||||
const page = normalizePathSep(pg);
|
||||
let toSend = {
|
||||
invalid: true
|
||||
};
|
||||
for (const compilerType of [
|
||||
COMPILER_NAMES.client,
|
||||
COMPILER_NAMES.server,
|
||||
COMPILER_NAMES.edgeServer,
|
||||
]){
|
||||
const pageKey = `${compilerType}${page}`;
|
||||
const entryInfo = entries[pageKey];
|
||||
// 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 {
|
||||
invalid: true
|
||||
};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 404 is an on demand entry but when a new page is added we have to refresh the page
|
||||
toSend = page === "/_error" ? {
|
||||
invalid: true
|
||||
} : {
|
||||
success: true
|
||||
};
|
||||
// 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(pageKey)) {
|
||||
lastClientAccessPages.unshift(pageKey);
|
||||
// Maintain the buffer max length
|
||||
if (lastClientAccessPages.length > pagesBufferLength) {
|
||||
lastClientAccessPages.pop();
|
||||
}
|
||||
}
|
||||
entryInfo.lastActiveTime = Date.now();
|
||||
entryInfo.dispose = false;
|
||||
}
|
||||
return toSend;
|
||||
}
|
||||
return {
|
||||
async ensurePage ({ page , clientOnly , appPaths =null }) {
|
||||
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 {
|
||||
const pagePathData = await findPagePathData(rootDir, page, nextConfig.pageExtensions, pagesDir, appDir);
|
||||
const isInsideAppDir = !!appDir && pagePathData.absolutePagePath.startsWith(appDir);
|
||||
const addEntry = (compilerType)=>{
|
||||
const entryKey = `${compilerType}${pagePathData.page}`;
|
||||
if (entries[entryKey]) {
|
||||
entries[entryKey].dispose = false;
|
||||
entries[entryKey].lastActiveTime = Date.now();
|
||||
if (entries[entryKey].status === BUILT) {
|
||||
return {
|
||||
entryKey,
|
||||
newEntry: false,
|
||||
shouldInvalidate: false
|
||||
};
|
||||
}
|
||||
return {
|
||||
entryKey,
|
||||
newEntry: false,
|
||||
shouldInvalidate: true
|
||||
};
|
||||
}
|
||||
entries[entryKey] = {
|
||||
type: 0,
|
||||
appPaths,
|
||||
absolutePagePath: pagePathData.absolutePagePath,
|
||||
request: pagePathData.absolutePagePath,
|
||||
bundlePath: pagePathData.bundlePath,
|
||||
dispose: false,
|
||||
lastActiveTime: Date.now(),
|
||||
status: ADDED
|
||||
};
|
||||
return {
|
||||
entryKey: entryKey,
|
||||
newEntry: true,
|
||||
shouldInvalidate: true
|
||||
};
|
||||
};
|
||||
const staticInfo = await getPageStaticInfo({
|
||||
pageFilePath: pagePathData.absolutePagePath,
|
||||
nextConfig,
|
||||
isDev: true,
|
||||
pageType: isInsideAppDir ? "app" : "pages"
|
||||
});
|
||||
const added = new Map();
|
||||
const isServerComponent = isInsideAppDir && staticInfo.rsc !== RSC_MODULE_TYPES.client;
|
||||
await runDependingOnPageType({
|
||||
page: pagePathData.page,
|
||||
pageRuntime: staticInfo.runtime,
|
||||
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 = `${COMPILER_NAMES.edgeServer}${pagePathData.page}`;
|
||||
if (entries[edgeServerEntry]) {
|
||||
// Runtime switched from edge to server
|
||||
delete entries[edgeServerEntry];
|
||||
}
|
||||
},
|
||||
onEdgeServer: ()=>{
|
||||
added.set(COMPILER_NAMES.edgeServer, addEntry(COMPILER_NAMES.edgeServer));
|
||||
const serverEntry = `${COMPILER_NAMES.server}${pagePathData.page}`;
|
||||
if (entries[serverEntry]) {
|
||||
// Runtime switched from server to edge
|
||||
delete entries[serverEntry];
|
||||
}
|
||||
}
|
||||
});
|
||||
const addedValues = [
|
||||
...added.values()
|
||||
];
|
||||
const entriesThatShouldBeInvalidated = addedValues.filter((entry)=>entry.shouldInvalidate);
|
||||
const hasNewEntry = addedValues.some((entry)=>entry.newEntry);
|
||||
if (hasNewEntry) {
|
||||
reportTrigger(!clientOnly && hasNewEntry ? `${pagePathData.page} (client and server)` : pagePathData.page);
|
||||
}
|
||||
if (entriesThatShouldBeInvalidated.length > 0) {
|
||||
const invalidatePromises = entriesThatShouldBeInvalidated.map(({ entryKey })=>{
|
||||
return new Promise((resolve, reject)=>{
|
||||
doneCallbacks.once(entryKey, (err)=>{
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
invalidator.invalidate([
|
||||
...added.keys()
|
||||
]);
|
||||
await Promise.all(invalidatePromises);
|
||||
}
|
||||
} finally{
|
||||
clearTimeout(stalledEnsureTimeout);
|
||||
}
|
||||
},
|
||||
onHMR (client) {
|
||||
client.addEventListener("message", ({ data })=>{
|
||||
try {
|
||||
const parsedData = JSON.parse(typeof data !== "string" ? data.toString() : data);
|
||||
if (parsedData.event === "ping") {
|
||||
const result = parsedData.appDirRoute ? handleAppDirPing(parsedData.tree) : handlePing(parsedData.page);
|
||||
client.send(JSON.stringify({
|
||||
...result,
|
||||
[parsedData.appDirRoute ? "action" : "event"]: "pong"
|
||||
}));
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//# sourceMappingURL=on-demand-entry-handler.js.map
|
||||
Reference in New Issue
Block a user