@blumintinc/eslint-plugin-blumint 1.21.6 → 1.21.7
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/lib/index.js
CHANGED
|
@@ -305,6 +305,34 @@ const isWriteTarget = (node) => {
|
|
|
305
305
|
return false;
|
|
306
306
|
}
|
|
307
307
|
};
|
|
308
|
+
/**
|
|
309
|
+
* The composite literal a reference is STORED INTO — the object in
|
|
310
|
+
* `{ items: ITEMS }`, the array in `[ITEMS]` — or null for every other
|
|
311
|
+
* position.
|
|
312
|
+
*
|
|
313
|
+
* Storing a reference does not copy it: the same array stays reachable through
|
|
314
|
+
* the container, so `holder.items.push(3)` writes through to the binding
|
|
315
|
+
* exactly as a direct alias does, and freezing it raises the same TS2339. A
|
|
316
|
+
* `SpreadElement` is excluded because it genuinely builds a fresh value
|
|
317
|
+
* (`const COPY = [...ITEMS]`), and a computed key is excluded because it coerces
|
|
318
|
+
* the reference to a property name rather than retaining it.
|
|
319
|
+
*/
|
|
320
|
+
const storageContainerOf = (node) => {
|
|
321
|
+
const parent = node.parent;
|
|
322
|
+
if (!parent) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
if (parent.type === utils_1.AST_NODE_TYPES.Property &&
|
|
326
|
+
parent.value === node &&
|
|
327
|
+
parent.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
328
|
+
return parent.parent;
|
|
329
|
+
}
|
|
330
|
+
if (parent.type === utils_1.AST_NODE_TYPES.ArrayExpression &&
|
|
331
|
+
parent.elements.some((element) => element === node)) {
|
|
332
|
+
return parent;
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
};
|
|
308
336
|
/**
|
|
309
337
|
* The declarator a reference initializes IN WHOLE — `OTHER` in
|
|
310
338
|
* `const OTHER = ITEMS` — or null for every other position. Such a declaration
|
|
@@ -319,19 +347,27 @@ const isWriteTarget = (node) => {
|
|
|
319
347
|
* tolerated — staying silent is the cheap error here, emitting a fix that stops
|
|
320
348
|
* the file compiling is not.
|
|
321
349
|
*
|
|
322
|
-
* A reference
|
|
323
|
-
*
|
|
324
|
-
* extracts a member rather than the whole, so
|
|
350
|
+
* A reference STORED INTO a composite literal is followed through that
|
|
351
|
+
* container, since storing does not copy — see `storageContainerOf`. A
|
|
352
|
+
* destructuring id extracts a member rather than the whole, so it is not an
|
|
353
|
+
* alias here.
|
|
325
354
|
*/
|
|
326
355
|
const aliasDeclaratorOf = (identifier) => {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
declarator
|
|
331
|
-
declarator
|
|
332
|
-
|
|
356
|
+
// Ascends strictly, so reaching a node with no parent terminates the walk.
|
|
357
|
+
let value = outermostValueOf(identifier);
|
|
358
|
+
for (;;) {
|
|
359
|
+
const declarator = value.parent;
|
|
360
|
+
if (declarator?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
361
|
+
declarator.init === value &&
|
|
362
|
+
declarator.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
363
|
+
return declarator;
|
|
364
|
+
}
|
|
365
|
+
const container = storageContainerOf(value);
|
|
366
|
+
if (!container) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
value = outermostValueOf(container);
|
|
333
370
|
}
|
|
334
|
-
return declarator;
|
|
335
371
|
};
|
|
336
372
|
/**
|
|
337
373
|
* Whether the binding is written through anywhere in the file, under its own
|
|
@@ -68,6 +68,28 @@ function isFirestoreModuleSource(source) {
|
|
|
68
68
|
return (productSegment === product || !!productSegment?.startsWith(`${product}-`));
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* First segments that mark a path alias into the project's own tree rather than
|
|
73
|
+
* a published package. A scoped package's first segment is `@scope`, so a bare
|
|
74
|
+
* `@` can only be an alias.
|
|
75
|
+
*/
|
|
76
|
+
const FIRST_PARTY_SOURCE_ROOTS = new Set(['@', '~', 'src', 'app', 'lib']);
|
|
77
|
+
/**
|
|
78
|
+
* Whether a specifier names a published package, which is the only thing that
|
|
79
|
+
* can REFUTE Firestore provenance.
|
|
80
|
+
*
|
|
81
|
+
* A relative or absolute path, or a path alias, resolves into first-party code
|
|
82
|
+
* this rule cannot follow, so such a source says nothing about which product
|
|
83
|
+
* the binding came from. Reading its failure to look like `firebase/firestore`
|
|
84
|
+
* as proof of a different product is what silenced the rule on every
|
|
85
|
+
* `import { db } from '../../config/firebaseAdmin'` re-export.
|
|
86
|
+
*/
|
|
87
|
+
function isBarePackageSource(source) {
|
|
88
|
+
if (source.startsWith('.') || source.startsWith('/') || source === '') {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return !FIRST_PARTY_SOURCE_ROOTS.has(moduleSegments(source)[0]);
|
|
92
|
+
}
|
|
71
93
|
/**
|
|
72
94
|
* The module `name` is imported from, or null when the file declares the name
|
|
73
95
|
* itself (a local helper, a parameter) or nothing declares it at all.
|
|
@@ -117,11 +139,13 @@ function provenanceIdentifier(callee) {
|
|
|
117
139
|
/**
|
|
118
140
|
* Whether a `runTransaction` call is the Firestore one this rule speaks about.
|
|
119
141
|
*
|
|
120
|
-
* The gate speaks only when it knows: a binding that resolves to an import
|
|
121
|
-
* judged by its module source, and anything else — a bare
|
|
122
|
-
* local helper, a member call on an unresolvable receiver
|
|
123
|
-
*
|
|
124
|
-
*
|
|
142
|
+
* The gate speaks only when it knows: a binding that resolves to an import of a
|
|
143
|
+
* published package is judged by its module source, and anything else — a bare
|
|
144
|
+
* call, a parameter, a local helper, a member call on an unresolvable receiver,
|
|
145
|
+
* an import from the project's own tree — keeps the rule's posture of
|
|
146
|
+
* reporting, since a name with no traceable origin is far more often Firestore
|
|
147
|
+
* (`db.runTransaction(...)`) than not. Only a package specifier can refute
|
|
148
|
+
* Firestore; a first-party path merely fails to confirm it.
|
|
125
149
|
*/
|
|
126
150
|
function isFirestoreTransactionCall(node, context) {
|
|
127
151
|
if (!isRunTransactionCall(node)) {
|
|
@@ -132,7 +156,7 @@ function isFirestoreTransactionCall(node, context) {
|
|
|
132
156
|
return true;
|
|
133
157
|
}
|
|
134
158
|
const source = importedSourceOf(ASTHelpers_1.ASTHelpers.getScope(context, node), carrier.name);
|
|
135
|
-
if (source === null) {
|
|
159
|
+
if (source === null || !isBarePackageSource(source)) {
|
|
136
160
|
return true;
|
|
137
161
|
}
|
|
138
162
|
return isFirestoreModuleSource(source);
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.21.7",
|
|
4
|
+
"date": "2026-09-04T21:52:00.765Z",
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "global-const-style",
|
|
8
|
+
"changeType": "fix",
|
|
9
|
+
"issues": [
|
|
10
|
+
2327
|
|
11
|
+
],
|
|
12
|
+
"summary": "follow the binding into a container (closes #2327)"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "no-try-catch-already-exists-in-transaction",
|
|
16
|
+
"changeType": "fix",
|
|
17
|
+
"issues": [
|
|
18
|
+
2326
|
|
19
|
+
],
|
|
20
|
+
"summary": "report on first-party imports (closes #2326)"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
2
24
|
{
|
|
3
25
|
"version": "1.21.6",
|
|
4
26
|
"date": "2026-09-04T20:52:21.747Z",
|