ConcatenationScope.js 5.5 KB

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