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 +5 -2
- package/docs/rules/ui-composition.md +3 -1
- package/index.js +1 -1
- package/lib/rules/ui-composition.js +17 -14
- package/package.json +1 -1
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
|
-
|
|
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.
|
|
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
|
-
|
|
185
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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);
|