@webpieces/ai-hook-rules 0.3.185 → 0.3.187

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.
Files changed (74) hide show
  1. package/package.json +3 -4
  2. package/src/core/custom-rule-adapter.d.ts +21 -0
  3. package/src/core/custom-rule-adapter.js +48 -0
  4. package/src/core/custom-rule-adapter.js.map +1 -0
  5. package/src/core/load-rules.d.ts +3 -2
  6. package/src/core/load-rules.js +71 -50
  7. package/src/core/load-rules.js.map +1 -1
  8. package/src/core/rule-base.d.ts +38 -0
  9. package/src/core/rule-base.js +36 -0
  10. package/src/core/rule-base.js.map +1 -0
  11. package/src/core/rules/branch-creation-guard.d.ts +12 -3
  12. package/src/core/rules/branch-creation-guard.js +37 -37
  13. package/src/core/rules/branch-creation-guard.js.map +1 -1
  14. package/src/core/rules/catch-error-pattern.d.ts +10 -3
  15. package/src/core/rules/catch-error-pattern.js +26 -26
  16. package/src/core/rules/catch-error-pattern.js.map +1 -1
  17. package/src/core/rules/max-file-lines.d.ts +13 -3
  18. package/src/core/rules/max-file-lines.js +14 -15
  19. package/src/core/rules/max-file-lines.js.map +1 -1
  20. package/src/core/rules/merge-in-progress-guard.d.ts +9 -3
  21. package/src/core/rules/merge-in-progress-guard.js +12 -13
  22. package/src/core/rules/merge-in-progress-guard.js.map +1 -1
  23. package/src/core/rules/no-any-unknown.d.ts +10 -3
  24. package/src/core/rules/no-any-unknown.js +14 -13
  25. package/src/core/rules/no-any-unknown.js.map +1 -1
  26. package/src/core/rules/no-destructure.d.ts +13 -3
  27. package/src/core/rules/no-destructure.js +13 -12
  28. package/src/core/rules/no-destructure.js.map +1 -1
  29. package/src/core/rules/no-direct-main-update.d.ts +9 -3
  30. package/src/core/rules/no-direct-main-update.js +12 -13
  31. package/src/core/rules/no-direct-main-update.js.map +1 -1
  32. package/src/core/rules/no-edit-on-main.d.ts +13 -3
  33. package/src/core/rules/no-edit-on-main.js +14 -14
  34. package/src/core/rules/no-edit-on-main.js.map +1 -1
  35. package/src/core/rules/no-implicit-any.d.ts +10 -3
  36. package/src/core/rules/no-implicit-any.js +12 -12
  37. package/src/core/rules/no-implicit-any.js.map +1 -1
  38. package/src/core/rules/no-js-files.d.ts +13 -3
  39. package/src/core/rules/no-js-files.js +13 -16
  40. package/src/core/rules/no-js-files.js.map +1 -1
  41. package/src/core/rules/no-shell-substitution.d.ts +9 -3
  42. package/src/core/rules/no-shell-substitution.js +22 -23
  43. package/src/core/rules/no-shell-substitution.js.map +1 -1
  44. package/src/core/rules/no-symbol-di-tokens.d.ts +13 -3
  45. package/src/core/rules/no-symbol-di-tokens.js +14 -13
  46. package/src/core/rules/no-symbol-di-tokens.js.map +1 -1
  47. package/src/core/rules/no-unmanaged-exceptions.d.ts +10 -3
  48. package/src/core/rules/no-unmanaged-exceptions.js +17 -17
  49. package/src/core/rules/no-unmanaged-exceptions.js.map +1 -1
  50. package/src/core/rules/pr-creation-guard.d.ts +9 -3
  51. package/src/core/rules/pr-creation-guard.js +12 -13
  52. package/src/core/rules/pr-creation-guard.js.map +1 -1
  53. package/src/core/rules/pr-merge-cleanup.d.ts +9 -3
  54. package/src/core/rules/pr-merge-cleanup.js +13 -14
  55. package/src/core/rules/pr-merge-cleanup.js.map +1 -1
  56. package/src/core/rules/require-return-type.d.ts +10 -3
  57. package/src/core/rules/require-return-type.js +23 -23
  58. package/src/core/rules/require-return-type.js.map +1 -1
  59. package/src/core/rules/throw-cause-required.d.ts +10 -3
  60. package/src/core/rules/throw-cause-required.js +12 -12
  61. package/src/core/rules/throw-cause-required.js.map +1 -1
  62. package/src/core/rules/validate-ts-in-src.d.ts +14 -3
  63. package/src/core/rules/validate-ts-in-src.js +15 -18
  64. package/src/core/rules/validate-ts-in-src.js.map +1 -1
  65. package/src/core/runner.js +38 -67
  66. package/src/core/runner.js.map +1 -1
  67. package/src/core/types.d.ts +14 -13
  68. package/src/core/types.js.map +1 -1
  69. package/src/index.d.ts +21 -1
  70. package/src/index.js +47 -1
  71. package/src/index.js.map +1 -1
  72. package/src/core/configs/default.d.ts +0 -2
  73. package/src/core/configs/default.js +0 -22
  74. package/src/core/configs/default.js.map +0 -1
@@ -1,18 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrMergeCleanupRule = void 0;
3
4
  const child_process_1 = require("child_process");
4
5
  const types_1 = require("../types");
5
- const prMergeCleanup = {
6
- name: 'pr-merge-cleanup',
7
- description: 'After merging a PR, require switching to main, pulling, and deleting the local branch.',
8
- scope: 'bash',
9
- files: [],
10
- defaultOptions: {},
11
- fixHint: [
6
+ const rule_base_1 = require("../rule-base");
7
+ function truncate(s) {
8
+ const MAX = 120;
9
+ return s.length <= MAX ? s : s.slice(0, MAX) + '';
10
+ }
11
+ class PrMergeCleanupRule extends rule_base_1.BashRuleBase {
12
+ constructor(config) { super(config, 'pr-merge-cleanup'); }
13
+ description = 'After merging a PR, require switching to main, pulling, and deleting the local branch.';
14
+ fixHint = [
12
15
  'After merging a PR you must clean up the local branch.',
13
16
  'Run this combined command instead:',
14
17
  ' gh pr merge --squash && git checkout main && git pull && git branch -d <current-branch>',
15
- ],
18
+ ];
16
19
  check(ctx) {
17
20
  if (!/gh\s+pr\s+merge/.test(ctx.command))
18
21
  return [];
@@ -29,11 +32,7 @@ const prMergeCleanup = {
29
32
  'Run this combined command instead:',
30
33
  ` gh pr merge --squash && git checkout main && git pull && git branch -d ${currentBranch}`,
31
34
  ].join('\n'))];
32
- },
33
- };
34
- function truncate(s) {
35
- const MAX = 120;
36
- return s.length <= MAX ? s : s.slice(0, MAX) + '…';
35
+ }
37
36
  }
