source: frontend/node_modules/webpack/lib/dependencies/ImportMetaPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 15.0 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Ivan Kopeykin @vankop
4*/
5
6"use strict";
7
8const { pathToFileURL } = require("url");
9const { SyncBailHook } = require("tapable");
10const Compilation = require("../Compilation");
11const DefinePlugin = require("../DefinePlugin");
12const {
13 JAVASCRIPT_MODULE_TYPE_AUTO,
14 JAVASCRIPT_MODULE_TYPE_ESM
15} = require("../ModuleTypeConstants");
16const RuntimeGlobals = require("../RuntimeGlobals");
17const Template = require("../Template");
18const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression");
19const {
20 evaluateToIdentifier,
21 evaluateToNumber,
22 evaluateToString,
23 toConstantDependency
24} = require("../javascript/JavascriptParserHelpers");
25const { propertyAccess } = require("../util/property");
26const ConstDependency = require("./ConstDependency");
27const ModuleInitFragmentDependency = require("./ModuleInitFragmentDependency");
28
29/** @typedef {import("estree").MemberExpression} MemberExpression */
30/** @typedef {import("estree").Identifier} Identifier */
31/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
32/** @typedef {import("../Compiler")} Compiler */
33/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
34/** @typedef {import("../NormalModule")} NormalModule */
35/** @typedef {import("../javascript/JavascriptParser")} Parser */
36/** @typedef {import("../javascript/JavascriptParser").Range} Range */
37/** @typedef {import("../javascript/JavascriptParser").Members} Members */
38/** @typedef {import("../javascript/JavascriptParser").DestructuringAssignmentProperty} DestructuringAssignmentProperty */
39/** @typedef {import("./ConstDependency").RawRuntimeRequirements} RawRuntimeRequirements */
40
41const PLUGIN_NAME = "ImportMetaPlugin";
42
43/** @type {WeakMap<Compilation, { stringify: string, env: Record<string, string> }>} */
44const compilationMetaEnvMap = new WeakMap();
45
46/**
47 * Collect import.meta.env definitions from DefinePlugin and build JSON string
48 * @param {Compilation} compilation the compilation
49 * @returns {{ stringify: string, env: Record<string, string> }} env object as JSON string
50 */
51const collectImportMetaEnvDefinitions = (compilation) => {
52 const cached = compilationMetaEnvMap.get(compilation);
53 if (cached) {
54 return cached;
55 }
56
57 const definePluginHooks = DefinePlugin.getCompilationHooks(compilation);
58 const definitions = definePluginHooks.definitions.call({});
59 /** @type {Record<string, string>} */
60 const env = {};
61 /** @type {string[]} */
62 const pairs = [];
63 for (const key of Object.keys(definitions)) {
64 if (key.startsWith("import.meta.env.")) {
65 const envKey = key.slice("import.meta.env.".length);
66 const value = definitions[key];
67 pairs.push(`${JSON.stringify(envKey)}:${value}`);
68 env[envKey] = /** @type {string} */ (value);
69 }
70 }
71 const result = { stringify: `{${pairs.join(",")}}`, env };
72 compilationMetaEnvMap.set(compilation, result);
73 return result;
74};
75
76/**
77 * Defines the import meta plugin hooks type used by this module.
78 * @typedef {object} ImportMetaPluginHooks
79 * @property {SyncBailHook<[DestructuringAssignmentProperty], string | void>} propertyInDestructuring
80 */
81
82/** @type {WeakMap<Compilation, ImportMetaPluginHooks>} */
83const compilationHooksMap = new WeakMap();
84
85class ImportMetaPlugin {
86 /**
87 * Returns the attached hooks.
88 * @param {Compilation} compilation the compilation
89 * @returns {ImportMetaPluginHooks} the attached hooks
90 */
91 static getCompilationHooks(compilation) {
92 if (!(compilation instanceof Compilation)) {
93 throw new TypeError(
94 "The 'compilation' argument must be an instance of Compilation"
95 );
96 }
97 let hooks = compilationHooksMap.get(compilation);
98 if (hooks === undefined) {
99 hooks = {
100 propertyInDestructuring: new SyncBailHook(["property"])
101 };
102 compilationHooksMap.set(compilation, hooks);
103 }
104 return hooks;
105 }
106
107 /**
108 * Applies the plugin by registering its hooks on the compiler.
109 * @param {Compiler} compiler compiler
110 */
111 apply(compiler) {
112 compiler.hooks.compilation.tap(
113 PLUGIN_NAME,
114 (compilation, { normalModuleFactory }) => {
115 const hooks = ImportMetaPlugin.getCompilationHooks(compilation);
116
117 compilation.dependencyTemplates.set(
118 ModuleInitFragmentDependency,
119 new ModuleInitFragmentDependency.Template()
120 );
121
122 /**
123 * Returns file url.
124 * @param {NormalModule} module module
125 * @returns {string} file url
126 */
127 const getUrl = (module) => pathToFileURL(module.resource).toString();
128 /**
129 * Processes the provided parser.
130 * @param {Parser} parser parser parser
131 * @param {JavascriptParserOptions} parserOptions parserOptions
132 * @returns {void}
133 */
134 const parserHandler = (parser, { importMeta }) => {
135 if (importMeta === false) {
136 const { importMetaName } = compilation.outputOptions;
137 if (importMetaName === "import.meta") return;
138
139 parser.hooks.expression
140 .for("import.meta")
141 .tap(PLUGIN_NAME, (metaProperty) => {
142 const dep = new ConstDependency(
143 /** @type {string} */ (importMetaName),
144 /** @type {Range} */ (metaProperty.range)
145 );
146 dep.loc = /** @type {DependencyLocation} */ (metaProperty.loc);
147 parser.state.module.addPresentationalDependency(dep);
148 return true;
149 });
150 return;
151 }
152
153 // import.meta direct
154 const webpackVersion = Number.parseInt(
155 require("../../package.json").version,
156 10
157 );
158 const importMetaUrl = () =>
159 JSON.stringify(getUrl(parser.state.module));
160 const importMetaWebpackVersion = () => JSON.stringify(webpackVersion);
161 /**
162 * Import meta unknown property.
163 * @param {Members} members members
164 * @returns {string} error message
165 */
166 const importMetaUnknownProperty = (members) => {
167 if (importMeta === "preserve-unknown") {
168 return `import.meta${propertyAccess(members, 0)}`;
169 }
170 return `${Template.toNormalComment(
171 `unsupported import.meta.${members.join(".")}`
172 )} undefined${propertyAccess(members, 1)}`;
173 };
174
175 parser.hooks.typeof
176 .for("import.meta")
177 .tap(
178 PLUGIN_NAME,
179 toConstantDependency(parser, JSON.stringify("object"))
180 );
181 parser.hooks.collectDestructuringAssignmentProperties.tap(
182 PLUGIN_NAME,
183 (expr) => {
184 if (expr.type === "MetaProperty") return true;
185 }
186 );
187 parser.hooks.expression
188 .for("import.meta")
189 .tap(PLUGIN_NAME, (metaProperty) => {
190 /** @type {RawRuntimeRequirements} */
191 const runtimeRequirements = [];
192 const moduleArgument = parser.state.module.moduleArgument;
193
194 const referencedPropertiesInDestructuring =
195 parser.destructuringAssignmentPropertiesFor(metaProperty);
196 if (!referencedPropertiesInDestructuring) {
197 const varName = "__webpack_import_meta__";
198 const { stringify: envStringify } =
199 collectImportMetaEnvDefinitions(compilation);
200 const knownProps =
201 `{url: ${importMetaUrl()}, ` +
202 `webpack: ${importMetaWebpackVersion()}, ` +
203 `main: ${RuntimeGlobals.moduleCache}[${RuntimeGlobals.entryModuleId}] === ${moduleArgument}, ` +
204 `env: ${envStringify}}`;
205 const initCode =
206 importMeta === "preserve-unknown"
207 ? `var ${varName} = Object.assign(import.meta, ${knownProps});\n`
208 : `var ${varName} = ${knownProps};\n`;
209 const initDep = new ModuleInitFragmentDependency(
210 initCode,
211 [
212 RuntimeGlobals.moduleCache,
213 RuntimeGlobals.entryModuleId,
214 RuntimeGlobals.module
215 ],
216 varName
217 );
218 initDep.loc = /** @type {DependencyLocation} */ (
219 metaProperty.loc
220 );
221 parser.state.module.addPresentationalDependency(initDep);
222 const dep = new ConstDependency(
223 varName,
224 /** @type {Range} */ (metaProperty.range),
225 runtimeRequirements
226 );
227 dep.loc = /** @type {DependencyLocation} */ (metaProperty.loc);
228 parser.state.module.addPresentationalDependency(dep);
229 return true;
230 }
231
232 let str = "";
233 for (const prop of referencedPropertiesInDestructuring) {
234 const value = hooks.propertyInDestructuring.call(prop);
235
236 if (value) {
237 str += value;
238 continue;
239 }
240
241 switch (prop.id) {
242 case "url":
243 str += `url: ${importMetaUrl()},`;
244 break;
245 case "webpack":
246 str += `webpack: ${importMetaWebpackVersion()},`;
247 break;
248 case "main":
249 str += `main: ${RuntimeGlobals.moduleCache}[${RuntimeGlobals.entryModuleId}] === ${moduleArgument},`;
250 runtimeRequirements.push(
251 RuntimeGlobals.moduleCache,
252 RuntimeGlobals.entryModuleId,
253 RuntimeGlobals.module
254 );
255 break;
256 case "env":
257 str += `env: ${collectImportMetaEnvDefinitions(compilation).stringify},`;
258 break;
259 default:
260 str += `[${JSON.stringify(
261 prop.id
262 )}]: ${importMetaUnknownProperty([prop.id])},`;
263 break;
264 }
265 }
266 const dep = new ConstDependency(
267 `({${str}})`,
268 /** @type {Range} */ (metaProperty.range),
269 runtimeRequirements
270 );
271 dep.loc = /** @type {DependencyLocation} */ (metaProperty.loc);
272 parser.state.module.addPresentationalDependency(dep);
273 return true;
274 });
275 parser.hooks.evaluateTypeof
276 .for("import.meta")
277 .tap(PLUGIN_NAME, evaluateToString("object"));
278 parser.hooks.evaluateIdentifier.for("import.meta").tap(
279 PLUGIN_NAME,
280 evaluateToIdentifier("import.meta", "import.meta", () => [], true)
281 );
282
283 // import.meta.url
284 parser.hooks.typeof
285 .for("import.meta.url")
286 .tap(
287 PLUGIN_NAME,
288 toConstantDependency(parser, JSON.stringify("string"))
289 );
290 parser.hooks.expression
291 .for("import.meta.url")
292 .tap(PLUGIN_NAME, (expr) => {
293 const dep = new ConstDependency(
294 importMetaUrl(),
295 /** @type {Range} */ (expr.range)
296 );
297 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
298 parser.state.module.addPresentationalDependency(dep);
299 return true;
300 });
301 parser.hooks.evaluateTypeof
302 .for("import.meta.url")
303 .tap(PLUGIN_NAME, evaluateToString("string"));
304 parser.hooks.evaluateIdentifier
305 .for("import.meta.url")
306 .tap(PLUGIN_NAME, (expr) =>
307 new BasicEvaluatedExpression()
308 .setString(getUrl(parser.state.module))
309 .setRange(/** @type {Range} */ (expr.range))
310 );
311
312 // import.meta.webpack
313 parser.hooks.expression
314 .for("import.meta.webpack")
315 .tap(
316 PLUGIN_NAME,
317 toConstantDependency(parser, importMetaWebpackVersion())
318 );
319 parser.hooks.typeof
320 .for("import.meta.webpack")
321 .tap(
322 PLUGIN_NAME,
323 toConstantDependency(parser, JSON.stringify("number"))
324 );
325 parser.hooks.evaluateTypeof
326 .for("import.meta.webpack")
327 .tap(PLUGIN_NAME, evaluateToString("number"));
328 parser.hooks.evaluateIdentifier
329 .for("import.meta.webpack")
330 .tap(PLUGIN_NAME, evaluateToNumber(webpackVersion));
331
332 parser.hooks.expression
333 .for("import.meta.main")
334 .tap(
335 PLUGIN_NAME,
336 toConstantDependency(
337 parser,
338 `${RuntimeGlobals.moduleCache}[${RuntimeGlobals.entryModuleId}] === ${RuntimeGlobals.module}`,
339 [
340 RuntimeGlobals.moduleCache,
341 RuntimeGlobals.entryModuleId,
342 RuntimeGlobals.module
343 ]
344 )
345 );
346 parser.hooks.typeof
347 .for("import.meta.main")
348 .tap(
349 PLUGIN_NAME,
350 toConstantDependency(parser, JSON.stringify("boolean"))
351 );
352 parser.hooks.evaluateTypeof
353 .for("import.meta.main")
354 .tap(PLUGIN_NAME, evaluateToString("boolean"));
355
356 // import.meta.env
357 parser.hooks.typeof
358 .for("import.meta.env")
359 .tap(
360 PLUGIN_NAME,
361 toConstantDependency(parser, JSON.stringify("object"))
362 );
363 parser.hooks.expressionMemberChain
364 .for("import.meta")
365 .tap(PLUGIN_NAME, (expr, members) => {
366 if (members[0] === "env" && members[1]) {
367 const name = members[1];
368 const { env } = collectImportMetaEnvDefinitions(compilation);
369 if (!Object.prototype.hasOwnProperty.call(env, name)) {
370 const dep = new ConstDependency(
371 "undefined",
372 /** @type {Range} */ (expr.range)
373 );
374 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
375 parser.state.module.addPresentationalDependency(dep);
376 return true;
377 }
378 }
379 });
380 parser.hooks.expression
381 .for("import.meta.env")
382 .tap(PLUGIN_NAME, (expr) => {
383 const { stringify } =
384 collectImportMetaEnvDefinitions(compilation);
385
386 const dep = new ConstDependency(
387 stringify,
388 /** @type {Range} */ (expr.range)
389 );
390 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
391 parser.state.module.addPresentationalDependency(dep);
392 return true;
393 });
394 parser.hooks.evaluateTypeof
395 .for("import.meta.env")
396 .tap(PLUGIN_NAME, evaluateToString("object"));
397 parser.hooks.evaluateIdentifier
398 .for("import.meta.env")
399 .tap(PLUGIN_NAME, (expr) =>
400 new BasicEvaluatedExpression()
401 .setTruthy()
402 .setSideEffects(false)
403 .setRange(/** @type {Range} */ (expr.range))
404 );
405
406 // Unknown properties
407 parser.hooks.unhandledExpressionMemberChain
408 .for("import.meta")
409 .tap(PLUGIN_NAME, (expr, members) => {
410 // unknown import.meta properties should be determined at runtime
411 if (importMeta === "preserve-unknown") {
412 return true;
413 }
414
415 // keep import.meta.env unknown property
416 // don't evaluate import.meta.env.UNKNOWN_PROPERTY -> undefined.UNKNOWN_PROPERTY
417 // `dirname` and `filename` logic in NodeStuffPlugin
418 if (
419 members[0] === "env" ||
420 members[0] === "dirname" ||
421 members[0] === "filename"
422 ) {
423 return true;
424 }
425 const dep = new ConstDependency(
426 importMetaUnknownProperty(members),
427 /** @type {Range} */ (expr.range)
428 );
429 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
430 parser.state.module.addPresentationalDependency(dep);
431 return true;
432 });
433
434 parser.hooks.evaluate
435 .for("MemberExpression")
436 .tap(PLUGIN_NAME, (expression) => {
437 const expr = /** @type {MemberExpression} */ (expression);
438 if (
439 expr.object.type === "MetaProperty" &&
440 expr.object.meta.name === "import" &&
441 expr.object.property.name === "meta" &&
442 expr.property.type ===
443 (expr.computed ? "Literal" : "Identifier")
444 ) {
445 return new BasicEvaluatedExpression()
446 .setUndefined()
447 .setRange(/** @type {Range} */ (expr.range));
448 }
449 });
450 };
451
452 normalModuleFactory.hooks.parser
453 .for(JAVASCRIPT_MODULE_TYPE_AUTO)
454 .tap(PLUGIN_NAME, parserHandler);
455 normalModuleFactory.hooks.parser
456 .for(JAVASCRIPT_MODULE_TYPE_ESM)
457 .tap(PLUGIN_NAME, parserHandler);
458 }
459 );
460 }
461}
462
463module.exports = ImportMetaPlugin;
Note: See TracBrowser for help on using the repository browser.