| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const Entrypoint = require("../Entrypoint");
- const RuntimeGlobals = require("../RuntimeGlobals");
- const { propertyAccess } = require("../util/property");
- /** @import ChunkGraph from "../ChunkGraph" */
- /** @import Module, { RuntimeRequirements } from "../Module" */
- /** @import RuntimeTemplate from "../RuntimeTemplate" */
- /** @import ModuleGraph from "../ModuleGraph" */
- /** @import { RuntimeSpec } from "../util/runtime" */
- /** @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;
- module.exports.getRequireEsmModuleExportsAccess =
- getRequireEsmModuleExportsAccess;
- /**
- * Whether the base keyword reads the module's top-level `this`.
- * @param {CommonJSDependencyBaseKeywords} depBase commonjs dependency base
- * @returns {boolean} true when the base is `this`
- */
- const isThisBase = (depBase) =>
- depBase === "this" || depBase === "Object.defineProperty(this)";
- /**
- * A classic worker script's top-level `this` is its global scope, not the
- * module's exports. True only for a runtime every entry of which is a worker,
- * so a module imported elsewhere keeps exports semantics in that runtime.
- * @param {Module} module the module
- * @param {ChunkGraph} chunkGraph the chunk graph
- * @param {RuntimeSpec} runtime the runtime code is generated for
- * @param {RuntimeTemplate=} runtimeTemplate the runtime template
- * @returns {boolean} true when `this` is the worker's global scope
- */
- const isWorkerEntryThis = (module, chunkGraph, runtime, runtimeTemplate) => {
- // a module worker or worklet is an ES module, where `this` is undefined and
- // there is nothing to resolve it to
- if (
- typeof runtime !== "string" ||
- runtimeTemplate === undefined ||
- runtimeTemplate.isModule()
- ) {
- return false;
- }
- let workerEntry = false;
- for (const chunk of chunkGraph.getModuleChunks(module)) {
- if (chunk.runtime !== runtime) continue;
- for (const group of chunk.groupsIterable) {
- if (!(group instanceof Entrypoint)) continue;
- const { options } = group;
- // an entry may pin a worker to a non-worker entry's runtime, leaving one
- // code generation for both roles: keep exports, the only safe answer
- if (!options.worker || options.worklet) return false;
- workerEntry = true;
- }
- }
- return workerEntry;
- };
- /**
- * Returns type and base.
- * @param {CommonJSDependencyBaseKeywords} depBase commonjs dependency base
- * @param {Module} module module
- * @param {RuntimeRequirements} runtimeRequirements runtime requirements
- * @param {boolean=} isWorkerEntry whether `this` is a classic worker's global scope
- * @returns {[string, string]} type and base
- */
- module.exports.handleDependencyBase = (
- depBase,
- module,
- runtimeRequirements,
- isWorkerEntry
- ) => {
- /** @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":
- if (isWorkerEntry) {
- runtimeRequirements.add(RuntimeGlobals.global);
- base = RuntimeGlobals.global;
- } else {
- 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)":
- if (isWorkerEntry) {
- runtimeRequirements.add(RuntimeGlobals.global);
- base = RuntimeGlobals.global;
- } else {
- runtimeRequirements.add(RuntimeGlobals.thisAsExports);
- base = "this";
- }
- type = "Object.defineProperty";
- break;
- default:
- throw new Error(`Unsupported base ${depBase}`);
- }
- return [type, base];
- };
- module.exports.isRequireEsmModuleExportsModule =
- isRequireEsmModuleExportsModule;
- module.exports.isThisBase = isThisBase;
- module.exports.isWorkerEntryThis = isWorkerEntryThis;
|