UmdLibraryPlugin.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource, OriginalSource } = require("webpack-sources");
  7. const ExternalModule = require("../ExternalModule");
  8. const Template = require("../Template");
  9. const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
  10. /** @typedef {import("webpack-sources").Source} Source */
  11. /** @typedef {import("../../declarations/WebpackOptions").LibraryCustomUmdCommentObject} LibraryCustomUmdCommentObject */
  12. /** @typedef {import("../../declarations/WebpackOptions").LibraryCustomUmdObject} LibraryCustomUmdObject */
  13. /** @typedef {import("../../declarations/WebpackOptions").LibraryName} LibraryName */
  14. /** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
  15. /** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
  16. /** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
  17. /** @typedef {import("../ExternalModule").RequestRecord} RequestRecord */
  18. /**
  19. * Defines the shared type used by this module.
  20. * @template T
  21. * @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T>
  22. */
  23. /**
  24. * Accessor to object access.
  25. * @param {string[]} accessor the accessor to convert to path
  26. * @returns {string} the path
  27. */
  28. const accessorToObjectAccess = (accessor) =>
  29. accessor.map((a) => `[${JSON.stringify(a)}]`).join("");
  30. /** @typedef {string | string[]} Accessor */
  31. /**
  32. * Returns the path.
  33. * @param {string | undefined} base the path prefix
  34. * @param {Accessor} accessor the accessor
  35. * @param {string=} joinWith the element separator
  36. * @returns {string} the path
  37. */
  38. const accessorAccess = (base, accessor, joinWith = ", ") => {
  39. const accessors = Array.isArray(accessor) ? accessor : [accessor];
  40. return accessors
  41. .map((_, idx) => {
  42. const a = base
  43. ? base + accessorToObjectAccess(accessors.slice(0, idx + 1))
  44. : accessors[0] + accessorToObjectAccess(accessors.slice(1, idx + 1));
  45. if (idx === accessors.length - 1) return a;
  46. if (idx === 0 && base === undefined) {
  47. return `${a} = typeof ${a} === "object" ? ${a} : {}`;
  48. }
  49. return `${a} = ${a} || {}`;
  50. })
  51. .join(joinWith);
  52. };
  53. /**
  54. * Defines the umd library plugin options type used by this module.
  55. * @typedef {object} UmdLibraryPluginOptions
  56. * @property {LibraryType} type
  57. * @property {boolean=} optionalAmdExternalAsGlobal
  58. */
  59. /**
  60. * Defines the umd library plugin parsed type used by this module.
  61. * @typedef {object} UmdLibraryPluginParsed
  62. * @property {string | string[] | undefined} name
  63. * @property {LibraryCustomUmdObject} names
  64. * @property {string | LibraryCustomUmdCommentObject | undefined} auxiliaryComment
  65. * @property {boolean | undefined} namedDefine
  66. */
  67. /**
  68. * Represents the umd library plugin runtime component.
  69. * @typedef {UmdLibraryPluginParsed} T
  70. * @extends {AbstractLibraryPlugin<UmdLibraryPluginParsed>}
  71. */
  72. class UmdLibraryPlugin extends AbstractLibraryPlugin {
  73. /**
  74. * Creates an instance of UmdLibraryPlugin.
  75. * @param {UmdLibraryPluginOptions} options the plugin option
  76. */
  77. constructor(options) {
  78. super({
  79. pluginName: "UmdLibraryPlugin",
  80. type: options.type
  81. });
  82. /** @type {UmdLibraryPluginOptions["optionalAmdExternalAsGlobal"]} */
  83. this.optionalAmdExternalAsGlobal = options.optionalAmdExternalAsGlobal;
  84. }
  85. /**
  86. * Returns preprocess as needed by overriding.
  87. * @param {LibraryOptions} library normalized library option
  88. * @returns {T} preprocess as needed by overriding
  89. */
  90. parseOptions(library) {
  91. /** @type {LibraryName | undefined} */
  92. let name;
  93. /** @type {LibraryCustomUmdObject} */
  94. let names;
  95. if (typeof library.name === "object" && !Array.isArray(library.name)) {
  96. name = library.name.root || library.name.amd || library.name.commonjs;
  97. names = library.name;
  98. } else {
  99. name = library.name;
  100. const singleName = Array.isArray(name) ? name[0] : name;
  101. names = {
  102. commonjs: singleName,
  103. root: library.name,
  104. amd: singleName
  105. };
  106. }
  107. return {
  108. name,
  109. names,
  110. auxiliaryComment: library.auxiliaryComment,
  111. namedDefine: library.umdNamedDefine
  112. };
  113. }
  114. /**
  115. * Returns source with library export.
  116. * @param {Source} source source
  117. * @param {RenderContext} renderContext render context
  118. * @param {LibraryContext<T>} libraryContext context
  119. * @returns {Source} source with library export
  120. */
  121. render(
  122. source,
  123. { chunkGraph, runtimeTemplate, chunk, moduleGraph },
  124. { options, compilation }
  125. ) {
  126. const modules = chunkGraph
  127. .getChunkModules(chunk)
  128. .filter(
  129. (m) =>
  130. m instanceof ExternalModule &&
  131. (m.externalType === "umd" || m.externalType === "umd2")
  132. );
  133. let externals = /** @type {ExternalModule[]} */ (modules);
  134. /** @type {ExternalModule[]} */
  135. const optionalExternals = [];
  136. /** @type {ExternalModule[]} */
  137. let requiredExternals = [];
  138. if (this.optionalAmdExternalAsGlobal) {
  139. for (const m of externals) {
  140. if (m.isOptional(moduleGraph)) {
  141. optionalExternals.push(m);
  142. } else {
  143. requiredExternals.push(m);
  144. }
  145. }
  146. externals = [...requiredExternals, ...optionalExternals];
  147. } else {
  148. requiredExternals = externals;
  149. }
  150. /**
  151. * Returns the replaced keys.
  152. * @param {string} str the string to replace
  153. * @returns {string} the replaced keys
  154. */
  155. const replaceKeys = (str) =>
  156. compilation.getPath(str, {
  157. chunk
  158. });
  159. /**
  160. * Externals deps array.
  161. * @param {ExternalModule[]} modules external modules
  162. * @returns {string} result
  163. */
  164. const externalsDepsArray = (modules) =>
  165. `[${replaceKeys(
  166. modules
  167. .map((m) =>
  168. JSON.stringify(
  169. typeof m.request === "object"
  170. ? /** @type {RequestRecord} */
  171. (m.request).amd
  172. : m.request
  173. )
  174. )
  175. .join(", ")
  176. )}]`;
  177. /**
  178. * Externals root array.
  179. * @param {ExternalModule[]} modules external modules
  180. * @returns {string} result
  181. */
  182. const externalsRootArray = (modules) =>
  183. replaceKeys(
  184. modules
  185. .map((m) => {
  186. let request = m.request;
  187. if (typeof request === "object") {
  188. request =
  189. /** @type {RequestRecord} */
  190. (request).root;
  191. }
  192. return `root${accessorToObjectAccess([
  193. ...(Array.isArray(request) ? request : [request])
  194. ])}`;
  195. })
  196. .join(", ")
  197. );
  198. /**
  199. * Externals require array.
  200. * @param {string} type the type
  201. * @returns {string} external require array
  202. */
  203. const externalsRequireArray = (type) =>
  204. replaceKeys(
  205. externals
  206. .map((m) => {
  207. let request = m.request;
  208. if (typeof request === "object") {
  209. request =
  210. /** @type {RequestRecord} */
  211. (request)[type];
  212. }
  213. if (request === undefined) {
  214. throw new Error(
  215. `Missing external configuration for type:${type}`
  216. );
  217. }
  218. let expr = Array.isArray(request)
  219. ? `require(${JSON.stringify(
  220. request[0]
  221. )})${accessorToObjectAccess(request.slice(1))}`
  222. : `require(${JSON.stringify(request)})`;
  223. if (m.isOptional(moduleGraph)) {
  224. expr = `(function webpackLoadOptionalExternalModule() { try { return ${expr}; } catch(e) {} }())`;
  225. }
  226. return expr;
  227. })
  228. .join(", ")
  229. );
  230. /**
  231. * Externals arguments.
  232. * @param {ExternalModule[]} modules external modules
  233. * @returns {string} arguments
  234. */
  235. const externalsArguments = (modules) =>
  236. modules
  237. .map(
  238. (m) =>
  239. `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
  240. `${chunkGraph.getModuleId(m)}`
  241. )}__`
  242. )
  243. .join(", ");
  244. /**
  245. * Returns stringified library name.
  246. * @param {Accessor} library library name
  247. * @returns {string} stringified library name
  248. */
  249. const libraryName = (library) =>
  250. JSON.stringify(
  251. replaceKeys(
  252. /** @type {string} */
  253. ([...(Array.isArray(library) ? library : [library])].pop())
  254. )
  255. );
  256. /** @type {string} */
  257. let amdFactory;
  258. if (optionalExternals.length > 0) {
  259. const wrapperArguments = externalsArguments(requiredExternals);
  260. const factoryArguments =
  261. requiredExternals.length > 0
  262. ? `${externalsArguments(requiredExternals)}, ${externalsRootArray(
  263. optionalExternals
  264. )}`
  265. : externalsRootArray(optionalExternals);
  266. amdFactory =
  267. `function webpackLoadOptionalExternalModuleAmd(${wrapperArguments}) {\n` +
  268. ` return factory(${factoryArguments});\n` +
  269. " }";
  270. } else {
  271. amdFactory = "factory";
  272. }
  273. const { auxiliaryComment, namedDefine, names } = options;
  274. /**
  275. * Gets auxiliary comment.
  276. * @param {keyof LibraryCustomUmdCommentObject} type type
  277. * @returns {string} comment
  278. */
  279. const getAuxiliaryComment = (type) => {
  280. if (auxiliaryComment) {
  281. if (typeof auxiliaryComment === "string") {
  282. return `\t//${auxiliaryComment}\n`;
  283. }
  284. if (auxiliaryComment[type]) return `\t//${auxiliaryComment[type]}\n`;
  285. }
  286. return "";
  287. };
  288. return new ConcatSource(
  289. new OriginalSource(
  290. `(function webpackUniversalModuleDefinition(root, factory) {\n${getAuxiliaryComment(
  291. "commonjs2"
  292. )} if(typeof exports === 'object' && typeof module === 'object')\n` +
  293. ` module.exports = factory(${externalsRequireArray(
  294. "commonjs2"
  295. )});\n${getAuxiliaryComment(
  296. "amd"
  297. )} else if(typeof define === 'function' && define.amd)\n${
  298. requiredExternals.length > 0
  299. ? names.amd && namedDefine === true
  300. ? ` define(${libraryName(names.amd)}, ${externalsDepsArray(
  301. requiredExternals
  302. )}, ${amdFactory});\n`
  303. : ` define(${externalsDepsArray(requiredExternals)}, ${
  304. amdFactory
  305. });\n`
  306. : names.amd && namedDefine === true
  307. ? ` define(${libraryName(names.amd)}, [], ${amdFactory});\n`
  308. : ` define([], ${amdFactory});\n`
  309. }${
  310. names.root || names.commonjs
  311. ? `${getAuxiliaryComment(
  312. "commonjs"
  313. )} else if(typeof exports === 'object')\n` +
  314. ` exports[${libraryName(
  315. /** @type {Accessor} */
  316. (names.commonjs || names.root)
  317. )}] = factory(${externalsRequireArray(
  318. "commonjs"
  319. )});\n${getAuxiliaryComment("root")} else\n` +
  320. ` ${replaceKeys(
  321. accessorAccess(
  322. "root",
  323. /** @type {Accessor} */
  324. (names.root || names.commonjs)
  325. )
  326. )} = factory(${externalsRootArray(externals)});\n`
  327. : ` else {\n${
  328. externals.length > 0
  329. ? ` var a = typeof exports === 'object' ? factory(${externalsRequireArray(
  330. "commonjs"
  331. )}) : factory(${externalsRootArray(externals)});\n`
  332. : " var a = factory();\n"
  333. } for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n` +
  334. " }\n"
  335. }})(${runtimeTemplate.globalObject}, ${
  336. runtimeTemplate.supportsArrowFunction()
  337. ? `(${externalsArguments(externals)}) =>`
  338. : `function(${externalsArguments(externals)})`
  339. } {\nreturn `,
  340. "webpack/universalModuleDefinition"
  341. ),
  342. source,
  343. ";\n})"
  344. );
  345. }
  346. }
  347. module.exports = UmdLibraryPlugin;