| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const Dependency = require("../Dependency");
- const { UsageState } = require("../ExportsInfo");
- const Template = require("../Template");
- const { equals } = require("../util/ArrayHelpers");
- const makeSerializable = require("../util/makeSerializable");
- const { propertyAccess } = require("../util/property");
- const {
- ESM_MODULE_EXPORTS_NAME,
- getRequireEsmModuleExportsAccess,
- handleDependencyBase,
- isRequireEsmModuleExportsModule
- } = require("./CommonJsDependencyHelpers");
- const ModuleDependency = require("./ModuleDependency");
- const processExportInfo = require("./processExportInfo");
- /** @import { ReplaceSource } from "webpack-sources" */
- /**
- * @import {
- * ExportsSpec,
- * GetConditionFn,
- * RawReferencedExports,
- * ReferencedExports,
- * TRANSITIVE,
- * ExportInfoName
- * } from "../Dependency"
- */
- /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
- /** @import ExportsInfo, { ExportInfo } from "../ExportsInfo" */
- /** @import Module from "../Module" */
- /** @import ModuleGraph from "../ModuleGraph" */
- /** @import ModuleGraphConnection from "../ModuleGraphConnection" */
- /** @import { Range } from "../javascript/JavascriptParser" */
- /** @import { RuntimeSpec } from "../util/runtime" */
- /**
- * @import {
- * CommonJSDependencyBaseKeywords
- * } from "./CommonJsDependencyHelpers"
- */
- /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[undefined | boolean, Range, Range | null, CommonJSDependencyBaseKeywords, ExportInfoName[], ExportInfoName[], boolean, boolean]>} ObjectDeserializerContext */
- /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[undefined | boolean, Range, Range | null, CommonJSDependencyBaseKeywords, ExportInfoName[], ExportInfoName[], boolean, boolean]>} ObjectSerializerContext */
- const idsSymbol = /** @type {symbol} */ (
- Symbol("CommonJsExportRequireDependency.ids")
- );
- const EMPTY_OBJECT = {};
- /** @typedef {Set<string>} Exports */
- /** @typedef {Set<string>} Checked */
- class CommonJsExportRequireDependency extends ModuleDependency {
- /**
- * Creates an instance of CommonJsExportRequireDependency.
- * @param {Range} range range
- * @param {Range | null} valueRange value range
- * @param {CommonJSDependencyBaseKeywords} base base
- * @param {ExportInfoName[]} names names
- * @param {string} request request
- * @param {ExportInfoName[]} ids ids
- * @param {boolean} resultUsed true, when the result is used
- */
- constructor(range, valueRange, base, names, request, ids, resultUsed) {
- super(request);
- this.range = range;
- this.valueRange = valueRange;
- /** @type {CommonJSDependencyBaseKeywords} */
- this.base = base;
- /** @type {string[]} */
- this.names = names;
- /** @type {string[]} */
- this.ids = ids;
- /** @type {boolean} */
- this.resultUsed = resultUsed;
- /** @type {undefined | boolean} */
- this.asiSafe = undefined;
- // true when the reexport is a lazy `{ get: () => require(...) }` accessor
- // (as produced by `Object.defineProperty`) rather than an eager value.
- /** @type {boolean} */
- this.getter = false;
- }
- get type() {
- return "cjs export require";
- }
- get category() {
- return "commonjs";
- }
- /**
- * Could affect referencing module.
- * @returns {boolean | TRANSITIVE} true, when changes to the referenced module could affect the referencing module; TRANSITIVE, when changes to the referenced module could affect referencing modules of the referencing module
- */
- couldAffectReferencingModule() {
- return Dependency.TRANSITIVE;
- }
- /**
- * Returns true if this dependency can be concatenated
- * @param {boolean} concatenateCommonJsModules whether optimization.concatenateModules.commonjs is enabled
- * @returns {boolean} true if this dependency can be concatenated
- */
- canConcatenate(concatenateCommonJsModules) {
- return concatenateCommonJsModules;
- }
- /**
- * 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) {
- // Conservative: keep `module.exports = require(...)` (names empty) active even
- // when the parent is evaluation-only and the target is side-effect-free.
- if (this.resultUsed || this.names.length === 0) return null;
- const names = this.names;
- const getter = this.getter;
- return (connection, runtime) => {
- const parentModule = moduleGraph.getParentModule(this);
- if (!parentModule) return true;
- const used = moduleGraph
- .getExportsInfo(parentModule)
- .getUsedName(names, runtime);
- if (used !== false) return true;
- if (getter) return false;
- const refModule = connection.resolvedModule;
- if (!refModule) return true;
- return refModule.getSideEffectsConnectionState(moduleGraph);
- };
- }
- /**
- * Returns the imported id.
- * @param {ModuleGraph} moduleGraph the module graph
- * @returns {ExportInfoName[]} the imported id
- */
- getIds(moduleGraph) {
- return moduleGraph.getMeta(this)[idsSymbol] || this.ids;
- }
- /**
- * Updates ids using the provided module graph.
- * @param {ModuleGraph} moduleGraph the module graph
- * @param {ExportInfoName[]} ids the imported ids
- * @returns {void}
- */
- setIds(moduleGraph, ids) {
- moduleGraph.getMeta(this)[idsSymbol] = ids;
- }
- /**
- * 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 ids = this.getIds(moduleGraph);
- const importedModule = moduleGraph.getModule(this);
- if (
- importedModule &&
- isRequireEsmModuleExportsModule(importedModule, moduleGraph)
- ) {
- // `require(esm)` unwraps the "module.exports" named export; any
- // further property access lands on that value (which webpack does
- // not model), so only the "module.exports" export is observable.
- return [{ name: [ESM_MODULE_EXPORTS_NAME], canInline: false }];
- }
- const getFullResult = () => {
- if (ids.length === 0) {
- return Dependency.EXPORTS_OBJECT_REFERENCED;
- }
- return [
- {
- name: ids,
- canMangle: false,
- canInline: false
- }
- ];
- };
- if (this.resultUsed) return getFullResult();
- /** @type {ExportsInfo | undefined} */
- let exportsInfo = moduleGraph.getExportsInfo(
- /** @type {Module} */ (moduleGraph.getParentModule(this))
- );
- for (const name of this.names) {
- const exportInfo =
- /** @type {InstanceType<ExportInfo>} */
- (exportsInfo.getReadOnlyExportInfo(name));
- const used = exportInfo.getUsed(runtime);
- if (used === UsageState.Unused) return Dependency.NO_EXPORTS_REFERENCED;
- if (used !== UsageState.OnlyPropertiesUsed) return getFullResult();
- exportsInfo = exportInfo.exportsInfo;
- if (!exportsInfo) return getFullResult();
- }
- if (exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused) {
- return getFullResult();
- }
- /** @type {RawReferencedExports} */
- const referencedExports = [];
- for (const exportInfo of exportsInfo.orderedExports) {
- processExportInfo(
- runtime,
- referencedExports,
- [...ids, exportInfo.name],
- exportInfo,
- false
- );
- }
- return referencedExports.map((name) => ({
- name,
- canMangle: false,
- canInline: false
- }));
- }
- /**
- * Returns the exported names
- * @param {ModuleGraph} moduleGraph module graph
- * @returns {ExportsSpec | undefined} export names
- */
- getExports(moduleGraph) {
- const importedModule = moduleGraph.getModule(this);
- const esmUnwrap =
- importedModule &&
- isRequireEsmModuleExportsModule(importedModule, moduleGraph);
- if (this.names.length === 1) {
- const ids = this.getIds(moduleGraph);
- const name = this.names[0];
- const from = moduleGraph.getConnection(this);
- if (!from) return;
- const exportChain = esmUnwrap
- ? [ESM_MODULE_EXPORTS_NAME, ...ids]
- : ids.length === 0
- ? null
- : ids;
- return {
- exports: [
- {
- name,
- from,
- export: exportChain,
- // we can't mangle names that are in an empty object
- // because one could access the prototype property
- // when export isn't set yet
- canMangle: !(name in EMPTY_OBJECT) && false
- }
- ],
- dependencies: [from.module]
- };
- } else if (this.names.length > 0) {
- const name = this.names[0];
- return {
- exports: [
- {
- name,
- // we can't mangle names that are in an empty object
- // because one could access the prototype property
- // when export isn't set yet
- canMangle: !(name in EMPTY_OBJECT) && false
- }
- ],
- dependencies: undefined
- };
- }
- const from = moduleGraph.getConnection(this);
- if (!from) return;
- if (esmUnwrap) {
- // Full re-export `module.exports = require("./esm")` of a module
- // with a `"module.exports"` named export: the wrapping module's
- // `module.exports` becomes the unwrapped value, whose own
- // properties webpack cannot enumerate statically.
- return {
- exports: true,
- canMangle: false,
- dependencies: [from.module]
- };
- }
- // The imported namespace ESM hasn't been flagged by
- // FlagDependencyExportsPlugin yet (no exports determined), so its
- // `"module.exports"` unwrap eligibility is still unknown. Star-reexporting
- // now would add `__esModule` (and names) the monotonic merge can't retract
- // once the module turns out to unwrap, making the result order-dependent
- // across runtimes; defer — the `from.module` dependency re-queues us once
- // its exports (owned names, or dynamic `other`) become known.
- if (
- importedModule &&
- importedModule.getExportsType(moduleGraph, false) === "namespace"
- ) {
- const importedExportsInfo = moduleGraph.getExportsInfo(importedModule);
- if (
- importedExportsInfo.otherExportsInfo.provided === false &&
- importedExportsInfo.ownedExports[Symbol.iterator]().next().done
- ) {
- return { exports: [], dependencies: [from.module] };
- }
- }
- const reexportInfo = this.getStarReexports(
- moduleGraph,
- undefined,
- from.module
- );
- const ids = this.getIds(moduleGraph);
- if (reexportInfo) {
- return {
- exports: Array.from(
- /** @type {Exports} */
- (reexportInfo.exports),
- (name) => ({
- name,
- from,
- export: [...ids, name],
- canMangle: !(name in EMPTY_OBJECT) && false
- })
- ),
- dependencies: [from.module]
- };
- }
- return {
- exports: true,
- from: ids.length === 0 ? from : undefined,
- canMangle: false,
- dependencies: [from.module]
- };
- }
- /**
- * Gets star reexports.
- * @param {ModuleGraph} moduleGraph the module graph
- * @param {RuntimeSpec} runtime the runtime
- * @param {Module} importedModule the imported module (optional)
- * @returns {{ exports?: Exports, checked?: Checked } | undefined} information
- */
- getStarReexports(
- moduleGraph,
- runtime,
- importedModule = /** @type {Module} */ (moduleGraph.getModule(this))
- ) {
- /** @type {ExportsInfo | undefined} */
- let importedExportsInfo = moduleGraph.getExportsInfo(importedModule);
- const ids = this.getIds(moduleGraph);
- if (ids.length > 0) {
- importedExportsInfo = importedExportsInfo.getNestedExportsInfo(ids);
- }
- /** @type {ExportsInfo | undefined} */
- let exportsInfo = moduleGraph.getExportsInfo(
- /** @type {Module} */ (moduleGraph.getParentModule(this))
- );
- if (this.names.length > 0) {
- exportsInfo = exportsInfo.getNestedExportsInfo(this.names);
- }
- const noExtraExports =
- importedExportsInfo &&
- importedExportsInfo.otherExportsInfo.provided === false;
- const noExtraImports =
- exportsInfo &&
- exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused;
- if (!noExtraExports && !noExtraImports) {
- return;
- }
- const isNamespaceImport =
- importedModule.getExportsType(moduleGraph, false) === "namespace";
- /** @type {Exports} */
- const exports = new Set();
- /** @type {Checked} */
- const checked = new Set();
- if (noExtraImports) {
- for (const exportInfo of /** @type {ExportsInfo} */ (exportsInfo)
- .orderedExports) {
- const name = exportInfo.name;
- if (exportInfo.getUsed(runtime) === UsageState.Unused) continue;
- if (name === "__esModule" && isNamespaceImport) {
- exports.add(name);
- } else if (importedExportsInfo) {
- const importedExportInfo =
- importedExportsInfo.getReadOnlyExportInfo(name);
- if (importedExportInfo.provided === false) continue;
- exports.add(name);
- if (importedExportInfo.provided === true) continue;
- checked.add(name);
- } else {
- exports.add(name);
- checked.add(name);
- }
- }
- } else if (noExtraExports) {
- for (const importedExportInfo of /** @type {ExportsInfo} */ (
- importedExportsInfo
- ).orderedExports) {
- const name = importedExportInfo.name;
- if (importedExportInfo.provided === false) continue;
- if (exportsInfo) {
- const exportInfo = exportsInfo.getReadOnlyExportInfo(name);
- if (exportInfo.getUsed(runtime) === UsageState.Unused) continue;
- }
- exports.add(name);
- if (importedExportInfo.provided === true) continue;
- checked.add(name);
- }
- if (isNamespaceImport) {
- exports.add("__esModule");
- checked.delete("__esModule");
- }
- }
- return { exports, checked };
- }
- /**
- * Serializes this instance into the provided serializer context.
- * @param {ObjectSerializerContext} context context
- */
- serialize(context) {
- context
- .write(this.asiSafe)
- .write(this.range)
- .write(this.valueRange)
- .write(this.base)
- .write(this.names)
- .write(this.ids)
- .write(this.resultUsed)
- .write(this.getter);
- super.serialize(context);
- }
- /**
- * Restores this instance from the provided deserializer context.
- * @param {ObjectDeserializerContext} context context
- */
- deserialize(context) {
- this.asiSafe = context.read();
- const c1 = context.rest;
- this.range = c1.read();
- const c2 = c1.rest;
- this.valueRange = c2.read();
- const c3 = c2.rest;
- this.base = c3.read();
- const c4 = c3.rest;
- this.names = c4.read();
- const c5 = c4.rest;
- this.ids = c5.read();
- const c6 = c5.rest;
- this.resultUsed = c6.read();
- const c7 = c6.rest;
- this.getter = c7.read();
- super.deserialize(c7.rest);
- }
- }
- makeSerializable(
- CommonJsExportRequireDependency,
- "webpack/lib/dependencies/CommonJsExportRequireDependency"
- );
- CommonJsExportRequireDependency.Template = class CommonJsExportRequireDependencyTemplate 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,
- {
- module,
- runtimeTemplate,
- chunkGraph,
- moduleGraph,
- runtimeRequirements,
- runtime,
- concatenationScope
- }
- ) {
- const dep = /** @type {CommonJsExportRequireDependency} */ (dependency);
- // CJS exports are never inlined
- const used = /** @type {string | string[] | false} */ (
- moduleGraph.getExportsInfo(module).getUsedName(dep.names, runtime)
- );
- const connection = /** @type {ModuleGraphConnection | undefined} */ (
- moduleGraph.getConnection(dep)
- );
- // Inactive unused reexport: no module id; drop to a no-op.
- // Missing connection (e.g. IgnorePlugin) must keep the require so it throws.
- if (!used && connection && !connection.isTargetActive(runtime)) {
- source.replace(dep.range[0], dep.range[1] - 1, "/* unused reexport */ 0");
- return;
- }
- const [type, base] = handleDependencyBase(
- dep.base,
- module,
- runtimeRequirements
- );
- const importedModule = moduleGraph.getModule(dep);
- /** @type {string} */
- let requireExpr;
- if (
- concatenationScope &&
- importedModule &&
- concatenationScope.isModuleInScope(importedModule)
- ) {
- // The alias reads the target's exports value, so it binds like `require()`
- // rather than an ESM import: no interop.
- const ids = dep.getIds(moduleGraph);
- const requestedIds = isRequireEsmModuleExportsModule(
- importedModule,
- moduleGraph
- )
- ? [ESM_MODULE_EXPORTS_NAME, ...ids]
- : ids;
- requireExpr = concatenationScope.createModuleReference(importedModule, {
- ids: requestedIds.length > 0 ? requestedIds : undefined,
- asiSafe: true,
- moduleExportsAccess: true
- });
- } else {
- requireExpr = runtimeTemplate.moduleExports({
- module: importedModule,
- chunkGraph,
- request: dep.request,
- weak: dep.weak,
- runtimeRequirements
- });
- if (importedModule) {
- const ids = dep.getIds(moduleGraph);
- const esmRequireAccess = getRequireEsmModuleExportsAccess(
- importedModule,
- moduleGraph,
- runtime
- );
- if (esmRequireAccess !== null) {
- requireExpr += `${esmRequireAccess}${propertyAccess(ids)}`;
- } else {
- // CJS exports are never inlined
- const usedImported = /** @type {string | string[] | false} */ (
- moduleGraph.getExportsInfo(importedModule).getUsedName(ids, runtime)
- );
- if (usedImported) {
- const comment = equals(usedImported, ids)
- ? ""
- : `${Template.toNormalComment(propertyAccess(ids))} `;
- requireExpr += `${comment}${propertyAccess(/** @type {string[]} */ (usedImported))}`;
- }
- }
- }
- }
- switch (type) {
- case "expression":
- source.replace(
- dep.range[0],
- dep.range[1] - 1,
- used
- ? `${base}${propertyAccess(/** @type {string[]} */ (used))} = ${requireExpr}`
- : `/* unused reexport */ ${requireExpr}`
- );
- return;
- case "Object.defineProperty": {
- // `Object.defineProperty(exports, "name", { value: require("...") })`
- // or the lazy getter form `{ get: () => require("...") }` used by
- // barrel files (e.g. webpack's own `lib/index.js`).
- const valueRange = /** @type {Range} */ (dep.valueRange);
- if (!used) {
- // Active unused eager reexport: keep side effects. Unused getters
- // are inactive and already replaced with `0` above.
- source.replace(
- dep.range[0],
- dep.range[1] - 1,
- `/* unused reexport */ ${requireExpr}`
- );
- return;
- }
- const descriptor = dep.getter
- ? "enumerable: true, get: () => ("
- : "value: (";
- source.replace(
- dep.range[0],
- valueRange[0] - 1,
- `Object.defineProperty(${base}${propertyAccess(
- /** @type {string[]} */ (used).slice(0, -1)
- )}, ${JSON.stringify(used[used.length - 1])}, { ${descriptor}`
- );
- source.replace(valueRange[0], valueRange[1] - 1, requireExpr);
- source.replace(valueRange[1], dep.range[1] - 1, ") })");
- return;
- }
- default:
- throw new Error("Unexpected type");
- }
- }
- };
- CommonJsExportRequireDependency.idsSymbol = idsSymbol;
- module.exports = CommonJsExportRequireDependency;
|