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.
170 lines (159 loc) • 6.38 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
;
const InitFragment = require("../InitFragment");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const { propertyAccess } = require("../util/property");
/** @typedef {import("../ConcatenationScope")} ConcatenationScope */
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
/** @typedef {"exports" | "module.exports" | "this" | "Object.defineProperty(exports)" | "Object.defineProperty(module.exports)" | "Object.defineProperty(this)"} CommonJSDependencyBaseKeywords */
/**
* The well-known name of the ESM named export that, when present, is unwrapped
* by CommonJS `require()` to match Node.js v23+ `require(esm)` semantics:
* https://nodejs.org/docs/latest/api/modules.html#loading-ecmascript-modules-using-require
*/
const ESM_MODULE_EXPORTS_NAME = "module.exports";
/**
* Whether `require()` of `importedModule` would trigger Node.js's
* `require(esm)` `"module.exports"` named-export unwrap. This is the
* usage-independent eligibility check: it only looks at module type and
* whether the export is declared, so it can be safely used from
* `getReferencedExports` before usage info is finalized (otherwise the
* check would be circular — we'd need `"module.exports"` to already be
* marked used in order to ask whether to mark it used).
* @param {Module} importedModule the imported module
* @param {ModuleGraph} moduleGraph the module graph
* @returns {boolean} true if `require()` should unwrap `"module.exports"`
*/
const isRequireEsmModuleExportsModule = (importedModule, moduleGraph) => {
if (importedModule.getExportsType(moduleGraph, false) !== "namespace") {
return false;
}
const exportsInfo = moduleGraph.getExportsInfo(importedModule);
const exportInfo = exportsInfo.getReadOnlyExportInfo(ESM_MODULE_EXPORTS_NAME);
return exportInfo.provided === true;
};
/**
* When CommonJS `require()` resolves to an ES module that has a named export
* with the literal string name `"module.exports"`, Node.js returns the value of
* that export instead of the namespace object. Returns the property-access
* expression to apply to the require result for that unwrapping, or `null` if
* the imported module is not eligible (not strictly ESM, or no such export,
* or the export was tree-shaken away).
* @param {Module} importedModule the imported module
* @param {ModuleGraph} moduleGraph the module graph
* @param {RuntimeSpec} runtime the runtime for which the module is analysed
* @returns {string | null} property-access expression (e.g. `["module.exports"]`), or `null`
*/
const getRequireEsmModuleExportsAccess = (
importedModule,
moduleGraph,
runtime
) => {
if (!isRequireEsmModuleExportsModule(importedModule, moduleGraph)) {
return null;
}
const exportsInfo = moduleGraph.getExportsInfo(importedModule);
// CJS exports are never inlined
const usedName = exportsInfo.getUsedName([ESM_MODULE_EXPORTS_NAME], runtime);
if (usedName === false) return null;
return propertyAccess(/** @type {readonly string[]} */ (usedName));
};
module.exports.ESM_MODULE_EXPORTS_NAME = ESM_MODULE_EXPORTS_NAME;
/**
* Rewrites an `exports.<names>` reference of a concatenated CommonJS module to
* a hoisted module-scope variable and registers that variable as the export's
* symbol, so other concatenated modules bind to it directly.
* @param {ConcatenationScope} concatenationScope the concatenation scope
* @param {InitFragment<GenerateContext>[]} initFragments init fragments (receives the `var` declaration)
* @param {string[]} names full member chain of the reference (at least one entry)
* @returns {string} replacement expression for the whole reference
*/
module.exports.getConcatenatedExportAccess = (
concatenationScope,
initFragments,
names
) => {
const name = names[0];
const identifier = Template.toIdentifier(name);
// A sanitized name could collide with another export ("a-b" vs "a_b"),
// so non-identifier names get a hex suffix of the raw name.
const symbol = `__WEBPACK_CJS_EXPORT_${
identifier === name
? name
: `${identifier}_${Buffer.from(name, "utf8").toString("hex")}`
}__`;
initFragments.push(
new InitFragment(
`var ${symbol};\n`,
InitFragment.STAGE_CONSTANTS,
0,
`cjs concatenated export ${name}`
)
);
concatenationScope.registerExport(name, symbol);
return `${symbol}${propertyAccess(names, 1)}`;
};
module.exports.getRequireEsmModuleExportsAccess =
getRequireEsmModuleExportsAccess;
/**
* Returns type and base.
* @param {CommonJSDependencyBaseKeywords} depBase commonjs dependency base
* @param {Module} module module
* @param {RuntimeRequirements} runtimeRequirements runtime requirements
* @returns {[string, string]} type and base
*/
module.exports.handleDependencyBase = (
depBase,
module,
runtimeRequirements
) => {
/** @type {string} */
let base;
/** @type {string} */
let type;
switch (depBase) {
case "exports":
runtimeRequirements.add(RuntimeGlobals.exports);
base = module.exportsArgument;
type = "expression";
break;
case "module.exports":
runtimeRequirements.add(RuntimeGlobals.module);
base = `${module.moduleArgument}.exports`;
type = "expression";
break;
case "this":
runtimeRequirements.add(RuntimeGlobals.thisAsExports);
base = "this";
type = "expression";
break;
case "Object.defineProperty(exports)":
runtimeRequirements.add(RuntimeGlobals.exports);
base = module.exportsArgument;
type = "Object.defineProperty";
break;
case "Object.defineProperty(module.exports)":
runtimeRequirements.add(RuntimeGlobals.module);
base = `${module.moduleArgument}.exports`;
type = "Object.defineProperty";
break;
case "Object.defineProperty(this)":
runtimeRequirements.add(RuntimeGlobals.thisAsExports);
base = "this";
type = "Object.defineProperty";
break;
default:
throw new Error(`Unsupported base ${depBase}`);
}
return [type, base];
};
module.exports.isRequireEsmModuleExportsModule =
isRequireEsmModuleExportsModule;