pr-checkmate 1.22.7 → 1.22.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/constants.d.ts +16 -1
- package/dist/config/constants.js +90 -38
- package/dist/config/index.d.ts +15 -2
- package/dist/config/index.js +28 -7
- package/dist/config/loader.js +5 -1
- package/dist/core/checks/circular-deps.js +5 -6
- package/dist/core/checks/duplicate-check.js +7 -11
- package/dist/core/checks/lint.js +21 -10
- package/dist/core/checks/markdown-lint.js +4 -17
- package/dist/core/checks/prettier.js +6 -6
- package/dist/core/checks/spellcheck.js +7 -15
- package/dist/core/context.js +40 -10
- package/dist/core/targets.d.ts +13 -0
- package/dist/core/targets.js +59 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/init.js +23 -8
- package/dist/interfaces/index.d.ts +2 -1
- package/dist/scripts/copy-configs.js +5 -1
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/eslint-default-rules.json +62 -0
- package/eslint.config.mjs +6 -66
- package/package.json +2 -1
- package/src/config/eslint-default-rules.json +62 -0
- package/dist/utils/delta.d.ts +0 -5
- package/dist/utils/delta.js +0 -53
- package/src/config/eslint.config.mjs +0 -180
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Build output, dependency, tooling, and report directories that are never
|
|
3
|
+
* product code. Every check excludes them by directory-name segment match (see
|
|
4
|
+
* `targets.ts`): a match on any segment of a path drops the file. Untracked
|
|
5
|
+
* build dirs are already excluded because checks read git-tracked files only —
|
|
6
|
+
* this list additionally covers dirs that repos commonly *commit* (`.github`,
|
|
7
|
+
* `.vscode`, `.idea`, `.claude`) and any checked-in build artifacts.
|
|
8
|
+
*
|
|
9
|
+
* Covers the common ecosystems: Node/JS/web, Python, Java/Kotlin/Gradle/Android,
|
|
10
|
+
* Swift/iOS/Xcode, and C/C++/CMake. Add more here.
|
|
11
|
+
*/
|
|
12
|
+
export declare const IGNORED_DIRS: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Glob form of the ignore list, for tools that take glob ignores directly
|
|
15
|
+
* (ESLint `--ignore-pattern`, cspell `--exclude`, the generated
|
|
16
|
+
* `.prettierignore`). Combines {@link IGNORED_DIRS} with a few tool-specific
|
|
17
|
+
* nested report globs that are not plain directory names.
|
|
3
18
|
*/
|
|
4
19
|
export declare const IGNORE_PATTERNS: string[];
|
|
5
20
|
/**
|
package/dist/config/constants.js
CHANGED
|
@@ -3,57 +3,109 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CONFIG_FILE = exports.documentation = exports.DEFAULT_ALLOW_PACKAGES = exports.DEFAULT_COMMANDS = exports.SOURCE_EXTENSIONS = exports.DUPLICATE_CODE_FORMATS = exports.CONFIG_FILE_PATTERNS = exports.DEAD_CODE_IGNORE_DEFAULTS = exports.DUPLICATE_IGNORE_DEFAULTS = exports.IGNORE_PATTERNS = void 0;
|
|
6
|
+
exports.CONFIG_FILE = exports.documentation = exports.DEFAULT_ALLOW_PACKAGES = exports.DEFAULT_COMMANDS = exports.SOURCE_EXTENSIONS = exports.DUPLICATE_CODE_FORMATS = exports.CONFIG_FILE_PATTERNS = exports.DEAD_CODE_IGNORE_DEFAULTS = exports.DUPLICATE_IGNORE_DEFAULTS = exports.IGNORE_PATTERNS = exports.IGNORED_DIRS = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Build output, dependency, tooling, and report directories that are never
|
|
10
|
+
* product code. Every check excludes them by directory-name segment match (see
|
|
11
|
+
* `targets.ts`): a match on any segment of a path drops the file. Untracked
|
|
12
|
+
* build dirs are already excluded because checks read git-tracked files only —
|
|
13
|
+
* this list additionally covers dirs that repos commonly *commit* (`.github`,
|
|
14
|
+
* `.vscode`, `.idea`, `.claude`) and any checked-in build artifacts.
|
|
15
|
+
*
|
|
16
|
+
* Covers the common ecosystems: Node/JS/web, Python, Java/Kotlin/Gradle/Android,
|
|
17
|
+
* Swift/iOS/Xcode, and C/C++/CMake. Add more here.
|
|
18
|
+
*/
|
|
19
|
+
exports.IGNORED_DIRS = [
|
|
20
|
+
// VCS, editors, and CI/tooling config
|
|
21
|
+
'.git',
|
|
22
|
+
'.github',
|
|
23
|
+
'.gitlab',
|
|
24
|
+
'.vscode',
|
|
25
|
+
'.vs',
|
|
26
|
+
'.idea',
|
|
27
|
+
'.claude',
|
|
28
|
+
'.cursor',
|
|
29
|
+
'.husky',
|
|
30
|
+
'.circleci',
|
|
31
|
+
// Node / JS / web — build output & caches
|
|
32
|
+
'node_modules',
|
|
33
|
+
'dist',
|
|
34
|
+
'build',
|
|
35
|
+
'build-release',
|
|
36
|
+
'out',
|
|
37
|
+
'coverage',
|
|
38
|
+
'.next',
|
|
39
|
+
'.nuxt',
|
|
40
|
+
'.svelte-kit',
|
|
41
|
+
'.turbo',
|
|
42
|
+
'.cache',
|
|
43
|
+
'.parcel-cache',
|
|
44
|
+
'.vercel',
|
|
45
|
+
'.wrangler',
|
|
46
|
+
'.serverless',
|
|
47
|
+
'.terraform',
|
|
48
|
+
// Python
|
|
49
|
+
'__pycache__',
|
|
50
|
+
'.pytest_cache',
|
|
51
|
+
'.mypy_cache',
|
|
52
|
+
'.ruff_cache',
|
|
53
|
+
'.tox',
|
|
54
|
+
'.venv',
|
|
55
|
+
'venv',
|
|
56
|
+
'.eggs',
|
|
57
|
+
'htmlcov',
|
|
58
|
+
// Java / Kotlin / Gradle / Android
|
|
59
|
+
'.gradle',
|
|
60
|
+
'.kotlin',
|
|
61
|
+
'target',
|
|
62
|
+
'gen',
|
|
63
|
+
'captures',
|
|
64
|
+
// Swift / iOS / Xcode
|
|
65
|
+
'.build',
|
|
66
|
+
'.swiftpm',
|
|
67
|
+
'DerivedData',
|
|
68
|
+
'Pods',
|
|
69
|
+
'Carthage',
|
|
70
|
+
// C / C++ / CMake
|
|
71
|
+
'CMakeFiles',
|
|
72
|
+
'cmake-build-debug',
|
|
73
|
+
'cmake-build-release',
|
|
74
|
+
'_deps',
|
|
75
|
+
// Test reports / results / logs / release artifacts
|
|
76
|
+
'reports',
|
|
77
|
+
'report',
|
|
78
|
+
'test-reports',
|
|
79
|
+
'test-report',
|
|
80
|
+
'test-output',
|
|
81
|
+
'test-results',
|
|
82
|
+
'results',
|
|
83
|
+
'result',
|
|
84
|
+
'logs',
|
|
85
|
+
'allure-report',
|
|
86
|
+
'allure-results',
|
|
87
|
+
'playwright-report',
|
|
88
|
+
'.wdio',
|
|
89
|
+
];
|
|
90
|
+
/**
|
|
91
|
+
* Glob form of the ignore list, for tools that take glob ignores directly
|
|
92
|
+
* (ESLint `--ignore-pattern`, cspell `--exclude`, the generated
|
|
93
|
+
* `.prettierignore`). Combines {@link IGNORED_DIRS} with a few tool-specific
|
|
94
|
+
* nested report globs that are not plain directory names.
|
|
10
95
|
*/
|
|
11
96
|
exports.IGNORE_PATTERNS = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
'**/.git/**',
|
|
15
|
-
'**/build/**',
|
|
16
|
-
'**/coverage/**',
|
|
17
|
-
'**/.next/**',
|
|
18
|
-
'**/.nuxt/**',
|
|
19
|
-
'**/out/**',
|
|
20
|
-
'**/__pycache__/**',
|
|
21
|
-
'**/.pytest_cache/**',
|
|
22
|
-
'**/.venv/**',
|
|
23
|
-
'**/venv/**',
|
|
24
|
-
'**/allure-report/**',
|
|
25
|
-
'**/allure-results/**',
|
|
26
|
-
'**/reports/**',
|
|
27
|
-
'**/report/**',
|
|
28
|
-
'**/test-reports/**',
|
|
29
|
-
'**/test-report/**',
|
|
30
|
-
'**/test-output/**',
|
|
31
|
-
'**/test-results/**',
|
|
32
|
-
'**/results/**',
|
|
33
|
-
'**/result/**',
|
|
34
|
-
'**/logs/**',
|
|
35
|
-
// Jest
|
|
97
|
+
...exports.IGNORED_DIRS.map(dir => `**/${dir}/**`),
|
|
98
|
+
// Tool-specific nested report dirs / files (not bare directory names)
|
|
36
99
|
'**/jest-html-reporters-*/**',
|
|
37
100
|
'**/jest-stare/**',
|
|
38
|
-
// Cypress
|
|
39
101
|
'**/cypress/reports/**',
|
|
40
102
|
'**/cypress/videos/**',
|
|
41
103
|
'**/cypress/screenshots/**',
|
|
42
|
-
// Playwright
|
|
43
|
-
'**/playwright-report/**',
|
|
44
|
-
// WebdriverIO
|
|
45
|
-
'**/.wdio/**',
|
|
46
|
-
'**/wdio-logs/**',
|
|
47
|
-
// Mocha / Jasmine
|
|
48
104
|
'**/mochawesome-report/**',
|
|
49
105
|
'**/mochawesome-reports/**',
|
|
50
|
-
// JUnit / Surefire / Maven
|
|
51
106
|
'**/target/surefire-reports/**',
|
|
52
107
|
'**/target/failsafe-reports/**',
|
|
53
|
-
|
|
54
|
-
'**/.pytest_cache/**',
|
|
55
|
-
'**/htmlcov/**',
|
|
56
|
-
// Misc reports
|
|
108
|
+
'**/wdio-logs/**',
|
|
57
109
|
'**/lint-report/**',
|
|
58
110
|
'**/audit-report/**',
|
|
59
111
|
];
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
export * from './constants';
|
|
2
2
|
export { loadProjectConfig } from '../config/loader';
|
|
3
3
|
/**
|
|
4
|
-
* Get
|
|
5
|
-
*
|
|
4
|
+
* Get the configured source path(s) dynamically as a normalized list.
|
|
5
|
+
* Accepts a single string or an array in pr-checkmate.json; empty/invalid
|
|
6
|
+
* values fall back to `['.']` (the whole repo). Clients narrow scanning to a
|
|
7
|
+
* subdirectory by setting `sourcePath` in their pr-checkmate.json. Always reads
|
|
8
|
+
* fresh config.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getSourcePaths(): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Get a single source path (first configured entry) for callers that only
|
|
13
|
+
* support one directory. Prefer `getSourcePaths()` for scan targets.
|
|
6
14
|
*/
|
|
7
15
|
export declare function getSourcePath(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Get the effective list of directory names excluded from every check
|
|
18
|
+
* (client `ignoreDirs` in pr-checkmate.json, or the curated default).
|
|
19
|
+
*/
|
|
20
|
+
export declare function getIgnoredDirs(): string[];
|
|
8
21
|
/**
|
|
9
22
|
* Get path to package config files
|
|
10
23
|
*/
|
package/dist/config/index.js
CHANGED
|
@@ -18,16 +18,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.loadProjectConfig = void 0;
|
|
21
|
+
exports.getSourcePaths = getSourcePaths;
|
|
21
22
|
exports.getSourcePath = getSourcePath;
|
|
23
|
+
exports.getIgnoredDirs = getIgnoredDirs;
|
|
22
24
|
exports.getPackageConfigPath = getPackageConfigPath;
|
|
23
25
|
exports.hasLocalConfig = hasLocalConfig;
|
|
24
26
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
25
27
|
const node_path_1 = __importDefault(require("node:path"));
|
|
28
|
+
const loader_1 = require("../config/loader");
|
|
26
29
|
const utils_1 = require("../utils");
|
|
27
30
|
const constants_1 = require("./constants");
|
|
28
31
|
__exportStar(require("./constants"), exports);
|
|
29
|
-
var
|
|
30
|
-
Object.defineProperty(exports, "loadProjectConfig", { enumerable: true, get: function () { return
|
|
32
|
+
var loader_2 = require("../config/loader");
|
|
33
|
+
Object.defineProperty(exports, "loadProjectConfig", { enumerable: true, get: function () { return loader_2.loadProjectConfig; } });
|
|
31
34
|
/**
|
|
32
35
|
* Load configuration dynamically (always reads fresh config)
|
|
33
36
|
*/
|
|
@@ -41,15 +44,33 @@ function loadConfig() {
|
|
|
41
44
|
catch (err) {
|
|
42
45
|
utils_1.logger.warn(`⚠️ Failed to read config from ${constants_1.CONFIG_FILE}, using defaults${err}`);
|
|
43
46
|
}
|
|
44
|
-
return { sourcePath: '
|
|
47
|
+
return { sourcePath: '.' };
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
|
-
* Get
|
|
48
|
-
*
|
|
50
|
+
* Get the configured source path(s) dynamically as a normalized list.
|
|
51
|
+
* Accepts a single string or an array in pr-checkmate.json; empty/invalid
|
|
52
|
+
* values fall back to `['.']` (the whole repo). Clients narrow scanning to a
|
|
53
|
+
* subdirectory by setting `sourcePath` in their pr-checkmate.json. Always reads
|
|
54
|
+
* fresh config.
|
|
55
|
+
*/
|
|
56
|
+
function getSourcePaths() {
|
|
57
|
+
const { sourcePath } = loadConfig();
|
|
58
|
+
const paths = (Array.isArray(sourcePath) ? sourcePath : [sourcePath]).filter((p) => typeof p === 'string' && p.trim().length > 0);
|
|
59
|
+
return paths.length > 0 ? paths : ['.'];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get a single source path (first configured entry) for callers that only
|
|
63
|
+
* support one directory. Prefer `getSourcePaths()` for scan targets.
|
|
49
64
|
*/
|
|
50
65
|
function getSourcePath() {
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
return getSourcePaths()[0];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the effective list of directory names excluded from every check
|
|
70
|
+
* (client `ignoreDirs` in pr-checkmate.json, or the curated default).
|
|
71
|
+
*/
|
|
72
|
+
function getIgnoredDirs() {
|
|
73
|
+
return (0, loader_1.loadProjectConfig)().ignoreDirs ?? [];
|
|
53
74
|
}
|
|
54
75
|
/**
|
|
55
76
|
* Get path to package config files
|
package/dist/config/loader.js
CHANGED
|
@@ -34,6 +34,9 @@ function mergeWithDefaults(userConfig) {
|
|
|
34
34
|
const defaults = getDefaultConfig();
|
|
35
35
|
return {
|
|
36
36
|
sourcePath: userConfig.sourcePath || defaults.sourcePath,
|
|
37
|
+
// Replace (not merge): the client sees the full default list in their
|
|
38
|
+
// generated config and edits it directly, so their list is authoritative.
|
|
39
|
+
ignoreDirs: userConfig.ignoreDirs ?? defaults.ignoreDirs,
|
|
37
40
|
lint: {
|
|
38
41
|
rules: { ...defaults.lint?.rules, ...userConfig.lint?.rules },
|
|
39
42
|
ignorePatterns: [
|
|
@@ -90,7 +93,8 @@ function mergeWithDefaults(userConfig) {
|
|
|
90
93
|
*/
|
|
91
94
|
function getDefaultConfig() {
|
|
92
95
|
return {
|
|
93
|
-
sourcePath: '
|
|
96
|
+
sourcePath: '.',
|
|
97
|
+
ignoreDirs: [...constants_1.IGNORED_DIRS],
|
|
94
98
|
lint: {
|
|
95
99
|
rules: {
|
|
96
100
|
'no-console': 'warn',
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.circularDepsCheck = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
4
|
const config_1 = require("../../config");
|
|
9
5
|
const utils_1 = require("../../utils");
|
|
10
6
|
const execa_1 = require("../../utils/execa");
|
|
7
|
+
const targets_1 = require("../targets");
|
|
11
8
|
const types_1 = require("../types");
|
|
12
9
|
exports.circularDepsCheck = {
|
|
13
10
|
name: 'Circular Deps',
|
|
@@ -16,8 +13,10 @@ exports.circularDepsCheck = {
|
|
|
16
13
|
return ctx.languages.includes('typescript') || ctx.languages.includes('javascript');
|
|
17
14
|
},
|
|
18
15
|
async run(ctx) {
|
|
19
|
-
const
|
|
20
|
-
|
|
16
|
+
const files = await (0, targets_1.listTrackedFiles)(ctx, ['*.ts', '*.tsx', '*.js', '*.jsx'], (0, config_1.getSourcePaths)());
|
|
17
|
+
if (files.length === 0)
|
|
18
|
+
return (0, types_1.pass)();
|
|
19
|
+
const result = await (0, execa_1.execa)('npx', ['dpdm', '--no-warning', '--no-tree', '--circular', ...files], {
|
|
21
20
|
cwd: ctx.cwd,
|
|
22
21
|
reject: false,
|
|
23
22
|
});
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.duplicateCheck = void 0;
|
|
7
|
-
const
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
4
|
+
const config_1 = require("../../config");
|
|
9
5
|
const constants_1 = require("../../config/constants");
|
|
10
6
|
const utils_1 = require("../../utils");
|
|
11
|
-
const delta_1 = require("../../utils/delta");
|
|
12
7
|
const execa_1 = require("../../utils/execa");
|
|
8
|
+
const targets_1 = require("../targets");
|
|
13
9
|
const types_1 = require("../types");
|
|
14
10
|
// Always excluded regardless of client config — scanning these never yields
|
|
15
11
|
// meaningful production duplication. Config files (eslint/*.config.*) are data,
|
|
@@ -44,11 +40,11 @@ exports.duplicateCheck = {
|
|
|
44
40
|
async run(ctx) {
|
|
45
41
|
const minLines = ctx.config.duplicate?.minLines ?? 5;
|
|
46
42
|
const minTokens = ctx.config.duplicate?.minTokens ?? 50;
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
return (0, types_1.skip)('
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
const targetPaths = await (0, targets_1.resolveScopedTargetFiles)(ctx, ['*.ts', '*.tsx', '*.js', '*.jsx'], (0, config_1.getSourcePaths)());
|
|
44
|
+
if (targetPaths === null)
|
|
45
|
+
return (0, types_1.skip)('git unavailable');
|
|
46
|
+
if (targetPaths.length === 0)
|
|
47
|
+
return (0, types_1.skip)('no relevant files');
|
|
52
48
|
const jscpdBin = (0, utils_1.resolvePackageBin)('jscpd', 'run-jscpd.js');
|
|
53
49
|
// Client-configurable exclusions (pr-checkmate.json `duplicate.ignore`),
|
|
54
50
|
// defaulting to test files/folders, appended to the always-on base ignores.
|
package/dist/core/checks/lint.js
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.lintCheck = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
4
|
const config_1 = require("../../config");
|
|
9
5
|
const utils_1 = require("../../utils");
|
|
10
|
-
const delta_1 = require("../../utils/delta");
|
|
11
6
|
const execa_1 = require("../../utils/execa");
|
|
7
|
+
const targets_1 = require("../targets");
|
|
12
8
|
const types_1 = require("../types");
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the ESLint binary, preferring the consumer project's own copy so a
|
|
11
|
+
* repo pinned to ESLint 8 (+ legacy `.eslintrc.*`) keeps working. Falls back to
|
|
12
|
+
* the version bundled with pr-checkmate when the project has no local install.
|
|
13
|
+
*/
|
|
14
|
+
function resolveESLintBin(cwd) {
|
|
15
|
+
try {
|
|
16
|
+
return (0, utils_1.resolvePackageBin)('eslint', 'bin/eslint.js', { paths: [cwd] });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return (0, utils_1.resolvePackageBin)('eslint', 'bin/eslint.js');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
13
22
|
async function getESLintMajorVersion(eslintBin) {
|
|
14
23
|
try {
|
|
15
24
|
const { stdout } = await (0, execa_1.execa)(process.execPath, [eslintBin, '--version']);
|
|
@@ -27,10 +36,12 @@ exports.lintCheck = {
|
|
|
27
36
|
return ctx.languages.includes('typescript') || ctx.languages.includes('javascript');
|
|
28
37
|
},
|
|
29
38
|
async run(ctx) {
|
|
30
|
-
const eslintBin = (
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
const eslintBin = resolveESLintBin(ctx.cwd);
|
|
40
|
+
const targets = await (0, targets_1.resolveScopedTargetFiles)(ctx, ['*.ts', '*.tsx', '*.js', '*.jsx'], (0, config_1.getSourcePaths)());
|
|
41
|
+
if (targets === null)
|
|
42
|
+
return (0, types_1.skip)('git unavailable');
|
|
43
|
+
if (targets.length === 0)
|
|
44
|
+
return (0, types_1.pass)('no TypeScript/JavaScript files to lint');
|
|
34
45
|
const hasLocal = (0, config_1.hasLocalConfig)([
|
|
35
46
|
'eslint.config.mjs',
|
|
36
47
|
'eslint.config.js',
|
|
@@ -50,7 +61,7 @@ exports.lintCheck = {
|
|
|
50
61
|
if (ctx.write)
|
|
51
62
|
args.push('--fix');
|
|
52
63
|
if (hasLocal) {
|
|
53
|
-
utils_1.logger.info(`[ESLint]: Using local config (
|
|
64
|
+
utils_1.logger.info(`[ESLint]: Using local config (${targets.length} file(s))`);
|
|
54
65
|
}
|
|
55
66
|
else {
|
|
56
67
|
const configFile = version >= 9 ? 'eslint.config.mjs' : '.eslintrc.js';
|
|
@@ -9,8 +9,8 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const config_1 = require("../../config");
|
|
11
11
|
const utils_1 = require("../../utils");
|
|
12
|
-
const delta_1 = require("../../utils/delta");
|
|
13
12
|
const execa_1 = require("../../utils/execa");
|
|
13
|
+
const targets_1 = require("../targets");
|
|
14
14
|
const types_1 = require("../types");
|
|
15
15
|
function loadUserMarkdownConfig(cwd) {
|
|
16
16
|
const configPath = node_path_1.default.join(cwd, 'pr-checkmate.json');
|
|
@@ -31,21 +31,6 @@ function buildMergedConfig(bundledConfigPath, userRules) {
|
|
|
31
31
|
node_fs_1.default.writeFileSync(tmpFile, JSON.stringify(merged, null, 2));
|
|
32
32
|
return tmpFile;
|
|
33
33
|
}
|
|
34
|
-
async function resolveTargets(cwd) {
|
|
35
|
-
const changedFiles = await (0, delta_1.getChangedFiles)(['.md', '.mdx']);
|
|
36
|
-
if (changedFiles !== null)
|
|
37
|
-
return changedFiles;
|
|
38
|
-
try {
|
|
39
|
-
const { stdout } = await (0, execa_1.execa)('git', ['ls-files', '*.md', '**/*.md'], { cwd, reject: false });
|
|
40
|
-
return stdout
|
|
41
|
-
.split('\n')
|
|
42
|
-
.map(f => f.trim())
|
|
43
|
-
.filter(f => f.endsWith('.md'));
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
return [`${cwd}/**/*.md`];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
34
|
exports.markdownLintCheck = {
|
|
50
35
|
name: 'Markdown',
|
|
51
36
|
phase: 'informational',
|
|
@@ -53,7 +38,9 @@ exports.markdownLintCheck = {
|
|
|
53
38
|
return true;
|
|
54
39
|
},
|
|
55
40
|
async run(ctx) {
|
|
56
|
-
const targets = await
|
|
41
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, ['*.md', '*.mdx']);
|
|
42
|
+
if (targets === null)
|
|
43
|
+
return (0, types_1.skip)('git unavailable');
|
|
57
44
|
if (targets.length === 0)
|
|
58
45
|
return (0, types_1.skip)('no markdown files');
|
|
59
46
|
const userConfig = loadUserMarkdownConfig(ctx.cwd);
|
|
@@ -8,8 +8,8 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const config_1 = require("../../config");
|
|
10
10
|
const utils_1 = require("../../utils");
|
|
11
|
-
const delta_1 = require("../../utils/delta");
|
|
12
11
|
const execa_1 = require("../../utils/execa");
|
|
12
|
+
const targets_1 = require("../targets");
|
|
13
13
|
const types_1 = require("../types");
|
|
14
14
|
function loadUserPrettierConfig(cwd) {
|
|
15
15
|
const configPath = node_path_1.default.join(cwd, 'pr-checkmate.json');
|
|
@@ -42,11 +42,11 @@ exports.prettierCheck = {
|
|
|
42
42
|
return ctx.languages.includes('typescript') || ctx.languages.includes('javascript');
|
|
43
43
|
},
|
|
44
44
|
async run(ctx) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
const targets = await (0, targets_1.resolveScopedTargetFiles)(ctx, ['*.ts', '*.tsx', '*.js', '*.jsx', '*.json', '*.md'], (0, config_1.getSourcePaths)());
|
|
46
|
+
if (targets === null)
|
|
47
|
+
return (0, types_1.skip)('git unavailable');
|
|
48
|
+
if (targets.length === 0)
|
|
49
|
+
return (0, types_1.pass)('no files to format');
|
|
50
50
|
const projectIgnore = node_path_1.default.join(ctx.cwd, '.prettierignore');
|
|
51
51
|
const gitignore = node_path_1.default.join(ctx.cwd, '.gitignore');
|
|
52
52
|
let tempIgnore = null;
|
|
@@ -8,8 +8,8 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const config_1 = require("../../config");
|
|
10
10
|
const utils_1 = require("../../utils");
|
|
11
|
-
const delta_1 = require("../../utils/delta");
|
|
12
11
|
const execa_1 = require("../../utils/execa");
|
|
12
|
+
const targets_1 = require("../targets");
|
|
13
13
|
const types_1 = require("../types");
|
|
14
14
|
function loadUserCspellConfig(cwd) {
|
|
15
15
|
const configPath = node_path_1.default.join(cwd, 'pr-checkmate.json');
|
|
@@ -57,20 +57,12 @@ exports.spellcheckCheck = {
|
|
|
57
57
|
return true;
|
|
58
58
|
},
|
|
59
59
|
async run(ctx) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
'.md',
|
|
67
|
-
'.mdx',
|
|
68
|
-
'.json',
|
|
69
|
-
]);
|
|
70
|
-
const patterns = changedFiles ?? [
|
|
71
|
-
node_path_1.default.join(ctx.cwd, sourcePath, '**/*.{ts,tsx,js,jsx,md,mdx,json}'),
|
|
72
|
-
...config_1.IGNORE_PATTERNS.map(p => `--exclude=${p}`),
|
|
73
|
-
];
|
|
60
|
+
const files = await (0, targets_1.resolveScopedTargetFiles)(ctx, ['*.ts', '*.tsx', '*.js', '*.jsx', '*.md', '*.mdx', '*.json'], (0, config_1.getSourcePaths)());
|
|
61
|
+
if (files === null)
|
|
62
|
+
return (0, types_1.skip)('git unavailable');
|
|
63
|
+
if (files.length === 0)
|
|
64
|
+
return (0, types_1.pass)('no files to spellcheck');
|
|
65
|
+
const patterns = [...files, ...config_1.IGNORE_PATTERNS.map(p => `--exclude=${p}`)];
|
|
74
66
|
const hasLocal = (0, config_1.hasLocalConfig)(['cspell.json', '.cspell.json', 'cspell.config.json']);
|
|
75
67
|
let tempConfigPath = null;
|
|
76
68
|
const cspellBin = (0, utils_1.resolvePackageBin)('cspell', 'bin.mjs');
|
package/dist/core/context.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.buildContext = buildContext;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
4
8
|
const loader_1 = require("../config/loader");
|
|
5
9
|
const utils_1 = require("../utils");
|
|
6
10
|
const detect_language_1 = require("../utils/detect-language");
|
|
@@ -10,6 +14,21 @@ const execa_1 = require("../utils/execa");
|
|
|
10
14
|
* Prefers explicit opts, then GitHub PR event, then Bitbucket PR env, then git HEAD^.
|
|
11
15
|
* Returns undefined SHAs when nothing usable is found → checks fall back to full scan.
|
|
12
16
|
*/
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a git ref to a concrete commit SHA, or undefined when it can't be
|
|
19
|
+
* resolved in this clone. `--verify --quiet` emits nothing (instead of echoing
|
|
20
|
+
* the literal ref) and exits non-zero for a missing ref — e.g. `HEAD^` on a
|
|
21
|
+
* single-commit repo, or a base branch not fetched in a shallow CI clone. That
|
|
22
|
+
* way an unresolvable base yields `undefined` and callers fall back to a full
|
|
23
|
+
* scan, rather than diffing against a garbage ref and silently checking nothing.
|
|
24
|
+
*/
|
|
25
|
+
async function resolveRef(cwd, ref) {
|
|
26
|
+
const { stdout } = await (0, execa_1.execa)('git', ['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], {
|
|
27
|
+
cwd,
|
|
28
|
+
reject: false,
|
|
29
|
+
});
|
|
30
|
+
return stdout.trim() || undefined;
|
|
31
|
+
}
|
|
13
32
|
async function resolveDiffRange(cwd, opts) {
|
|
14
33
|
if (opts.baseSha || opts.headSha) {
|
|
15
34
|
return { baseSha: opts.baseSha, headSha: opts.headSha };
|
|
@@ -22,17 +41,28 @@ async function resolveDiffRange(cwd, opts) {
|
|
|
22
41
|
catch {
|
|
23
42
|
return {};
|
|
24
43
|
}
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
// GitHub Actions: the PR event payload carries the base branch SHA. Read it
|
|
45
|
+
// directly so delta works without relying on any client-set variable
|
|
46
|
+
// (GITHUB_EVENT_* are injected by the runner).
|
|
47
|
+
if (process.env.GITHUB_EVENT_NAME === 'pull_request' && process.env.GITHUB_EVENT_PATH) {
|
|
48
|
+
try {
|
|
49
|
+
const event = JSON.parse(node_fs_1.default.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf-8'));
|
|
50
|
+
const eventBase = event?.pull_request?.base?.sha;
|
|
51
|
+
// Only use it if the commit is actually present in this clone; otherwise
|
|
52
|
+
// fall through so a shallow clone degrades to a full scan, not 0 files.
|
|
53
|
+
const baseSha = eventBase ? await resolveRef(cwd, eventBase) : undefined;
|
|
54
|
+
if (baseSha)
|
|
55
|
+
return { baseSha, headSha };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Malformed/unreadable event — fall through to the git-based resolution.
|
|
59
|
+
}
|
|
35
60
|
}
|
|
61
|
+
// Bitbucket exposes the PR destination branch as an env var (also runner-set).
|
|
62
|
+
const bitbucketTarget = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
|
|
63
|
+
const ref = bitbucketTarget ? `origin/${bitbucketTarget}` : 'HEAD^';
|
|
64
|
+
const baseSha = await resolveRef(cwd, ref);
|
|
65
|
+
return { baseSha, headSha };
|
|
36
66
|
}
|
|
37
67
|
/**
|
|
38
68
|
* Assemble the shared CheckContext once per run. Language detection is skipped
|
package/dist/core/targets.d.ts
CHANGED
|
@@ -9,3 +9,16 @@ import type { CheckContext } from './types';
|
|
|
9
9
|
* excluded, so every returned path exists on disk and is safe to read.
|
|
10
10
|
*/
|
|
11
11
|
export declare function resolveTargetFiles(ctx: CheckContext, globs: string[]): Promise<string[] | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Like {@link resolveTargetFiles} but restricts results to the configured
|
|
14
|
+
* `sourcePath`(s). Preserves the semantics of the old `sourcePath` full-scan
|
|
15
|
+
* fallback while gaining delta support on every platform via `ctx.baseSha`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveScopedTargetFiles(ctx: CheckContext, globs: string[], sourcePaths: string[]): Promise<string[] | null>;
|
|
18
|
+
/**
|
|
19
|
+
* List ALL tracked files matching `globs` (never delta), scoped to
|
|
20
|
+
* `sourcePath`(s). For whole-repo analyses like circular-dependency detection
|
|
21
|
+
* that need the full graph, not just the PR diff. Using `git ls-files` means
|
|
22
|
+
* untracked `node_modules` is excluded by construction.
|
|
23
|
+
*/
|
|
24
|
+
export declare function listTrackedFiles(ctx: CheckContext, globs: string[], sourcePaths: string[]): Promise<string[]>;
|
package/dist/core/targets.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveTargetFiles = resolveTargetFiles;
|
|
4
|
+
exports.resolveScopedTargetFiles = resolveScopedTargetFiles;
|
|
5
|
+
exports.listTrackedFiles = listTrackedFiles;
|
|
6
|
+
const config_1 = require("../config");
|
|
4
7
|
const utils_1 = require("../utils");
|
|
5
8
|
const execa_1 = require("../utils/execa");
|
|
9
|
+
/**
|
|
10
|
+
* Drop files that live under any excluded directory (client `ignoreDirs` or the
|
|
11
|
+
* curated default), matched on any path segment so nesting depth doesn't matter.
|
|
12
|
+
* Applied to every check's file list so build/tooling/report dirs are never
|
|
13
|
+
* scanned, even when committed.
|
|
14
|
+
*/
|
|
15
|
+
function excludeIgnoredDirs(files) {
|
|
16
|
+
const ignored = new Set((0, config_1.getIgnoredDirs)());
|
|
17
|
+
if (ignored.size === 0)
|
|
18
|
+
return files;
|
|
19
|
+
return files.filter(f => !f.split('/').some(segment => ignored.has(segment)));
|
|
20
|
+
}
|
|
6
21
|
/**
|
|
7
22
|
* Lists the files a language check should operate on, matched by pathspec `globs`
|
|
8
23
|
* (e.g. `['*.py']`). Uses the PR diff range when available (delta mode), else
|
|
@@ -26,10 +41,10 @@ async function resolveTargetFiles(ctx, globs) {
|
|
|
26
41
|
]
|
|
27
42
|
: ['ls-files', ...globs];
|
|
28
43
|
const { stdout } = await (0, execa_1.execa)('git', args, { cwd: ctx.cwd, reject: false });
|
|
29
|
-
const files = stdout
|
|
44
|
+
const files = excludeIgnoredDirs(stdout
|
|
30
45
|
.split('\n')
|
|
31
46
|
.map(f => f.trim())
|
|
32
|
-
.filter(Boolean);
|
|
47
|
+
.filter(Boolean));
|
|
33
48
|
utils_1.logger.info(`[targets]: ${ctx.baseSha ? '📂 Delta' : 'Full scan'}: ${files.length} file(s) for ${globs.join(', ')}`);
|
|
34
49
|
return files;
|
|
35
50
|
}
|
|
@@ -38,3 +53,45 @@ async function resolveTargetFiles(ctx, globs) {
|
|
|
38
53
|
return null;
|
|
39
54
|
}
|
|
40
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Restrict a list of repo-relative paths to the configured `sourcePath`(s).
|
|
58
|
+
* A source path of `.` (or one that normalizes to empty) imposes no
|
|
59
|
+
* restriction, so the whole repo is considered.
|
|
60
|
+
*/
|
|
61
|
+
function scopeToSourcePaths(files, sourcePaths) {
|
|
62
|
+
const scopes = sourcePaths.map(p => p.replace(/^\.\/?/, '').replace(/\/+$/, '')).filter(Boolean);
|
|
63
|
+
if (scopes.length === 0)
|
|
64
|
+
return files;
|
|
65
|
+
return files.filter(f => scopes.some(s => f === s || f.startsWith(`${s}/`)));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Like {@link resolveTargetFiles} but restricts results to the configured
|
|
69
|
+
* `sourcePath`(s). Preserves the semantics of the old `sourcePath` full-scan
|
|
70
|
+
* fallback while gaining delta support on every platform via `ctx.baseSha`.
|
|
71
|
+
*/
|
|
72
|
+
async function resolveScopedTargetFiles(ctx, globs, sourcePaths) {
|
|
73
|
+
const files = await resolveTargetFiles(ctx, globs);
|
|
74
|
+
if (files === null)
|
|
75
|
+
return null;
|
|
76
|
+
return scopeToSourcePaths(files, sourcePaths);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* List ALL tracked files matching `globs` (never delta), scoped to
|
|
80
|
+
* `sourcePath`(s). For whole-repo analyses like circular-dependency detection
|
|
81
|
+
* that need the full graph, not just the PR diff. Using `git ls-files` means
|
|
82
|
+
* untracked `node_modules` is excluded by construction.
|
|
83
|
+
*/
|
|
84
|
+
async function listTrackedFiles(ctx, globs, sourcePaths) {
|
|
85
|
+
try {
|
|
86
|
+
const { stdout } = await (0, execa_1.execa)('git', ['ls-files', ...globs], { cwd: ctx.cwd, reject: false });
|
|
87
|
+
const files = excludeIgnoredDirs(stdout
|
|
88
|
+
.split('\n')
|
|
89
|
+
.map(f => f.trim())
|
|
90
|
+
.filter(Boolean));
|
|
91
|
+
return scopeToSourcePaths(files, sourcePaths);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
utils_1.logger.warn(`[targets]: ⚠️ Could not list tracked files\n${err}`);
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
export { runChecks, buildContext, REGISTRY } from './core';
|
|
7
7
|
export { pass, warn, fail, skip, defineCheck } from './core';
|
|
8
8
|
export type { Check, CheckContext, CheckOutcome, CheckReport, CheckResult, CheckStatus, Language, Phase, ReporterFn, ReporterKind, RunOptions, } from './core';
|
|
9
|
-
export { getSourcePath, getPackageConfigPath, hasLocalConfig } from './config';
|
|
9
|
+
export { getSourcePath, getSourcePaths, getIgnoredDirs, getPackageConfigPath, hasLocalConfig, } from './config';
|
|
10
10
|
export type { PRCheckMateConfig } from './interfaces';
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module pr-checkmate
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.hasLocalConfig = exports.getPackageConfigPath = exports.getSourcePath = exports.defineCheck = exports.skip = exports.fail = exports.warn = exports.pass = exports.REGISTRY = exports.buildContext = exports.runChecks = void 0;
|
|
8
|
+
exports.hasLocalConfig = exports.getPackageConfigPath = exports.getIgnoredDirs = exports.getSourcePaths = exports.getSourcePath = exports.defineCheck = exports.skip = exports.fail = exports.warn = exports.pass = exports.REGISTRY = exports.buildContext = exports.runChecks = void 0;
|
|
9
9
|
// Primary programmatic API: run checks and receive a structured report.
|
|
10
10
|
// Never throws for violations, never calls process.exit — the caller decides.
|
|
11
11
|
var core_1 = require("./core");
|
|
@@ -22,5 +22,7 @@ Object.defineProperty(exports, "defineCheck", { enumerable: true, get: function
|
|
|
22
22
|
// Config helpers for consumers embedding pr-checkmate.
|
|
23
23
|
var config_1 = require("./config");
|
|
24
24
|
Object.defineProperty(exports, "getSourcePath", { enumerable: true, get: function () { return config_1.getSourcePath; } });
|
|
25
|
+
Object.defineProperty(exports, "getSourcePaths", { enumerable: true, get: function () { return config_1.getSourcePaths; } });
|
|
26
|
+
Object.defineProperty(exports, "getIgnoredDirs", { enumerable: true, get: function () { return config_1.getIgnoredDirs; } });
|
|
25
27
|
Object.defineProperty(exports, "getPackageConfigPath", { enumerable: true, get: function () { return config_1.getPackageConfigPath; } });
|
|
26
28
|
Object.defineProperty(exports, "hasLocalConfig", { enumerable: true, get: function () { return config_1.hasLocalConfig; } });
|
package/dist/init.js
CHANGED
|
@@ -10,19 +10,33 @@ const inquirer_1 = __importDefault(require("inquirer"));
|
|
|
10
10
|
const config_1 = require("./config");
|
|
11
11
|
const utils_1 = require("./utils");
|
|
12
12
|
const CONFIG_FILE = path_1.default.resolve(process.cwd(), 'pr-checkmate.json');
|
|
13
|
+
/**
|
|
14
|
+
* Load the canonical default ESLint rules (the same set eslint.config.mjs
|
|
15
|
+
* applies) so the generated pr-checkmate.json lists every rule for the user to
|
|
16
|
+
* change or disable. Falls back to an empty object if the file is unavailable.
|
|
17
|
+
*/
|
|
18
|
+
function loadDefaultEslintRules() {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(fs_1.default.readFileSync((0, config_1.getPackageConfigPath)('eslint-default-rules.json'), 'utf-8'));
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
utils_1.logger.warn(`[init]: ⚠️ Could not load default ESLint rules: ${err}`);
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
13
27
|
/**
|
|
14
28
|
* Default configuration structure with customization options
|
|
15
29
|
*/
|
|
16
30
|
const DEFAULT_CONFIG = {
|
|
17
|
-
sourcePath: '
|
|
31
|
+
sourcePath: '.',
|
|
32
|
+
// Directories excluded from every check (matched on any path segment). Edit
|
|
33
|
+
// freely — add your own generated/vendored dirs or remove ones you want scanned.
|
|
34
|
+
ignoreDirs: config_1.IGNORED_DIRS,
|
|
18
35
|
commands: config_1.DEFAULT_COMMANDS,
|
|
19
|
-
// ESLint
|
|
36
|
+
// ESLint — the full default rule set is written out so every rule is visible
|
|
37
|
+
// and editable. Change any value, or set a rule to "off" to disable it.
|
|
20
38
|
eslint: {
|
|
21
|
-
rules:
|
|
22
|
-
// Off by default: its `--fix` injects empty `/** */` JSDoc stubs into
|
|
23
|
-
// client source files. Set to 'warn'/'error' to opt into doc enforcement.
|
|
24
|
-
'jsdoc/require-jsdoc': 'off',
|
|
25
|
-
},
|
|
39
|
+
rules: loadDefaultEslintRules(),
|
|
26
40
|
ignorePatterns: [],
|
|
27
41
|
},
|
|
28
42
|
// Prettier customization
|
|
@@ -90,7 +104,8 @@ async function promptSourcePath() {
|
|
|
90
104
|
type: 'list',
|
|
91
105
|
name: 'sourcePath',
|
|
92
106
|
message: 'Where is your project source code?',
|
|
93
|
-
|
|
107
|
+
default: '.',
|
|
108
|
+
choices: [{ name: '. (entire repo — recommended)', value: '.' }, 'src', 'app', MANUAL_ENTRY],
|
|
94
109
|
},
|
|
95
110
|
{
|
|
96
111
|
type: 'input',
|
|
@@ -21,7 +21,8 @@ export interface CspellConfig {
|
|
|
21
21
|
ignoreRegExpList: string[];
|
|
22
22
|
}
|
|
23
23
|
export interface PRCheckMateConfig {
|
|
24
|
-
sourcePath: string;
|
|
24
|
+
sourcePath: string | string[];
|
|
25
|
+
ignoreDirs?: string[];
|
|
25
26
|
languages?: Language[];
|
|
26
27
|
lint?: {
|
|
27
28
|
rules?: Record<string, any>;
|
|
@@ -7,7 +7,11 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
const utils_1 = require("../utils");
|
|
9
9
|
const configFiles = [
|
|
10
|
-
|
|
10
|
+
// Source is named so ESLint's auto-discovery does NOT treat it as a nested
|
|
11
|
+
// config for files under src/config/ — it ships to the package root as the
|
|
12
|
+
// canonical `eslint.config.mjs`.
|
|
13
|
+
{ source: 'src/config/eslint-flat-config.mjs', dest: 'eslint.config.mjs' },
|
|
14
|
+
{ source: 'src/config/eslint-default-rules.json', dest: 'eslint-default-rules.json' },
|
|
11
15
|
{ source: 'src/config/.eslintrc.js', dest: '.eslintrc.js' },
|
|
12
16
|
{ source: 'src/config/.prettierrc', dest: '.prettierrc' },
|
|
13
17
|
{ source: 'src/config/cspell.json', dest: 'cspell.json' },
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -21,7 +21,6 @@ exports.cleanupTempFile = cleanupTempFile;
|
|
|
21
21
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
22
22
|
const logger_1 = require("./logger");
|
|
23
23
|
__exportStar(require("./logger"), exports);
|
|
24
|
-
__exportStar(require("./delta"), exports);
|
|
25
24
|
__exportStar(require("./result-collector"), exports);
|
|
26
25
|
__exportStar(require("./resolve-bin"), exports);
|
|
27
26
|
__exportStar(require("./own-deps"), exports);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"no-console": "warn",
|
|
3
|
+
"no-debugger": "error",
|
|
4
|
+
"prefer-const": "error",
|
|
5
|
+
"eqeqeq": ["error", "always"],
|
|
6
|
+
"@typescript-eslint/no-unused-vars": [
|
|
7
|
+
"error",
|
|
8
|
+
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
|
|
9
|
+
],
|
|
10
|
+
"@typescript-eslint/explicit-function-return-type": ["warn"],
|
|
11
|
+
"@typescript-eslint/no-explicit-any": ["warn"],
|
|
12
|
+
"@typescript-eslint/no-floating-promises": ["error"],
|
|
13
|
+
"space-before-function-paren": "off",
|
|
14
|
+
"operator-linebreak": "off",
|
|
15
|
+
"max-len": ["warn", { "code": 100 }],
|
|
16
|
+
"semi": ["error", "always"],
|
|
17
|
+
"quotes": ["error", "single", { "avoidEscape": true }],
|
|
18
|
+
"object-curly-spacing": ["error", "always"],
|
|
19
|
+
"space-infix-ops": "error",
|
|
20
|
+
"keyword-spacing": ["error", { "before": true, "after": true }],
|
|
21
|
+
"padding-line-between-statements": [
|
|
22
|
+
"error",
|
|
23
|
+
{ "blankLine": "always", "prev": "block", "next": "*" },
|
|
24
|
+
{ "blankLine": "always", "prev": "*", "next": "return" }
|
|
25
|
+
],
|
|
26
|
+
"sonarjs/cognitive-complexity": ["warn", 30],
|
|
27
|
+
"sonarjs/no-duplicate-string": ["warn", { "threshold": 3 }],
|
|
28
|
+
"sonarjs/no-identical-functions": "warn",
|
|
29
|
+
"import/order": [
|
|
30
|
+
"warn",
|
|
31
|
+
{
|
|
32
|
+
"groups": ["builtin", "external", "internal", "parent", "sibling", "index"],
|
|
33
|
+
"alphabetize": { "order": "asc", "caseInsensitive": true }
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"import/no-duplicates": "error",
|
|
37
|
+
"jsdoc/require-jsdoc": [
|
|
38
|
+
"warn",
|
|
39
|
+
{
|
|
40
|
+
"publicOnly": true,
|
|
41
|
+
"require": {
|
|
42
|
+
"FunctionDeclaration": true,
|
|
43
|
+
"MethodDefinition": true,
|
|
44
|
+
"ClassDeclaration": true,
|
|
45
|
+
"ArrowFunctionExpression": false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"jsdoc/check-param-names": "warn",
|
|
50
|
+
"jsdoc/check-tag-names": "warn",
|
|
51
|
+
"jsdoc/check-types": "warn",
|
|
52
|
+
"prettier/prettier": [
|
|
53
|
+
"error",
|
|
54
|
+
{
|
|
55
|
+
"semi": true,
|
|
56
|
+
"singleQuote": true,
|
|
57
|
+
"bracketSpacing": true,
|
|
58
|
+
"arrowParens": "avoid",
|
|
59
|
+
"printWidth": 100
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
package/eslint.config.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import { createRequire } from 'module';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
|
4
5
|
import tsParser from '@typescript-eslint/parser';
|
|
@@ -8,73 +9,12 @@ import importPlugin from 'eslint-plugin-import-x';
|
|
|
8
9
|
import jsdocPlugin from 'eslint-plugin-jsdoc';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* Default ESLint rules
|
|
12
|
+
* Default ESLint rules — single source of truth, shared with init.ts so the
|
|
13
|
+
* pr-checkmate.json it generates lists exactly the rules applied here. Edit the
|
|
14
|
+
* rules in `eslint-default-rules.json` (shipped alongside this file).
|
|
12
15
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'no-debugger': 'error',
|
|
16
|
-
'prefer-const': 'error',
|
|
17
|
-
'eqeqeq': ['error', 'always'],
|
|
18
|
-
'@typescript-eslint/no-unused-vars': [
|
|
19
|
-
'error',
|
|
20
|
-
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
21
|
-
],
|
|
22
|
-
'@typescript-eslint/explicit-function-return-type': ['warn'],
|
|
23
|
-
'@typescript-eslint/no-explicit-any': ['warn'],
|
|
24
|
-
'@typescript-eslint/no-floating-promises': ['error'],
|
|
25
|
-
'space-before-function-paren': 'off',
|
|
26
|
-
'operator-linebreak': 'off',
|
|
27
|
-
'max-len': ['warn', { code: 100 }],
|
|
28
|
-
'semi': ['error', 'always'],
|
|
29
|
-
'quotes': ['error', 'single', { avoidEscape: true }],
|
|
30
|
-
'object-curly-spacing': ['error', 'always'],
|
|
31
|
-
'space-infix-ops': 'error',
|
|
32
|
-
'keyword-spacing': ['error', { before: true, after: true }],
|
|
33
|
-
'padding-line-between-statements': [
|
|
34
|
-
'error',
|
|
35
|
-
{ blankLine: 'always', prev: 'block', next: '*' },
|
|
36
|
-
{ blankLine: 'always', prev: '*', next: 'return' },
|
|
37
|
-
],
|
|
38
|
-
// Complexity analysis (#61)
|
|
39
|
-
'sonarjs/cognitive-complexity': ['warn', 30],
|
|
40
|
-
'sonarjs/no-duplicate-string': ['warn', { threshold: 3 }],
|
|
41
|
-
'sonarjs/no-identical-functions': 'warn',
|
|
42
|
-
// Import ordering (#65)
|
|
43
|
-
'import/order': [
|
|
44
|
-
'warn',
|
|
45
|
-
{
|
|
46
|
-
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
|
|
47
|
-
alphabetize: { order: 'asc', caseInsensitive: true },
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
'import/no-duplicates': 'error',
|
|
51
|
-
// Documentation (#62)
|
|
52
|
-
'jsdoc/require-jsdoc': [
|
|
53
|
-
'warn',
|
|
54
|
-
{
|
|
55
|
-
publicOnly: true,
|
|
56
|
-
require: {
|
|
57
|
-
FunctionDeclaration: true,
|
|
58
|
-
MethodDefinition: true,
|
|
59
|
-
ClassDeclaration: true,
|
|
60
|
-
ArrowFunctionExpression: false,
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
'jsdoc/check-param-names': 'warn',
|
|
65
|
-
'jsdoc/check-tag-names': 'warn',
|
|
66
|
-
'jsdoc/check-types': 'warn',
|
|
67
|
-
'prettier/prettier': [
|
|
68
|
-
'error',
|
|
69
|
-
{
|
|
70
|
-
semi: true,
|
|
71
|
-
singleQuote: true,
|
|
72
|
-
bracketSpacing: true,
|
|
73
|
-
arrowParens: 'avoid',
|
|
74
|
-
printWidth: 100,
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
};
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
export const defaultRules = require('./eslint-default-rules.json');
|
|
78
18
|
|
|
79
19
|
/**
|
|
80
20
|
* Default ignore patterns
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr-checkmate",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.8",
|
|
4
4
|
"description": "Automated PR quality checks: linting, formatting, dependency analysis, and spellcheck",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-actions",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"README.md",
|
|
30
30
|
"scripts/copy-configs.js",
|
|
31
31
|
"eslint.config.mjs",
|
|
32
|
+
"eslint-default-rules.json",
|
|
32
33
|
".eslintrc.js",
|
|
33
34
|
".prettierrc",
|
|
34
35
|
"cspell.json",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"no-console": "warn",
|
|
3
|
+
"no-debugger": "error",
|
|
4
|
+
"prefer-const": "error",
|
|
5
|
+
"eqeqeq": ["error", "always"],
|
|
6
|
+
"@typescript-eslint/no-unused-vars": [
|
|
7
|
+
"error",
|
|
8
|
+
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
|
|
9
|
+
],
|
|
10
|
+
"@typescript-eslint/explicit-function-return-type": ["warn"],
|
|
11
|
+
"@typescript-eslint/no-explicit-any": ["warn"],
|
|
12
|
+
"@typescript-eslint/no-floating-promises": ["error"],
|
|
13
|
+
"space-before-function-paren": "off",
|
|
14
|
+
"operator-linebreak": "off",
|
|
15
|
+
"max-len": ["warn", { "code": 100 }],
|
|
16
|
+
"semi": ["error", "always"],
|
|
17
|
+
"quotes": ["error", "single", { "avoidEscape": true }],
|
|
18
|
+
"object-curly-spacing": ["error", "always"],
|
|
19
|
+
"space-infix-ops": "error",
|
|
20
|
+
"keyword-spacing": ["error", { "before": true, "after": true }],
|
|
21
|
+
"padding-line-between-statements": [
|
|
22
|
+
"error",
|
|
23
|
+
{ "blankLine": "always", "prev": "block", "next": "*" },
|
|
24
|
+
{ "blankLine": "always", "prev": "*", "next": "return" }
|
|
25
|
+
],
|
|
26
|
+
"sonarjs/cognitive-complexity": ["warn", 30],
|
|
27
|
+
"sonarjs/no-duplicate-string": ["warn", { "threshold": 3 }],
|
|
28
|
+
"sonarjs/no-identical-functions": "warn",
|
|
29
|
+
"import/order": [
|
|
30
|
+
"warn",
|
|
31
|
+
{
|
|
32
|
+
"groups": ["builtin", "external", "internal", "parent", "sibling", "index"],
|
|
33
|
+
"alphabetize": { "order": "asc", "caseInsensitive": true }
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"import/no-duplicates": "error",
|
|
37
|
+
"jsdoc/require-jsdoc": [
|
|
38
|
+
"warn",
|
|
39
|
+
{
|
|
40
|
+
"publicOnly": true,
|
|
41
|
+
"require": {
|
|
42
|
+
"FunctionDeclaration": true,
|
|
43
|
+
"MethodDefinition": true,
|
|
44
|
+
"ClassDeclaration": true,
|
|
45
|
+
"ArrowFunctionExpression": false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"jsdoc/check-param-names": "warn",
|
|
50
|
+
"jsdoc/check-tag-names": "warn",
|
|
51
|
+
"jsdoc/check-types": "warn",
|
|
52
|
+
"prettier/prettier": [
|
|
53
|
+
"error",
|
|
54
|
+
{
|
|
55
|
+
"semi": true,
|
|
56
|
+
"singleQuote": true,
|
|
57
|
+
"bracketSpacing": true,
|
|
58
|
+
"arrowParens": "avoid",
|
|
59
|
+
"printWidth": 100
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
package/dist/utils/delta.d.ts
DELETED
package/dist/utils/delta.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getChangedFiles = getChangedFiles;
|
|
7
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const execa_1 = require("./execa");
|
|
10
|
-
const logger_1 = require("./logger");
|
|
11
|
-
/**
|
|
12
|
-
* Returns list of changed files in the current PR filtered by extension.
|
|
13
|
-
* Returns null when not in a GitHub PR context (falls back to full-project scan).
|
|
14
|
-
*/
|
|
15
|
-
async function getChangedFiles(extensions) {
|
|
16
|
-
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
17
|
-
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
18
|
-
if (eventName !== 'pull_request' || !eventPath || !node_fs_1.default.existsSync(eventPath)) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
let baseSha = null;
|
|
22
|
-
try {
|
|
23
|
-
const event = JSON.parse(node_fs_1.default.readFileSync(eventPath, 'utf-8'));
|
|
24
|
-
baseSha = event?.pull_request?.base?.sha ?? null;
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
if (!baseSha)
|
|
30
|
-
return null;
|
|
31
|
-
try {
|
|
32
|
-
const { stdout } = await (0, execa_1.execa)('git', [
|
|
33
|
-
'diff',
|
|
34
|
-
'--name-only',
|
|
35
|
-
'--diff-filter=ACMR',
|
|
36
|
-
baseSha,
|
|
37
|
-
'HEAD',
|
|
38
|
-
]);
|
|
39
|
-
const files = stdout
|
|
40
|
-
.split('\n')
|
|
41
|
-
.map(f => f.trim())
|
|
42
|
-
.filter(f => f && extensions.some(ext => f.endsWith(ext)))
|
|
43
|
-
.map(f => node_path_1.default.join(process.cwd(), f));
|
|
44
|
-
if (files.length === 0)
|
|
45
|
-
return null;
|
|
46
|
-
logger_1.logger.info(`[delta]: 📂 Delta mode: checking ${files.length} changed file(s)`);
|
|
47
|
-
return files;
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
logger_1.logger.warn(`[delta]: ⚠️ Could not get changed files, falling back to full scan\n${err}`);
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
|
4
|
-
import tsParser from '@typescript-eslint/parser';
|
|
5
|
-
import prettierPlugin from 'eslint-plugin-prettier';
|
|
6
|
-
import sonarjsPlugin from 'eslint-plugin-sonarjs';
|
|
7
|
-
import importPlugin from 'eslint-plugin-import-x';
|
|
8
|
-
import jsdocPlugin from 'eslint-plugin-jsdoc';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Default ESLint rules - can be exported for use in init.ts
|
|
12
|
-
*/
|
|
13
|
-
export const defaultRules = {
|
|
14
|
-
'no-console': 'warn',
|
|
15
|
-
'no-debugger': 'error',
|
|
16
|
-
'prefer-const': 'error',
|
|
17
|
-
'eqeqeq': ['error', 'always'],
|
|
18
|
-
'@typescript-eslint/no-unused-vars': [
|
|
19
|
-
'error',
|
|
20
|
-
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
21
|
-
],
|
|
22
|
-
'@typescript-eslint/explicit-function-return-type': ['warn'],
|
|
23
|
-
'@typescript-eslint/no-explicit-any': ['warn'],
|
|
24
|
-
'@typescript-eslint/no-floating-promises': ['error'],
|
|
25
|
-
'space-before-function-paren': 'off',
|
|
26
|
-
'operator-linebreak': 'off',
|
|
27
|
-
'max-len': ['warn', { code: 100 }],
|
|
28
|
-
'semi': ['error', 'always'],
|
|
29
|
-
'quotes': ['error', 'single', { avoidEscape: true }],
|
|
30
|
-
'object-curly-spacing': ['error', 'always'],
|
|
31
|
-
'space-infix-ops': 'error',
|
|
32
|
-
'keyword-spacing': ['error', { before: true, after: true }],
|
|
33
|
-
'padding-line-between-statements': [
|
|
34
|
-
'error',
|
|
35
|
-
{ blankLine: 'always', prev: 'block', next: '*' },
|
|
36
|
-
{ blankLine: 'always', prev: '*', next: 'return' },
|
|
37
|
-
],
|
|
38
|
-
// Complexity analysis (#61)
|
|
39
|
-
'sonarjs/cognitive-complexity': ['warn', 30],
|
|
40
|
-
'sonarjs/no-duplicate-string': ['warn', { threshold: 3 }],
|
|
41
|
-
'sonarjs/no-identical-functions': 'warn',
|
|
42
|
-
// Import ordering (#65)
|
|
43
|
-
'import/order': [
|
|
44
|
-
'warn',
|
|
45
|
-
{
|
|
46
|
-
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
|
|
47
|
-
alphabetize: { order: 'asc', caseInsensitive: true },
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
'import/no-duplicates': 'error',
|
|
51
|
-
// Documentation (#62)
|
|
52
|
-
'jsdoc/require-jsdoc': [
|
|
53
|
-
'warn',
|
|
54
|
-
{
|
|
55
|
-
publicOnly: true,
|
|
56
|
-
require: {
|
|
57
|
-
FunctionDeclaration: true,
|
|
58
|
-
MethodDefinition: true,
|
|
59
|
-
ClassDeclaration: true,
|
|
60
|
-
ArrowFunctionExpression: false,
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
'jsdoc/check-param-names': 'warn',
|
|
65
|
-
'jsdoc/check-tag-names': 'warn',
|
|
66
|
-
'jsdoc/check-types': 'warn',
|
|
67
|
-
'prettier/prettier': [
|
|
68
|
-
'error',
|
|
69
|
-
{
|
|
70
|
-
semi: true,
|
|
71
|
-
singleQuote: true,
|
|
72
|
-
bracketSpacing: true,
|
|
73
|
-
arrowParens: 'avoid',
|
|
74
|
-
printWidth: 100,
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Default ignore patterns
|
|
81
|
-
*/
|
|
82
|
-
const defaultIgnores = [
|
|
83
|
-
'**/node_modules/**',
|
|
84
|
-
'**/dist/**',
|
|
85
|
-
'**/.git/**',
|
|
86
|
-
'**/build/**',
|
|
87
|
-
'**/coverage/**',
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Load user's pr-checkmate.json from project root
|
|
92
|
-
* Works both when developing the package and when installed in node_modules
|
|
93
|
-
*/
|
|
94
|
-
function loadUserConfig() {
|
|
95
|
-
let currentDir = process.cwd();
|
|
96
|
-
let configPath = path.join(currentDir, 'pr-checkmate.json');
|
|
97
|
-
|
|
98
|
-
// If running from node_modules, find project root
|
|
99
|
-
if (currentDir.includes('node_modules')) {
|
|
100
|
-
const parts = currentDir.split(path.sep);
|
|
101
|
-
const nodeModulesIndex = parts.lastIndexOf('node_modules');
|
|
102
|
-
if (nodeModulesIndex > 0) {
|
|
103
|
-
currentDir = parts.slice(0, nodeModulesIndex).join(path.sep);
|
|
104
|
-
configPath = path.join(currentDir, 'pr-checkmate.json');
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (fs.existsSync(configPath)) {
|
|
109
|
-
try {
|
|
110
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
111
|
-
console.error(`[loadUserConfig]: ✅ Loaded user config from: ${configPath}`);
|
|
112
|
-
return config;
|
|
113
|
-
} catch (err) {
|
|
114
|
-
console.warn(`[loadUserConfig]: ⚠️ Failed to parse ${configPath}:`, err.message);
|
|
115
|
-
}
|
|
116
|
-
} else {
|
|
117
|
-
console.error(`[loadUserConfig]: No pr-checkmate.json found, using defaults`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return { sourcePath: 'src', eslint: {} };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const userConfig = loadUserConfig();
|
|
124
|
-
const SRC = userConfig.sourcePath || 'src';
|
|
125
|
-
|
|
126
|
-
// Merge default rules with user-defined rules
|
|
127
|
-
const mergedRules = {
|
|
128
|
-
...defaultRules,
|
|
129
|
-
...(userConfig.eslint?.rules || {}),
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Merge default ignores with user-defined ignores
|
|
133
|
-
const mergedIgnores = [
|
|
134
|
-
...defaultIgnores,
|
|
135
|
-
'**/.eslintrc.js',
|
|
136
|
-
'**/eslint.config.mjs',
|
|
137
|
-
...(userConfig.eslint?.ignorePatterns || []),
|
|
138
|
-
];
|
|
139
|
-
|
|
140
|
-
// Log customization info
|
|
141
|
-
const customRulesCount = Object.keys(userConfig.eslint?.rules || {}).length;
|
|
142
|
-
const customIgnoresCount = (userConfig.eslint?.ignorePatterns || []).length;
|
|
143
|
-
|
|
144
|
-
if (customRulesCount > 0) {
|
|
145
|
-
console.error(`Applied ${customRulesCount} custom ESLint rules`);
|
|
146
|
-
}
|
|
147
|
-
if (customIgnoresCount > 0) {
|
|
148
|
-
console.error(`🚫 Applied ${customIgnoresCount} custom ignore patterns`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
console.error(`ESLint checking: ${SRC}/**/*.{ts,tsx,js,jsx}`);
|
|
152
|
-
|
|
153
|
-
export default [
|
|
154
|
-
// Global ignores must be in a separate config object
|
|
155
|
-
{
|
|
156
|
-
ignores: mergedIgnores,
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
files: [`${SRC}/**/*.{ts,tsx,js,jsx}`],
|
|
160
|
-
|
|
161
|
-
languageOptions: {
|
|
162
|
-
parser: tsParser,
|
|
163
|
-
parserOptions: {
|
|
164
|
-
ecmaVersion: 2020,
|
|
165
|
-
sourceType: 'module',
|
|
166
|
-
project: './tsconfig.json',
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
plugins: {
|
|
171
|
-
'@typescript-eslint': tsPlugin,
|
|
172
|
-
prettier: prettierPlugin,
|
|
173
|
-
sonarjs: sonarjsPlugin,
|
|
174
|
-
import: importPlugin,
|
|
175
|
-
jsdoc: jsdocPlugin,
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
rules: mergedRules,
|
|
179
|
-
},
|
|
180
|
-
];
|