@webpieces/code-rules 0.3.328 → 0.3.330

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/code-rules",
3
- "version": "0.3.328",
3
+ "version": "0.3.330",
4
4
  "description": "Standalone code validation rules extracted from architecture-validators, no Nx dependency required",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -20,7 +20,7 @@
20
20
  "directory": "packages/tooling/code-rules"
21
21
  },
22
22
  "dependencies": {
23
- "@webpieces/rules-config": "0.3.328"
23
+ "@webpieces/rules-config": "0.3.330"
24
24
  },
25
25
  "publishConfig": {
26
26
  "access": "public"
package/src/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver'
13
13
  export { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';
14
14
  export { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';
15
15
  export { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';
16
+ export { InjectAnnotationNotNeededForConcreteClassValidator, findRedundantInjectInSource } from './validate-inject-annotation-not-needed-for-concrete-class';
16
17
  export { FrameworkTagValidator } from './validate-framework-tag';
17
18
  export { RoleTagValidator } from './validate-role-tag';
18
19
  export { PrismaValidateDtosValidator } from './validate-dtos';
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateCode = exports.PrismaConverterValidator = exports.PrismaValidateDtosValidator = exports.RoleTagValidator = exports.FrameworkTagValidator = exports.NoFunctionOutsideClassValidator = exports.NoProcessExitOutsideMainValidator = exports.NoSymbolDiTokensValidator = exports.NoDirectApiResolverValidator = exports.NoDestructureValidator = exports.NoUnmanagedExceptionsValidator = exports.CatchErrorPatternValidator = exports.NoInlineTypeLiteralsValidator = exports.RequireReturnTypeValidator = exports.MaxFileLinesValidator = exports.runNewMethods = exports.runModifiedMethods = exports.MaxMethodLinesValidator = exports.NoImplicitAnyValidator = exports.NoAnyUnknownValidator = exports.CodeValidator = void 0;
3
+ exports.validateCode = exports.PrismaConverterValidator = exports.PrismaValidateDtosValidator = exports.RoleTagValidator = exports.FrameworkTagValidator = exports.findRedundantInjectInSource = exports.InjectAnnotationNotNeededForConcreteClassValidator = exports.NoFunctionOutsideClassValidator = exports.NoProcessExitOutsideMainValidator = exports.NoSymbolDiTokensValidator = exports.NoDirectApiResolverValidator = exports.NoDestructureValidator = exports.NoUnmanagedExceptionsValidator = exports.CatchErrorPatternValidator = exports.NoInlineTypeLiteralsValidator = exports.RequireReturnTypeValidator = exports.MaxFileLinesValidator = exports.runNewMethods = exports.runModifiedMethods = exports.MaxMethodLinesValidator = exports.NoImplicitAnyValidator = exports.NoAnyUnknownValidator = exports.CodeValidator = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  var code_validator_1 = require("./code-validator");
6
6
  Object.defineProperty(exports, "CodeValidator", { enumerable: true, get: function () { return code_validator_1.CodeValidator; } });
@@ -33,6 +33,9 @@ var validate_no_process_exit_outside_main_1 = require("./validate-no-process-exi
33
33
  Object.defineProperty(exports, "NoProcessExitOutsideMainValidator", { enumerable: true, get: function () { return validate_no_process_exit_outside_main_1.NoProcessExitOutsideMainValidator; } });
34
34
  var validate_no_function_outside_class_1 = require("./validate-no-function-outside-class");
35
35
  Object.defineProperty(exports, "NoFunctionOutsideClassValidator", { enumerable: true, get: function () { return validate_no_function_outside_class_1.NoFunctionOutsideClassValidator; } });
36
+ var validate_inject_annotation_not_needed_for_concrete_class_1 = require("./validate-inject-annotation-not-needed-for-concrete-class");
37
+ Object.defineProperty(exports, "InjectAnnotationNotNeededForConcreteClassValidator", { enumerable: true, get: function () { return validate_inject_annotation_not_needed_for_concrete_class_1.InjectAnnotationNotNeededForConcreteClassValidator; } });
38
+ Object.defineProperty(exports, "findRedundantInjectInSource", { enumerable: true, get: function () { return validate_inject_annotation_not_needed_for_concrete_class_1.findRedundantInjectInSource; } });
36
39
  var validate_framework_tag_1 = require("./validate-framework-tag");
37
40
  Object.defineProperty(exports, "FrameworkTagValidator", { enumerable: true, get: function () { return validate_framework_tag_1.FrameworkTagValidator; } });
38
41
  var validate_role_tag_1 = require("./validate-role-tag");
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,mDAAiE;AAAxD,+GAAA,aAAa,OAAA;AAEtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,uEAAoE;AAA3D,kIAAA,sBAAsB,OAAA;AAC/B,yEAA0F;AAAjF,oIAAA,uBAAuB,OAAA;AAAE,+HAAA,kBAAkB,OAAA;AACpD,+DAAuD;AAA9C,qHAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,mIAAA,0BAA0B,OAAA;AACnC,uEAA2E;AAAlE,yIAAA,6BAA6B,OAAA;AACtC,+EAA4E;AAAnE,0IAAA,0BAA0B,OAAA;AACnC,uFAAoF;AAA3E,kJAAA,8BAA8B,OAAA;AACvC,qEAAmE;AAA1D,iIAAA,sBAAsB,OAAA;AAC/B,qFAAiF;AAAxE,+IAAA,4BAA4B,OAAA;AACrC,+EAA2E;AAAlE,yIAAA,yBAAyB,OAAA;AAClC,iGAA4F;AAAnF,0JAAA,iCAAiC,OAAA;AAC1C,2FAAuF;AAA9E,qJAAA,+BAA+B,OAAA;AACxC,mEAAiE;AAAxD,+HAAA,qBAAqB,OAAA;AAC9B,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,iDAA8D;AAArD,4HAAA,2BAA2B,OAAA;AACpC,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA;AACjC,iDAA0D;AAAjD,sIAAA,OAAO,OAAgB","sourcesContent":["export { CodeValidator, ExecutorResult } from './code-validator';\n\nexport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nexport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nexport { MaxMethodLinesValidator, runModifiedMethods } from './validate-modified-methods';\nexport { runNewMethods } from './validate-new-methods';\nexport { MaxFileLinesValidator } from './validate-modified-files';\nexport { RequireReturnTypeValidator } from './validate-return-types';\nexport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nexport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nexport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nexport { NoDestructureValidator } from './validate-no-destructure';\nexport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nexport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nexport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nexport { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';\nexport { FrameworkTagValidator } from './validate-framework-tag';\nexport { RoleTagValidator } from './validate-role-tag';\nexport { PrismaValidateDtosValidator } from './validate-dtos';\nexport { PrismaConverterValidator } from './validate-prisma-converters';\nexport { default as validateCode } from './validate-code';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,mDAAiE;AAAxD,+GAAA,aAAa,OAAA;AAEtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,uEAAoE;AAA3D,kIAAA,sBAAsB,OAAA;AAC/B,yEAA0F;AAAjF,oIAAA,uBAAuB,OAAA;AAAE,+HAAA,kBAAkB,OAAA;AACpD,+DAAuD;AAA9C,qHAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,mIAAA,0BAA0B,OAAA;AACnC,uEAA2E;AAAlE,yIAAA,6BAA6B,OAAA;AACtC,+EAA4E;AAAnE,0IAAA,0BAA0B,OAAA;AACnC,uFAAoF;AAA3E,kJAAA,8BAA8B,OAAA;AACvC,qEAAmE;AAA1D,iIAAA,sBAAsB,OAAA;AAC/B,qFAAiF;AAAxE,+IAAA,4BAA4B,OAAA;AACrC,+EAA2E;AAAlE,yIAAA,yBAAyB,OAAA;AAClC,iGAA4F;AAAnF,0JAAA,iCAAiC,OAAA;AAC1C,2FAAuF;AAA9E,qJAAA,+BAA+B,OAAA;AACxC,uIAA6J;AAApJ,8LAAA,kDAAkD,OAAA;AAAE,uKAAA,2BAA2B,OAAA;AACxF,mEAAiE;AAAxD,+HAAA,qBAAqB,OAAA;AAC9B,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,iDAA8D;AAArD,4HAAA,2BAA2B,OAAA;AACpC,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA;AACjC,iDAA0D;AAAjD,sIAAA,OAAO,OAAgB","sourcesContent":["export { CodeValidator, ExecutorResult } from './code-validator';\n\nexport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nexport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nexport { MaxMethodLinesValidator, runModifiedMethods } from './validate-modified-methods';\nexport { runNewMethods } from './validate-new-methods';\nexport { MaxFileLinesValidator } from './validate-modified-files';\nexport { RequireReturnTypeValidator } from './validate-return-types';\nexport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nexport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nexport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nexport { NoDestructureValidator } from './validate-no-destructure';\nexport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nexport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nexport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nexport { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';\nexport { InjectAnnotationNotNeededForConcreteClassValidator, findRedundantInjectInSource } from './validate-inject-annotation-not-needed-for-concrete-class';\nexport { FrameworkTagValidator } from './validate-framework-tag';\nexport { RoleTagValidator } from './validate-role-tag';\nexport { PrismaValidateDtosValidator } from './validate-dtos';\nexport { PrismaConverterValidator } from './validate-prisma-converters';\nexport { default as validateCode } from './validate-code';\n"]}
@@ -18,6 +18,7 @@ const validate_no_direct_api_resolver_1 = require("./validate-no-direct-api-reso
18
18
  const validate_no_symbol_di_tokens_1 = require("./validate-no-symbol-di-tokens");
19
19
  const validate_no_process_exit_outside_main_1 = require("./validate-no-process-exit-outside-main");
20
20
  const validate_no_function_outside_class_1 = require("./validate-no-function-outside-class");
21
+ const validate_inject_annotation_not_needed_for_concrete_class_1 = require("./validate-inject-annotation-not-needed-for-concrete-class");
21
22
  const validate_framework_tag_1 = require("./validate-framework-tag");
22
23
  const validate_role_tag_1 = require("./validate-role-tag");
23
24
  const validate_match_rules_1 = require("./validate-match-rules");
@@ -45,6 +46,7 @@ function buildValidators(config, matchRules) {
45
46
  new validate_no_symbol_di_tokens_1.NoSymbolDiTokensValidator(config['no-symbol-di-tokens'] ?? new rules_config_1.NoSymbolDiTokensConfig()),
46
47
  new validate_no_process_exit_outside_main_1.NoProcessExitOutsideMainValidator(config['no-process-exit-outside-main'] ?? new rules_config_1.NoProcessExitOutsideMainConfig()),
47
48
  new validate_no_function_outside_class_1.NoFunctionOutsideClassValidator(config['no-function-outside-class'] ?? new rules_config_1.NoFunctionOutsideClassConfig()),
49
+ new validate_inject_annotation_not_needed_for_concrete_class_1.InjectAnnotationNotNeededForConcreteClassValidator(config['inject-annotation-not-needed-for-concrete-class'] ?? new rules_config_1.InjectAnnotationNotNeededForConcreteClassConfig()),
48
50
  new validate_framework_tag_1.FrameworkTagValidator(config['framework-tag'] ?? new rules_config_1.FrameworkTagConfig()),
49
51
  new validate_role_tag_1.RoleTagValidator(config['role-tag'] ?? new rules_config_1.RoleTagConfig()),
50
52
  // One validator per client-authored match-rules entry (each with its own name/mode/epoch).
@@ -1 +1 @@
1
- {"version":3,"file":"validate-code.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-code.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n loadAndValidate,\n WebpiecesRulesConfig,\n BaseRuleConfig,\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n CatchErrorPatternConfig,\n NoUnmanagedExceptionsConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n} from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { runValidators } from './rule-reporter';\nimport { MaxMethodLinesValidator } from './validate-modified-methods';\nimport { MaxFileLinesValidator } from './validate-modified-files';\nimport { RequireReturnTypeValidator } from './validate-return-types';\nimport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nimport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nimport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nimport { PrismaValidateDtosValidator } from './validate-dtos';\nimport { PrismaConverterValidator } from './validate-prisma-converters';\nimport { NoDestructureValidator } from './validate-no-destructure';\nimport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nimport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nimport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nimport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nimport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nimport { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';\nimport { FrameworkTagValidator } from './validate-framework-tag';\nimport { RoleTagValidator } from './validate-role-tag';\nimport { MatchRulesValidator } from './validate-match-rules';\nimport { MatchRuleConfig } from '@webpieces/rules-config';\n\nexport { ExecutorResult } from './code-validator';\n\n/**\n * Build every code validator from the typed WebpiecesRulesConfig. Each validator is\n * constructed with its own `*Config` (the genuinely-consumed config class), so the\n * config types are exercised at compile time. Missing rule keys fall back to an empty\n * config instance — `loadAndValidate` already validates that configured rules\n * carry an explicit `mode`, so in practice every key is present.\n */\nfunction buildValidators(\n config: WebpiecesRulesConfig,\n matchRules: readonly MatchRuleConfig[],\n): CodeValidator<BaseRuleConfig>[] {\n return [\n new MaxMethodLinesValidator(config['max-method-lines'] ?? new MaxMethodLinesConfig()),\n new MaxFileLinesValidator(config['max-file-lines'] ?? new MaxFileLinesConfig()),\n new RequireReturnTypeValidator(config['require-return-type'] ?? new RequireReturnTypeConfig()),\n new NoInlineTypeLiteralsValidator(config['no-inline-type-literals'] ?? new NoInlineTypeLiteralsConfig()),\n new NoAnyUnknownValidator(config['no-any-unknown'] ?? new NoAnyUnknownConfig()),\n new NoImplicitAnyValidator(config['no-implicit-any'] ?? new NoImplicitAnyConfig()),\n new PrismaValidateDtosValidator(config['prisma-validate-dtos'] ?? new PrismaValidateDtosConfig()),\n new PrismaConverterValidator(config['prisma-converter'] ?? new PrismaConverterConfig()),\n new NoDestructureValidator(config['no-destructure'] ?? new NoDestructureConfig()),\n new CatchErrorPatternValidator(config['catch-error-pattern'] ?? new CatchErrorPatternConfig()),\n new NoUnmanagedExceptionsValidator(config['no-unmanaged-exceptions'] ?? new NoUnmanagedExceptionsConfig()),\n new NoDirectApiResolverValidator(config['angular-no-direct-api-in-resolver'] ?? new AngularNoDirectApiInResolverConfig()),\n new NoSymbolDiTokensValidator(config['no-symbol-di-tokens'] ?? new NoSymbolDiTokensConfig()),\n new NoProcessExitOutsideMainValidator(config['no-process-exit-outside-main'] ?? new NoProcessExitOutsideMainConfig()),\n new NoFunctionOutsideClassValidator(config['no-function-outside-class'] ?? new NoFunctionOutsideClassConfig()),\n new FrameworkTagValidator(config['framework-tag'] ?? new FrameworkTagConfig()),\n new RoleTagValidator(config['role-tag'] ?? new RoleTagConfig()),\n // One validator per client-authored match-rules entry (each with its own name/mode/epoch).\n ...matchRules.map((mr: MatchRuleConfig) => new MatchRulesValidator(mr)),\n ];\n}\n\n/**\n * Run all configured code validators against the workspace.\n *\n * Owns config loading: callers just pass the workspace root. Config comes from\n * webpieces.config.json (loaded via @webpieces/rules-config so ai-hooks and this\n * executor agree on every rule's enabled/mode/options). A validator runs only when\n * `shouldRun()` is true (i.e. its mode is not OFF and no branch/epoch escape hatch matches).\n */\nexport default async function runValidator(workspaceRoot: string): Promise<ExecutorResult> {\n const loaded = loadAndValidate(workspaceRoot);\n if (loaded.configPath === null) {\n console.error('\\n❌ No webpieces.config.json found at workspace root (or any ancestor).\\n');\n return { success: false };\n }\n\n console.log(`\\n📄 Loaded config: ${loaded.configPath}`);\n\n const validators = buildValidators(loaded.rulesConfig, loaded.matchRules);\n const active = validators.filter((v: CodeValidator<BaseRuleConfig>) => v.shouldRun());\n\n if (active.length === 0) {\n console.log('\\n⏭️ Skipping all code validations (all modes: OFF)\\n');\n return { success: true };\n }\n\n console.log('\\n📏 Running Code Validations\\n');\n console.log(` Active rules: ${active.map((v: CodeValidator<BaseRuleConfig>) => v.name).join(', ')}`);\n console.log('');\n\n // Per-validator isolation lives in runValidators: a bug in one validator no longer aborts the\n // rest, and a thrown RuleFailError is reported like any other failure.\n const result = await runValidators(active, workspaceRoot);\n const allSuccess = result.success;\n\n console.log(allSuccess ? '\\n✅ All code validations passed\\n' : '\\n❌ Some code validations failed\\n');\n return { success: allSuccess };\n}\n"]}
1
+ {"version":3,"file":"validate-code.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-code.ts"],"names":[],"mappings":";;AA2FA,+BA4BC;AAvHD,0DAsBiC;AAEjC,mDAAgD;AAChD,2EAAsE;AACtE,uEAAkE;AAClE,mEAAqE;AACrE,yEAA2E;AAC3E,uEAAkE;AAClE,yEAAoE;AACpE,mDAA8D;AAC9D,6EAAwE;AACxE,uEAAmE;AACnE,iFAA4E;AAC5E,yFAAoF;AACpF,uFAAiF;AACjF,iFAA2E;AAC3E,mGAA4F;AAC5F,6FAAuF;AACvF,yIAAgI;AAChI,qEAAiE;AACjE,2DAAuD;AACvD,iEAA6D;AAK7D;;;;;;GAMG;AACH,SAAS,eAAe,CACpB,MAA4B,EAC5B,UAAsC;IAEtC,OAAO;QACH,IAAI,mDAAuB,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,IAAI,mCAAoB,EAAE,CAAC;QACrF,IAAI,+CAAqB,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI,iCAAkB,EAAE,CAAC;QAC/E,IAAI,kDAA0B,CAAC,MAAM,CAAC,qBAAqB,CAAC,IAAI,IAAI,sCAAuB,EAAE,CAAC;QAC9F,IAAI,wDAA6B,CAAC,MAAM,CAAC,yBAAyB,CAAC,IAAI,IAAI,yCAA0B,EAAE,CAAC;QACxG,IAAI,+CAAqB,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI,iCAAkB,EAAE,CAAC;QAC/E,IAAI,iDAAsB,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,IAAI,kCAAmB,EAAE,CAAC;QAClF,IAAI,2CAA2B,CAAC,MAAM,CAAC,sBAAsB,CAAC,IAAI,IAAI,uCAAwB,EAAE,CAAC;QACjG,IAAI,qDAAwB,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,IAAI,oCAAqB,EAAE,CAAC;QACvF,IAAI,gDAAsB,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI,kCAAmB,EAAE,CAAC;QACjF,IAAI,yDAA0B,CAAC,MAAM,CAAC,qBAAqB,CAAC,IAAI,IAAI,sCAAuB,EAAE,CAAC;QAC9F,IAAI,iEAA8B,CAAC,MAAM,CAAC,yBAAyB,CAAC,IAAI,IAAI,0CAA2B,EAAE,CAAC;QAC1G,IAAI,8DAA4B,CAAC,MAAM,CAAC,mCAAmC,CAAC,IAAI,IAAI,iDAAkC,EAAE,CAAC;QACzH,IAAI,wDAAyB,CAAC,MAAM,CAAC,qBAAqB,CAAC,IAAI,IAAI,qCAAsB,EAAE,CAAC;QAC5F,IAAI,yEAAiC,CAAC,MAAM,CAAC,8BAA8B,CAAC,IAAI,IAAI,6CAA8B,EAAE,CAAC;QACrH,IAAI,oEAA+B,CAAC,MAAM,CAAC,2BAA2B,CAAC,IAAI,IAAI,2CAA4B,EAAE,CAAC;QAC9G,IAAI,6GAAkD,CAAC,MAAM,CAAC,iDAAiD,CAAC,IAAI,IAAI,8DAA+C,EAAE,CAAC;QAC1K,IAAI,8CAAqB,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,IAAI,iCAAkB,EAAE,CAAC;QAC9E,IAAI,oCAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,IAAI,4BAAa,EAAE,CAAC;QAC/D,2FAA2F;QAC3F,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,EAAmB,EAAE,EAAE,CAAC,IAAI,0CAAmB,CAAC,EAAE,CAAC,CAAC;KAC1E,CAAC;AACN,CAAC;AAED;;;;;;;GAOG;AACY,KAAK,UAAU,YAAY,CAAC,aAAqB;IAC5D,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,aAAa,CAAC,CAAC;IAC9C,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,2EAA2E,CAAC,CAAC;QAC3F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IAExD,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAgC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IAEtF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;QACtE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAgC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,8FAA8F;IAC9F,uEAAuE;IACvE,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAa,EAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;IAElC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,mCAAmC,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC;IACrG,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import {\n loadAndValidate,\n WebpiecesRulesConfig,\n BaseRuleConfig,\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n CatchErrorPatternConfig,\n NoUnmanagedExceptionsConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n} from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { runValidators } from './rule-reporter';\nimport { MaxMethodLinesValidator } from './validate-modified-methods';\nimport { MaxFileLinesValidator } from './validate-modified-files';\nimport { RequireReturnTypeValidator } from './validate-return-types';\nimport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nimport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nimport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nimport { PrismaValidateDtosValidator } from './validate-dtos';\nimport { PrismaConverterValidator } from './validate-prisma-converters';\nimport { NoDestructureValidator } from './validate-no-destructure';\nimport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nimport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nimport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nimport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nimport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nimport { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';\nimport { InjectAnnotationNotNeededForConcreteClassValidator } from './validate-inject-annotation-not-needed-for-concrete-class';\nimport { FrameworkTagValidator } from './validate-framework-tag';\nimport { RoleTagValidator } from './validate-role-tag';\nimport { MatchRulesValidator } from './validate-match-rules';\nimport { MatchRuleConfig } from '@webpieces/rules-config';\n\nexport { ExecutorResult } from './code-validator';\n\n/**\n * Build every code validator from the typed WebpiecesRulesConfig. Each validator is\n * constructed with its own `*Config` (the genuinely-consumed config class), so the\n * config types are exercised at compile time. Missing rule keys fall back to an empty\n * config instance — `loadAndValidate` already validates that configured rules\n * carry an explicit `mode`, so in practice every key is present.\n */\nfunction buildValidators(\n config: WebpiecesRulesConfig,\n matchRules: readonly MatchRuleConfig[],\n): CodeValidator<BaseRuleConfig>[] {\n return [\n new MaxMethodLinesValidator(config['max-method-lines'] ?? new MaxMethodLinesConfig()),\n new MaxFileLinesValidator(config['max-file-lines'] ?? new MaxFileLinesConfig()),\n new RequireReturnTypeValidator(config['require-return-type'] ?? new RequireReturnTypeConfig()),\n new NoInlineTypeLiteralsValidator(config['no-inline-type-literals'] ?? new NoInlineTypeLiteralsConfig()),\n new NoAnyUnknownValidator(config['no-any-unknown'] ?? new NoAnyUnknownConfig()),\n new NoImplicitAnyValidator(config['no-implicit-any'] ?? new NoImplicitAnyConfig()),\n new PrismaValidateDtosValidator(config['prisma-validate-dtos'] ?? new PrismaValidateDtosConfig()),\n new PrismaConverterValidator(config['prisma-converter'] ?? new PrismaConverterConfig()),\n new NoDestructureValidator(config['no-destructure'] ?? new NoDestructureConfig()),\n new CatchErrorPatternValidator(config['catch-error-pattern'] ?? new CatchErrorPatternConfig()),\n new NoUnmanagedExceptionsValidator(config['no-unmanaged-exceptions'] ?? new NoUnmanagedExceptionsConfig()),\n new NoDirectApiResolverValidator(config['angular-no-direct-api-in-resolver'] ?? new AngularNoDirectApiInResolverConfig()),\n new NoSymbolDiTokensValidator(config['no-symbol-di-tokens'] ?? new NoSymbolDiTokensConfig()),\n new NoProcessExitOutsideMainValidator(config['no-process-exit-outside-main'] ?? new NoProcessExitOutsideMainConfig()),\n new NoFunctionOutsideClassValidator(config['no-function-outside-class'] ?? new NoFunctionOutsideClassConfig()),\n new InjectAnnotationNotNeededForConcreteClassValidator(config['inject-annotation-not-needed-for-concrete-class'] ?? new InjectAnnotationNotNeededForConcreteClassConfig()),\n new FrameworkTagValidator(config['framework-tag'] ?? new FrameworkTagConfig()),\n new RoleTagValidator(config['role-tag'] ?? new RoleTagConfig()),\n // One validator per client-authored match-rules entry (each with its own name/mode/epoch).\n ...matchRules.map((mr: MatchRuleConfig) => new MatchRulesValidator(mr)),\n ];\n}\n\n/**\n * Run all configured code validators against the workspace.\n *\n * Owns config loading: callers just pass the workspace root. Config comes from\n * webpieces.config.json (loaded via @webpieces/rules-config so ai-hooks and this\n * executor agree on every rule's enabled/mode/options). A validator runs only when\n * `shouldRun()` is true (i.e. its mode is not OFF and no branch/epoch escape hatch matches).\n */\nexport default async function runValidator(workspaceRoot: string): Promise<ExecutorResult> {\n const loaded = loadAndValidate(workspaceRoot);\n if (loaded.configPath === null) {\n console.error('\\n❌ No webpieces.config.json found at workspace root (or any ancestor).\\n');\n return { success: false };\n }\n\n console.log(`\\n📄 Loaded config: ${loaded.configPath}`);\n\n const validators = buildValidators(loaded.rulesConfig, loaded.matchRules);\n const active = validators.filter((v: CodeValidator<BaseRuleConfig>) => v.shouldRun());\n\n if (active.length === 0) {\n console.log('\\n⏭️ Skipping all code validations (all modes: OFF)\\n');\n return { success: true };\n }\n\n console.log('\\n📏 Running Code Validations\\n');\n console.log(` Active rules: ${active.map((v: CodeValidator<BaseRuleConfig>) => v.name).join(', ')}`);\n console.log('');\n\n // Per-validator isolation lives in runValidators: a bug in one validator no longer aborts the\n // rest, and a thrown RuleFailError is reported like any other failure.\n const result = await runValidators(active, workspaceRoot);\n const allSuccess = result.success;\n\n console.log(allSuccess ? '\\n✅ All code validations passed\\n' : '\\n❌ Some code validations failed\\n');\n return { success: allSuccess };\n}\n"]}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Validate Inject Annotation Not Needed For Concrete Class
3
+ *
4
+ * Flags a REDUNDANT `@inject(X)` on a CONSTRUCTOR parameter whose token is textually identical to the
5
+ * parameter's own declared type:
6
+ *
7
+ * BAD: constructor(@inject(RequestContextHeaders) private readonly h: RequestContextHeaders) {}
8
+ * GOOD: constructor(private readonly h: RequestContextHeaders) {}
9
+ *
10
+ * WHY: in this inversify setup a constructor parameter is auto-resolved by its class TYPE via
11
+ * reflect-metadata (`emitDecoratorMetadata` emits `design:paramtypes`). So `private readonly h: Foo`
12
+ * already binds `Foo` — the `@inject(Foo)` adds nothing (see CLAUDE.md, and the sibling
13
+ * no-symbol-di-tokens rule that pushes the same inject-by-type pattern). AI keeps carpet-bombing
14
+ * `@inject`; this fails the build on the redundant form so it stops.
15
+ *
16
+ * Uses the TypeScript AST (not a regex) so "token equals the parameter type" is decided precisely.
17
+ * That single rule is what makes every LEGITIMATE token pass untouched — a real token never equals the
18
+ * type it resolves:
19
+ * - `@inject(TASK_PROXY_CLIENT_PROVIDER) x: Provider<TaskProxyClient>` (Symbol token ≠ type)
20
+ * - `@inject(WEBPIECES_CONFIG_TOKEN) c: WebpiecesConfig` (Symbol ≠ interface type)
21
+ * - `@inject('some-string') x: Foo` (string token ≠ type)
22
+ * - `@inject(Symbol.for('X')) x: Foo` (not a bare identifier)
23
+ *
24
+ * SCOPE: only CONSTRUCTOR parameters. Property injection (`@inject(Foo) private foo: Foo` on a class
25
+ * field) genuinely REQUIRES `@inject` in inversify — there is no design:type auto-wiring for properties
26
+ * — so it is left alone.
27
+ *
28
+ * ALLOWED (skip — NOT violations)
29
+ * - Any `@inject(TOKEN)` whose token differs from the parameter's type (Symbols, string tokens,
30
+ * provider tokens, interface tokens).
31
+ * - Property/field injection, and any parameter without an `@inject` decorator.
32
+ * - Test files (*.test.ts, *.spec.ts, __tests__/**).
33
+ * - Files under a configured `allowedPaths` glob (shared isPathExcluded glob/prefix/segment semantics).
34
+ * - Lines with `// webpieces-disable inject-annotation-not-needed-for-concrete-class -- <reason>`
35
+ * (only when disableAllowed).
36
+ *
37
+ * MODES (AST + LINE-SCOPED)
38
+ * - OFF: Skip.
39
+ * - NEW_AND_MODIFIED_CODE: Flag only on changed lines (diff hunks).
40
+ * - NEW_AND_MODIFIED_FILES: Flag every occurrence in any modified file.
41
+ */
42
+ import { InjectAnnotationNotNeededForConcreteClassConfig } from '@webpieces/rules-config';
43
+ import { CodeValidator, ExecutorResult } from './code-validator';
44
+ interface InjectViolationInfo {
45
+ line: number;
46
+ column: number;
47
+ context: string;
48
+ hasDisableComment: boolean;
49
+ }
50
+ export declare function findRedundantInjectInSource(content: string, filePath: string, disableAllowed: boolean): InjectViolationInfo[];
51
+ export declare class InjectAnnotationNotNeededForConcreteClassValidator extends CodeValidator<InjectAnnotationNotNeededForConcreteClassConfig> {
52
+ constructor(config: InjectAnnotationNotNeededForConcreteClassConfig);
53
+ run(workspaceRoot: string): Promise<ExecutorResult>;
54
+ }
55
+ export {};
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ /**
3
+ * Validate Inject Annotation Not Needed For Concrete Class
4
+ *
5
+ * Flags a REDUNDANT `@inject(X)` on a CONSTRUCTOR parameter whose token is textually identical to the
6
+ * parameter's own declared type:
7
+ *
8
+ * BAD: constructor(@inject(RequestContextHeaders) private readonly h: RequestContextHeaders) {}
9
+ * GOOD: constructor(private readonly h: RequestContextHeaders) {}
10
+ *
11
+ * WHY: in this inversify setup a constructor parameter is auto-resolved by its class TYPE via
12
+ * reflect-metadata (`emitDecoratorMetadata` emits `design:paramtypes`). So `private readonly h: Foo`
13
+ * already binds `Foo` — the `@inject(Foo)` adds nothing (see CLAUDE.md, and the sibling
14
+ * no-symbol-di-tokens rule that pushes the same inject-by-type pattern). AI keeps carpet-bombing
15
+ * `@inject`; this fails the build on the redundant form so it stops.
16
+ *
17
+ * Uses the TypeScript AST (not a regex) so "token equals the parameter type" is decided precisely.
18
+ * That single rule is what makes every LEGITIMATE token pass untouched — a real token never equals the
19
+ * type it resolves:
20
+ * - `@inject(TASK_PROXY_CLIENT_PROVIDER) x: Provider<TaskProxyClient>` (Symbol token ≠ type)
21
+ * - `@inject(WEBPIECES_CONFIG_TOKEN) c: WebpiecesConfig` (Symbol ≠ interface type)
22
+ * - `@inject('some-string') x: Foo` (string token ≠ type)
23
+ * - `@inject(Symbol.for('X')) x: Foo` (not a bare identifier)
24
+ *
25
+ * SCOPE: only CONSTRUCTOR parameters. Property injection (`@inject(Foo) private foo: Foo` on a class
26
+ * field) genuinely REQUIRES `@inject` in inversify — there is no design:type auto-wiring for properties
27
+ * — so it is left alone.
28
+ *
29
+ * ALLOWED (skip — NOT violations)
30
+ * - Any `@inject(TOKEN)` whose token differs from the parameter's type (Symbols, string tokens,
31
+ * provider tokens, interface tokens).
32
+ * - Property/field injection, and any parameter without an `@inject` decorator.
33
+ * - Test files (*.test.ts, *.spec.ts, __tests__/**).
34
+ * - Files under a configured `allowedPaths` glob (shared isPathExcluded glob/prefix/segment semantics).
35
+ * - Lines with `// webpieces-disable inject-annotation-not-needed-for-concrete-class -- <reason>`
36
+ * (only when disableAllowed).
37
+ *
38
+ * MODES (AST + LINE-SCOPED)
39
+ * - OFF: Skip.
40
+ * - NEW_AND_MODIFIED_CODE: Flag only on changed lines (diff hunks).
41
+ * - NEW_AND_MODIFIED_FILES: Flag every occurrence in any modified file.
42
+ */
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.InjectAnnotationNotNeededForConcreteClassValidator = void 0;
45
+ exports.findRedundantInjectInSource = findRedundantInjectInSource;
46
+ const tslib_1 = require("tslib");
47
+ const fs = tslib_1.__importStar(require("fs"));
48
+ const path = tslib_1.__importStar(require("path"));
49
+ const ts = tslib_1.__importStar(require("typescript"));
50
+ const rules_config_1 = require("@webpieces/rules-config");
51
+ const code_validator_1 = require("./code-validator");
52
+ const resolve_mode_1 = require("./resolve-mode");
53
+ const RULE_NAME = rules_config_1.RULE_NAMES.INJECT_ANNOTATION_NOT_NEEDED_FOR_CONCRETE_CLASS;
54
+ const SHARED_MESSAGE = `@inject(Foo) on a constructor parameter typed \`: Foo\` is noise — inversify resolves a constructor
55
+ parameter by its class type via reflect-metadata, so \`private readonly foo: Foo\` already binds it
56
+ (see CLAUDE.md). Pick one:
57
+ Fix Option 1 (PREFERRED): delete the decorator — constructor(private readonly foo: Foo) {}
58
+ Fix Option 2: if you actually need a DIFFERENT binding token (a Symbol for a generated
59
+ http-client-node / cloud-tasks client, or an interface/config with multiple impls), inject that
60
+ token so it DIFFERS from the type: @inject(FOO_TOKEN) foo: SomeInterface.
61
+ Property injection (@inject on a class field, not a constructor param) is NOT flagged — it genuinely
62
+ needs @inject. Only a constructor param whose token equals its own type is redundant.`;
63
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
64
+ function isTestFile(filePath) {
65
+ return filePath.includes('.spec.ts') || filePath.includes('.test.ts') || filePath.includes('__tests__/');
66
+ }
67
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
68
+ function isDeclarationFile(filePath) {
69
+ return filePath.endsWith('.d.ts');
70
+ }
71
+ // The violation line (or the line just above it) carries the disable comment.
72
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
73
+ function hasDisableOnLine(fileLines, lineNumber) {
74
+ const current = fileLines[lineNumber - 1] ?? '';
75
+ const previous = lineNumber >= 2 ? (fileLines[lineNumber - 2] ?? '') : '';
76
+ return (0, rules_config_1.hasDisable)(current, RULE_NAME) || (0, rules_config_1.hasDisable)(previous, RULE_NAME);
77
+ }
78
+ // The `@inject(X)` decorator's single argument, when it is a BARE identifier (a class/token name).
79
+ // Returns null for Symbol.for(...), property-access (`Symbol.x`), string literals, or a non-inject
80
+ // decorator — those can never equal the parameter type, so they are never redundant.
81
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
82
+ function injectIdentifierArg(decorator) {
83
+ const expr = decorator.expression;
84
+ if (!ts.isCallExpression(expr))
85
+ return null;
86
+ if (!ts.isIdentifier(expr.expression) || expr.expression.text !== 'inject')
87
+ return null;
88
+ if (expr.arguments.length !== 1)
89
+ return null;
90
+ const arg = expr.arguments[0];
91
+ return arg && ts.isIdentifier(arg) ? arg.text : null;
92
+ }
93
+ // The parameter's declared type when it is a simple `TypeReference` with an identifier name
94
+ // (`Foo`, or `Foo<Bar>` → `Foo`). Qualified names (`ns.Foo`) and non-references return null.
95
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
96
+ function paramTypeName(param) {
97
+ const typeNode = param.type;
98
+ if (!typeNode || !ts.isTypeReferenceNode(typeNode))
99
+ return null;
100
+ return ts.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : null;
101
+ }
102
+ // AST scan: a CONSTRUCTOR parameter is a violation when it carries `@inject(X)` and its declared type
103
+ // is exactly `X`. Property injection and method params are ignored (only constructor params).
104
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
105
+ function findRedundantInjectInSource(content, filePath, disableAllowed) {
106
+ if (isDeclarationFile(filePath))
107
+ return [];
108
+ const fileLines = content.split('\n');
109
+ const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
110
+ const violations = [];
111
+ function checkParameter(param) {
112
+ const decorators = ts.canHaveDecorators(param) ? ts.getDecorators(param) : undefined;
113
+ if (!decorators)
114
+ return;
115
+ const typeName = paramTypeName(param);
116
+ if (!typeName)
117
+ return;
118
+ for (const decorator of decorators) {
119
+ if (injectIdentifierArg(decorator) === typeName) {
120
+ recordViolation(param, `@inject(${typeName}) is redundant — the parameter is typed \`: ${typeName}\` and resolves by type`, fileLines, sourceFile, violations, disableAllowed);
121
+ return;
122
+ }
123
+ }
124
+ }
125
+ function visit(node) {
126
+ if (ts.isConstructorDeclaration(node)) {
127
+ for (const param of node.parameters)
128
+ checkParameter(param);
129
+ }
130
+ ts.forEachChild(node, visit);
131
+ }
132
+ visit(sourceFile);
133
+ return violations;
134
+ }
135
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
136
+ function recordViolation(node, context, fileLines, sourceFile, violations, disableAllowed) {
137
+ const startPos = node.getStart(sourceFile);
138
+ if (startPos < 0)
139
+ return;
140
+ const pos = sourceFile.getLineAndCharacterOfPosition(startPos);
141
+ const line = pos.line + 1;
142
+ const column = pos.character + 1;
143
+ const disabled = hasDisableOnLine(fileLines, line);
144
+ // When disableAllowed is false, a disable comment does NOT clear the violation.
145
+ const effectiveDisabled = disableAllowed ? disabled : false;
146
+ violations.push({ line, column, context, hasDisableComment: effectiveDisabled });
147
+ }
148
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
149
+ function findRedundantInjectInFile(filePath, workspaceRoot, disableAllowed, allowedPaths) {
150
+ if (isTestFile(filePath))
151
+ return [];
152
+ if ((0, rules_config_1.isPathExcluded)(filePath, allowedPaths))
153
+ return [];
154
+ const fullPath = path.join(workspaceRoot, filePath);
155
+ if (!fs.existsSync(fullPath))
156
+ return [];
157
+ const content = fs.readFileSync(fullPath, 'utf-8');
158
+ return findRedundantInjectInSource(content, filePath, disableAllowed);
159
+ }
160
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
161
+ function findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed, allowedPaths) {
162
+ const violations = [];
163
+ for (const file of changedFiles) {
164
+ const changedLines = (0, rules_config_1.getChangedLineNumbers)((0, rules_config_1.getFileDiff)(workspaceRoot, file, base, head));
165
+ if (changedLines.size === 0)
166
+ continue;
167
+ for (const v of findRedundantInjectInFile(file, workspaceRoot, disableAllowed, allowedPaths)) {
168
+ if (disableAllowed && v.hasDisableComment)
169
+ continue;
170
+ if (!changedLines.has(v.line))
171
+ continue;
172
+ violations.push({ file, line: v.line, column: v.column, context: v.context });
173
+ }
174
+ }
175
+ return violations;
176
+ }
177
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
178
+ function findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed, allowedPaths) {
179
+ const violations = [];
180
+ for (const file of changedFiles) {
181
+ for (const v of findRedundantInjectInFile(file, workspaceRoot, disableAllowed, allowedPaths)) {
182
+ if (disableAllowed && v.hasDisableComment)
183
+ continue;
184
+ violations.push({ file, line: v.line, column: v.column, context: v.context });
185
+ }
186
+ }
187
+ return violations;
188
+ }
189
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
190
+ function reportViolations(violations, mode, disableAllowed) {
191
+ console.error('');
192
+ console.error('❌ Redundant @inject — the parameter\'s type already resolves it.');
193
+ console.error('');
194
+ console.error(SHARED_MESSAGE);
195
+ console.error('');
196
+ for (const v of violations) {
197
+ console.error(` ❌ ${v.file}:${v.line}:${v.column}`);
198
+ console.error(` ${v.context}`);
199
+ }
200
+ console.error('');
201
+ console.error(disableAllowed
202
+ ? ` Escape hatch (use sparingly): // webpieces-disable ${RULE_NAME} -- <reason>`
203
+ : ' Escape hatch: DISABLED (disableAllowed: false)');
204
+ console.error(` Whole-tree exemption: add a glob to ${RULE_NAME}.allowedPaths in webpieces.config.json`);
205
+ console.error(`\n Current mode: ${mode}\n`);
206
+ }
207
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
208
+ function resolveMode(normalMode, epoch, branchPattern) {
209
+ if (normalMode === 'OFF')
210
+ return normalMode;
211
+ const skip = (0, resolve_mode_1.shouldSkipRule)(epoch, branchPattern);
212
+ if (skip.skip) {
213
+ console.log(`\n⏭️ Skipping ${RULE_NAME} validation (${skip.reason})\n`);
214
+ return 'OFF';
215
+ }
216
+ return normalMode;
217
+ }
218
+ // webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members
219
+ async function runValidatorImpl(options, workspaceRoot) {
220
+ const mode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);
221
+ const disableAllowed = options.disableAllowed ?? true;
222
+ const allowedPaths = options.allowedPaths ?? [];
223
+ if (mode === 'OFF') {
224
+ console.log(`\n⏭️ Skipping ${RULE_NAME} validation (mode: OFF)\n`);
225
+ return { success: true };
226
+ }
227
+ console.log(`\n📏 Validating ${RULE_NAME}\n`);
228
+ console.log(` Mode: ${mode}`);
229
+ let base = process.env['NX_BASE'];
230
+ const head = process.env['NX_HEAD'];
231
+ if (!base) {
232
+ base = (0, rules_config_1.detectBase)(workspaceRoot) ?? undefined;
233
+ if (!base) {
234
+ console.log(`\n⏭️ Skipping ${RULE_NAME} validation (could not detect base branch)\n`);
235
+ return { success: true };
236
+ }
237
+ }
238
+ console.log(` Base: ${base}`);
239
+ console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}\n`);
240
+ const changedFiles = (0, rules_config_1.getChangedFiles)(workspaceRoot, base, head);
241
+ if (changedFiles.length === 0) {
242
+ console.log('✅ No TypeScript files changed');
243
+ return { success: true };
244
+ }
245
+ console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
246
+ let violations = [];
247
+ if (mode === 'NEW_AND_MODIFIED_CODE') {
248
+ violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed, allowedPaths);
249
+ }
250
+ else if (mode === 'NEW_AND_MODIFIED_FILES') {
251
+ violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed, allowedPaths);
252
+ }
253
+ if (violations.length === 0) {
254
+ console.log('✅ No redundant-@inject violations found');
255
+ return { success: true };
256
+ }
257
+ reportViolations(violations, mode, disableAllowed);
258
+ return { success: false };
259
+ }
260
+ class InjectAnnotationNotNeededForConcreteClassValidator extends code_validator_1.CodeValidator {
261
+ constructor(config) {
262
+ super(config, RULE_NAME);
263
+ }
264
+ async run(workspaceRoot) {
265
+ return runValidatorImpl(this.config, workspaceRoot);
266
+ }
267
+ }
268
+ exports.InjectAnnotationNotNeededForConcreteClassValidator = InjectAnnotationNotNeededForConcreteClassValidator;
269
+ //# sourceMappingURL=validate-inject-annotation-not-needed-for-concrete-class.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-inject-annotation-not-needed-for-concrete-class.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-inject-annotation-not-needed-for-concrete-class.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Inject Annotation Not Needed For Concrete Class\n *\n * Flags a REDUNDANT `@inject(X)` on a CONSTRUCTOR parameter whose token is textually identical to the\n * parameter's own declared type:\n *\n * BAD: constructor(@inject(RequestContextHeaders) private readonly h: RequestContextHeaders) {}\n * GOOD: constructor(private readonly h: RequestContextHeaders) {}\n *\n * WHY: in this inversify setup a constructor parameter is auto-resolved by its class TYPE via\n * reflect-metadata (`emitDecoratorMetadata` emits `design:paramtypes`). So `private readonly h: Foo`\n * already binds `Foo` — the `@inject(Foo)` adds nothing (see CLAUDE.md, and the sibling\n * no-symbol-di-tokens rule that pushes the same inject-by-type pattern). AI keeps carpet-bombing\n * `@inject`; this fails the build on the redundant form so it stops.\n *\n * Uses the TypeScript AST (not a regex) so \"token equals the parameter type\" is decided precisely.\n * That single rule is what makes every LEGITIMATE token pass untouched — a real token never equals the\n * type it resolves:\n * - `@inject(TASK_PROXY_CLIENT_PROVIDER) x: Provider<TaskProxyClient>` (Symbol token ≠ type)\n * - `@inject(WEBPIECES_CONFIG_TOKEN) c: WebpiecesConfig` (Symbol ≠ interface type)\n * - `@inject('some-string') x: Foo` (string token ≠ type)\n * - `@inject(Symbol.for('X')) x: Foo` (not a bare identifier)\n *\n * SCOPE: only CONSTRUCTOR parameters. Property injection (`@inject(Foo) private foo: Foo` on a class\n * field) genuinely REQUIRES `@inject` in inversify — there is no design:type auto-wiring for properties\n * — so it is left alone.\n *\n * ALLOWED (skip — NOT violations)\n * - Any `@inject(TOKEN)` whose token differs from the parameter's type (Symbols, string tokens,\n * provider tokens, interface tokens).\n * - Property/field injection, and any parameter without an `@inject` decorator.\n * - Test files (*.test.ts, *.spec.ts, __tests__/**).\n * - Files under a configured `allowedPaths` glob (shared isPathExcluded glob/prefix/segment semantics).\n * - Lines with `// webpieces-disable inject-annotation-not-needed-for-concrete-class -- <reason>`\n * (only when disableAllowed).\n *\n * MODES (AST + LINE-SCOPED)\n * - OFF: Skip.\n * - NEW_AND_MODIFIED_CODE: Flag only on changed lines (diff hunks).\n * - NEW_AND_MODIFIED_FILES: Flag every occurrence in any modified file.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\nimport { hasDisable, RULE_NAMES, InjectAnnotationNotNeededForConcreteClassConfig, ModifiedCodeMode, detectBase, getChangedFiles, getFileDiff, getChangedLineNumbers, isPathExcluded } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\nconst RULE_NAME = RULE_NAMES.INJECT_ANNOTATION_NOT_NEEDED_FOR_CONCRETE_CLASS;\n\nconst SHARED_MESSAGE = `@inject(Foo) on a constructor parameter typed \\`: Foo\\` is noise — inversify resolves a constructor\nparameter by its class type via reflect-metadata, so \\`private readonly foo: Foo\\` already binds it\n(see CLAUDE.md). Pick one:\n Fix Option 1 (PREFERRED): delete the decorator — constructor(private readonly foo: Foo) {}\n Fix Option 2: if you actually need a DIFFERENT binding token (a Symbol for a generated\n http-client-node / cloud-tasks client, or an interface/config with multiple impls), inject that\n token so it DIFFERS from the type: @inject(FOO_TOKEN) foo: SomeInterface.\nProperty injection (@inject on a class field, not a constructor param) is NOT flagged — it genuinely\nneeds @inject. Only a constructor param whose token equals its own type is redundant.`;\n\ninterface InjectViolation {\n file: string;\n line: number;\n column: number;\n context: string;\n}\n\ninterface InjectViolationInfo {\n line: number;\n column: number;\n context: string;\n hasDisableComment: boolean;\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction isTestFile(filePath: string): boolean {\n return filePath.includes('.spec.ts') || filePath.includes('.test.ts') || filePath.includes('__tests__/');\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction isDeclarationFile(filePath: string): boolean {\n return filePath.endsWith('.d.ts');\n}\n\n// The violation line (or the line just above it) carries the disable comment.\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction hasDisableOnLine(fileLines: string[], lineNumber: number): boolean {\n const current = fileLines[lineNumber - 1] ?? '';\n const previous = lineNumber >= 2 ? (fileLines[lineNumber - 2] ?? '') : '';\n return hasDisable(current, RULE_NAME) || hasDisable(previous, RULE_NAME);\n}\n\n// The `@inject(X)` decorator's single argument, when it is a BARE identifier (a class/token name).\n// Returns null for Symbol.for(...), property-access (`Symbol.x`), string literals, or a non-inject\n// decorator — those can never equal the parameter type, so they are never redundant.\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction injectIdentifierArg(decorator: ts.Decorator): string | null {\n const expr = decorator.expression;\n if (!ts.isCallExpression(expr)) return null;\n if (!ts.isIdentifier(expr.expression) || expr.expression.text !== 'inject') return null;\n if (expr.arguments.length !== 1) return null;\n const arg = expr.arguments[0];\n return arg && ts.isIdentifier(arg) ? arg.text : null;\n}\n\n// The parameter's declared type when it is a simple `TypeReference` with an identifier name\n// (`Foo`, or `Foo<Bar>` → `Foo`). Qualified names (`ns.Foo`) and non-references return null.\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction paramTypeName(param: ts.ParameterDeclaration): string | null {\n const typeNode = param.type;\n if (!typeNode || !ts.isTypeReferenceNode(typeNode)) return null;\n return ts.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : null;\n}\n\n// AST scan: a CONSTRUCTOR parameter is a violation when it carries `@inject(X)` and its declared type\n// is exactly `X`. Property injection and method params are ignored (only constructor params).\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nexport function findRedundantInjectInSource(content: string, filePath: string, disableAllowed: boolean): InjectViolationInfo[] {\n if (isDeclarationFile(filePath)) return [];\n const fileLines = content.split('\\n');\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n const violations: InjectViolationInfo[] = [];\n\n function checkParameter(param: ts.ParameterDeclaration): void {\n const decorators = ts.canHaveDecorators(param) ? ts.getDecorators(param) : undefined;\n if (!decorators) return;\n const typeName = paramTypeName(param);\n if (!typeName) return;\n for (const decorator of decorators) {\n if (injectIdentifierArg(decorator) === typeName) {\n recordViolation(param, `@inject(${typeName}) is redundant — the parameter is typed \\`: ${typeName}\\` and resolves by type`, fileLines, sourceFile, violations, disableAllowed);\n return;\n }\n }\n }\n\n function visit(node: ts.Node): void {\n if (ts.isConstructorDeclaration(node)) {\n for (const param of node.parameters) checkParameter(param);\n }\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return violations;\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction recordViolation(\n node: ts.Node,\n context: string,\n fileLines: string[],\n sourceFile: ts.SourceFile,\n violations: InjectViolationInfo[],\n disableAllowed: boolean,\n): void {\n const startPos = node.getStart(sourceFile);\n if (startPos < 0) return;\n const pos = sourceFile.getLineAndCharacterOfPosition(startPos);\n const line = pos.line + 1;\n const column = pos.character + 1;\n const disabled = hasDisableOnLine(fileLines, line);\n // When disableAllowed is false, a disable comment does NOT clear the violation.\n const effectiveDisabled = disableAllowed ? disabled : false;\n violations.push({ line, column, context, hasDisableComment: effectiveDisabled });\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction findRedundantInjectInFile(filePath: string, workspaceRoot: string, disableAllowed: boolean, allowedPaths: string[]): InjectViolationInfo[] {\n if (isTestFile(filePath)) return [];\n if (isPathExcluded(filePath, allowedPaths)) return [];\n const fullPath = path.join(workspaceRoot, filePath);\n if (!fs.existsSync(fullPath)) return [];\n const content = fs.readFileSync(fullPath, 'utf-8');\n return findRedundantInjectInSource(content, filePath, disableAllowed);\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction findViolationsForModifiedCode(workspaceRoot: string, changedFiles: string[], base: string, head: string | undefined, disableAllowed: boolean, allowedPaths: string[]): InjectViolation[] {\n const violations: InjectViolation[] = [];\n for (const file of changedFiles) {\n const changedLines = getChangedLineNumbers(getFileDiff(workspaceRoot, file, base, head));\n if (changedLines.size === 0) continue;\n for (const v of findRedundantInjectInFile(file, workspaceRoot, disableAllowed, allowedPaths)) {\n if (disableAllowed && v.hasDisableComment) continue;\n if (!changedLines.has(v.line)) continue;\n violations.push({ file, line: v.line, column: v.column, context: v.context });\n }\n }\n return violations;\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction findViolationsForModifiedFiles(workspaceRoot: string, changedFiles: string[], disableAllowed: boolean, allowedPaths: string[]): InjectViolation[] {\n const violations: InjectViolation[] = [];\n for (const file of changedFiles) {\n for (const v of findRedundantInjectInFile(file, workspaceRoot, disableAllowed, allowedPaths)) {\n if (disableAllowed && v.hasDisableComment) continue;\n violations.push({ file, line: v.line, column: v.column, context: v.context });\n }\n }\n return violations;\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction reportViolations(violations: InjectViolation[], mode: ModifiedCodeMode, disableAllowed: boolean): void {\n console.error('');\n console.error('❌ Redundant @inject — the parameter\\'s type already resolves it.');\n console.error('');\n console.error(SHARED_MESSAGE);\n console.error('');\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}:${v.column}`);\n console.error(` ${v.context}`);\n }\n console.error('');\n console.error(disableAllowed\n ? ` Escape hatch (use sparingly): // webpieces-disable ${RULE_NAME} -- <reason>`\n : ' Escape hatch: DISABLED (disableAllowed: false)');\n console.error(` Whole-tree exemption: add a glob to ${RULE_NAME}.allowedPaths in webpieces.config.json`);\n console.error(`\\n Current mode: ${mode}\\n`);\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nfunction resolveMode(normalMode: ModifiedCodeMode, epoch: number | undefined, branchPattern: string | undefined): ModifiedCodeMode {\n if (normalMode === 'OFF') return normalMode;\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping ${RULE_NAME} validation (${skip.reason})\\n`);\n return 'OFF';\n }\n return normalMode;\n}\n\n// webpieces-disable no-function-outside-class -- the rule engine is inherently functional; validators can't be class members\nasync function runValidatorImpl(options: InjectAnnotationNotNeededForConcreteClassConfig, workspaceRoot: string): Promise<ExecutorResult> {\n const mode: ModifiedCodeMode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);\n const disableAllowed = options.disableAllowed ?? true;\n const allowedPaths = options.allowedPaths ?? [];\n\n if (mode === 'OFF') {\n console.log(`\\n⏭️ Skipping ${RULE_NAME} validation (mode: OFF)\\n`);\n return { success: true };\n }\n\n console.log(`\\n📏 Validating ${RULE_NAME}\\n`);\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n if (!base) {\n console.log(`\\n⏭️ Skipping ${RULE_NAME} validation (could not detect base branch)\\n`);\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}\\n`);\n\n const changedFiles = getChangedFiles(workspaceRoot, base, head);\n if (changedFiles.length === 0) {\n console.log('✅ No TypeScript files changed');\n return { success: true };\n }\n\n console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);\n\n let violations: InjectViolation[] = [];\n if (mode === 'NEW_AND_MODIFIED_CODE') {\n violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed, allowedPaths);\n } else if (mode === 'NEW_AND_MODIFIED_FILES') {\n violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed, allowedPaths);\n }\n\n if (violations.length === 0) {\n console.log('✅ No redundant-@inject violations found');\n return { success: true };\n }\n\n reportViolations(violations, mode, disableAllowed);\n return { success: false };\n}\n\nexport class InjectAnnotationNotNeededForConcreteClassValidator extends CodeValidator<InjectAnnotationNotNeededForConcreteClassConfig> {\n constructor(config: InjectAnnotationNotNeededForConcreteClassConfig) {\n super(config, RULE_NAME);\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}