@webpieces/ai-hook-rules 0.4.671 → 0.4.672

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/ai-hook-rules",
3
- "version": "0.4.671",
3
+ "version": "0.4.672",
4
4
  "description": "Pluggable write-time validation framework for AI coding agents (@webpieces/ai-hook-rules). Claude Code PreToolUse + openclaw before_tool_call adapters share one rule engine.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -25,7 +25,7 @@
25
25
  "directory": "packages/tooling/ai-hook-rules"
26
26
  },
27
27
  "dependencies": {
28
- "@webpieces/rules-config": "0.4.671"
28
+ "@webpieces/rules-config": "0.4.672"
29
29
  },
30
30
  "publishConfig": {
31
31
  "access": "public"
@@ -3,6 +3,7 @@ import type { EditContext, Violation } from '../types';
3
3
  import { EditRuleBase } from '../rule-base';
4
4
  import { FixHint } from '../fix-hint';
5
5
  export declare class NoCustomCssRule extends EditRuleBase<NoCustomCssConfig> {
6
+ private readonly pathScope;
6
7
  constructor(config: NoCustomCssConfig);
7
8
  readonly description = "Ban hand-written CSS in Angular (styles/styleUrls in @Component, inline style=, [style.x], [ngStyle]) \u2014 style with Tailwind utility classes.";
8
9
  readonly files: string[];
@@ -13,6 +14,4 @@ export declare class NoCustomCssRule extends EditRuleBase<NoCustomCssConfig> {
13
14
  check(ctx: EditContext): readonly Violation[];
14
15
  /** The banned-pattern label for a line, or '' when clean. Branches on the file being a template. */
15
16
  private detailForLine;
16
- private isAllowedPath;
17
- private globToRegex;
18
17
  }
@@ -13,9 +13,14 @@ const RE_STYLES_ARRAY = /(^|\s)styles\s*:\s*\[/; // styles: [ ... ] (component i
13
13
  const RE_INLINE_STYLE = /(^|\s)style\s*=\s*["']/; // static style="…"
14
14
  const RE_NG_STYLE = /\[?ngStyle\]?\s*=/; // [ngStyle]="…"
15
15
  const RE_STYLE_BINDING = /\[style(?:\.[\w.-]+)?\]/; // [style] / [style.width]
16
- const TEST_PATHS = [/\.test\.ts$/, /\.spec\.ts$/, /__tests__\//];
17
16
  class NoCustomCssRule extends rule_base_1.EditRuleBase {
18
- constructor(config) { super(config, 'no-custom-css', 'no-custom-css'); }
17
+ // The SAME exemption the CI validator narrows its changed-file set with. Shared, because a second copy
18
+ // of this predicate is how `allowGlobs` came to be honoured here and ignored there.
19
+ pathScope;
20
+ constructor(config) {
21
+ super(config, 'no-custom-css', 'no-custom-css');
22
+ this.pathScope = new rules_config_1.NoCustomCssScope(config);
23
+ }
19
24
  description = 'Ban hand-written CSS in Angular (styles/styleUrls in @Component, inline style=, [style.x], [ngStyle]) — style with Tailwind utility classes.';
20
25
  files = ['**/*.ts', '**/*.tsx', '**/*.html'];
21
26
  defaultOptions = { allowGlobs: [] };
@@ -24,11 +29,11 @@ class NoCustomCssRule extends rule_base_1.EditRuleBase {
24
29
  new rules_config_1.Option('Delete the CSS and use Tailwind utility classes (flex, grid, gap-4, text-red-600).', true),
25
30
  new rules_config_1.Option('Need a specific value? Use an arbitrary-value class: w-[240px], bg-[#fffde7], grid-cols-[2fr_2fr_48px].'),
26
31
  new rules_config_1.Option('Dynamic on/off? Prefer [class.x]="cond" over a style binding.'),
32
+ new rules_config_1.Option('Whole tree not yours to restyle (a vendored kit, a generated artifact)? Add a glob to no-custom-css.allowGlobs in webpieces.config.json — it exempts the path in the editor and in CI alike.'),
27
33
  ], new fix_hint_1.DisableEscape(this.config.disableAllowed ?? true, '// webpieces-disable no-custom-css -- <reason>'));
28
34
  }
29
35
  check(ctx) {
30
- const allowGlobs = this.config.allowGlobs ?? [];
31
- if (this.isAllowedPath(ctx.relativePath, allowGlobs))
36
+ if (this.pathScope.isExempt(ctx.relativePath))
32
37
  return [];
33
38
  const isHtml = ctx.relativePath.endsWith('.html');
34
39
  const disableAllowed = this.config.disableAllowed ?? true;
@@ -61,43 +66,6 @@ class NoCustomCssRule extends rule_base_1.EditRuleBase {
61
66
  return '`styles` block in @Component';
62
67
  return '';
63
68
  }
64
- isAllowedPath(relativePath, allowGlobs) {
65
- if (TEST_PATHS.some((re) => re.test(relativePath)))
66
- return true;
67
- return allowGlobs.some((pattern) => this.globToRegex(pattern).test(relativePath));
68
- }
69
- globToRegex(pattern) {
70
- let re = '';
71
- let i = 0;
72
- while (i < pattern.length) {
73
- const ch = pattern[i];
74
- if (ch === '*') {
75
- if (pattern[i + 1] === '*') {
76
- re += '.*';
77
- i += 2;
78
- if (pattern[i] === '/')
79
- i += 1;
80
- continue;
81
- }
82
- re += '[^/]*';
83
- i += 1;
84
- continue;
85
- }
86
- if (ch === '?') {
87
- re += '[^/]';
88
- i += 1;
89
- continue;
90
- }
91
- if ('.+^$(){}|[]\\'.includes(ch)) {
92
- re += '\\' + ch;
93
- i += 1;
94
- continue;
95
- }
96
- re += ch;
97
- i += 1;
98
- }
99
- return new RegExp('^' + re + '$');
100
- }
101
69
  }
102
70
  exports.NoCustomCssRule = NoCustomCssRule;
103
71
  //# sourceMappingURL=no-custom-css.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-custom-css.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-custom-css.ts"],"names":[],"mappings":"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","sourcesContent":["import { NoCustomCssConfig, RULE_NAMES, Option } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\nimport { FixHint, DisableEscape } from '../fix-hint';\n\n// Edit-time (regex/line) counterpart to the CI code-rule validate-no-custom-css. The hook has no TS\n// AST, so the `.ts` side matches the @Component style props by shape; the `.html` side matches inline\n// style attributes/bindings. Both are diff-precise via the hook's per-line edit context.\nconst RE_STYLE_URLS = /(^|\\s)styleUrls?\\s*:/; // styleUrls: / styleUrl:\nconst RE_STYLES_ARRAY = /(^|\\s)styles\\s*:\\s*\\[/; // styles: [ ... ] (component inline CSS)\nconst RE_INLINE_STYLE = /(^|\\s)style\\s*=\\s*[\"']/; // static style=\"…\"\nconst RE_NG_STYLE = /\\[?ngStyle\\]?\\s*=/; // [ngStyle]=\"…\"\nconst RE_STYLE_BINDING = /\\[style(?:\\.[\\w.-]+)?\\]/; // [style] / [style.width]\n\nconst TEST_PATHS: RegExp[] = [/\\.test\\.ts$/, /\\.spec\\.ts$/, /__tests__\\//];\n\nexport class NoCustomCssRule extends EditRuleBase<NoCustomCssConfig> {\n constructor(config: NoCustomCssConfig) { super(config, 'no-custom-css', 'no-custom-css'); }\n\n readonly description =\n 'Ban hand-written CSS in Angular (styles/styleUrls in @Component, inline style=, [style.x], [ngStyle]) — style with Tailwind utility classes.';\n override readonly files = ['**/*.ts', '**/*.tsx', '**/*.html'];\n override readonly defaultOptions = { allowGlobs: [] };\n\n get fixHint(): FixHint {\n return new FixHint(\n 'Custom CSS bypasses Tailwind-first styling.',\n 'Pick one:',\n [\n new Option('Delete the CSS and use Tailwind utility classes (flex, grid, gap-4, text-red-600).', true),\n new Option('Need a specific value? Use an arbitrary-value class: w-[240px], bg-[#fffde7], grid-cols-[2fr_2fr_48px].'),\n new Option('Dynamic on/off? Prefer [class.x]=\"cond\" over a style binding.'),\n ],\n new DisableEscape(this.config.disableAllowed ?? true, '// webpieces-disable no-custom-css -- <reason>'),\n );\n }\n\n check(ctx: EditContext): readonly Violation[] {\n const allowGlobs = this.config.allowGlobs ?? [];\n if (this.isAllowedPath(ctx.relativePath, allowGlobs)) return [];\n\n const isHtml = ctx.relativePath.endsWith('.html');\n const disableAllowed = this.config.disableAllowed ?? true;\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const detail = this.detailForLine(ctx.strippedLines[i] ?? '', isHtml);\n if (!detail) continue;\n const lineNum = i + 1;\n if (disableAllowed && ctx.isLineDisabled(lineNum, RULE_NAMES.NO_CUSTOM_CSS)) continue;\n violations.push(new V(lineNum, `${detail}: ${ctx.lines[i]?.trim() ?? ''}`));\n }\n return violations;\n }\n\n /** The banned-pattern label for a line, or '' when clean. Branches on the file being a template. */\n private detailForLine(line: string, isHtml: boolean): string {\n if (isHtml) {\n if (RE_NG_STYLE.test(line)) return 'inline `[ngStyle]`';\n if (RE_STYLE_BINDING.test(line)) return 'inline `[style.x]` binding';\n if (RE_INLINE_STYLE.test(line)) return 'inline `style=` attribute';\n return '';\n }\n if (RE_STYLE_URLS.test(line)) return '`styleUrls`/`styleUrl` in @Component';\n if (RE_STYLES_ARRAY.test(line)) return '`styles` block in @Component';\n return '';\n }\n\n private isAllowedPath(relativePath: string, allowGlobs: readonly string[]): boolean {\n if (TEST_PATHS.some((re: RegExp) => re.test(relativePath))) return true;\n return allowGlobs.some((pattern: string) => this.globToRegex(pattern).test(relativePath));\n }\n\n private globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*') {\n if (pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n }\n}\n"]}
1
+ {"version":3,"file":"no-custom-css.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-custom-css.ts"],"names":[],"mappings":"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","sourcesContent":["import { NoCustomCssConfig, NoCustomCssScope, RULE_NAMES, Option } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\nimport { FixHint, DisableEscape } from '../fix-hint';\n\n// Edit-time (regex/line) counterpart to the CI code-rule validate-no-custom-css. The hook has no TS\n// AST, so the `.ts` side matches the @Component style props by shape; the `.html` side matches inline\n// style attributes/bindings. Both are diff-precise via the hook's per-line edit context.\nconst RE_STYLE_URLS = /(^|\\s)styleUrls?\\s*:/; // styleUrls: / styleUrl:\nconst RE_STYLES_ARRAY = /(^|\\s)styles\\s*:\\s*\\[/; // styles: [ ... ] (component inline CSS)\nconst RE_INLINE_STYLE = /(^|\\s)style\\s*=\\s*[\"']/; // static style=\"…\"\nconst RE_NG_STYLE = /\\[?ngStyle\\]?\\s*=/; // [ngStyle]=\"…\"\nconst RE_STYLE_BINDING = /\\[style(?:\\.[\\w.-]+)?\\]/; // [style] / [style.width]\n\nexport class NoCustomCssRule extends EditRuleBase<NoCustomCssConfig> {\n // The SAME exemption the CI validator narrows its changed-file set with. Shared, because a second copy\n // of this predicate is how `allowGlobs` came to be honoured here and ignored there.\n private readonly pathScope: NoCustomCssScope;\n\n constructor(config: NoCustomCssConfig) {\n super(config, 'no-custom-css', 'no-custom-css');\n this.pathScope = new NoCustomCssScope(config);\n }\n\n readonly description =\n 'Ban hand-written CSS in Angular (styles/styleUrls in @Component, inline style=, [style.x], [ngStyle]) — style with Tailwind utility classes.';\n override readonly files = ['**/*.ts', '**/*.tsx', '**/*.html'];\n override readonly defaultOptions = { allowGlobs: [] };\n\n get fixHint(): FixHint {\n return new FixHint(\n 'Custom CSS bypasses Tailwind-first styling.',\n 'Pick one:',\n [\n new Option('Delete the CSS and use Tailwind utility classes (flex, grid, gap-4, text-red-600).', true),\n new Option('Need a specific value? Use an arbitrary-value class: w-[240px], bg-[#fffde7], grid-cols-[2fr_2fr_48px].'),\n new Option('Dynamic on/off? Prefer [class.x]=\"cond\" over a style binding.'),\n new Option('Whole tree not yours to restyle (a vendored kit, a generated artifact)? Add a glob to no-custom-css.allowGlobs in webpieces.config.json — it exempts the path in the editor and in CI alike.'),\n ],\n new DisableEscape(this.config.disableAllowed ?? true, '// webpieces-disable no-custom-css -- <reason>'),\n );\n }\n\n check(ctx: EditContext): readonly Violation[] {\n if (this.pathScope.isExempt(ctx.relativePath)) return [];\n\n const isHtml = ctx.relativePath.endsWith('.html');\n const disableAllowed = this.config.disableAllowed ?? true;\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const detail = this.detailForLine(ctx.strippedLines[i] ?? '', isHtml);\n if (!detail) continue;\n const lineNum = i + 1;\n if (disableAllowed && ctx.isLineDisabled(lineNum, RULE_NAMES.NO_CUSTOM_CSS)) continue;\n violations.push(new V(lineNum, `${detail}: ${ctx.lines[i]?.trim() ?? ''}`));\n }\n return violations;\n }\n\n /** The banned-pattern label for a line, or '' when clean. Branches on the file being a template. */\n private detailForLine(line: string, isHtml: boolean): string {\n if (isHtml) {\n if (RE_NG_STYLE.test(line)) return 'inline `[ngStyle]`';\n if (RE_STYLE_BINDING.test(line)) return 'inline `[style.x]` binding';\n if (RE_INLINE_STYLE.test(line)) return 'inline `style=` attribute';\n return '';\n }\n if (RE_STYLE_URLS.test(line)) return '`styleUrls`/`styleUrl` in @Component';\n if (RE_STYLES_ARRAY.test(line)) return '`styles` block in @Component';\n return '';\n }\n}\n"]}