diff --git a/README.md b/README.md index 47d7289..10a4bb4 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ matrixai-lint --fix | `--user-config` | Uses detected `eslint.config.[js,mjs,cjs,ts]` from the project root if found | | `--eslint-config ` | Explicitly use a custom ESLint config file | | `--eslint ` | ESLint targets (files, roots, or globs); implies ESLint domain selection | -| `--shell ` | Shell targets (files, roots, or globs) used to derive shell search roots | +| `--shell ` | Shell targets (files, roots, or globs); implies shell domain selection | | `--domain ` | Run only selected domains (`eslint`, `shell`, `markdown`) | | `--skip-domain ` | Skip selected domains (`eslint`, `shell`, `markdown`) | | `--list-domains` | Print available domains and short descriptions, then exit 0 | @@ -66,6 +66,32 @@ Domain selection behavior: - File paths and glob patterns are reduced to search roots, then `*.sh` files are discovered under those roots. +#### Targeted workflows + +- Only ESLint on a subset of files: + + ```sh + matrixai-lint --eslint "src/**/*.{ts,tsx}" --domain eslint + ``` + +- Only shell scripts under specific roots: + + ```sh + matrixai-lint --shell scripts packages/*/scripts + ``` + +- Markdown only: + + ```sh + matrixai-lint --domain markdown + ``` + +- Mixed scoped run (ESLint + shell only): + + ```sh + matrixai-lint --eslint "src/**/*.{ts,tsx}" --shell scripts + ``` + #### Examples ```sh @@ -98,7 +124,7 @@ export default config; ```js // eslint.config.js -import matrixai from '@matrixai/lint/configs/js.js'; +import matrixai from '@matrixai/lint/configs/eslint.js'; export default matrixai; ``` @@ -146,8 +172,8 @@ Supported imports: - `@matrixai/lint`: named export `config`; types `MatrixAILintCfg`, `RawMatrixCfg`, `CLIOptions`. -- `@matrixai/lint/configs/js.js`: default export of the ESLint Flat Config array - (same shape as `config`). +- `@matrixai/lint/configs/eslint.js`: default export of the ESLint Flat Config + array (same shape as `config`). - `@matrixai/lint/configs/prettier.config.js`: reusable Prettier options object. The exported `config` is intended as a composable base preset for downstream diff --git a/package-lock.json b/package-lock.json index a97af0c..4cf2ac2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@matrixai/lint", - "version": "0.2.12", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@matrixai/lint", - "version": "0.2.12", + "version": "0.3.0", "license": "Apache-2.0", "dependencies": { "@eslint/compat": "^1.2.5", diff --git a/package.json b/package.json index 589bd5e..492d85d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@matrixai/lint", - "version": "0.2.12", + "version": "0.3.0", "author": "Roger Qiu", "description": "Org wide custom eslint rules", "license": "Apache-2.0", @@ -38,7 +38,7 @@ "tsx": "tsx", "lint": "npm run prepare && node ./dist/bin/lint.js --domain eslint shell markdown --eslint \"{src,scripts,tests}/**/*.{js,mjs,ts,mts,jsx,tsx}\" --shell src scripts tests", "lintfix": "npm run prepare && node ./dist/bin/lint.js --fix --domain eslint shell markdown --eslint \"{src,scripts,tests}/**/*.{js,mjs,ts,mts,jsx,tsx}\" --shell src scripts tests", - "lint-shell": "find ./src ./tests ./scripts -type f -regextype posix-extended -regex '.*\\.(sh)' -exec shellcheck {} +", + "lint-shell": "npm run prepare && node ./dist/bin/lint.js --domain shell --shell src scripts tests", "docs": "shx rm -rf ./docs && typedoc --gitRevision master --tsconfig ./tsconfig.build.json --out ./docs src", "test": "node ./scripts/test.mjs" }, diff --git a/plans/refactor/PLAN.md b/plans/refactor/PLAN.md index 82f7aed..633f222 100644 --- a/plans/refactor/PLAN.md +++ b/plans/refactor/PLAN.md @@ -5,15 +5,15 @@ Refactor `matrixai-lint` into a domain-oriented lint engine that: - Performs smart auto-discovery (project relevance) independently from tool availability. -- Distinguishes built-in npm-based capabilities (for example ESLint/Prettier) from optional external tools (for example ShellCheck, nixfmt). +- Distinguishes built-in npm-based capabilities (for example ESLint/Prettier) from optional external tools (for example ShellCheck). - Supports explicit domain selection (include/exclude/only) with consistent target semantics. -- Provides clean extension points for adding new domains. +- Provides clear built-in domain boundaries and downstream ESLint extensibility via config composition. ## Non-goals - No behavior-changing implementation in this epic document (this is design/spec only). - No changes to the shipped ESLint ruleset content (rule tuning is out of scope). -- No attempt to fully replace ESLint/Prettier/ShellCheck/nixfmt; this project orchestrates them. +- No attempt to fully replace ESLint/Prettier/ShellCheck; this project orchestrates them. ## Constraints / invariants @@ -82,7 +82,6 @@ Examples of relevance signals: - ESLint (JS/TS): any `*.{js,mjs,cjs,jsx,ts,tsx,mts,cts,json}` in the target set. - Markdown/Prettier: any `*.{md,mdx}` in the target set (plus `README.md` by convention). - ShellCheck: any `*.sh` in the target set. -- Nixfmt: any `*.nix` in the target set. Relevance must be computed against the *effective target set* (CLI paths/globs + config defaults), not against the whole filesystem blindly. @@ -93,7 +92,7 @@ Relevance must be computed against the *effective target set* (CLI paths/globs + Availability is domain-specific: - **Built-in npm-based** domains (ESLint, Prettier) are expected to be available whenever `@matrixai/lint` is installed. -- **External optional** domains (ShellCheck, nixfmt) may not exist in downstream environments. +- **External optional** domains (ShellCheck) may not exist in downstream environments. ### Decision matrix @@ -109,17 +108,17 @@ This model explicitly distinguishes "capability exists" from "domain matters". --- -## Proposed domain/plugin architecture +## Proposed domain architecture ### High-level components - **Lint engine**: parses CLI/config, computes target set, selects domains, orchestrates execution, aggregates results. -- **Domain plugins**: encapsulate discovery, tool checks, execution, and reporting for one domain. +- **Domain modules**: encapsulate discovery, tool checks, execution, and reporting for one domain. - **Capability registry**: standardizes dependency checks (npm module resolution vs external binary lookup). -### Domain plugin interface (conceptual) +### Domain module interface (conceptual) -Each domain plugin should define: +Each domain module should define: - `id`: stable string identifier (for example `eslint`, `shellcheck`). - `discover(ctx)`: returns relevance result and default target patterns for this domain. @@ -144,9 +143,6 @@ The engine is responsible for: 3. **shellcheck** - External optional tool. - Lints `*.sh`. -4. **nixfmt** - - External optional tool. - - Formats/checks `*.nix`. ### Mermaid: selection and execution pipeline @@ -186,7 +182,7 @@ Dependencies are classified at evaluation time: ### Policy rules - **Auto-discovery must never fail the command** due solely to missing optional external tools. -- **Explicit domain selection must be strict**: if a user asks for `shellcheck` or `nixfmt`, and the tool is missing, exit non-zero. +- **Explicit domain selection must be strict**: if a user asks for `shellcheck`, and the tool is missing, exit non-zero. - Provide an actionable error message (what is missing, how the tool was detected, how to install). --- @@ -240,7 +236,7 @@ Nixfmt-specific: --- -## Config schema proposal (v2) + examples + extension points +## Config schema proposal (v2) + examples ### File discovery @@ -279,26 +275,16 @@ Continue to support `matrixai-lint-config.json`, but evolve it to a versioned sc "tool": { "command": "shellcheck" } - }, - "nixfmt": { - "enabled": "auto", - "targets": ["**/*.nix"], - "tool": { - "command": "nixfmt" - } } - }, - "plugins": [ - { "module": "./lint-domains/custom-domain.mjs" } - ] + } } ``` Notes: - `enabled` supports: `auto` | `on` | `off`. -- `plugins` is the extension point: additional domain modules can be loaded to register domains. - Domain `tool.command` allows a configurable executable name/path for external tools. +- Extensibility for downstream ESLint behavior is via ESLint Flat Config composition from the shipped preset (`@matrixai/lint/configs/eslint.js`) and CLI config selection flags such as `--eslint-config` and `--user-config`. ### Backward compatibility (v1) @@ -334,7 +320,6 @@ Define a stable default domain order for readability and compatibility, while al 1. `eslint` 2. `shellcheck` 3. `markdown-prettier` -4. `nixfmt` ### Exit codes @@ -366,15 +351,15 @@ If downstream requires a single non-zero code, document that any non-zero is fai Assume the project contains `src/**/*.ts`, `docs/**/*.md`, `scripts/foo.sh`, and `nix/shell.nix`. -| Invocation | Domain selection intent | Expected executed domains | If `shellcheck` missing | If `nixfmt` missing | -| --- | --- | --- | --- | --- | -| `matrixai-lint` | auto | eslint, markdown-prettier, shellcheck, nixfmt (if relevant) | shellcheck skipped with warning | nixfmt skipped with warning | -| `matrixai-lint --fix` | auto + fix | eslint (fix), markdown-prettier (write), shellcheck (check), nixfmt (format) | shellcheck skipped with warning | nixfmt skipped with warning | -| `matrixai-lint --domain eslint` | explicit | eslint only | n/a | n/a | -| `matrixai-lint --domain shellcheck` | explicit | shellcheck only | fail (missing tool) | n/a | -| `matrixai-lint --skip-domain shellcheck` | auto minus one | eslint, markdown-prettier, nixfmt | n/a | nixfmt skipped with warning | -| `matrixai-lint docs` | auto scoped | markdown-prettier (and maybe eslint if JS files under docs) | likely not relevant | likely not relevant | -| `matrixai-lint --eslint "src/**/*.ts"` | implicit eslint targeting | eslint (explicit targets) + other auto domains unless skipped | shellcheck still auto (unless targets exclude it) | nixfmt still auto (unless targets exclude it) | +| Invocation | Domain selection intent | Expected executed domains | If `shellcheck` missing | +| --- | --- | --- | --- | +| `matrixai-lint` | auto | eslint, markdown-prettier, shellcheck (if relevant) | shellcheck skipped with warning | +| `matrixai-lint --fix` | auto + fix | eslint (fix), markdown-prettier (write), shellcheck (check) | shellcheck skipped with warning | +| `matrixai-lint --domain eslint` | explicit | eslint only | n/a | +| `matrixai-lint --domain shellcheck` | explicit | shellcheck only | fail (missing tool) | +| `matrixai-lint --skip-domain shellcheck` | auto minus one | eslint, markdown-prettier | n/a | +| `matrixai-lint docs` | auto scoped | markdown-prettier (and maybe eslint if JS files under docs) | likely not relevant | +| `matrixai-lint --eslint "src/**/*.ts"` | implicit eslint targeting | eslint (explicit targets) + other auto domains unless skipped | shellcheck still auto (unless targets exclude it) | | `matrixai-lint --eslnt` | invalid option typo | none | n/a | n/a | Notes: @@ -397,17 +382,17 @@ Notes: ### Suggested rollout phases 1. **Phase 1: Internal refactor (no CLI changes)** - - Introduce the domain abstraction internally and re-implement current behavior using plugins. + - Introduce the domain abstraction internally and re-implement current behavior using built-in domains. - Preserve existing fixed sequencing as the default order. - Remove correctness hazards while preserving user-facing defaults: - enforce single final aggregation path (no early success return masking failures) - normalize thrown domain errors into unified reporting. -2. **Phase 2: Smart detection and missing-tool policy** - - Implement relevance vs availability model. - - Apply optional-tool skip semantics by default. - - Align multi-tsconfig target derivation with parser project configuration. - - Fix include-pattern normalization so extension-bearing entries remain valid. +2. **Phase 2: Smart detection and missing-tool policy** *(complete)* + - Implemented relevance vs availability model. + - Applied optional-tool skip semantics by default. + - Aligned multi-tsconfig target derivation with parser project configuration (lint targets and ESLint parser projects now share the canonical tsconfig set). + - Fixed include-pattern normalization so extension-bearing entries remain valid. 3. **Phase 3: New CLI for domain selection** - Add `--domain`, `--skip-domain`, `--list-domains`, `--explain`. @@ -420,7 +405,7 @@ Notes: - Continue supporting v1 config as a subset mapping to the eslint domain. 5. **Phase 5: Extension point hardening** - - Stabilize a plugin loading mechanism and document the domain plugin API. + - Stabilize built-in domain extension surfaces and document downstream ESLint composition guidance. - Expand CI matrix/scenarios to cover domain-only invocations not exercised by current repo scripts. ### Risks and mitigations diff --git a/plans/refactor/STATE.md b/plans/refactor/STATE.md index 122f93d..f6f31bc 100644 --- a/plans/refactor/STATE.md +++ b/plans/refactor/STATE.md @@ -4,6 +4,7 @@ - First implementation slice for CLI execution semantics has been delivered. - The target architecture is described in [`PLAN.md`](PLAN.md). +- Multi-tsconfig targeting alignment is complete; lint target derivation and ESLint parser projects now share the same canonical tsconfig set. ## Implemented in full multi-tsconfig targeting alignment slice @@ -22,7 +23,7 @@ - only extensionless include entries are expanded with supported ESLint extensions - malformed glob synthesis paths from blanket suffixing are eliminated - Completed parser-project/target alignment across ESLint runtime paths: - - [`src/configs/js.ts`](../../src/configs/js.ts) continues to source parser project from resolved tsconfig list + - [`src/configs/eslint.ts`](../../src/configs/eslint.ts) continues to source parser project from resolved tsconfig list - [`src/utils.ts`](../../src/utils.ts) now injects parser `project` override from the same resolved config used for target derivation - [`src/domains/eslint.ts`](../../src/domains/eslint.ts) detection now derives default scope from the same multi-tsconfig union logic - Added regression coverage for multi-tsconfig alignment: @@ -38,10 +39,10 @@ - normalized output provides a single resolved shape for CLI/engine consumers - Updated runtime consumers to use the dedicated config module: - [`src/utils.ts`](../../src/utils.ts) - - [`src/configs/js.ts`](../../src/configs/js.ts) + - [`src/configs/eslint.ts`](../../src/configs/eslint.ts) - Expanded public exports for coherent extensibility: - [`package.json`](../../package.json) includes `./configs/*.js` typed/import exports - - canonical downstream preset import is `@matrixai/lint/configs/js.js` + - canonical downstream preset import is `@matrixai/lint/configs/eslint.js` - reusable downstream config import path available for `@matrixai/lint/configs/prettier.config.js` - Updated tests and docs for the new boundary: - adjusted programmatic export test in [`tests/index.test.ts`](../../tests/index.test.ts) @@ -149,7 +150,7 @@ - Implemented changes: - replaced [`src/configs/prettier.config.mjs`](../../src/configs/prettier.config.mjs) with [`src/configs/prettier.config.js`](../../src/configs/prettier.config.js) - updated imports and runtime references in: - - [`src/configs/js.ts`](../../src/configs/js.ts) + - [`src/configs/eslint.ts`](../../src/configs/eslint.ts) - [`src/bin/lint.ts`](../../src/bin/lint.ts) - Rationale: - the file is consumed directly at runtime by both ESLint config composition and Prettier CLI `--config` @@ -183,7 +184,7 @@ Notes on resolved items: ## Next actions -- Continue phased implementation from [`PLAN.md`](PLAN.md): - - move toward domain/plugin architecture internals - - align config evolution with v2 schema and precedence - - extend domain coverage and execution diagnostics (`--explain`, registry-style reporting) +- Final cleanup for release: + - polish docs for domain/target selection and public import paths + - align scripts to canonical domain-targeting CLI usage + - cut release (version bump, build, test, lintfix) diff --git a/src/configs/js.ts b/src/configs/eslint.ts similarity index 100% rename from src/configs/js.ts rename to src/configs/eslint.ts diff --git a/src/index.ts b/src/index.ts index ea67d18..96d5f50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -export { default as config } from './configs/js.js'; +export { default as config } from './configs/eslint.js'; export type { CLIOptions, LintDomain, diff --git a/src/utils.ts b/src/utils.ts index be043b0..93ca929 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -59,7 +59,7 @@ async function runESLint({ logger: Logger; }): Promise { const dirname = path.dirname(url.fileURLToPath(import.meta.url)); - const defaultConfigPath = path.resolve(dirname, './configs/js.js'); + const defaultConfigPath = path.resolve(dirname, './configs/eslint.js'); const lintConfig = resolvedConfig ?? resolveLintConfig(); const parserProjectOverride = { languageOptions: { diff --git a/tests/index.test.ts b/tests/index.test.ts index b1002b5..620b23e 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -1,4 +1,4 @@ -import eslintConfigDefault from '#configs/js.js'; +import eslintConfigDefault from '#configs/eslint.js'; import { config } from '#index.js'; describe('index exports', () => {