source: frontend/node_modules/webpack/lib/url/URLParserPlugin.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 8.2 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Haijie Xie @hai-x
4*/
5
6"use strict";
7
8const { pathToFileURL } = require("url");
9const RuntimeGlobals = require("../RuntimeGlobals");
10const ConstDependency = require("../dependencies/ConstDependency");
11const ContextDependencyHelpers = require("../dependencies/ContextDependencyHelpers");
12const URLContextDependency = require("../dependencies/URLContextDependency");
13const URLDependency = require("../dependencies/URLDependency");
14const CommentCompilationWarning = require("../errors/CommentCompilationWarning");
15const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
16const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression");
17const { approve } = require("../javascript/JavascriptParserHelpers");
18const InnerGraph = require("../optimize/InnerGraph");
19
20/** @typedef {import("estree").MemberExpression} MemberExpression */
21/** @typedef {import("estree").NewExpression} NewExpressionNode */
22/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
23/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
24/** @typedef {import("../NormalModule")} NormalModule */
25/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
26/** @typedef {import("../javascript/JavascriptParser").Range} Range */
27
28const PLUGIN_NAME = "URLParserPlugin";
29
30/**
31 * Returns file url.
32 * @param {NormalModule} module module
33 * @returns {URL} file url
34 */
35const getUrl = (module) => pathToFileURL(module.resource);
36
37/**
38 * Checks whether this object is meta url.
39 * @param {JavascriptParser} parser parser parser
40 * @param {MemberExpression} arg arg
41 * @returns {boolean} true when it is `meta.url`, otherwise false
42 */
43const isMetaUrl = (parser, arg) => {
44 const chain = parser.extractMemberExpressionChain(arg);
45
46 if (
47 chain.members.length !== 1 ||
48 chain.object.type !== "MetaProperty" ||
49 chain.object.meta.name !== "import" ||
50 chain.object.property.name !== "meta" ||
51 chain.members[0] !== "url"
52 ) {
53 return false;
54 }
55
56 return true;
57};
58
59/** @type {WeakMap<NewExpressionNode, BasicEvaluatedExpression | undefined>} */
60const getEvaluatedExprCache = new WeakMap();
61
62/**
63 * Gets evaluated expr.
64 * @param {NewExpressionNode} expr expression
65 * @param {JavascriptParser} parser parser parser
66 * @returns {BasicEvaluatedExpression | undefined} basic evaluated expression
67 */
68const getEvaluatedExpr = (expr, parser) => {
69 let result = getEvaluatedExprCache.get(expr);
70 if (result !== undefined) return result;
71
72 /**
73 * Returns basic evaluated expression.
74 * @returns {BasicEvaluatedExpression | undefined} basic evaluated expression
75 */
76 const evaluate = () => {
77 if (expr.arguments.length !== 2) return;
78
79 const [arg1, arg2] = expr.arguments;
80
81 if (arg2.type !== "MemberExpression" || arg1.type === "SpreadElement") {
82 return;
83 }
84 if (!isMetaUrl(parser, arg2)) return;
85
86 return parser.evaluateExpression(arg1);
87 };
88
89 result = evaluate();
90 getEvaluatedExprCache.set(expr, result);
91
92 return result;
93};
94
95class URLParserPlugin {
96 /**
97 * Creates an instance of URLParserPlugin.
98 * @param {JavascriptParserOptions} options options
99 */
100 constructor(options) {
101 /** @type {JavascriptParserOptions} */
102 this.options = options;
103 }
104
105 /**
106 * Applies the plugin by registering its hooks on the compiler.
107 * @param {JavascriptParser} parser the parser
108 * @returns {void}
109 */
110 apply(parser) {
111 const relative = this.options.url === "relative";
112
113 parser.hooks.canRename.for("URL").tap(PLUGIN_NAME, approve);
114 parser.hooks.evaluateNewExpression.for("URL").tap(PLUGIN_NAME, (expr) => {
115 const evaluatedExpr = getEvaluatedExpr(expr, parser);
116 const request = evaluatedExpr && evaluatedExpr.asString();
117
118 if (!request) return;
119 const url = new URL(request, getUrl(parser.state.module));
120
121 return new BasicEvaluatedExpression()
122 .setString(url.toString())
123 .setRange(/** @type {Range} */ (expr.range));
124 });
125 parser.hooks.new.for("URL").tap(PLUGIN_NAME, (_expr) => {
126 const expr = /** @type {NewExpressionNode} */ (_expr);
127 const { options: importOptions, errors: commentErrors } =
128 parser.parseCommentOptions(/** @type {Range} */ (expr.range));
129
130 if (commentErrors) {
131 for (const e of commentErrors) {
132 const { comment } = e;
133 parser.state.module.addWarning(
134 new CommentCompilationWarning(
135 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
136 /** @type {DependencyLocation} */ (comment.loc)
137 )
138 );
139 }
140 }
141
142 if (importOptions && importOptions.webpackIgnore !== undefined) {
143 if (typeof importOptions.webpackIgnore !== "boolean") {
144 parser.state.module.addWarning(
145 new UnsupportedFeatureWarning(
146 `\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
147 /** @type {DependencyLocation} */ (expr.loc)
148 )
149 );
150 return;
151 } else if (importOptions.webpackIgnore) {
152 if (expr.arguments.length !== 2) return;
153
154 const [, arg2] = expr.arguments;
155
156 if (arg2.type !== "MemberExpression" || !isMetaUrl(parser, arg2)) {
157 return;
158 }
159
160 const dep = new ConstDependency(
161 RuntimeGlobals.baseURI,
162 /** @type {Range} */ (arg2.range),
163 [RuntimeGlobals.baseURI]
164 );
165 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
166 parser.state.module.addPresentationalDependency(dep);
167
168 return true;
169 }
170 }
171
172 const evaluatedExpr = getEvaluatedExpr(expr, parser);
173 if (!evaluatedExpr) return;
174
175 /** @type {string | undefined} */
176 let request;
177
178 // static URL
179 if ((request = evaluatedExpr.asString())) {
180 const [arg1, arg2] = expr.arguments;
181 const dep = new URLDependency(
182 request,
183 [
184 /** @type {Range} */ (arg1.range)[0],
185 /** @type {Range} */ (arg2.range)[1]
186 ],
187 /** @type {Range} */ (expr.range),
188 relative
189 );
190 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
191 parser.state.current.addDependency(dep);
192 InnerGraph.onUsage(parser.state, (e) => (dep.usedByExports = e));
193 return true;
194 }
195
196 if (this.options.dynamicUrl === false) return;
197
198 // context URL
199 /** @type {undefined | RegExp} */
200 let include;
201 /** @type {undefined | RegExp} */
202 let exclude;
203
204 if (importOptions) {
205 if (importOptions.webpackInclude !== undefined) {
206 if (
207 !importOptions.webpackInclude ||
208 !(importOptions.webpackInclude instanceof RegExp)
209 ) {
210 parser.state.module.addWarning(
211 new UnsupportedFeatureWarning(
212 `\`webpackInclude\` expected a regular expression, but received: ${importOptions.webpackInclude}.`,
213 /** @type {DependencyLocation} */ (expr.loc)
214 )
215 );
216 } else {
217 include = importOptions.webpackInclude;
218 }
219 }
220 if (importOptions.webpackExclude !== undefined) {
221 if (
222 !importOptions.webpackExclude ||
223 !(importOptions.webpackExclude instanceof RegExp)
224 ) {
225 parser.state.module.addWarning(
226 new UnsupportedFeatureWarning(
227 `\`webpackExclude\` expected a regular expression, but received: ${importOptions.webpackExclude}.`,
228 /** @type {DependencyLocation} */ (expr.loc)
229 )
230 );
231 } else {
232 exclude = importOptions.webpackExclude;
233 }
234 }
235 }
236 const dep = ContextDependencyHelpers.create(
237 URLContextDependency,
238 /** @type {Range} */ (expr.range),
239 evaluatedExpr,
240 expr,
241 this.options,
242 {
243 include,
244 exclude,
245 mode: "sync",
246 typePrefix: "new URL with import.meta.url",
247 category: "url"
248 },
249 parser
250 );
251 if (!dep) return;
252 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
253 dep.optional = Boolean(parser.scope.inTry);
254 parser.state.current.addDependency(dep);
255 return true;
256 });
257 parser.hooks.isPure.for("NewExpression").tap(PLUGIN_NAME, (_expr) => {
258 const expr = /** @type {NewExpressionNode} */ (_expr);
259 const { callee } = expr;
260 if (callee.type !== "Identifier") return;
261 const calleeInfo = parser.getFreeInfoFromVariable(callee.name);
262 if (!calleeInfo || calleeInfo.name !== "URL") return;
263
264 const evaluatedExpr = getEvaluatedExpr(expr, parser);
265 const request = evaluatedExpr && evaluatedExpr.asString();
266
267 if (request) return true;
268 });
269 }
270}
271
272module.exports = URLParserPlugin;
Note: See TracBrowser for help on using the repository browser.