UNPKG

next

Version:

The React Framework

171 lines (170 loc) • 8.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, /** * Rspack Persistent Cache Strategy for Next.js Development * * Rspack's persistent caching differs from Webpack in how it manages module graphs. * While Webpack incrementally updates modules, Rspack operates on complete module * graph snapshots for cache restoration. * * Problem: * - Next.js dev server starts with no page modules in the initial entry points * - When Rspack restores from persistent cache, it finds no modules and purges * the entire module graph * - Later page requests find no cached module information, preventing cache reuse * * Solution: * - Track successfully built page entries after each compilation * - Restore these entries on dev server restart to maintain module graph continuity * - This ensures previously compiled pages can leverage persistent cache for faster builds */ "default", { enumerable: true, get: function() { return HotReloaderRspack; } }); const _path = /*#__PURE__*/ _interop_require_default(require("path")); const _promises = /*#__PURE__*/ _interop_require_default(require("fs/promises")); const _crypto = require("crypto"); const _hotreloaderwebpack = /*#__PURE__*/ _interop_require_default(require("./hot-reloader-webpack")); const _ondemandentryhandler = require("./on-demand-entry-handler"); const _constants = require("../../shared/lib/constants"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class HotReloaderRspack extends _hotreloaderwebpack.default { async afterCompile(multiCompiler) { // Always initialize the fallback error watcher for Rspack. // Rspack may restore/retain the previous build's error state, so without this // a page that previously failed to build might not be rebuilt on the next request. await super.buildFallbackError(); const rspackStartSpan = this.hotReloaderSpan.traceChild('rspack-after-compile'); await rspackStartSpan.traceAsyncFn(async ()=>{ const hash = (0, _crypto.createHash)('sha1'); multiCompiler.compilers.forEach((compiler)=>{ const cache = compiler.options.cache; if (typeof cache === 'object' && 'version' in cache && cache.version) { hash.update(cache.version); if (compiler.name === _constants.COMPILER_NAMES.client) { this.isClientCacheEnabled = true; } else if (compiler.name === _constants.COMPILER_NAMES.server) { this.isServerCacheEnabled = true; } else if (compiler.name === _constants.COMPILER_NAMES.edgeServer) { this.isEdgeServerCacheEnabled = true; } } else { hash.update('-'); } return undefined; }); this.builtEntriesCachePath = _path.default.join(this.distDir, 'cache', 'rspack', hash.digest('hex').substring(0, 16), 'built-entries.json'); const hasBuiltEntriesCache = await _promises.default.access(this.builtEntriesCachePath).then(()=>true, ()=>false); if (hasBuiltEntriesCache) { try { const builtEntries = JSON.parse(await _promises.default.readFile(this.builtEntriesCachePath, 'utf-8') || '{}'); await Promise.all(Object.keys(builtEntries).map(async (entryKey)=>{ const entryData = builtEntries[entryKey]; const isEntry = entryData.type === _ondemandentryhandler.EntryTypes.ENTRY; const isChildEntry = entryData.type === _ondemandentryhandler.EntryTypes.CHILD_ENTRY; // Check if the page was removed or disposed and remove it if (isEntry) { const pageExists = !entryData.dispose && await _promises.default.access(entryData.absolutePagePath).then(()=>true, ()=>false); if (!pageExists) { delete builtEntries[entryKey]; return; } else if (!('hash' in builtEntries[entryKey]) || builtEntries[entryKey].hash !== await calculateFileHash(entryData.absolutePagePath)) { delete builtEntries[entryKey]; return; } } // For child entries, if it has an entry file and it's gone, remove it if (isChildEntry) { if (entryData.absoluteEntryFilePath) { const pageExists = !entryData.dispose && await _promises.default.access(entryData.absoluteEntryFilePath).then(()=>true, ()=>false); if (!pageExists) { delete builtEntries[entryKey]; return; } else { if (!('hash' in builtEntries[entryKey]) || builtEntries[entryKey].hash !== await calculateFileHash(entryData.absoluteEntryFilePath)) { delete builtEntries[entryKey]; return; } } } } })); Object.assign((0, _ondemandentryhandler.getEntries)(multiCompiler.outputPath), builtEntries); } catch (error) { console.error('Rspack failed to read built entries cache: ', error); } } }); } async ensurePage({ page, clientOnly, appPaths, definition, isApp, url }) { await super.ensurePage({ page, clientOnly, appPaths, definition, isApp, url }); const entries = (0, _ondemandentryhandler.getEntries)(this.multiCompiler.outputPath); const builtEntries = {}; await Promise.all(Object.keys(entries).map(async (entryName)=>{ const entry = entries[entryName]; if (entry.status !== _ondemandentryhandler.BUILT) return; const result = /^(client|server|edge-server)@(app|pages|root)@(.*)/g.exec(entryName); const [, key /* pageType */ , , ] = result// this match should always happen ; if (key === 'client' && !this.isClientCacheEnabled) return; if (key === 'server' && !this.isServerCacheEnabled) return; if (key === 'edge-server' && !this.isEdgeServerCacheEnabled) return; // TODO: Rspack does not store middleware entries in persistent cache, causing // test/integration/middleware-src/test/index.test.ts to fail. This is a temporary // workaround to skip middleware entry caching until Rspack properly supports it. if (page === '/middleware') { return; } let hash; if (entry.type === _ondemandentryhandler.EntryTypes.ENTRY) { hash = await calculateFileHash(entry.absolutePagePath); } else if (entry.absoluteEntryFilePath) { hash = await calculateFileHash(entry.absoluteEntryFilePath); } if (!hash) { return; } builtEntries[entryName] = entry; builtEntries[entryName].hash = hash; })); const hasBuitEntriesCache = await _promises.default.access(this.builtEntriesCachePath).then(()=>true, ()=>false); try { if (!hasBuitEntriesCache) { await _promises.default.mkdir(_path.default.dirname(this.builtEntriesCachePath), { recursive: true }); } await _promises.default.writeFile(this.builtEntriesCachePath, JSON.stringify(builtEntries, null, 2)); } catch (error) { console.error('Rspack failed to write built entries cache: ', error); } } constructor(...args){ super(...args), this.isClientCacheEnabled = false, this.isServerCacheEnabled = false, this.isEdgeServerCacheEnabled = false; } } async function calculateFileHash(filePath, algorithm = 'sha256') { if (!await _promises.default.access(filePath).then(()=>true, ()=>false)) { return; } const fileBuffer = await _promises.default.readFile(filePath); const hash = (0, _crypto.createHash)(algorithm); hash.update(fileBuffer); return hash.digest('hex'); } //# sourceMappingURL=hot-reloader-rspack.js.map