ConcatenationScope.js 5.4 KB

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