Skip to content

Commit 939ab98

Browse files
committed
feat(cli): add markdown target flag for scoped markdown linting
add markdown target option and selection parity with eslint and shell pass markdown patterns through lint context to markdown detection add tests and docs for markdown target workflows
1 parent 6000b08 commit 939ab98

9 files changed

Lines changed: 92 additions & 5 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ matrixai-lint --fix
4242
| `--user-config` | Uses detected `eslint.config.[js,mjs,cjs,ts]` from the project root if found |
4343
| `--eslint-config <path>` | Explicitly use a custom ESLint config file |
4444
| `--eslint <targets>` | ESLint targets (files, roots, or globs); implies ESLint domain selection |
45+
| `--markdown <targets>` | Markdown targets (files, roots, or globs); implies markdown domain selection |
4546
| `--shell <targets>` | Shell targets (files, roots, or globs); implies shell domain selection |
4647
| `--domain <id...>` | Run only selected domains (`eslint`, `shell`, `markdown`) |
4748
| `--skip-domain <id...>` | Skip selected domains (`eslint`, `shell`, `markdown`) |
@@ -58,13 +59,20 @@ Domain selection behavior:
5859
- `--eslint ...` runs ESLint only.
5960
- `--shell ...` runs shell only.
6061
- Passing both runs both.
62+
- Passing `--markdown` implies markdown domain selection.
63+
- `--markdown ...` runs markdown only.
64+
- Combined with other target flags, only those targeted domains run.
6165
- `shellcheck` is optional only for default auto-run shell execution.
6266
- If shell is explicitly requested (`--shell ...` or `--domain shell`),
6367
missing `shellcheck` is a failure.
6468
- `--shell` accepts target paths and glob patterns.
6569
- Directories are used as roots.
6670
- File paths and glob patterns are reduced to search roots, then `*.sh` files
6771
are discovered under those roots.
72+
- `--markdown` accepts target paths and glob patterns.
73+
- Directories are used as roots.
74+
- File paths and glob patterns are reduced to search roots, then `*.md` /
75+
`*.mdx` files are discovered under those roots.
6876

6977
#### Targeted workflows
7078

@@ -86,6 +94,12 @@ Domain selection behavior:
8694
matrixai-lint --domain markdown
8795
```
8896

97+
- Markdown only under selected roots:
98+
99+
```sh
100+
matrixai-lint --markdown standards templates README.md
101+
```
102+
89103
- Mixed scoped run (ESLint + shell only):
90104

91105
```sh
@@ -99,6 +113,7 @@ matrixai-lint --fix
99113
matrixai-lint --user-config
100114
matrixai-lint --eslint-config ./eslint.config.js --fix
101115
matrixai-lint --eslint "src/**/*.{ts,tsx}" --shell scripts
116+
matrixai-lint --markdown standards templates README.md
102117
matrixai-lint --domain eslint markdown
103118
matrixai-lint --skip-domain markdown
104119
matrixai-lint --list-domains

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"license": "Apache-2.0",
77
"repository": {
88
"type": "git",
9-
"url": "https://github.com/MatrixAI/js-eslint.git"
9+
"url": "git+https://github.com/MatrixAI/js-eslint.git"
1010
},
1111
"type": "module",
1212
"exports": {
@@ -29,7 +29,7 @@
2929
"#*": "./dist/*"
3030
},
3131
"bin": {
32-
"matrixai-lint": "./dist/bin/lint.js"
32+
"matrixai-lint": "dist/bin/lint.js"
3333
},
3434
"scripts": {
3535
"prepare": "tsc -p ./tsconfig.build.json",

src/bin/lint.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ program
4444
)
4545
.option('--eslint-config <path>', 'Path to explicit ESLint config file')
4646
.option('--eslint <target...>', 'ESLint targets (files, roots, or globs)')
47+
.option('--markdown <target...>', 'Markdown targets (files, roots, or globs)')
4748
.option(
4849
'--shell <target...>',
4950
'Shell targets (files, roots, or globs) used to derive shellcheck search roots',
@@ -139,6 +140,7 @@ async function main(argv = process.argv) {
139140
const explain = Boolean(options.explain);
140141

141142
const eslintPatterns: string[] | undefined = options.eslint;
143+
const markdownPatterns: string[] | undefined = options.markdown;
142144
const shellPatterns: string[] | undefined = options.shell;
143145
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
144146
resolveDomainSelection(options);
@@ -197,6 +199,7 @@ async function main(argv = process.argv) {
197199
chosenConfig,
198200
isConfigValid,
199201
eslintPatterns,
202+
markdownPatterns,
200203
shellPatterns,
201204
},
202205
});
@@ -214,6 +217,7 @@ async function main(argv = process.argv) {
214217
chosenConfig,
215218
isConfigValid,
216219
eslintPatterns,
220+
markdownPatterns,
217221
shellPatterns,
218222
},
219223
});

src/domains/engine.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type LintDomainEngineContext = {
88
isConfigValid: boolean;
99
eslintPatterns?: string[];
1010
shellPatterns?: string[];
11+
markdownPatterns?: string[];
1112
};
1213

1314
type LintDomainAvailabilityKind = 'required' | 'optional';

