SystemLibraryPlugin.js 8.2 KB

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