UmdLibraryPlugin.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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. return accessor.map(a => `[${JSON.stringify(a)}]`).join("");
  31. };
  32. /**
  33. * @param {string|undefined} base the path prefix
  34. * @param {string|string[]} 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. 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[]} name
  61. * @property {LibraryCustomUmdObject} names
  62. * @property {string | LibraryCustomUmdCommentObject} auxiliaryComment
  63. * @property {boolean} 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} */
  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.concat(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. return compilation.getPath(str, {
  149. chunk
  150. });
  151. };
  152. /**
  153. * @param {ExternalModule[]} modules external modules
  154. * @returns {string} result
  155. */
  156. const externalsDepsArray = modules => {
  157. return `[${replaceKeys(
  158. modules
  159. .map(m =>
  160. JSON.stringify(
  161. typeof m.request === "object"
  162. ? /** @type {RequestRecord} */
  163. (m.request).amd
  164. : m.request
  165. )
  166. )
  167. .join(", ")
  168. )}]`;
  169. };
  170. /**
  171. * @param {ExternalModule[]} modules external modules
  172. * @returns {string} result
  173. */
  174. const externalsRootArray = modules => {
  175. return replaceKeys(
  176. modules
  177. .map(m => {
  178. let request = m.request;
  179. if (typeof request === "object")
  180. request =
  181. /** @type {RequestRecord} */
  182. (request).root;
  183. return `root${accessorToObjectAccess([].concat(request))}`;
  184. })
  185. .join(", ")
  186. );
  187. };
  188. /**
  189. * @param {string} type the type
  190. * @returns {string} external require array
  191. */
  192. const externalsRequireArray = type => {
  193. return replaceKeys(
  194. externals
  195. .map(m => {
  196. let expr;
  197. let request = m.request;
  198. if (typeof request === "object") {
  199. request =
  200. /** @type {RequestRecord} */
  201. (request)[type];
  202. }
  203. if (request === undefined) {
  204. throw new Error(
  205. "Missing external configuration for type:" + type
  206. );
  207. }
  208. if (Array.isArray(request)) {
  209. expr = `require(${JSON.stringify(
  210. request[0]
  211. )})${accessorToObjectAccess(request.slice(1))}`;
  212. } else {
  213. expr = `require(${JSON.stringify(request)})`;
  214. }
  215. if (m.isOptional(moduleGraph)) {
  216. expr = `(function webpackLoadOptionalExternalModule() { try { return ${expr}; } catch(e) {} }())`;
  217. }
  218. return expr;
  219. })
  220. .join(", ")
  221. );
  222. };
  223. /**
  224. * @param {ExternalModule[]} modules external modules
  225. * @returns {string} arguments
  226. */
  227. const externalsArguments = modules => {
  228. return modules
  229. .map(
  230. m =>
  231. `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
  232. `${chunkGraph.getModuleId(m)}`
  233. )}__`
  234. )
  235. .join(", ");
  236. };
  237. /**
  238. * @param {string| string[]} library library name
  239. * @returns {string} stringified library name
  240. */
  241. const libraryName = library => {
  242. return JSON.stringify(
  243. replaceKeys(/** @type {string[]} */ ([]).concat(library).pop())
  244. );
  245. };
  246. let amdFactory;
  247. if (optionalExternals.length > 0) {
  248. const wrapperArguments = externalsArguments(requiredExternals);
  249. const factoryArguments =
  250. requiredExternals.length > 0
  251. ? externalsArguments(requiredExternals) +
  252. ", " +
  253. externalsRootArray(optionalExternals)
  254. : externalsRootArray(optionalExternals);
  255. amdFactory =
  256. `function webpackLoadOptionalExternalModuleAmd(${wrapperArguments}) {\n` +
  257. ` return factory(${factoryArguments});\n` +
  258. " }";
  259. } else {
  260. amdFactory = "factory";
  261. }
  262. const { auxiliaryComment, namedDefine, names } = options;
  263. /**
  264. * @param {keyof LibraryCustomUmdCommentObject} type type
  265. * @returns {string} comment
  266. */
  267. const getAuxiliaryComment = type => {
  268. if (auxiliaryComment) {
  269. if (typeof auxiliaryComment === "string")
  270. return "\t//" + auxiliaryComment + "\n";
  271. if (auxiliaryComment[type])
  272. return "\t//" + auxiliaryComment[type] + "\n";
  273. }
  274. return "";
  275. };
  276. return new ConcatSource(
  277. new OriginalSource(
  278. "(function webpackUniversalModuleDefinition(root, factory) {\n" +
  279. getAuxiliaryComment("commonjs2") +
  280. " if(typeof exports === 'object' && typeof module === 'object')\n" +
  281. " module.exports = factory(" +
  282. externalsRequireArray("commonjs2") +
  283. ");\n" +
  284. getAuxiliaryComment("amd") +
  285. " else if(typeof define === 'function' && define.amd)\n" +
  286. (requiredExternals.length > 0
  287. ? names.amd && namedDefine === true
  288. ? " define(" +
  289. libraryName(names.amd) +
  290. ", " +
  291. externalsDepsArray(requiredExternals) +
  292. ", " +
  293. amdFactory +
  294. ");\n"
  295. : " define(" +
  296. externalsDepsArray(requiredExternals) +
  297. ", " +
  298. amdFactory +
  299. ");\n"
  300. : names.amd && namedDefine === true
  301. ? " define(" +
  302. libraryName(names.amd) +
  303. ", [], " +
  304. amdFactory +
  305. ");\n"
  306. : " define([], " + amdFactory + ");\n") +
  307. (names.root || names.commonjs
  308. ? getAuxiliaryComment("commonjs") +
  309. " else if(typeof exports === 'object')\n" +
  310. " exports[" +
  311. libraryName(names.commonjs || names.root) +
  312. "] = factory(" +
  313. externalsRequireArray("commonjs") +
  314. ");\n" +
  315. getAuxiliaryComment("root") +
  316. " else\n" +
  317. " " +
  318. replaceKeys(
  319. accessorAccess("root", names.root || names.commonjs)
  320. ) +
  321. " = factory(" +
  322. externalsRootArray(externals) +
  323. ");\n"
  324. : " else {\n" +
  325. (externals.length > 0
  326. ? " var a = typeof exports === 'object' ? factory(" +
  327. externalsRequireArray("commonjs") +
  328. ") : factory(" +
  329. externalsRootArray(externals) +
  330. ");\n"
  331. : " var a = factory();\n") +
  332. " for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n" +
  333. " }\n") +
  334. `})(${runtimeTemplate.outputOptions.globalObject}, ${
  335. runtimeTemplate.supportsArrowFunction()
  336. ? `(${externalsArguments(externals)}) =>`
  337. : `function(${externalsArguments(externals)})`
  338. } {\nreturn `,
  339. "webpack/universalModuleDefinition"
  340. ),
  341. source,
  342. ";\n})"
  343. );
  344. }
  345. }
  346. module.exports = UmdLibraryPlugin;