How to Configure Monorepo Linting in TSSLint with Multiple tsconfig.json Files
TSSLint treats each tsconfig.json as an independent linting unit, allowing you to lint an entire monorepo by passing multiple --project flags with glob patterns that the CLI expands into parallel worker processes.
In a monorepo environment, managing linting across multiple packages with different TypeScript configurations can be complex. TSSLint simplifies this by treating every TypeScript project—defined by a tsconfig.json file—as a first-class linting boundary. This architecture allows the CLI to orchestrate multiple projects simultaneously while respecting per-project rule sets and language plugins.
Understanding TSSLint's Project-Based Architecture
TSSLint's CLI, located in packages/cli/index.ts, is designed around the concept that each tsconfig.json represents a distinct linting context. This design enables fine-grained control over how different parts of a monorepo are analyzed.
The tsconfig.json as a Linting Boundary
When you invoke the TSSLint CLI, it constructs a map called tsconfigAndLanguages (lines 57-71 in packages/cli/index.ts). This map associates each discovered tsconfig.json path with a list of language identifiers. The CLI then instantiates a Project class (lines 40-63) for each entry, which loads the companion tsslint.config.ts file using ts.findConfigFile (line 65).
Language Plugin Resolution
For projects using frameworks like Vue, MDX, or Astro, TSSLint dynamically loads language plugins through packages/cli/lib/languagePlugins.ts. The load function (lines 13-34) adds the appropriate language support based on the identifiers attached to each project during the initial flag parsing phase.
Configuring Multiple Projects in the CLI
The TSSLint CLI accepts multiple project paths through various flags, expanding glob patterns to discover configurations across your monorepo structure.
Using Glob Patterns with --project Flags
The CLI scans command-line arguments for project-related flags—including --project, --vue-project, --vue-vine-project, --mdx-project, --astro-project, and --ts-macro-project. For each flag, it glob-expands the following arguments using fs.globSync, allowing patterns like packages/*/tsconfig.json to resolve to multiple configuration files:
# Lint every package in a monorepo with a single command
npx tsslint --project packages/*/tsconfig.json
Framework-Specific Project Flags
When a flag includes a framework name, TSSLint automatically attaches the corresponding language identifier to that project. This ensures the correct parser is used without manual configuration:
# Lint TypeScript packages alongside a Vue application
npx tsslint \
--project packages/*/tsconfig.json \
--vue-project apps/web/tsconfig.json
You can also use brace expansion to combine multiple patterns in one argument:
npx tsslint \
--project {tsconfig.json,packages/*/tsconfig.json,extensions/*/tsconfig.json}
Per-Project Configuration with tsslint.config.ts
Each tsconfig.json can have its own tsslint.config.ts file located in the same directory. When the Project class initializes, it resolves this configuration file using TypeScript's ts.findConfigFile utility (line 65 in packages/cli/index.ts).
Here is an example of a project-specific configuration:
// packages/foo/tsslint.config.ts
import { defineConfig } from '@tsslint/config';
import { fromTSLRules } from '@tsslint/config';
import { core } from 'tsl';
export default defineConfig({
rules: fromTSLRules(core.all()), // project-specific rule set
});
This architecture allows different packages in your monorepo to enforce different rule sets, severity levels, or plugin configurations while still being linted in a single coordinated run.
Parallel Execution and Worker Architecture
After constructing the Project instances, TSSLint spawns lint workers to process files in parallel. The startWorker function (starting at line 27 in packages/cli/index.ts) creates worker threads that lint each file in every project while maintaining per-project caches.
This parallelization ensures that linting a monorepo with dozens of packages scales efficiently, as each project runs independently while the CLI aggregates diagnostics into a unified console report.
Summary
- TSSLint treats each
tsconfig.jsonas an independent linting unit, creating aProjectinstance for every configuration found. - The CLI accepts multiple
--projectflags with glob patterns (e.g.,packages/*/tsconfig.json), expanding them usingfs.globSyncto discover all projects in a monorepo. - Framework-specific flags (
--vue-project,--mdx-project, etc.) automatically attach the correct language identifiers and load appropriate plugins frompackages/cli/lib/languagePlugins.ts. - Each project resolves its own
tsslint.config.tsviats.findConfigFile, allowing per-package rule customization. - Lint workers run in parallel via
startWorker, aggregating results across all projects into a single report.
Frequently Asked Questions
How does TSSLint handle multiple tsconfig.json files in a single command?
TSSLint scans CLI arguments for --project and framework-specific flags, then uses fs.globSync to expand glob patterns like packages/*/tsconfig.json into individual project paths. It creates a Project instance for each discovered tsconfig.json, allowing you to lint an entire monorepo with one command.
Can different packages in a monorepo have different linting rules?
Yes. Each tsconfig.json directory can contain its own tsslint.config.ts file. When the CLI initializes a Project instance, it calls ts.findConfigFile (line 65 in packages/cli/index.ts) to locate and load the project-specific configuration, enabling different rules, severities, or plugins per package.
What is the difference between --project and --vue-project flags?
The --project flag treats the specified tsconfig.json as a standard TypeScript project, while --vue-project (and similar framework flags like --mdx-project, --astro-project) attaches a language identifier to the project. This identifier triggers the loading of framework-specific language plugins via packages/cli/lib/languagePlugins.ts, ensuring proper parsing of Vue SFCs or MDX files.
How does TSSLint optimize performance when linting large monorepos?
TSSLint uses a worker-based architecture where startWorker (starting at line 27 in packages/cli/index.ts) spawns parallel lint workers for each project. These workers process files concurrently while maintaining per-project caches, and the CLI aggregates diagnostics into a unified report. This parallelization ensures that adding more packages to a monorepo does not linearly increase linting time.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →