source: frontend/node_modules/enhanced-resolve/lib/ImportsFieldPlugin.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: 7.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 DescriptionFileUtils = require("./DescriptionFileUtils");
9const forEachBail = require("./forEachBail");
10const { processImportsField } = require("./util/entrypoints");
11const { parseIdentifier } = require("./util/identifier");
12const { invalidSegmentRegEx } = require("./util/path");
13
14/** @typedef {import("./Resolver")} Resolver */
15/** @typedef {import("./Resolver").JsonObject} JsonObject */
16/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
17/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
18/** @typedef {import("./util/entrypoints").FieldProcessor} FieldProcessor */
19/** @typedef {import("./util/entrypoints").ImportsField} ImportsField */
20
21const dotCode = ".".charCodeAt(0);
22
23module.exports = class ImportsFieldPlugin {
24 /**
25 * @param {string | ResolveStepHook} source source
26 * @param {Set<string>} conditionNames condition names
27 * @param {string | string[]} fieldNamePath name path
28 * @param {string | ResolveStepHook} targetFile target file
29 * @param {string | ResolveStepHook} targetPackage target package
30 */
31 constructor(
32 source,
33 conditionNames,
34 fieldNamePath,
35 targetFile,
36 targetPackage,
37 ) {
38 this.source = source;
39 this.targetFile = targetFile;
40 this.targetPackage = targetPackage;
41 this.conditionNames = conditionNames;
42 this.fieldName = fieldNamePath;
43 // `null` is cached for description files that have no imports field,
44 // so subsequent resolves against the same package.json skip the
45 // `DescriptionFileUtils.getField` walk entirely.
46 /** @type {WeakMap<JsonObject, FieldProcessor | null>} */
47 this._fieldProcessorCache = new WeakMap();
48 }
49
50 /**
51 * @param {Resolver} resolver the resolver
52 * @returns {void}
53 */
54 apply(resolver) {
55 const targetFile = resolver.ensureHook(this.targetFile);
56 const targetPackage = resolver.ensureHook(this.targetPackage);
57
58 resolver
59 .getHook(this.source)
60 .tapAsync("ImportsFieldPlugin", (request, resolveContext, callback) => {
61 // When there is no description file, abort
62 if (!request.descriptionFileData || request.request === undefined) {
63 return callback();
64 }
65
66 const { descriptionFileData } = request;
67 // Skip the concat when there's nothing to append — the common
68 // case has empty query/fragment, so this avoids an allocation
69 // per resolve. Mirrors the pattern in ExportsFieldPlugin.
70 const remainingRequest =
71 request.query || request.fragment
72 ? request.request + request.query + request.fragment
73 : request.request;
74
75 /** @type {string[]} */
76 let paths;
77 /** @type {string | null} */
78 let usedField;
79
80 try {
81 // Look up the cached processor first. On a cache hit we
82 // avoid re-walking the description file for the imports
83 // field — and `null` is cached for description files that
84 // have no imports field at all, so those skip the read
85 // entirely. `processImportsField` can throw on a
86 // malformed `imports` map, so building the processor must
87 // stay inside this try/catch.
88 let fieldProcessor =
89 this._fieldProcessorCache.get(descriptionFileData);
90 if (
91 fieldProcessor === undefined &&
92 !this._fieldProcessorCache.has(descriptionFileData)
93 ) {
94 const importsField =
95 /** @type {ImportsField | null | undefined} */
96 (
97 DescriptionFileUtils.getField(
98 descriptionFileData,
99 this.fieldName,
100 )
101 );
102 fieldProcessor = importsField
103 ? processImportsField(importsField)
104 : null;
105 this._fieldProcessorCache.set(descriptionFileData, fieldProcessor);
106 }
107 if (!fieldProcessor) return callback();
108
109 if (request.directory) {
110 return callback(
111 new Error(
112 `Resolving to directories is not possible with the imports field (request was ${remainingRequest}/)`,
113 ),
114 );
115 }
116
117 [paths, usedField] = fieldProcessor(
118 remainingRequest,
119 this.conditionNames,
120 );
121 } catch (/** @type {unknown} */ err) {
122 if (resolveContext.log) {
123 resolveContext.log(
124 `Imports field in ${request.descriptionFilePath} can't be processed: ${err}`,
125 );
126 }
127 return callback(/** @type {Error} */ (err));
128 }
129
130 if (paths.length === 0) {
131 return callback(
132 new Error(
133 `Package import ${remainingRequest} is not imported from package ${request.descriptionFileRoot} (see imports field in ${request.descriptionFilePath})`,
134 ),
135 );
136 }
137
138 forEachBail(
139 paths,
140 /**
141 * @param {string} path path
142 * @param {(err?: null | Error, result?: null | ResolveRequest) => void} callback callback
143 * @param {number} i index
144 * @returns {void}
145 */
146 (path, callback, i) => {
147 const parsedIdentifier = parseIdentifier(path);
148
149 if (!parsedIdentifier) return callback();
150
151 const [path_, query, fragment] = parsedIdentifier;
152
153 switch (path_.charCodeAt(0)) {
154 // should be relative
155 case dotCode: {
156 const withoutDotSlash = path_.slice(2);
157 if (invalidSegmentRegEx.test(withoutDotSlash)) {
158 if (paths.length === i) {
159 return callback(
160 new Error(
161 `Invalid "imports" target "${path}" defined for "${usedField}" in the package config ${request.descriptionFilePath}, targets must start with "./"`,
162 ),
163 );
164 }
165
166 return callback();
167 }
168
169 /** @type {ResolveRequest} */
170 const obj = {
171 ...request,
172 request: undefined,
173 path: resolver.join(
174 /** @type {string} */ (request.descriptionFileRoot),
175 path_,
176 ),
177 relativePath: path_,
178 query,
179 fragment,
180 };
181
182 resolver.doResolve(
183 targetFile,
184 obj,
185 `using imports field: ${path}`,
186 resolveContext,
187 (err, result) => {
188 if (err) return callback(err);
189 // Don't allow to continue - https://github.com/webpack/enhanced-resolve/issues/400
190 if (result === undefined) return callback(null, null);
191 callback(null, result);
192 },
193 );
194 break;
195 }
196
197 // package resolving
198 default: {
199 /** @type {ResolveRequest} */
200 const obj = {
201 ...request,
202 request: path_,
203 relativePath: path_,
204 fullySpecified: true,
205 query,
206 fragment,
207 };
208
209 resolver.doResolve(
210 targetPackage,
211 obj,
212 `using imports field: ${path}`,
213 resolveContext,
214 (err, result) => {
215 if (err) return callback(err);
216 // Don't allow to continue - https://github.com/webpack/enhanced-resolve/issues/400
217 if (result === undefined) return callback(null, null);
218 callback(null, result);
219 },
220 );
221 }
222 }
223 },
224 /**
225 * @param {null | Error=} err error
226 * @param {null | ResolveRequest=} result result
227 * @returns {void}
228 */
229 (err, result) => callback(err, result || null),
230 );
231 });
232 }
233};
Note: See TracBrowser for help on using the repository browser.