38
- exports.default = prMergeCleanup;
37
+ exports.PrMergeCleanupRule = PrMergeCleanupRule;
39
38
  //# sourceMappingURL=pr-merge-cleanup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pr-merge-cleanup.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-merge-cleanup.ts"],"names":[],"mappings":";;AAAA,iDAAyC;AAEzC,oCAA0C;AAE1C,MAAM,cAAc,GAAa;IAC7B,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,wFAAwF;IACrG,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,EAAE;IACT,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,wDAAwD;QACxD,oCAAoC;QACpC,2FAA2F;KAC9F;IAED,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAEpD,MAAM,WAAW,GAAG,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,WAAW,IAAI,SAAS;YAAE,OAAO,EAAE,CAAC;QAExC,MAAM,aAAa,GAAG,IAAA,wBAAQ,EAAC,iCAAiC,EAAE;YAC9D,GAAG,EAAE,GAAG,CAAC,aAAa;YACtB,QAAQ,EAAE,MAAM;SACnB,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,2EAA2E;gBAC3E,oCAAoC;gBACpC,4EAA4E,aAAa,EAAE;aAC9F,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ,CAAC;AAEF,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,kBAAe,cAAc,CAAC","sourcesContent":["import { execSync } from 'child_process';\nimport type { BashRule, BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst prMergeCleanup: BashRule = {\n name: 'pr-merge-cleanup',\n description: 'After merging a PR, require switching to main, pulling, and deleting the local branch.',\n scope: 'bash',\n files: [],\n defaultOptions: {},\n fixHint: [\n 'After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ' gh pr merge --squash && git checkout main && git pull && git branch -d <current-branch>',\n ],\n\n check(ctx: BashContext): readonly Violation[] {\n if (!/gh\\s+pr\\s+merge/.test(ctx.command)) return [];\n\n const hasCheckout = /git\\s+(checkout|switch)\\s+main/.test(ctx.command);\n const hasDelete = /git\\s+branch\\s+-[dD]/.test(ctx.command);\n\n if (hasCheckout && hasDelete) return [];\n\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: ctx.workspaceRoot,\n encoding: 'utf8',\n }).trim();\n\n return [new V(\n 1,\n truncate(ctx.command),\n [\n '[pr-merge-cleanup] After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ` gh pr merge --squash && git checkout main && git pull && git branch -d ${currentBranch}`,\n ].join('\\n'),\n )];\n },\n};\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport default prMergeCleanup;\n"]}
1
+ {"version":3,"file":"pr-merge-cleanup.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-merge-cleanup.ts"],"names":[],"mappings":";;;AAAA,iDAAyC;AAKzC,oCAA0C;AAC1C,4CAA4C;AAE5C,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,MAAa,kBAAmB,SAAQ,wBAAkC;IACtE,YAAY,MAA4B,IAAI,KAAK,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAEvE,WAAW,GAAG,wFAAwF,CAAC;IACvG,OAAO,GAAG;QACf,wDAAwD;QACxD,oCAAoC;QACpC,2FAA2F;KAC9F,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAEpD,MAAM,WAAW,GAAG,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,WAAW,IAAI,SAAS;YAAE,OAAO,EAAE,CAAC;QAExC,MAAM,aAAa,GAAG,IAAA,wBAAQ,EAAC,iCAAiC,EAAE;YAC9D,GAAG,EAAE,GAAG,CAAC,aAAa;YACtB,QAAQ,EAAE,MAAM;SACnB,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,2EAA2E;gBAC3E,oCAAoC;gBACpC,4EAA4E,aAAa,EAAE;aAC9F,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AAjCD,gDAiCC","sourcesContent":["import { execSync } from 'child_process';\n\nimport { PrMergeCleanupConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport class PrMergeCleanupRule extends BashRuleBase<PrMergeCleanupConfig> {\n constructor(config: PrMergeCleanupConfig) { super(config, 'pr-merge-cleanup'); }\n\n readonly description = 'After merging a PR, require switching to main, pulling, and deleting the local branch.';\n readonly fixHint = [\n 'After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ' gh pr merge --squash && git checkout main && git pull && git branch -d <current-branch>',\n ];\n\n check(ctx: BashContext): readonly Violation[] {\n if (!/gh\\s+pr\\s+merge/.test(ctx.command)) return [];\n\n const hasCheckout = /git\\s+(checkout|switch)\\s+main/.test(ctx.command);\n const hasDelete = /git\\s+branch\\s+-[dD]/.test(ctx.command);\n\n if (hasCheckout && hasDelete) return [];\n\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: ctx.workspaceRoot,\n encoding: 'utf8',\n }).trim();\n\n return [new V(\n 1,\n truncate(ctx.command),\n [\n '[pr-merge-cleanup] After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ` gh pr merge --squash && git checkout main && git pull && git branch -d ${currentBranch}`,\n ].join('\\n'),\n )];\n }\n}\n"]}
@@ -1,3 +1,10 @@
1
- import type { EditRule } from '../types';
2
- declare const requireReturnTypeRule: EditRule;
3
- export default requireReturnTypeRule;
1
+ import { RequireReturnTypeConfig } from '@webpieces/rules-config';
2
+ import type { EditContext, Violation } from '../types';
3
+ import { EditRuleBase } from '../rule-base';
4
+ export declare class RequireReturnTypeRule extends EditRuleBase<RequireReturnTypeConfig> {
5
+ constructor(config: RequireReturnTypeConfig);
6
+ readonly description = "Every function and method must declare its return type.";
7
+ readonly files: string[];
8
+ readonly fixHint: string[];
9
+ check(ctx: EditContext): readonly Violation[];
10
+ }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const types_1 = require("../types");
3
+ exports.RequireReturnTypeRule = void 0;
4
4
  const rules_config_1 = require("@webpieces/rules-config");
5
+ const types_1 = require("../types");
6
+ const rule_base_1 = require("../rule-base");
5
7
  // Matches function/method signatures that don't have `: ReturnType` before the `{` body opener.
6
8
  // Pattern: function name(<params>) { — missing `: Type` between `)` and `{`
7
9
  const FUNC_DECL_MISSING = /\bfunction\s+\w+\s*(?:<[^>]*>)?\s*\([^)]*\)\s*\{/;
@@ -13,16 +15,26 @@ const ARROW_MISSING = /\bconst\s+\w+\s*=\s*(?:async\s+)?(?:<[^>]*>)?\s*\([^)]*\)
13
15
  const HAS_RETURN_TYPE = /\)\s*:\s*\S/;
14
16
  // Skip constructors, getters, setters, and control flow keywords
