@terpjs/eslint-boundaries 0.5.3 → 0.5.4
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 +1 -1
- package/src/budget.js +1 -0
- package/src/budget.test.js +4 -0
- package/src/findings.js +11 -0
- package/src/findings.test.js +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terpjs/eslint-boundaries",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Terp frontend boundary rules (as data) + the ESLint adapter: no cross-module imports, no package internals, design-token-only styling (no style/className/module stylesheets), token-styled components for raw HTML tags, router-only in-app links, generated-client-only, and browser XSS/navigation sink bans. Strict-only (no modes); governed opt-outs via terp-allow markers + the escape-hatch budget ratchet (terp-boundaries-budget).",
|
|
6
6
|
"main": "./src/index.js",
|
package/src/budget.js
CHANGED
package/src/budget.test.js
CHANGED
|
@@ -178,6 +178,7 @@ describe("terp-boundaries-budget --format json (the findings envelope)", () => {
|
|
|
178
178
|
expect(envelope.findings[0].rule).toBe("frontend/escape-hatch");
|
|
179
179
|
expect(envelope.findings[0].path).toBe("escape-hatch-budget.json");
|
|
180
180
|
expect(envelope.findings[0].message).toMatch(/unbudgeted marker/);
|
|
181
|
+
expect(envelope.ok).toBe(false);
|
|
181
182
|
expect(run.stderr).toMatch(/unbudgeted marker/);
|
|
182
183
|
});
|
|
183
184
|
|
|
@@ -188,6 +189,9 @@ describe("terp-boundaries-budget --format json (the findings envelope)", () => {
|
|
|
188
189
|
const envelope = JSON.parse(run.stdout);
|
|
189
190
|
expect(envelope.rules).toEqual(["frontend/escape-hatch"]);
|
|
190
191
|
expect(envelope.findings).toEqual([]);
|
|
192
|
+
// The version marker stays 1 on a clean run — `ok` is what says it passed.
|
|
193
|
+
expect(envelope.terp_findings).toBe(1);
|
|
194
|
+
expect(envelope.ok).toBe(true);
|
|
191
195
|
});
|
|
192
196
|
|
|
193
197
|
it("still reads a positional budget path alongside the flag", () => {
|
package/src/findings.js
CHANGED
|
@@ -8,11 +8,19 @@
|
|
|
8
8
|
* envelope** on stdout:
|
|
9
9
|
*
|
|
10
10
|
* { "terp_findings": 1, "tool": "@terpjs/eslint-boundaries",
|
|
11
|
+
* "ok": true, // did this run pass? (no findings)
|
|
11
12
|
* "rules": ["frontend/<rule>", …], // every catalog rule this run evaluated
|
|
12
13
|
* "not_applicable": ["frontend/<rule>", …], // opt-in rules this app has not enabled
|
|
13
14
|
* "findings": [{ rule, path, line, message }, …], // spec findings.schema.json shape
|
|
14
15
|
* "unattributed": [{ path, line, message, reported_as }, …] }
|
|
15
16
|
*
|
|
17
|
+
* `terp_findings: 1` is the envelope's **format version**, not a count — it is the
|
|
18
|
+
* discriminator a consumer matches on (the same role `terp_check_report: 1` plays for
|
|
19
|
+
* the check report) and it stays 1 whether the run found zero problems or twenty. The
|
|
20
|
+
* count lives in `findings.length`, and the verdict in `ok`. Reading the marker as a
|
|
21
|
+
* finding count is a real reported misread, which is why `ok` is there: a consumer that
|
|
22
|
+
* wants "did this pass" should never have to interpret a version number.
|
|
23
|
+
*
|
|
16
24
|
* `rules` is the evaluated-rule inventory ({@link catalogRuleIds}, minus the opt-in
|
|
17
25
|
* rules listed under `not_applicable` — today `frontend/layout-contract` when the app
|
|
18
26
|
* has no checked-in layout-contract.json, so a consumer never renders an unenforced
|
|
@@ -129,6 +137,9 @@ export function renderEnvelope(results, cwd = process.cwd(), options = {}) {
|
|
|
129
137
|
envelope: {
|
|
130
138
|
terp_findings: 1,
|
|
131
139
|
tool: "@terpjs/eslint-boundaries",
|
|
140
|
+
// The verdict, stated: `terp_findings` is a format version and reads like a
|
|
141
|
+
// count, so leaving "did it pass" implicit invites the wrong answer.
|
|
142
|
+
ok: findings.length === 0,
|
|
132
143
|
rules: catalogRuleIds().filter((id) => !notApplicable.includes(id)),
|
|
133
144
|
not_applicable: notApplicable,
|
|
134
145
|
findings,
|
package/src/findings.test.js
CHANGED
|
@@ -81,6 +81,29 @@ describe("renderEnvelope", () => {
|
|
|
81
81
|
expect(human.length).toBe(envelope.findings.length);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
+
it("states the verdict, so the format version is never read as a count", async () => {
|
|
85
|
+
// `terp_findings: 1` is a discriminator; it stays 1 whatever the run found. A
|
|
86
|
+
// reported misread ("0 findings but terp_findings says 1") cost an app a cycle,
|
|
87
|
+
// so the envelope answers "did this pass" outright.
|
|
88
|
+
const clean = await lintModule("export function View() {\n return null;\n}\n");
|
|
89
|
+
const cleanEnvelope = renderEnvelope(clean, path.resolve("."), {
|
|
90
|
+
layoutContract: true,
|
|
91
|
+
}).envelope;
|
|
92
|
+
expect(cleanEnvelope.findings).toEqual([]);
|
|
93
|
+
expect(cleanEnvelope.terp_findings).toBe(1);
|
|
94
|
+
expect(cleanEnvelope.ok).toBe(true);
|
|
95
|
+
|
|
96
|
+
const dirty = await lintModule(
|
|
97
|
+
'export function View() {\n return <button style={{ color: "#fff" }}>x</button>;\n}\n',
|
|
98
|
+
);
|
|
99
|
+
const dirtyEnvelope = renderEnvelope(dirty, path.resolve("."), {
|
|
100
|
+
layoutContract: true,
|
|
101
|
+
}).envelope;
|
|
102
|
+
expect(dirtyEnvelope.findings.length).toBeGreaterThan(1);
|
|
103
|
+
expect(dirtyEnvelope.terp_findings).toBe(1);
|
|
104
|
+
expect(dirtyEnvelope.ok).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
84
107
|
it("publishes an un-opted-in layout contract as not_applicable, never as passing", async () => {
|
|
85
108
|
// The opt-in rule is inert without a checked-in layout-contract.json; keeping it
|
|
86
109
|
// in `rules` would let a consumer render "evaluated, zero findings" = green for a
|