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.
227 lines (209 loc) • 7.79 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
;
const Template = require("../Template");
const { equals } = require("../util/ArrayHelpers");
const { getTrimmedIdsAndRange } = require("../util/chainedImports");
const makeSerializable = require("../util/makeSerializable");
const memoize = require("../util/memoize");
const { propertyAccess } = require("../util/property");
const {
ESM_MODULE_EXPORTS_NAME,
getRequireEsmModuleExportsAccess,
isRequireEsmModuleExportsModule
} = require("./CommonJsDependencyHelpers");
const ModuleDependency = require("./ModuleDependency");
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
/** @typedef {import("../Dependency").ReferencedExports} ReferencedExports */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
/** @typedef {import("../ExportsInfo").ExportInfoName} ExportInfoName */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
/** @typedef {import("../util/chainedImports").IdRanges} IdRanges */
/** @typedef {import("./HarmonyImportGuard").DependencyGuard} DependencyGuard */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectSerializerContext */
const getHarmonyImportGuard = memoize(() => require("./HarmonyImportGuard"));
class CommonJsFullRequireDependency extends ModuleDependency {
/**
* Creates an instance of CommonJsFullRequireDependency.
* @param {string} request the request string
* @param {Range} range location in source code
* @param {ExportInfoName[]} names accessed properties on module
* @param {IdRanges=} idRanges ranges for members of ids; the two arrays are right-aligned
*/
constructor(
request,
range,
names,
idRanges /* TODO webpack 6 make this non-optional. It must always be set to properly trim ids. */
) {
super(request);
this.range = range;
/** @type {string[]} */
this.names = names;
/** @type {IdRanges | undefined} */
this.idRanges = idRanges;
/** @type {boolean} */
this.call = false;
/** @type {undefined | boolean} */
this.asiSafe = undefined;
/** @type {DependencyGuard[] | undefined} */
this.branchGuards = undefined;
}
/**
* Returns function to determine if the connection is active.
* @param {ModuleGraph} moduleGraph module graph
* @returns {null | false | GetConditionFn} function to determine if the connection is active
*/
getCondition(moduleGraph) {
const guards = this.branchGuards;
if (guards === undefined) return null;
return (connection, runtime) =>
!getHarmonyImportGuard().isDeadByGuards(guards, moduleGraph, runtime);
}
/**
* Returns list of exports referenced by this dependency
* @param {ModuleGraph} moduleGraph module graph
* @param {RuntimeSpec} runtime the runtime for which the module is analysed
* @returns {ReferencedExports} referenced exports
*/
getReferencedExports(moduleGraph, runtime) {
const importedModule = moduleGraph.getModule(this);
// CommonJS property access is never rewritten to a literal, so it can't inline
if (
importedModule &&
isRequireEsmModuleExportsModule(importedModule, moduleGraph)
) {
// When `require(esm)` unwraps a `"module.exports"` named export, the
// user's property access lands on that value (which webpack does not
// model), so only the "module.exports" export itself is referenced.
return [{ name: [ESM_MODULE_EXPORTS_NAME], canInline: false }];
}
if (
this.call &&
(!importedModule ||
importedModule.getExportsType(moduleGraph, false) !== "namespace")
) {
return [{ name: this.names.slice(0, -1), canInline: false }];
}
return [{ name: this.names, canInline: false }];
}
/**
* Serializes this instance into the provided serializer context.
* @param {ObjectSerializerContext} context context
*/
serialize(context) {
context
.write(this.names)
.write(this.idRanges)
.write(this.call)
.write(this.asiSafe)
.write(this.branchGuards);
super.serialize(context);
}
/**
* Restores this instance from the provided deserializer context.
* @param {ObjectDeserializerContext} context context
*/
deserialize(context) {
this.names = context.read();
const c1 = context.rest;
this.idRanges = c1.read();
const c2 = c1.rest;
this.call = c2.read();
const c3 = c2.rest;
this.asiSafe = c3.read();
const c4 = c3.rest;
this.branchGuards = c4.read();
super.deserialize(c4.rest);
}
get type() {
return "cjs full require";
}
get category() {
return "commonjs";
}
}
CommonJsFullRequireDependency.Template = class CommonJsFullRequireDependencyTemplate extends (
ModuleDependency.Template
) {
/**
* Applies the plugin by registering its hooks on the compiler.
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(
dependency,
source,
{ runtimeTemplate, moduleGraph, chunkGraph, runtimeRequirements, runtime }
) {
const dep = /** @type {CommonJsFullRequireDependency} */ (dependency);
if (!dep.range) return;
const connection = moduleGraph.getConnection(dep);
// Dead branch: module is excluded and has no id; code is never executed.
if (connection && !connection.isTargetActive(runtime)) {
// Replaces the whole member chain, so no property access is left dangling
source.replace(dep.range[0], dep.range[1] - 1, "null /* dead branch */");
return;
}
const importedModule = moduleGraph.getModule(dep);
let requireExpr = runtimeTemplate.moduleExports({
module: importedModule,
chunkGraph,
request: dep.request,
weak: dep.weak,
runtimeRequirements
});
const esmRequireAccess = importedModule
? getRequireEsmModuleExportsAccess(importedModule, moduleGraph, runtime)
: null;
const {
trimmedRange: [trimmedRangeStart, trimmedRangeEnd],
trimmedIds
} = getTrimmedIdsAndRange(
dep.names,
dep.range,
dep.idRanges,
moduleGraph,
dep
);
if (esmRequireAccess !== null) {
const access = `${esmRequireAccess}${propertyAccess(trimmedIds)}`;
requireExpr =
dep.asiSafe === true
? `(${requireExpr}${access})`
: `${requireExpr}${access}`;
} else if (importedModule) {
// CJS exports are never inlined
const usedImported = /** @type {string | string[] | false} */ (
moduleGraph
.getExportsInfo(importedModule)
.getUsedName(trimmedIds, runtime)
);
if (usedImported) {
const comment = equals(usedImported, trimmedIds)
? ""
: `${Template.toNormalComment(propertyAccess(trimmedIds))} `;
const access = `${comment}${propertyAccess(/** @type {string[]} */ (usedImported))}`;
requireExpr =
dep.asiSafe === true
? `(${requireExpr}${access})`
: `${requireExpr}${access}`;
}
}
source.replace(trimmedRangeStart, trimmedRangeEnd - 1, requireExpr);
}
};
makeSerializable(
CommonJsFullRequireDependency,
"webpack/lib/dependencies/CommonJsFullRequireDependency"
);
module.exports = CommonJsFullRequireDependency;