ConcatenationScope.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const {
  7. DEFAULT_EXPORT,
  8. NAMESPACE_OBJECT_EXPORT
  9. } = require("./util/concatenate");
  10. /** @typedef {import("./Chunk")} Chunk */
  11. /** @typedef {import("./Module")} Module */
  12. /** @typedef {import("./optimize/ConcatenatedModule").ConcatenatedModuleInfo} ConcatenatedModuleInfo */
  13. /** @typedef {import("./optimize/ConcatenatedModule").ModuleInfo} ModuleInfo */
  14. /** @typedef {import("./optimize/ConcatenatedModule").ExportName} Ids */
  15. const MODULE_REFERENCE_REGEXP =
  16. /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_directImport)?(_deferredImport)?(?:_asiSafe(\d))?__$/;
  17. /**
  18. * Encodes how a concatenated module reference should be interpreted when it is
  19. * later reconstructed from its placeholder identifier.
  20. * @typedef {object} ModuleReferenceOptions
  21. * @property {Ids} ids the properties or exports selected from the referenced module
  22. * @property {boolean} call true, when this referenced export is called
  23. * @property {boolean} directImport true, when this referenced export is directly imported (not via property access)
  24. * @property {boolean} deferredImport true, when this referenced export is deferred
  25. * @property {boolean | undefined} asiSafe if the position is ASI safe or unknown
  26. */
  27. /**
  28. * Tracks the symbols and cross-module references needed while rendering a
  29. * concatenated module.
  30. */
  31. class ConcatenationScope {
  32. /**
  33. * Creates the mutable scope object used while rendering a concatenated
  34. * module and its cross-module references.
  35. * @param {ModuleInfo[] | Map<Module, ModuleInfo>} modulesMap all module info by module
  36. * @param {ConcatenatedModuleInfo} currentModule the current module info
  37. * @param {Set<string>} usedNames all used names
  38. */
  39. constructor(modulesMap, currentModule, usedNames) {
  40. this._currentModule = currentModule;
  41. if (Array.isArray(modulesMap)) {
  42. /** @type {Map<Module, ConcatenatedModuleInfo>} */
  43. const map = new Map();
  44. for (const info of modulesMap) {
  45. map.set(info.module, /** @type {ConcatenatedModuleInfo} */ (info));
  46. }
  47. modulesMap = map;
  48. }
  49. this.usedNames = usedNames;
  50. this._modulesMap = modulesMap;
  51. }
  52. /**
  53. * Checks whether a module participates in the current concatenation scope.
  54. * @param {Module} module the referenced module
  55. * @returns {boolean} true, when it's in the scope
  56. */
  57. isModuleInScope(module) {
  58. return this._modulesMap.has(module);
  59. }
  60. /**
  61. * Records the symbol that should be used when the current module exports a
  62. * named binding.
  63. * @param {string} exportName name of the export
  64. * @param {string} symbol identifier of the export in source code
  65. */
  66. registerExport(exportName, symbol) {
  67. if (!this._currentModule.exportMap) {
  68. this._currentModule.exportMap = new Map();
  69. }
  70. if (!this._currentModule.exportMap.has(exportName)) {
  71. this._currentModule.exportMap.set(exportName, symbol);
  72. }
  73. }
  74. /**
  75. * Records a raw expression that can be used to reference an export without
  76. * going through the normal symbol map.
  77. * @param {string} exportName name of the export
  78. * @param {string} expression expression to be used
  79. */
  80. registerRawExport(exportName, expression) {
  81. if (!this._currentModule.rawExportMap) {
  82. this._currentModule.rawExportMap = new Map();
  83. }
  84. if (!this._currentModule.rawExportMap.has(exportName)) {
  85. this._currentModule.rawExportMap.set(exportName, expression);
  86. }
  87. }
  88. /**
  89. * Returns the raw expression registered for an export, if one exists.
  90. * @param {string} exportName name of the export
  91. * @returns {string | undefined} the expression of the export
  92. */
  93. getRawExport(exportName) {
  94. if (!this._currentModule.rawExportMap) {
  95. return undefined;
  96. }
  97. return this._currentModule.rawExportMap.get(exportName);
  98. }
  99. /**
  100. * Replaces the raw expression for an export only when that export already
  101. * has an entry in the raw export map.
  102. * @param {string} exportName name of the export
  103. * @param {string} expression expression to be used
  104. */
  105. setRawExportMap(exportName, expression) {
  106. if (!this._currentModule.rawExportMap) {
  107. this._currentModule.rawExportMap = new Map();
  108. }
  109. if (this._currentModule.rawExportMap.has(exportName)) {
  110. this._currentModule.rawExportMap.set(exportName, expression);
  111. }
  112. }
  113. /**
  114. * Records the symbol that should be used for the synthetic namespace export.
  115. * @param {string} symbol identifier of the export in source code
  116. */
  117. registerNamespaceExport(symbol) {
  118. this._currentModule.namespaceExportSymbol = symbol;
  119. }
  120. /**
  121. * Encodes a reference to another concatenated module as a placeholder
  122. * identifier that can be parsed later during code generation.
  123. * @param {Module} module the referenced module
  124. * @param {Partial<ModuleReferenceOptions>} options options
  125. * @returns {string} the reference as identifier
  126. */
  127. createModuleReference(
  128. module,
  129. {
  130. ids = undefined,
  131. call = false,
  132. directImport = false,
  133. deferredImport = false,
  134. asiSafe = false
  135. }
  136. ) {
  137. const info = /** @type {ModuleInfo} */ (this._modulesMap.get(module));
  138. const callFlag = call ? "_call" : "";
  139. const directImportFlag = directImport ? "_directImport" : "";
  140. const deferredImportFlag = deferredImport ? "_deferredImport" : "";
  141. const asiSafeFlag = asiSafe
  142. ? "_asiSafe1"
  143. : asiSafe === false
  144. ? "_asiSafe0"
  145. : "";
  146. const exportData = ids
  147. ? Buffer.from(JSON.stringify(ids), "utf8").toString("hex")
  148. : "ns";
  149. // a "._" is appended to allow "delete ...", which would cause a SyntaxError in strict mode
  150. return `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${callFlag}${directImportFlag}${deferredImportFlag}${asiSafeFlag}__._`;
  151. }
  152. /**
  153. * Checks whether an identifier is one of webpack's encoded concatenation
  154. * module references.
  155. * @param {string} name the identifier
  156. * @returns {boolean} true, when it's an module reference
  157. */
  158. static isModuleReference(name) {
  159. return MODULE_REFERENCE_REGEXP.test(name);
  160. }
  161. /**
  162. * Parses an encoded module reference back into its module index and
  163. * reference flags.
  164. * @param {string} name the identifier
  165. * @returns {ModuleReferenceOptions & { index: number } | null} parsed options and index
  166. */
  167. static matchModuleReference(name) {
  168. const match = MODULE_REFERENCE_REGEXP.exec(name);
  169. if (!match) return null;
  170. const index = Number(match[1]);
  171. const asiSafe = match[6];
  172. return {
  173. index,
  174. ids:
  175. match[2] === "ns"
  176. ? []
  177. : JSON.parse(Buffer.from(match[2], "hex").toString("utf8")),
  178. call: Boolean(match[3]),
  179. directImport: Boolean(match[4]),
  180. deferredImport: Boolean(match[5]),
  181. asiSafe: asiSafe ? asiSafe === "1" : undefined
  182. };
  183. }
  184. }
  185. ConcatenationScope.DEFAULT_EXPORT = DEFAULT_EXPORT;
  186. ConcatenationScope.NAMESPACE_OBJECT_EXPORT = NAMESPACE_OBJECT_EXPORT;
  187. module.exports = ConcatenationScope;