@webpieces/code-rules 0.3.244 → 0.3.246

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.244",
3
+ "version": "0.3.246",
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.244"
23
+ "@webpieces/rules-config": "0.3.246"
24
24
  },
25
25
  "publishConfig": {
26
26
  "access": "public"
package/src/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export { NoDestructureValidator } from './validate-no-destructure';
12
12
  export { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';
13
13
  export { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';
14
14
  export { EnforceControllerNamingValidator } from './validate-enforce-controller-naming';
15
+ export { FrameworkTagValidator } from './validate-framework-tag';
15
16
  export { PrismaValidateDtosValidator } from './validate-dtos';
16
17
  export { PrismaConverterValidator } from './validate-prisma-converters';
17
18
  export { default as validateCode } from './validate-code';
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.EnforceControllerNamingValidator = 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.FrameworkTagValidator = exports.EnforceControllerNamingValidator = 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; } });
@@ -31,6 +31,8 @@ var validate_no_symbol_di_tokens_1 = require("./validate-no-symbol-di-tokens");
31
31
  Object.defineProperty(exports, "NoSymbolDiTokensValidator", { enumerable: true, get: function () { return validate_no_symbol_di_tokens_1.NoSymbolDiTokensValidator; } });
32
32
  var validate_enforce_controller_naming_1 = require("./validate-enforce-controller-naming");
33
33
  Object.defineProperty(exports, "EnforceControllerNamingValidator", { enumerable: true, get: function () { return validate_enforce_controller_naming_1.EnforceControllerNamingValidator; } });
34
+ var validate_framework_tag_1 = require("./validate-framework-tag");
35
+ Object.defineProperty(exports, "FrameworkTagValidator", { enumerable: true, get: function () { return validate_framework_tag_1.FrameworkTagValidator; } });
34
36
  var validate_dtos_1 = require("./validate-dtos");
35
37
  Object.defineProperty(exports, "PrismaValidateDtosValidator", { enumerable: true, get: function () { return validate_dtos_1.PrismaValidateDtosValidator; } });