15
17
  const SKIP_PATTERN = /\b(?:constructor|get\s+\w+|set\s+\w+|if|else|while|for|switch|catch|return)\s*\(/;
16
- const requireReturnTypeRule = {
17
- name: 'require-return-type',
18
- description: 'Every function and method must declare its return type.',
19
- scope: 'edit',
20
- files: ['**/*.ts', '**/*.tsx'],
21
- defaultOptions: {},
22
- fixHint: [
18
+ function isMissingReturnType(line) {
19
+ if (SKIP_PATTERN.test(line))
20
+ return false;
21
+ const isFuncLike = FUNC_DECL_MISSING.test(line) ||
22
+ METHOD_MISSING.test(line) ||
23
+ ARROW_MISSING.test(line);
24
+ if (!isFuncLike)
25
+ return false;
26
+ if (HAS_RETURN_TYPE.test(line))
27
+ return false;
28
+ return true;
29
+ }
30
+ class RequireReturnTypeRule extends rule_base_1.EditRuleBase {
31
+ constructor(config) { super(config, 'require-return-type'); }
32
+ description = 'Every function and method must declare its return type.';
33
+ files = ['**/*.ts', '**/*.tsx'];
34
+ fixHint = [
23
35
  'Add return type: function foo(x: T): ReturnType { ... }',
24
36
  '// webpieces-disable require-return-type -- <reason>',
25
- ],
37
+ ];
26
38
  check(ctx) {
27
39
  const violations = [];
28
40
  for (let i = 0; i < ctx.strippedLines.length; i += 1) {
@@ -35,19 +47,7 @@ const requireReturnTypeRule = {
35
47
  violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), 'Missing return type annotation.'));
36
48
  }
37
49
  return violations;
38
- },
39
- };
40
- function isMissingReturnType(line) {
41
- if (SKIP_PATTERN.test(line))
42
- return false;
43
- const isFuncLike = FUNC_DECL_MISSING.test(line) ||
44
- METHOD_MISSING.test(line) ||
45
- ARROW_MISSING.test(line);
46
- if (!isFuncLike)
47
- return false;
48
- if (HAS_RETURN_TYPE.test(line))
49
- return false;
50
- return true;
50
+ }
51
51
  }
52
- exports.default = requireReturnTypeRule;
52
+ exports.RequireReturnTypeRule = RequireReturnTypeRule;
53
53
  //# sourceMappingURL=require-return-type.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"require-return-type.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/require-return-type.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAC1C,0DAAqD;AAErD,gGAAgG;AAChG,4EAA4E;AAC5E,MAAM,iBAAiB,GAAG,kDAAkD,CAAC;AAE7E,8EAA8E;AAC9E,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAEjF,qFAAqF;AACrF,MAAM,aAAa,GAAG,gEAAgE,CAAC;AAEvF,qEAAqE;AACrE,MAAM,eAAe,GAAG,aAAa,CAAC;AAEtC,iEAAiE;AACjE,MAAM,YAAY,GAAG,kFAAkF,CAAC;AAExG,MAAM,qBAAqB,GAAa;IACpC,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,yDAAyD;IACtE,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,yDAAyD;QACzD,sDAAsD;KACzD;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAU,CAAC,mBAAmB,CAAC;gBAAE,SAAS;YAC1E,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iCAAiC,CACpC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,SAAS,mBAAmB,CAAC,IAAY;IACrC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,UAAU,GACZ,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9B,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,kBAAe,qBAAqB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { RULE_NAMES } from '@webpieces/rules-config';\n\n// Matches function/method signatures that don't have `: ReturnType` before the `{` body opener.\n// Pattern: function name(<params>) { — missing `: Type` between `)` and `{`\nconst FUNC_DECL_MISSING = /\\bfunction\\s+\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Matches class method signatures: indented, optional async, name(<params>) {\nconst METHOD_MISSING = /^\\s{2,}(?:async\\s+)?\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Arrow function: const name = (async)? (<params>) => — missing `: Type` before `=>`\nconst ARROW_MISSING = /\\bconst\\s+\\w+\\s*=\\s*(?:async\\s+)?(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*=>/;\n\n// Lines that have ): ReturnType before { or => — these are COMPLIANT\nconst HAS_RETURN_TYPE = /\\)\\s*:\\s*\\S/;\n\n// Skip constructors, getters, setters, and control flow keywords\nconst SKIP_PATTERN = /\\b(?:constructor|get\\s+\\w+|set\\s+\\w+|if|else|while|for|switch|catch|return)\\s*\\(/;\n\nconst requireReturnTypeRule: EditRule = {\n name: 'require-return-type',\n description: 'Every function and method must declare its return type.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Add return type: function foo(x: T): ReturnType { ... }',\n '// webpieces-disable require-return-type -- <reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!isMissingReturnType(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.REQUIRE_RETURN_TYPE)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Missing return type annotation.',\n ));\n }\n return violations;\n },\n};\n\nfunction isMissingReturnType(line: string): boolean {\n if (SKIP_PATTERN.test(line)) return false;\n const isFuncLike =\n FUNC_DECL_MISSING.test(line) ||\n METHOD_MISSING.test(line) ||\n ARROW_MISSING.test(line);\n if (!isFuncLike) return false;\n if (HAS_RETURN_TYPE.test(line)) return false;\n return true;\n}\n\nexport default requireReturnTypeRule;\n"]}
