SystemLibraryPlugin.js 8.1 KB

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