Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ matrixai-lint --fix
| `--user-config` | Uses detected `eslint.config.[js,mjs,cjs,ts]` from the project root if found |
| `--eslint-config <path>` | Explicitly use a custom ESLint config file |
| `--eslint <targets>` | ESLint targets (files, roots, or globs); implies ESLint domain selection |
| `--markdown <targets>` | Markdown targets (files, roots, or globs); implies markdown domain selection |
| `--shell <targets>` | Shell targets (files, roots, or globs); implies shell domain selection |
| `--domain <id...>` | Run only selected domains (`eslint`, `shell`, `markdown`) |
| `--skip-domain <id...>` | Skip selected domains (`eslint`, `shell`, `markdown`) |
Expand All @@ -58,13 +59,20 @@ Domain selection behavior:
- `--eslint ...` runs ESLint only.
- `--shell ...` runs shell only.
- Passing both runs both.
- Passing `--markdown` implies markdown domain selection.
- `--markdown ...` runs markdown only.
- Combined with other target flags, only those targeted domains run.
- `shellcheck` is optional only for default auto-run shell execution.
- If shell is explicitly requested (`--shell ...` or `--domain shell`),
missing `shellcheck` is a failure.
- `--shell` accepts target paths and glob patterns.
- Directories are used as roots.
- File paths and glob patterns are reduced to search roots, then `*.sh` files
are discovered under those roots.
- `--markdown` accepts target paths and glob patterns.
- Directories are used as roots.
- File paths and glob patterns are reduced to search roots, then `*.md` /
`*.mdx` files are discovered under those roots.

#### Targeted workflows

Expand All @@ -86,6 +94,12 @@ Domain selection behavior:
matrixai-lint --domain markdown
```

- Markdown only under selected roots:

```sh
matrixai-lint --markdown standards templates README.md
```

- Mixed scoped run (ESLint + shell only):

```sh
Expand All @@ -99,6 +113,7 @@ matrixai-lint --fix
matrixai-lint --user-config
matrixai-lint --eslint-config ./eslint.config.js --fix
matrixai-lint --eslint "src/**/*.{ts,tsx}" --shell scripts
matrixai-lint --markdown standards templates README.md
matrixai-lint --domain eslint markdown
matrixai-lint --skip-domain markdown
matrixai-lint --list-domains
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@matrixai/lint",
"version": "0.3.0",
"version": "0.4.0",
"author": "Roger Qiu",
"description": "Org wide custom eslint rules",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/MatrixAI/js-eslint.git"
"url": "git+https://github.com/MatrixAI/js-eslint.git"
},
"type": "module",
"exports": {
Expand All @@ -29,7 +29,7 @@
"#*": "./dist/*"
},
"bin": {
"matrixai-lint": "./dist/bin/lint.js"
"matrixai-lint": "dist/bin/lint.js"
},
"scripts": {
"prepare": "tsc -p ./tsconfig.build.json",
Expand Down
4 changes: 4 additions & 0 deletions src/bin/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ program
)
.option('--eslint-config <path>', 'Path to explicit ESLint config file')
.option('--eslint <target...>', 'ESLint targets (files, roots, or globs)')
.option('--markdown <target...>', 'Markdown targets (files, roots, or globs)')
.option(
'--shell <target...>',
'Shell targets (files, roots, or globs) used to derive shellcheck search roots',
Expand Down Expand Up @@ -139,6 +140,7 @@ async function main(argv = process.argv) {
const explain = Boolean(options.explain);

const eslintPatterns: string[] | undefined = options.eslint;
const markdownPatterns: string[] | undefined = options.markdown;
const shellPatterns: string[] | undefined = options.shell;
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
resolveDomainSelection(options);
Expand Down Expand Up @@ -197,6 +199,7 @@ async function main(argv = process.argv) {
chosenConfig,
isConfigValid,
eslintPatterns,
markdownPatterns,
shellPatterns,
},
});
Expand All @@ -214,6 +217,7 @@ async function main(argv = process.argv) {
chosenConfig,
isConfigValid,
eslintPatterns,
markdownPatterns,
shellPatterns,
},
});
Expand Down
1 change: 1 addition & 0 deletions src/domains/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type LintDomainEngineContext = {
isConfigValid: boolean;
eslintPatterns?: string[];
shellPatterns?: string[];
markdownPatterns?: string[];
};

