提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
5.7 KiB

  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. * @typedef {object} ModuleReferenceOptions
  19. * @property {Ids} ids the properties/exports of the module
  20. * @property {boolean} call true, when this referenced export is called
  21. * @property {boolean} directImport true, when this referenced export is directly imported (not via property access)
  22. * @property {boolean} deferredImport true, when this referenced export is deferred
  23. * @property {boolean | undefined} asiSafe if the position is ASI safe or unknown
  24. */
  25. class ConcatenationScope {
  26. /**
  27. * @param {ModuleInfo[] | Map<Module, ModuleInfo>} modulesMap all module info by module
  28. * @param {ConcatenatedModuleInfo} currentModule the current module info
  29. * @param {Set<string>} usedNames all used names
  30. */
  31. constructor(modulesMap, currentModule, usedNames) {
  32. this._currentModule = currentModule;
  33. if (Array.isArray(modulesMap)) {
  34. const map = new Map();
  35. for (const info of modulesMap) {
  36. map.set(info.module, info);
  37. }
  38. modulesMap = map;
  39. }
  40. this.usedNames = usedNames;
  41. this._modulesMap = modulesMap;
  42. }
  43. /**
  44. * @param {Module} module the referenced module
  45. * @returns {boolean} true, when it's in the scope
  46. */
  47. isModuleInScope(module) {
  48. return this._modulesMap.has(module);
  49. }
  50. /**
  51. * @param {string} exportName name of the export
  52. * @param {string} symbol identifier of the export in source code
  53. */
  54. registerExport(exportName, symbol) {
  55. if (!this._currentModule.exportMap) {
  56. this._currentModule.exportMap = new Map();
  57. }
  58. if (!this._currentModule.exportMap.has(exportName)) {
  59. this._currentModule.exportMap.set(exportName, symbol);
  60. }
  61. }
  62. /**
  63. * @param {string} exportName name of the export
  64. * @param {string} expression expression to be used
  65. */
  66. registerRawExport(exportName, expression) {
  67. if (!this._currentModule.rawExportMap) {
  68. this._currentModule.rawExportMap = new Map();
  69. }
  70. if (!this._currentModule.rawExportMap.has(exportName)) {
  71. this._currentModule.rawExportMap.set(exportName, expression);
  72. }
  73. }
  74. /**
  75. * @param {string} exportName name of the export
  76. * @returns {string | undefined} the expression of the export
  77. */
  78. getRawExport(exportName) {
  79. if (!this._currentModule.rawExportMap) {
  80. return undefined;
  81. }
  82. return this._currentModule.rawExportMap.get(exportName);
  83. }
  84. /**
  85. * @param {string} exportName name of the export
  86. * @param {string} expression expression to be used
  87. */
  88. setRawExportMap(exportName, expression) {
  89. if (!this._currentModule.rawExportMap) {
  90. this._currentModule.rawExportMap = new Map();
  91. }
  92. if (this._currentModule.rawExportMap.has(exportName)) {
  93. this._currentModule.rawExportMap.set(exportName, expression);
  94. }
  95. }
  96. /**
  97. * @param {string} symbol identifier of the export in source code
  98. */
  99. registerNamespaceExport(symbol) {
  100. this._currentModule.namespaceExportSymbol = symbol;
  101. }
  102. /**
  103. * @param {string} symbol identifier of the export in source code
  104. * @returns {boolean} registered success
  105. */
  106. registerUsedName(symbol) {
  107. if (this.usedNames.has(symbol)) {
  108. return false;
  109. }
  110. this.usedNames.add(symbol);
  111. return true;
  112. }
  113. /**
  114. * @param {Module} module the referenced module
  115. * @param {Partial<ModuleReferenceOptions>} options options
  116. * @returns {string} the reference as identifier
  117. */
  118. createModuleReference(
  119. module,
  120. {
  121. ids = undefined,
  122. call = false,
  123. directImport = false,
  124. deferredImport = false,
  125. asiSafe = false
  126. }
  127. ) {
  128. const info = /** @type {ModuleInfo} */ (this._modulesMap.get(module));
  129. const callFlag = call ? "_call" : "";
  130. const directImportFlag = directImport ? "_directImport" : "";
  131. const deferredImportFlag = deferredImport ? "_deferredImport" : "";
  132. const asiSafeFlag = asiSafe
  133. ? "_asiSafe1"
  134. : asiSafe === false
  135. ? "_asiSafe0"
  136. : "";
  137. const exportData = ids
  138. ? Buffer.from(JSON.stringify(ids), "utf8").toString("hex")
  139. : "ns";
  140. // a "._" is appended to allow "delete ...", which would cause a SyntaxError in strict mode
  141. return `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${callFlag}${directImportFlag}${deferredImportFlag}${asiSafeFlag}__._`;
  142. }
  143. /**
  144. * @param {string} name the identifier
  145. * @returns {boolean} true, when it's an module reference
  146. */
  147. static isModuleReference(name) {
  148. return MODULE_REFERENCE_REGEXP.test(name);
  149. }
  150. /**
  151. * @param {string} name the identifier
  152. * @returns {ModuleReferenceOptions & { index: number } | null} parsed options and index
  153. */
  154. static matchModuleReference(name) {
  155. const match = MODULE_REFERENCE_REGEXP.exec(name);
  156. if (!match) return null;
  157. const index = Number(match[1]);
  158. const asiSafe = match[6];
  159. return {
  160. index,
  161. ids:
  162. match[2] === "ns"
  163. ? []
  164. : JSON.parse(Buffer.from(match[2], "hex").toString("utf8")),
  165. call: Boolean(match[3]),
  166. directImport: Boolean(match[4]),
  167. deferredImport: Boolean(match[5]),
  168. asiSafe: asiSafe ? asiSafe === "1" : undefined
  169. };
  170. }
  171. }
  172. ConcatenationScope.DEFAULT_EXPORT = DEFAULT_EXPORT;
  173. ConcatenationScope.NAMESPACE_OBJECT_EXPORT = NAMESPACE_OBJECT_EXPORT;
  174. /** @type {WeakMap<Chunk, Set<string>>} */
  175. ConcatenationScope.chunkUsedNames = new WeakMap();
  176. module.exports = ConcatenationScope;