1
+ {"version":3,"file":"require-return-type.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/require-return-type.ts"],"names":[],"mappings":";;;AAAA,0DAA8E;AAG9E,oCAA0C;AAC1C,4CAA4C;AAE5C,gGAAgG;AAChG,4EAA4E;AAC5E,MAAM,iBAAiB,GAAG,kDAAkD,CAAC;AAE7E,8EAA8E;AAC9E,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAEjF,qFAAqF;AACrF,MAAM,aAAa,GAAG,gEAAgE,CAAC;AAEvF,qEAAqE;AACrE,MAAM,eAAe,GAAG,aAAa,CAAC;AAEtC,iEAAiE;AACjE,MAAM,YAAY,GAAG,kFAAkF,CAAC;AAExG,SAAS,mBAAmB,CAAC,IAAY;IACrC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,UAAU,GACZ,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9B,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAa,qBAAsB,SAAQ,wBAAqC;IAC5E,YAAY,MAA+B,IAAI,KAAK,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAE7E,WAAW,GAAG,yDAAyD,CAAC;IAC/D,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzC,OAAO,GAAG;QACf,yDAAyD;QACzD,sDAAsD;KACzD,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAU,CAAC,mBAAmB,CAAC;gBAAE,SAAS;YAC1E,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iCAAiC,CACpC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AAzBD,sDAyBC","sourcesContent":["import { RequireReturnTypeConfig, RULE_NAMES } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\n\n// Matches function/method signatures that don't have `: ReturnType` before the `{` body opener.\n// Pattern: function name(<params>) { — missing `: Type` between `)` and `{`\nconst FUNC_DECL_MISSING = /\\bfunction\\s+\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Matches class method signatures: indented, optional async, name(<params>) {\nconst METHOD_MISSING = /^\\s{2,}(?:async\\s+)?\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Arrow function: const name = (async)? (<params>) => — missing `: Type` before `=>`\nconst ARROW_MISSING = /\\bconst\\s+\\w+\\s*=\\s*(?:async\\s+)?(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*=>/;\n\n// Lines that have ): ReturnType before { or => — these are COMPLIANT\nconst HAS_RETURN_TYPE = /\\)\\s*:\\s*\\S/;\n\n// Skip constructors, getters, setters, and control flow keywords\nconst SKIP_PATTERN = /\\b(?:constructor|get\\s+\\w+|set\\s+\\w+|if|else|while|for|switch|catch|return)\\s*\\(/;\n\nfunction isMissingReturnType(line: string): boolean {\n if (SKIP_PATTERN.test(line)) return false;\n const isFuncLike =\n FUNC_DECL_MISSING.test(line) ||\n METHOD_MISSING.test(line) ||\n ARROW_MISSING.test(line);\n if (!isFuncLike) return false;\n if (HAS_RETURN_TYPE.test(line)) return false;\n return true;\n}\n\nexport class RequireReturnTypeRule extends EditRuleBase<RequireReturnTypeConfig> {\n constructor(config: RequireReturnTypeConfig) { super(config, 'require-return-type'); }\n\n readonly description = 'Every function and method must declare its return type.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n readonly fixHint = [\n 'Add return type: function foo(x: T): ReturnType { ... }',\n '// webpieces-disable require-return-type -- <reason>',\n ];\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!isMissingReturnType(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.REQUIRE_RETURN_TYPE)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Missing return type annotation.',\n ));\n }\n return violations;\n }\n}\n"]}
@@ -1,3 +1,10 @@
1
- import type { EditRule } from '../types';
2
- declare const throwCauseRequiredRule: EditRule;
3
- export default throwCauseRequiredRule;
1
+ import { ThrowCauseRequiredConfig } from '@webpieces/rules-config';
2
+ import type { EditContext, Violation } from '../types';
3
+ import { EditRuleBase } from '../rule-base';
4
+ export declare class ThrowCauseRequiredRule extends EditRuleBase<ThrowCauseRequiredConfig> {
5
+ constructor(config: ThrowCauseRequiredConfig);
6
+ readonly description = "When rethrowing with added context, chain the original exception: throw new Error(\"msg\", { cause: error })";
7
+ readonly files: string[];
8
+ readonly fixHint: string[];
9
+ check(ctx: EditContext): readonly Violation[];
10
+ }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const types_1 = require("../types");
3
+ exports.ThrowCauseRequiredRule = void 0;
4
4
  const rules_config_1 = require("@webpieces/rules-config");
5
+ const types_1 = require("../types");
6
+ const rule_base_1 = require("../rule-base");
5
7
  const instruct_ai_writer_1 = require("../instruct-ai-writer");
6
8
  /**
7
9
  * Matches: throw new SomeClass(
@@ -15,18 +17,16 @@ const ERROR_MESSAGE_PATTERN = /\berror\d*\.message\b/;
15
17
  * Matches: cause: error or cause: error2 etc. (the good pattern)
16
18
  */
17
19
  const CAUSE_PATTERN = /\bcause\s*:\s*error\d*\b/;
18
- const throwCauseRequiredRule = {
19
- name: 'throw-cause-required',
20
- description: 'When rethrowing with added context, chain the original exception: throw new Error("msg", { cause: error })',
21
- scope: 'edit',
22
- files: ['**/*.ts', '**/*.tsx'],
23
- defaultOptions: {},
24
- fixHint: [
20
+ class ThrowCauseRequiredRule extends rule_base_1.EditRuleBase {
21
+ constructor(config) { super(config, 'throw-cause-required'); }
22
+ description = 'When rethrowing with added context, chain the original exception: throw new Error("msg", { cause: error })';
23
+ files = ['**/*.ts', '**/*.tsx'];
24
+ fixHint = [
25
25
  'Option 1 — Remove the try-catch entirely. Letting the original exception bubble is usually the best option.',
26
26
  'Option 2 — throw new Error("add more info here", { cause: error }); chains the error and preserves the full stack trace.',
27
27
  'Option 3 — throw new SpecificError("add info here", { cause: error }); e.g. new InformAiError("what happened for AI", { cause: error }).',
28
28
  '[Only if disableAllowed:true] Option 4 — // webpieces-disable throw-cause-required -- <reason>',
29
- ],
29
+ ];
30
30
  check(ctx) {
31
31
  const violations = [];
32
32
  const lines = ctx.strippedLines;
@@ -46,7 +46,7 @@ const throwCauseRequiredRule = {
46
46
  if (violations.length > 0)
47
47
  (0, instruct_ai_writer_1.writeTemplateIfMissing)(ctx.workspaceRoot, 'webpieces.exceptions.md');
48
48
  return violations;
49
- },
50
- };
51
- exports.default = throwCauseRequiredRule;
49
+ }
50
+ }
51
+ exports.ThrowCauseRequiredRule = ThrowCauseRequiredRule;
52
52
  //# sourceMappingURL=throw-cause-required.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"throw-cause-required.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/throw-cause-required.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAC1C,0DAAqD;AACrD,8DAA+D;AAE/D;;GAEG;AACH,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAErD;;GAEG;AACH,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAEtD;;GAEG;AACH,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD,MAAM,sBAAsB,GAAa;IACrC,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EAAE,4GAA4G;IACzH,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,6GAA6G;QAC7G,0HAA0H;QAC1H,0IAA0I;QAC1I,gGAAgG;KACnG;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAChD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACpD,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAE3C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAU,CAAC,oBAAoB,CAAC;gBAAE,SAAS;YAE3E,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iGAAiG,CACpG,CAAC,CAAC;QACP,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,IAAA,2CAAsB,EAAC,GAAG,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;QAChG,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,kBAAe,sBAAsB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { RULE_NAMES } from '@webpieces/rules-config';\nimport { writeTemplateIfMissing } from '../instruct-ai-writer';\n\n/**\n * Matches: throw new SomeClass(\n */\nconst THROW_NEW_PATTERN = /\\bthrow\\s+new\\s+\\w+\\s*\\(/;\n\n/**\n * Matches: error.message or error2.message or error3.message\n */\nconst ERROR_MESSAGE_PATTERN = /\\berror\\d*\\.message\\b/;\n\n/**\n * Matches: cause: error or cause: error2 etc. (the good pattern)\n */\nconst CAUSE_PATTERN = /\\bcause\\s*:\\s*error\\d*\\b/;\n\nconst throwCauseRequiredRule: EditRule = {\n name: 'throw-cause-required',\n description: 'When rethrowing with added context, chain the original exception: throw new Error(\"msg\", { cause: error })',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Option 1 — Remove the try-catch entirely. Letting the original exception bubble is usually the best option.',\n 'Option 2 — throw new Error(\"add more info here\", { cause: error }); chains the error and preserves the full stack trace.',\n 'Option 3 — throw new SpecificError(\"add info here\", { cause: error }); e.g. new InformAiError(\"what happened for AI\", { cause: error }).',\n '[Only if disableAllowed:true] Option 4 — // webpieces-disable throw-cause-required -- <reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n const lines = ctx.strippedLines;\n\n for (let i = 0; i < lines.length; i += 1) {\n const stripped = lines[i];\n if (!THROW_NEW_PATTERN.test(stripped)) continue;\n if (!ERROR_MESSAGE_PATTERN.test(stripped)) continue;\n if (CAUSE_PATTERN.test(stripped)) continue;\n\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.THROW_CAUSE_REQUIRED)) continue;\n\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Rethrowing with error.message loses the original stack trace. Use { cause: error } to chain it.',\n ));\n }\n\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n },\n};\n\nexport default throwCauseRequiredRule;\n"]}