type LintDomainAvailabilityKind = 'required' | 'optional';
Expand Down
12 changes: 11 additions & 1 deletion src/domains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function resolveDomainSelection(options: CLIOptions): {
const hasDomainSelectors = domainFlags.length > 0 || skipDomains.size > 0;
const hasExplicitESLintTargets = (options.eslint?.length ?? 0) > 0;
const hasExplicitShellTargets = (options.shell?.length ?? 0) > 0;
const hasExplicitMarkdownTargets = (options.markdown?.length ?? 0) > 0;
const explicitlyRequestedDomains = new Set<LintDomain>(domainFlags);
const selectionSources = new Map<LintDomain, LintDomainSelectionSource>();

Expand All @@ -38,6 +39,9 @@ function resolveDomainSelection(options: CLIOptions): {
if (hasExplicitShellTargets) {
explicitlyRequestedDomains.add('shell');
}
if (hasExplicitMarkdownTargets) {
explicitlyRequestedDomains.add('markdown');
}

let selectedDomains: Set<LintDomain>;

Expand All @@ -48,7 +52,9 @@ function resolveDomainSelection(options: CLIOptions): {
}
} else if (
!hasDomainSelectors &&
(hasExplicitESLintTargets || hasExplicitShellTargets)
(hasExplicitESLintTargets ||
hasExplicitShellTargets ||
hasExplicitMarkdownTargets)
) {
selectedDomains = new Set<LintDomain>();
if (hasExplicitESLintTargets) {
Expand All @@ -59,6 +65,10 @@ function resolveDomainSelection(options: CLIOptions): {
selectedDomains.add('shell');
selectionSources.set('shell', 'target-flag');
}
if (hasExplicitMarkdownTargets) {
selectedDomains.add('markdown');
selectionSources.set('markdown', 'target-flag');
}
} else {
selectedDomains = new Set<LintDomain>(LINT_DOMAINS);
for (const domain of LINT_DOMAINS) {
Expand Down
7 changes: 5 additions & 2 deletions src/domains/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ function createMarkdownDomainPlugin({
return {
domain: 'markdown',
description: 'Format and check Markdown/MDX files with Prettier.',
detect: () => {
const searchPatterns = DEFAULT_MARKDOWN_SEARCH_ROOTS;
detect: ({ markdownPatterns }) => {
const searchPatterns =
markdownPatterns != null && markdownPatterns.length > 0
? markdownPatterns
: DEFAULT_MARKDOWN_SEARCH_ROOTS;
const searchRoots = resolveSearchRootsFromPatterns(searchPatterns);
const matchedFiles = collectMarkdownFilesFromScope(searchRoots);

Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type CLIOptions = {
eslintConfig?: string;
eslint?: string[];
shell?: string[];
markdown?: string[];
domain?: LintDomain[];
skipDomain?: LintDomain[];
listDomains?: boolean;
Expand Down
25 changes: 25 additions & 0 deletions tests/bin/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,31 @@ describe('matrixai-lint CLI domain semantics', () => {
expect(prettierCalls).toHaveLength(0);
});

test('--markdown no longer triggers eslint/shell domains', async () => {
await fs.promises.mkdir(path.join(dataDir, 'standards'), {
recursive: true,
});
await fs.promises.writeFile(
path.join(dataDir, 'standards', 'guide.md'),
'# guide\n',
'utf8',
);

await expect(
main(['node', 'matrixai-lint', '--markdown', 'standards']),
).resolves.toBeUndefined();

const shellCalls = capturedExecCalls.filter((c) => c.file === 'shellcheck');
expect(shellCalls).toHaveLength(0);

const prettierCalls = capturedExecCalls.filter(
(c) =>
c.file === 'prettier' ||
c.args.some((arg) => /prettier\.cjs$/.test(arg)),
);
expect(prettierCalls.length).toBeGreaterThan(0);
});

test('explicit shell request + missing shellcheck fails', async () => {
jest
.spyOn(childProcess, 'spawnSync')
Expand Down
28 changes: 28 additions & 0 deletions tests/domains/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,34 @@ describe('domain selection', () => {
expect(selectionSources.get('eslint')).toBe('target-flag');
});

test('markdown target flag implies explicit markdown domain request', () => {
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
resolveDomainSelection({
fix: false,
userConfig: false,
markdown: ['standards', 'templates', 'README.md'],
});

expect([...selectedDomains]).toStrictEqual(['markdown']);
expect([...explicitlyRequestedDomains]).toStrictEqual(['markdown']);
expect(selectionSources.get('markdown')).toBe('target-flag');
});

test('domain flag remains authoritative over markdown target flag', () => {
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
resolveDomainSelection({
fix: false,
userConfig: false,
domain: ['eslint'],
markdown: ['standards'],
});

expect([...selectedDomains]).toStrictEqual(['eslint']);
expect([...explicitlyRequestedDomains]).toStrictEqual(['eslint']);
expect(selectionSources.get('eslint')).toBe('domain-flag');
expect(selectionSources.has('markdown')).toBe(false);
});

test('--domain keeps explicit domains and --skip-domain removes them', () => {
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
resolveDomainSelection({
Expand Down
Loading