Skip to content

Commit 87ff89b

Browse files
authored
Merge pull request #16 from MatrixAI/feature-use-commander-package-for-cli-options
Use commander package to replace current implementation of CLI options
2 parents 8802f34 + f5376cc commit 87ff89b

File tree

5 files changed

+153
-52
lines changed

5 files changed

+153
-52
lines changed

package-lock.json

+70-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6-4
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
"build": "shx rm -rf ./dist && tsc -p ./tsconfig.build.json && shx chmod +x dist/bin/lint.js",
2727
"postversion": "npm install --package-lock-only --ignore-scripts --silent",
2828
"tsx": "tsx",
29-
"lint": "test -f ./dist/bin/lint.js || npm run build && ./dist/bin/lint.js",
30-
"lintfix": "sh -c 'test -f ./dist/bin/lint.js || npm run build && ./dist/bin/lint.js --fix'",
29+
"lint": "test -f ./dist/bin/lint.js || npm run build && ./dist/bin/lint.js --eslint \"{src,scripts,tests}/**/*.{js,mjs,ts,mts,jsx,tsx}\" --shell src scripts tests",
30+
"lintfix": "sh -c 'test -f ./dist/bin/lint.js || npm run build && ./dist/bin/lint.js --fix --eslint \"{src,scripts,tests}/**/*.{js,mjs,ts,mts,jsx,tsx}\"' --shell src scripts tests",
3131
"lint-shell": "find ./src ./tests ./scripts -type f -regextype posix-extended -regex '.*\\.(sh)' -exec shellcheck {} +",
3232
"docs": "shx rm -rf ./docs && typedoc --entryPointStrategy expand --gitRevision master --tsconfig ./tsconfig.build.json --out ./docs src",
3333
"test": "node ./scripts/test.mjs"
@@ -38,11 +38,11 @@
3838
"@typescript-eslint/eslint-plugin": "^8.27.0",
3939
"@typescript-eslint/parser": "^8.27.0",
4040
"@typescript-eslint/utils": "^8.26.1",
41-
"eslint": "^9.18.0",
41+
"eslint": ">=9.0.0",
4242
"eslint-config-prettier": "^9.1.0",
4343
"eslint-plugin-import": "^2.31.0",
44-
"eslint-plugin-prettier": "^5.2.1",
4544
"eslint-plugin-jsx-a11y": "^6.10.2",
45+
"eslint-plugin-prettier": "^5.2.1",
4646
"eslint-plugin-react": "^7.37.4",
4747
"eslint-plugin-react-hooks": "^5.1.0",
4848
"eslint-plugin-tailwindcss": "^3.18.0"
@@ -52,9 +52,11 @@
5252
"@swc/jest": "^0.2.29",
5353
"@types/jest": "^29.5.2",
5454
"@types/node": "^20.5.7",
55+
"commander": "^13.1.0",
5556
"jest": "^29.6.2",
5657
"jest-extended": "^4.0.2",
5758
"jest-junit": "^16.0.0",
59+
"minimatch": "^10.0.1",
5860
"prettier": "^3.0.0",
5961
"shx": "^0.3.4",
6062
"tsconfig-paths": "^3.9.0",

src/bin/lint.ts

+36-25
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,45 @@
11
#!/usr/bin/env node
2+
import type { CLIOptions } from '../types.js';
23
import os from 'node:os';
34
import path from 'node:path';
45
import process from 'node:process';
56
import childProcess from 'node:child_process';
67
import fs from 'node:fs';
8+
import { Command } from 'commander';
79
import * as utils from '../utils.js';
810

911
const platform = os.platform();
12+
const program = new Command();
13+
const DEFAULT_SHELLCHECK_SEARCH_ROOTS = ['./src', './scripts', './tests'];
14+
15+
program
16+
.name('matrixai-lint')
17+
.description(
18+
'Lint source files, scripts, and markdown with configured rules.',
19+
)
20+
.option('-f, --fix', 'Automatically fix problems')
21+
.option(
22+
'--user-config',
23+
'Use user-provided ESLint config instead of built-in one',
24+
)
25+
.option('--config <path>', 'Path to explicit ESLint config file')
26+
.option('--eslint <pat...>', 'Glob(s) to pass to ESLint')
27+
.option('--shell <pat...>', 'Glob(s) to pass to shell-check')
28+
.allowUnknownOption(true); // Optional: force rejection of unknown flags
1029

