Spaces:
Running
Running
File size: 8,864 Bytes
c592d77 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | "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 |