UmdLibraryPlugin.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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. const replaceKeys = str => {
  139. return compilation.getPath(str, {
  140. chunk
  141. });
  142. };
  143. const externalsDepsArray = modules => {
  144. return `[${replaceKeys(
  145. modules
  146. .map(m =>
  147. JSON.stringify(
  148. typeof m.request === "object" ? m.request.amd : m.request
  149. )
  150. )
  151. .join(", ")
  152. )}]`;
  153. };
  154. const externalsRootArray = modules => {
  155. return replaceKeys(
  156. modules
  157. .map(m => {
  158. let request = m.request;
  159. if (typeof request === "object") request = request.root;
  160. return `root${accessorToObjectAccess([].concat(request))}`;
  161. })
  162. .join(", ")
  163. );
  164. };
  165. const externalsRequireArray = type => {
  166. return replaceKeys(
  167. externals
  168. .map(m => {
  169. let expr;
  170. let request = m.request;
  171. if (typeof request === "object") {
  172. request = request[type];
  173. }
  174. if (request === undefined) {
  175. throw new Error(
  176. "Missing external configuration for type:" + type
  177. );
  178. }
  179. if (Array.isArray(request)) {
  180. expr = `require(${JSON.stringify(
  181. request[0]
  182. )})${accessorToObjectAccess(request.slice(1))}`;
  183. } else {
  184. expr = `require(${JSON.stringify(request)})`;
  185. }
  186. if (m.isOptional(moduleGraph)) {
  187. expr = `(function webpackLoadOptionalExternalModule() { try { return ${expr}; } catch(e) {} }())`;
  188. }
  189. return expr;
  190. })
  191. .join(", ")
  192. );
  193. };
  194. const externalsArguments = modules => {
  195. return modules
  196. .map(
  197. m =>
  198. `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
  199. `${chunkGraph.getModuleId(m)}`
  200. )}__`
  201. )
  202. .join(", ");
  203. };
  204. const libraryName = library => {
  205. return JSON.stringify(replaceKeys([].concat(library).pop()));
  206. };
  207. let amdFactory;
  208. if (optionalExternals.length > 0) {
  209. const wrapperArguments = externalsArguments(requiredExternals);
  210. const factoryArguments =
  211. requiredExternals.length > 0
  212. ? externalsArguments(requiredExternals) +
  213. ", " +
  214. externalsRootArray(optionalExternals)
  215. : externalsRootArray(optionalExternals);
  216. amdFactory =
  217. `function webpackLoadOptionalExternalModuleAmd(${wrapperArguments}) {\n` +
  218. ` return factory(${factoryArguments});\n` +
  219. " }";
  220. } else {
  221. amdFactory = "factory";
  222. }
  223. const { auxiliaryComment, namedDefine, names } = options;
  224. const getAuxiliaryComment = type => {
  225. if (auxiliaryComment) {
  226. if (typeof auxiliaryComment === "string")
  227. return "\t//" + auxiliaryComment + "\n";
  228. if (auxiliaryComment[type])
  229. return "\t//" + auxiliaryComment[type] + "\n";
  230. }
  231. return "";
  232. };
  233. return new ConcatSource(
  234. new OriginalSource(
  235. "(function webpackUniversalModuleDefinition(root, factory) {\n" +
  236. getAuxiliaryComment("commonjs2") +
  237. " if(typeof exports === 'object' && typeof module === 'object')\n" +
  238. " module.exports = factory(" +
  239. externalsRequireArray("commonjs2") +
  240. ");\n" +
  241. getAuxiliaryComment("amd") +
  242. " else if(typeof define === 'function' && define.amd)\n" +
  243. (requiredExternals.length > 0
  244. ? names.amd && namedDefine === true
  245. ? " define(" +
  246. libraryName(names.amd) +
  247. ", " +
  248. externalsDepsArray(requiredExternals) +
  249. ", " +
  250. amdFactory +
  251. ");\n"
  252. : " define(" +
  253. externalsDepsArray(requiredExternals) +
  254. ", " +
  255. amdFactory +
  256. ");\n"
  257. : names.amd && namedDefine === true
  258. ? " define(" +
  259. libraryName(names.amd) +
  260. ", [], " +
  261. amdFactory +
  262. ");\n"
  263. : " define([], " + amdFactory + ");\n") +
  264. (names.root || names.commonjs
  265. ? getAuxiliaryComment("commonjs") +
  266. " else if(typeof exports === 'object')\n" +
  267. " exports[" +
  268. libraryName(names.commonjs || names.root) +
  269. "] = factory(" +
  270. externalsRequireArray("commonjs") +
  271. ");\n" +
  272. getAuxiliaryComment("root") +
  273. " else\n" +
  274. " " +
  275. replaceKeys(
  276. accessorAccess("root", names.root || names.commonjs)
  277. ) +
  278. " = factory(" +
  279. externalsRootArray(externals) +
  280. ");\n"
  281. : " else {\n" +
  282. (externals.length > 0
  283. ? " var a = typeof exports === 'object' ? factory(" +
  284. externalsRequireArray("commonjs") +
  285. ") : factory(" +
  286. externalsRootArray(externals) +
  287. ");\n"
  288. : " var a = factory();\n") +
  289. " for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n" +
  290. " }\n") +
  291. `})(${runtimeTemplate.outputOptions.globalObject}, ${
  292. runtimeTemplate.supportsArrowFunction()
  293. ? `(${externalsArguments(externals)}) =>`
  294. : `function(${externalsArguments(externals)})`
  295. } {\nreturn `,
  296. "webpack/universalModuleDefinition"
  297. ),
  298. source,
  299. ";\n})"
  300. );
  301. }
  302. }
  303. module.exports = UmdLibraryPlugin;