eslint-plugin-kerfjs 5.0.0-beta.57 → 5.0.0-beta.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,15 +13,17 @@ This plugin sits alongside two other defense layers shipped by [`kerfjs`](https:
13
13
  | Opt-in dev-warns (`KERF_DEV_WARN_*`) | Hard Rules 4 (rebuilt listeners), 7 (untracked signals), 8 (narrow set) | Runtime |
14
14
  | **This plugin** | Hard Rules 2, 5, 6, 10, 12 — AST-shaped antipatterns; plus rename-safety / `raw()`-audit nudges and a project-hygiene check for the bundled AI-assistant configs | **Edit time** |
15
15
 
16
- All but one rule are AST-only — no `@typescript-eslint/parser` _service_ dependency is required by the plugin (consumers configure their own parser). The exception, `ai-assistant-configs`, reads the filesystem instead of the AST and runs once per lint pass.
16
+ The core syntax rules analyze the AST without TypeScript parser services. `ai-assistant-configs` reads the filesystem, and the optional UI rules also resolve catalog and profile artifacts. Consumers configure their own parser; no `@typescript-eslint/parser` _service_ is required.
17
17
 
18
18
  ## Install
19
19
 
20
20
  ```bash
21
21
  npm install --save-dev eslint-plugin-kerfjs
22
- npm install @kerfjs/ui
23
22
  ```
24
23
 
24
+ Install `@kerfjs/ui` as well when using `recommended-ui` or `strict-ui`; the
25
+ core `recommended` preset does not require it.
26
+
25
27
  ### Supported ESLint versions
26
28
 
27
29
  `peerDependencies` declares `^9.0.0 || ^10.0.0`, and that is a **tested** range rather than an optimistic one: the rule suite runs against the latest release of each of those majors in CI on every push. A major is added to the range only after the suite has passed on it.
@@ -129,6 +131,7 @@ When a real bug ships that the existing defense stack misses AND a new lint rule
129
131
  ```bash
130
132
  npm install
131
133
  npm test
134
+ npm run test:eslint-matrix # real packed-plugin loading on every supported ESLint major
132
135
  ```
133
136
 
134
137
  The AST rules' test suites use ESLint's `RuleTester` with `@typescript-eslint/parser`. The `ai-assistant-configs` tests are filesystem-driven — they build temp project roots with fixture `node_modules/kerfjs/ai/` bundles, drive the classifier directly, and use the real ESLint API to prove plain lint is read-only while explicit `--fix` performs the external write.
@@ -30,7 +30,9 @@ cataloged roots, or nothing) is checked as those roots:
30
30
 
31
31
  - **Zones (`KUI-L202`):** the zone must accept every declared root.
32
32
  - **Parents (`KUI-L201`):** each root's listed parents apply where the wrapper
33
- is placed, and a wrapper used as a parent counts as each of its roots.
33
+ is placed, and a wrapper used as a parent counts as each of its roots. A
34
+ placement reports once, using the first violated root requirement before a
35
+ separate wrapper requirement if both apply.
34
36
  - **Cardinality (`KUI-L203`):** the wrapper counts as zero or one child, since
35
37
  it may render nothing.
36
38
 
package/index.js CHANGED
@@ -14,7 +14,7 @@ import uiPublicBoundaries from './lib/rules/ui-public-boundaries.js';
14
14
  import uiWiring from './lib/rules/ui-wiring.js';
15
15
 
16
16
  const plugin = {
17
- meta: { name: 'eslint-plugin-kerfjs', version: '5.0.0-beta.57' },
17
+ meta: { name: 'eslint-plugin-kerfjs', version: '5.0.0-beta.59' },
18
18
  rules: {
19
19
  'no-inline-jsx-event-handlers': noInlineJsxEventHandlers,
20
20
  'no-raw-with-dynamic-arg': noRawWithDynamicArg,
@@ -167,34 +167,37 @@ export default {
167
167
  const entry = contract.entries.get(key);
168
168
  if (!entry) return;
169
169
  // A wrapper answers to its own parent contract and to each root's.
170
+ // Report one violated requirement per placement, with roots first.
170
171
  const placed = [
171
- { entry, child: key },
172
172
  ...(entry.rendersAs ?? []).flatMap((root) => {
173
173
  const rootEntry = contract.entries.get(root);
174
174
  return rootEntry
175
175
  ? [{ entry: rootEntry, child: `${key} (renders ${root})` }]
176
176
  : [];
177
177
  }),
178
+ { entry, child: key },
178
179
  ].filter(
179
180
  ({ entry: placedEntry }) => placedEntry.parents?.mode === 'listed',
180
181
  );
181
182
  if (placed.length && !isExcepted(contract, PARENT_CODE, filename)) {
182
183
  const parent = directParentKey(node, registry, contract);
183
- if (parent)
184
- for (const { entry: placedEntry, child } of placed)
185
- if (
184
+ if (parent) {
185
+ const violation = placed.find(
186
+ ({ entry: placedEntry }) =>
186
187
  !rootsOf(parent.key, contract).every((root) =>
187
188
  placedEntry.parents.entries.includes(root),
188
- )
189
- )
190
- context.report({
191
- node: node.name,
192
- messageId: 'parent',
193
- data: {
194
- child,
195
- parents: placedEntry.parents.entries.join(', '),
196
- },
197
- });
189
+ ),
190
+ );
191
+ if (violation)
192
+ context.report({
193
+ node: node.name,
194
+ messageId: 'parent',
195
+ data: {
196
+ child: violation.child,
197
+ parents: violation.entry.parents.entries.join(', '),
198
+ },
199
+ });
200
+ }
198
201
  }
199
202
  for (const zone of entry.zones ?? []) {
200
203
  const bound = boundZone(node, zone, registry, contract);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-kerfjs",
3
- "version": "5.0.0-beta.57",
3
+ "version": "5.0.0-beta.59",
4
4
  "description": "ESLint rules that enforce kerf's hard rules — catches AI-shaped bugs at edit time.",
5
5
  "type": "module",
6
6
  "license": "MIT",