pr-checkmate 1.19.3 → 1.19.5

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/README.md CHANGED
@@ -1,8 +1,7 @@
1
- # 🔍 PR CheckMate
1
+ # 🔍 PR CheckMate â™Ÿī¸
2
2
  > **PR CheckMate** is an npm package for automating Pull Request checks.
3
3
  > It helps teams maintain code quality by automatically validating linting, formatting, dependencies, and spelling before merging to `main`.
4
4
 
5
-
6
5
  ## ⚡ Why PR CheckMate?
7
6
  * ✅ Automatic checks on every PR without extra setup.
8
7
  * ✅ No need to install ESLint, Prettier, or cspell in your project — all included.
@@ -3,11 +3,10 @@ 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.SOURCE_PATH = exports.config = exports.CONFIG_FILE = void 0;
6
+ exports.SOURCE_PATH = exports.config = void 0;
7
+ const constants_1 = require("./constants");
7
8
  const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- exports.CONFIG_FILE = node_path_1.default.resolve(process.cwd(), 'pr-checkmate.json');
10
- exports.config = node_fs_1.default.existsSync(exports.CONFIG_FILE)
11
- ? JSON.parse(node_fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8'))
9
+ exports.config = node_fs_1.default.existsSync(constants_1.CONFIG_FILE)
10
+ ? JSON.parse(node_fs_1.default.readFileSync(constants_1.CONFIG_FILE, 'utf-8'))
12
11
  : { sourcePath: 'src' };
13
12
  exports.SOURCE_PATH = exports.config.sourcePath;
@@ -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
- exports.DEFAULT_COMMANDS = exports.SOURCE_EXTENSIONS = exports.CONFIG_FILE_PATTERNS = exports.IGNORE_PATTERNS = void 0;
6
+ exports.CONFIG_FILE = exports.documentation = exports.DEFAULT_COMMANDS = exports.SOURCE_EXTENSIONS = exports.CONFIG_FILE_PATTERNS = exports.IGNORE_PATTERNS = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
4
8
  /**
5
9
  * Common patterns to ignore in all checks
6
10
  */
@@ -81,3 +85,10 @@ exports.DEFAULT_COMMANDS = {
81
85
  'npx pr-checkmate security': 'Security scan',
82
86
  'npx pr-checkmate spellcheck': 'Run spellcheck via cspell',
83
87
  };
88
+ exports.documentation = {
89
+ eslint: 'https://eslint.org/docs/latest/rules/',
90
+ typescriptEslint: 'https://typescript-eslint.io/rules/',
91
+ prettier: 'https://prettier.io/docs/en/options.html',
92
+ cspell: 'https://cspell.org/configuration/',
93
+ };
94
+ exports.CONFIG_FILE = node_path_1.default.resolve(process.cwd(), 'pr-checkmate.json');
@@ -17,27 +17,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.SOURCE_PATH = exports.config = exports.CONFIG_FILE = void 0;
20
+ exports.SOURCE_PATH = exports.config = exports.loadProjectConfig = void 0;
21
21
  exports.getSourcePath = getSourcePath;
22
22
  exports.getPackageConfigPath = getPackageConfigPath;
23
23
  exports.hasLocalConfig = hasLocalConfig;
24
24
  const node_fs_1 = __importDefault(require("node:fs"));
25
25
  const node_path_1 = __importDefault(require("node:path"));
26
26
  const utils_1 = require("../utils");
27
+ const constants_1 = require("./constants");
27
28
  __exportStar(require("./constants"), exports);
28
- exports.CONFIG_FILE = node_path_1.default.resolve(process.cwd(), 'pr-checkmate.json');
29
+ var loader_1 = require("../config/loader");
30
+ Object.defineProperty(exports, "loadProjectConfig", { enumerable: true, get: function () { return loader_1.loadProjectConfig; } });
29
31
  /**
30
32
  * Load configuration dynamically (always reads fresh config)
31
33
  */
32
34
  function loadConfig() {
33
35
  try {
34
- if (node_fs_1.default.existsSync(exports.CONFIG_FILE)) {
35
- const configContent = node_fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8');
36
+ if (node_fs_1.default.existsSync(constants_1.CONFIG_FILE)) {
37
+ const configContent = node_fs_1.default.readFileSync(constants_1.CONFIG_FILE, 'utf-8');
36
38
  return JSON.parse(configContent);
37
39
  }
38
40
  }
39
41
  catch (err) {
40
- utils_1.logger.warn(`âš ī¸ Failed to read config from ${exports.CONFIG_FILE}, using defaults${err}`);
42
+ utils_1.logger.warn(`âš ī¸ Failed to read config from ${constants_1.CONFIG_FILE}, using defaults${err}`);
41
43
  }
42
44
  return { sourcePath: 'src' };
43
45
  }
@@ -0,0 +1,118 @@
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.DEFAULT_CONFIG = void 0;
7
+ exports.loadProjectConfig = loadProjectConfig;
8
+ exports.getDefaultConfig = getDefaultConfig;
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const utils_1 = require("../utils");
12
+ /**
13
+ * Load project configuration from pr-checkmate.json
14
+ * Falls back to defaults if not found
15
+ */
16
+ function loadProjectConfig() {
17
+ const projectRoot = process.cwd();
18
+ const configPath = node_path_1.default.join(projectRoot, 'pr-checkmate.json');
19
+ if (node_fs_1.default.existsSync(configPath)) {
20
+ try {
21
+ const content = node_fs_1.default.readFileSync(configPath, 'utf-8');
22
+ const config = JSON.parse(content);
23
+ return mergeWithDefaults(config);
24
+ }
25
+ catch (err) {
26
+ utils_1.logger.warn(`[loadProjectConfig]: âš ī¸ Failed to parse pr-checkmate.json: ${err}`);
27
+ }
28
+ }
29
+ return getDefaultConfig();
30
+ }
31
+ /**
32
+ * Deep merge user config with defaults
33
+ */
34
+ function mergeWithDefaults(userConfig) {
35
+ const defaults = getDefaultConfig();
36
+ return {
37
+ sourcePath: userConfig.sourcePath || defaults.sourcePath,
38
+ lint: {
39
+ rules: { ...defaults.lint?.rules, ...userConfig.lint?.rules },
40
+ ignorePatterns: [
41
+ ...(defaults.lint?.ignorePatterns || []),
42
+ ...(userConfig.lint?.ignorePatterns || []),
43
+ ],
44
+ },
45
+ prettier: {
46
+ config: { ...defaults.prettier?.config, ...userConfig.prettier?.config },
47
+ },
48
+ spellcheck: {
49
+ words: [...(userConfig.spellcheck?.words || [])],
50
+ ignoreWords: [...(userConfig.spellcheck?.ignoreWords || [])],
51
+ ignorePaths: [
52
+ ...(defaults.spellcheck?.ignorePaths || []),
53
+ ...(userConfig.spellcheck?.ignorePaths || []),
54
+ ],
55
+ },
56
+ security: userConfig.security || defaults.security,
57
+ };
58
+ }
59
+ /**
60
+ * Get default configuration
61
+ */
62
+ function getDefaultConfig() {
63
+ return {
64
+ sourcePath: 'src',
65
+ lint: {
66
+ rules: {
67
+ 'no-console': 'warn',
68
+ 'no-debugger': 'error',
69
+ 'prefer-const': 'error',
70
+ eqeqeq: ['error', 'always'],
71
+ '@typescript-eslint/no-unused-vars': [
72
+ 'error',
73
+ { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
74
+ ],
75
+ '@typescript-eslint/explicit-function-return-type': ['warn'],
76
+ '@typescript-eslint/no-explicit-any': ['warn'],
77
+ '@typescript-eslint/no-floating-promises': ['error'],
78
+ 'max-len': ['warn', { code: 100 }],
79
+ semi: ['error', 'always'],
80
+ quotes: ['error', 'single', { avoidEscape: true }],
81
+ 'object-curly-spacing': ['error', 'always'],
82
+ 'keyword-spacing': ['error', { before: true, after: true }],
83
+ },
84
+ ignorePatterns: [
85
+ '**/node_modules/**',
86
+ '**/dist/**',
87
+ '**/.git/**',
88
+ '**/build/**',
89
+ '**/coverage/**',
90
+ '**/.next/**',
91
+ ],
92
+ },
93
+ prettier: {
94
+ config: {
95
+ semi: true,
96
+ singleQuote: true,
97
+ trailingComma: 'all',
98
+ bracketSpacing: true,
99
+ arrowParens: 'avoid',
100
+ printWidth: 100,
101
+ tabWidth: 2,
102
+ },
103
+ },
104
+ spellcheck: {
105
+ words: [],
106
+ ignoreWords: [],
107
+ ignorePaths: ['**/node_modules/**', '**/dist/**', '**/.git/**', '**/build/**'],
108
+ },
109
+ security: {
110
+ gitleaks: { enabled: true },
111
+ 'npm-audit': { enabled: true, auditLevel: 'moderate' },
112
+ },
113
+ };
114
+ }
115
+ /**
116
+ * Export config for external use
117
+ */
118
+ exports.DEFAULT_CONFIG = getDefaultConfig();
package/dist/init.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
5
  };
@@ -11,6 +12,29 @@ const inquirer_1 = __importDefault(require("inquirer"));
11
12
  const config_1 = require("./config");
12
13
  const utils_1 = require("./utils");
13
14
  exports.CONFIG_FILE = path_1.default.resolve(process.cwd(), 'pr-checkmate.json');
15
+ /**
16
+ * Default configuration structure with customization options
17
+ */
18
+ const DEFAULT_CONFIG = {
19
+ sourcePath: 'src',
20
+ commands: config_1.DEFAULT_COMMANDS,
21
+ // ESLint customization
22
+ eslint: {
23
+ rules: {},
24
+ ignorePatterns: [],
25
+ },
26
+ // Prettier customization
27
+ prettier: {
28
+ rules: {},
29
+ },
30
+ // Cspell customization
31
+ cspell: {
32
+ words: [],
33
+ ignorePaths: [],
34
+ ignoreRegExpList: [],
35
+ },
36
+ documentation: config_1.documentation,
37
+ };
14
38
  async function init() {
15
39
  const root = process.cwd();
16
40
  if (!fs_1.default.existsSync(exports.CONFIG_FILE)) {
@@ -25,18 +49,19 @@ async function init() {
25
49
  type: 'input',
26
50
  name: 'manualPath',
27
51
  message: 'Enter path:',
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
52
  when: (a) => a.sourcePath === 'Enter manually',
30
53
  validate: (input) => input.trim().length > 0 || 'Cannot be empty',
31
54
  },
32
55
  ]);
33
56
  const sourcePath = answers.sourcePath === 'Enter manually' ? answers.manualPath : answers.sourcePath;
34
57
  const config = {
58
+ ...DEFAULT_CONFIG,
35
59
  sourcePath,
36
- commands: config_1.DEFAULT_COMMANDS,
37
60
  };
38
61
  fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(config, null, 2));
39
62
  utils_1.logger.info(`[init]: ✅ Created pr-checkmate.json with sourcePath: ${sourcePath}`);
63
+ utils_1.logger.info(`[init]: 💡 You can now customize ESLint rules, Prettier settings,
64
+ and cspell words in pr-checkmate.json`);
40
65
  const tsconfigPath = path_1.default.join(root, 'tsconfig.json');
41
66
  if (!fs_1.default.existsSync(tsconfigPath)) {
42
67
  const newTsConfig = {
@@ -61,19 +86,24 @@ async function init() {
61
86
  }
62
87
  }
63
88
  else {
64
- utils_1.logger.info('⚡ pr-checkmate.json already exists — updating it.');
89
+ utils_1.logger.info('[init]: pr-checkmate.json already exists — updating it.');
65
90
  try {
66
91
  const raw = fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8');
67
92
  const json = JSON.parse(raw);
68
- if (!json.commands) {
69
- json.commands = config_1.DEFAULT_COMMANDS;
70
- }
71
- fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(json, null, 2));
72
- utils_1.logger.info('[init]: 🔧 Updated pr-checkmate.json with jobs + commands.');
93
+ // Merge with defaults to add new fields
94
+ const updated = {
95
+ ...DEFAULT_CONFIG,
96
+ ...json,
97
+ eslint: { ...DEFAULT_CONFIG.eslint, ...(json.eslint || {}) },
98
+ prettier: { ...DEFAULT_CONFIG.prettier, ...(json.prettier || {}) },
99
+ cspell: { ...DEFAULT_CONFIG.cspell, ...(json.cspell || {}) },
100
+ };
101
+ fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(updated, null, 2));
102
+ utils_1.logger.info('[init]: 🔧 Updated pr-checkmate.json with customization options.');
103
+ utils_1.logger.info('[init]: 💡 You can now add custom ESLint rules, cspell words, and more!');
73
104
  }
74
105
  catch (e) {
75
- utils_1.logger.error(`[init]: ❌ Failed to update existing pr-checkmate.json
76
- config: ${JSON.stringify(exports.CONFIG_FILE)}\n${e}`);
106
+ utils_1.logger.error(`[init]: ❌ Failed to update pr-checkmate.json: ${e}`);
77
107
  }
78
108
  }