1
+ {"version":3,"file":"throw-cause-required.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/throw-cause-required.ts"],"names":[],"mappings":";;;AAAA,0DAA+E;AAG/E,oCAA0C;AAC1C,4CAA4C;AAC5C,8DAA+D;AAE/D;;GAEG;AACH,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAErD;;GAEG;AACH,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAEtD;;GAEG;AACH,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD,MAAa,sBAAuB,SAAQ,wBAAsC;IAC9E,YAAY,MAAgC,IAAI,KAAK,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAE/E,WAAW,GAAG,4GAA4G,CAAC;IAClH,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzC,OAAO,GAAG;QACf,6GAA6G;QAC7G,0HAA0H;QAC1H,0IAA0I;QAC1I,gGAAgG;KACnG,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAChD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACpD,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAE3C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAU,CAAC,oBAAoB,CAAC;gBAAE,SAAS;YAE3E,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iGAAiG,CACpG,CAAC,CAAC;QACP,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,IAAA,2CAAsB,EAAC,GAAG,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;QAChG,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AAnCD,wDAmCC","sourcesContent":["import { ThrowCauseRequiredConfig, RULE_NAMES } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\nimport { writeTemplateIfMissing } from '../instruct-ai-writer';\n\n/**\n * Matches: throw new SomeClass(\n */\nconst THROW_NEW_PATTERN = /\\bthrow\\s+new\\s+\\w+\\s*\\(/;\n\n/**\n * Matches: error.message or error2.message or error3.message\n */\nconst ERROR_MESSAGE_PATTERN = /\\berror\\d*\\.message\\b/;\n\n/**\n * Matches: cause: error or cause: error2 etc. (the good pattern)\n */\nconst CAUSE_PATTERN = /\\bcause\\s*:\\s*error\\d*\\b/;\n\nexport class ThrowCauseRequiredRule extends EditRuleBase<ThrowCauseRequiredConfig> {\n constructor(config: ThrowCauseRequiredConfig) { super(config, 'throw-cause-required'); }\n\n readonly description = 'When rethrowing with added context, chain the original exception: throw new Error(\"msg\", { cause: error })';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n readonly fixHint = [\n 'Option 1 — Remove the try-catch entirely. Letting the original exception bubble is usually the best option.',\n 'Option 2 — throw new Error(\"add more info here\", { cause: error }); chains the error and preserves the full stack trace.',\n 'Option 3 — throw new SpecificError(\"add info here\", { cause: error }); e.g. new InformAiError(\"what happened for AI\", { cause: error }).',\n '[Only if disableAllowed:true] Option 4 — // webpieces-disable throw-cause-required -- <reason>',\n ];\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n const lines = ctx.strippedLines;\n\n for (let i = 0; i < lines.length; i += 1) {\n const stripped = lines[i];\n if (!THROW_NEW_PATTERN.test(stripped)) continue;\n if (!ERROR_MESSAGE_PATTERN.test(stripped)) continue;\n if (CAUSE_PATTERN.test(stripped)) continue;\n\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.THROW_CAUSE_REQUIRED)) continue;\n\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Rethrowing with error.message loses the original stack trace. Use { cause: error } to chain it.',\n ));\n }\n\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n }\n}\n"]}
@@ -1,3 +1,14 @@
1
- import type { FileRule } from '../types';
2
- declare const validateTsInSrcRule: FileRule;
3
- export default validateTsInSrcRule;
1
+ import { ValidateTsInSrcConfig } from '@webpieces/rules-config';
2
+ import type { FileContext, Violation } from '../types';
3
+ import { FileRuleBase } from '../rule-base';
4
+ export declare class ValidateTsInSrcRule extends FileRuleBase<ValidateTsInSrcConfig> {
5
+ constructor(config: ValidateTsInSrcConfig);
6
+ readonly description = "Every .ts file must belong to a project's src/ directory.";
7
+ readonly files: string[];
8
+ readonly defaultOptions: {
9
+ excludePaths: string[];
10
+ allowedRootFiles: string[];
11
+ };
12
+ readonly fixHint: string[];
13
+ check(ctx: FileContext): readonly Violation[];
14
+ }
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidateTsInSrcRule = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  const fs = tslib_1.__importStar(require("fs"));
5
6
  const path = tslib_1.__importStar(require("path"));
6
7
  const rules_config_1 = require("@webpieces/rules-config");
7
8
  const types_1 = require("../types");
9
+ const rule_base_1 = require("../rule-base");
8
10
  const DEFAULT_EXCLUDE_PATHS = [
9
11
  'node_modules', 'dist', '.nx', '.git',
10
12
  '**/*.d.ts', '**/jest.config.ts',
@@ -19,28 +21,23 @@ function findProjectRoot(filePath, workspaceRoot) {
19
21
  }
20
22
  return null;
21
23
  }