36
38
  var validate_prisma_converters_1 = require("./validate-prisma-converters");
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,2FAAwF;AAA/E,sJAAA,gCAAgC,OAAA;AACzC,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 { EnforceControllerNamingValidator } from './validate-enforce-controller-naming';\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,2FAAwF;AAA/E,sJAAA,gCAAgC,OAAA;AACzC,mEAAiE;AAAxD,+HAAA,qBAAqB,OAAA;AAC9B,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 { EnforceControllerNamingValidator } from './validate-enforce-controller-naming';\nexport { FrameworkTagValidator } from './validate-framework-tag';\nexport { PrismaValidateDtosValidator } from './validate-dtos';\nexport { PrismaConverterValidator } from './validate-prisma-converters';\nexport { default as validateCode } from './validate-code';\n"]}
@@ -17,6 +17,7 @@ const validate_no_unmanaged_exceptions_1 = require("./validate-no-unmanaged-exce
17
17
  const validate_no_direct_api_resolver_1 = require("./validate-no-direct-api-resolver");
18
18
  const validate_no_symbol_di_tokens_1 = require("./validate-no-symbol-di-tokens");
19
19
  const validate_enforce_controller_naming_1 = require("./validate-enforce-controller-naming");
20
+ const validate_framework_tag_1 = require("./validate-framework-tag");
20
21
  const validate_match_rules_1 = require("./validate-match-rules");
21
22
  /**
22
23
  * Build every code validator from the typed WebpiecesRulesConfig. Each validator is
@@ -41,6 +42,7 @@ function buildValidators(config, matchRules) {
41
42
  new validate_no_direct_api_resolver_1.NoDirectApiResolverValidator(config['angular-no-direct-api-in-resolver'] ?? new rules_config_1.AngularNoDirectApiInResolverConfig()),
42
43
  new validate_no_symbol_di_tokens_1.NoSymbolDiTokensValidator(config['no-symbol-di-tokens'] ?? new rules_config_1.NoSymbolDiTokensConfig()),
43
44
  new validate_enforce_controller_naming_1.EnforceControllerNamingValidator(config['enforce-controller-naming'] ?? new rules_config_1.EnforceControllerNamingConfig()),
45
+ new validate_framework_tag_1.FrameworkTagValidator(config['framework-tag'] ?? new rules_config_1.FrameworkTagConfig()),
44
46
  // One validator per client-authored match-rules entry (each with its own name/mode/epoch).
45
47
  ...matchRules.map((mr) => new validate_match_rules_1.MatchRulesValidator(mr)),
46
48
  ];
@@ -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 EnforceControllerNamingConfig,\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 { EnforceControllerNamingValidator } from './validate-enforce-controller-naming';\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 EnforceControllerNamingValidator(config['enforce-controller-naming'] ?? new EnforceControllerNamingConfig()),\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":"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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 EnforceControllerNamingConfig,\n FrameworkTagConfig,\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 { EnforceControllerNamingValidator } from './validate-enforce-controller-naming';\nimport { FrameworkTagValidator } from './validate-framework-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 EnforceControllerNamingValidator(config['enforce-controller-naming'] ?? new EnforceControllerNamingConfig()),\n new FrameworkTagValidator(config['framework-tag'] ?? new FrameworkTagConfig()),\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,51 @@
1
+ /**
2
+ * Validate Framework Tag Executor
3
+ *
4
+ * Every project that a changed source file belongs to must carry a
5
+ * `framework:<value>` nx tag in its project.json. That tag is the project's
6
+ * "libType" — which client side it targets:
7
+ *
8
+ * framework:angular | framework:react | framework:express | framework:all
9
+ *
10
+ * ("all" = a library usable by any side.) It is the source of truth for the
11
+ * `framework` field written into architecture/dependencies.json and for the
12
+ * `library-types-match-client` rule, which uses it to keep an express project
13
+ * from depending on an angular-only library (and vice-versa).
14
+ *
15
+ * ============================================================================
16
+ * VIOLATION (BAD)
17
+ * ============================================================================
18
+ * A project.json with no `framework:` tag whose source was modified:
19
+ * { "name": "my-lib", "tags": [] }
20
+ *
21
+ * ============================================================================
22
+ * ALLOWED
23
+ * ============================================================================
24
+ * { "name": "my-lib", "tags": ["framework:all"] }
25
+ *
26
+ * ============================================================================
27
+ * MODES (PROJECT-BASED)
28
+ * ============================================================================
29
+ * - OFF: Skip validation entirely
30
+ * - NEW_AND_MODIFIED_CODE
31
+ * / NEW_AND_MODIFIED_FILES: Require a framework tag on every project that owns
32
+ * a changed .ts file (both modes behave the same here
33
+ * — the check is project-level, not line-level).
34
+ */
35
+ import { FrameworkTagConfig } from '@webpieces/rules-config';
36
+ import { CodeValidator, ExecutorResult } from './code-validator';
37
+ /**
38
+ * A project (identified by its project.json) that owns at least one changed
39
+ * file but is missing a `framework:` tag.
40
+ */
41
+ declare class UntaggedProject {
42
+ readonly name: string;
43
+ readonly projectJsonPath: string;
44
+ constructor(name: string, projectJsonPath: string);
45
+ }
46
+ export declare function findUntaggedProjects(workspaceRoot: string, changedFiles: string[]): UntaggedProject[];
47
+ export declare class FrameworkTagValidator extends CodeValidator<FrameworkTagConfig> {
48
+ constructor(config: FrameworkTagConfig);
49
+ run(workspaceRoot: string): Promise<ExecutorResult>;
50
+ }
51
+ export {};
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ /**
3
+ * Validate Framework Tag Executor
4
+ *
5
+ * Every project that a changed source file belongs to must carry a
6
+ * `framework:<value>` nx tag in its project.json. That tag is the project's
7
+ * "libType" — which client side it targets:
8
+ *
9
+ * framework:angular | framework:react | framework:express | framework:all
10
+ *
11
+ * ("all" = a library usable by any side.) It is the source of truth for the
12
+ * `framework` field written into architecture/dependencies.json and for the
13
+ * `library-types-match-client` rule, which uses it to keep an express project
14
+ * from depending on an angular-only library (and vice-versa).
15
+ *
16
+ * ============================================================================
17
+ * VIOLATION (BAD)
18
+ * ============================================================================
19
+ * A project.json with no `framework:` tag whose source was modified:
20
+ * { "name": "my-lib", "tags": [] }
21
+ *
22
+ * ============================================================================
23
+ * ALLOWED
24
+ * ============================================================================
25
+ * { "name": "my-lib", "tags": ["framework:all"] }
26
+ *
27
+ * ============================================================================
28
+ * MODES (PROJECT-BASED)
29
+ * ============================================================================
30
+ * - OFF: Skip validation entirely
31
+ * - NEW_AND_MODIFIED_CODE
32
+ * / NEW_AND_MODIFIED_FILES: Require a framework tag on every project that owns
33
+ * a changed .ts file (both modes behave the same here
34
+ * — the check is project-level, not line-level).
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.FrameworkTagValidator = void 0;
38
+ exports.findUntaggedProjects = findUntaggedProjects;
39
+ const tslib_1 = require("tslib");
40
+ const fs = tslib_1.__importStar(require("fs"));
41
+ const path = tslib_1.__importStar(require("path"));
42
+ const rules_config_1 = require("@webpieces/rules-config");
43
+ const code_validator_1 = require("./code-validator");
44
+ const resolve_mode_1 = require("./resolve-mode");
45
+ const FRAMEWORK_TAG_PREFIX = 'framework:';
46
+ const DEFAULT_KNOWN_TYPES = ['angular', 'react', 'express', 'all'];
47
+ /**
48
+ * A project (identified by its project.json) that owns at least one changed
49
+ * file but is missing a `framework:` tag.
50
+ */
51
+ class UntaggedProject {
52
+ name;
53
+ projectJsonPath;
54
+ constructor(name, projectJsonPath) {
55
+ this.name = name;
56
+ this.projectJsonPath = projectJsonPath;
57
+ }
58
+ }
59
+ /**
60
+ * The parts of a project.json this rule reads.
61
+ */
62
+ class ProjectJson {
63
+ name;
64
+ tags;
65
+ constructor(name, tags) {
66
+ this.name = name;
67
+ this.tags = tags;
68
+ }
69
+ }
70
+ /**
71
+ * Parse the `name` + `tags` from a project.json. Returns an empty ProjectJson
72
+ * (no name, no tags) when the file is unreadable/unparseable — config
73
+ * validation owns malformed-json reporting; this rule just nudges for a tag and
74
+ * must never crash the build.
75
+ */
76
+ function readProjectJson(projectJsonPath, workspaceRoot) {
77
+ const fullPath = path.join(workspaceRoot, projectJsonPath);
78
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
79
+ try {
80
+ const parsed = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
81
+ const name = typeof parsed.name === 'string' && parsed.name.length > 0 ? parsed.name : null;
82
+ const tags = Array.isArray(parsed.tags) ? parsed.tags.filter((tag) => typeof tag === 'string') : [];
83
+ return new ProjectJson(name, tags);
84
+ }
85
+ catch (err) {
86
+ const error = (0, rules_config_1.toError)(err);
87
+ void error; // swallow — malformed project.json is not this rule's concern
88
+ return new ProjectJson(null, []);
89
+ }
90
+ }
91
+ /**
92
+ * Walk up from a changed file to the nearest ancestor directory containing a
93
+ * project.json (the file's owning nx project). Returns the repo-relative
94
+ * project.json path, or null when the file belongs to no project (e.g. a
95
+ * workspace-root config file).
96
+ */
97
+ function findOwningProjectJson(changedFile, workspaceRoot) {
98
+ let dir = path.dirname(changedFile);
99
+ while (true) {
100
+ const candidate = path.join(dir, 'project.json');
101
+ if (fs.existsSync(path.join(workspaceRoot, candidate))) {
102
+ return candidate;
103
+ }
104
+ const parent = path.dirname(dir);
105
+ if (parent === dir || dir === '.' || dir === '') {
106
+ return null;
107
+ }
108
+ dir = parent;
109
+ }
110
+ }
111
+ function hasFrameworkTag(tags) {
112
+ return tags.some((tag) => tag.startsWith(FRAMEWORK_TAG_PREFIX) && tag.slice(FRAMEWORK_TAG_PREFIX.length).trim().length > 0);
113
+ }
114
+ function findUntaggedProjects(workspaceRoot, changedFiles) {
115
+ const projectJsonPaths = new Set();
116
+ for (const file of changedFiles) {
117
+ const owning = findOwningProjectJson(file, workspaceRoot);
118
+ if (owning !== null) {
119
+ projectJsonPaths.add(owning);
120
+ }
121
+ }
122
+ const untagged = [];
123
+ for (const projectJsonPath of Array.from(projectJsonPaths).sort()) {
124
+ const projectJson = readProjectJson(projectJsonPath, workspaceRoot);
125
+ if (!hasFrameworkTag(projectJson.tags)) {
126
+ const name = projectJson.name ?? path.dirname(projectJsonPath);
127
+ untagged.push(new UntaggedProject(name, projectJsonPath));
128
+ }
129
+ }
130
+ return untagged;
131
+ }
132
+ function reportViolations(untagged, knownTypes, mode) {
133
+ const suggestion = knownTypes.map((t) => `${FRAMEWORK_TAG_PREFIX}${t}`).join(' | ');
134
+ console.error('');
135
+ console.error('❌ Every modified project must declare which client side it targets (a framework tag)!');
136
+ console.error('');
137
+ console.error(` Add ONE of these to the project's project.json "tags" array: ${suggestion}`);
138
+ console.error(' ("all" = a library usable by any side — angular, react and express can all import it.)');
139
+ console.error(' This libType drives architecture/dependencies.json and the library-types-match-client rule.');
140
+ console.error('');
141
+ for (const project of untagged) {
142
+ console.error(` ❌ ${project.name} — ${project.projectJsonPath}`);
143
+ console.error(` add e.g. "tags": ["${FRAMEWORK_TAG_PREFIX}all"]`);
144
+ }
145
+ console.error('');
146
+ console.error(` Current mode: ${mode}`);
147
+ console.error('');
148
+ }
149
+ function resolveMode(normalMode, epoch, branchPattern) {
150
+ if (normalMode === 'OFF') {
151
+ return normalMode;
152
+ }
153
+ const skip = (0, resolve_mode_1.shouldSkipRule)(epoch, branchPattern);
154
+ if (skip.skip) {
155
+ console.log(`\n⏭️ Skipping framework-tag validation (${skip.reason})`);
156
+ console.log('');
157
+ return 'OFF';
158
+ }
159
+ return normalMode;
160
+ }
161
+ async function runValidatorImpl(options, workspaceRoot) {
162
+ const mode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);
163
+ const knownTypes = options.knownTypes && options.knownTypes.length > 0 ? options.knownTypes : DEFAULT_KNOWN_TYPES;
164
+ if (mode === 'OFF') {
165
+ console.log('\n⏭️ Skipping framework-tag validation (mode: OFF)');
166
+ console.log('');
167
+ return { success: true };
168
+ }
169
+ console.log('\n📏 Validating Framework Tag\n');
170
+ console.log(` Mode: ${mode}`);
171
+ let base = process.env['NX_BASE'];
172
+ const head = process.env['NX_HEAD'];
173
+ if (!base) {
174
+ base = (0, rules_config_1.detectBase)(workspaceRoot) ?? undefined;
175
+ if (!base) {
176
+ console.log('\n⏭️ Skipping framework-tag validation (could not detect base branch)');
177
+ console.log('');
178
+ return { success: true };
179
+ }
180
+ }
181
+ console.log(` Base: ${base}`);
182
+ console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);
183
+ console.log('');
184
+ const changedFiles = (0, rules_config_1.getChangedFiles)(workspaceRoot, base, head);
185
+ if (changedFiles.length === 0) {
186
+ console.log('✅ No TypeScript files changed');
187
+ return { success: true };
188
+ }
189
+ const untagged = findUntaggedProjects(workspaceRoot, changedFiles);
190
+ if (untagged.length === 0) {
191
+ console.log('✅ Every modified project declares a framework tag');
192
+ return { success: true };
193
+ }
194
+ reportViolations(untagged, knownTypes, mode);
195
+ return { success: false };
196
+ }
197
+ class FrameworkTagValidator extends code_validator_1.CodeValidator {
198
+ constructor(config) {
199
+ super(config, 'framework-tag');
200
+ }
201
+ async run(workspaceRoot) {
202
+ return runValidatorImpl(this.config, workspaceRoot);
203
+ }
204
+ }
205
+ exports.FrameworkTagValidator = FrameworkTagValidator;
206
+ //# sourceMappingURL=validate-framework-tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-framework-tag.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-framework-tag.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Framework Tag Executor\n *\n * Every project that a changed source file belongs to must carry a\n * `framework:<value>` nx tag in its project.json. That tag is the project's\n * \"libType\" — which client side it targets:\n *\n * framework:angular | framework:react | framework:express | framework:all\n *\n * (\"all\" = a library usable by any side.) It is the source of truth for the\n * `framework` field written into architecture/dependencies.json and for the\n * `library-types-match-client` rule, which uses it to keep an express project\n * from depending on an angular-only library (and vice-versa).\n *\n * ============================================================================\n * VIOLATION (BAD)\n * ============================================================================\n * A project.json with no `framework:` tag whose source was modified:\n * { \"name\": \"my-lib\", \"tags\": [] }\n *\n * ============================================================================\n * ALLOWED\n * ============================================================================\n * { \"name\": \"my-lib\", \"tags\": [\"framework:all\"] }\n *\n * ============================================================================\n * MODES (PROJECT-BASED)\n * ============================================================================\n * - OFF: Skip validation entirely\n * - NEW_AND_MODIFIED_CODE\n * / NEW_AND_MODIFIED_FILES: Require a framework tag on every project that owns\n * a changed .ts file (both modes behave the same here\n * — the check is project-level, not line-level).\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { ModifiedCodeMode, FrameworkTagConfig, detectBase, getChangedFiles, toError } from '@webpieces/rules-config';\nimport { CodeValidator, ExecutorResult } from './code-validator';\nimport { shouldSkipRule } from './resolve-mode';\n\nconst FRAMEWORK_TAG_PREFIX = 'framework:';\nconst DEFAULT_KNOWN_TYPES = ['angular', 'react', 'express', 'all'];\n\n/**\n * A project (identified by its project.json) that owns at least one changed\n * file but is missing a `framework:` tag.\n */\nclass UntaggedProject {\n constructor(\n public readonly name: string,\n public readonly projectJsonPath: string\n ) {}\n}\n\n/**\n * Raw shape parsed out of a project.json (only the fields this rule reads).\n */\ntype RawProjectJson = { name?: string; tags?: string[] };\n\n/**\n * The parts of a project.json this rule reads.\n */\nclass ProjectJson {\n constructor(\n public readonly name: string | null,\n public readonly tags: string[]\n ) {}\n}\n\n/**\n * Parse the `name` + `tags` from a project.json. Returns an empty ProjectJson\n * (no name, no tags) when the file is unreadable/unparseable — config\n * validation owns malformed-json reporting; this rule just nudges for a tag and\n * must never crash the build.\n */\nfunction readProjectJson(projectJsonPath: string, workspaceRoot: string): ProjectJson {\n const fullPath = path.join(workspaceRoot, projectJsonPath);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const parsed: RawProjectJson = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const name = typeof parsed.name === 'string' && parsed.name.length > 0 ? parsed.name : null;\n const tags = Array.isArray(parsed.tags) ? parsed.tags.filter((tag: string) => typeof tag === 'string') : [];\n return new ProjectJson(name, tags);\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — malformed project.json is not this rule's concern\n return new ProjectJson(null, []);\n }\n}\n\n/**\n * Walk up from a changed file to the nearest ancestor directory containing a\n * project.json (the file's owning nx project). Returns the repo-relative\n * project.json path, or null when the file belongs to no project (e.g. a\n * workspace-root config file).\n */\nfunction findOwningProjectJson(changedFile: string, workspaceRoot: string): string | null {\n let dir = path.dirname(changedFile);\n while (true) {\n const candidate = path.join(dir, 'project.json');\n if (fs.existsSync(path.join(workspaceRoot, candidate))) {\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir || dir === '.' || dir === '') {\n return null;\n }\n dir = parent;\n }\n}\n\nfunction hasFrameworkTag(tags: string[]): boolean {\n return tags.some((tag: string) => tag.startsWith(FRAMEWORK_TAG_PREFIX) && tag.slice(FRAMEWORK_TAG_PREFIX.length).trim().length > 0);\n}\n\nexport function findUntaggedProjects(workspaceRoot: string, changedFiles: string[]): UntaggedProject[] {\n const projectJsonPaths = new Set<string>();\n for (const file of changedFiles) {\n const owning = findOwningProjectJson(file, workspaceRoot);\n if (owning !== null) {\n projectJsonPaths.add(owning);\n }\n }\n\n const untagged: UntaggedProject[] = [];\n for (const projectJsonPath of Array.from(projectJsonPaths).sort()) {\n const projectJson = readProjectJson(projectJsonPath, workspaceRoot);\n if (!hasFrameworkTag(projectJson.tags)) {\n const name = projectJson.name ?? path.dirname(projectJsonPath);\n untagged.push(new UntaggedProject(name, projectJsonPath));\n }\n }\n return untagged;\n}\n\nfunction reportViolations(untagged: UntaggedProject[], knownTypes: string[], mode: ModifiedCodeMode): void {\n const suggestion = knownTypes.map((t: string) => `${FRAMEWORK_TAG_PREFIX}${t}`).join(' | ');\n console.error('');\n console.error('❌ Every modified project must declare which client side it targets (a framework tag)!');\n console.error('');\n console.error(` Add ONE of these to the project's project.json \"tags\" array: ${suggestion}`);\n console.error(' (\"all\" = a library usable by any side — angular, react and express can all import it.)');\n console.error(' This libType drives architecture/dependencies.json and the library-types-match-client rule.');\n console.error('');\n\n for (const project of untagged) {\n console.error(` ❌ ${project.name} — ${project.projectJsonPath}`);\n console.error(` add e.g. \"tags\": [\"${FRAMEWORK_TAG_PREFIX}all\"]`);\n }\n console.error('');\n console.error(` Current mode: ${mode}`);\n console.error('');\n}\n\nfunction resolveMode(normalMode: ModifiedCodeMode, epoch: number | undefined, branchPattern: string | undefined): ModifiedCodeMode {\n if (normalMode === 'OFF') {\n return normalMode;\n }\n const skip = shouldSkipRule(epoch, branchPattern);\n if (skip.skip) {\n console.log(`\\n⏭️ Skipping framework-tag validation (${skip.reason})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nasync function runValidatorImpl(options: FrameworkTagConfig, workspaceRoot: string): Promise<ExecutorResult> {\n const mode: ModifiedCodeMode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch, options.ignoreRuleWhileOnBranch);\n const knownTypes = options.knownTypes && options.knownTypes.length > 0 ? options.knownTypes : DEFAULT_KNOWN_TYPES;\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping framework-tag validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating Framework Tag\\n');\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n if (!base) {\n console.log('\\n⏭️ Skipping framework-tag validation (could not detect base branch)');\n console.log('');\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);\n console.log('');\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 const untagged = findUntaggedProjects(workspaceRoot, changedFiles);\n if (untagged.length === 0) {\n console.log('✅ Every modified project declares a framework tag');\n return { success: true };\n }\n\n reportViolations(untagged, knownTypes, mode);\n return { success: false };\n}\n\nexport class FrameworkTagValidator extends CodeValidator<FrameworkTagConfig> {\n constructor(config: FrameworkTagConfig) {\n super(config, 'framework-tag');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n return runValidatorImpl(this.config, workspaceRoot);\n }\n}\n"]}