UmdLibraryPlugin.js 9.9 KB

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