source: frontend/node_modules/webpack/lib/schemes/VirtualUrlPlugin.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.7 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Natsu @xiaoxiaojx
4*/
5
6"use strict";
7
8const { getContext } = require("loader-runner");
9
10const NormalModule = require("../NormalModule");
11const ModuleNotFoundError = require("../errors/ModuleNotFoundError");
12const { isAbsolute, join } = require("../util/fs");
13const { parseResourceWithoutFragment } = require("../util/identifier");
14
15const DEFAULT_SCHEME = "virtual";
16
17const PLUGIN_NAME = "VirtualUrlPlugin";
18
19/**
20 * Defines the compiler type used by this module.
21 * @typedef {import("../Compiler")} Compiler
22 * @typedef {import("../../declarations/plugins/schemes/VirtualUrlPlugin").VirtualModule} VirtualModuleConfig
23 * @typedef {import("../../declarations/plugins/schemes/VirtualUrlPlugin").VirtualModuleContent} VirtualModuleInput
24 * @typedef {import("../../declarations/plugins/schemes/VirtualUrlPlugin").VirtualUrlOptions} VirtualUrlOptions
25 */
26
27/** @typedef {(loaderContext: LoaderContext<EXPECTED_ANY>) => Promise<string | Buffer> | string | Buffer} SourceFn */
28/** @typedef {() => string} VersionFn */
29/** @typedef {{ [key: string]: VirtualModuleInput }} VirtualModules */
30
31/**
32 * Defines the loader context type used by this module.
33 * @template T
34 * @typedef {import("../../declarations/LoaderContext").LoaderContext<T>} LoaderContext
35 */
36
37/**
38 * Normalizes a virtual module definition into a standard format
39 * @param {VirtualModuleInput} virtualConfig The virtual module to normalize
40 * @returns {VirtualModuleConfig} The normalized virtual module
41 */
42function normalizeModule(virtualConfig) {
43 if (typeof virtualConfig === "string") {
44 return {
45 type: "",
46 source() {
47 return virtualConfig;
48 }
49 };
50 } else if (typeof virtualConfig === "function") {
51 return {
52 type: "",
53 source: virtualConfig
54 };
55 }
56 return virtualConfig;
57}
58
59/** @typedef {{ [key: string]: VirtualModuleConfig }} NormalizedModules */
60
61/**
62 * Normalizes all virtual modules with the given scheme
63 * @param {VirtualModules} virtualConfigs The virtual modules to normalize
64 * @param {string} scheme The URL scheme to use
65 * @returns {NormalizedModules} The normalized virtual modules
66 */
67function normalizeModules(virtualConfigs, scheme) {
68 return Object.keys(virtualConfigs).reduce((pre, id) => {
69 pre[toVid(id, scheme)] = normalizeModule(virtualConfigs[id]);
70 return pre;
71 }, /** @type {NormalizedModules} */ ({}));
72}
73
74/**
75 * Converts a module id and scheme to a virtual module id
76 * @param {string} id The module id
77 * @param {string} scheme The URL scheme
78 * @returns {string} The virtual module id
79 */
80function toVid(id, scheme) {
81 return `${scheme}:${id}`;
82}
83
84/**
85 * Converts a virtual module id to a module id
86 * @param {string} vid The virtual module id
87 * @param {string} scheme The URL scheme
88 * @returns {string} The module id
89 */
90function fromVid(vid, scheme) {
91 return vid.replace(`${scheme}:`, "");
92}
93
94const VALUE_DEP_VERSION = `webpack/${PLUGIN_NAME}/version`;
95
96/**
97 * Converts a module id and scheme to a cache key
98 * @param {string} id The module id
99 * @param {string} scheme The URL scheme
100 * @returns {string} The cache key
101 */
102function toCacheKey(id, scheme) {
103 return `${VALUE_DEP_VERSION}/${toVid(id, scheme)}`;
104}
105
106class VirtualUrlPlugin {
107 /**
108 * Creates an instance of VirtualUrlPlugin.
109 * @param {VirtualModules} modules The virtual modules
110 * @param {Omit<VirtualUrlOptions, "modules"> | string=} schemeOrOptions The URL scheme to use
111 */
112 constructor(modules, schemeOrOptions) {
113 /** @type {VirtualUrlOptions} */
114 this.options = {
115 modules,
116 ...(typeof schemeOrOptions === "string"
117 ? { scheme: schemeOrOptions }
118 : schemeOrOptions || {})
119 };
120
121 /** @type {string} */
122 this.scheme = this.options.scheme || DEFAULT_SCHEME;
123 /** @type {VirtualUrlOptions["context"]} */
124 this.context = this.options.context || "auto";
125 /** @type {NormalizedModules} */
126 this.modules = normalizeModules(this.options.modules, this.scheme);
127 }
128
129 /**
130 * Applies the plugin by registering its hooks on the compiler.
131 * @param {Compiler} compiler the compiler instance
132 * @returns {void}
133 */
134 apply(compiler) {
135 compiler.hooks.validate.tap(PLUGIN_NAME, () => {
136 compiler.validate(
137 () => require("../../schemas/plugins/schemes/VirtualUrlPlugin.json"),
138 this.options,
139 {
140 name: "Virtual Url Plugin",
141 baseDataPath: "options"
142 },
143 (options) =>
144 require("../../schemas/plugins/schemes/VirtualUrlPlugin.check")(
145 options
146 )
147 );
148 });
149
150 const scheme = this.scheme;
151 const cachedParseResourceWithoutFragment =
152 parseResourceWithoutFragment.bindCache(compiler.root);
153
154 compiler.hooks.compilation.tap(
155 PLUGIN_NAME,
156 (compilation, { normalModuleFactory }) => {
157 compilation.hooks.assetPath.tap(
158 { name: PLUGIN_NAME, before: "TemplatedPathPlugin" },
159 (path, data) => {
160 if (data.filename && this.modules[data.filename]) {
161 /**
162 * Returns safe path.
163 * @param {string} str path
164 * @returns {string} safe path
165 */
166 const toSafePath = (str) =>
167 `__${str
168 .replace(/:/g, "__")
169 .replace(/^[^a-z0-9]+|[^a-z0-9]+$/gi, "")
170 .replace(/[^a-z0-9._-]+/gi, "_")}`;
171
172 // filename: virtual:logo.svg -> __virtual__logo.svg
173 data.filename = toSafePath(data.filename);
174 }
175 return path;
176 }
177 );
178
179 normalModuleFactory.hooks.resolveForScheme
180 .for(scheme)
181 .tap(PLUGIN_NAME, (resourceData) => {
182 const virtualConfig = this.findVirtualModuleConfigById(
183 resourceData.resource
184 );
185 const url = cachedParseResourceWithoutFragment(
186 resourceData.resource
187 );
188 const path = url.path;
189 const type = virtualConfig.type || "";
190 const context = virtualConfig.context || this.context;
191
192 resourceData.path = path + type;
193 resourceData.resource = path;
194
195 if (context === "auto") {
196 const context = getContext(path);
197 if (context === path) {
198 resourceData.context = compiler.context;
199 } else {
200 const resolvedContext = fromVid(context, scheme);
201 resourceData.context = isAbsolute(resolvedContext)
202 ? resolvedContext
203 : join(
204 /** @type {import("..").InputFileSystem} */
205 (compiler.inputFileSystem),
206 compiler.context,
207 resolvedContext
208 );
209 }
210 } else if (context && typeof context === "string") {
211 resourceData.context = context;
212 } else {
213 resourceData.context = compiler.context;
214 }
215
216 if (virtualConfig.version) {
217 const cacheKey = toCacheKey(resourceData.resource, scheme);
218 const cacheVersion = this.getCacheVersion(virtualConfig.version);
219 compilation.valueCacheVersions.set(
220 cacheKey,
221 /** @type {string} */ (cacheVersion)
222 );
223 }
224
225 return true;
226 });
227
228 const hooks = NormalModule.getCompilationHooks(compilation);
229 hooks.readResource
230 .for(scheme)
231 .tapAsync(PLUGIN_NAME, async (loaderContext, callback) => {
232 const { resourcePath } = loaderContext;
233 const module = /** @type {NormalModule} */ (loaderContext._module);
234 const cacheKey = toCacheKey(resourcePath, scheme);
235
236 const addVersionValueDependency = () => {
237 if (!module || !module.buildInfo) return;
238
239 const buildInfo = module.buildInfo;
240 if (!buildInfo.valueDependencies) {
241 buildInfo.valueDependencies = new Map();
242 }
243
244 const cacheVersion = compilation.valueCacheVersions.get(cacheKey);
245 if (compilation.valueCacheVersions.has(cacheKey)) {
246 buildInfo.valueDependencies.set(
247 cacheKey,
248 /** @type {string} */ (cacheVersion)
249 );
250 }
251 };
252
253 try {
254 const virtualConfig =
255 this.findVirtualModuleConfigById(resourcePath);
256 const content = await virtualConfig.source(loaderContext);
257 addVersionValueDependency();
258 callback(null, content);
259 } catch (err) {
260 callback(/** @type {Error} */ (err));
261 }
262 });
263 }
264 );
265 }
266
267 /**
268 * Finds virtual module config by id.
269 * @param {string} id The module id
270 * @returns {VirtualModuleConfig} The virtual module config
271 */
272 findVirtualModuleConfigById(id) {
273 const config = this.modules[id];
274 if (!config) {
275 throw new ModuleNotFoundError(
276 null,
277 new Error(`Can't resolve virtual module ${id}`),
278 {
279 name: `virtual module ${id}`
280 }
281 );
282 }
283 return config;
284 }
285
286 /**
287 * Get the cache version for a given version value
288 * @param {VersionFn | true | string} version The version value or function
289 * @returns {string | undefined} The cache version
290 */
291 getCacheVersion(version) {
292 return version === true
293 ? undefined
294 : (typeof version === "function" ? version() : version) || "unset";
295 }
296}
297
298VirtualUrlPlugin.DEFAULT_SCHEME = DEFAULT_SCHEME;
299
300module.exports = VirtualUrlPlugin;
Note: See TracBrowser for help on using the repository browser.