SystemLibraryPlugin.js 7.1 KB

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