1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const { ConcatSource } = require("webpack-sources");
|
9 | const { UsageState } = require("../ExportsInfo");
|
10 | const Template = require("../Template");
|
11 | const propertyAccess = require("../util/propertyAccess");
|
12 | const { getEntryRuntime } = require("../util/runtime");
|
13 | const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | const KEYWORD_REGEX =
|
28 | /^(await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|super|switch|static|this|throw|try|true|typeof|var|void|while|with|yield)$/;
|
29 | const IDENTIFIER_REGEX =
|
30 | /^[\p{L}\p{Nl}$_][\p{L}\p{Nl}$\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$/iu;
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | const isNameValid = name => {
|
38 | return !KEYWORD_REGEX.test(name) && IDENTIFIER_REGEX.test(name);
|
39 | };
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | const accessWithInit = (accessor, existingLength, initLast = false) => {
|
48 |
|
49 |
|
50 | const base = accessor[0];
|
51 | if (accessor.length === 1 && !initLast) return base;
|
52 | let current =
|
53 | existingLength > 0
|
54 | ? base
|
55 | : `(${base} = typeof ${base} === "undefined" ? {} : ${base})`;
|
56 |
|
57 |
|
58 | let i = 1;
|
59 |
|
60 |
|
61 | let propsSoFar;
|
62 |
|
63 |
|
64 | if (existingLength > i) {
|
65 | propsSoFar = accessor.slice(1, existingLength);
|
66 | i = existingLength;
|
67 | current += propertyAccess(propsSoFar);
|
68 | } else {
|
69 | propsSoFar = [];
|
70 | }
|
71 |
|
72 |
|
73 |
|
74 | const initUntil = initLast ? accessor.length : accessor.length - 1;
|
75 | for (; i < initUntil; i++) {
|
76 | const prop = accessor[i];
|
77 | propsSoFar.push(prop);
|
78 | current = `(${current}${propertyAccess([prop])} = ${base}${propertyAccess(
|
79 | propsSoFar
|
80 | )} || {})`;
|
81 | }
|
82 |
|
83 |
|
84 | if (i < accessor.length)
|
85 | current = `${current}${propertyAccess([accessor[accessor.length - 1]])}`;
|
86 |
|
87 | return current;
|
88 | };
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | class AssignLibraryPlugin extends AbstractLibraryPlugin {
|
110 | |
111 |
|
112 |
|
113 | constructor(options) {
|
114 | super({
|
115 | pluginName: "AssignLibraryPlugin",
|
116 | type: options.type
|
117 | });
|
118 | this.prefix = options.prefix;
|
119 | this.declare = options.declare;
|
120 | this.unnamed = options.unnamed;
|
121 | this.named = options.named || "assign";
|
122 | }
|
123 |
|
124 | |
125 |
|
126 |
|
127 |
|
128 | parseOptions(library) {
|
129 | const { name } = library;
|
130 | if (this.unnamed === "error") {
|
131 | if (typeof name !== "string" && !Array.isArray(name)) {
|
132 | throw new Error(
|
133 | `Library name must be a string or string array. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
|
134 | );
|
135 | }
|
136 | } else {
|
137 | if (name && typeof name !== "string" && !Array.isArray(name)) {
|
138 | throw new Error(
|
139 | `Library name must be a string, string array or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
|
140 | );
|
141 | }
|
142 | }
|
143 | return {
|
144 | name: (name),
|
145 | export: library.export
|
146 | };
|
147 | }
|
148 |
|
149 | |
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 | finishEntryModule(
|
156 | module,
|
157 | entryName,
|
158 | { options, compilation, compilation: { moduleGraph } }
|
159 | ) {
|
160 | const runtime = getEntryRuntime(compilation, entryName);
|
161 | if (options.export) {
|
162 | const exportsInfo = moduleGraph.getExportInfo(
|
163 | module,
|
164 | Array.isArray(options.export) ? options.export[0] : options.export
|
165 | );
|
166 | exportsInfo.setUsed(UsageState.Used, runtime);
|
167 | exportsInfo.canMangleUse = false;
|
168 | } else {
|
169 | const exportsInfo = moduleGraph.getExportsInfo(module);
|
170 | exportsInfo.setUsedInUnknownWay(runtime);
|
171 | }
|
172 | moduleGraph.addExtraReason(module, "used as library export");
|
173 | }
|
174 |
|
175 | _getPrefix(compilation) {
|
176 | return this.prefix === "global"
|
177 | ? [compilation.runtimeTemplate.globalObject]
|
178 | : this.prefix;
|
179 | }
|
180 |
|
181 | _getResolvedFullName(options, chunk, compilation) {
|
182 | const prefix = this._getPrefix(compilation);
|
183 | const fullName = options.name ? prefix.concat(options.name) : prefix;
|
184 | return fullName.map(n =>
|
185 | compilation.getPath(n, {
|
186 | chunk
|
187 | })
|
188 | );
|
189 | }
|
190 |
|
191 | |
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | render(source, { chunk }, { options, compilation }) {
|
198 | const fullNameResolved = this._getResolvedFullName(
|
199 | options,
|
200 | chunk,
|
201 | compilation
|
202 | );
|
203 | if (this.declare) {
|
204 | const base = fullNameResolved[0];
|
205 | if (!isNameValid(base)) {
|
206 | throw new Error(
|
207 | `Library name base (${base}) must be a valid identifier when using a var declaring library type. Either use a valid identifier (e. g. ${Template.toIdentifier(
|
208 | base
|
209 | )}) or use a different library type (e. g. 'type: "global"', which assign a property on the global scope instead of declaring a variable). ${
|
210 | AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE
|
211 | }`
|
212 | );
|
213 | }
|
214 | source = new ConcatSource(`${this.declare} ${base};\n`, source);
|
215 | }
|
216 | return source;
|
217 | }
|
218 |
|
219 | |
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 | embedInRuntimeBailout(
|
226 | module,
|
227 | { chunk, codeGenerationResults },
|
228 | { options, compilation }
|
229 | ) {
|
230 | const { data } = codeGenerationResults.get(module, chunk.runtime);
|
231 | const topLevelDeclarations =
|
232 | (data && data.get("topLevelDeclarations")) ||
|
233 | (module.buildInfo && module.buildInfo.topLevelDeclarations);
|
234 | if (!topLevelDeclarations)
|
235 | return "it doesn't tell about top level declarations.";
|
236 | const fullNameResolved = this._getResolvedFullName(
|
237 | options,
|
238 | chunk,
|
239 | compilation
|
240 | );
|
241 | const base = fullNameResolved[0];
|
242 | if (topLevelDeclarations.has(base))
|
243 | return `it declares '${base}' on top-level, which conflicts with the current library output.`;
|
244 | }
|
245 |
|
246 | |
247 |
|
248 |
|
249 |
|
250 |
|
251 | strictRuntimeBailout({ chunk }, { options, compilation }) {
|
252 | if (
|
253 | this.declare ||
|
254 | this.prefix === "global" ||
|
255 | this.prefix.length > 0 ||
|
256 | !options.name
|
257 | ) {
|
258 | return;
|
259 | }
|
260 | return "a global variable is assign and maybe created";
|
261 | }
|
262 |
|
263 | |
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 | renderStartup(
|
271 | source,
|
272 | module,
|
273 | { moduleGraph, chunk },
|
274 | { options, compilation }
|
275 | ) {
|
276 | const fullNameResolved = this._getResolvedFullName(
|
277 | options,
|
278 | chunk,
|
279 | compilation
|
280 | );
|
281 | const staticExports = this.unnamed === "static";
|
282 | const exportAccess = options.export
|
283 | ? propertyAccess(
|
284 | Array.isArray(options.export) ? options.export : [options.export]
|
285 | )
|
286 | : "";
|
287 | const result = new ConcatSource(source);
|
288 | if (staticExports) {
|
289 | const exportsInfo = moduleGraph.getExportsInfo(module);
|
290 | const exportTarget = accessWithInit(
|
291 | fullNameResolved,
|
292 | this._getPrefix(compilation).length,
|
293 | true
|
294 | );
|
295 | for (const exportInfo of exportsInfo.orderedExports) {
|
296 | if (!exportInfo.provided) continue;
|
297 | const nameAccess = propertyAccess([exportInfo.name]);
|
298 | result.add(
|
299 | `${exportTarget}${nameAccess} = __webpack_exports__${exportAccess}${nameAccess};\n`
|
300 | );
|
301 | }
|
302 | result.add(
|
303 | `Object.defineProperty(${exportTarget}, "__esModule", { value: true });\n`
|
304 | );
|
305 | } else if (options.name ? this.named === "copy" : this.unnamed === "copy") {
|
306 | result.add(
|
307 | `var __webpack_export_target__ = ${accessWithInit(
|
308 | fullNameResolved,
|
309 | this._getPrefix(compilation).length,
|
310 | true
|
311 | )};\n`
|
312 | );
|
313 | let exports = "__webpack_exports__";
|
314 | if (exportAccess) {
|
315 | result.add(
|
316 | `var __webpack_exports_export__ = __webpack_exports__${exportAccess};\n`
|
317 | );
|
318 | exports = "__webpack_exports_export__";
|
319 | }
|
320 | result.add(
|
321 | `for(var i in ${exports}) __webpack_export_target__[i] = ${exports}[i];\n`
|
322 | );
|
323 | result.add(
|
324 | `if(${exports}.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });\n`
|
325 | );
|
326 | } else {
|
327 | result.add(
|
328 | `${accessWithInit(
|
329 | fullNameResolved,
|
330 | this._getPrefix(compilation).length,
|
331 | false
|
332 | )} = __webpack_exports__${exportAccess};\n`
|
333 | );
|
334 | }
|
335 | return result;
|
336 | }
|
337 |
|
338 | |
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 | runtimeRequirements(chunk, set, libraryContext) {
|
345 |
|
346 | }
|
347 |
|
348 | |
349 |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 | chunkHash(chunk, hash, chunkHashContext, { options, compilation }) {
|
356 | hash.update("AssignLibraryPlugin");
|
357 | const fullNameResolved = this._getResolvedFullName(
|
358 | options,
|
359 | chunk,
|
360 | compilation
|
361 | );
|
362 | if (options.name ? this.named === "copy" : this.unnamed === "copy") {
|
363 | hash.update("copy");
|
364 | }
|
365 | if (this.declare) {
|
366 | hash.update(this.declare);
|
367 | }
|
368 | hash.update(fullNameResolved.join("."));
|
369 | if (options.export) {
|
370 | hash.update(`${options.export}`);
|
371 | }
|
372 | }
|
373 | }
|
374 |
|
375 | module.exports = AssignLibraryPlugin;
|