1130
/* eslint-disable no-console */
1231
async function main(argv = process.argv) {
13-
argv = argv.slice(2);
32+
await program.parseAsync(argv);
33+
const options = program.opts<CLIOptions>();
34+
35+
const fix = Boolean(options.fix);
36+
const useUserConfig = Boolean(options.userConfig);
37+
const explicitConfigPath: string | undefined = options.config;
38+
39+
const eslintPatterns: string[] | undefined = options.eslint;
40+
const shellPatterns: string[] | undefined = options.shell;
1441

1542
let hadFailure = false;
16-
let fix = false;
17-
let useUserConfig = false;
18-
let explicitConfigPath: string | undefined;
19-
const restArgs: string[] = [];
20-
21-
while (argv.length > 0) {
22-
const option = argv.shift()!;
23-
switch (option) {
24-
case '--fix':
25-
fix = true;
26-
break;
27-
case '--user-config':
28-
useUserConfig = true;
29-
break;
30-
case '--config':
31-
explicitConfigPath = argv.shift(); // Grab the next token
32-
break;
33-
default:
34-
restArgs.push(option);
35-
}
36-
}
3743

3844
// Resolve which config file to use
3945
let chosenConfig: string | undefined;
@@ -59,14 +65,19 @@ async function main(argv = process.argv) {
5965
}
6066

6167
try {
62-
await utils.runESLint({ fix, configPath: chosenConfig });
68+
await utils.runESLint({
69+
fix,
70+
configPath: chosenConfig,
71+
explicitGlobs: eslintPatterns,
72+
});
6373
} catch (err) {
6474
console.error(`ESLint failed: \n${err}`);
6575
hadFailure = true;
6676
}
6777

68-
const shellcheckDefaultSearchRoots = ['./src', './scripts', './tests'];
69-
const searchRoots = shellcheckDefaultSearchRoots
78+
const searchRoots = (
79+
shellPatterns?.length ? shellPatterns : DEFAULT_SHELLCHECK_SEARCH_ROOTS
80+
)
7081
.map((p) => path.resolve(process.cwd(), p))
7182
.filter((p) => fs.existsSync(p));
7283

src/types.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,12 @@ type RawMatrixCfg = Partial<{
88
forceInclude: unknown;
99
}>; // “might have these two keys, values are unknown”
1010

11-
export type { MatrixAILintCfg, RawMatrixCfg };
11+
type CLIOptions = {
12+
fix: boolean;
13+
userConfig: boolean;
14+
config?: string;
15+
eslint?: string[];
16+
shell?: string[];
17+
};
18+
19+
export type { MatrixAILintCfg, RawMatrixCfg, CLIOptions };

src/utils.ts

+32-12
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,72 @@ import { ESLint } from 'eslint';
1111
async function runESLint({
1212
fix,
1313
configPath,
14+
explicitGlobs,
1415
}: {
1516
fix: boolean;
1617
configPath?: string;
18+
explicitGlobs?: string[];
1719
}) {
1820
const dirname = path.dirname(url.fileURLToPath(import.meta.url));
1921
const defaultConfigPath = path.resolve(dirname, './configs/js.js');
2022

21-
const matrixaiLintConfig = resolveMatrixConfig();
22-
const forceInclude = matrixaiLintConfig.forceInclude;
23-
const tsconfigPaths = matrixaiLintConfig.tsconfigPaths;
23+
// PATH A – user supplied explicit globs
24+
if (explicitGlobs?.length) {
25+
console.log('Linting with explicit patterns:');
26+
explicitGlobs.forEach((g) => console.log(' ' + g));
27+
28+
const eslint = new ESLint({
29+
overrideConfigFile: configPath || defaultConfigPath,
30+
fix,
31+
errorOnUnmatchedPattern: false,
32+
warnIgnored: false,
33+
ignorePatterns: [], // Trust caller entirely
34+
});
35+
36+
await lintAndReport(eslint, explicitGlobs, fix);
37+
return;
38+
}
39+
40+
// PATH B – default behaviour (tsconfig + matrix config)
41+
const { forceInclude, tsconfigPaths } = resolveMatrixConfig();
2442

2543
if (tsconfigPaths.length === 0) {
2644
console.error('[matrixai-lint] ⚠ No tsconfig.json files found.');
2745
}
2846

2947
console.log(`Found ${tsconfigPaths.length} tsconfig.json files:`);
30-
tsconfigPaths.forEach((tsconfigPath) => console.log(' ' + tsconfigPath));
48+
tsconfigPaths.forEach((p) => console.log(' ' + p));
3149

32-
const { files: lintFiles, ignore } = buildPatterns(
50+
const { files: patterns, ignore: ignorePats } = buildPatterns(
3351
tsconfigPaths[0],
3452
forceInclude,
3553
);
3654

3755
console.log('Linting files:');
38-
lintFiles.forEach((file) => console.log(' ' + file));
56+
patterns.forEach((p) => console.log(' ' + p));
3957

4058
const eslint = new ESLint({
4159
overrideConfigFile: configPath || defaultConfigPath,
4260
fix,
4361
errorOnUnmatchedPattern: false,
4462
warnIgnored: false,
45-
ignorePatterns: ignore,
63+
ignorePatterns: ignorePats,
4664
});
4765

48-
const results = await eslint.lintFiles(lintFiles);
66+
await lintAndReport(eslint, patterns, fix);
67+
}
68+
69+
async function lintAndReport(eslint: ESLint, patterns: string[], fix: boolean) {
70+
const results = await eslint.lintFiles(patterns);
4971

5072
if (fix) {
5173
await ESLint.outputFixes(results);
5274
}
5375

5476
const formatter = await eslint.loadFormatter('stylish');
55-
const resultText = formatter.format(results);
56-
console.log(resultText);
57-
58-
/* eslint-enable no-console */
77+
console.log(formatter.format(results));
5978
}
79+
/* eslint-enable no-console */
6080

6181
/**
6282
* Find the user's ESLint config file in the current working directory.

0 commit comments

Comments
 (0)