SystemLibraryPlugin.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Joel Denning @joeldenning
  4. */
  5. "use strict";
  6. const { ConcatSource } = require("webpack-sources");
  7. const { UsageState } = require("../ExportsInfo");
  8. const ExternalModule = require("../ExternalModule");
  9. const Template = require("../Template");
  10. const propertyAccess = require("../util/propertyAccess");
  11. const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
  12. /** @typedef {import("webpack-sources").Source} Source */
  13. /** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
  14. /** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
  15. /** @typedef {import("../Chunk")} Chunk */
  16. /** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
  17. /** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
  18. /** @typedef {import("../util/Hash")} Hash */
  19. /**
  20. * @template T
  21. * @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T>
  22. */
  23. /**
  24. * @typedef {object} SystemLibraryPluginOptions
  25. * @property {LibraryType} type
  26. */
  27. /**
  28. * @typedef {object} SystemLibraryPluginParsed
  29. * @property {string} name
  30. */
  31. /**
  32. * @typedef {SystemLibraryPluginParsed} T
  33. * @extends {AbstractLibraryPlugin<SystemLibraryPluginParsed>}
  34. */
  35. class SystemLibraryPlugin extends AbstractLibraryPlugin {
  36. /**
  37. * @param {SystemLibraryPluginOptions} options the plugin options
  38. */
  39. constructor(options) {
  40. super({
  41. pluginName: "SystemLibraryPlugin",
  42. type: options.type
  43. });
  44. }
  45. /**
  46. * @param {LibraryOptions} library normalized library option
  47. * @returns {T | false} preprocess as needed by overriding
  48. */
  49. parseOptions(library) {
  50. const { name } = library;
  51. if (name && typeof name !== "string") {
  52. throw new Error(
  53. `System.js library name must be a simple string or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
  54. );
  55. }
  56. const _name = /** @type {string} */ (name);
  57. return {
  58. name: _name
  59. };
  60. }
  61. /**
  62. * @param {Source} source source
  63. * @param {RenderContext} renderContext render context
  64. * @param {LibraryContext<T>} libraryContext context
  65. * @returns {Source} source with library export
  66. */
  67. render(source, { chunkGraph, moduleGraph, chunk }, { options, compilation }) {
  68. const modules = chunkGraph
  69. .getChunkModules(chunk)
  70. .filter(
  71. (m) => m instanceof ExternalModule && m.externalType === "system"
  72. );
  73. const externals = /** @type {ExternalModule[]} */ (modules);
  74. // The name this bundle should be registered as with System
  75. const name = options.name
  76. ? `${JSON.stringify(compilation.getPath(options.name, { chunk }))}, `
  77. : "";
  78. // The array of dependencies that are external to webpack and will be provided by System
  79. const systemDependencies = JSON.stringify(
  80. externals.map((m) =>
  81. typeof m.request === "object" && !Array.isArray(m.request)
  82. ? m.request.amd
  83. : m.request
  84. )
  85. );
  86. // The name of the variable provided by System for exporting
  87. const dynamicExport = "__WEBPACK_DYNAMIC_EXPORT__";
  88. // An array of the internal variable names for the webpack externals
  89. const externalWebpackNames = externals.map(
  90. (m) =>
  91. `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
  92. `${chunkGraph.getModuleId(m)}`
  93. )}__`
  94. );
  95. // Declaring variables for the internal variable names for the webpack externals
  96. const externalVarDeclarations = externalWebpackNames
  97. .map((name) => `var ${name} = {};`)
  98. .join("\n");
  99. // Define __esModule flag on all internal variables and helpers
  100. /** @type {string[]} */
  101. const externalVarInitialization = [];
  102. // The system.register format requires an array of setter functions for externals.
  103. const setters =
  104. externalWebpackNames.length === 0
  105. ? ""
  106. : Template.asString([
  107. "setters: [",
  108. Template.indent(
  109. externals
  110. .map((module, i) => {
  111. const external = externalWebpackNames[i];
  112. const exportsInfo = moduleGraph.getExportsInfo(module);
  113. const otherUnused =
  114. exportsInfo.otherExportsInfo.getUsed(chunk.runtime) ===
  115. UsageState.Unused;
  116. const instructions = [];
  117. const handledNames = [];
  118. for (const exportInfo of exportsInfo.orderedExports) {
  119. const used = exportInfo.getUsedName(
  120. undefined,
  121. chunk.runtime
  122. );
  123. if (used) {
  124. if (otherUnused || used !== exportInfo.name) {
  125. if (exportInfo.name === "default") {
  126. // Ideally we should use `module && module.__esModule ? module['default'] : module`
  127. // But we need to keep compatibility with SystemJS format libraries (they are using `default`) and bundled SystemJS libraries from commonjs format
  128. instructions.push(
  129. `${external}${propertyAccess([
  130. used
  131. ])} = module["default"] || module;`
  132. );
  133. } else {
  134. instructions.push(
  135. `${external}${propertyAccess([
  136. used
  137. ])} = module${propertyAccess([exportInfo.name])};`
  138. );
  139. }
  140. handledNames.push(exportInfo.name);
  141. }
  142. } else {
  143. handledNames.push(exportInfo.name);
  144. }
  145. }
  146. if (!otherUnused) {
  147. if (
  148. !Array.isArray(module.request) ||
  149. module.request.length === 1
  150. ) {
  151. externalVarInitialization.push(
  152. `Object.defineProperty(${external}, "__esModule", { value: true });`
  153. );
  154. }
  155. // See comment above
  156. instructions.push(
  157. `${external}["default"] = module["default"] || module;`
  158. );
  159. if (handledNames.length > 0) {
  160. const name = `${external}handledNames`;
  161. externalVarInitialization.push(
  162. `var ${name} = ${JSON.stringify(handledNames)};`
  163. );
  164. instructions.push(
  165. Template.asString([
  166. "Object.keys(module).forEach(function(key) {",
  167. Template.indent([
  168. `if(${name}.indexOf(key) >= 0)`,
  169. Template.indent(`${external}[key] = module[key];`)
  170. ]),
  171. "});"
  172. ])
  173. );
  174. } else {
  175. instructions.push(
  176. Template.asString([
  177. "Object.keys(module).forEach(function(key) {",
  178. Template.indent([`${external}[key] = module[key];`]),
  179. "});"
  180. ])
  181. );
  182. }
  183. }
  184. if (instructions.length === 0) return "function() {}";
  185. return Template.asString([
  186. "function(module) {",
  187. Template.indent(instructions),
  188. "}"
  189. ]);
  190. })
  191. .join(",\n")
  192. ),
  193. "],"
  194. ]);
  195. return new ConcatSource(
  196. Template.asString([
  197. `System.register(${name}${systemDependencies}, function(${dynamicExport}, __system_context__) {`,
  198. Template.indent([
  199. externalVarDeclarations,
  200. Template.asString(externalVarInitialization),
  201. "return {",
  202. Template.indent([
  203. setters,
  204. "execute: function() {",
  205. Template.indent(`${dynamicExport}(`)
  206. ])
  207. ]),
  208. ""
  209. ]),
  210. source,
  211. Template.asString([
  212. "",
  213. Template.indent([
  214. Template.indent([Template.indent([");"]), "}"]),
  215. "};"
  216. ]),
  217. "})"
  218. ])
  219. );
  220. }
  221. /**
  222. * @param {Chunk} chunk the chunk
  223. * @param {Hash} hash hash
  224. * @param {ChunkHashContext} chunkHashContext chunk hash context
  225. * @param {LibraryContext<T>} libraryContext context
  226. * @returns {void}
  227. */
  228. chunkHash(chunk, hash, chunkHashContext, { options, compilation }) {
  229. hash.update("SystemLibraryPlugin");
  230. if (options.name) {
  231. hash.update(compilation.getPath(options.name, { chunk }));
  232. }
  233. }
  234. }
  235. module.exports = SystemLibraryPlugin;