@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.
Files changed (2) hide show
  1. package/dist/resolver.js +242 -42
  2. 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
- let cachedTsConfig = undefined;
41
- function resetImportResolutionCache(cwd = process.cwd()) {
42
- const configPath = path.join(cwd, 'tsconfig.json');
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
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
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
- cachedTsConfig = null;
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 getTsConfig() {
55
- if (cachedTsConfig !== undefined) {
56
- return cachedTsConfig;
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
- const tsConfigPath = path.join(process.cwd(), 'tsconfig.json');
59
- if (fs.existsSync(tsConfigPath)) {
60
- try {
61
- const config = require(tsConfigPath);
62
- cachedTsConfig = {
63
- config: config.compilerOptions || null,
64
- basePath: path.dirname(tsConfigPath),
65
- };
66
- }
67
- catch {
68
- cachedTsConfig = null;
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
- else {
72
- cachedTsConfig = null;
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 cachedTsConfig;
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 tsConfig = getTsConfig();
106
- const config = tsConfig?.config;
107
- if (config?.paths) {
108
- const root = tsConfig.basePath;
109
- for (const [alias, targets] of Object.entries(config.paths)) {
110
- const prefix = alias.replace(/\*$/, '');
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/utils",
3
- "version": "19.2.3",
3
+ "version": "19.2.5",
4
4
  "description": "Plumeria Utils",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",