webpack
Version:
Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
264 lines (251 loc) • 9.12 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
*/
;
const RuntimeGlobals = require("../RuntimeGlobals");
const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
const {
generateJavascriptHMR
} = require("../hmr/JavascriptHotModuleReplacementHelper");
const { chunkHasJs } = require("../javascript/JavascriptModulesPlugin");
const { getInitialChunkIds } = require("../javascript/StartupHelpers");
const { renderBaseUri } = require("../runtime/baseUri");
const compileBooleanMatcher = require("../util/compileBooleanMatcher");
/** @import Chunk from "../Chunk" */
/** @import ChunkGraph from "../ChunkGraph" */
/** @import Compilation from "../Compilation" */
/** @import RuntimeTemplate from "../RuntimeTemplate" */
/** @import { ReadOnlyRuntimeRequirements } from "../Module" */
class RequireChunkLoadingRuntimeModule extends RuntimeModule {
/**
* Creates an instance of RequireChunkLoadingRuntimeModule.
* @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements
*/
constructor(runtimeRequirements) {
super("require chunk loading", RuntimeModule.STAGE_ATTACH);
/** @type {ReadOnlyRuntimeRequirements} */
this.runtimeRequirements = runtimeRequirements;
}
/**
* The `[handlerMap, key]` pairs this module installs onto a chunk handler map
* such as `__webpack_require__.f`, or `null` where it cannot name them.
* @returns {[string, string][] | null} installed chunk handlers (do not mutate)
*/
getInstalledChunkHandlers() {
const runtimeRequirements = this.runtimeRequirements;
/** @type {[string, string][]} */
const handlers = [];
if (runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers)) {
handlers.push([RuntimeGlobals.ensureChunkHandlers, "require"]);
}
// The hot update flow installs this one onto the same map while it runs.
if (runtimeRequirements.has(RuntimeGlobals.hmrDownloadUpdateHandlers)) {
handlers.push([RuntimeGlobals.ensureChunkHandlers, "requireHmr"]);
}
if (runtimeRequirements.has(RuntimeGlobals.onChunksLoaded)) {
handlers.push([RuntimeGlobals.onChunksLoaded, "require"]);
}
return handlers;
}
/**
* Returns generated code.
* @private
* @param {Chunk} chunk chunk
* @param {string} rootOutputDir root output directory
* @param {RuntimeTemplate} runtimeTemplate the runtime template
* @returns {string} generated code
*/
_generateBaseUri(chunk, rootOutputDir, runtimeTemplate) {
const options = chunk.getEntryOptions();
return renderBaseUri(
options ? options.baseUri : undefined,
`require(${runtimeTemplate.renderNodePrefixForCoreModule("url")}).pathToFileURL(${
rootOutputDir !== "./"
? `__dirname + ${JSON.stringify(`/${rootOutputDir}`)}`
: "__filename"
})`
);
}
/**
* Generates runtime code for this runtime module.
* @returns {string | null} runtime code
*/
generate() {
const compilation = /** @type {Compilation} */ (this.compilation);
const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
const chunk = /** @type {Chunk} */ (this.chunk);
const { runtimeTemplate } = compilation;
const fn = RuntimeGlobals.ensureChunkHandlers;
const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI);
const withExternalInstallChunk = this.runtimeRequirements.has(
RuntimeGlobals.externalInstallChunk
);
const withOnChunkLoad = this.runtimeRequirements.has(
RuntimeGlobals.onChunksLoaded
);
const withLoading = this.runtimeRequirements.has(
RuntimeGlobals.ensureChunkHandlers
);
const withHmr = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadUpdateHandlers
);
const withHmrManifest = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadManifest
);
const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);
const hasJsMatcher = compileBooleanMatcher(conditionMap);
const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
const rootOutputDir = runtimeTemplate.chunkRootOutputDir(chunk, true);
const stateExpression = withHmr
? `${RuntimeGlobals.hmrRuntimeStatePrefix}_require`
: undefined;
const cst = runtimeTemplate.renderConst();
const installedChunksObject = `{\n${Template.indent(
Array.from(initialChunkIds, (id) => `${JSON.stringify(id)}: 1`).join(
",\n"
)
)}\n}`;
// Every part below that reads the table. A chunk asking only for `.b` gets this
// module for the base uri alone, and then has nothing to look up.
const withInstalledChunks =
withOnChunkLoad || withLoading || withExternalInstallChunk || withHmr;
return Template.asString([
withBaseURI
? this._generateBaseUri(chunk, rootOutputDir, runtimeTemplate)
: "// no baseURI",
"",
withInstalledChunks
? Template.asString([
"// object to store loaded chunks",
'// "1" means "loaded", otherwise not loaded yet',
`${cst} installedChunks = ${
stateExpression
? runtimeTemplate.assignOr(
stateExpression,
installedChunksObject
)
: installedChunksObject
};`
])
: "// no installed chunks",
"",
withOnChunkLoad
? `${
RuntimeGlobals.onChunksLoaded
}.require = ${runtimeTemplate.returningFunction(
"installedChunks[chunkId]",
"chunkId"
)};`
: "// no on chunks loaded",
"",
withLoading || withExternalInstallChunk
? `${cst} installChunk = ${runtimeTemplate.basicFunction("chunk", [
`${cst} moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;`,
"for(var moduleId in moreModules) {",
Template.indent([
`if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
Template.indent([
`${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];`
]),
"}"
]),
"}",
`if(runtime) runtime(${RuntimeGlobals.require});`,
"for(var i = 0; i < chunkIds.length; i++)",
Template.indent("installedChunks[chunkIds[i]] = 1;"),
withOnChunkLoad ? `${RuntimeGlobals.onChunksLoaded}();` : ""
])};`
: "// no chunk install function needed",
"",
withLoading
? Template.asString([
"// require() chunk loading for javascript",
`${fn}.require = ${runtimeTemplate.basicFunction(
"chunkId, promises",
hasJsMatcher !== false
? [
'// "1" is the signal for "already loaded"',
"if(!installedChunks[chunkId]) {",
Template.indent([
hasJsMatcher === true
? "if(true) { // all chunks have JS"
: `if(${hasJsMatcher("chunkId")}) {`,
Template.indent([
// The require function loads and runs a chunk. When the chunk is being run,
// it can call __webpack_require__.C to directly complete installed.
`${cst} installedChunk = require(${JSON.stringify(
rootOutputDir
)} + ${
RuntimeGlobals.getChunkScriptFilename
}(chunkId));`,
"if (!installedChunks[chunkId]) {",
Template.indent(["installChunk(installedChunk);"]),
"}"
]),
"} else installedChunks[chunkId] = 1;",
""
]),
"}"
]
: "installedChunks[chunkId] = 1;"
)};`
])
: "// no chunk loading",
"",
withExternalInstallChunk
? Template.asString([
`module.exports = ${RuntimeGlobals.require};`,
`${RuntimeGlobals.externalInstallChunk} = installChunk;`
])
: "// no external install chunk",
"",
withHmr
? Template.asString([
"function loadUpdateChunk(chunkId, updatedModulesList) {",
Template.indent([
`${cst} update = require(${JSON.stringify(rootOutputDir)} + ${
RuntimeGlobals.getChunkUpdateScriptFilename
}(chunkId));`,
`${cst} updatedModules = update.modules;`,
`${cst} runtime = update.runtime;`,
"for(var moduleId in updatedModules) {",
Template.indent([
`if(${RuntimeGlobals.hasOwnProperty}(updatedModules, moduleId)) {`,
Template.indent([
"currentUpdate[moduleId] = updatedModules[moduleId];",
`${runtimeTemplate.optionalChaining("updatedModulesList", "push(moduleId)")};`
]),
"}"
]),
"}",
"if(runtime) currentUpdateRuntime.push(runtime);"
]),
"}",
"",
generateJavascriptHMR("require")
])
: "// no HMR",
"",
withHmrManifest
? Template.asString([
`${RuntimeGlobals.hmrDownloadManifest} = function() {`,
Template.indent([
"return Promise.resolve().then(function() {",
Template.indent([
`return require(${JSON.stringify(rootOutputDir)} + ${
RuntimeGlobals.getUpdateManifestFilename
}());`
]),
`}).catch(${runtimeTemplate.basicFunction("err", [
"if(['MODULE_NOT_FOUND', 'ENOENT'].includes(err.code)) return;",
"throw err;"
])});`
]),
"}"
])
: "// no HMR manifest"
]);
}
}
module.exports = RequireChunkLoadingRuntimeModule;