source: imaps-frontend/node_modules/webpack/lib/ContextModuleFactory.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 14.0 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 asyncLib = require("neo-async");
9const { AsyncSeriesWaterfallHook, SyncWaterfallHook } = require("tapable");
10const ContextModule = require("./ContextModule");
11const ModuleFactory = require("./ModuleFactory");
12const ContextElementDependency = require("./dependencies/ContextElementDependency");
13const LazySet = require("./util/LazySet");
14const { cachedSetProperty } = require("./util/cleverMerge");
15const { createFakeHook } = require("./util/deprecation");
16const { join } = require("./util/fs");
17
18/** @typedef {import("./ContextModule").ContextModuleOptions} ContextModuleOptions */
19/** @typedef {import("./ContextModule").ResolveDependenciesCallback} ResolveDependenciesCallback */
20/** @typedef {import("./Module")} Module */
21/** @typedef {import("./ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
22/** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
23/** @typedef {import("./ResolverFactory")} ResolverFactory */
24/** @typedef {import("./dependencies/ContextDependency")} ContextDependency */
25/** @typedef {import("enhanced-resolve").ResolveRequest} ResolveRequest */
26/**
27 * @template T
28 * @typedef {import("./util/deprecation").FakeHook<T>} FakeHook<T>
29 */
30/** @typedef {import("./util/fs").IStats} IStats */
31/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
32/** @typedef {{ context: string, request: string }} ContextAlternativeRequest */
33
34const EMPTY_RESOLVE_OPTIONS = {};
35
36module.exports = class ContextModuleFactory extends ModuleFactory {
37 /**
38 * @param {ResolverFactory} resolverFactory resolverFactory
39 */
40 constructor(resolverFactory) {
41 super();
42 /** @type {AsyncSeriesWaterfallHook<[ContextAlternativeRequest[], ContextModuleOptions]>} */
43 const alternativeRequests = new AsyncSeriesWaterfallHook([
44 "modules",
45 "options"
46 ]);
47 this.hooks = Object.freeze({
48 /** @type {AsyncSeriesWaterfallHook<[TODO]>} */
49 beforeResolve: new AsyncSeriesWaterfallHook(["data"]),
50 /** @type {AsyncSeriesWaterfallHook<[TODO]>} */
51 afterResolve: new AsyncSeriesWaterfallHook(["data"]),
52 /** @type {SyncWaterfallHook<[string[]]>} */
53 contextModuleFiles: new SyncWaterfallHook(["files"]),
54 /** @type {FakeHook<Pick<AsyncSeriesWaterfallHook<[ContextAlternativeRequest[]]>, "tap" | "tapAsync" | "tapPromise" | "name">>} */
55 alternatives: createFakeHook(
56 {
57 name: "alternatives",
58 /** @type {AsyncSeriesWaterfallHook<[ContextAlternativeRequest[]]>["intercept"]} */
59 intercept: interceptor => {
60 throw new Error(
61 "Intercepting fake hook ContextModuleFactory.hooks.alternatives is not possible, use ContextModuleFactory.hooks.alternativeRequests instead"
62 );
63 },
64 /** @type {AsyncSeriesWaterfallHook<[ContextAlternativeRequest[]]>["tap"]} */
65 tap: (options, fn) => {
66 alternativeRequests.tap(options, fn);
67 },
68 /** @type {AsyncSeriesWaterfallHook<[ContextAlternativeRequest[]]>["tapAsync"]} */
69 tapAsync: (options, fn) => {
70 alternativeRequests.tapAsync(options, (items, _options, callback) =>
71 fn(items, callback)
72 );
73 },
74 /** @type {AsyncSeriesWaterfallHook<[ContextAlternativeRequest[]]>["tapPromise"]} */
75 tapPromise: (options, fn) => {
76 alternativeRequests.tapPromise(options, fn);
77 }
78 },
79 "ContextModuleFactory.hooks.alternatives has deprecated in favor of ContextModuleFactory.hooks.alternativeRequests with an additional options argument.",
80 "DEP_WEBPACK_CONTEXT_MODULE_FACTORY_ALTERNATIVES"
81 ),
82 alternativeRequests
83 });
84 this.resolverFactory = resolverFactory;
85 }
86
87 /**
88 * @param {ModuleFactoryCreateData} data data object
89 * @param {function((Error | null)=, ModuleFactoryResult=): void} callback callback
90 * @returns {void}
91 */
92 create(data, callback) {
93 const context = data.context;
94 const dependencies = data.dependencies;
95 const resolveOptions = data.resolveOptions;
96 const dependency = /** @type {ContextDependency} */ (dependencies[0]);
97 const fileDependencies = new LazySet();
98 const missingDependencies = new LazySet();
99 const contextDependencies = new LazySet();
100 this.hooks.beforeResolve.callAsync(
101 {
102 context,
103 dependencies,
104 layer: data.contextInfo.issuerLayer,
105 resolveOptions,
106 fileDependencies,
107 missingDependencies,
108 contextDependencies,
109 ...dependency.options
110 },
111 (err, beforeResolveResult) => {
112 if (err) {
113 return callback(err, {
114 fileDependencies,
115 missingDependencies,
116 contextDependencies
117 });
118 }
119
120 // Ignored
121 if (!beforeResolveResult) {
122 return callback(null, {
123 fileDependencies,
124 missingDependencies,
125 contextDependencies
126 });
127 }
128
129 const context = beforeResolveResult.context;
130 const request = beforeResolveResult.request;
131 const resolveOptions = beforeResolveResult.resolveOptions;
132
133 let loaders;
134 let resource;
135 let loadersPrefix = "";
136 const idx = request.lastIndexOf("!");
137 if (idx >= 0) {
138 let loadersRequest = request.slice(0, idx + 1);
139 let i;
140 for (
141 i = 0;
142 i < loadersRequest.length && loadersRequest[i] === "!";
143 i++
144 ) {
145 loadersPrefix += "!";
146 }
147 loadersRequest = loadersRequest
148 .slice(i)
149 .replace(/!+$/, "")
150 .replace(/!!+/g, "!");
151 loaders = loadersRequest === "" ? [] : loadersRequest.split("!");
152 resource = request.slice(idx + 1);
153 } else {
154 loaders = [];
155 resource = request;
156 }
157
158 const contextResolver = this.resolverFactory.get(
159 "context",
160 dependencies.length > 0
161 ? cachedSetProperty(
162 resolveOptions || EMPTY_RESOLVE_OPTIONS,
163 "dependencyType",
164 dependencies[0].category
165 )
166 : resolveOptions
167 );
168 const loaderResolver = this.resolverFactory.get("loader");
169
170 asyncLib.parallel(
171 [
172 callback => {
173 const results = /** @type ResolveRequest[] */ ([]);
174 /**
175 * @param {ResolveRequest} obj obj
176 * @returns {void}
177 */
178 const yield_ = obj => {
179 results.push(obj);
180 };
181
182 contextResolver.resolve(
183 {},
184 context,
185 resource,
186 {
187 fileDependencies,
188 missingDependencies,
189 contextDependencies,
190 yield: yield_
191 },
192 err => {
193 if (err) return callback(err);
194 callback(null, results);
195 }
196 );
197 },
198 callback => {
199 asyncLib.map(
200 loaders,
201 (loader, callback) => {
202 loaderResolver.resolve(
203 {},
204 context,
205 loader,
206 {
207 fileDependencies,
208 missingDependencies,
209 contextDependencies
210 },
211 (err, result) => {
212 if (err) return callback(err);
213 callback(null, /** @type {string} */ (result));
214 }
215 );
216 },
217 callback
218 );
219 }
220 ],
221 (err, result) => {
222 if (err) {
223 return callback(err, {
224 fileDependencies,
225 missingDependencies,
226 contextDependencies
227 });
228 }
229 let [contextResult, loaderResult] =
230 /** @type {[ResolveRequest[], string[]]} */ (result);
231 if (contextResult.length > 1) {
232 const first = contextResult[0];
233 contextResult = contextResult.filter(r => r.path);
234 if (contextResult.length === 0) contextResult.push(first);
235 }
236 this.hooks.afterResolve.callAsync(
237 {
238 addon:
239 loadersPrefix +
240 loaderResult.join("!") +
241 (loaderResult.length > 0 ? "!" : ""),
242 resource:
243 contextResult.length > 1
244 ? contextResult.map(r => r.path)
245 : contextResult[0].path,
246 resolveDependencies: this.resolveDependencies.bind(this),
247 resourceQuery: contextResult[0].query,
248 resourceFragment: contextResult[0].fragment,
249 ...beforeResolveResult
250 },
251 (err, result) => {
252 if (err) {
253 return callback(err, {
254 fileDependencies,
255 missingDependencies,
256 contextDependencies
257 });
258 }
259
260 // Ignored
261 if (!result) {
262 return callback(null, {
263 fileDependencies,
264 missingDependencies,
265 contextDependencies
266 });
267 }
268
269 return callback(null, {
270 module: new ContextModule(result.resolveDependencies, result),
271 fileDependencies,
272 missingDependencies,
273 contextDependencies
274 });
275 }
276 );
277 }
278 );
279 }
280 );
281 }
282
283 /**
284 * @param {InputFileSystem} fs file system
285 * @param {ContextModuleOptions} options options
286 * @param {ResolveDependenciesCallback} callback callback function
287 * @returns {void}
288 */
289 resolveDependencies(fs, options, callback) {
290 const cmf = this;
291 const {
292 resource,
293 resourceQuery,
294 resourceFragment,
295 recursive,
296 regExp,
297 include,
298 exclude,
299 referencedExports,
300 category,
301 typePrefix,
302 attributes
303 } = options;
304 if (!regExp || !resource) return callback(null, []);
305
306 /**
307 * @param {string} ctx context
308 * @param {string} directory directory
309 * @param {Set<string>} visited visited
310 * @param {ResolveDependenciesCallback} callback callback
311 */
312 const addDirectoryChecked = (ctx, directory, visited, callback) => {
313 /** @type {NonNullable<InputFileSystem["realpath"]>} */
314 (fs.realpath)(directory, (err, _realPath) => {
315 if (err) return callback(err);
316 const realPath = /** @type {string} */ (_realPath);
317 if (visited.has(realPath)) return callback(null, []);
318 /** @type {Set<string> | undefined} */
319 let recursionStack;
320 addDirectory(
321 ctx,
322 directory,
323 (_, dir, callback) => {
324 if (recursionStack === undefined) {
325 recursionStack = new Set(visited);
326 recursionStack.add(realPath);
327 }
328 addDirectoryChecked(ctx, dir, recursionStack, callback);
329 },
330 callback
331 );
332 });
333 };
334
335 /**
336 * @param {string} ctx context
337 * @param {string} directory directory
338 * @param {function(string, string, function(): void): void} addSubDirectory addSubDirectoryFn
339 * @param {ResolveDependenciesCallback} callback callback
340 */
341 const addDirectory = (ctx, directory, addSubDirectory, callback) => {
342 fs.readdir(directory, (err, files) => {
343 if (err) return callback(err);
344 const processedFiles = cmf.hooks.contextModuleFiles.call(
345 /** @type {string[]} */ (files).map(file => file.normalize("NFC"))
346 );
347 if (!processedFiles || processedFiles.length === 0)
348 return callback(null, []);
349 asyncLib.map(
350 processedFiles.filter(p => p.indexOf(".") !== 0),
351 (segment, callback) => {
352 const subResource = join(fs, directory, segment);
353
354 if (!exclude || !subResource.match(exclude)) {
355 fs.stat(subResource, (err, _stat) => {
356 if (err) {
357 if (err.code === "ENOENT") {
358 // ENOENT is ok here because the file may have been deleted between
359 // the readdir and stat calls.
360 return callback();
361 }
362 return callback(err);
363 }
364
365 const stat = /** @type {IStats} */ (_stat);
366
367 if (stat.isDirectory()) {
368 if (!recursive) return callback();
369 addSubDirectory(ctx, subResource, callback);
370 } else if (
371 stat.isFile() &&
372 (!include || subResource.match(include))
373 ) {
374 /** @type {{ context: string, request: string }} */
375 const obj = {
376 context: ctx,
377 request: `.${subResource.slice(ctx.length).replace(/\\/g, "/")}`
378 };
379
380 this.hooks.alternativeRequests.callAsync(
381 [obj],
382 options,
383 (err, alternatives) => {
384 if (err) return callback(err);
385 callback(
386 null,
387 /** @type {ContextAlternativeRequest[]} */
388 (alternatives)
389 .filter(obj =>
390 regExp.test(/** @type {string} */ (obj.request))
391 )
392 .map(obj => {
393 const dep = new ContextElementDependency(
394 `${obj.request}${resourceQuery}${resourceFragment}`,
395 obj.request,
396 typePrefix,
397 /** @type {string} */
398 (category),
399 referencedExports,
400 /** @type {TODO} */
401 (obj.context),
402 attributes
403 );
404 dep.optional = true;
405 return dep;
406 })
407 );
408 }
409 );
410 } else {
411 callback();
412 }
413 });
414 } else {
415 callback();
416 }
417 },
418 (err, result) => {
419 if (err) return callback(err);
420
421 if (!result) return callback(null, []);
422
423 const flattenedResult = [];
424
425 for (const item of result) {
426 if (item) flattenedResult.push(...item);
427 }
428
429 callback(null, flattenedResult);
430 }
431 );
432 });
433 };
434
435 /**
436 * @param {string} ctx context
437 * @param {string} dir dir
438 * @param {ResolveDependenciesCallback} callback callback
439 * @returns {void}
440 */
441 const addSubDirectory = (ctx, dir, callback) =>
442 addDirectory(ctx, dir, addSubDirectory, callback);
443
444 /**
445 * @param {string} resource resource
446 * @param {ResolveDependenciesCallback} callback callback
447 */
448 const visitResource = (resource, callback) => {
449 if (typeof fs.realpath === "function") {
450 addDirectoryChecked(resource, resource, new Set(), callback);
451 } else {
452 addDirectory(resource, resource, addSubDirectory, callback);
453 }
454 };
455
456 if (typeof resource === "string") {
457 visitResource(resource, callback);
458 } else {
459 asyncLib.map(resource, visitResource, (err, _result) => {
460 if (err) return callback(err);
461 const result = /** @type {ContextElementDependency[][]} */ (_result);
462
463 // result dependencies should have unique userRequest
464 // ordered by resolve result
465 /** @type {Set<string>} */
466 const temp = new Set();
467 /** @type {ContextElementDependency[]} */
468 const res = [];
469 for (let i = 0; i < result.length; i++) {
470 const inner = result[i];
471 for (const el of inner) {
472 if (temp.has(el.userRequest)) continue;
473 res.push(el);
474 temp.add(el.userRequest);
475 }
476 }
477 callback(null, res);
478 });
479 }
480 }
481};
Note: See TracBrowser for help on using the repository browser.