@starci/eslint-canon-be 1.2.0 → 1.2.2

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/index.mjs CHANGED
@@ -1,107 +1,107 @@
1
- /**
2
- * The back-end plugin: every law's rules, gathered once.
3
- *
4
- * A consuming repository imports THIS and nothing below it. Registering rules one by one in the
5
- * target's own plugin file is how the two lists drift: a law gains a rule here, nobody adds it there,
6
- * and the rule ships as a document. That is not hypothetical for this axis - the back end kept a
7
- * hand-written plugin whose names had already diverged from canon's, so `no-nest-logger` and
8
- * `no-framework-logger` were one law wearing two names and no build could say which had fired.
9
- *
10
- * The target's `eslint.config.mjs` stays the authority on what is switched on, at what level and
11
- * over which globs. That is a different question from what EXISTS, and this file answers only the
12
- * second.
13
- *
14
- * WHAT THIS FILE REFUSES TO DO. It does not rename anything. A rule's published name is part of the
15
- * law that declares it, because that name is what appears in a build log, in a disable comment and in
16
- * every conversation about the failure. Aliasing one here to match a target's older spelling would
17
- * leave two names for one rule and no way to tell which a message came from.
18
- */
19
- import { recommended as authorizationRecommended, rules as authorizationRules } from "./authorization.mjs"
20
- import { recommended as cdcRecommended, rules as cdcRules } from "./cdc.mjs"
21
- import { recommended as commentsRecommended, rules as commentsRules } from "./comments.mjs"
22
- import { recommended as cqrsRecommended, rules as cqrsRules } from "./cqrs.mjs"
23
- import { recommended as dataAccessRecommended, rules as dataAccessRules } from "./data-access.mjs"
24
- import { recommended as e2eFlowRecommended, rules as e2eFlowRules } from "./e2e-flow.mjs"
25
- import { recommended as eventDeliveryRecommended, rules as eventDeliveryRules } from "./event-delivery.mjs"
26
- import { recommended as exceptionIdentityRecommended, rules as exceptionIdentityRules } from "./exception-identity.mjs"
27
- import { recommended as exceptionsRecommended, rules as exceptionsRules } from "./exceptions.mjs"
28
- import { recommended as moduleLayeringRecommended, rules as moduleLayeringRules } from "./module-layering.mjs"
29
- import { recommended as namingRecommended, rules as namingRules } from "./naming.mjs"
30
- import { recommended as observabilityRecommended, rules as observabilityRules } from "./observability.mjs"
31
- import { recommended as testingRecommended, rules as testingRules } from "./testing.mjs"
32
- import { recommended as transportRecommended, rules as transportRules } from "./transport.mjs"
33
- import { recommended as typeSafetyRecommended, rules as typeSafetyRules } from "./type-safety.mjs"
34
-
35
- /** Each law's contribution, kept separate so a duplicate name is detectable rather than silent. */
36
- const CONTRIBUTIONS = [
37
- { law: "authorization", rules: authorizationRules, recommended: authorizationRecommended },
38
- { law: "cdc", rules: cdcRules, recommended: cdcRecommended },
39
- { law: "comments", rules: commentsRules, recommended: commentsRecommended },
40
- { law: "cqrs", rules: cqrsRules, recommended: cqrsRecommended },
41
- { law: "data-access", rules: dataAccessRules, recommended: dataAccessRecommended },
42
- { law: "e2e-flow", rules: e2eFlowRules, recommended: e2eFlowRecommended },
43
- { law: "event-delivery", rules: eventDeliveryRules, recommended: eventDeliveryRecommended },
44
- { law: "exception-identity", rules: exceptionIdentityRules, recommended: exceptionIdentityRecommended },
45
- { law: "exceptions", rules: exceptionsRules, recommended: exceptionsRecommended },
46
- { law: "module-layering", rules: moduleLayeringRules, recommended: moduleLayeringRecommended },
47
- { law: "naming", rules: namingRules, recommended: namingRecommended },
48
- { law: "observability", rules: observabilityRules, recommended: observabilityRecommended },
49
- { law: "testing", rules: testingRules, recommended: testingRecommended },
50
- { law: "transport", rules: transportRules, recommended: transportRecommended },
51
- { law: "type-safety", rules: typeSafetyRules, recommended: typeSafetyRecommended },
52
- ]
53
-
54
- /** Every gathered law. */
55
- export const lawOwners = CONTRIBUTIONS.map((entry) => entry.law)
56
-
57
- /**
58
- * Every (law, rule name) pair as DECLARED, before any collapsing.
59
- *
60
- * This exists because `ruleOwners` and `rules` below are both built with `Object.fromEntries`, which
61
- * silently keeps the last writer when two laws declare one name. A test that walks either of them
62
- * therefore cannot see a collision: the duplicate is already gone by the time it looks, so every
63
- * name appears exactly once BY CONSTRUCTION and the check passes forever while being blind.
64
- *
65
- * That is exactly what happened - three back-end rules were declared by two laws each, one copy was
66
- * discarded on import, and the guard reported green the whole time. A gate that cannot fail is
67
- * indistinguishable from no gate, so the raw declarations are published here and the guard walks
68
- * these instead.
69
- */
70
- export const ruleDeclarations = CONTRIBUTIONS.flatMap((entry) =>
71
- Object.keys(entry.rules).map((name) => ({ law: entry.law, name })),
72
- )
73
-
74
- /**
75
- * Which law declares each rule.
76
- *
77
- * Exported because a failing rule is a question about a LAW, and the shortest path from a build log
78
- * to the document that explains it is this map. It answers "who owns this name" for a name that
79
- * SHIPPED; proving no two laws claim one name is `ruleDeclarations`' job, not this map's.
80
- */
81
- export const ruleOwners = Object.fromEntries(
82
- CONTRIBUTIONS.flatMap((entry) => Object.keys(entry.rules).map((name) => [name, entry.law])),
83
- )
84
-
85
- /** Every rule this canon publishes, keyed by its published name. */
86
- export const rules = Object.fromEntries(
87
- CONTRIBUTIONS.flatMap((entry) => Object.entries(entry.rules)),
88
- )
89
-
90
- /**
91
- * The levels this canon asks for, as the plugin's own opinion.
92
- *
93
- * Unlike the front end, this axis allows `warn`, and the allowance is the burn-down playbook rather
94
- * than a softer standard: a rule measured with debt arrives at `warn` with a ledger entry naming the
95
- * offenders, and flips to `error` the day that entry closes. Switching a rule on over existing
96
- * offenders is what teaches an author to scroll past it, and a reader who has learned to scroll past
97
- * one rule reads none of them.
98
- */
99
- export const recommended = Object.fromEntries(
100
- CONTRIBUTIONS.flatMap((entry) => Object.entries(entry.recommended)),
101
- )
102
-
103
- /** The plugin object, shaped the way a flat config expects it. */
104
- export default {
105
- meta: { name: "eslint-plugin-starci-be" },
106
- rules,
107
- }
1
+ /**
2
+ * The back-end plugin: every law's rules, gathered once.
3
+ *
4
+ * A consuming repository imports THIS and nothing below it. Registering rules one by one in the
5
+ * target's own plugin file is how the two lists drift: a law gains a rule here, nobody adds it there,
6
+ * and the rule ships as a document. That is not hypothetical for this axis - the back end kept a
7
+ * hand-written plugin whose names had already diverged from canon's, so `no-nest-logger` and
8
+ * `no-framework-logger` were one law wearing two names and no build could say which had fired.
9
+ *
10
+ * The target's `eslint.config.mjs` stays the authority on what is switched on, at what level and
11
+ * over which globs. That is a different question from what EXISTS, and this file answers only the
12
+ * second.
13
+ *
14
+ * WHAT THIS FILE REFUSES TO DO. It does not rename anything. A rule's published name is part of the
15
+ * law that declares it, because that name is what appears in a build log, in a disable comment and in
16
+ * every conversation about the failure. Aliasing one here to match a target's older spelling would
17
+ * leave two names for one rule and no way to tell which a message came from.
18
+ */
19
+ import { recommended as authorizationRecommended, rules as authorizationRules } from "./authorization.mjs"
20
+ import { recommended as cdcRecommended, rules as cdcRules } from "./cdc.mjs"
21
+ import { recommended as commentsRecommended, rules as commentsRules } from "./comments.mjs"
22
+ import { recommended as cqrsRecommended, rules as cqrsRules } from "./cqrs.mjs"
23
+ import { recommended as dataAccessRecommended, rules as dataAccessRules } from "./data-access.mjs"
24
+ import { recommended as e2eFlowRecommended, rules as e2eFlowRules } from "./e2e-flow.mjs"
25
+ import { recommended as eventDeliveryRecommended, rules as eventDeliveryRules } from "./event-delivery.mjs"
26
+ import { recommended as exceptionIdentityRecommended, rules as exceptionIdentityRules } from "./exception-identity.mjs"
27
+ import { recommended as exceptionsRecommended, rules as exceptionsRules } from "./exceptions.mjs"
28
+ import { recommended as moduleLayeringRecommended, rules as moduleLayeringRules } from "./module-layering.mjs"
29
+ import { recommended as namingRecommended, rules as namingRules } from "./naming.mjs"
30
+ import { recommended as observabilityRecommended, rules as observabilityRules } from "./observability.mjs"
31
+ import { recommended as testingRecommended, rules as testingRules } from "./testing.mjs"
32
+ import { recommended as transportRecommended, rules as transportRules } from "./transport.mjs"
33
+ import { recommended as typeSafetyRecommended, rules as typeSafetyRules } from "./type-safety.mjs"
34
+
35
+ /** Each law's contribution, kept separate so a duplicate name is detectable rather than silent. */
36
+ const CONTRIBUTIONS = [
37
+ { law: "authorization", rules: authorizationRules, recommended: authorizationRecommended },
38
+ { law: "cdc", rules: cdcRules, recommended: cdcRecommended },
39
+ { law: "comments", rules: commentsRules, recommended: commentsRecommended },
40
+ { law: "cqrs", rules: cqrsRules, recommended: cqrsRecommended },
41
+ { law: "data-access", rules: dataAccessRules, recommended: dataAccessRecommended },
42
+ { law: "e2e-flow", rules: e2eFlowRules, recommended: e2eFlowRecommended },
43
+ { law: "event-delivery", rules: eventDeliveryRules, recommended: eventDeliveryRecommended },
44
+ { law: "exception-identity", rules: exceptionIdentityRules, recommended: exceptionIdentityRecommended },
45
+ { law: "exceptions", rules: exceptionsRules, recommended: exceptionsRecommended },
46
+ { law: "module-layering", rules: moduleLayeringRules, recommended: moduleLayeringRecommended },
47
+ { law: "naming", rules: namingRules, recommended: namingRecommended },
48
+ { law: "observability", rules: observabilityRules, recommended: observabilityRecommended },
49
+ { law: "testing", rules: testingRules, recommended: testingRecommended },
50
+ { law: "transport", rules: transportRules, recommended: transportRecommended },
51
+ { law: "type-safety", rules: typeSafetyRules, recommended: typeSafetyRecommended },
52
+ ]
53
+
54
+ /** Every gathered law. */
55
+ export const lawOwners = CONTRIBUTIONS.map((entry) => entry.law)
56
+
57
+ /**
58
+ * Every (law, rule name) pair as DECLARED, before any collapsing.
59
+ *
60
+ * This exists because `ruleOwners` and `rules` below are both built with `Object.fromEntries`, which
61
+ * silently keeps the last writer when two laws declare one name. A test that walks either of them
62
+ * therefore cannot see a collision: the duplicate is already gone by the time it looks, so every
63
+ * name appears exactly once BY CONSTRUCTION and the check passes forever while being blind.
64
+ *
65
+ * That is exactly what happened - three back-end rules were declared by two laws each, one copy was
66
+ * discarded on import, and the guard reported green the whole time. A gate that cannot fail is
67
+ * indistinguishable from no gate, so the raw declarations are published here and the guard walks
68
+ * these instead.
69
+ */
70
+ export const ruleDeclarations = CONTRIBUTIONS.flatMap((entry) =>
71
+ Object.keys(entry.rules).map((name) => ({ law: entry.law, name })),
72
+ )
73
+
74
+ /**
75
+ * Which law declares each rule.
76
+ *
77
+ * Exported because a failing rule is a question about a LAW, and the shortest path from a build log
78
+ * to the document that explains it is this map. It answers "who owns this name" for a name that
79
+ * SHIPPED; proving no two laws claim one name is `ruleDeclarations`' job, not this map's.
80
+ */
81
+ export const ruleOwners = Object.fromEntries(
82
+ CONTRIBUTIONS.flatMap((entry) => Object.keys(entry.rules).map((name) => [name, entry.law])),
83
+ )
84
+
85
+ /** Every rule this canon publishes, keyed by its published name. */
86
+ export const rules = Object.fromEntries(
87
+ CONTRIBUTIONS.flatMap((entry) => Object.entries(entry.rules)),
88
+ )
89
+
90
+ /**
91
+ * The levels this canon asks for, as the plugin's own opinion.
92
+ *
93
+ * Unlike the front end, this axis allows `warn`, and the allowance is the burn-down playbook rather
94
+ * than a softer standard: a rule measured with debt arrives at `warn` with a ledger entry naming the
95
+ * offenders, and flips to `error` the day that entry closes. Switching a rule on over existing
96
+ * offenders is what teaches an author to scroll past it, and a reader who has learned to scroll past
97
+ * one rule reads none of them.
98
+ */
99
+ export const recommended = Object.fromEntries(
100
+ CONTRIBUTIONS.flatMap((entry) => Object.entries(entry.recommended)),
101
+ )
102
+
103
+ /** The plugin object, shaped the way a flat config expects it. */
104
+ export default {
105
+ meta: { name: "eslint-plugin-starci-be" },
106
+ rules,
107
+ }
package/lib/path.mjs ADDED
@@ -0,0 +1,2 @@
1
+ /** Forward-slash form of a filename, so Windows paths compare like every other path. */
2
+ export const normalizePath = (filename) => String(filename || "").replace(/\\/g, "/")