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