UmdLibraryPlugin.js 10 KB

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