@violetflux/eslint-plugin-kerros 0.3.0 → 0.3.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/dist/index.cjs +96 -46
- package/dist/index.mjs +96 -46
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -472,32 +472,51 @@ const setMutationMethods = /* @__PURE__ */ new Set([
|
|
|
472
472
|
"clear",
|
|
473
473
|
"delete"
|
|
474
474
|
]);
|
|
475
|
-
/** Build
|
|
476
|
-
function
|
|
475
|
+
/** Build a reusable index that streams dynamic call-site contexts without retaining every path. */
|
|
476
|
+
function createFunctionCallSiteContexts(edges) {
|
|
477
477
|
const incoming = /* @__PURE__ */ new Map();
|
|
478
478
|
for (const edge of edges) {
|
|
479
479
|
const existing = incoming.get(edge.callee) ?? [];
|
|
480
480
|
existing.push(edge);
|
|
481
481
|
incoming.set(edge.callee, existing);
|
|
482
482
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
483
|
+
/** Visit paths independently while retaining only the current DFS path. */
|
|
484
|
+
const some = (target, predicate) => {
|
|
485
|
+
const calls = /* @__PURE__ */ new Map();
|
|
486
|
+
const active = /* @__PURE__ */ new Set([target]);
|
|
487
|
+
const frames = [{
|
|
488
|
+
advanced: false,
|
|
489
|
+
edges: incoming.get(target) ?? [],
|
|
490
|
+
fn: target,
|
|
491
|
+
index: 0
|
|
492
|
+
}];
|
|
493
|
+
while (frames.length > 0) {
|
|
494
|
+
const frame = frames.at(-1);
|
|
495
|
+
if (!frame) break;
|
|
496
|
+
const edge = frame.edges[frame.index];
|
|
497
|
+
if (edge) {
|
|
498
|
+
frame.index += 1;
|
|
499
|
+
if (active.has(edge.caller)) continue;
|
|
500
|
+
frame.advanced = true;
|
|
501
|
+
calls.set(frame.fn, edge.site);
|
|
502
|
+
active.add(edge.caller);
|
|
503
|
+
frames.push({
|
|
504
|
+
advanced: false,
|
|
505
|
+
edges: incoming.get(edge.caller) ?? [],
|
|
506
|
+
fn: edge.caller,
|
|
507
|
+
index: 0,
|
|
508
|
+
parent: frame.fn
|
|
509
|
+
});
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (!frame.advanced && predicate(calls)) return true;
|
|
513
|
+
frames.pop();
|
|
514
|
+
active.delete(frame.fn);
|
|
515
|
+
if (frame.parent) calls.delete(frame.parent);
|
|
496
516
|
}
|
|
497
|
-
|
|
517
|
+
return false;
|
|
498
518
|
};
|
|
499
|
-
|
|
500
|
-
return contexts;
|
|
519
|
+
return { some };
|
|
501
520
|
}
|
|
502
521
|
/** Track assignment sources and resolve definitions that reach a concrete reference point. */
|
|
503
522
|
function createReferenceOriginTracker(program) {
|
|
@@ -699,7 +718,7 @@ const objectEnumerationMethods = /* @__PURE__ */ new Set([
|
|
|
699
718
|
"keys",
|
|
700
719
|
"values"
|
|
701
720
|
]);
|
|
702
|
-
/** Read the
|
|
721
|
+
/** Read the tracked value argument from a broad enumeration or serialization call. */
|
|
703
722
|
function getBroadArgument(node) {
|
|
704
723
|
const [argument] = node.arguments;
|
|
705
724
|
if (!argument || argument.type === "SpreadElement") return false;
|
|
@@ -714,48 +733,72 @@ const noBroadStoreAccess = createRule({
|
|
|
714
733
|
name: "no-broad-store-access",
|
|
715
734
|
meta: {
|
|
716
735
|
type: "problem",
|
|
717
|
-
docs: { description: "Prevent
|
|
718
|
-
schema: [
|
|
719
|
-
|
|
736
|
+
docs: { description: "Prevent broad enumeration and serialization of complete Store snapshots." },
|
|
737
|
+
schema: [{
|
|
738
|
+
type: "object",
|
|
739
|
+
properties: { includeObjectFields: { type: "boolean" } },
|
|
740
|
+
additionalProperties: false
|
|
741
|
+
}],
|
|
742
|
+
messages: {
|
|
743
|
+
broadAccess: "Do not enumerate, serialize, or spread a complete selector-free Store snapshot.",
|
|
744
|
+
broadObjectField: "Do not enumerate, serialize, or spread an object field from a selector-free Store snapshot."
|
|
745
|
+
}
|
|
720
746
|
},
|
|
721
|
-
defaultOptions: [],
|
|
722
|
-
create(context) {
|
|
747
|
+
defaultOptions: [{ includeObjectFields: false }],
|
|
748
|
+
create(context, [options]) {
|
|
723
749
|
const { getIdentifierSymbol, getType, isStoreHookCall } = createKerrosTypeTools(context);
|
|
724
750
|
const origins = createReferenceOriginTracker(context.sourceCode.ast);
|
|
725
|
-
/**
|
|
726
|
-
const
|
|
751
|
+
/** Classify whether an expression is a complete Store snapshot or one object field from it. */
|
|
752
|
+
const readStoreOrigin = (input, seen = /* @__PURE__ */ new Set()) => {
|
|
727
753
|
const node = unwrapExpression(input);
|
|
728
754
|
if (node.type === "CallExpression") {
|
|
729
755
|
const selector = node.arguments[0];
|
|
730
|
-
return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & typescript.default.TypeFlags.Undefined) !== 0) && isStoreHookCall(node);
|
|
756
|
+
return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & typescript.default.TypeFlags.Undefined) !== 0) && isStoreHookCall(node) ? "snapshot" : void 0;
|
|
731
757
|
}
|
|
732
|
-
if (node.type === "AssignmentExpression") return
|
|
733
|
-
if (node.type === "MemberExpression") return
|
|
734
|
-
if (node.type !== "Identifier") return
|
|
758
|
+
if (node.type === "AssignmentExpression") return readStoreOrigin(node.right, seen);
|
|
759
|
+
if (node.type === "MemberExpression") return readStoreOrigin(node.object, seen) ? "objectField" : void 0;
|
|
760
|
+
if (node.type !== "Identifier") return;
|
|
735
761
|
const symbol = getIdentifierSymbol(node);
|
|
736
|
-
if (!symbol || seen.has(symbol)) return
|
|
762
|
+
if (!symbol || seen.has(symbol)) return;
|
|
737
763
|
seen.add(symbol);
|
|
738
|
-
|
|
764
|
+
let result;
|
|
765
|
+
for (const source of origins.resolve(symbol, node)) {
|
|
766
|
+
const origin = readStoreOrigin(source.expression, seen);
|
|
767
|
+
if (!origin) continue;
|
|
768
|
+
if (!source.objectField && origin === "snapshot") {
|
|
769
|
+
result = "snapshot";
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
772
|
+
result = "objectField";
|
|
773
|
+
}
|
|
739
774
|
seen.delete(symbol);
|
|
740
|
-
return
|
|
775
|
+
return result;
|
|
741
776
|
};
|
|
742
777
|
/** Report one operation that subscribes to every enumerable field. */
|
|
743
778
|
const reportBroadAccess = (expression) => {
|
|
744
|
-
|
|
779
|
+
const origin = readStoreOrigin(expression);
|
|
780
|
+
if (origin === "snapshot") context.report({
|
|
745
781
|
node: expression,
|
|
746
782
|
messageId: "broadAccess"
|
|
747
783
|
});
|
|
784
|
+
else if (origin === "objectField" && options.includeObjectFields) context.report({
|
|
785
|
+
node: expression,
|
|
786
|
+
messageId: "broadObjectField"
|
|
787
|
+
});
|
|
748
788
|
};
|
|
749
789
|
/** Track object-valued bindings destructured from a snapshot. */
|
|
750
|
-
const recordObjectBindings = (pattern, source, write) => {
|
|
790
|
+
const recordObjectBindings = (pattern, source, write, objectField = false) => {
|
|
751
791
|
if (pattern.type === "Identifier") {
|
|
752
792
|
if ((getType(pattern).flags & typescript.default.TypeFlags.Object) === 0) return;
|
|
753
793
|
const symbol = getIdentifierSymbol(pattern);
|
|
754
|
-
if (symbol) origins.record(symbol,
|
|
794
|
+
if (symbol) origins.record(symbol, {
|
|
795
|
+
expression: source,
|
|
796
|
+
objectField
|
|
797
|
+
}, write);
|
|
755
798
|
return;
|
|
756
799
|
}
|
|
757
800
|
if (pattern.type === "AssignmentPattern") {
|
|
758
|
-
recordObjectBindings(pattern.left, source, write);
|
|
801
|
+
recordObjectBindings(pattern.left, source, write, objectField);
|
|
759
802
|
return;
|
|
760
803
|
}
|
|
761
804
|
if (pattern.type === "RestElement") return;
|
|
@@ -763,8 +806,8 @@ const noBroadStoreAccess = createRule({
|
|
|
763
806
|
const entries = pattern.type === "ObjectPattern" ? pattern.properties : pattern.elements;
|
|
764
807
|
for (const entry of entries) {
|
|
765
808
|
if (!entry) continue;
|
|
766
|
-
if (entry.type === "Property") recordObjectBindings(entry.value, source, write);
|
|
767
|
-
else recordObjectBindings(entry, source, write);
|
|
809
|
+
if (entry.type === "Property") recordObjectBindings(entry.value, source, write, true);
|
|
810
|
+
else recordObjectBindings(entry, source, write, true);
|
|
768
811
|
}
|
|
769
812
|
};
|
|
770
813
|
return {
|
|
@@ -779,7 +822,10 @@ const noBroadStoreAccess = createRule({
|
|
|
779
822
|
if (!node.init) return;
|
|
780
823
|
if (node.id.type === "Identifier") {
|
|
781
824
|
const symbol = getIdentifierSymbol(node.id);
|
|
782
|
-
if (symbol) origins.record(symbol,
|
|
825
|
+
if (symbol) origins.record(symbol, {
|
|
826
|
+
expression: node.init,
|
|
827
|
+
objectField: false
|
|
828
|
+
}, node);
|
|
783
829
|
return;
|
|
784
830
|
}
|
|
785
831
|
if (node.id.type === "ObjectPattern" && node.id.properties.some((property) => property.type === "RestElement")) reportBroadAccess(node.init);
|
|
@@ -788,7 +834,10 @@ const noBroadStoreAccess = createRule({
|
|
|
788
834
|
AssignmentExpression(node) {
|
|
789
835
|
if (node.left.type !== "Identifier") return;
|
|
790
836
|
const symbol = getIdentifierSymbol(node.left);
|
|
791
|
-
if (symbol) origins.record(symbol,
|
|
837
|
+
if (symbol) origins.record(symbol, {
|
|
838
|
+
expression: node.right,
|
|
839
|
+
objectField: false
|
|
840
|
+
}, node);
|
|
792
841
|
}
|
|
793
842
|
};
|
|
794
843
|
}
|
|
@@ -1481,7 +1530,7 @@ const noRenderInstanceSnapshot = createRule({
|
|
|
1481
1530
|
changed = true;
|
|
1482
1531
|
}
|
|
1483
1532
|
}
|
|
1484
|
-
const
|
|
1533
|
+
const callContexts = createFunctionCallSiteContexts(localEdges.filter((edge) => rendered.has(edge.caller)));
|
|
1485
1534
|
/** Test whether an expression is a local alias of a Store instance Hook result. */
|
|
1486
1535
|
const isInstanceDerived = (input, calls, seen = /* @__PURE__ */ new Set()) => {
|
|
1487
1536
|
const node = unwrapExpression(input);
|
|
@@ -1514,7 +1563,7 @@ const noRenderInstanceSnapshot = createRule({
|
|
|
1514
1563
|
};
|
|
1515
1564
|
for (const snapshot of snapshots) {
|
|
1516
1565
|
if (!snapshot.owner || !rendered.has(snapshot.owner)) continue;
|
|
1517
|
-
if (
|
|
1566
|
+
if (callContexts.some(snapshot.owner, (calls) => {
|
|
1518
1567
|
return snapshot.kind === "instance" ? isInstanceDerived(snapshot.source, calls) : isSnapshotReaderDerived(snapshot.source, calls);
|
|
1519
1568
|
})) context.report({
|
|
1520
1569
|
node: snapshot.node,
|
|
@@ -1682,9 +1731,10 @@ const noStoreMutation = createRule({
|
|
|
1682
1731
|
site: node
|
|
1683
1732
|
});
|
|
1684
1733
|
}
|
|
1685
|
-
|
|
1734
|
+
const callContexts = createFunctionCallSiteContexts(localEdges);
|
|
1735
|
+
for (const candidate of candidates) if (candidate.owner ? callContexts.some(candidate.owner, (calls) => {
|
|
1686
1736
|
return isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), calls);
|
|
1687
|
-
})) context.report({
|
|
1737
|
+
}) : isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map())) context.report({
|
|
1688
1738
|
node: candidate.node,
|
|
1689
1739
|
messageId: "mutation"
|
|
1690
1740
|
});
|
|
@@ -2451,7 +2501,7 @@ const rules = {
|
|
|
2451
2501
|
const plugin = {
|
|
2452
2502
|
meta: {
|
|
2453
2503
|
name: "@violetflux/eslint-plugin-kerros",
|
|
2454
|
-
version: "0.3.
|
|
2504
|
+
version: "0.3.2"
|
|
2455
2505
|
},
|
|
2456
2506
|
rules
|
|
2457
2507
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -444,32 +444,51 @@ const setMutationMethods = /* @__PURE__ */ new Set([
|
|
|
444
444
|
"clear",
|
|
445
445
|
"delete"
|
|
446
446
|
]);
|
|
447
|
-
/** Build
|
|
448
|
-
function
|
|
447
|
+
/** Build a reusable index that streams dynamic call-site contexts without retaining every path. */
|
|
448
|
+
function createFunctionCallSiteContexts(edges) {
|
|
449
449
|
const incoming = /* @__PURE__ */ new Map();
|
|
450
450
|
for (const edge of edges) {
|
|
451
451
|
const existing = incoming.get(edge.callee) ?? [];
|
|
452
452
|
existing.push(edge);
|
|
453
453
|
incoming.set(edge.callee, existing);
|
|
454
454
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
455
|
+
/** Visit paths independently while retaining only the current DFS path. */
|
|
456
|
+
const some = (target, predicate) => {
|
|
457
|
+
const calls = /* @__PURE__ */ new Map();
|
|
458
|
+
const active = /* @__PURE__ */ new Set([target]);
|
|
459
|
+
const frames = [{
|
|
460
|
+
advanced: false,
|
|
461
|
+
edges: incoming.get(target) ?? [],
|
|
462
|
+
fn: target,
|
|
463
|
+
index: 0
|
|
464
|
+
}];
|
|
465
|
+
while (frames.length > 0) {
|
|
466
|
+
const frame = frames.at(-1);
|
|
467
|
+
if (!frame) break;
|
|
468
|
+
const edge = frame.edges[frame.index];
|
|
469
|
+
if (edge) {
|
|
470
|
+
frame.index += 1;
|
|
471
|
+
if (active.has(edge.caller)) continue;
|
|
472
|
+
frame.advanced = true;
|
|
473
|
+
calls.set(frame.fn, edge.site);
|
|
474
|
+
active.add(edge.caller);
|
|
475
|
+
frames.push({
|
|
476
|
+
advanced: false,
|
|
477
|
+
edges: incoming.get(edge.caller) ?? [],
|
|
478
|
+
fn: edge.caller,
|
|
479
|
+
index: 0,
|
|
480
|
+
parent: frame.fn
|
|
481
|
+
});
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (!frame.advanced && predicate(calls)) return true;
|
|
485
|
+
frames.pop();
|
|
486
|
+
active.delete(frame.fn);
|
|
487
|
+
if (frame.parent) calls.delete(frame.parent);
|
|
468
488
|
}
|
|
469
|
-
|
|
489
|
+
return false;
|
|
470
490
|
};
|
|
471
|
-
|
|
472
|
-
return contexts;
|
|
491
|
+
return { some };
|
|
473
492
|
}
|
|
474
493
|
/** Track assignment sources and resolve definitions that reach a concrete reference point. */
|
|
475
494
|
function createReferenceOriginTracker(program) {
|
|
@@ -671,7 +690,7 @@ const objectEnumerationMethods = /* @__PURE__ */ new Set([
|
|
|
671
690
|
"keys",
|
|
672
691
|
"values"
|
|
673
692
|
]);
|
|
674
|
-
/** Read the
|
|
693
|
+
/** Read the tracked value argument from a broad enumeration or serialization call. */
|
|
675
694
|
function getBroadArgument(node) {
|
|
676
695
|
const [argument] = node.arguments;
|
|
677
696
|
if (!argument || argument.type === "SpreadElement") return false;
|
|
@@ -686,48 +705,72 @@ const noBroadStoreAccess = createRule({
|
|
|
686
705
|
name: "no-broad-store-access",
|
|
687
706
|
meta: {
|
|
688
707
|
type: "problem",
|
|
689
|
-
docs: { description: "Prevent
|
|
690
|
-
schema: [
|
|
691
|
-
|
|
708
|
+
docs: { description: "Prevent broad enumeration and serialization of complete Store snapshots." },
|
|
709
|
+
schema: [{
|
|
710
|
+
type: "object",
|
|
711
|
+
properties: { includeObjectFields: { type: "boolean" } },
|
|
712
|
+
additionalProperties: false
|
|
713
|
+
}],
|
|
714
|
+
messages: {
|
|
715
|
+
broadAccess: "Do not enumerate, serialize, or spread a complete selector-free Store snapshot.",
|
|
716
|
+
broadObjectField: "Do not enumerate, serialize, or spread an object field from a selector-free Store snapshot."
|
|
717
|
+
}
|
|
692
718
|
},
|
|
693
|
-
defaultOptions: [],
|
|
694
|
-
create(context) {
|
|
719
|
+
defaultOptions: [{ includeObjectFields: false }],
|
|
720
|
+
create(context, [options]) {
|
|
695
721
|
const { getIdentifierSymbol, getType, isStoreHookCall } = createKerrosTypeTools(context);
|
|
696
722
|
const origins = createReferenceOriginTracker(context.sourceCode.ast);
|
|
697
|
-
/**
|
|
698
|
-
const
|
|
723
|
+
/** Classify whether an expression is a complete Store snapshot or one object field from it. */
|
|
724
|
+
const readStoreOrigin = (input, seen = /* @__PURE__ */ new Set()) => {
|
|
699
725
|
const node = unwrapExpression(input);
|
|
700
726
|
if (node.type === "CallExpression") {
|
|
701
727
|
const selector = node.arguments[0];
|
|
702
|
-
return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & ts.TypeFlags.Undefined) !== 0) && isStoreHookCall(node);
|
|
728
|
+
return (node.arguments.length === 0 || node.arguments.length === 1 && selector?.type !== "SpreadElement" && (getType(selector).flags & ts.TypeFlags.Undefined) !== 0) && isStoreHookCall(node) ? "snapshot" : void 0;
|
|
703
729
|
}
|
|
704
|
-
if (node.type === "AssignmentExpression") return
|
|
705
|
-
if (node.type === "MemberExpression") return
|
|
706
|
-
if (node.type !== "Identifier") return
|
|
730
|
+
if (node.type === "AssignmentExpression") return readStoreOrigin(node.right, seen);
|
|
731
|
+
if (node.type === "MemberExpression") return readStoreOrigin(node.object, seen) ? "objectField" : void 0;
|
|
732
|
+
if (node.type !== "Identifier") return;
|
|
707
733
|
const symbol = getIdentifierSymbol(node);
|
|
708
|
-
if (!symbol || seen.has(symbol)) return
|
|
734
|
+
if (!symbol || seen.has(symbol)) return;
|
|
709
735
|
seen.add(symbol);
|
|
710
|
-
|
|
736
|
+
let result;
|
|
737
|
+
for (const source of origins.resolve(symbol, node)) {
|
|
738
|
+
const origin = readStoreOrigin(source.expression, seen);
|
|
739
|
+
if (!origin) continue;
|
|
740
|
+
if (!source.objectField && origin === "snapshot") {
|
|
741
|
+
result = "snapshot";
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
result = "objectField";
|
|
745
|
+
}
|
|
711
746
|
seen.delete(symbol);
|
|
712
|
-
return
|
|
747
|
+
return result;
|
|
713
748
|
};
|
|
714
749
|
/** Report one operation that subscribes to every enumerable field. */
|
|
715
750
|
const reportBroadAccess = (expression) => {
|
|
716
|
-
|
|
751
|
+
const origin = readStoreOrigin(expression);
|
|
752
|
+
if (origin === "snapshot") context.report({
|
|
717
753
|
node: expression,
|
|
718
754
|
messageId: "broadAccess"
|
|
719
755
|
});
|
|
756
|
+
else if (origin === "objectField" && options.includeObjectFields) context.report({
|
|
757
|
+
node: expression,
|
|
758
|
+
messageId: "broadObjectField"
|
|
759
|
+
});
|
|
720
760
|
};
|
|
721
761
|
/** Track object-valued bindings destructured from a snapshot. */
|
|
722
|
-
const recordObjectBindings = (pattern, source, write) => {
|
|
762
|
+
const recordObjectBindings = (pattern, source, write, objectField = false) => {
|
|
723
763
|
if (pattern.type === "Identifier") {
|
|
724
764
|
if ((getType(pattern).flags & ts.TypeFlags.Object) === 0) return;
|
|
725
765
|
const symbol = getIdentifierSymbol(pattern);
|
|
726
|
-
if (symbol) origins.record(symbol,
|
|
766
|
+
if (symbol) origins.record(symbol, {
|
|
767
|
+
expression: source,
|
|
768
|
+
objectField
|
|
769
|
+
}, write);
|
|
727
770
|
return;
|
|
728
771
|
}
|
|
729
772
|
if (pattern.type === "AssignmentPattern") {
|
|
730
|
-
recordObjectBindings(pattern.left, source, write);
|
|
773
|
+
recordObjectBindings(pattern.left, source, write, objectField);
|
|
731
774
|
return;
|
|
732
775
|
}
|
|
733
776
|
if (pattern.type === "RestElement") return;
|
|
@@ -735,8 +778,8 @@ const noBroadStoreAccess = createRule({
|
|
|
735
778
|
const entries = pattern.type === "ObjectPattern" ? pattern.properties : pattern.elements;
|
|
736
779
|
for (const entry of entries) {
|
|
737
780
|
if (!entry) continue;
|
|
738
|
-
if (entry.type === "Property") recordObjectBindings(entry.value, source, write);
|
|
739
|
-
else recordObjectBindings(entry, source, write);
|
|
781
|
+
if (entry.type === "Property") recordObjectBindings(entry.value, source, write, true);
|
|
782
|
+
else recordObjectBindings(entry, source, write, true);
|
|
740
783
|
}
|
|
741
784
|
};
|
|
742
785
|
return {
|
|
@@ -751,7 +794,10 @@ const noBroadStoreAccess = createRule({
|
|
|
751
794
|
if (!node.init) return;
|
|
752
795
|
if (node.id.type === "Identifier") {
|
|
753
796
|
const symbol = getIdentifierSymbol(node.id);
|
|
754
|
-
if (symbol) origins.record(symbol,
|
|
797
|
+
if (symbol) origins.record(symbol, {
|
|
798
|
+
expression: node.init,
|
|
799
|
+
objectField: false
|
|
800
|
+
}, node);
|
|
755
801
|
return;
|
|
756
802
|
}
|
|
757
803
|
if (node.id.type === "ObjectPattern" && node.id.properties.some((property) => property.type === "RestElement")) reportBroadAccess(node.init);
|
|
@@ -760,7 +806,10 @@ const noBroadStoreAccess = createRule({
|
|
|
760
806
|
AssignmentExpression(node) {
|
|
761
807
|
if (node.left.type !== "Identifier") return;
|
|
762
808
|
const symbol = getIdentifierSymbol(node.left);
|
|
763
|
-
if (symbol) origins.record(symbol,
|
|
809
|
+
if (symbol) origins.record(symbol, {
|
|
810
|
+
expression: node.right,
|
|
811
|
+
objectField: false
|
|
812
|
+
}, node);
|
|
764
813
|
}
|
|
765
814
|
};
|
|
766
815
|
}
|
|
@@ -1453,7 +1502,7 @@ const noRenderInstanceSnapshot = createRule({
|
|
|
1453
1502
|
changed = true;
|
|
1454
1503
|
}
|
|
1455
1504
|
}
|
|
1456
|
-
const
|
|
1505
|
+
const callContexts = createFunctionCallSiteContexts(localEdges.filter((edge) => rendered.has(edge.caller)));
|
|
1457
1506
|
/** Test whether an expression is a local alias of a Store instance Hook result. */
|
|
1458
1507
|
const isInstanceDerived = (input, calls, seen = /* @__PURE__ */ new Set()) => {
|
|
1459
1508
|
const node = unwrapExpression(input);
|
|
@@ -1486,7 +1535,7 @@ const noRenderInstanceSnapshot = createRule({
|
|
|
1486
1535
|
};
|
|
1487
1536
|
for (const snapshot of snapshots) {
|
|
1488
1537
|
if (!snapshot.owner || !rendered.has(snapshot.owner)) continue;
|
|
1489
|
-
if (
|
|
1538
|
+
if (callContexts.some(snapshot.owner, (calls) => {
|
|
1490
1539
|
return snapshot.kind === "instance" ? isInstanceDerived(snapshot.source, calls) : isSnapshotReaderDerived(snapshot.source, calls);
|
|
1491
1540
|
})) context.report({
|
|
1492
1541
|
node: snapshot.node,
|
|
@@ -1654,9 +1703,10 @@ const noStoreMutation = createRule({
|
|
|
1654
1703
|
site: node
|
|
1655
1704
|
});
|
|
1656
1705
|
}
|
|
1657
|
-
|
|
1706
|
+
const callContexts = createFunctionCallSiteContexts(localEdges);
|
|
1707
|
+
for (const candidate of candidates) if (candidate.owner ? callContexts.some(candidate.owner, (calls) => {
|
|
1658
1708
|
return isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), calls);
|
|
1659
|
-
})) context.report({
|
|
1709
|
+
}) : isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map())) context.report({
|
|
1660
1710
|
node: candidate.node,
|
|
1661
1711
|
messageId: "mutation"
|
|
1662
1712
|
});
|
|
@@ -2423,7 +2473,7 @@ const rules = {
|
|
|
2423
2473
|
const plugin = {
|
|
2424
2474
|
meta: {
|
|
2425
2475
|
name: "@violetflux/eslint-plugin-kerros",
|
|
2426
|
-
version: "0.3.
|
|
2476
|
+
version: "0.3.2"
|
|
2427
2477
|
},
|
|
2428
2478
|
rules
|
|
2429
2479
|
};
|