22
- const validateTsInSrcRule = {
23
- name: 'validate-ts-in-src',
24
- description: 'Every .ts file must belong to a project\'s src/ directory.',
25
- scope: 'file',
26
- files: ['**/*.ts', '**/*.tsx'],
27
- defaultOptions: {
24
+ class ValidateTsInSrcRule extends rule_base_1.FileRuleBase {
25
+ constructor(config) { super(config, 'validate-ts-in-src'); }
26
+ description = 'Every .ts file must belong to a project\'s src/ directory.';
27
+ files = ['**/*.ts', '**/*.tsx'];
28
+ defaultOptions = {
28
29
  excludePaths: DEFAULT_EXCLUDE_PATHS,
29
30
  allowedRootFiles: DEFAULT_ALLOWED_ROOT_FILES,
30
- },
31
- fixHint: [
31
+ };
32
+ fixHint = [
32
33
  'Move the file into an existing project\'s src/ directory, or create a new project with project.json that owns the directory.',
33
34
  'Add a dir or glob (e.g. "**/codegen.ts") to validate-ts-in-src.excludePaths in webpieces.config.json',
34
- ],
35
+ ];
35
36
  check(ctx) {
36
37
  if (ctx.tool !== 'Write')
37
38
  return [];
38
- const excludePaths = Array.isArray(ctx.options['excludePaths'])
39
- ? ctx.options['excludePaths']
40
- : DEFAULT_EXCLUDE_PATHS;
41
- const allowedRootFiles = Array.isArray(ctx.options['allowedRootFiles'])
42
- ? ctx.options['allowedRootFiles']
43
- : DEFAULT_ALLOWED_ROOT_FILES;
39
+ const excludePaths = this.config.excludePaths ?? DEFAULT_EXCLUDE_PATHS;
40
+ const allowedRootFiles = this.config.allowedRootFiles ?? DEFAULT_ALLOWED_ROOT_FILES;
44
41
  // Holistic exclusion (Layer 1 + Layer 2): bare dir names + globs.
45
42
  if ((0, rules_config_1.isPathExcluded)(ctx.relativePath, excludePaths))
46
43
  return [];
@@ -57,7 +54,7 @@ const validateTsInSrcRule = {
57
54
  return [new types_1.Violation(1, ctx.relativePath, `File is inside project \`${projectName}\` but outside its src/ directory. Move it into src/.`)];
58
55
  }
59
56
  return [];
60
- },
61
- };
62
- exports.default = validateTsInSrcRule;
57
+ }
58
+ }
59
+ exports.ValidateTsInSrcRule = ValidateTsInSrcRule;
63
60
  //# sourceMappingURL=validate-ts-in-src.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-ts-in-src.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/validate-ts-in-src.ts"],"names":[],"mappings":";;;AAAA,+CAAyB;AACzB,mDAA6B;AAE7B,0DAAyD;AAGzD,oCAA0C;AAE1C,MAAM,qBAAqB,GAAG;IAC1B,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IACrC,WAAW,EAAE,mBAAmB;CACnC,CAAC;AACF,MAAM,0BAA0B,GAAG,CAAC,eAAe,CAAC,CAAC;AAErD,SAAS,eAAe,CAAC,QAAgB,EAAE,aAAqB;IAC5D,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,OAAO,GAAG,KAAK,aAAa,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC5D,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC9D,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAM,mBAAmB,GAAa;IAClC,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EAAE,4DAA4D;IACzE,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE;QACZ,YAAY,EAAE,qBAAqB;QACnC,gBAAgB,EAAE,0BAA0B;KAC/C;IACD,OAAO,EAAE;QACL,8HAA8H;QAC9H,sGAAsG;KACzG;IAED,KAAK,CAAC,GAAgB;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,EAAE,CAAC;QAEpC,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;YAC3D,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAa;YACzC,CAAC,CAAC,qBAAqB,CAAC;QAC5B,MAAM,gBAAgB,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;YACnE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAa;YAC7C,CAAC,CAAC,0BAA0B,CAAC;QAEjC,kEAAkE;QAClE,IAAI,IAAA,6BAAc,EAAC,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC;YAAE,OAAO,EAAE,CAAC;QAE9D,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAEzF,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAErE,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,GAAG,CAAC,YAAY,EAChB,8EAA8E,CACjF,CAAC,CAAC;QACP,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,KAAK,EAAE,CAAC;YACvE,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,GAAG,CAAC,YAAY,EAChB,4BAA4B,WAAW,uDAAuD,CACjG,CAAC,CAAC;QACP,CAAC;QAED,OAAO,EAAE,CAAC;IACd,CAAC;CACJ,CAAC;AAEF,kBAAe,mBAAmB,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { isPathExcluded } from '@webpieces/rules-config';\n\nimport type { FileRule, FileContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst DEFAULT_EXCLUDE_PATHS = [\n 'node_modules', 'dist', '.nx', '.git',\n '**/*.d.ts', '**/jest.config.ts',\n];\nconst DEFAULT_ALLOWED_ROOT_FILES = ['jest.setup.ts'];\n\nfunction findProjectRoot(filePath: string, workspaceRoot: string): string | null {\n let dir = path.dirname(filePath);\n while (dir !== workspaceRoot && dir.startsWith(workspaceRoot)) {\n if (fs.existsSync(path.join(dir, 'project.json'))) return dir;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nconst validateTsInSrcRule: FileRule = {\n name: 'validate-ts-in-src',\n description: 'Every .ts file must belong to a project\\'s src/ directory.',\n scope: 'file',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {\n excludePaths: DEFAULT_EXCLUDE_PATHS,\n allowedRootFiles: DEFAULT_ALLOWED_ROOT_FILES,\n },\n fixHint: [\n 'Move the file into an existing project\\'s src/ directory, or create a new project with project.json that owns the directory.',\n 'Add a dir or glob (e.g. \"**/codegen.ts\") to validate-ts-in-src.excludePaths in webpieces.config.json',\n ],\n\n check(ctx: FileContext): readonly Violation[] {\n if (ctx.tool !== 'Write') return [];\n\n const excludePaths = Array.isArray(ctx.options['excludePaths'])\n ? ctx.options['excludePaths'] as string[]\n : DEFAULT_EXCLUDE_PATHS;\n const allowedRootFiles = Array.isArray(ctx.options['allowedRootFiles'])\n ? ctx.options['allowedRootFiles'] as string[]\n : DEFAULT_ALLOWED_ROOT_FILES;\n\n // Holistic exclusion (Layer 1 + Layer 2): bare dir names + globs.\n if (isPathExcluded(ctx.relativePath, excludePaths)) return [];\n\n const relParts = ctx.relativePath.split(path.sep);\n if (relParts.length === 1 && allowedRootFiles.indexOf(relParts[0] ?? '') >= 0) return [];\n\n const projectRoot = findProjectRoot(ctx.filePath, ctx.workspaceRoot);\n\n if (!projectRoot) {\n return [new V(\n 1,\n ctx.relativePath,\n 'File is not inside any Nx project. Move it into a project\\'s src/ directory.',\n )];\n }\n\n const relToProject = path.relative(projectRoot, ctx.filePath);\n if (!relToProject.startsWith('src' + path.sep) && relToProject !== 'src') {\n const projectName = path.relative(ctx.workspaceRoot, projectRoot);\n return [new V(\n 1,\n ctx.relativePath,\n `File is inside project \\`${projectName}\\` but outside its src/ directory. Move it into src/.`,\n )];\n }\n\n return [];\n },\n};\n\nexport default validateTsInSrcRule;\n"]}
