@component-compass/cli 0.0.5 → 0.1.1

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 (41) hide show
  1. package/dist/cli.js +29 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/db-import.d.ts +12 -0
  4. package/dist/commands/db-import.js +54 -0
  5. package/dist/commands/db-import.js.map +1 -0
  6. package/dist/commands/scan.d.ts +2 -0
  7. package/dist/commands/scan.js +250 -17
  8. package/dist/commands/scan.js.map +1 -1
  9. package/dist/config/loader.js +2 -0
  10. package/dist/config/loader.js.map +1 -1
  11. package/dist/config/schema.d.ts +1 -0
  12. package/dist/config/schema.js.map +1 -1
  13. package/dist/manifest/lazy-resolver.d.ts +56 -0
  14. package/dist/manifest/lazy-resolver.js +102 -0
  15. package/dist/manifest/lazy-resolver.js.map +1 -1
  16. package/dist/occurrences.d.ts +1 -1
  17. package/dist/occurrences.js +3 -4
  18. package/dist/occurrences.js.map +1 -1
  19. package/dist/rollup.js.map +1 -1
  20. package/dist/scan/wrap-aggregation.d.ts +7 -0
  21. package/dist/scan/wrap-aggregation.js +94 -0
  22. package/dist/scan/wrap-aggregation.js.map +1 -0
  23. package/dist/seeds.js +13 -0
  24. package/dist/seeds.js.map +1 -1
  25. package/dist/types.d.ts +1 -0
  26. package/dist/upload.d.ts +26 -0
  27. package/dist/upload.js +47 -0
  28. package/dist/upload.js.map +1 -0
  29. package/dist/walker/files.js +7 -1
  30. package/dist/walker/files.js.map +1 -1
  31. package/dist/walker/resolve-import.d.ts +6 -1
  32. package/dist/walker/resolve-import.js +100 -41
  33. package/dist/walker/resolve-import.js.map +1 -1
  34. package/dist/walker/tsconfig-discovery.d.ts +34 -0
  35. package/dist/walker/tsconfig-discovery.js +50 -0
  36. package/dist/walker/tsconfig-discovery.js.map +1 -0
  37. package/dist/walker/tsconfig-loader.d.ts +35 -0
  38. package/dist/walker/tsconfig-loader.js +138 -0
  39. package/dist/walker/tsconfig-loader.js.map +1 -0
  40. package/package.json +29 -27
  41. package/schema/config.schema.json +1 -0