src/domains/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function resolveDomainSelection(options: CLIOptions): {
2929
const hasDomainSelectors = domainFlags.length > 0 || skipDomains.size > 0;
3030
const hasExplicitESLintTargets = (options.eslint?.length ?? 0) > 0;
3131
const hasExplicitShellTargets = (options.shell?.length ?? 0) > 0;
32+
const hasExplicitMarkdownTargets = (options.markdown?.length ?? 0) > 0;
3233
const explicitlyRequestedDomains = new Set<LintDomain>(domainFlags);
3334
const selectionSources = new Map<LintDomain, LintDomainSelectionSource>();
3435

@@ -38,6 +39,9 @@ function resolveDomainSelection(options: CLIOptions): {
3839
if (hasExplicitShellTargets) {
3940
explicitlyRequestedDomains.add('shell');
4041
}
42+
if (hasExplicitMarkdownTargets) {
43+
explicitlyRequestedDomains.add('markdown');
44+
}
4145

4246
let selectedDomains: Set<LintDomain>;
4347

@@ -48,7 +52,9 @@ function resolveDomainSelection(options: CLIOptions): {
4852
}
4953
} else if (
5054
!hasDomainSelectors &&
51-
(hasExplicitESLintTargets || hasExplicitShellTargets)
55+
(hasExplicitESLintTargets ||
56+
hasExplicitShellTargets ||
57+
hasExplicitMarkdownTargets)
5258
) {
5359
selectedDomains = new Set<LintDomain>();
5460
if (hasExplicitESLintTargets) {
@@ -59,6 +65,10 @@ function resolveDomainSelection(options: CLIOptions): {
5965
selectedDomains.add('shell');
6066
selectionSources.set('shell', 'target-flag');
6167
}
68+
if (hasExplicitMarkdownTargets) {
69+
selectedDomains.add('markdown');
70+
selectionSources.set('markdown', 'target-flag');
71+
}
6272
} else {
6373
selectedDomains = new Set<LintDomain>(LINT_DOMAINS);
6474
for (const domain of LINT_DOMAINS) {

src/domains/markdown.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ function createMarkdownDomainPlugin({
4646
return {
4747
domain: 'markdown',
4848
description: 'Format and check Markdown/MDX files with Prettier.',
49-
detect: () => {
50-
const searchPatterns = DEFAULT_MARKDOWN_SEARCH_ROOTS;
49+
detect: ({ markdownPatterns }) => {
50+
const searchPatterns =
51+
markdownPatterns != null && markdownPatterns.length > 0
52+
? markdownPatterns
53+
: DEFAULT_MARKDOWN_SEARCH_ROOTS;
5154
const searchRoots = resolveSearchRootsFromPatterns(searchPatterns);
5255
const matchedFiles = collectMarkdownFilesFromScope(searchRoots);
5356

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type CLIOptions = {
3535
eslintConfig?: string;
3636
eslint?: string[];
3737
shell?: string[];
38+
markdown?: string[];
3839
domain?: LintDomain[];
3940
skipDomain?: LintDomain[];
4041
listDomains?: boolean;

tests/bin/lint.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,31 @@ describe('matrixai-lint CLI domain semantics', () => {
9696
expect(prettierCalls).toHaveLength(0);
9797
});
9898

99+
test('--markdown no longer triggers eslint/shell domains', async () => {
100+
await fs.promises.mkdir(path.join(dataDir, 'standards'), {
101+
recursive: true,
102+
});
103+
await fs.promises.writeFile(
104+
path.join(dataDir, 'standards', 'guide.md'),
105+
'# guide\n',
106+
'utf8',
107+
);
108+
109+
await expect(
110+
main(['node', 'matrixai-lint', '--markdown', 'standards']),
111+
).resolves.toBeUndefined();
112+
113+
const shellCalls = capturedExecCalls.filter((c) => c.file === 'shellcheck');
114+
expect(shellCalls).toHaveLength(0);
115+
116+
const prettierCalls = capturedExecCalls.filter(
117+
(c) =>
118+
c.file === 'prettier' ||
119+
c.args.some((arg) => /prettier\.cjs$/.test(arg)),
120+
);
121+
expect(prettierCalls.length).toBeGreaterThan(0);
122+
});
123+
99124
test('explicit shell request + missing shellcheck fails', async () => {
100125
jest
101126
.spyOn(childProcess, 'spawnSync')

tests/domains/index.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,34 @@ describe('domain selection', () => {
457457
expect(selectionSources.get('eslint')).toBe('target-flag');
458458
});
459459

460+
test('markdown target flag implies explicit markdown domain request', () => {
461+
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
462+
resolveDomainSelection({
463+
fix: false,
464+
userConfig: false,
465+
markdown: ['standards', 'templates', 'README.md'],
466+
});
467+
468+
expect([...selectedDomains]).toStrictEqual(['markdown']);
469+
expect([...explicitlyRequestedDomains]).toStrictEqual(['markdown']);
470+
expect(selectionSources.get('markdown')).toBe('target-flag');
471+
});
472+
473+
test('domain flag remains authoritative over markdown target flag', () => {
474+
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
475+
resolveDomainSelection({
476+
fix: false,
477+
userConfig: false,
478+
domain: ['eslint'],
479+
markdown: ['standards'],
480+
});
481+
482+
expect([...selectedDomains]).toStrictEqual(['eslint']);
483+
expect([...explicitlyRequestedDomains]).toStrictEqual(['eslint']);
484+
expect(selectionSources.get('eslint')).toBe('domain-flag');
485+
expect(selectionSources.has('markdown')).toBe(false);
486+
});
487+
460488
test('--domain keeps explicit domains and --skip-domain removes them', () => {
461489
const { selectedDomains, explicitlyRequestedDomains, selectionSources } =
462490
resolveDomainSelection({

0 commit comments

Comments
 (0)