79
109
  }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -15,19 +15,19 @@ const configFiles = [
15
15
  { source: 'tsconfig.json', dest: 'tsconfig.json' },
16
16
  ];
17
17
  function copyConfigs() {
18
- const root = node_path_1.default.resolve(__dirname, '../..'); // root of package
19
- utils_1.logger.log('đŸ“Ļ Copying config files to package root...\n');
18
+ const root = node_path_1.default.resolve(__dirname, '../..');
19
+ utils_1.logger.log('[copyConfigs]: đŸ“Ļ Copying config files to package root...\n');
20
20
  configFiles.forEach(({ source, dest }) => {
21
21
  const srcPath = node_path_1.default.join(root, source);
22
- const destPath = node_path_1.default.join(root, dest); // 💡 now root, not dist/
22
+ const destPath = node_path_1.default.join(root, dest);
23
23
  if (!node_fs_1.default.existsSync(srcPath)) {
24
- utils_1.logger.warn(`âš ī¸ Source not found: ${source}`);
24
+ utils_1.logger.warn(`[copyConfigs]: âš ī¸ Source not found: ${source}`);
25
25
  return;
26
26
  }
27
27
  node_fs_1.default.copyFileSync(srcPath, destPath);
28
- utils_1.logger.log(`✅ Copied ${dest}`);
28
+ utils_1.logger.log(`[copyConfigs]: ✅ Copied ${dest}`);
29
29
  });
30
- utils_1.logger.info('\n✨ Configs successfully copied to root!\n');
30
+ utils_1.logger.info('\n[copyConfigs]: ✨ Configs successfully copied to root!\n');
31
31
  }
