HarmonyExportImportedSpecifierDependency.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const NullDependency = require("./NullDependency");
  7. const HarmonyModulesHelpers = require("./HarmonyModulesHelpers");
  8. class HarmonyExportImportedSpecifierDependency extends NullDependency {
  9. constructor(originModule, importDependency, importedVar, id, name) {
  10. super();
  11. this.originModule = originModule;
  12. this.importDependency = importDependency;
  13. this.importedVar = importedVar;
  14. this.id = id;
  15. this.name = name;
  16. }
  17. get type() {
  18. return "harmony export imported specifier";
  19. }
  20. getReference() {
  21. const name = this.name;
  22. const used = this.originModule.isUsed(name);
  23. const active = HarmonyModulesHelpers.isActive(this.originModule, this);
  24. const importedModule = this.importDependency.module;
  25. if(!importedModule || !used || !active) return null;
  26. if(!this.originModule.usedExports) return null;
  27. if(name) {
  28. const nameIsNotInUsedExports = Array.isArray(this.originModule.usedExports) && this.originModule.usedExports.indexOf(name) < 0;
  29. if(nameIsNotInUsedExports) return null;
  30. // export { name as name }
  31. if(this.id) {
  32. return {
  33. module: importedModule,
  34. importedNames: [this.id]
  35. };
  36. }
  37. // export { * as name }
  38. return {
  39. module: importedModule,
  40. importedNames: true
  41. };
  42. }
  43. // export *
  44. if(Array.isArray(this.originModule.usedExports)) {
  45. // reexport * with known used exports
  46. var activeExports = HarmonyModulesHelpers.getActiveExports(this.originModule, this);
  47. if(Array.isArray(importedModule.providedExports)) {
  48. return {
  49. module: importedModule,
  50. importedNames: this.originModule.usedExports.filter((id) => {
  51. const notInActiveExports = activeExports.indexOf(id) < 0;
  52. const notDefault = id !== "default";
  53. const inProvidedExports = importedModule.providedExports.indexOf(id) >= 0;
  54. return notInActiveExports && notDefault && inProvidedExports;
  55. }),
  56. };
  57. }
  58. return {
  59. module: importedModule,
  60. importedNames: this.originModule.usedExports.filter(id => {
  61. const notInActiveExports = activeExports.indexOf(id) < 0;
  62. const notDefault = id !== "default";
  63. return notInActiveExports && notDefault;
  64. }),
  65. };
  66. }
  67. if(Array.isArray(importedModule.providedExports)) {
  68. return {
  69. module: importedModule,
  70. importedNames: importedModule.providedExports.filter(id => id !== "default"),
  71. };
  72. }
  73. return {
  74. module: importedModule,
  75. importedNames: true,
  76. };
  77. }
  78. getExports() {
  79. if(this.name) {
  80. return {
  81. exports: [this.name]
  82. };
  83. }
  84. const importedModule = this.importDependency.module;
  85. if(!importedModule) {
  86. // no imported module available
  87. return {
  88. exports: null
  89. };
  90. }
  91. if(Array.isArray(importedModule.providedExports)) {
  92. return {
  93. exports: importedModule.providedExports.filter(id => id !== "default"),
  94. dependencies: [importedModule]
  95. };
  96. }
  97. if(importedModule.providedExports) {
  98. return {
  99. exports: true
  100. };
  101. }
  102. return {
  103. exports: null,
  104. dependencies: [importedModule]
  105. };
  106. }
  107. describeHarmonyExport() {
  108. const importedModule = this.importDependency.module;
  109. if(!this.name && importedModule && Array.isArray(importedModule.providedExports)) {
  110. // for a star export and when we know which exports are provided, we can tell so
  111. return {
  112. exportedName: importedModule.providedExports,
  113. precedence: 3
  114. };
  115. }
  116. return {
  117. exportedName: this.name,
  118. precedence: this.name ? 2 : 3
  119. };
  120. }
  121. updateHash(hash) {
  122. super.updateHash(hash);
  123. const hashValue = this.getHashValue(this.importDependency.module);
  124. hash.update(hashValue);
  125. }
  126. getHashValue(importedModule) {
  127. if(!importedModule) {
  128. return "";
  129. }
  130. const stringifiedUsedExport = JSON.stringify(importedModule.usedExports);
  131. const stringifiedProvidedExport = JSON.stringify(importedModule.providedExports);
  132. return importedModule.used + stringifiedUsedExport + stringifiedProvidedExport;
  133. }
  134. }
  135. module.exports = HarmonyExportImportedSpecifierDependency;
  136. HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedSpecifierDependencyTemplate {
  137. apply(dep, source, outputOptions, requestShortener) {
  138. const content = this.getContent(dep);
  139. source.insert(-1, content);
  140. }
  141. getContent(dep) {
  142. const name = dep.importedVar;
  143. const used = dep.originModule.isUsed(dep.name);
  144. const importedModule = dep.importDependency.module;
  145. const active = HarmonyModulesHelpers.isActive(dep.originModule, dep);
  146. const importsExportsUnknown = !importedModule || !Array.isArray(importedModule.providedExports);
  147. const getReexportStatement = this.reexportStatementCreator(dep.originModule, importsExportsUnknown, name);
  148. // we want to rexport something, but the export isn't used
  149. if(!used) {
  150. return "/* unused harmony reexport " + dep.name + " */\n";
  151. }
  152. // we want to reexport something but another exports overrides this one
  153. if(!active) {
  154. return "/* inactive harmony reexport " + (dep.name || "namespace") + " */\n";
  155. }
  156. // we want to reexport the default export from a non-hamory module
  157. const isNotAHarmonyModule = !(importedModule && (!importedModule.meta || importedModule.meta.harmonyModule));
  158. if(dep.name && dep.id === "default" && isNotAHarmonyModule) {
  159. return "/* harmony reexport (default from non-hamory) */ " + getReexportStatement(JSON.stringify(used), null);
  160. }
  161. // we want to reexport a key as new key
  162. if(dep.name && dep.id) {
  163. var idUsed = importedModule && importedModule.isUsed(dep.id);
  164. return "/* harmony reexport (binding) */ " + getReexportStatement(JSON.stringify(used), JSON.stringify(idUsed));
  165. }
  166. // we want to reexport the module object as named export
  167. if(dep.name) {
  168. return "/* harmony reexport (module object) */ " + getReexportStatement(JSON.stringify(used), "");
  169. }
  170. // we know which exports are used
  171. if(Array.isArray(dep.originModule.usedExports)) {
  172. const activeExports = HarmonyModulesHelpers.getActiveExports(dep.originModule, dep);
  173. const items = dep.originModule.usedExports.map(function(id) {
  174. if(id === "default") return;
  175. if(activeExports.indexOf(id) >= 0) return;
  176. if(importedModule.isProvided(id) === false) return;
  177. var exportUsed = dep.originModule.isUsed(id);
  178. var idUsed = importedModule && importedModule.isUsed(id);
  179. return [exportUsed, idUsed];
  180. }).filter(Boolean);
  181. if(items.length === 0) {
  182. return "/* unused harmony namespace reexport */\n";
  183. }
  184. return items.map(function(item) {
  185. return "/* harmony namespace reexport (by used) */ " + getReexportStatement(JSON.stringify(item[0]), JSON.stringify(item[1]));
  186. }).join("");
  187. }
  188. // not sure which exports are used, but we know which are provided
  189. if(dep.originModule.usedExports && importedModule && Array.isArray(importedModule.providedExports)) {
  190. const activeExports = HarmonyModulesHelpers.getActiveExports(dep.originModule, dep);
  191. const items = importedModule.providedExports.map(function(id) {
  192. if(id === "default") return;
  193. if(activeExports.indexOf(id) >= 0) return;
  194. var exportUsed = dep.originModule.isUsed(id);
  195. var idUsed = importedModule && importedModule.isUsed(id);
  196. return [exportUsed, idUsed];
  197. }).filter(Boolean);
  198. if(items.length === 0) {
  199. return "/* empty harmony namespace reexport */\n";
  200. }
  201. return items.map(function(item) {
  202. return "/* harmony namespace reexport (by provided) */ " + getReexportStatement(JSON.stringify(item[0]), JSON.stringify(item[1]));
  203. }).join("");
  204. }
  205. // not sure which exports are used and provided
  206. if(dep.originModule.usedExports) {
  207. const activeExports = HarmonyModulesHelpers.getActiveExports(dep.originModule, dep);
  208. let content = "/* harmony namespace reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in " + name + ") ";
  209. // Filter out exports which are defined by other exports
  210. // and filter out default export because it cannot be reexported with *
  211. if(activeExports.length > 0)
  212. content += "if(" + JSON.stringify(activeExports.concat("default")) + ".indexOf(__WEBPACK_IMPORT_KEY__) < 0) ";
  213. else
  214. content += "if(__WEBPACK_IMPORT_KEY__ !== 'default') ";
  215. const exportsName = dep.originModule.exportsArgument || "exports";
  216. return content + `(function(key) { __webpack_require__.d(${exportsName}, key, function() { return ${name}[key]; }) }(__WEBPACK_IMPORT_KEY__));\n`;
  217. }
  218. return "/* unused harmony reexport namespace */\n";
  219. }
  220. reexportStatementCreator(module, importsExportsUnknown, name) {
  221. const exportsName = module.exportsArgument || "exports";
  222. const getReexportStatement = (key, valueKey) => {
  223. const conditional = this.getConditional(importsExportsUnknown, valueKey, name);
  224. const returnValue = this.getReturnValue(valueKey);
  225. return `${conditional}__webpack_require__.d(${exportsName}, ${key}, function() { return ${name}${returnValue}; });\n`;
  226. };
  227. return getReexportStatement;
  228. }
  229. getConditional(importsExportsUnknown, valueKey, name) {
  230. if(!importsExportsUnknown || !valueKey) {
  231. return "";
  232. }
  233. return `if(__webpack_require__.o(${name}, ${valueKey})) `;
  234. }
  235. getReturnValue(valueKey) {
  236. if(valueKey === null) {
  237. return "_default.a";
  238. }
  239. return valueKey && "[" + valueKey + "]";
  240. }
  241. };