source: frontend/node_modules/webpack/lib/TemplatedPathPlugin.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: 11.5 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Jason Anderson @diurnalist
4*/
5
6"use strict";
7
8const { basename, extname } = require("path");
9const util = require("util");
10const Chunk = require("./Chunk");
11const Module = require("./Module");
12const { parseResource } = require("./util/identifier");
13const memoize = require("./util/memoize");
14
15const getMimeTypes = memoize(() => require("./util/mimeTypes"));
16
17/** @typedef {import("./ChunkGraph")} ChunkGraph */
18/** @typedef {import("./ChunkGraph").ModuleId} ModuleId */
19/** @typedef {import("./Compilation").AssetInfo} AssetInfo */
20/** @typedef {import("./Compilation").PathData} PathData */
21/** @typedef {import("./Compilation").PathDataChunk} PathDataChunk */
22/** @typedef {import("./Compilation").PathDataModule} PathDataModule */
23/** @typedef {import("./Compiler")} Compiler */
24
25const REGEXP = /\[\\*([\w:]+)\\*\]/g;
26
27/** @type {PathData["prepareId"]} */
28const prepareId = (id) => {
29 if (typeof id !== "string") return id;
30
31 if (/^"\s\+*.*\+\s*"$/.test(id)) {
32 const match = /^"\s\+*\s*(.*)\s*\+\s*"$/.exec(id);
33
34 return `" + (${
35 /** @type {string[]} */ (match)[1]
36 } + "").replace(/(^[.-]|[^a-zA-Z0-9_-])+/g, "_") + "`;
37 }
38
39 return id.replace(/(^[.-]|[^a-z0-9_-])+/gi, "_");
40};
41
42/**
43 * Defines the replacer function callback.
44 * @callback ReplacerFunction
45 * @param {string} match
46 * @param {string | undefined} arg
47 * @param {string} input
48 */
49
50/**
51 * Returns hash replacer function.
52 * @param {ReplacerFunction} replacer replacer
53 * @param {((arg0: number) => string) | undefined} handler handler
54 * @param {AssetInfo | undefined} assetInfo asset info
55 * @param {string} hashName hash name
56 * @returns {Replacer} hash replacer function
57 */
58const hashLength = (replacer, handler, assetInfo, hashName) => {
59 /** @type {Replacer} */
60 const fn = (match, arg, input) => {
61 /** @type {string} */
62 let result;
63 const length = arg && Number.parseInt(arg, 10);
64
65 if (length && handler) {
66 result = handler(length);
67 } else {
68 const hash = replacer(match, arg, input);
69
70 result = length ? hash.slice(0, length) : hash;
71 }
72 if (assetInfo) {
73 assetInfo.immutable = true;
74 if (Array.isArray(assetInfo[hashName])) {
75 assetInfo[hashName] = [...assetInfo[hashName], result];
76 } else if (assetInfo[hashName]) {
77 assetInfo[hashName] = [assetInfo[hashName], result];
78 } else {
79 assetInfo[hashName] = result;
80 }
81 }
82 return result;
83 };
84
85 return fn;
86};
87
88/** @typedef {(match: string, arg: string | undefined, input: string) => string} Replacer */
89
90/**
91 * Returns replacer.
92 * @param {string | number | null | undefined | (() => string | number | null | undefined)} value value
93 * @param {boolean=} allowEmpty allow empty
94 * @returns {Replacer} replacer
95 */
96const replacer = (value, allowEmpty) => {
97 /** @type {Replacer} */
98 const fn = (match, arg, input) => {
99 if (typeof value === "function") {
100 value = value();
101 }
102 if (value === null || value === undefined) {
103 if (!allowEmpty) {
104 throw new Error(
105 `Path variable ${match} not implemented in this context: ${input}`
106 );
107 }
108
109 return "";
110 }
111
112 return `${value}`;
113 };
114
115 return fn;
116};
117
118/** @type {Map<string, (...args: EXPECTED_ANY[]) => EXPECTED_ANY>} */
119const deprecationCache = new Map();
120const deprecatedFunction = (() => () => {})();
121/**
122 * Returns function with deprecation output.
123 * @template {(...args: EXPECTED_ANY[]) => EXPECTED_ANY} T
124 * @param {T} fn function
125 * @param {string} message message
126 * @param {string} code code
127 * @returns {T} function with deprecation output
128 */
129const deprecated = (fn, message, code) => {
130 let d = deprecationCache.get(message);
131 if (d === undefined) {
132 d = util.deprecate(deprecatedFunction, message, code);
133 deprecationCache.set(message, d);
134 }
135 return /** @type {T} */ (
136 (...args) => {
137 d();
138 return fn(...args);
139 }
140 );
141};
142
143/**
144 * Callback used to compute a path from contextual data. The type parameter
145 * narrows the `pathData` shape when the caller knows it operates in a chunk
146 * (`PathDataChunk`) or module (`PathDataModule`) context — defaults to the
147 * fully-optional `PathData` for backward compatibility.
148 * @template {PathData} [T=PathData]
149 * @typedef {(pathData: T, assetInfo?: AssetInfo) => string} TemplatePathFn
150 */
151
152/**
153 * Either a raw template string (e.g. `"[name].[contenthash].js"`) or a
154 * generic `TemplatePathFn`. Method signatures that need to thread a narrowed
155 * `PathData` shape spell the function side out as `TemplatePathFn<T>`
156 * directly — `TemplatePath` itself stays a plain alias so local JSDoc
157 * re-imports keep a single shared identity.
158 * @typedef {string | TemplatePathFn} TemplatePath
159 */
160
161/**
162 * Returns the interpolated path.
163 * @template {PathData} [T=PathData]
164 * @param {string | TemplatePathFn<T>} path the raw path
165 * @param {T} data context data
166 * @param {AssetInfo=} assetInfo extra info about the asset (will be written to)
167 * @returns {string} the interpolated path
168 */
169const interpolate = (path, data, assetInfo) => {
170 const chunkGraph = data.chunkGraph;
171
172 /** @type {Map<string, Replacer>} */
173 const replacements = new Map();
174
175 // Filename context
176 //
177 // Placeholders
178 //
179 // for /some/path/file.js?query#fragment:
180 // [file] - /some/path/file.js
181 // [query] - ?query
182 // [fragment] - #fragment
183 // [base] - file.js
184 // [path] - /some/path/
185 // [name] - file
186 // [ext] - .js
187 if (typeof data.filename === "string") {
188 // check that filename is data uri
189 const match = data.filename.match(/^data:([^;,]+)/);
190 if (match) {
191 const ext = getMimeTypes().extension(match[1]);
192 const emptyReplacer = replacer("", true);
193 // "XXXX" used for `updateHash`, so we don't need it here
194 const contentHash =
195 data.contentHash && !/X+/.test(data.contentHash)
196 ? data.contentHash
197 : false;
198 const baseReplacer = contentHash ? replacer(contentHash) : emptyReplacer;
199
200 replacements.set("file", emptyReplacer);
201 replacements.set("query", emptyReplacer);
202 replacements.set("fragment", emptyReplacer);
203 replacements.set("path", emptyReplacer);
204 replacements.set("base", baseReplacer);
205 replacements.set("name", baseReplacer);
206 replacements.set("ext", replacer(ext ? `.${ext}` : "", true));
207 // Legacy
208 replacements.set(
209 "filebase",
210 deprecated(
211 baseReplacer,
212 "[filebase] is now [base]",
213 "DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
214 )
215 );
216 } else {
217 const { path: file, query, fragment } = parseResource(data.filename);
218
219 const ext = extname(file);
220 const base = basename(file);
221 const name = base.slice(0, base.length - ext.length);
222 const path = file.slice(0, file.length - base.length);
223
224 replacements.set("file", replacer(file));
225 replacements.set("query", replacer(query, true));
226 replacements.set("fragment", replacer(fragment, true));
227 replacements.set("path", replacer(path, true));
228 replacements.set("base", replacer(base));
229 replacements.set("name", replacer(name));
230 replacements.set("ext", replacer(ext, true));
231 // Legacy
232 replacements.set(
233 "filebase",
234 deprecated(
235 replacer(base),
236 "[filebase] is now [base]",
237 "DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
238 )
239 );
240 }
241 }
242
243 // Compilation context
244 //
245 // Placeholders
246 //
247 // [fullhash] - data.hash (3a4b5c6e7f)
248 //
249 // Legacy Placeholders
250 //
251 // [hash] - data.hash (3a4b5c6e7f)
252 if (data.hash) {
253 const hashReplacer = hashLength(
254 replacer(data.hash),
255 data.hashWithLength,
256 assetInfo,
257 "fullhash"
258 );
259
260 replacements.set("fullhash", hashReplacer);
261
262 // Legacy
263 replacements.set(
264 "hash",
265 deprecated(
266 hashReplacer,
267 "[hash] is now [fullhash] (also consider using [chunkhash] or [contenthash], see documentation for details)",
268 "DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_HASH"
269 )
270 );
271 }
272
273 // Chunk Context
274 //
275 // Placeholders
276 //
277 // [id] - chunk.id (0.js)
278 // [name] - chunk.name (app.js)
279 // [chunkhash] - chunk.hash (7823t4t4.js)
280 // [contenthash] - chunk.contentHash[type] (3256u3zg.js)
281 if (data.chunk) {
282 const chunk = data.chunk;
283
284 const contentHashType = data.contentHashType;
285
286 const idReplacer = replacer(chunk.id);
287 const nameReplacer = replacer(chunk.name || chunk.id);
288 const chunkhashReplacer = hashLength(
289 replacer(chunk instanceof Chunk ? chunk.renderedHash : chunk.hash),
290 "hashWithLength" in chunk ? chunk.hashWithLength : undefined,
291 assetInfo,
292 "chunkhash"
293 );
294 const contenthashReplacer = hashLength(
295 replacer(
296 data.contentHash ||
297 (contentHashType &&
298 chunk.contentHash &&
299 chunk.contentHash[contentHashType])
300 ),
301 data.contentHashWithLength ||
302 ("contentHashWithLength" in chunk && chunk.contentHashWithLength
303 ? chunk.contentHashWithLength[/** @type {string} */ (contentHashType)]
304 : undefined),
305 assetInfo,
306 "contenthash"
307 );
308
309 replacements.set("id", idReplacer);
310 replacements.set("name", nameReplacer);
311 replacements.set("chunkhash", chunkhashReplacer);
312 replacements.set("contenthash", contenthashReplacer);
313 }
314
315 // Module Context
316 //
317 // Placeholders
318 //
319 // [id] - module.id (2.png)
320 // [hash] - module.hash (6237543873.png)
321 //
322 // Legacy Placeholders
323 //
324 // [moduleid] - module.id (2.png)
325 // [modulehash] - module.hash (6237543873.png)
326 if (data.module) {
327 const module = data.module;
328
329 const idReplacer = replacer(() =>
330 (data.prepareId || prepareId)(
331 module instanceof Module
332 ? /** @type {ModuleId} */
333 (/** @type {ChunkGraph} */ (chunkGraph).getModuleId(module))
334 : module.id
335 )
336 );
337 const moduleHashReplacer = hashLength(
338 replacer(() =>
339 module instanceof Module
340 ? /** @type {ChunkGraph} */
341 (chunkGraph).getRenderedModuleHash(module, data.runtime)
342 : module.hash
343 ),
344 "hashWithLength" in module ? module.hashWithLength : undefined,
345 assetInfo,
346 "modulehash"
347 );
348 const contentHashReplacer = hashLength(
349 replacer(/** @type {string} */ (data.contentHash)),
350 undefined,
351 assetInfo,
352 "contenthash"
353 );
354
355 replacements.set("id", idReplacer);
356 replacements.set("modulehash", moduleHashReplacer);
357 replacements.set("contenthash", contentHashReplacer);
358 replacements.set(
359 "hash",
360 data.contentHash ? contentHashReplacer : moduleHashReplacer
361 );
362 // Legacy
363 replacements.set(
364 "moduleid",
365 deprecated(
366 idReplacer,
367 "[moduleid] is now [id]",
368 "DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_MODULE_ID"
369 )
370 );
371 }
372
373 // Other things
374 if (data.url) {
375 replacements.set("url", replacer(data.url));
376 }
377 if (typeof data.runtime === "string") {
378 replacements.set(
379 "runtime",
380 replacer(() =>
381 (data.prepareId || prepareId)(/** @type {string} */ (data.runtime))
382 )
383 );
384 } else {
385 replacements.set("runtime", replacer("_"));
386 }
387
388 if (typeof path === "function") {
389 path = path(data, assetInfo);
390 }
391
392 path = path.replace(REGEXP, (match, content) => {
393 if (content.length + 2 === match.length) {
394 const contentMatch = /^(\w+)(?::(\w+))?$/.exec(content);
395 if (!contentMatch) return match;
396 const [, kind, arg] = contentMatch;
397 const replacer = replacements.get(kind);
398 if (replacer !== undefined) {
399 return replacer(match, arg, /** @type {string} */ (path));
400 }
401 } else if (match.startsWith("[\\") && match.endsWith("\\]")) {
402 return `[${match.slice(2, -2)}]`;
403 }
404 return match;
405 });
406
407 return path;
408};
409
410const plugin = "TemplatedPathPlugin";
411
412class TemplatedPathPlugin {
413 /**
414 * Applies the plugin by registering its hooks on the compiler.
415 * @param {Compiler} compiler the compiler instance
416 * @returns {void}
417 */
418 apply(compiler) {
419 compiler.hooks.compilation.tap(plugin, (compilation) => {
420 compilation.hooks.assetPath.tap(plugin, interpolate);
421 });
422 }
423}
424
425module.exports = TemplatedPathPlugin;
426module.exports.interpolate = interpolate;
Note: See TracBrowser for help on using the repository browser.