1
+ {"version":3,"file":"validate-ts-in-src.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/validate-ts-in-src.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAE7B,0DAAgF;AAGhF,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,qBAAqB,GAAG;IAC1B,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IACrC,WAAW,EAAE,mBAAmB;CACnC,CAAC;AACF,MAAM,0BAA0B,GAAG,CAAC,eAAe,CAAC,CAAC;AAErD,SAAS,eAAe,CAAC,QAAgB,EAAE,aAAqB;IAC5D,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,OAAO,GAAG,KAAK,aAAa,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC5D,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC9D,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAa,mBAAoB,SAAQ,wBAAmC;IACxE,YAAY,MAA6B,IAAI,KAAK,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAE1E,WAAW,GAAG,4DAA4D,CAAC;IAClE,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChC,cAAc,GAAG;QAC/B,YAAY,EAAE,qBAAqB;QACnC,gBAAgB,EAAE,0BAA0B;KAC/C,CAAC;IACO,OAAO,GAAG;QACf,8HAA8H;QAC9H,sGAAsG;KACzG,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,EAAE,CAAC;QAEpC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,qBAAqB,CAAC;QACvE,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,IAAI,0BAA0B,CAAC;QAEpF,kEAAkE;QAClE,IAAI,IAAA,6BAAc,EAAC,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC;YAAE,OAAO,EAAE,CAAC;QAE9D,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAEzF,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAErE,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,GAAG,CAAC,YAAY,EAChB,8EAA8E,CACjF,CAAC,CAAC;QACP,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,KAAK,EAAE,CAAC;YACvE,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,GAAG,CAAC,YAAY,EAChB,4BAA4B,WAAW,uDAAuD,CACjG,CAAC,CAAC;QACP,CAAC;QAED,OAAO,EAAE,CAAC;IACd,CAAC;CACJ;AAhDD,kDAgDC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { ValidateTsInSrcConfig, isPathExcluded } from '@webpieces/rules-config';\n\nimport type { FileContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { FileRuleBase } from '../rule-base';\n\nconst DEFAULT_EXCLUDE_PATHS = [\n 'node_modules', 'dist', '.nx', '.git',\n '**/*.d.ts', '**/jest.config.ts',\n];\nconst DEFAULT_ALLOWED_ROOT_FILES = ['jest.setup.ts'];\n\nfunction findProjectRoot(filePath: string, workspaceRoot: string): string | null {\n let dir = path.dirname(filePath);\n while (dir !== workspaceRoot && dir.startsWith(workspaceRoot)) {\n if (fs.existsSync(path.join(dir, 'project.json'))) return dir;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nexport class ValidateTsInSrcRule extends FileRuleBase<ValidateTsInSrcConfig> {\n constructor(config: ValidateTsInSrcConfig) { super(config, 'validate-ts-in-src'); }\n\n readonly description = 'Every .ts file must belong to a project\\'s src/ directory.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n override readonly defaultOptions = {\n excludePaths: DEFAULT_EXCLUDE_PATHS,\n allowedRootFiles: DEFAULT_ALLOWED_ROOT_FILES,\n };\n readonly fixHint = [\n 'Move the file into an existing project\\'s src/ directory, or create a new project with project.json that owns the directory.',\n 'Add a dir or glob (e.g. \"**/codegen.ts\") to validate-ts-in-src.excludePaths in webpieces.config.json',\n ];\n\n check(ctx: FileContext): readonly Violation[] {\n if (ctx.tool !== 'Write') return [];\n\n const excludePaths = this.config.excludePaths ?? DEFAULT_EXCLUDE_PATHS;\n const allowedRootFiles = this.config.allowedRootFiles ?? DEFAULT_ALLOWED_ROOT_FILES;\n\n // Holistic exclusion (Layer 1 + Layer 2): bare dir names + globs.\n if (isPathExcluded(ctx.relativePath, excludePaths)) return [];\n\n const relParts = ctx.relativePath.split(path.sep);\n if (relParts.length === 1 && allowedRootFiles.indexOf(relParts[0] ?? '') >= 0) return [];\n\n const projectRoot = findProjectRoot(ctx.filePath, ctx.workspaceRoot);\n\n if (!projectRoot) {\n return [new V(\n 1,\n ctx.relativePath,\n 'File is not inside any Nx project. Move it into a project\\'s src/ directory.',\n )];\n }\n\n const relToProject = path.relative(projectRoot, ctx.filePath);\n if (!relToProject.startsWith('src' + path.sep) && relToProject !== 'src') {\n const projectName = path.relative(ctx.workspaceRoot, projectRoot);\n return [new V(\n 1,\n ctx.relativePath,\n `File is inside project \\`${projectName}\\` but outside its src/ directory. Move it into src/.`,\n )];\n }\n\n return [];\n }\n}\n"]}
@@ -6,36 +6,35 @@ const tslib_1 = require("tslib");
6
6
  const path = tslib_1.__importStar(require("path"));
7
7
  const rules_config_1 = require("@webpieces/rules-config");
8
8
  const build_context_1 = require("./build-context");
9
- const load_config_1 = require("./load-config");
10
9
  const load_rules_1 = require("./load-rules");
11
10
  const to_error_1 = require("./to-error");
12
11
  const report_1 = require("./report");
13
12
  const types_1 = require("./types");
13
+ const CONFIG_MISSING_REPORT = 'webpieces.config.json not found.\n' +
14
+ 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\n' +
15
+ 'Do not proceed until the human has done this.';
14
16
  function run(toolKind, input, cwd) {
15
17
  return runInternal(toolKind, input, cwd);
16
18
  }