package/dist/upload.js ADDED
@@ -0,0 +1,47 @@
1
+ import { gzipSync } from "node:zlib";
2
+ export class UploadError extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.code = code;
7
+ }
8
+ }
9
+ export function resolveUploadTarget(opts) {
10
+ // biome-ignore lint/complexity/useLiteralKeys: env access
11
+ const host = opts.flagHost ?? process.env["CC_HOST"] ?? opts.configHost;
12
+ if (!host) {
13
+ throw new Error("No upload host configured. Pass --host, set CC_HOST, or add `host` to component-compass.config.json.");
14
+ }
15
+ // biome-ignore lint/complexity/useLiteralKeys: env access
16
+ const token = process.env["CC_TOKEN"];
17
+ if (!token) {
18
+ throw new Error("CC_TOKEN env var is required for upload.");
19
+ }
20
+ return { host, token };
21
+ }
22
+ const GZIP_THRESHOLD = 1024 * 1024; // 1 MB
23
+ export async function uploadArtifact(opts) {
24
+ const url = `${opts.host.replace(/\/$/, "")}/api/scans`;
25
+ const useGzip = opts.artifactJson.length > GZIP_THRESHOLD;
26
+ const body = useGzip
27
+ ? gzipSync(Buffer.from(opts.artifactJson, "utf8"))
28
+ : opts.artifactJson;
29
+ const headers = {
30
+ "Content-Type": "application/json",
31
+ Authorization: `Bearer ${opts.token}`,
32
+ };
33
+ if (useGzip)
34
+ headers["Content-Encoding"] = "gzip";
35
+ const res = await fetch(url, { method: "POST", headers, body });
36
+ if (res.status === 201 || res.status === 200) {
37
+ const json = (await res.json());
38
+ return {
39
+ status: res.status === 201 ? "inserted" : "exists",
40
+ scanId: json.scanId,
41
+ url: json.url,
42
+ };
43
+ }
44
+ const text = await res.text().catch(() => "");
45
+ throw new UploadError(res.status, `Upload failed (${res.status}): ${text || res.statusText}`);
46
+ }
47
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAQrC,MAAM,OAAO,WAAY,SAAQ,KAAK;IAE3B;IADT,YACS,IAAY,EACnB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,SAAI,GAAJ,IAAI,CAAQ;IAIrB,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,IAGnC;IACC,0DAA0D;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC;IACxE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;IACJ,CAAC;IACD,0DAA0D;IAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAE3C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAIpC;IACC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,cAAc,CAAC;IAC1D,MAAM,IAAI,GAAoB,OAAO;QACnC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IACtB,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;KACtC,CAAC;IACF,IAAI,OAAO;QAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC;IAElD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoC,CAAC;QACnE,OAAO;YACL,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ;YAClD,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9C,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,GAAG,CAAC,MAAM,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;AAChG,CAAC"}
@@ -5,7 +5,7 @@ export async function walkFiles(opts) {
5
5
  // layer; the explicit knob exists so users can opt out for WIP scans of
6
6
  // untracked work. `suppressErrors` silences EACCES on unreadable dirs
7
7
  // (vendored fixtures, transient OS turds) — the walker keeps going.
8
- return globby(opts.include, {
8
+ const files = await globby(opts.include, {
9
9
  cwd: opts.root,
10
10
  ignore: opts.exclude,
11
11
  gitignore: opts.gitignore,
@@ -14,5 +14,11 @@ export async function walkFiles(opts) {
14
14
  onlyFiles: true,
15
15
  suppressErrors: true,
16
16
  });
17
+ // globby walks directories concurrently; the returned order depends on which
18
+ // dir-listing task resolves first and varies run-to-run. File order is the
19
+ // sole seed for every downstream order in the pipeline (graph builder, engine
20
+ // resolve, occurrence array, seed Map insertion), so a stable sort here is
21
+ // load-bearing for artefact determinism.
22
+ return files.sort();
17
23
  }
18
24
  //# sourceMappingURL=files.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/walker/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAShC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAiB;IAC/C,yEAAyE;IACzE,2EAA2E;IAC3E,wEAAwE;IACxE,sEAAsE;IACtE,oEAAoE;IACpE,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;QAC1B,GAAG,EAAE,IAAI,CAAC,IAAI;QACd,MAAM,EAAE,IAAI,CAAC,OAAO;QACpB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,GAAG,EAAE,KAAK;QACV,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,IAAI;QACf,cAAc,EAAE,IAAI;KACrB,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/walker/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAShC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAiB;IAC/C,yEAAyE;IACzE,2EAA2E;IAC3E,wEAAwE;IACxE,sEAAsE;IACtE,oEAAoE;IACpE,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;QACvC,GAAG,EAAE,IAAI,CAAC,IAAI;QACd,MAAM,EAAE,IAAI,CAAC,OAAO;QACpB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,GAAG,EAAE,KAAK;QACV,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,IAAI;QACf,cAAc,EAAE,IAAI;KACrB,CAAC,CAAC;IACH,6EAA6E;IAC7E,2EAA2E;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E,yCAAyC;IACzC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC"}
@@ -1,9 +1,14 @@
1
+ import type { ResolveImport } from "@component-compass/plugin-core";
2
+ export type { ResolveImport } from "@component-compass/plugin-core";
1
3
  export type CreateImportResolverOptions = {
2
4
  repoRoot: string;
3
5
  /** Map of alias pattern → array of target patterns, both using `*` as the wildcard. */
4
6
  aliases?: Record<string, string[]>;
5
7
  /** Absolute path to a tsconfig.json whose `compilerOptions.paths` should layer in. */
6
8
  tsconfigPath?: string;
9
+ /** Called once per non-fatal warning produced while loading the tsconfig chain
10
+ * (unreadable file, parse error, unresolvable extends, cycle). Invoked at
11
+ * resolver construction time, not per resolution call. */
12
+ onWarning?: (message: string) => void;
7
13
  };
8
- export type ResolveImport = (from: string, spec: string) => string | null;
9
14
  export declare function createImportResolver(opts: CreateImportResolverOptions): ResolveImport;
@@ -1,6 +1,7 @@
1
- import { readFileSync, statSync } from "node:fs";
1
+ import { statSync } from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import { dirname, isAbsolute, resolve as pathResolve } from "node:path";
4
+ import { loadTsconfigChain, compileAliasPattern } from "./tsconfig-loader.js";
4
5
  /**
5
6
  * Module specifier resolver layered on top of Node's module resolution.
6
7
  *
@@ -12,20 +13,38 @@ import { dirname, isAbsolute, resolve as pathResolve } from "node:path";
12
13
  * Returns the absolute path of the resolved file, or `null` if no layer
13
14
  * matches. See spec §3.5.
14
15
  *
15
- * v1 limitations:
16
- * - Tsconfig `extends` chains are NOT followed; only the named file's own
17
- * `compilerOptions.paths` and `baseUrl` are consulted.
18
- * - No caching of resolved specifiers (workload is small for v1 scans).
16
+ * Resolver instances memoise results by `${dirname(from)}::${spec}`.
17
+ * Each uncached call does up to ~25 statSync probes plus require.resolve;
18
+ * the walker phase hits the same specifier once per file, and the
19
+ * reference-graph engine (via graph.moduleResolver) re-queries the same
20
+ * pairs during helper-callers and reference resolution. The key uses
21
+ * `dirname(from)` because resolution output depends only on the caller's
22
+ * directory: alias/tsconfig targets are resolved against fixed bases, and
23
+ * require.resolve's `paths` argument is set from `fromDir` alone.
19
24
  */
20
25
  const FILE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".vue"];
21
- const REGEX_META = /[.+?^${}()|[\]\\]/g;
22
26
  export function createImportResolver(opts) {
23
27
  const configEntries = compileAliasMap(opts.aliases, opts.repoRoot);
24
- const tsconfigEntries = loadTsconfigEntries(opts.tsconfigPath);
28
+ const tsconfigEntries = loadTsconfigEntries(opts.tsconfigPath, opts.onWarning);
25
29
  // Reused for `require.resolve` fallback. Anchoring at <repoRoot>/_ ensures
26
30
  // Node walks up through <repoRoot>/node_modules and parents.
27
31
  const requireFromRepo = createRequire(pathResolve(opts.repoRoot, "_"));
32
+ const cache = new Map();
28
33
  return function resolveImport(from, spec) {
34
+ // Normalise `from` to absolute. The engine passes repo-relative paths
35
+ // (e.g. "src/App.tsx") for in-graph emission; `require.resolve`'s `paths`
36
+ // array requires absolute directories.
37
+ const absFrom = isAbsolute(from) ? from : pathResolve(opts.repoRoot, from);
38
+ const fromDir = dirname(absFrom);
39
+ const cacheKey = `${fromDir}\0${spec}`;
40
+ const cached = cache.get(cacheKey);
41
+ if (cached !== undefined)
42
+ return cached;
43
+ const result = resolveUncached(fromDir, spec);
44
+ cache.set(cacheKey, result);
45
+ return result;
46
+ };
47
+ function resolveUncached(fromDir, spec) {
29
48
  for (const layers of [configEntries, tsconfigEntries]) {
30
49
  for (const entry of layers) {
31
50
  const m = entry.pattern.exec(spec);
@@ -40,57 +59,84 @@ export function createImportResolver(opts) {
40
59
  }
41
60
  }
42
61
  }
62
+ // Plain relative/bare resolution via Node. require.resolve doesn't try
63
+ // TS/JSX/Vue extensions by default, so probe them on first failure —
64
+ // mirrors the alias-branch tryFileWithExtensions behaviour and preserves
65
+ // the extensionless-relative-TS-import contract that classifyImportOrigin
66
+ // used to enforce via its own (now-deleted) tryResolve.
43
67
  try {
44
- return requireFromRepo.resolve(spec, { paths: [dirname(from), opts.repoRoot] });
68
+ return requireFromRepo.resolve(spec, { paths: [fromDir, opts.repoRoot] });
45
69
  }
46
70
  catch {
71
+ for (const ext of FILE_EXTENSIONS) {
72
+ try {
73
+ return requireFromRepo.resolve(`${spec}${ext}`, { paths: [fromDir, opts.repoRoot] });
74
+ }
75
+ catch {
76
+ // continue
77
+ }
78
+ }
79
+ // Directory-with-index fallback: Node only auto-resolves `index.js`/`.json`/`.node`.
80
+ // Consumer codebases use `index.jsx`/`.tsx`/`.vue`/`.ts` too; probe them explicitly so
81
+ // `import "./modal"` resolves when `modal/index.jsx` exists. Without this, the structural
82
+ // lazy-import detector (and any classifyImportOrigin caller) reports unresolved on a
83
+ // target that does exist on disk.
84
+ for (const ext of FILE_EXTENSIONS) {
85
+ try {
86
+ return requireFromRepo.resolve(`${spec}/index${ext}`, { paths: [fromDir, opts.repoRoot] });
87
+ }
88
+ catch {
89
+ // continue
90
+ }
91
+ }
92
+ // Folder-with-basename (#91): `./avatar` → `./avatar/avatar.{ext}`,
93
+ // the Next.js convention. Gated on relative specs because bare-package
94
+ // imports have a `main` field and don't use this shape.
95
+ if (spec.startsWith(".")) {
96
+ const segments = spec.split("/");
97
+ const basename = segments[segments.length - 1];
98
+ if (basename) {
99
+ for (const ext of FILE_EXTENSIONS) {
100
+ try {
101
+ return requireFromRepo.resolve(`${spec}/${basename}${ext}`, {
102
+ paths: [fromDir, opts.repoRoot],
103
+ });
104
+ }
105
+ catch {
106
+ // continue
107
+ }
108
+ }
109
+ }
110
+ }
47
111
  return null;
48
112
  }
49
- };
113
+ }
50
114
  }
51
115
  function compileAliasMap(aliases, base) {
52
116
  if (!aliases)
53
117
  return [];
54
118
  const out = [];
55
119
  for (const [key, targets] of Object.entries(aliases)) {
56
- out.push({ pattern: compilePattern(key), targets, base });
120
+ out.push({ pattern: compileAliasPattern(key), targets, base });
57
121
  }
58
122
  return out;
59
123
  }
60
- function loadTsconfigEntries(tsconfigPath) {
124
+ function loadTsconfigEntries(tsconfigPath, onWarning) {
61
125
  if (!tsconfigPath)
62
126
  return [];
63
- let raw;
64
- try {
65
- raw = readFileSync(tsconfigPath, "utf8");
66
- }
67
- catch {
68
- return [];
69
- }
70
- let parsed;
71
- try {
72
- parsed = JSON.parse(raw);
73
- }
74
- catch {
75
- return [];
76
- }
77
- const co = parsed.compilerOptions ?? {};
78
- if (!co.paths)
79
- return [];
80
- const tsconfigDir = dirname(tsconfigPath);
81
- const base = co.baseUrl ? pathResolve(tsconfigDir, co.baseUrl) : tsconfigDir;
82
- const out = [];
83
- for (const [key, targets] of Object.entries(co.paths)) {
84
- out.push({ pattern: compilePattern(key), targets, base });
85
- }
86
- return out;
127
+ const { entries, warnings } = loadTsconfigChain(tsconfigPath);
128
+ if (onWarning)
129
+ for (const w of warnings)
130
+ onWarning(w);
131
+ return entries;
87
132
  }
88
- /** Compiles an alias pattern (literal text + `*` wildcard) into an anchored regex
89
- * with the wildcard captured for substitution into the target. */
90
- function compilePattern(pattern) {
91
- return new RegExp(`^${pattern.replace(REGEX_META, "\\$&").replace(/\*/g, "(.*)")}$`);
92
- }
93
- /** Tries the path as-is, with each extension, and as a directory with `index.{ext}`. */
133
+ /**
134
+ * Tries the path as-is, with each extension, as a directory with `index.{ext}`,
135
+ * and finally as a directory with `<basename>.{ext}` (the Next.js folder
136
+ * convention also used by some React Native projects: `./avatar` resolves to
137
+ * `./avatar/avatar.tsx`). The `isFile` guard rules out false positives — if
138
+ * the basename file doesn't exist, we return null (#91).
139
+ */
94
140
  function tryFileWithExtensions(absPath) {
95
141
  if (!isAbsolute(absPath))
96
142
  return null;
@@ -106,6 +152,19 @@ function tryFileWithExtensions(absPath) {
106
152
  if (isFile(indexPath))
107
153
  return indexPath;
108
154
  }
155
+ // Folder-with-basename: `./avatar` → `./avatar/avatar.{ext}`. Node's
156
+ // resolver doesn't try this; Next.js layers it on framework-side. Safe to
157
+ // probe unconditionally because the file-existence check rejects false
158
+ // positives.
159
+ const segments = absPath.split(/[\\/]/);
160
+ const basename = segments[segments.length - 1];
161
+ if (basename) {
162
+ for (const ext of FILE_EXTENSIONS) {
163
+ const basenamePath = pathResolve(absPath, `${basename}${ext}`);
164
+ if (isFile(basenamePath))
165
+ return basenamePath;
166
+ }
167
+ }
109
168
  return null;
110
169
  }
111
170
  function isFile(p) {
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-import.js","sourceRoot":"","sources":["../../src/walker/resolve-import.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolve-import.js","sourceRoot":"","sources":["../../src/walker/resolve-import.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ export type TsconfigDiscoveryInput = {
2
+ /** Directory containing component-compass.config.json. */
3
+ configDir: string;
4
+ /** Repo root (typically same as configDir, but can differ in nested layouts). */
5
+ repoRoot: string;
6
+ /**
7
+ * Explicit override from cc config. Absolute or relative to configDir.
8
+ * Honored unconditionally when present — caller's `createImportResolver`
9
+ * returns an empty tsconfig layer if the file is missing/broken, matching
10
+ * the same outcome a typo'd path produces.
11
+ */
12
+ tsconfigPath?: string;
13
+ };
14
+ /**
15
+ * Tsconfig discovery — single source of truth for where the resolver finds
16
+ * the consumer's tsconfig. Layered:
17
+ *
18
+ * 1. Explicit `tsconfigPath` from cc config → use it (absolute or
19
+ * configDir-relative).
20
+ * 2. `<configDir>/tsconfig.json` if it exists.
21
+ * 3. `<configDir>/tsconfig.base.json` if it exists.
22
+ * 4. `<repoRoot>/tsconfig.json` if different from configDir and it exists.
23
+ * 5. `<repoRoot>/tsconfig.base.json` if different from configDir and it exists.
24
+ * 6. null — no tsconfig; tsconfig `paths` layer of `createImportResolver`
25
+ * becomes empty.
26
+ *
27
+ * Discovery returns the leaf path; `loadTsconfigChain` (in tsconfig-loader)
28
+ * handles `extends` from there. Deliberately does NOT walk up from configDir
29
+ * or repoRoot.
30
+ *
31
+ * Per-package tsconfigs in monorepos (e.g. PIE Aperture's nextjs-app-v14/
32
+ * tsconfig.json) are out of scope — see spec R5.
33
+ */
34
+ export declare function resolveTsconfigPath(input: TsconfigDiscoveryInput): string | null;
@@ -0,0 +1,50 @@
1
+ import { existsSync } from "node:fs";
2
+ import { isAbsolute, resolve, join } from "node:path";
3
+ /**
4
+ * Tsconfig discovery — single source of truth for where the resolver finds
5
+ * the consumer's tsconfig. Layered:
6
+ *
7
+ * 1. Explicit `tsconfigPath` from cc config → use it (absolute or
8
+ * configDir-relative).
9
+ * 2. `<configDir>/tsconfig.json` if it exists.
10
+ * 3. `<configDir>/tsconfig.base.json` if it exists.
11
+ * 4. `<repoRoot>/tsconfig.json` if different from configDir and it exists.
12
+ * 5. `<repoRoot>/tsconfig.base.json` if different from configDir and it exists.
13
+ * 6. null — no tsconfig; tsconfig `paths` layer of `createImportResolver`
14
+ * becomes empty.
15
+ *
16
+ * Discovery returns the leaf path; `loadTsconfigChain` (in tsconfig-loader)
17
+ * handles `extends` from there. Deliberately does NOT walk up from configDir
18
+ * or repoRoot.
19
+ *
20
+ * Per-package tsconfigs in monorepos (e.g. PIE Aperture's nextjs-app-v14/
21
+ * tsconfig.json) are out of scope — see spec R5.
22
+ */
23
+ export function resolveTsconfigPath(input) {
24
+ if (input.tsconfigPath) {
25
+ return isAbsolute(input.tsconfigPath)
26
+ ? input.tsconfigPath
27
+ : resolve(input.configDir, input.tsconfigPath);
28
+ }
29
+ // Probe order at each location: tsconfig.json, then tsconfig.base.json.
30
+ // tsconfig.base.json is the de facto monorepo convention (Next.js, Nx,
31
+ // Turborepo defaults). When extends chains live in a per-app tsconfig.json
32
+ // that points to a root tsconfig.base.json, picking up the .base.json
33
+ // directly still resolves the path aliases — the loader treats whatever
34
+ // is discovered as the chain's leaf.
35
+ const candidates = ["tsconfig.json", "tsconfig.base.json"];
36
+ for (const candidate of candidates) {
37
+ const configDirCandidate = join(input.configDir, candidate);
38
+ if (existsSync(configDirCandidate))
39
+ return configDirCandidate;
40
+ }
41
+ if (input.repoRoot !== input.configDir) {
42
+ for (const candidate of candidates) {
43
+ const repoRootCandidate = join(input.repoRoot, candidate);
44
+ if (existsSync(repoRootCandidate))
45
+ return repoRootCandidate;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ //# sourceMappingURL=tsconfig-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsconfig-discovery.js","sourceRoot":"","sources":["../../src/walker/tsconfig-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgBtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAA6B;IAC/D,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACvB,OAAO,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC;YACnC,CAAC,CAAC,KAAK,CAAC,YAAY;YACpB,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IACnD,CAAC;IACD,wEAAwE;IACxE,uEAAuE;IACvE,2EAA2E;IAC3E,sEAAsE;IACtE,wEAAwE;IACxE,qCAAqC;IACrC,MAAM,UAAU,GAAG,CAAC,eAAe,EAAE,oBAAoB,CAAC,CAAC;IAC3D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC5D,IAAI,UAAU,CAAC,kBAAkB,CAAC;YAAE,OAAO,kBAAkB,CAAC;IAChE,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;QACvC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAC1D,IAAI,UAAU,CAAC,iBAAiB,CAAC;gBAAE,OAAO,iBAAiB,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,35 @@
1
+ /** Compiled alias record consumed by `createImportResolver`. */
2
+ export type AliasEntry = {
3
+ /** Anchored regex matching the specifier; capture group 1 is the wildcard expansion. */
4
+ pattern: RegExp;
5
+ /** Substitution targets in declaration order. `*` is replaced with the captured wildcard. */
6
+ targets: string[];
7
+ /** Absolute directory anchoring `targets` (the effective baseUrl of the file that declared `paths`). */
8
+ base: string;
9
+ };
10
+ export type LoadTsconfigChainResult = {
11
+ entries: AliasEntry[];
12
+ warnings: string[];
13
+ };
14
+ /**
15
+ * Load a tsconfig and its `extends` chain, returning a flat list of alias
16
+ * entries ready for `createImportResolver`. Pure: no side effects beyond fs
17
+ * reads. Non-fatal failures (missing files, JSONC errors, cycles) are
18
+ * appended to `warnings` rather than thrown — callers decide where to emit.
19
+ *
20
+ * Inheritance semantics match TypeScript:
21
+ * - `extends` is string OR array of strings; arrays apply in order, later
22
+ * overrides earlier.
23
+ * - `compilerOptions.paths`: REPLACE not merge. If the child defines
24
+ * `paths`, the child wins entirely; otherwise the parent's flows through.
25
+ * - `compilerOptions.baseUrl`: REPLACE. Anchored to the file that defines
26
+ * it.
27
+ * - Each `AliasEntry.base` pins to the effective baseUrl of the file that
28
+ * contributed its `paths` (so the resolver doesn't need to know which
29
+ * file produced which entry).
30
+ */
31
+ export declare function loadTsconfigChain(absPath: string): LoadTsconfigChainResult;
32
+ /** Compile a TS-paths-style alias pattern into an anchored RegExp. The single
33
+ * `*` wildcard becomes capture group 1, available for substitution into the
34
+ * target. Shared between tsconfig-derived and config-derived alias entries. */
35
+ export declare function compileAliasPattern(pattern: string): RegExp;
@@ -0,0 +1,138 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, isAbsolute, resolve as pathResolve } from "node:path";
4
+ import { parse as parseJsonc, printParseErrorCode } from "jsonc-parser";
5
+ const REGEX_META = /[.+?^${}()|[\]\\]/g;
6
+ /**
7
+ * Load a tsconfig and its `extends` chain, returning a flat list of alias
8
+ * entries ready for `createImportResolver`. Pure: no side effects beyond fs
9
+ * reads. Non-fatal failures (missing files, JSONC errors, cycles) are
10
+ * appended to `warnings` rather than thrown — callers decide where to emit.
11
+ *
12
+ * Inheritance semantics match TypeScript:
13
+ * - `extends` is string OR array of strings; arrays apply in order, later
14
+ * overrides earlier.
15
+ * - `compilerOptions.paths`: REPLACE not merge. If the child defines
16
+ * `paths`, the child wins entirely; otherwise the parent's flows through.
17
+ * - `compilerOptions.baseUrl`: REPLACE. Anchored to the file that defines
18
+ * it.
19
+ * - Each `AliasEntry.base` pins to the effective baseUrl of the file that
20
+ * contributed its `paths` (so the resolver doesn't need to know which
21
+ * file produced which entry).
22
+ */
23
+ export function loadTsconfigChain(absPath) {
24
+ const warnings = [];
25
+ const seen = new Set();
26
+ const merged = loadChainInner(absPath, seen, warnings);
27
+ if (!merged || !merged.paths)
28
+ return { entries: [], warnings };
29
+ const entries = [];
30
+ for (const [pattern, targets] of Object.entries(merged.paths)) {
31
+ entries.push({
32
+ pattern: compileAliasPattern(pattern),
33
+ targets,
34
+ base: merged.baseDir,
35
+ });
36
+ }
37
+ return { entries, warnings };
38
+ }
39
+ function loadChainInner(absPath, seen, warnings) {
40
+ if (seen.has(absPath)) {
41
+ warnings.push(`tsconfig extends cycle at ${absPath}; ignoring this hop`);
42
+ return null;
43
+ }
44
+ seen.add(absPath);
45
+ const parsed = readAndParse(absPath, warnings);
46
+ if (!parsed)
47
+ return null;
48
+ // Resolve parents first, then apply this file's overrides on top.
49
+ let inherited = null;
50
+ if (parsed.extends) {
51
+ const parents = Array.isArray(parsed.extends) ? parsed.extends : [parsed.extends];
52
+ for (const ext of parents) {
53
+ const parentAbs = resolveExtends(absPath, ext, warnings);
54
+ if (!parentAbs)
55
+ continue;
56
+ const parentResult = loadChainInner(parentAbs, seen, warnings);
57
+ if (!parentResult)
58
+ continue;
59
+ inherited = mergeOverride(inherited, parentResult);
60
+ }
61
+ }
62
+ const ownBaseDir = effectiveBaseDir(absPath, parsed);
63
+ const own = {
64
+ paths: parsed.compilerOptions?.paths,
65
+ baseDir: ownBaseDir,
66
+ };
67
+ return mergeOverride(inherited, own);
68
+ }
69
+ function readAndParse(absPath, warnings) {
70
+ let raw;
71
+ try {
72
+ raw = readFileSync(absPath, "utf8");
73
+ }
74
+ catch (err) {
75
+ warnings.push(`tsconfig not readable at ${absPath}: ${err.message}`);
76
+ return null;
77
+ }
78
+ const errors = [];
79
+ const parsed = parseJsonc(raw, errors, {
80
+ allowTrailingComma: true,
81
+ disallowComments: false,
82
+ });
83
+ if (errors.length > 0) {
84
+ const messages = errors.map((e) => printParseErrorCode(e.error)).join(", ");
85
+ warnings.push(`tsconfig parse errors in ${absPath}: ${messages}`);
86
+ if (parsed === undefined)
87
+ return null;
88
+ }
89
+ if (parsed === null || typeof parsed !== "object") {
90
+ warnings.push(`tsconfig at ${absPath} did not produce an object`);
91
+ return null;
92
+ }
93
+ return parsed;
94
+ }
95
+ function resolveExtends(fromAbs, spec, warnings) {
96
+ // Relative paths resolve from the importing file's directory. Bare specs
97
+ // (e.g. "@tsconfig/node20/tsconfig.json") go through Node module resolution
98
+ // anchored at the same location.
99
+ const fromDir = dirname(fromAbs);
100
+ if (spec.startsWith(".") || isAbsolute(spec)) {
101
+ const candidate = isAbsolute(spec) ? spec : pathResolve(fromDir, spec);
102
+ // TS conventions: if the path ends without .json, try appending it.
103
+ const withExt = candidate.endsWith(".json") ? candidate : `${candidate}.json`;
104
+ return withExt;
105
+ }
106
+ try {
107
+ const req = createRequire(fromAbs);
108
+ return req.resolve(spec);
109
+ }
110
+ catch (err) {
111
+ warnings.push(`tsconfig extends "${spec}" from ${fromAbs} did not resolve`);
112
+ return null;
113
+ }
114
+ }
115
+ function effectiveBaseDir(absPath, parsed) {
116
+ const fileDir = dirname(absPath);
117
+ const baseUrl = parsed.compilerOptions?.baseUrl;
118
+ if (!baseUrl)
119
+ return fileDir;
120
+ return pathResolve(fileDir, baseUrl);
121
+ }
122
+ function mergeOverride(parent, child) {
123
+ // TS replace semantics: if child has paths, child wins entirely; otherwise
124
+ // parent's paths flow through. Each MergedPaths carries the baseDir tied to
125
+ // the *paths* it owns.
126
+ if (child.paths)
127
+ return child;
128
+ if (parent?.paths)
129
+ return parent;
130
+ return child;
131
+ }
132
+ /** Compile a TS-paths-style alias pattern into an anchored RegExp. The single
133
+ * `*` wildcard becomes capture group 1, available for substitution into the
134
+ * target. Shared between tsconfig-derived and config-derived alias entries. */
135
+ export function compileAliasPattern(pattern) {
136
+ return new RegExp(`^${pattern.replace(REGEX_META, "\\$&").replace(/\*/g, "(.*)")}$`);
137
+ }
138
+ //# sourceMappingURL=tsconfig-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsconfig-loader.js","sourceRoot":"","sources":["../../src/walker/tsconfig-loader.ts"],"names":[],"mappings":"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"}