UNPKG

8.78 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const { ConcatSource, OriginalSource } = require("webpack-sources");
8const Template = require("./Template");
9
10/** @typedef {import("../declarations/WebpackOptions").LibraryCustomUmdObject} LibraryCustomUmdObject */
11/** @typedef {import("./Compilation")} Compilation */
12
13/**
14 * @param {string[]} accessor the accessor to convert to path
15 * @returns {string} the path
16 */
17const accessorToObjectAccess = accessor => {
18 return accessor.map(a => `[${JSON.stringify(a)}]`).join("");
19};
20
21/**
22 * @param {string=} base the path prefix
23 * @param {string|string[]} accessor the accessor
24 * @param {string=} joinWith the element separator
25 * @returns {string} the path
26 */
27const accessorAccess = (base, accessor, joinWith = ", ") => {
28 const accessors = Array.isArray(accessor) ? accessor : [accessor];
29 return accessors
30 .map((_, idx) => {
31 const a = base
32 ? base + accessorToObjectAccess(accessors.slice(0, idx + 1))
33 : accessors[0] + accessorToObjectAccess(accessors.slice(1, idx + 1));
34 if (idx === accessors.length - 1) return a;
35 if (idx === 0 && base === undefined)
36 return `${a} = typeof ${a} === "object" ? ${a} : {}`;
37 return `${a} = ${a} || {}`;
38 })
39 .join(joinWith);
40};
41
42/** @typedef {string | string[] | LibraryCustomUmdObject} UmdMainTemplatePluginName */
43
44/**
45 * @typedef {Object} AuxiliaryCommentObject
46 * @property {string} root
47 * @property {string} commonjs
48 * @property {string} commonjs2
49 * @property {string} amd
50 */
51
52/**
53 * @typedef {Object} UmdMainTemplatePluginOption
54 * @property {boolean=} optionalAmdExternalAsGlobal
55 * @property {boolean} namedDefine
56 * @property {string | AuxiliaryCommentObject} auxiliaryComment
57 */
58
59class UmdMainTemplatePlugin {
60 /**
61 * @param {UmdMainTemplatePluginName} name the name of the UMD library
62 * @param {UmdMainTemplatePluginOption} options the plugin option
63 */
64 constructor(name, options) {
65 if (typeof name === "object" && !Array.isArray(name)) {
66 this.name = name.root || name.amd || name.commonjs;
67 this.names = name;
68 } else {
69 this.name = name;
70 this.names = {
71 commonjs: name,
72 root: name,
73 amd: name
74 };
75 }
76 this.optionalAmdExternalAsGlobal = options.optionalAmdExternalAsGlobal;
77 this.namedDefine = options.namedDefine;
78 this.auxiliaryComment = options.auxiliaryComment;
79 }
80
81 /**
82 * @param {Compilation} compilation the compilation instance
83 * @returns {void}
84 */
85 apply(compilation) {
86 const { mainTemplate, chunkTemplate, runtimeTemplate } = compilation;
87
88 const onRenderWithEntry = (source, chunk, hash) => {
89 let externals = chunk
90 .getModules()
91 .filter(
92 m =>
93 m.external &&
94 (m.externalType === "umd" || m.externalType === "umd2")
95 );
96 const optionalExternals = [];
97 let requiredExternals = [];
98 if (this.optionalAmdExternalAsGlobal) {
99 for (const m of externals) {
100 if (m.optional) {
101 optionalExternals.push(m);
102 } else {
103 requiredExternals.push(m);
104 }
105 }
106 externals = requiredExternals.concat(optionalExternals);
107 } else {
108 requiredExternals = externals;
109 }
110
111 const replaceKeys = str => {
112 return mainTemplate.getAssetPath(str, {
113 hash,
114 chunk
115 });
116 };
117
118 const externalsDepsArray = modules => {
119 return `[${replaceKeys(
120 modules
121 .map(m =>
122 JSON.stringify(
123 typeof m.request === "object" ? m.request.amd : m.request
124 )
125 )
126 .join(", ")
127 )}]`;
128 };
129
130 const externalsRootArray = modules => {
131 return replaceKeys(
132 modules
133 .map(m => {
134 let request = m.request;
135 if (typeof request === "object") request = request.root;
136 return `root${accessorToObjectAccess([].concat(request))}`;
137 })
138 .join(", ")
139 );
140 };
141
142 const externalsRequireArray = type => {
143 return replaceKeys(
144 externals
145 .map(m => {
146 let expr;
147 let request = m.request;
148 if (typeof request === "object") {
149 request = request[type];
150 }
151 if (request === undefined) {
152 throw new Error(
153 "Missing external configuration for type:" + type
154 );
155 }
156 if (Array.isArray(request)) {
157 expr = `require(${JSON.stringify(
158 request[0]
159 )})${accessorToObjectAccess(request.slice(1))}`;
160 } else {
161 expr = `require(${JSON.stringify(request)})`;
162 }
163 if (m.optional) {
164 expr = `(function webpackLoadOptionalExternalModule() { try { return ${expr}; } catch(e) {} }())`;
165 }
166 return expr;
167 })
168 .join(", ")
169 );
170 };
171
172 const externalsArguments = modules => {
173 return modules
174 .map(
175 m =>
176 `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(`${m.id}`)}__`
177 )
178 .join(", ");
179 };
180
181 const libraryName = library => {
182 return JSON.stringify(replaceKeys([].concat(library).pop()));
183 };
184
185 let amdFactory;
186 if (optionalExternals.length > 0) {
187 const wrapperArguments = externalsArguments(requiredExternals);
188 const factoryArguments =
189 requiredExternals.length > 0
190 ? externalsArguments(requiredExternals) +
191 ", " +
192 externalsRootArray(optionalExternals)
193 : externalsRootArray(optionalExternals);
194 amdFactory =
195 `function webpackLoadOptionalExternalModuleAmd(${wrapperArguments}) {\n` +
196 ` return factory(${factoryArguments});\n` +
197 " }";
198 } else {
199 amdFactory = "factory";
200 }
201
202 const auxiliaryComment = this.auxiliaryComment;
203
204 const getAuxilaryComment = type => {
205 if (auxiliaryComment) {
206 if (typeof auxiliaryComment === "string")
207 return "\t//" + auxiliaryComment + "\n";
208 if (auxiliaryComment[type])
209 return "\t//" + auxiliaryComment[type] + "\n";
210 }
211 return "";
212 };
213
214 return new ConcatSource(
215 new OriginalSource(
216 "(function webpackUniversalModuleDefinition(root, factory) {\n" +
217 getAuxilaryComment("commonjs2") +
218 " if(typeof exports === 'object' && typeof module === 'object')\n" +
219 " module.exports = factory(" +
220 externalsRequireArray("commonjs2") +
221 ");\n" +
222 getAuxilaryComment("amd") +
223 " else if(typeof define === 'function' && define.amd)\n" +
224 (requiredExternals.length > 0
225 ? this.names.amd && this.namedDefine === true
226 ? " define(" +
227 libraryName(this.names.amd) +
228 ", " +
229 externalsDepsArray(requiredExternals) +
230 ", " +
231 amdFactory +
232 ");\n"
233 : " define(" +
234 externalsDepsArray(requiredExternals) +
235 ", " +
236 amdFactory +
237 ");\n"
238 : this.names.amd && this.namedDefine === true
239 ? " define(" +
240 libraryName(this.names.amd) +
241 ", [], " +
242 amdFactory +
243 ");\n"
244 : " define([], " + amdFactory + ");\n") +
245 (this.names.root || this.names.commonjs
246 ? getAuxilaryComment("commonjs") +
247 " else if(typeof exports === 'object')\n" +
248 " exports[" +
249 libraryName(this.names.commonjs || this.names.root) +
250 "] = factory(" +
251 externalsRequireArray("commonjs") +
252 ");\n" +
253 getAuxilaryComment("root") +
254 " else\n" +
255 " " +
256 replaceKeys(
257 accessorAccess("root", this.names.root || this.names.commonjs)
258 ) +
259 " = factory(" +
260 externalsRootArray(externals) +
261 ");\n"
262 : " else {\n" +
263 (externals.length > 0
264 ? " var a = typeof exports === 'object' ? factory(" +
265 externalsRequireArray("commonjs") +
266 ") : factory(" +
267 externalsRootArray(externals) +
268 ");\n"
269 : " var a = factory();\n") +
270 " for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n" +
271 " }\n") +
272 `})(${
273 runtimeTemplate.outputOptions.globalObject
274 }, function(${externalsArguments(externals)}) {\nreturn `,
275 "webpack/universalModuleDefinition"
276 ),
277 source,
278 ";\n})"
279 );
280 };
281
282 for (const template of [mainTemplate, chunkTemplate]) {
283 template.hooks.renderWithEntry.tap(
284 "UmdMainTemplatePlugin",
285 onRenderWithEntry
286 );
287 }
288
289 mainTemplate.hooks.globalHashPaths.tap("UmdMainTemplatePlugin", paths => {
290 if (this.names.root) paths = paths.concat(this.names.root);
291 if (this.names.amd) paths = paths.concat(this.names.amd);
292 if (this.names.commonjs) paths = paths.concat(this.names.commonjs);
293 return paths;
294 });
295
296 mainTemplate.hooks.hash.tap("UmdMainTemplatePlugin", hash => {
297 hash.update("umd");
298 hash.update(`${this.names.root}`);
299 hash.update(`${this.names.amd}`);
300 hash.update(`${this.names.commonjs}`);
301 });
302 }
303}
304
305module.exports = UmdMainTemplatePlugin;