source: imaps-frontend/node_modules/webpack/lib/dependencies/WorkerPlugin.js

main
Last change on this file was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 16.7 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { pathToFileURL } = require("url");
9const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
10const CommentCompilationWarning = require("../CommentCompilationWarning");
11const {
12 JAVASCRIPT_MODULE_TYPE_AUTO,
13 JAVASCRIPT_MODULE_TYPE_ESM
14} = require("../ModuleTypeConstants");
15const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
16const EnableChunkLoadingPlugin = require("../javascript/EnableChunkLoadingPlugin");
17const { equals } = require("../util/ArrayHelpers");
18const createHash = require("../util/createHash");
19const { contextify } = require("../util/identifier");
20const EnableWasmLoadingPlugin = require("../wasm/EnableWasmLoadingPlugin");
21const ConstDependency = require("./ConstDependency");
22const CreateScriptUrlDependency = require("./CreateScriptUrlDependency");
23const {
24 harmonySpecifierTag
25} = require("./HarmonyImportDependencyParserPlugin");
26const WorkerDependency = require("./WorkerDependency");
27
28/** @typedef {import("estree").CallExpression} CallExpression */
29/** @typedef {import("estree").Expression} Expression */
30/** @typedef {import("estree").ObjectExpression} ObjectExpression */
31/** @typedef {import("estree").Pattern} Pattern */
32/** @typedef {import("estree").Property} Property */
33/** @typedef {import("estree").SpreadElement} SpreadElement */
34/** @typedef {import("../../declarations/WebpackOptions").ChunkLoading} ChunkLoading */
35/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
36/** @typedef {import("../../declarations/WebpackOptions").OutputModule} OutputModule */
37/** @typedef {import("../../declarations/WebpackOptions").WasmLoading} WasmLoading */
38/** @typedef {import("../../declarations/WebpackOptions").WorkerPublicPath} WorkerPublicPath */
39/** @typedef {import("../Compiler")} Compiler */
40/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
41/** @typedef {import("../Entrypoint").EntryOptions} EntryOptions */
42/** @typedef {import("../NormalModule")} NormalModule */
43/** @typedef {import("../Parser").ParserState} ParserState */
44/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
45/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
46/** @typedef {import("../javascript/JavascriptParser")} Parser */
47/** @typedef {import("../javascript/JavascriptParser").Range} Range */
48/** @typedef {import("../util/createHash").Algorithm} Algorithm */
49/** @typedef {import("./HarmonyImportDependencyParserPlugin").HarmonySettings} HarmonySettings */
50
51/**
52 * @param {NormalModule} module module
53 * @returns {string} url
54 */
55const getUrl = module => pathToFileURL(module.resource).toString();
56
57const WorkerSpecifierTag = Symbol("worker specifier tag");
58
59const DEFAULT_SYNTAX = [
60 "Worker",
61 "SharedWorker",
62 "navigator.serviceWorker.register()",
63 "Worker from worker_threads"
64];
65
66/** @type {WeakMap<ParserState, number>} */
67const workerIndexMap = new WeakMap();
68
69const PLUGIN_NAME = "WorkerPlugin";
70
71class WorkerPlugin {
72 /**
73 * @param {ChunkLoading=} chunkLoading chunk loading
74 * @param {WasmLoading=} wasmLoading wasm loading
75 * @param {OutputModule=} module output module
76 * @param {WorkerPublicPath=} workerPublicPath worker public path
77 */
78 constructor(chunkLoading, wasmLoading, module, workerPublicPath) {
79 this._chunkLoading = chunkLoading;
80 this._wasmLoading = wasmLoading;
81 this._module = module;
82 this._workerPublicPath = workerPublicPath;
83 }
84
85 /**
86 * Apply the plugin
87 * @param {Compiler} compiler the compiler instance
88 * @returns {void}
89 */
90 apply(compiler) {
91 if (this._chunkLoading) {
92 new EnableChunkLoadingPlugin(this._chunkLoading).apply(compiler);
93 }
94 if (this._wasmLoading) {
95 new EnableWasmLoadingPlugin(this._wasmLoading).apply(compiler);
96 }
97 const cachedContextify = contextify.bindContextCache(
98 compiler.context,
99 compiler.root
100 );
101 compiler.hooks.thisCompilation.tap(
102 PLUGIN_NAME,
103 (compilation, { normalModuleFactory }) => {
104 compilation.dependencyFactories.set(
105 WorkerDependency,
106 normalModuleFactory
107 );
108 compilation.dependencyTemplates.set(
109 WorkerDependency,
110 new WorkerDependency.Template()
111 );
112 compilation.dependencyTemplates.set(
113 CreateScriptUrlDependency,
114 new CreateScriptUrlDependency.Template()
115 );
116
117 /**
118 * @param {JavascriptParser} parser the parser
119 * @param {Expression} expr expression
120 * @returns {[BasicEvaluatedExpression, [number, number]] | void} parsed
121 */
122 const parseModuleUrl = (parser, expr) => {
123 if (
124 expr.type !== "NewExpression" ||
125 expr.callee.type === "Super" ||
126 expr.arguments.length !== 2
127 )
128 return;
129 const [arg1, arg2] = expr.arguments;
130 if (arg1.type === "SpreadElement") return;
131 if (arg2.type === "SpreadElement") return;
132 const callee = parser.evaluateExpression(expr.callee);
133 if (!callee.isIdentifier() || callee.identifier !== "URL") return;
134 const arg2Value = parser.evaluateExpression(arg2);
135 if (
136 !arg2Value.isString() ||
137 !(/** @type {string} */ (arg2Value.string).startsWith("file://")) ||
138 arg2Value.string !== getUrl(parser.state.module)
139 ) {
140 return;
141 }
142 const arg1Value = parser.evaluateExpression(arg1);
143 return [
144 arg1Value,
145 [
146 /** @type {Range} */ (arg1.range)[0],
147 /** @type {Range} */ (arg2.range)[1]
148 ]
149 ];
150 };
151
152 /**
153 * @param {JavascriptParser} parser the parser
154 * @param {ObjectExpression} expr expression
155 * @returns {{ expressions: Record<string, Expression | Pattern>, otherElements: (Property | SpreadElement)[], values: Record<string, any>, spread: boolean, insertType: "comma" | "single", insertLocation: number }} parsed object
156 */
157 const parseObjectExpression = (parser, expr) => {
158 /** @type {Record<string, any>} */
159 const values = {};
160 /** @type {Record<string, Expression | Pattern>} */
161 const expressions = {};
162 /** @type {(Property | SpreadElement)[]} */
163 const otherElements = [];
164 let spread = false;
165 for (const prop of expr.properties) {
166 if (prop.type === "SpreadElement") {
167 spread = true;
168 } else if (
169 prop.type === "Property" &&
170 !prop.method &&
171 !prop.computed &&
172 prop.key.type === "Identifier"
173 ) {
174 expressions[prop.key.name] = prop.value;
175 if (!prop.shorthand && !prop.value.type.endsWith("Pattern")) {
176 const value = parser.evaluateExpression(
177 /** @type {Expression} */ (prop.value)
178 );
179 if (value.isCompileTimeValue())
180 values[prop.key.name] = value.asCompileTimeValue();
181 }
182 } else {
183 otherElements.push(prop);
184 }
185 }
186 const insertType = expr.properties.length > 0 ? "comma" : "single";
187 const insertLocation = /** @type {Range} */ (
188 expr.properties[expr.properties.length - 1].range
189 )[1];
190 return {
191 expressions,
192 otherElements,
193 values,
194 spread,
195 insertType,
196 insertLocation
197 };
198 };
199
200 /**
201 * @param {Parser} parser parser parser
202 * @param {JavascriptParserOptions} parserOptions parserOptions
203 * @returns {void}
204 */
205 const parserPlugin = (parser, parserOptions) => {
206 if (parserOptions.worker === false) return;
207 const options = !Array.isArray(parserOptions.worker)
208 ? ["..."]
209 : parserOptions.worker;
210 /**
211 * @param {CallExpression} expr expression
212 * @returns {boolean | void} true when handled
213 */
214 const handleNewWorker = expr => {
215 if (expr.arguments.length === 0 || expr.arguments.length > 2)
216 return;
217 const [arg1, arg2] = expr.arguments;
218 if (arg1.type === "SpreadElement") return;
219 if (arg2 && arg2.type === "SpreadElement") return;
220 const parsedUrl = parseModuleUrl(parser, arg1);
221 if (!parsedUrl) return;
222 const [url, range] = parsedUrl;
223 if (!url.isString()) return;
224 const {
225 expressions,
226 otherElements,
227 values: options,
228 spread: hasSpreadInOptions,
229 insertType,
230 insertLocation
231 } = arg2 && arg2.type === "ObjectExpression"
232 ? parseObjectExpression(parser, arg2)
233 : {
234 /** @type {Record<string, Expression | Pattern>} */
235 expressions: {},
236 otherElements: [],
237 /** @type {Record<string, any>} */
238 values: {},
239 spread: false,
240 insertType: arg2 ? "spread" : "argument",
241 insertLocation: arg2
242 ? /** @type {Range} */ (arg2.range)
243 : /** @type {Range} */ (arg1.range)[1]
244 };
245 const { options: importOptions, errors: commentErrors } =
246 parser.parseCommentOptions(/** @type {Range} */ (expr.range));
247
248 if (commentErrors) {
249 for (const e of commentErrors) {
250 const { comment } = e;
251 parser.state.module.addWarning(
252 new CommentCompilationWarning(
253 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
254 /** @type {DependencyLocation} */ (comment.loc)
255 )
256 );
257 }
258 }
259
260 /** @type {EntryOptions} */
261 const entryOptions = {};
262
263 if (importOptions) {
264 if (importOptions.webpackIgnore !== undefined) {
265 if (typeof importOptions.webpackIgnore !== "boolean") {
266 parser.state.module.addWarning(
267 new UnsupportedFeatureWarning(
268 `\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
269 /** @type {DependencyLocation} */ (expr.loc)
270 )
271 );
272 } else if (importOptions.webpackIgnore) {
273 return false;
274 }
275 }
276 if (importOptions.webpackEntryOptions !== undefined) {
277 if (
278 typeof importOptions.webpackEntryOptions !== "object" ||
279 importOptions.webpackEntryOptions === null
280 ) {
281 parser.state.module.addWarning(
282 new UnsupportedFeatureWarning(
283 `\`webpackEntryOptions\` expected a object, but received: ${importOptions.webpackEntryOptions}.`,
284 /** @type {DependencyLocation} */ (expr.loc)
285 )
286 );
287 } else {
288 Object.assign(
289 entryOptions,
290 importOptions.webpackEntryOptions
291 );
292 }
293 }
294 if (importOptions.webpackChunkName !== undefined) {
295 if (typeof importOptions.webpackChunkName !== "string") {
296 parser.state.module.addWarning(
297 new UnsupportedFeatureWarning(
298 `\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`,
299 /** @type {DependencyLocation} */ (expr.loc)
300 )
301 );
302 } else {
303 entryOptions.name = importOptions.webpackChunkName;
304 }
305 }
306 }
307
308 if (
309 !Object.prototype.hasOwnProperty.call(entryOptions, "name") &&
310 options &&
311 typeof options.name === "string"
312 ) {
313 entryOptions.name = options.name;
314 }
315
316 if (entryOptions.runtime === undefined) {
317 const i = workerIndexMap.get(parser.state) || 0;
318 workerIndexMap.set(parser.state, i + 1);
319 const name = `${cachedContextify(
320 parser.state.module.identifier()
321 )}|${i}`;
322 const hash = createHash(
323 /** @type {Algorithm} */
324 (compilation.outputOptions.hashFunction)
325 );
326 hash.update(name);
327 const digest =
328 /** @type {string} */
329 (hash.digest(compilation.outputOptions.hashDigest));
330 entryOptions.runtime = digest.slice(
331 0,
332 compilation.outputOptions.hashDigestLength
333 );
334 }
335
336 const block = new AsyncDependenciesBlock({
337 name: entryOptions.name,
338 entryOptions: {
339 chunkLoading: this._chunkLoading,
340 wasmLoading: this._wasmLoading,
341 ...entryOptions
342 }
343 });
344 block.loc = expr.loc;
345 const dep = new WorkerDependency(
346 /** @type {string} */ (url.string),
347 range,
348 {
349 publicPath: this._workerPublicPath
350 }
351 );
352 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
353 block.addDependency(dep);
354 parser.state.module.addBlock(block);
355
356 if (compilation.outputOptions.trustedTypes) {
357 const dep = new CreateScriptUrlDependency(
358 /** @type {Range} */ (expr.arguments[0].range)
359 );
360 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
361 parser.state.module.addDependency(dep);
362 }
363
364 if (expressions.type) {
365 const expr = expressions.type;
366 if (options.type !== false) {
367 const dep = new ConstDependency(
368 this._module ? '"module"' : "undefined",
369 /** @type {Range} */ (expr.range)
370 );
371 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
372 parser.state.module.addPresentationalDependency(dep);
373 /** @type {TODO} */
374 (expressions).type = undefined;
375 }
376 } else if (insertType === "comma") {
377 if (this._module || hasSpreadInOptions) {
378 const dep = new ConstDependency(
379 `, type: ${this._module ? '"module"' : "undefined"}`,
380 insertLocation
381 );
382 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
383 parser.state.module.addPresentationalDependency(dep);
384 }
385 } else if (insertType === "spread") {
386 const dep1 = new ConstDependency(
387 "Object.assign({}, ",
388 /** @type {Range} */ (insertLocation)[0]
389 );
390 const dep2 = new ConstDependency(
391 `, { type: ${this._module ? '"module"' : "undefined"} })`,
392 /** @type {Range} */ (insertLocation)[1]
393 );
394 dep1.loc = /** @type {DependencyLocation} */ (expr.loc);
395 dep2.loc = /** @type {DependencyLocation} */ (expr.loc);
396 parser.state.module.addPresentationalDependency(dep1);
397 parser.state.module.addPresentationalDependency(dep2);
398 } else if (insertType === "argument" && this._module) {
399 const dep = new ConstDependency(
400 ', { type: "module" }',
401 insertLocation
402 );
403 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
404 parser.state.module.addPresentationalDependency(dep);
405 }
406
407 parser.walkExpression(expr.callee);
408 for (const key of Object.keys(expressions)) {
409 if (expressions[key]) parser.walkExpression(expressions[key]);
410 }
411 for (const prop of otherElements) {
412 parser.walkProperty(prop);
413 }
414 if (insertType === "spread") {
415 parser.walkExpression(arg2);
416 }
417
418 return true;
419 };
420 /**
421 * @param {string} item item
422 */
423 const processItem = item => {
424 if (
425 item.startsWith("*") &&
426 item.includes(".") &&
427 item.endsWith("()")
428 ) {
429 const firstDot = item.indexOf(".");
430 const pattern = item.slice(1, firstDot);
431 const itemMembers = item.slice(firstDot + 1, -2);
432
433 parser.hooks.preDeclarator.tap(PLUGIN_NAME, (decl, statement) => {
434 if (decl.id.type === "Identifier" && decl.id.name === pattern) {
435 parser.tagVariable(decl.id.name, WorkerSpecifierTag);
436 return true;
437 }
438 });
439 parser.hooks.pattern.for(pattern).tap(PLUGIN_NAME, pattern => {
440 parser.tagVariable(pattern.name, WorkerSpecifierTag);
441 return true;
442 });
443 parser.hooks.callMemberChain
444 .for(WorkerSpecifierTag)
445 .tap(PLUGIN_NAME, (expression, members) => {
446 if (itemMembers !== members.join(".")) {
447 return;
448 }
449
450 return handleNewWorker(expression);
451 });
452 } else if (item.endsWith("()")) {
453 parser.hooks.call
454 .for(item.slice(0, -2))
455 .tap(PLUGIN_NAME, handleNewWorker);
456 } else {
457 const match = /^(.+?)(\(\))?\s+from\s+(.+)$/.exec(item);
458 if (match) {
459 const ids = match[1].split(".");
460 const call = match[2];
461 const source = match[3];
462 (call ? parser.hooks.call : parser.hooks.new)
463 .for(harmonySpecifierTag)
464 .tap(PLUGIN_NAME, expr => {
465 const settings = /** @type {HarmonySettings} */ (
466 parser.currentTagData
467 );
468 if (
469 !settings ||
470 settings.source !== source ||
471 !equals(settings.ids, ids)
472 ) {
473 return;
474 }
475 return handleNewWorker(expr);
476 });
477 } else {
478 parser.hooks.new.for(item).tap(PLUGIN_NAME, handleNewWorker);
479 }
480 }
481 };
482 for (const item of options) {
483 if (item === "...") {
484 for (const itemFromDefault of DEFAULT_SYNTAX) {
485 processItem(itemFromDefault);
486 }
487 } else processItem(item);
488 }
489 };
490 normalModuleFactory.hooks.parser
491 .for(JAVASCRIPT_MODULE_TYPE_AUTO)
492 .tap(PLUGIN_NAME, parserPlugin);
493 normalModuleFactory.hooks.parser
494 .for(JAVASCRIPT_MODULE_TYPE_ESM)
495 .tap(PLUGIN_NAME, parserPlugin);
496 }
497 );
498 }
499}
500module.exports = WorkerPlugin;
Note: See TracBrowser for help on using the repository browser.