UmdLibraryPlugin.js 10 KB

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