SystemLibraryPlugin.js 7.8 KB

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