next
Version:
The React Framework
107 lines (106 loc) • 5.26 kB
JavaScript
import { webpack } from 'next/dist/compiled/webpack/webpack';
import createDebug from 'next/dist/compiled/debug';
const debug = createDebug('next:deferred-entries-plugin');
const PLUGIN_NAME = 'DeferredEntriesPlugin';
/**
* A webpack plugin that handles deferred entries by:
* 1. Accepting deferred entrypoints separately from the main config
* 2. After non-deferred entries are compiled, calling the onBeforeDeferredEntries callback
* 3. Then adding and compiling the deferred entries within the same compilation
*
* This approach avoids module ID conflicts that would occur with separate compilations.
*/ export class DeferredEntriesPlugin {
constructor(options){
this.callbackCalled = false;
this.onBeforeDeferredEntries = options.config.experimental.onBeforeDeferredEntries;
this.deferredEntrypoints = options.deferredEntrypoints;
this.deferredEntrySourceDirectories = options.deferredEntrySourceDirectories;
}
apply(compiler) {
// Skip if no deferred entrypoints to process
if (!this.deferredEntrypoints || Object.keys(this.deferredEntrypoints).length === 0) {
return;
}
// Use finishMake hook to add deferred entries after all initial entries are processed
// This is the same pattern used by FlightClientEntryPlugin
compiler.hooks.finishMake.tapPromise(PLUGIN_NAME, async (compilation)=>{
// Only process if we haven't called callback yet
if (this.callbackCalled) {
return;
}
this.callbackCalled = true;
// Call the onBeforeDeferredEntries callback
if (this.onBeforeDeferredEntries) {
var _this_deferredEntrySourceDirectories;
debug('calling onBeforeDeferredEntries callback');
await this.onBeforeDeferredEntries();
// The callback can update deferred entry source files after Webpack has
// already cached them while building the initial entries. Only purge
// their owning directories so unrelated entries keep their cache state.
if ((_this_deferredEntrySourceDirectories = this.deferredEntrySourceDirectories) == null ? void 0 : _this_deferredEntrySourceDirectories.length) {
var _compiler_inputFileSystem_purge, _compiler_inputFileSystem;
(_compiler_inputFileSystem = compiler.inputFileSystem) == null ? void 0 : (_compiler_inputFileSystem_purge = _compiler_inputFileSystem.purge) == null ? void 0 : _compiler_inputFileSystem_purge.call(_compiler_inputFileSystem, this.deferredEntrySourceDirectories);
}
debug('onBeforeDeferredEntries callback completed');
}
// Add deferred entries to compilation
const addEntryPromises = [];
const bundler = webpack;
debug('adding deferred entries:', Object.keys(this.deferredEntrypoints));
for (const [name, entryData] of Object.entries(this.deferredEntrypoints)){
debug('processing deferred entry:', name, entryData);
// Normalize entry data structure
let entry;
if (typeof entryData === 'string') {
entry = {
import: [
entryData
]
};
} else if (Array.isArray(entryData)) {
entry = {
import: entryData
};
} else {
entry = entryData;
}
// Get imports array
const imports = entry.import ? Array.isArray(entry.import) ? entry.import : [
entry.import
] : [];
if (imports.length === 0) {
continue;
}
// Normalize dependOn to always be an array
const dependOn = entry.dependOn ? Array.isArray(entry.dependOn) ? entry.dependOn : [
entry.dependOn
] : undefined;
// Create dependencies for all imports
for (const importPath of imports){
if (typeof importPath !== 'string') {
continue;
}
const dep = bundler.EntryPlugin.createDependency(importPath, {
name
});
addEntryPromises.push(new Promise((resolve, reject)=>{
compilation.addEntry(compiler.context, dep, {
name,
layer: entry.layer,
runtime: entry.runtime,
dependOn
}, (err)=>{
if (err) {
reject(err);
} else {
resolve();
}
});
}));
}
}
await Promise.all(addEntryPromises);
});
}
}
//# sourceMappingURL=deferred-entries-plugin.js.map