@beignet/cli 0.0.48 → 0.0.50

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 (111) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +117 -22
  3. package/dist/analysis/contract-factories.d.ts +17 -0
  4. package/dist/analysis/contract-factories.d.ts.map +1 -0
  5. package/dist/analysis/contract-factories.js +176 -0
  6. package/dist/analysis/contract-factories.js.map +1 -0
  7. package/dist/analysis/layers.d.ts +5 -0
  8. package/dist/analysis/layers.d.ts.map +1 -0
  9. package/dist/analysis/layers.js +172 -0
  10. package/dist/analysis/layers.js.map +1 -0
  11. package/dist/analysis/port-wiring.d.ts +23 -0
  12. package/dist/analysis/port-wiring.d.ts.map +1 -0
  13. package/dist/analysis/port-wiring.js +379 -0
  14. package/dist/analysis/port-wiring.js.map +1 -0
  15. package/dist/analysis/source-index.d.ts +5 -0
  16. package/dist/analysis/source-index.d.ts.map +1 -1
  17. package/dist/analysis/source-index.js +171 -42
  18. package/dist/analysis/source-index.js.map +1 -1
  19. package/dist/analysis/workspace.d.ts +11 -4
  20. package/dist/analysis/workspace.d.ts.map +1 -1
  21. package/dist/analysis/workspace.js +78 -8
  22. package/dist/analysis/workspace.js.map +1 -1
  23. package/dist/app-map-changes.d.ts +103 -0
  24. package/dist/app-map-changes.d.ts.map +1 -0
  25. package/dist/app-map-changes.js +949 -0
  26. package/dist/app-map-changes.js.map +1 -0
  27. package/dist/app-map.d.ts.map +1 -1
  28. package/dist/app-map.js +311 -92
  29. package/dist/app-map.js.map +1 -1
  30. package/dist/check.js +37 -3
  31. package/dist/check.js.map +1 -1
  32. package/dist/explain.js +4 -4
  33. package/dist/explain.js.map +1 -1
  34. package/dist/git-changes.d.ts +30 -0
  35. package/dist/git-changes.d.ts.map +1 -0
  36. package/dist/git-changes.js +367 -0
  37. package/dist/git-changes.js.map +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +26 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/inspect.d.ts.map +1 -1
  42. package/dist/inspect.js +706 -186
  43. package/dist/inspect.js.map +1 -1
  44. package/dist/lib.d.ts +3 -0
  45. package/dist/lib.d.ts.map +1 -1
  46. package/dist/lib.js +1 -0
  47. package/dist/lib.js.map +1 -1
  48. package/dist/lint.d.ts.map +1 -1
  49. package/dist/lint.js +309 -487
  50. package/dist/lint.js.map +1 -1
  51. package/dist/make/inbox.d.ts.map +1 -1
  52. package/dist/make/inbox.js +11 -4
  53. package/dist/make/inbox.js.map +1 -1
  54. package/dist/make/payments.js +2 -2
  55. package/dist/make/shared.d.ts.map +1 -1
  56. package/dist/make/shared.js +31 -27
  57. package/dist/make/shared.js.map +1 -1
  58. package/dist/make.d.ts.map +1 -1
  59. package/dist/make.js +144 -8
  60. package/dist/make.js.map +1 -1
  61. package/dist/mcp.d.ts +1 -1
  62. package/dist/mcp.d.ts.map +1 -1
  63. package/dist/mcp.js +43 -21
  64. package/dist/mcp.js.map +1 -1
  65. package/dist/provider-add.d.ts.map +1 -1
  66. package/dist/provider-add.js +142 -7
  67. package/dist/provider-add.js.map +1 -1
  68. package/dist/registry-edits.d.ts +7 -0
  69. package/dist/registry-edits.d.ts.map +1 -1
  70. package/dist/registry-edits.js +237 -53
  71. package/dist/registry-edits.js.map +1 -1
  72. package/dist/templates/agents.d.ts.map +1 -1
  73. package/dist/templates/agents.js +37 -1
  74. package/dist/templates/agents.js.map +1 -1
  75. package/dist/templates/base.d.ts.map +1 -1
  76. package/dist/templates/base.js +13 -2
  77. package/dist/templates/base.js.map +1 -1
  78. package/dist/templates/server.d.ts.map +1 -1
  79. package/dist/templates/server.js +2 -0
  80. package/dist/templates/server.js.map +1 -1
  81. package/dist/templates/shared.d.ts +1 -0
  82. package/dist/templates/shared.d.ts.map +1 -1
  83. package/dist/templates/shared.js +3 -2
  84. package/dist/templates/shared.js.map +1 -1
  85. package/package.json +7 -6
  86. package/skills/app-structure/SKILL.md +71 -7
  87. package/src/analysis/contract-factories.ts +256 -0
  88. package/src/analysis/layers.ts +266 -0
  89. package/src/analysis/port-wiring.ts +506 -0
  90. package/src/analysis/source-index.ts +225 -57
  91. package/src/analysis/workspace.ts +134 -9
  92. package/src/app-map-changes.ts +1462 -0
  93. package/src/app-map.ts +418 -109
  94. package/src/check.ts +51 -3
  95. package/src/explain.ts +4 -4
  96. package/src/git-changes.ts +511 -0
  97. package/src/index.ts +39 -1
  98. package/src/inspect.ts +1005 -238
  99. package/src/lib.ts +21 -0
  100. package/src/lint.ts +393 -643
  101. package/src/make/inbox.ts +11 -4
  102. package/src/make/payments.ts +2 -2
  103. package/src/make/shared.ts +31 -27
  104. package/src/make.ts +225 -8
  105. package/src/mcp.ts +51 -24
  106. package/src/provider-add.ts +186 -7
  107. package/src/registry-edits.ts +313 -63
  108. package/src/templates/agents.ts +37 -1
  109. package/src/templates/base.ts +13 -2
  110. package/src/templates/server.ts +2 -0
  111. package/src/templates/shared.ts +3 -2
