source: trip-planner-front/node_modules/webpack/lib/dependencies/WorkerPlugin.js@ 84d0fbb

Last change on this file since 84d0fbb was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

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