Commit bab124b
committed
feat: added multiple tsconfig union target derivation
- TypeScript config resolution and validation (`ts.readConfigFile`, file readability checks)
- Canonical normalization pipeline:
- `path.resolve`
- `Set` + `.sort()` for deterministic dedupe ordering
- Multi-tsconfig union target derivation
- ESLint target glob normalization:
- extension-bearing pattern detection
- extensionless expansion with supported extension set
- Include/exclude/forceInclude merge behavior across multiple tsconfigs
- Cross-tsconfig overlap heuristics for ignore filtering
- Parser project alignment in ESLint runtime:
- `@typescript-eslint/parser` project list synchronized with runtime-derived tsconfig set
- Domain engine + ESLint domain detection alignment with runtime target derivation
- Jest regression testing with temp filesystem fixtures
- ESM/Jest mocking limitations (read-only exports / non-configurable properties)
Files and Code Sections:
- `src/config.ts` (modified)
- Why important: central canonical tsconfig list normalization for all downstream consumers.
- Key edits:
- added tsconfig readability validation (not just exists)
- dedupe + deterministic sort for tsconfigPaths and forceInclude
- fallback root tsconfig only if readable
- Key snippet:
```ts
function isReadableTsconfigPath(tsconfigPath: string): boolean {
let stats: fs.Stats;
try {
stats = fs.statSync(tsconfigPath);
} catch {
return false;
}
if (!stats.isFile()) {
return false;
}
const readResult = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
return readResult.error == null;
}
function sanitizeTsconfigPaths(rawValue: unknown, root: string): string[] {
return dedupeAndSort(
toStringArray(rawValue)
.map((tsconfigPath) => path.resolve(root, tsconfigPath))
.filter((tsconfigPath) => isReadableTsconfigPath(tsconfigPath)),
);
}
```
- `src/utils.ts` (modified heavily)
- Why important: target derivation moved from single-tsconfig to full union; include/exclude/forceInclude correctness; parser project runtime alignment.
- Key edits:
- `buildPatterns` now accepts all tsconfig paths (not first entry)
- include normalization preserves extension-bearing entries, expands only extensionless patterns
- tsconfig-relative include/exclude rebased to cwd
- cross-tsconfig exclude overlap handling + forceInclude ignore suppression
- deterministic dedupe/sort for files and ignores
- runtime `overrideConfig` injects parserOptions.project from same resolved config used for target derivation
- Key snippets:
```ts
function buildPatterns(
tsconfigPaths: readonly string[],
forceInclude: string[] = [],
cwd = process.cwd(),
forceIncludeBaseDir = cwd,
): { files: string[]; ignore: string[] } { ... }
```
```ts
function hasExtensionOrGlobExtensionPattern(value: string): boolean {
return /(^|\/)[^/]*\.[^/]*$/.test(value);
}
function expandExtensionlessPattern(value: string): string {
const normalized = normalizeGlobValue(value).replace(/\/+$/, '');
if (normalized.length === 0) return '';
if (hasExtensionOrGlobExtensionPattern(normalized)) return normalized;
if (!GLOB_META_PATTERN.test(normalized)) {
return `${normalized}/**/*${ESLINT_TARGET_EXTENSION_GLOB}`;
}
if (normalized === '**') return `**/*${ESLINT_TARGET_EXTENSION_GLOB}`;
if (normalized.endsWith('/**')) return `${normalized}/*${ESLINT_TARGET_EXTENSION_GLOB}`;
return `${normalized}${ESLINT_TARGET_EXTENSION_GLOB}`;
}
```
```ts
const lintConfig = resolvedConfig ?? resolveLintConfig();
const parserProjectOverride = {
languageOptions: {
parserOptions: {
project: lintConfig.domains.eslint.tsconfigPaths,
},
},
};
const eslint = new ESLint({
overrideConfigFile: configPath || defaultConfigPath,
...
overrideConfig: parserProjectOverride,
});
```
- `src/domains/eslint.ts` (modified)
- Why important: ESLint domain detection now uses canonical multi-tsconfig union logic (same derivation family as run path).
- Key edits:
- new `resolveESLintDetectionPatterns` using `resolveLintConfig` + `utils.buildPatterns(...)`
- default detection falls back to default roots only when no tsconfigs/files are derivable
- Key snippet:
```ts
function resolveESLintDetectionPatterns(
eslintPatterns: readonly string[] | undefined,
): string[] {
if (eslintPatterns != null && eslintPatterns.length > 0) {
return [...eslintPatterns];
}
const resolvedConfig = resolveLintConfig();
const { tsconfigPaths, forceInclude } = resolvedConfig.domains.eslint;
if (tsconfigPaths.length === 0) return DEFAULT_ESLINT_SEARCH_ROOTS;
const { files } = utils.buildPatterns(
tsconfigPaths,
forceInclude,
process.cwd(),
resolvedConfig.root,
);
if (files.length === 0) return DEFAULT_ESLINT_SEARCH_ROOTS;
return files;
}
```
- `src/configs/js.ts` (read, not modified)
- Why important: confirmed parser project already sourced from `resolveLintConfig().domains.eslint.tsconfigPaths`; alignment then enforced at runtime via `src/utils.ts`.
- `tests/config.test.ts` (modified)
- Why important: regression coverage for canonical normalization, deterministic sort, dedupe, missing/unreadable filtering.
- Key changes:
- switched from brittle mocking to real temp-file fixtures
- added/updated tests asserting dedupe/sort and unreadable tsconfig filtering
- Representative assertions:
```ts
expect(resolved.domains.eslint.tsconfigPaths).toStrictEqual([
path.resolve(repoRoot, 'workspace', 'packages/core', 'tsconfig.json'),
path.resolve(repoRoot, 'workspace', 'tsconfig.json'),
]);
```
- `tests/domains/index.test.ts` (modified)
- Why important: regression coverage for domain-level multi-tsconfig detection and buildPatterns behavior.
- Added tests:
- `eslint detection derives scope from canonical multi-tsconfig union`
- `preserves extension-bearing include entries while expanding extensionless entries`
- `exclude and forceInclude interactions are stable across multiple tsconfigs`
- Representative assertion:
```ts
expect(patterns.files).toContain('pkg-one/src/**/*.tsx');
expect(patterns.files).toContain(
'pkg-two/src/**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts,json}',
);
```
- `tests/bin/lint.test.ts` (modified)
- Why important: user-visible CLI stability behavior update.
- Changes:
- test for canonical lint script flags updated to `.resolves.toBeUndefined()` after alignment fix
- added `default run tolerates missing configured tsconfig paths`
- Representative snippet:
```ts
await expect(
main(['node', 'matrixai-lint', '--domain', 'eslint']),
).resolves.toBeUndefined();
```
- `plans/refactor/STATE.md` (modified)
- Why important: milestone state/documentation update required by user.
- Added section: `Implemented in full multi-tsconfig targeting alignment slice`, documenting guarantees and touched areas.1 parent e185408 commit bab124b
File tree
7 files changed
+666
-54
lines changed- plans/refactor
- src
- domains
- tests
- bin
- domains
7 files changed
+666
-54
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
8 | 33 | | |
9 | 34 | | |
10 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
32 | 53 | | |
33 | | - | |
34 | | - | |
35 | | - | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
36 | 59 | | |
37 | 60 | | |
38 | 61 | | |
39 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
40 | 67 | | |
41 | 68 | | |
42 | 69 | | |
| |||
63 | 90 | | |
64 | 91 | | |
65 | 92 | | |
66 | | - | |
| 93 | + | |
67 | 94 | | |
68 | 95 | | |
69 | 96 | | |
70 | 97 | | |
71 | 98 | | |
72 | 99 | | |
73 | 100 | | |
74 | | - | |
| 101 | + | |
75 | 102 | | |
76 | 103 | | |
77 | 104 | | |
78 | 105 | | |
| 106 | + | |
| 107 | + | |
79 | 108 | | |
80 | 109 | | |
81 | 110 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
23 | 51 | | |
24 | 52 | | |
25 | 53 | | |
26 | 54 | | |
27 | 55 | | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
| 56 | + | |
32 | 57 | | |
33 | 58 | | |
34 | 59 | | |
| |||
0 commit comments