@plumeria/utils 19.2.3 → 19.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resolver.js +242 -42
- package/package.json +1 -1
package/dist/resolver.js
CHANGED
|
@@ -37,41 +37,240 @@ exports.resetImportResolutionCache = resetImportResolutionCache;
|
|
|
37
37
|
exports.resolveImportPath = resolveImportPath;
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
const CONFIG_DIR = '${configDir}';
|
|
41
|
+
let cachedPathsMatcher = undefined;
|
|
42
|
+
function stripComments(text) {
|
|
43
|
+
let out = '';
|
|
44
|
+
let inString = false;
|
|
45
|
+
for (let i = 0; i < text.length; i++) {
|
|
46
|
+
const char = text[i];
|
|
47
|
+
if (inString) {
|
|
48
|
+
if (char === '\\') {
|
|
49
|
+
out += char + (text[i + 1] ?? '');
|
|
50
|
+
i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (char === '"')
|
|
54
|
+
inString = false;
|
|
55
|
+
out += char;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (char === '"') {
|
|
59
|
+
inString = true;
|
|
60
|
+
out += char;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (char === '/' && text[i + 1] === '/') {
|
|
64
|
+
while (i < text.length && text[i] !== '\n')
|
|
65
|
+
i++;
|
|
66
|
+
out += '\n';
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (char === '/' && text[i + 1] === '*') {
|
|
70
|
+
i += 2;
|
|
71
|
+
while (i < text.length && !(text[i] === '*' && text[i + 1] === '/'))
|
|
72
|
+
i++;
|
|
73
|
+
i++;
|
|
74
|
+
out += ' ';
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
out += char;
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
function stripTrailingCommas(text) {
|
|
82
|
+
let out = '';
|
|
83
|
+
let inString = false;
|
|
84
|
+
for (let i = 0; i < text.length; i++) {
|
|
85
|
+
const char = text[i];
|
|
86
|
+
if (inString) {
|
|
87
|
+
if (char === '\\') {
|
|
88
|
+
out += char + (text[i + 1] ?? '');
|
|
89
|
+
i++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (char === '"')
|
|
93
|
+
inString = false;
|
|
94
|
+
out += char;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (char === '"') {
|
|
98
|
+
inString = true;
|
|
99
|
+
out += char;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (char === ',') {
|
|
103
|
+
let next = i + 1;
|
|
104
|
+
while (next < text.length && /\s/.test(text[next]))
|
|
105
|
+
next++;
|
|
106
|
+
if (text[next] === '}' || text[next] === ']')
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
out += char;
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
function readConfig(configPath) {
|
|
114
|
+
let text;
|
|
43
115
|
try {
|
|
44
|
-
|
|
45
|
-
cachedTsConfig = {
|
|
46
|
-
config: config.compilerOptions || null,
|
|
47
|
-
basePath: path.dirname(configPath),
|
|
48
|
-
};
|
|
116
|
+
text = fs.readFileSync(configPath, 'utf8');
|
|
49
117
|
}
|
|
50
118
|
catch {
|
|
51
|
-
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const config = JSON.parse(stripTrailingCommas(stripComments(text.replace(/^\uFEFF/, ''))));
|
|
123
|
+
return config && typeof config === 'object' ? config : null;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
52
127
|
}
|
|
53
128
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
129
|
+
function configFileAt(candidate) {
|
|
130
|
+
return fs.statSync(candidate, { throwIfNoEntry: false })?.isDirectory()
|
|
131
|
+
? path.join(candidate, 'tsconfig.json')
|
|
132
|
+
: candidate;
|
|
133
|
+
}
|
|
134
|
+
function packageNameOf(specifier) {
|
|
135
|
+
const segments = specifier.split('/');
|
|
136
|
+
return specifier.startsWith('@')
|
|
137
|
+
? segments.slice(0, 2).join('/')
|
|
138
|
+
: segments[0];
|
|
139
|
+
}
|
|
140
|
+
function resolveQuietly(specifier, basePath) {
|
|
141
|
+
try {
|
|
142
|
+
return require.resolve(specifier, { paths: [basePath] });
|
|
57
143
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function resolveExtendsPath(specifier, basePath) {
|
|
149
|
+
if (specifier.startsWith('.') || path.isAbsolute(specifier)) {
|
|
150
|
+
const resolved = configFileAt(path.resolve(basePath, specifier));
|
|
151
|
+
if (isFile(resolved))
|
|
152
|
+
return resolved;
|
|
153
|
+
const withExtension = resolved + '.json';
|
|
154
|
+
return isFile(withExtension) ? withExtension : null;
|
|
155
|
+
}
|
|
156
|
+
const packageName = packageNameOf(specifier);
|
|
157
|
+
const exported = () => {
|
|
158
|
+
const resolved = resolveQuietly(specifier, basePath);
|
|
159
|
+
return resolved?.endsWith('.json') ? resolved : null;
|
|
160
|
+
};
|
|
161
|
+
if (specifier !== packageName)
|
|
162
|
+
return exported();
|
|
163
|
+
const manifestPath = resolveQuietly(`${packageName}/package.json`, basePath);
|
|
164
|
+
if (!manifestPath)
|
|
165
|
+
return exported();
|
|
166
|
+
const manifest = readConfig(manifestPath);
|
|
167
|
+
if (manifest?.exports !== undefined) {
|
|
168
|
+
const resolved = exported();
|
|
169
|
+
if (resolved)
|
|
170
|
+
return resolved;
|
|
171
|
+
}
|
|
172
|
+
const packageDir = path.dirname(manifestPath);
|
|
173
|
+
if (typeof manifest?.tsconfig === 'string') {
|
|
174
|
+
const declared = configFileAt(path.resolve(packageDir, manifest.tsconfig));
|
|
175
|
+
if (isFile(declared))
|
|
176
|
+
return declared;
|
|
177
|
+
}
|
|
178
|
+
const fallback = path.join(packageDir, 'tsconfig.json');
|
|
179
|
+
return isFile(fallback) ? fallback : null;
|
|
180
|
+
}
|
|
181
|
+
function patternsOf(paths, basePath, entryPath) {
|
|
182
|
+
if (!paths || typeof paths !== 'object')
|
|
183
|
+
return null;
|
|
184
|
+
const patterns = [];
|
|
185
|
+
for (const [alias, targets] of Object.entries(paths)) {
|
|
186
|
+
if (!Array.isArray(targets) || targets.length === 0)
|
|
187
|
+
continue;
|
|
188
|
+
const written = targets.filter((target) => typeof target === 'string');
|
|
189
|
+
if (written.length === 0)
|
|
190
|
+
continue;
|
|
191
|
+
const star = alias.indexOf('*');
|
|
192
|
+
patterns.push({
|
|
193
|
+
prefix: star < 0 ? alias : alias.slice(0, star),
|
|
194
|
+
suffix: star < 0 ? '' : alias.slice(star + 1),
|
|
195
|
+
wildcard: star >= 0,
|
|
196
|
+
basePath,
|
|
197
|
+
targets: written.map((target) => target.startsWith(CONFIG_DIR)
|
|
198
|
+
? path.join(entryPath, target.slice(CONFIG_DIR.length))
|
|
199
|
+
: target),
|
|
200
|
+
});
|
|
70
201
|
}
|
|
71
|
-
|
|
72
|
-
|
|
202
|
+
return patterns;
|
|
203
|
+
}
|
|
204
|
+
function collectPatterns(configPath, seen, entryPath = path.dirname(configPath)) {
|
|
205
|
+
if (seen.has(configPath))
|
|
206
|
+
return null;
|
|
207
|
+
seen.add(configPath);
|
|
208
|
+
const config = readConfig(configPath);
|
|
209
|
+
if (!config)
|
|
210
|
+
return null;
|
|
211
|
+
const basePath = path.dirname(configPath);
|
|
212
|
+
const own = patternsOf(config.compilerOptions?.paths, basePath, entryPath);
|
|
213
|
+
if (own)
|
|
214
|
+
return own;
|
|
215
|
+
const inherited = Array.isArray(config.extends)
|
|
216
|
+
? config.extends.slice().reverse()
|
|
217
|
+
: config.extends
|
|
218
|
+
? [config.extends]
|
|
219
|
+
: [];
|
|
220
|
+
for (const specifier of inherited) {
|
|
221
|
+
const resolved = resolveExtendsPath(specifier, basePath);
|
|
222
|
+
if (!resolved)
|
|
223
|
+
continue;
|
|
224
|
+
const patterns = collectPatterns(resolved, seen, entryPath);
|
|
225
|
+
if (patterns)
|
|
226
|
+
return patterns;
|
|
227
|
+
}
|
|
228
|
+
for (const reference of config.references ?? []) {
|
|
229
|
+
if (!reference?.path)
|
|
230
|
+
continue;
|
|
231
|
+
const patterns = collectPatterns(configFileAt(path.resolve(basePath, reference.path)), seen);
|
|
232
|
+
if (patterns)
|
|
233
|
+
return patterns;
|
|
73
234
|
}
|
|
74
|
-
return
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
function buildMatcher(patterns) {
|
|
238
|
+
const exact = patterns.filter((pattern) => !pattern.wildcard);
|
|
239
|
+
const wildcard = patterns
|
|
240
|
+
.filter((pattern) => pattern.wildcard)
|
|
241
|
+
.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
242
|
+
return (specifier) => {
|
|
243
|
+
for (const pattern of exact) {
|
|
244
|
+
if (pattern.prefix === specifier) {
|
|
245
|
+
return pattern.targets.map((target) => path.resolve(pattern.basePath, target));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (const pattern of wildcard) {
|
|
249
|
+
if (specifier.length >= pattern.prefix.length + pattern.suffix.length &&
|
|
250
|
+
specifier.startsWith(pattern.prefix) &&
|
|
251
|
+
specifier.endsWith(pattern.suffix)) {
|
|
252
|
+
const matched = specifier.slice(pattern.prefix.length, specifier.length - pattern.suffix.length);
|
|
253
|
+
return pattern.targets.map((target) => path.resolve(pattern.basePath, target.replace('*', matched)));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return [];
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function loadPathsMatcher(configPath) {
|
|
260
|
+
const patterns = collectPatterns(configPath, new Set());
|
|
261
|
+
return patterns ? buildMatcher(patterns) : null;
|
|
262
|
+
}
|
|
263
|
+
function resetImportResolutionCache(cwd = process.cwd()) {
|
|
264
|
+
cachedPathsMatcher = loadPathsMatcher(path.join(cwd, 'tsconfig.json'));
|
|
265
|
+
}
|
|
266
|
+
function getPathsMatcher() {
|
|
267
|
+
if (cachedPathsMatcher !== undefined)
|
|
268
|
+
return cachedPathsMatcher;
|
|
269
|
+
const tsConfigPath = path.join(process.cwd(), 'tsconfig.json');
|
|
270
|
+
cachedPathsMatcher = fs.existsSync(tsConfigPath)
|
|
271
|
+
? loadPathsMatcher(tsConfigPath)
|
|
272
|
+
: null;
|
|
273
|
+
return cachedPathsMatcher;
|
|
75
274
|
}
|
|
76
275
|
const extensions = [
|
|
77
276
|
'.ts',
|
|
@@ -83,12 +282,22 @@ const extensions = [
|
|
|
83
282
|
'/index.js',
|
|
84
283
|
'/index.jsx',
|
|
85
284
|
];
|
|
285
|
+
const rewritten = {
|
|
286
|
+
'.js': ['.ts', '.tsx'],
|
|
287
|
+
'.jsx': ['.tsx'],
|
|
288
|
+
};
|
|
86
289
|
function isFile(candidate) {
|
|
87
290
|
return fs.statSync(candidate, { throwIfNoEntry: false })?.isFile() ?? false;
|
|
88
291
|
}
|
|
89
292
|
function resolveWithExtension(basePath) {
|
|
90
293
|
if (isFile(basePath))
|
|
91
294
|
return basePath;
|
|
295
|
+
const extension = path.extname(basePath);
|
|
296
|
+
for (const replacement of rewritten[extension] ?? []) {
|
|
297
|
+
const candidate = basePath.slice(0, -extension.length) + replacement;
|
|
298
|
+
if (isFile(candidate))
|
|
299
|
+
return candidate;
|
|
300
|
+
}
|
|
92
301
|
for (const ext of extensions) {
|
|
93
302
|
const fullPath = basePath + ext;
|
|
94
303
|
if (isFile(fullPath))
|
|
@@ -102,21 +311,12 @@ function resolveImportPath(importPath, importerPath) {
|
|
|
102
311
|
if (importPath.startsWith('.')) {
|
|
103
312
|
return resolveWithExtension(path.resolve(path.dirname(importerPath), importPath));
|
|
104
313
|
}
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (importPath.startsWith(prefix)) {
|
|
112
|
-
for (const target of targets) {
|
|
113
|
-
const resolvedTarget = target.replace(/\*$/, '');
|
|
114
|
-
const candidate = path.resolve(root, resolvedTarget + importPath.slice(prefix.length));
|
|
115
|
-
const result = resolveWithExtension(candidate);
|
|
116
|
-
if (result)
|
|
117
|
-
return result;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
314
|
+
const matchPaths = getPathsMatcher();
|
|
315
|
+
if (matchPaths) {
|
|
316
|
+
for (const candidate of matchPaths(importPath)) {
|
|
317
|
+
const result = resolveWithExtension(candidate);
|
|
318
|
+
if (result)
|
|
319
|
+
return result;
|
|
120
320
|
}
|
|
121
321
|
}
|
|
122
322
|
let currentDir = path.dirname(importerPath);
|