@@ -27,12 +27,19 @@ export type LocalImportReference = {
27
27
  column: number;
28
28
  };
29
29
 
30
+ export type ModuleReference = LocalImportReference & {
31
+ syntax: "dynamic-import" | "export" | "import" | "import-equals" | "require";
32
+ staticallyResolvable: boolean;
33
+ };
34
+
30
35
  type ImportBinding =
31
36
  | { kind: "named"; file: string; exportName: string }
32
37
  | { kind: "namespace"; file: string };
33
38
 
34
39
  type FileIndex = {
35
40
  declarations: Map<string, ts.Declaration>;
41
+ localExports: Map<string, string>;
42
+ locals: Map<string, ts.Declaration>;
36
43
  imports: Map<string, ImportBinding>;
37
44
  reexports: Map<string, { file: string; exportName: string }>;
38
45
  exportStars: string[];
@@ -68,6 +75,8 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
68
75
  const sourceFile = await workspace.readSourceFile(file);
69
76
  const index: FileIndex = {
70
77
  declarations: new Map(),
78
+ localExports: new Map(),
79
+ locals: new Map(),
71
80
  imports: new Map(),
72
81
  reexports: new Map(),
73
82
  exportStars: [],
@@ -115,6 +124,22 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
115
124
  const target = source
116
125
  ? resolveLocalSourceFile(workspace, file, source)
117
126
  : undefined;
127
+
128
+ if (!statement.moduleSpecifier) {
129
+ if (
130
+ statement.exportClause &&
131
+ ts.isNamedExports(statement.exportClause)
132
+ ) {
133
+ for (const element of statement.exportClause.elements) {
134
+ if (statement.isTypeOnly || element.isTypeOnly) continue;
135
+ index.localExports.set(
136
+ element.name.text,
137
+ element.propertyName?.text ?? element.name.text,
138
+ );
139
+ }
140
+ }
141
+ continue;
142
+ }
118
143
  if (!target) continue;
119
144
 
120
145
  if (!statement.exportClause) {
@@ -133,10 +158,13 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
133
158
  continue;
134
159
  }
135
160
 
136
- if (ts.isVariableStatement(statement) && hasExportModifier(statement)) {
161
+ if (ts.isVariableStatement(statement)) {
137
162
  for (const declaration of statement.declarationList.declarations) {
138
163
  if (ts.isIdentifier(declaration.name)) {
139
- index.declarations.set(declaration.name.text, declaration);
164
+ index.locals.set(declaration.name.text, declaration);
165
+ if (hasExportModifier(statement)) {
166
+ index.declarations.set(declaration.name.text, declaration);
167
+ }
140
168
  }
141
169
  }
142
170
  continue;
@@ -148,10 +176,12 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
148
176
  ts.isInterfaceDeclaration(statement) ||
149
177
  ts.isTypeAliasDeclaration(statement) ||
150
178
  ts.isEnumDeclaration(statement)) &&
151
- hasExportModifier(statement) &&
152
179
  statement.name
153
180
  ) {
154
- index.declarations.set(statement.name.text, statement);
181
+ index.locals.set(statement.name.text, statement);
182
+ if (hasExportModifier(statement)) {
183
+ index.declarations.set(statement.name.text, statement);
184
+ }
155
185
  }
156
186
  }
157
187
 
@@ -171,6 +201,14 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
171
201
  const declaration = index.declarations.get(exportName);
172
202
  if (declaration) return { file, exportName, declaration };
173
203
 
204
+ const localName = index.localExports.get(exportName);
205
+ const localDeclaration = localName
206
+ ? index.locals.get(localName)
207
+ : undefined;
208
+ if (localDeclaration) {
209
+ return { file, exportName, declaration: localDeclaration };
210
+ }
211
+
174
212
  const reexport = index.reexports.get(exportName);
175
213
  if (reexport) {
176
214
  return (
@@ -192,7 +230,7 @@ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
192
230
  return {
193
231
  async resolveName(file, name) {
194
232
  const index = await readIndex(file);
195
- const local = index.declarations.get(name);
233
+ const local = index.locals.get(name);
196
234
  if (local) {
197
235
  return { file, exportName: name, declaration: local };
198
236
  }
@@ -267,20 +305,48 @@ export function exportedVariableDeclarations(
267
305
  declaration: ts.VariableDeclaration;
268
306
  }> = [];
269
307
 
308
+ const locals = new Map<string, ts.VariableDeclaration>();
309
+ const seen = new Set<string>();
310
+ const addDeclaration = (
311
+ exportName: string,
312
+ declaration: ts.VariableDeclaration,
313
+ ) => {
314
+ const key = `${exportName}:${declaration.pos}`;
315
+ if (seen.has(key)) return;
316
+ seen.add(key);
317
+ declarations.push({ exportName, declaration });
318
+ };
319
+
270
320
  for (const statement of sourceFile.statements) {
271
- if (!ts.isVariableStatement(statement) || !hasExportModifier(statement)) {
272
- continue;
273
- }
321
+ if (!ts.isVariableStatement(statement)) continue;
274
322
  for (const declaration of statement.declarationList.declarations) {
275
323
  if (ts.isIdentifier(declaration.name)) {
276
- declarations.push({
277
- exportName: declaration.name.text,
278
- declaration,
279
- });
324
+ locals.set(declaration.name.text, declaration);
325
+ if (hasExportModifier(statement)) {
326
+ addDeclaration(declaration.name.text, declaration);
327
+ }
280
328
  }
281
329
  }
282
330
  }
283
331
 
332
+ for (const statement of sourceFile.statements) {
333
+ if (
334
+ !ts.isExportDeclaration(statement) ||
335
+ statement.moduleSpecifier ||
336
+ statement.isTypeOnly ||
337
+ !statement.exportClause ||
338
+ !ts.isNamedExports(statement.exportClause)
339
+ ) {
340
+ continue;
341
+ }
342
+ for (const element of statement.exportClause.elements) {
343
+ if (element.isTypeOnly) continue;
344
+ const localName = element.propertyName?.text ?? element.name.text;
345
+ const declaration = locals.get(localName);
346
+ if (declaration) addDeclaration(element.name.text, declaration);
347
+ }
348
+ }
349
+
284
350
  return declarations;
285
351
  }
286
352
 
@@ -321,64 +387,166 @@ export async function localImportReferences(
321
387
  workspace: AnalysisWorkspace,
322
388
  file: string,
323
389
  ): Promise<LocalImportReference[]> {
324
- const sourceFile = await workspace.readSourceFile(file);
325
- const references: LocalImportReference[] = [];
326
-
327
- for (const statement of sourceFile.statements) {
328
- if (
329
- !ts.isImportDeclaration(statement) &&
330
- !ts.isExportDeclaration(statement)
331
- ) {
332
- continue;
333
- }
334
- if (!statement.moduleSpecifier) continue;
335
-
336
- const importPath = moduleText(statement.moduleSpecifier);
337
- const resolvedFile = importPath
338
- ? resolveLocalSourceFile(workspace, file, importPath)
339
- : undefined;
340
- if (
341
- !importPath ||
342
- (!resolvedFile && !isLocalModuleSpecifier(workspace, file, importPath))
343
- ) {
344
- continue;
345
- }
390
+ const references = await moduleReferences(workspace, file);
391
+
392
+ return references
393
+ .filter(
394
+ (reference) =>
395
+ reference.staticallyResolvable &&
396
+ (reference.resolvedFile ||
397
+ isLocalModuleSpecifier(workspace, file, reference.importPath)),
398
+ )
399
+ .map(
400
+ ({ syntax: _syntax, staticallyResolvable: _static, ...reference }) =>
401
+ reference,
402
+ );
403
+ }
346
404
 
405
+ export async function moduleReferences(
406
+ workspace: AnalysisWorkspace,
407
+ file: string,
408
+ ): Promise<ModuleReference[]> {
409
+ const sourceFile = await workspace.readSourceFile(file);
410
+ const references: ModuleReference[] = [];
411
+
412
+ const addReference = ({
413
+ importPath,
414
+ kind,
415
+ node,
416
+ staticallyResolvable,
417
+ syntax,
418
+ }: {
419
+ importPath: string;
420
+ kind: "type" | "value";
421
+ node: ts.Node;
422
+ staticallyResolvable: boolean;
423
+ syntax: ModuleReference["syntax"];
424
+ }) => {
347
425
  const position = sourceFile.getLineAndCharacterOfPosition(
348
- statement.getStart(sourceFile),
426
+ node.getStart(sourceFile),
349
427
  );
350
- const typeOnly = ts.isImportDeclaration(statement)
351
- ? Boolean(
352
- statement.importClause?.isTypeOnly ||
353
- (statement.importClause?.namedBindings &&
354
- ts.isNamedImports(statement.importClause.namedBindings) &&
355
- statement.importClause.namedBindings.elements.length > 0 &&
356
- statement.importClause.namedBindings.elements.every(
357
- (element) => element.isTypeOnly,
358
- )),
359
- )
360
- : statement.isTypeOnly ||
361
- (statement.exportClause &&
362
- ts.isNamedExports(statement.exportClause) &&
363
- statement.exportClause.elements.length > 0 &&
364
- statement.exportClause.elements.every(
365
- (element) => element.isTypeOnly,
366
- ));
367
428
  references.push({
368
429
  file,
369
430
  importPath,
370
- resolvedFile,
371
- kind: typeOnly ? "type" : "value",
431
+ ...(staticallyResolvable
432
+ ? { resolvedFile: resolveLocalSourceFile(workspace, file, importPath) }
433
+ : {}),
434
+ kind,
372
435
  line: position.line + 1,
373
436
  column: position.character + 1,
437
+ syntax,
438
+ staticallyResolvable,
374
439
  });
375
- }
440
+ };
441
+
442
+ const visit = (node: ts.Node) => {
443
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier) {
444
+ const importPath = moduleText(node.moduleSpecifier);
445
+ if (importPath) {
446
+ addReference({
447
+ importPath,
448
+ kind: importDeclarationKind(node),
449
+ node,
450
+ staticallyResolvable: true,
451
+ syntax: "import",
452
+ });
453
+ }
454
+ } else if (ts.isExportDeclaration(node) && node.moduleSpecifier) {
455
+ const importPath = moduleText(node.moduleSpecifier);
456
+ if (importPath) {
457
+ addReference({
458
+ importPath,
459
+ kind: exportDeclarationKind(node),
460
+ node,
461
+ staticallyResolvable: true,
462
+ syntax: "export",
463
+ });
464
+ }
465
+ } else if (
466
+ ts.isImportEqualsDeclaration(node) &&
467
+ ts.isExternalModuleReference(node.moduleReference) &&
468
+ node.moduleReference.expression
469
+ ) {
470
+ const importPath = moduleText(node.moduleReference.expression);
471
+ if (importPath) {
472
+ addReference({
473
+ importPath,
474
+ kind: node.isTypeOnly ? "type" : "value",
475
+ node,
476
+ staticallyResolvable: true,
477
+ syntax: "import-equals",
478
+ });
479
+ }
480
+ } else if (ts.isCallExpression(node)) {
481
+ const syntax =
482
+ node.expression.kind === ts.SyntaxKind.ImportKeyword
483
+ ? "dynamic-import"
484
+ : ts.isIdentifier(node.expression) &&
485
+ node.expression.text === "require"
486
+ ? "require"
487
+ : undefined;
488
+ if (syntax) {
489
+ const argument = node.arguments[0];
490
+ const importPath = moduleText(argument);
491
+ addReference({
492
+ importPath:
493
+ importPath ?? argument?.getText(sourceFile) ?? "<missing>",
494
+ kind: "value",
495
+ node,
496
+ staticallyResolvable: importPath !== undefined,
497
+ syntax,
498
+ });
499
+ }
500
+ }
501
+
502
+ ts.forEachChild(node, visit);
503
+ };
504
+
505
+ visit(sourceFile);
376
506
 
377
507
  return references;
378
508
  }
379
509
 
380
- function moduleText(node: ts.Expression): string | undefined {
381
- return ts.isStringLiteral(node) ? node.text : undefined;
510
+ function importDeclarationKind(
511
+ declaration: ts.ImportDeclaration,
512
+ ): "type" | "value" {
513
+ const clause = declaration.importClause;
514
+ if (!clause) return "value";
515
+ if (clause.isTypeOnly) return "type";
516
+ if (clause.name) return "value";
517
+
518
+ const bindings = clause.namedBindings;
519
+ if (
520
+ bindings &&
521
+ ts.isNamedImports(bindings) &&
522
+ bindings.elements.length > 0 &&
523
+ bindings.elements.every((element) => element.isTypeOnly)
524
+ ) {
525
+ return "type";
526
+ }
527
+ return "value";
528
+ }
529
+
530
+ function exportDeclarationKind(
531
+ declaration: ts.ExportDeclaration,
532
+ ): "type" | "value" {
533
+ if (declaration.isTypeOnly) return "type";
534
+ if (
535
+ declaration.exportClause &&
536
+ ts.isNamedExports(declaration.exportClause) &&
537
+ declaration.exportClause.elements.length > 0 &&
538
+ declaration.exportClause.elements.every((element) => element.isTypeOnly)
539
+ ) {
540
+ return "type";
541
+ }
542
+ return "value";
543
+ }
544
+
545
+ function moduleText(node: ts.Expression | undefined): string | undefined {
546
+ return node &&
547
+ (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
548
+ ? node.text
549
+ : undefined;
382
550
  }
383
551
 
384
552
  function hasExportModifier(node: ts.Node): boolean {
@@ -2,9 +2,11 @@ import { readdir, readFile, stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import ts from "typescript";
4
4
  import {
5
+ type BeignetConfig,
5
6
  loadBeignetConfig,
6
7
  normalizePath,
7
8
  type ResolvedBeignetConfig,
9
+ resolveConfig,
8
10
  } from "../config.js";
9
11
 
10
12
  const ignoredDirectoryNames = new Set([
@@ -22,19 +24,36 @@ export type AnalysisWorkspace = {
22
24
  fileSet: Set<string>;
23
25
  config: ResolvedBeignetConfig;
24
26
  compilerOptions: ts.CompilerOptions;
27
+ compilerConfigFiles: string[];
28
+ moduleResolutionCache: ts.ModuleResolutionCache;
25
29
  readSource(file: string): Promise<string>;
26
30
  readSourceFile(file: string): Promise<ts.SourceFile>;
27
31
  };
28
32
 
33
+ type AnalysisWorkspaceOptions = {
34
+ config?: BeignetConfig;
35
+ };
36
+
29
37
  export async function createAnalysisWorkspace(
30
38
  cwd = process.cwd(),
39
+ options: AnalysisWorkspaceOptions = {},
31
40
  ): Promise<AnalysisWorkspace> {
32
41
  const targetDir = path.resolve(cwd);
33
42
  await assertDirectory(targetDir);
34
43
  const files = await listProjectFiles(targetDir);
35
44
  const fileSet = new Set(files);
36
- const config = await loadBeignetConfig(targetDir, files);
37
- const compilerOptions = resolveCompilerOptions(targetDir);
45
+ const config = options.config
46
+ ? resolveConfig(options.config)
47
+ : await loadBeignetConfig(targetDir, files);
48
+ const { compilerConfigFiles, compilerOptions } =
49
+ resolveCompilerConfiguration(targetDir);
50
+ const moduleResolutionCache = ts.createModuleResolutionCache(
51
+ targetDir,
52
+ ts.sys.useCaseSensitiveFileNames
53
+ ? (fileName) => fileName
54
+ : (fileName) => fileName.toLowerCase(),
55
+ compilerOptions,
56
+ );
38
57
  const sources = new Map<string, string>();
39
58
  const sourceFiles = new Map<string, ts.SourceFile>();
40
59
 
@@ -53,6 +72,8 @@ export async function createAnalysisWorkspace(
53
72
  fileSet,
54
73
  config,
55
74
  compilerOptions,
75
+ compilerConfigFiles,
76
+ moduleResolutionCache,
56
77
  readSource,
57
78
  async readSourceFile(file) {
58
79
  const cached = sourceFiles.get(file);
@@ -111,7 +132,7 @@ export async function listProjectFiles(targetDir: string): Promise<string[]> {
111
132
  export function resolveLocalSourceFile(
112
133
  workspace: Pick<
113
134
  AnalysisWorkspace,
114
- "compilerOptions" | "fileSet" | "targetDir"
135
+ "compilerOptions" | "fileSet" | "moduleResolutionCache" | "targetDir"
115
136
  >,
116
137
  importerFile: string,
117
138
  importPath: string,
@@ -126,10 +147,62 @@ export function resolveLocalSourceFile(
126
147
  return workspace.fileSet.has(file) ? file : undefined;
127
148
  }
128
149
 
150
+ export function resolveLocalModulePath(
151
+ workspace: Pick<
152
+ AnalysisWorkspace,
153
+ "compilerOptions" | "fileSet" | "moduleResolutionCache" | "targetDir"
154
+ >,
155
+ importerFile: string,
156
+ importPath: string,
157
+ ): string | undefined {
158
+ const resolvedFile = resolveLocalSourceFile(
159
+ workspace,
160
+ importerFile,
161
+ importPath,
162
+ );
163
+ if (resolvedFile) return resolvedFile;
164
+
165
+ if (!isLocalModuleSpecifier(workspace, importerFile, importPath)) {
166
+ return undefined;
167
+ }
168
+
169
+ if (importPath.startsWith(".")) {
170
+ return normalizePath(path.join(path.dirname(importerFile), importPath));
171
+ }
172
+
173
+ const baseUrl = compilerPathsBase(
174
+ workspace.compilerOptions,
175
+ workspace.targetDir,
176
+ );
177
+ for (const [pattern, targets] of Object.entries(
178
+ workspace.compilerOptions.paths ?? {},
179
+ )) {
180
+ if (pattern === "*") continue;
181
+ const wildcardValue = modulePatternWildcard(pattern, importPath);
182
+ if (wildcardValue === undefined) continue;
183
+
184
+ for (const target of targets) {
185
+ const substituted = target.replace("*", wildcardValue);
186
+ const absolute = path.resolve(baseUrl, substituted);
187
+ const relative = normalizePath(
188
+ path.relative(workspace.targetDir, absolute),
189
+ );
190
+ if (relative !== ".." && !relative.startsWith("../")) return relative;
191
+ }
192
+ }
193
+
194
+ const relative = normalizePath(
195
+ path.relative(workspace.targetDir, path.resolve(baseUrl, importPath)),
196
+ );
197
+ return relative !== ".." && !relative.startsWith("../")
198
+ ? relative
199
+ : undefined;
200
+ }
201
+
129
202
  export function isLocalModuleSpecifier(
130
203
  workspace: Pick<
131
204
  AnalysisWorkspace,
132
- "compilerOptions" | "fileSet" | "targetDir"
205
+ "compilerOptions" | "fileSet" | "moduleResolutionCache" | "targetDir"
133
206
  >,
134
207
  importerFile: string,
135
208
  importPath: string,
@@ -180,7 +253,10 @@ export function isLocalModuleSpecifier(
180
253
  }
181
254
 
182
255
  function resolveModule(
183
- workspace: Pick<AnalysisWorkspace, "compilerOptions" | "targetDir">,
256
+ workspace: Pick<
257
+ AnalysisWorkspace,
258
+ "compilerOptions" | "moduleResolutionCache" | "targetDir"
259
+ >,
184
260
  importerFile: string,
185
261
  importPath: string,
186
262
  ): ts.ResolvedModuleFull | undefined {
@@ -189,6 +265,7 @@ function resolveModule(
189
265
  path.join(workspace.targetDir, importerFile),
190
266
  workspace.compilerOptions,
191
267
  ts.sys,
268
+ workspace.moduleResolutionCache,
192
269
  ).resolvedModule;
193
270
  }
194
271
 
@@ -201,16 +278,64 @@ function modulePatternMatches(pattern: string, importPath: string): boolean {
201
278
  );
202
279
  }
203
280
 
204
- function resolveCompilerOptions(targetDir: string): ts.CompilerOptions {
205
- return (
281
+ function modulePatternWildcard(
282
+ pattern: string,
283
+ importPath: string,
284
+ ): string | undefined {
285
+ const wildcard = pattern.indexOf("*");
286
+ if (wildcard === -1) return pattern === importPath ? "" : undefined;
287
+
288
+ const prefix = pattern.slice(0, wildcard);
289
+ const suffix = pattern.slice(wildcard + 1);
290
+ if (!importPath.startsWith(prefix) || !importPath.endsWith(suffix)) {
291
+ return undefined;
292
+ }
293
+ return importPath.slice(prefix.length, importPath.length - suffix.length);
294
+ }
295
+
296
+ function resolveCompilerConfiguration(targetDir: string): {
297
+ compilerConfigFiles: string[];
298
+ compilerOptions: ts.CompilerOptions;
299
+ } {
300
+ const compilerConfigFiles = new Set<string>();
301
+ const configPath = path.join(targetDir, "tsconfig.json");
302
+ const compilerOptions =
206
303
  ts.getParsedCommandLineOfConfigFile(
207
- path.join(targetDir, "tsconfig.json"),
304
+ configPath,
208
305
  {},
209
306
  {
210
307
  ...ts.sys,
308
+ readFile(file) {
309
+ compilerConfigFiles.add(path.resolve(file));
310
+ return ts.sys.readFile(file);
311
+ },
211
312
  onUnRecoverableConfigFileDiagnostic: () => {},
212
313
  },
213
- )?.options ?? {}
314
+ )?.options ?? {};
315
+
316
+ return {
317
+ compilerConfigFiles: [...compilerConfigFiles].sort(),
318
+ compilerOptions: compilerOptions.paths
319
+ ? compilerOptions
320
+ : {
321
+ ...compilerOptions,
322
+ baseUrl: compilerOptions.baseUrl ?? targetDir,
323
+ paths: {
324
+ "@/*": ["*"],
325
+ },
326
+ },
327
+ };
328
+ }
329
+
330
+ function compilerPathsBase(
331
+ compilerOptions: ts.CompilerOptions,
332
+ targetDir: string,
333
+ ): string {
334
+ const optionsWithPathBase = compilerOptions as ts.CompilerOptions & {
335
+ pathsBasePath?: string;
336
+ };
337
+ return (
338
+ compilerOptions.baseUrl ?? optionsWithPathBase.pathsBasePath ?? targetDir
214
339
  );
215
340
  }
216
341