@ethisyscore/eslint-plugin-coreconnect 1.92.0 → 1.93.0
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": "@ethisyscore/eslint-plugin-coreconnect",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.93.0",
|
|
4
4
|
"description": "ESLint rules enforcing EthisysCore plugin frontend conventions. Published so a new rule reaches every plugin on a version bump, rather than being copied into each scaffold and drifting.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -87,9 +87,32 @@ export function looksLikeComponent(init) {
|
|
|
87
87
|
* @param {string} options.message
|
|
88
88
|
* @param {{ source: string, name: string }} options.canonical The promoted component this rule
|
|
89
89
|
* points people at. A local declaration that COMPOSES it is exempt - see below.
|
|
90
|
+
* @param {boolean} [options.fileScopedExemption] Exempt EVERY matching declaration in a file
|
|
91
|
+
* that composes the canonical component anywhere, instead of only the declaration whose own
|
|
92
|
+
* body reaches it.
|
|
93
|
+
*
|
|
94
|
+
* <p>Off by default, because range containment is what keeps these rules narrow. Opt in when
|
|
95
|
+
* the pattern deliberately matches FEATURE-SURFACE names rather than component names - the
|
|
96
|
+
* in-progress-banner rule matches `TechImportIndicator`, whose legitimate shape is a top-level
|
|
97
|
+
* component that renders the shared banner through a SIBLING helper. Range containment cannot
|
|
98
|
+
* express that: the helper composes the canonical component, the outer name is what matches the
|
|
99
|
+
* pattern, and the two are different declarations. Without this the rule fires on correctly
|
|
100
|
+
* adopted code - which it did, on the first real adoption, because the test asserting the
|
|
101
|
+
* exemption used the easy shape where the wrapper renders the canonical component directly.</p>
|
|
102
|
+
*
|
|
103
|
+
* <p>The cost, stated: a file that composes the canonical component AND hand-rolls a second
|
|
104
|
+
* banner beside it goes unreported. Accepted for that rule only - a false positive on every
|
|
105
|
+
* correct adoption is the worse failure, because it trains people to switch the rule off.</p>
|
|
90
106
|
* @returns {import("eslint").Rule.RuleModule}
|
|
91
107
|
*/
|
|
92
|
-
export function createNoLocalComponentRule({
|
|
108
|
+
export function createNoLocalComponentRule({
|
|
109
|
+
pattern,
|
|
110
|
+
messageId,
|
|
111
|
+
description,
|
|
112
|
+
message,
|
|
113
|
+
canonical,
|
|
114
|
+
fileScopedExemption = false,
|
|
115
|
+
}) {
|
|
93
116
|
// Fail at construction, not on the first source file that happens to contain an import. Without
|
|
94
117
|
// this a fifth rule added without a `canonical` line throws a TypeError from inside the
|
|
95
118
|
// ImportDeclaration visitor, which surfaces as an opaque lint crash on an arbitrary file.
|
|
@@ -145,7 +168,9 @@ export function createNoLocalComponentRule({ pattern, messageId, description, me
|
|
|
145
168
|
* one next door still reports the second.
|
|
146
169
|
*/
|
|
147
170
|
const composesCanonical = (node) =>
|
|
148
|
-
|
|
171
|
+
fileScopedExemption
|
|
172
|
+
? canonicalUses.length > 0
|
|
173
|
+
: canonicalUses.some((at) => at >= node.range[0] && at <= node.range[1]);
|
|
149
174
|
|
|
150
175
|
const check = (idNode, declarationNode) => {
|
|
151
176
|
if (!idNode || idNode.type !== "Identifier") {
|
|
@@ -31,4 +31,10 @@ export default createNoLocalComponentRule({
|
|
|
31
31
|
"Disallow a locally-declared in-progress banner - import InProgressBanner from @ethisyscore/plugin-ui/components/shared",
|
|
32
32
|
message:
|
|
33
33
|
"'{{name}}' looks like a local in-progress banner. Import { InProgressBanner } from '@ethisyscore/plugin-ui/components/shared' instead, so a background job looks the same wherever it appears. It takes title, children, value (omit for an indeterminate bar), action and sx.",
|
|
34
|
+
// FILE-SCOPED, unlike its four siblings, because this pattern matches feature-surface names.
|
|
35
|
+
// `TechImportIndicator` is a top-level component that renders the shared banner through a
|
|
36
|
+
// SIBLING helper (`ImportingGroup`), so the declaration matching the pattern and the one
|
|
37
|
+
// composing the canonical component are different nodes and range containment cannot exempt it.
|
|
38
|
+
// Without this the rule fired on the very first correct adoption.
|
|
39
|
+
fileScopedExemption: true,
|
|
34
40
|
});
|