32
32
  if (require.main === module) {
33
33
  copyConfigs();
@@ -16,13 +16,13 @@ async function runDependencyCheck() {
16
16
  const projectRoot = process.cwd();
17
17
  const packageJsonPath = node_path_1.default.join(projectRoot, 'package.json');
18
18
  if (!node_fs_1.default.existsSync(packageJsonPath)) {
19
- utils_1.logger.warn('âš ī¸ No package.json — skipping dependency check');
19
+ utils_1.logger.warn('[runDependencyCheck]: âš ī¸ No package.json — skipping dependency check');
20
20
  return;
21
21
  }
22
22
  const sourcePath = (0, config_1.getSourcePath)();
23
23
  const sourceFolder = node_path_1.default.join(projectRoot, sourcePath);
24
24
  if (!node_fs_1.default.existsSync(sourceFolder)) {
25
- utils_1.logger.warn(`âš ī¸ Source folder "${sourcePath}" not found — skipping deeper checks`);
25
+ utils_1.logger.warn(`[runDependencyCheck]: âš ī¸ Source folder "${sourcePath}" not found — skipping deeper checks`);
26
26
  return;
27
27
  }
28
28
  const ignored = config_1.IGNORE_PATTERNS.some(pattern => {
@@ -30,7 +30,7 @@ async function runDependencyCheck() {
30
30
  return clean && sourceFolder.includes(clean);
31
31
  });
32
32
  if (ignored) {
33
- utils_1.logger.warn('âš ī¸ Source path contains ignored directories');
33
+ utils_1.logger.warn('[runDependencyCheck]: âš ī¸ Source path contains ignored directories');
34
34
  }
35
- utils_1.logger.info('✅ Dependencies OK');
35
+ utils_1.logger.info('[runDependencyCheck]: ✅ Dependencies OK');
36
36
  }
@@ -28,7 +28,7 @@ __exportStar(require("./prettier-autoformat"), exports);
28
28
  __exportStar(require("./spellcheck"), exports);
29
29
  __exportStar(require("./security"), exports);
30
30
  async function runJob(jobName) {
31
- utils_1.logger.info(`🚀 Running job: ${jobName}`);
31
+ utils_1.logger.info(`[runJob]: 🚀 Running job: ${jobName}`);
32
32
  try {
33
33
  switch (jobName) {
34
34
  case 'all':
@@ -72,7 +72,7 @@ async function runJob(jobName) {
72
72
  }
73
73
  }
74
74
  catch (err) {
75
- utils_1.logger.error(`❌ Job "${jobName}" failed: ${err}`);
75
+ utils_1.logger.error(`[runJob]: ❌ Job "${jobName}" failed: ${err}`);
76
76
  throw err;
77
77
  }
78
78
  }
@@ -9,7 +9,7 @@ const utils_1 = require("../utils");
9
9
  const execa_1 = require("execa");
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  async function runLint() {
12
- utils_1.logger.info('🔍 Running ESLint...');
12
+ utils_1.logger.info('[runLint]: 🔍 Running ESLint...');
13
13
  const projectRoot = process.cwd();
14
14
  const sourcePath = (0, config_1.getSourcePath)();
15
15
  const sourceFullPath = node_path_1.default.join(projectRoot, sourcePath);
@@ -22,7 +22,7 @@ async function runLint() {
22
22
  ]);
23
23
  let args;
24
24
  if (hasLocal) {
25
- utils_1.logger.info(`📋 Using local ESLint config (checking: ${sourcePath})`);
25
+ utils_1.logger.info(`[runLint]: 📋 Using local ESLint config (checking: ${sourcePath})`);
26
26
  args = ['eslint', sourceFullPath, '--ext', '.ts,.tsx,.js,.jsx', '--fix'];
27
27
  }
28
28
  else {
@@ -41,10 +41,10 @@ async function runLint() {
41
41
  config_1.IGNORE_PATTERNS.forEach(p => args.push('--ignore-pattern', p));
42
42
  try {
43
43
  await (0, execa_1.execa)('npx', args, { stdio: 'inherit' });
44
- utils_1.logger.info('✅ ESLint passed');
44
+ utils_1.logger.info('[runLint]: ✅ ESLint passed');
45
45
  }
46
46
  catch (err) {
47
- utils_1.logger.warn('âš ī¸ ESLint found issues');
47
+ utils_1.logger.warn('[runLint]: âš ī¸ ESLint found issues');
48
48
  throw err;
49
49
  }
50
50
  }
@@ -9,6 +9,47 @@ const utils_1 = require("../utils");
9
9
  const execa_1 = require("execa");
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  const node_fs_1 = __importDefault(require("node:fs"));
12
+ /**
13
+ * Load user's Prettier customizations from pr-checkmate.json
14
+ */
15
+ function loadUserPrettierConfig() {
16
+ const configPath = node_path_1.default.join(process.cwd(), 'pr-checkmate.json');
17
+ if (!node_fs_1.default.existsSync(configPath)) {
18
+ return {};
19
+ }
20
+ try {
21
+ const config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
22
+ return config.prettier?.rules || {};
23
+ }
24
+ catch (err) {
25
+ utils_1.logger.warn(`[loadUserPrettierConfig]: âš ī¸ Failed to read prettier config from pr-checkmate.json: ${err}`);
26
+ return {};
27
+ }
28
+ }
29
+ /**
30
+ * Create temporary .prettierrc with merged settings
31
+ */
32
+ function createTempPrettierConfig() {
33
+ const packageConfigPath = (0, config_1.getPackageConfigPath)('.prettierrc');
34
+ const defaultConfig = JSON.parse(node_fs_1.default.readFileSync(packageConfigPath, 'utf-8'));
35
+ const userConfig = loadUserPrettierConfig();
36
+ // Merge configurations
37
+ const mergedConfig = {
38
+ ...defaultConfig,
39
+ ...userConfig,
40
+ };
41
+ // Log customizations
42
+ const customRulesCount = Object.keys(userConfig).length;
43
+ if (customRulesCount > 0) {
44
+ utils_1.logger.info(`[createTempPrettierConfig]: 🎨 Applied ${customRulesCount}
45
+ custom Prettier rules from pr-checkmate.json`);
46
+ utils_1.logger.debug(`[createTempPrettierConfig]: Custom rules: ${JSON.stringify(userConfig)}`);
47
+ }
48
+ // Write temporary config
49
+ const tempConfigPath = node_path_1.default.join(process.cwd(), '.prettierrc.temp.json');
50
+ node_fs_1.default.writeFileSync(tempConfigPath, JSON.stringify(mergedConfig, null, 2));
51
+ return tempConfigPath;
52
+ }
12
53
  /**
13
54
  * Create temporary .prettierignore file with ignore patterns
14
55
  */
@@ -20,24 +61,25 @@ function createTempIgnoreFile() {
20
61
  /**
21
62
  * Remove temporary file
22
63
  */
23
- function cleanupTempIgnoreFile(filePath) {
64
+ function cleanupTempFile(filePath) {
24
65
  try {
25
- if (node_fs_1.default.existsSync(filePath))
66
+ if (node_fs_1.default.existsSync(filePath)) {
26
67
  node_fs_1.default.unlinkSync(filePath);
68
+ }
27
69
  }
28
70
  catch (err) {
29
- utils_1.logger.warn(`[cleanupTempIgnoreFile]: âš ī¸ Failed cleanup: ${filePath}\n${err}`);
71
+ utils_1.logger.warn(`[cleanupTempFile]: âš ī¸ Failed cleanup: ${filePath}\n${err}`);
30
72
  }
31
73
  }
32
74
  async function runPrettier() {
33
- utils_1.logger.info('🎨 Running Prettier...');
75
+ utils_1.logger.info('[runPrettier]: 🎨 Running Prettier...');
34
76
  const root = process.cwd();
35
77
  const sourcePath = (0, config_1.getSourcePath)();
36
78
  const targetGlob = `${node_path_1.default.join(root, sourcePath)}/**/*.{ts,tsx,js,jsx,json,md}`;
37
79
  const projectIgnore = node_path_1.default.join(root, '.prettierignore');
38
80
  const gitignore = node_path_1.default.join(root, '.gitignore');
39
81
  let ignorePath;
40
- let temp = null;
82
+ let tempIgnore = null;
41
83
  if (node_fs_1.default.existsSync(projectIgnore)) {
42
84
  ignorePath = projectIgnore;
43
85
  }
@@ -45,8 +87,8 @@ async function runPrettier() {
45
87
  ignorePath = gitignore;
46
88
  }
47
89
  else {
48
- temp = createTempIgnoreFile();
49
- ignorePath = temp;
90
+ tempIgnore = createTempIgnoreFile();
91
+ ignorePath = tempIgnore;
50
92
  }
51
93
  const hasLocal = (0, config_1.hasLocalConfig)([
52
94
  '.prettierrc',
@@ -55,25 +97,30 @@ async function runPrettier() {
55
97
  'prettier.config.js',
56
98
  ]);
57
99
  let args;
100
+ let tempConfig = null;
58
101
  if (hasLocal) {
59
- utils_1.logger.info(`📋 Using local Prettier config (checking: ${sourcePath})`);
102
+ utils_1.logger.info(`[runPrettier]: 📋 Using local Prettier config (checking: ${sourcePath})`);
60
103
  args = ['prettier', '--write', targetGlob, '--ignore-path', ignorePath];
61
104
  }
62
105
  else {
63
- utils_1.logger.info(`đŸ“Ļ Using pr-checkmate Prettier config (checking: ${sourcePath})`);
64
- const configPath = (0, config_1.getPackageConfigPath)('.prettierrc');
65
- args = ['prettier', '--write', targetGlob, '--config', configPath, '--ignore-path', ignorePath];
106
+ utils_1.logger.info(`[runPrettier]: đŸ“Ļ Using pr-checkmate Prettier config (checking: ${sourcePath})`);
107
+ // Create temporary config with merged settings
108
+ tempConfig = createTempPrettierConfig();
109
+ args = ['prettier', '--write', targetGlob, '--config', tempConfig, '--ignore-path', ignorePath];
66
110
  }
67
111
  try {
68
112
  await (0, execa_1.execa)('npx', args, { stdio: 'inherit' });
69
- utils_1.logger.info('✅ Prettier completed');
113
+ utils_1.logger.info('[runPrettier]: ✅ Prettier completed');
70
114
  }
71
115
  catch (err) {
72
- utils_1.logger.error('❌ Prettier failed');
116
+ utils_1.logger.error('[runPrettier]: ❌ Prettier failed');
73
117
  throw err;
74
118
  }
75
119
  finally {
76
- if (temp)
77
- cleanupTempIgnoreFile(temp);
120
+ // Cleanup temporary files
121
+ if (tempIgnore)
122
+ cleanupTempFile(tempIgnore);
123
+ if (tempConfig)
124
+ cleanupTempFile(tempConfig);
78
125
  }
79
126
  }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const fs_1 = __importDefault(require("fs"));
7
7
  const child_process_1 = require("child_process");
8
+ const utils_1 = require("../utils");
8
9
  const path_1 = __importDefault(require("path"));
9
10
  function publishToGithub() {
10
11
  const root = process.cwd();
@@ -22,11 +23,11 @@ function publishToGithub() {
22
23
  try {
23
24
  (0, child_process_1.execSync)(`mv ${pkgPath} ${backupPath}`);
24
25
  (0, child_process_1.execSync)(`mv ${tempPath} ${pkgPath}`);
25
- (0, child_process_1.execSync)(`npm publish --registry=https://npm.pkg.github.com`);
26
- console.log('✅ Published to GitHub Packages');
26
+ (0, child_process_1.execSync)('npm publish --registry=https://npm.pkg.github.com');
27
+ utils_1.logger.log('[publishToGithub]: ✅ Published to GitHub Packages');
27
28
  }
28
29
  catch (err) {
29
- console.error(err);
30
+ utils_1.logger.error(`[publishToGithub]: ${err}`);
30
31
  }
31
32
  finally {
32
33
  (0, child_process_1.execSync)(`mv ${backupPath} ${pkgPath} || true`);
@@ -8,6 +8,66 @@ const config_1 = require("../config");
8
8
  const utils_1 = require("../utils");
9
9
  const execa_1 = require("execa");
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ /**
13
+ * Load user's cspell customizations from pr-checkmate.json
14
+ */
15
+ function loadUserCspellConfig() {
16
+ const configPath = node_path_1.default.join(process.cwd(), 'pr-checkmate.json');
17
+ if (!node_fs_1.default.existsSync(configPath)) {
18
+ return {};
19
+ }
20
+ try {
21
+ const config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
22
+ return config.cspell || {};
23
+ }
24
+ catch (err) {
25
+ utils_1.logger.warn(`[loadUserCspellConfig]: âš ī¸ Failed to read cspell config from pr-checkmate.json: ${err}`);
26
+ return {};
27
+ }
28
+ }
29
+ /**
30
+ * Create temporary cspell config with merged settings
31
+ */
32
+ function createTempCspellConfig() {
33
+ const packageConfigPath = (0, config_1.getPackageConfigPath)('cspell.json');
34
+ const defaultConfig = JSON.parse(node_fs_1.default.readFileSync(packageConfigPath, 'utf-8'));
35
+ const userConfig = loadUserCspellConfig();
36
+ // Merge configurations
37
+ const mergedConfig = {
38
+ ...defaultConfig,
39
+ words: [...defaultConfig.words, ...(userConfig.words || [])],
40
+ ignorePaths: [...defaultConfig.ignorePaths, ...(userConfig.ignorePaths || [])],
41
+ ignoreRegExpList: [...defaultConfig.ignoreRegExpList, ...(userConfig.ignoreRegExpList || [])],
42
+ };
43
+ // Log customizations
44
+ const customWordsCount = (userConfig.words || []).length;
45
+ const customIgnoresCount = (userConfig.ignorePaths || []).length;
46
+ if (customWordsCount > 0) {
47
+ utils_1.logger.info(`[createTempCspellConfig]: 📖 Added ${customWordsCount} custom words from pr-checkmate.json`);
48
+ }
49
+ if (customIgnoresCount > 0) {
50
+ utils_1.logger.info(`[createTempCspellConfig]: đŸšĢ Added ${customIgnoresCount}
51
+ custom ignore paths from pr-checkmate.json`);
52
+ }
53
+ // Write temporary config
54
+ const tempConfigPath = node_path_1.default.join(process.cwd(), '.cspell.temp.json');
55
+ node_fs_1.default.writeFileSync(tempConfigPath, JSON.stringify(mergedConfig, null, 2));
56
+ return tempConfigPath;
57
+ }
58
+ /**
59
+ * Clean up temporary config file
60
+ */
61
+ function cleanupTempConfig(configPath) {
62
+ try {
63
+ if (node_fs_1.default.existsSync(configPath)) {
64
+ node_fs_1.default.unlinkSync(configPath);
65
+ }
66
+ }
67
+ catch (err) {
68
+ utils_1.logger.warn(`âš ī¸ Failed to cleanup temp config: ${err}`);
69
+ }
70
+ }
11
71
  /**
12
72
  * Build cspell patterns + ignore patterns
13
73
  */
@@ -17,27 +77,35 @@ function buildSpellcheckPatterns(projectRoot, sourcePath) {
17
77
  return [base, ...excludes];
18
78
  }
19
79
  async function runSpellcheck() {
20
- utils_1.logger.info('🔤 Running Spellcheck...');
80
+ utils_1.logger.info('[runSpellcheck]: 🔤 Running Spellcheck...');
21
81
  const projectRoot = process.cwd();
22
82
  const sourcePath = (0, config_1.getSourcePath)();
23
83
  const patterns = buildSpellcheckPatterns(projectRoot, sourcePath);
24
84
  const hasLocal = (0, config_1.hasLocalConfig)(['cspell.json', '.cspell.json', 'cspell.config.json']);
25
85
  let args;
86
+ let tempConfigPath = null;
26
87
  if (hasLocal) {
27
88
  utils_1.logger.info(`📋 Using local cspell config (checking: ${sourcePath})`);
28
89
  args = ['cspell', ...patterns, '--no-progress'];
29
90
  }
30
91
  else {
31
- utils_1.logger.info(`đŸ“Ļ Using pr-checkmate cspell config (checking: ${sourcePath})`);
32
- const configPath = (0, config_1.getPackageConfigPath)('cspell.json');
33
- args = ['cspell', ...patterns, '--config', configPath, '--no-progress'];
92
+ utils_1.logger.info(`[runSpellcheck]: đŸ“Ļ Using pr-checkmate cspell config (checking: ${sourcePath})`);
93
+ // Create temporary config with merged settings
94
+ tempConfigPath = createTempCspellConfig();
95
+ args = ['cspell', ...patterns, '--config', tempConfigPath, '--no-progress'];
34
96
  }
35
97
  try {
36
98
  await (0, execa_1.execa)('npx', args, { stdio: 'inherit' });
37
- utils_1.logger.info('✅ Spellcheck passed');
99
+ utils_1.logger.info('[runSpellcheck]: ✅ Spellcheck passed');
38
100
  }
39
101
  catch (err) {
40
- utils_1.logger.warn('âš ī¸ Spellcheck found issues');
102
+ utils_1.logger.warn('[runSpellcheck]: âš ī¸ Spellcheck found issues');
41
103
  throw err;
42
104
  }
105
+ finally {
106
+ // Cleanup temporary config
107
+ if (tempConfigPath) {
108
+ cleanupTempConfig(tempConfigPath);
109
+ }
110
+ }
43
111
  }
package/eslint.config.mjs CHANGED
@@ -1,22 +1,125 @@
1
1
  import fs from 'fs';
2
+ import path from 'path';
2
3
  import tsPlugin from '@typescript-eslint/eslint-plugin';
3
4
  import tsParser from '@typescript-eslint/parser';
4
5
  import prettierPlugin from 'eslint-plugin-prettier';
5
6
 
6
- const config = JSON.parse(fs.readFileSync('./pr-checkmate.json', 'utf-8'));
7
- const SRC = config.sourcePath;
7
+ /**
8
+ * Default ESLint rules - can be exported for use in init.ts
9
+ */
10
+ export const defaultRules = {
11
+ 'no-console': 'warn',
12
+ 'no-debugger': 'error',
13
+ 'prefer-const': 'error',
14
+ 'eqeqeq': ['error', 'always'],
15
+ '@typescript-eslint/no-unused-vars': [
16
+ 'error',
17
+ { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
18
+ ],
19
+ '@typescript-eslint/explicit-function-return-type': ['warn'],
20
+ '@typescript-eslint/no-explicit-any': ['warn'],
21
+ '@typescript-eslint/no-floating-promises': ['error'],
22
+ 'space-before-function-paren': 'off',
23
+ 'operator-linebreak': 'off',
24
+ 'max-len': ['warn', { code: 100 }],
25
+ 'semi': ['error', 'always'],
26
+ 'quotes': ['error', 'single', { avoidEscape: true }],
27
+ 'object-curly-spacing': ['error', 'always'],
28
+ 'space-infix-ops': 'error',
29
+ 'keyword-spacing': ['error', { before: true, after: true }],
30
+ 'padding-line-between-statements': [
31
+ 'error',
32
+ { blankLine: 'always', prev: 'block', next: '*' },
33
+ { blankLine: 'always', prev: '*', next: 'return' },
34
+ ],
35
+ 'prettier/prettier': [
36
+ 'error',
37
+ {
38
+ semi: true,
39
+ singleQuote: true,
40
+ bracketSpacing: true,
41
+ arrowParens: 'avoid',
42
+ printWidth: 100,
43
+ },
44
+ ],
45
+ };
46
+
47
+ /**
48
+ * Default ignore patterns
49
+ */
50
+ const defaultIgnores = [
51
+ '**/node_modules/**',
52
+ '**/dist/**',
53
+ '**/.git/**',
54
+ '**/build/**',
55
+ '**/coverage/**',
56
+ ];
57
+
58
+ /**
59
+ * Load user's pr-checkmate.json from project root
60
+ * Works both when developing the package and when installed in node_modules
61
+ */
62
+ function loadUserConfig() {
63
+ let currentDir = process.cwd();
64
+ let configPath = path.join(currentDir, 'pr-checkmate.json');
65
+
66
+ // If running from node_modules, find project root
67
+ if (currentDir.includes('node_modules')) {
68
+ const parts = currentDir.split(path.sep);
69
+ const nodeModulesIndex = parts.lastIndexOf('node_modules');
70
+ if (nodeModulesIndex > 0) {
71
+ currentDir = parts.slice(0, nodeModulesIndex).join(path.sep);
72
+ configPath = path.join(currentDir, 'pr-checkmate.json');
73
+ }
74
+ }
75
+
76
+ if (fs.existsSync(configPath)) {
77
+ try {
78
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
79
+ console.log(`[loadUserConfig]: ✅ Loaded user config from: ${configPath}`);
80
+ return config;
81
+ } catch (err) {
82
+ console.warn(`[loadUserConfig]: âš ī¸ Failed to parse ${configPath}:`, err.message);
83
+ }
84
+ } else {
85
+ console.log(`[loadUserConfig]: â„šī¸ No pr-checkmate.json found, using defaults`);
86
+ }
87
+
88
+ return { sourcePath: 'src', eslint: {} };
89
+ }
90
+
91
+ const userConfig = loadUserConfig();
92
+ const SRC = userConfig.sourcePath || 'src';
93
+
94
+ // Merge default rules with user-defined rules
95
+ const mergedRules = {
96
+ ...defaultRules,
97
+ ...(userConfig.eslint?.rules || {}),
98
+ };
99
+
100
+ // Merge default ignores with user-defined ignores
101
+ const mergedIgnores = [
102
+ ...defaultIgnores,
103
+ ...(userConfig.eslint?.ignorePatterns || []),
104
+ ];
105
+
106
+ // Log customization info
107
+ const customRulesCount = Object.keys(userConfig.eslint?.rules || {}).length;
108
+ const customIgnoresCount = (userConfig.eslint?.ignorePatterns || []).length;
109
+
110
+ if (customRulesCount > 0) {
111
+ console.log(`🔧 Applied ${customRulesCount} custom ESLint rules`);
112
+ }
113
+ if (customIgnoresCount > 0) {
114
+ console.log(`đŸšĢ Applied ${customIgnoresCount} custom ignore patterns`);
115
+ }
116
+
117
+ console.log(`📋 ESLint checking: ${SRC}/**/*.{ts,tsx,js,jsx}`);
8
118
 
9
119
  export default [
10
120
  {
11
121
  files: [`${SRC}/**/*.{ts,tsx,js,jsx}`],
12
-
13
- ignores: [
14
- '**/node_modules/**',
15
- '**/dist/**',
16
- '**/.git/**',
17
- '**/build/**',
18
- '**/coverage/**',
19
- ],
122
+ ignores: mergedIgnores,
20
123
 
21
124
  languageOptions: {
22
125
  parser: tsParser,
@@ -32,41 +135,6 @@ export default [
32
135
  prettier: prettierPlugin,
33
136
  },
34
137
 
35
- rules: {
36
- 'no-console': 'warn',
37
- 'no-debugger': 'error',
38
- 'prefer-const': 'error',
39
- eqeqeq: ['error', 'always'],
40
- '@typescript-eslint/no-unused-vars': [
41
- 'error',
42
- { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
43
- ],
44
- '@typescript-eslint/explicit-function-return-type': ['warn'],
45
- '@typescript-eslint/no-explicit-any': ['warn'],
46
- '@typescript-eslint/no-floating-promises': ['error'],
47
- 'space-before-function-paren': 'off',
48
- 'operator-linebreak': 'off',
49
- 'max-len': ['warn', { code: 100 }],
50
- semi: ['error', 'always'],
51
- quotes: ['error', 'single', { avoidEscape: true }],
52
- 'object-curly-spacing': ['error', 'always'],
53
- 'space-infix-ops': 'error',
54
- 'keyword-spacing': ['error', { before: true, after: true }],
55
- 'padding-line-between-statements': [
56
- 'error',
57
- { blankLine: 'always', prev: 'block', next: '*' },
58
- { blankLine: 'always', prev: '*', next: 'return' },
59
- ],
60
- 'prettier/prettier': [
61
- 'error',
62
- {
63
- semi: true,
64
- singleQuote: true,
65
- bracketSpacing: true,
66
- arrowParens: 'avoid',
67
- printWidth: 100,
68
- },
69
- ],
70
- },
138
+ rules: mergedRules,
71
139
  },
72
- ];
140
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-checkmate",
3
- "version": "1.19.3",
3
+ "version": "1.19.5",
4
4
  "description": "Automated PR quality checks: linting, formatting, dependency analysis, and spellcheck",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -28,7 +28,8 @@
28
28
  ],
29
29
  "scripts": {
30
30
  "copy-configs": "ts-node src/scripts/copy-configs.ts",
31
- "build": "npm run clean && npm run copy-configs && tsc && chmod +x dist/cli.js",
31
+ "build": "npm run clean && npm run copy-configs && tsc && npm run fix-shebang",
32
+ "fix-shebang": "node -e \"const fs=require('fs'); const path='dist/cli.js'; let content=fs.readFileSync(path,'utf8'); if(!content.startsWith('#!')){content='#!/usr/bin/env node\\n'+content; fs.writeFileSync(path,content);} fs.chmodSync(path,'755');\"",
32
33
  "check": "npm run build && npm run lint && npm run prettier && npm run spellcheck",
33
34
  "prepublishOnly": "npm run build",
34
35
  "lint": "eslint \"src/**/*.{ts,tsx}\" --fix",
@@ -77,4 +78,4 @@
77
78
  "eslint": ">=8.0.0",
78
79
  "prettier": ">=3.0.0"
79
80
  }
80
- }
81
+ }