| 1 | /*
|
|---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
|---|
| 3 | Author Tobias Koppers @sokra
|
|---|
| 4 | */
|
|---|
| 5 |
|
|---|
| 6 | "use strict";
|
|---|
| 7 |
|
|---|
| 8 | const RuntimeGlobals = require("../RuntimeGlobals");
|
|---|
| 9 | const { propertyAccess } = require("../util/property");
|
|---|
| 10 |
|
|---|
| 11 | /** @typedef {import("../Module")} Module */
|
|---|
| 12 | /** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
|
|---|
| 13 | /** @typedef {import("../ModuleGraph")} ModuleGraph */
|
|---|
| 14 | /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
|---|
| 15 | /** @typedef {"exports" | "module.exports" | "this" | "Object.defineProperty(exports)" | "Object.defineProperty(module.exports)" | "Object.defineProperty(this)"} CommonJSDependencyBaseKeywords */
|
|---|
| 16 |
|
|---|
| 17 | /**
|
|---|
| 18 | * The well-known name of the ESM named export that, when present, is unwrapped
|
|---|
| 19 | * by CommonJS `require()` to match Node.js v23+ `require(esm)` semantics:
|
|---|
| 20 | * https://nodejs.org/docs/latest/api/modules.html#loading-ecmascript-modules-using-require
|
|---|
| 21 | */
|
|---|
| 22 | const ESM_MODULE_EXPORTS_NAME = "module.exports";
|
|---|
| 23 |
|
|---|
| 24 | /**
|
|---|
| 25 | * Whether `require()` of `importedModule` would trigger Node.js's
|
|---|
| 26 | * `require(esm)` `"module.exports"` named-export unwrap. This is the
|
|---|
| 27 | * usage-independent eligibility check: it only looks at module type and
|
|---|
| 28 | * whether the export is declared, so it can be safely used from
|
|---|
| 29 | * `getReferencedExports` before usage info is finalized (otherwise the
|
|---|
| 30 | * check would be circular — we'd need `"module.exports"` to already be
|
|---|
| 31 | * marked used in order to ask whether to mark it used).
|
|---|
| 32 | * @param {Module} importedModule the imported module
|
|---|
| 33 | * @param {ModuleGraph} moduleGraph the module graph
|
|---|
| 34 | * @returns {boolean} true if `require()` should unwrap `"module.exports"`
|
|---|
| 35 | */
|
|---|
| 36 | const isRequireEsmModuleExportsModule = (importedModule, moduleGraph) => {
|
|---|
| 37 | if (importedModule.getExportsType(moduleGraph, false) !== "namespace") {
|
|---|
| 38 | return false;
|
|---|
| 39 | }
|
|---|
| 40 | const exportsInfo = moduleGraph.getExportsInfo(importedModule);
|
|---|
| 41 | const exportInfo = exportsInfo.getReadOnlyExportInfo(ESM_MODULE_EXPORTS_NAME);
|
|---|
| 42 | return exportInfo.provided === true;
|
|---|
| 43 | };
|
|---|
| 44 |
|
|---|
| 45 | /**
|
|---|
| 46 | * When CommonJS `require()` resolves to an ES module that has a named export
|
|---|
| 47 | * with the literal string name `"module.exports"`, Node.js returns the value of
|
|---|
| 48 | * that export instead of the namespace object. Returns the property-access
|
|---|
| 49 | * expression to apply to the require result for that unwrapping, or `null` if
|
|---|
| 50 | * the imported module is not eligible (not strictly ESM, or no such export,
|
|---|
| 51 | * or the export was tree-shaken away).
|
|---|
| 52 | * @param {Module} importedModule the imported module
|
|---|
| 53 | * @param {ModuleGraph} moduleGraph the module graph
|
|---|
| 54 | * @param {RuntimeSpec} runtime the runtime for which the module is analysed
|
|---|
| 55 | * @returns {string | null} property-access expression (e.g. `["module.exports"]`), or `null`
|
|---|
| 56 | */
|
|---|
| 57 | const getRequireEsmModuleExportsAccess = (
|
|---|
| 58 | importedModule,
|
|---|
| 59 | moduleGraph,
|
|---|
| 60 | runtime
|
|---|
| 61 | ) => {
|
|---|
| 62 | if (!isRequireEsmModuleExportsModule(importedModule, moduleGraph)) {
|
|---|
| 63 | return null;
|
|---|
| 64 | }
|
|---|
| 65 | const exportsInfo = moduleGraph.getExportsInfo(importedModule);
|
|---|
| 66 | const usedName = exportsInfo.getUsedName([ESM_MODULE_EXPORTS_NAME], runtime);
|
|---|
| 67 | if (usedName === false) return null;
|
|---|
| 68 | return propertyAccess(/** @type {readonly string[]} */ (usedName));
|
|---|
| 69 | };
|
|---|
| 70 |
|
|---|
| 71 | module.exports.ESM_MODULE_EXPORTS_NAME = ESM_MODULE_EXPORTS_NAME;
|
|---|
| 72 | module.exports.getRequireEsmModuleExportsAccess =
|
|---|
| 73 | getRequireEsmModuleExportsAccess;
|
|---|
| 74 |
|
|---|
| 75 | /**
|
|---|
| 76 | * Returns type and base.
|
|---|
| 77 | * @param {CommonJSDependencyBaseKeywords} depBase commonjs dependency base
|
|---|
| 78 | * @param {Module} module module
|
|---|
| 79 | * @param {RuntimeRequirements} runtimeRequirements runtime requirements
|
|---|
| 80 | * @returns {[string, string]} type and base
|
|---|
| 81 | */
|
|---|
| 82 | module.exports.handleDependencyBase = (
|
|---|
| 83 | depBase,
|
|---|
| 84 | module,
|
|---|
| 85 | runtimeRequirements
|
|---|
| 86 | ) => {
|
|---|
| 87 | /** @type {string} */
|
|---|
| 88 | let base;
|
|---|
| 89 | /** @type {string} */
|
|---|
| 90 | let type;
|
|---|
| 91 | switch (depBase) {
|
|---|
| 92 | case "exports":
|
|---|
| 93 | runtimeRequirements.add(RuntimeGlobals.exports);
|
|---|
| 94 | base = module.exportsArgument;
|
|---|
| 95 | type = "expression";
|
|---|
| 96 | break;
|
|---|
| 97 | case "module.exports":
|
|---|
| 98 | runtimeRequirements.add(RuntimeGlobals.module);
|
|---|
| 99 | base = `${module.moduleArgument}.exports`;
|
|---|
| 100 | type = "expression";
|
|---|
| 101 | break;
|
|---|
| 102 | case "this":
|
|---|
| 103 | runtimeRequirements.add(RuntimeGlobals.thisAsExports);
|
|---|
| 104 | base = "this";
|
|---|
| 105 | type = "expression";
|
|---|
| 106 | break;
|
|---|
| 107 | case "Object.defineProperty(exports)":
|
|---|
| 108 | runtimeRequirements.add(RuntimeGlobals.exports);
|
|---|
| 109 | base = module.exportsArgument;
|
|---|
| 110 | type = "Object.defineProperty";
|
|---|
| 111 | break;
|
|---|
| 112 | case "Object.defineProperty(module.exports)":
|
|---|
| 113 | runtimeRequirements.add(RuntimeGlobals.module);
|
|---|
| 114 | base = `${module.moduleArgument}.exports`;
|
|---|
| 115 | type = "Object.defineProperty";
|
|---|
| 116 | break;
|
|---|
| 117 | case "Object.defineProperty(this)":
|
|---|
| 118 | runtimeRequirements.add(RuntimeGlobals.thisAsExports);
|
|---|
| 119 | base = "this";
|
|---|
| 120 | type = "Object.defineProperty";
|
|---|
| 121 | break;
|
|---|
| 122 | default:
|
|---|
| 123 | throw new Error(`Unsupported base ${depBase}`);
|
|---|
| 124 | }
|
|---|
| 125 |
|
|---|
| 126 | return [type, base];
|
|---|
| 127 | };
|
|---|
| 128 | module.exports.isRequireEsmModuleExportsModule =
|
|---|
| 129 | isRequireEsmModuleExportsModule;
|
|---|