Monorepos & incremental TypeScript
Opt into YAML-based pnpm workspace discovery, include/exclusion globs, dependency/reference ordering and partitioned TypeScript state.
Workspace planning remains off by default. Signalint 1.2.1 improves discovery and graph handling without turning it into a complete package-manager or compiler model.
{ "monorepoMode": "auto", "engines": { "oxlint": true, "tsc": true, "biome": false, "eslint": false }}SIGNALINT_MONOREPO_MODE=off|auto|strict overrides the config; invalid nonempty values are errors.
| Mode | Runtime behavior |
|---|---|
off | Single-project path, no workspace plan |
auto | Attempt discovery/planning; missing, malformed or cyclic workspace input can fall back to root checking |
strict | Missing/malformed workspace YAML and a detected graph cycle are errors; other root/empty-target fallbacks can still occur |
YAML parsing and supported patterns
Section titled “YAML parsing and supported patterns”Discovery now uses the yaml parser, not the old small line parser. Inline/block lists, quotes, comments and YAML aliases are parsed before extracting the string packages entries.
packages: - 'packages/*' - 'apps/**' - '!apps/**/test/**' - '!packages/legacy'Common literal paths, *, ?, recursive **, and negative exclusions are supported. Matching exclusions apply to candidates and their ancestor directories. Directory walking skips common artifact/state directories such as node_modules, .git, dist, build, .signalint, .next, .turbo, and .cache. Candidates require a valid named package.json; canonical real paths deduplicate discovered directories.
This is not a full package-manager glob implementation:
- Braces and character classes are treated literally, not expanded.
- For recursive patterns the walker starts from the prefix before
**; a wildcard in that prefix, such aspackages/*/**, can prevent discovery. Prefer literal-prefix recursive forms such aspackages/**. - Exclusions are collected and applied after includes; do not assume ordered re-inclusion semantics.
- Only the pnpm workspace file is discovered here; npm/Yarn workspace fields are not an interchangeable discovery source.
- Workspace configuration is trusted input. Exact/parent-relative configured paths are not an OS sandbox; review the selected packages and filesystem.
Dependency and TypeScript-reference graph
Section titled “Dependency and TypeScript-reference graph”The graph combines manifest dependency relationships with TypeScript project references. Dependency sections include dependencies, devDependencies, peerDependencies and optionalDependencies.
Explicit workspace:, file: and link: protocols identify local sibling relationships. Plain versions and *, ^ or ~ ranges are also considered when a sibling name/version matches the runtime’s static heuristic. This reduces false edges from unrelated registry names, but does not implement full semver, registry or lockfile resolution. The compatible-major/minor heuristic can conservatively include a sibling whose version would not satisfy npm’s exact range rules.
Package and reference directories are canonicalized, including directory-vs-tsconfig.json references. Reference extraction still strips comments with a small routine followed by JSON.parse; trailing commas and arbitrary TypeScript config semantics are not guaranteed.
The graph orders dependencies before dependents. Scoped project checks can include transitive dependents. In auto, cycles fall back to the root; in strict, they throw with blocked package names. The reported cycle-node set is the unprocessed graph remainder, not necessarily a minimal cycle witness. Root-level configuration/lockfile changes or no matching packages can still select root fallback.
File-check/compiler scope is a separate boundary
Section titled “File-check/compiler scope is a separate boundary”A check_files result is filtered to the supplied files. Its first requested file selects the TypeScript hash/fingerprint target and workspace dependency directories, while the default compiler runner still uses the root . config. Do not assume a multi-package file list creates an independent compiler invocation per package. Compare project/package scope and returned engine status when coverage matters.
A recognized solution-root config with references can select native --build behavior. Planning metadata alone does not certify successful typechecking.
Build info and cache partitioning
Section titled “Build info and cache partitioning”Normal TypeScript project mode uses --noEmit, --incremental and an explicit build-info path:
.signalint/cache/tsc/<project-id>/tsc.tsbuildinfoThe root ID is root; nested IDs use a relative-path slug plus an eight-character hash. SIGNALINT_STATE_DIR redirects this project-mode storage to <state-dir>/cache/tsc/<project-id>/.
Build mode differs: native/configured .tsbuildinfo destinations are not redirected by that project-mode mechanism. Review ignore rules and trusted output settings.
The result cache is partitioned by project ID. Supported config/reference traversal, the first recognized root lockfile, a bounded size/mtime source walk and dependency directories contribute to fingerprints. This is not complete tracking of every compiler input or same-size/same-mtime edit. See caching.
Source: discovery.ts, graph.ts, plan.ts, checkFiles.ts, and tsc.ts.