17
19
  function runInternal(toolKind, input, cwd) {
18
- const config = (0, load_config_1.loadConfig)(cwd);
19
- if (!config.configPath) {
20
- return new types_1.BlockedResult('webpieces.config.json not found.\n' +
21
- 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\n' +
22
- 'Do not proceed until the human has done this.');
23
- }
24
- const workspaceRoot = path.dirname(config.configPath);
20
+ const loaded = (0, rules_config_1.loadWebpiecesRulesConfig)(cwd);
21
+ if (!loaded)
22
+ return new types_1.BlockedResult(CONFIG_MISSING_REPORT);
23
+ const workspaceRoot = path.dirname(loaded.configPath);
25
24
  // Always allow edits to webpieces.config.json — it's the fix target when out of sync
26
- if (path.resolve(input.filePath) === path.resolve(config.configPath)) {
25
+ if (path.resolve(input.filePath) === path.resolve(loaded.configPath)) {
27
26
  return null;
28
27
  }
29
- const rules = (0, load_rules_1.loadRules)(config, workspaceRoot);
28
+ const rules = (0, load_rules_1.loadRules)(loaded.config, workspaceRoot);
30
29
  if (rules.length === 0)
31
30
  return null;
32
- const outOfSync = checkConfigSync(rules, config);
31
+ const outOfSync = checkConfigSync(rules, loaded.config);
33
32
  if (outOfSync)
34
33
  return outOfSync;
35
34
  const contexts = (0, build_context_1.buildContexts)(toolKind, input, workspaceRoot);
36
35
  const relativePath = path.relative(workspaceRoot, input.filePath);
37
- const editGroups = runEditRules(rules, contexts.editContexts, config);
38
- const fileGroups = runFileRules(rules, contexts.fileContext, config);
36
+ const editGroups = runEditRules(rules, contexts.editContexts);
37
+ const fileGroups = runFileRules(rules, contexts.fileContext);
39
38
  const allGroups = [...editGroups, ...fileGroups];
40
39
  if (allGroups.length === 0)
41
40
  return null;
@@ -46,28 +45,30 @@ function runBash(command, cwd) {
46
45
  return runBashInternal(command, cwd);
47
46
  }
48
47
  function runBashInternal(command, cwd) {
49
- const config = (0, load_config_1.loadConfig)(cwd);
50
- if (!config.configPath) {
51
- return new types_1.BlockedResult('webpieces.config.json not found.\n' +
52
- 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\n' +
53
- 'Do not proceed until the human has done this.');
54
- }
55
- const workspaceRoot = path.dirname(config.configPath);
56
- const rules = (0, load_rules_1.loadRules)(config, workspaceRoot);
48
+ const loaded = (0, rules_config_1.loadWebpiecesRulesConfig)(cwd);
49
+ if (!loaded)
50
+ return new types_1.BlockedResult(CONFIG_MISSING_REPORT);
51
+ const workspaceRoot = path.dirname(loaded.configPath);
52
+ const rules = (0, load_rules_1.loadRules)(loaded.config, workspaceRoot);
57
53
  if (rules.length === 0)
58
54
  return null;
59
- const outOfSync = checkConfigSync(rules, config);
55
+ const outOfSync = checkConfigSync(rules, loaded.config);
60
56
  if (outOfSync)
61
57
  return outOfSync;
62
58
  const ctx = (0, build_context_1.buildBashContext)(command, workspaceRoot);
63
- const groups = runBashRules(rules, ctx, config);
59
+ const groups = runBashRules(rules, ctx);
64
60
  if (groups.length === 0)
65
61
  return null;
66
62
  const report = (0, report_1.formatReport)('<bash>', groups);
67
63
  return new types_1.BlockedResult(report);
68
64
  }
65
+ // The set of rule names explicitly present in webpieces.config.json (every key except rulesDir).
66
+ function configuredRuleNames(config) {
67
+ return new Set(Object.keys(config).filter((k) => k !== 'rulesDir'));
68
+ }
69
69
  function checkConfigSync(rules, config) {
70
- const unconfiguredRules = rules.filter((r) => !config.userConfiguredRuleNames.has(r.name));
70
+ const configured = configuredRuleNames(config);
71
+ const unconfiguredRules = rules.filter((r) => !configured.has(r.name));
71
72
  if (unconfiguredRules.length === 0)
72
73
  return null;
73
74
  const lines = [
@@ -112,17 +113,20 @@ function runRuleCheck(rule, ctx) {
112
113
  return [new types_1.Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];
113
114
  }
114
115
  }
115
- function runBashRules(rules, bashContext, config) {
116
+ function ruleMatchesFile(rule, relativePath) {
117
+ for (const pattern of rule.files) {
118
+ if ((0, load_rules_1.globMatches)(pattern, relativePath))
119
+ return true;
120
+ }
121
+ return false;
122
+ }
123
+ function runBashRules(rules, bashContext) {
116
124
  const groups = [];
117
125
  for (const rule of rules) {
118
126
  if (rule.scope !== 'bash')
119
127
  continue;
120
- const ruleConfig = config.rules.get(rule.name);
121
- if (!ruleConfig || ruleConfig.isOff)
122
- continue;
123
- if (isRuleSkipped(ruleConfig))
128
+ if (!rule.shouldRun())
124
129
  continue;
125
- bashContext.options = mergeOptions(rule.defaultOptions, ruleConfig);
126
130
  const vs = runRuleCheck(rule, bashContext);
127
131
  if (vs.length > 0) {
128
132
  groups.push(new types_1.RuleGroup(rule.name, rule.description, [...rule.fixHint], [...vs]));
@@ -130,46 +134,17 @@ function runBashRules(rules, bashContext, config) {
130
134
  }
131
135
  return groups;
132
136
  }
133
- // Every rule honors the universal escape hatches: skip while on a named branch
134
- // (ignoreRuleWhileOnBranch) or until an epoch passes (ignoreModifiedUntilEpoch).
135
- function isRuleSkipped(ruleConfig) {
136
- return (0, rules_config_1.shouldSkipRule)(ruleConfig.options['ignoreModifiedUntilEpoch'], ruleConfig.options['ignoreRuleWhileOnBranch']).skip;
137
- }
138
- function ruleMatchesFile(rule, relativePath) {
139
- for (const pattern of rule.files) {
140
- if ((0, load_rules_1.globMatches)(pattern, relativePath))
141
- return true;
142
- }
143
- return false;
144
- }
145
- function mergeOptions(defaultOptions, ruleConfig) {
146
- // webpieces-disable no-any-unknown -- building an options bag from opaque RuleOptions
147
- const out = {};
148
- for (const key of Object.keys(defaultOptions))
149
- out[key] = defaultOptions[key];
150
- for (const key of Object.keys(ruleConfig.options)) {
151
- // 'mode' is the framework-level on/off switch, not a rule option.
152
- if (key === 'mode')
153
- continue;
154
- out[key] = ruleConfig.options[key];
155
- }
156
- return out;
157
- }
158
- function runEditRules(rules, editContexts, config) {
137
+ function runEditRules(rules, editContexts) {
159
138
  const groups = [];
160
139
  for (const rule of rules) {
161
140
  if (rule.scope !== 'edit')
162
141
  continue;
163
- const ruleConfig = config.rules.get(rule.name);
164
- if (!ruleConfig || ruleConfig.isOff)
165
- continue;
166
- if (isRuleSkipped(ruleConfig))
142
+ if (!rule.shouldRun())
167
143
  continue;
168
144
  const allViolations = [];
169
145
  for (const ctx of editContexts) {
170
146
  if (!ruleMatchesFile(rule, ctx.relativePath))
171
147
  continue;
172
- ctx.options = mergeOptions(rule.defaultOptions, ruleConfig);
173
148
  const vs = runRuleCheck(rule, ctx);
174
149
  for (const v of vs) {
175
150
  const copy = new types_1.Violation(v.line, v.snippet, v.message);
@@ -184,19 +159,15 @@ function runEditRules(rules, editContexts, config) {
184
159
  }
185
160
  return groups;
186
161
  }
187
- function runFileRules(rules, fileContext, config) {
162
+ function runFileRules(rules, fileContext) {
188
163
  const groups = [];
189
164
  for (const rule of rules) {
190
165
  if (rule.scope !== 'file')
191
166
  continue;
192
- const ruleConfig = config.rules.get(rule.name);
193
- if (!ruleConfig || ruleConfig.isOff)
194
- continue;
195
- if (isRuleSkipped(ruleConfig))
167
+ if (!rule.shouldRun())
196
168
  continue;
197
169
  if (!ruleMatchesFile(rule, fileContext.relativePath))
198
170
  continue;
199
- fileContext.options = mergeOptions(rule.defaultOptions, ruleConfig);
200
171
  const vs = runRuleCheck(rule, fileContext);
201
172
  if (vs.length > 0) {
202
173
  groups.push(new types_1.RuleGroup(rule.name, rule.description, [...rule.fixHint], [...vs]));