@violetflux/eslint-plugin-kerros 0.3.0 → 0.3.1

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 CHANGED
@@ -472,32 +472,51 @@ const setMutationMethods = /* @__PURE__ */ new Set([
472
472
  "clear",
473
473
  "delete"
474
474
  ]);
475
- /** Build separate dynamic call-site contexts for one local function. */
476
- function getFunctionCallSiteContexts(target, edges) {
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
- const contexts = [];
484
- /** Trace callers independently so states from different invocation paths never merge globally. */
485
- const trace = (fn, calls, stack) => {
486
- const edgesForFunction = incoming.get(fn) ?? [];
487
- let advanced = false;
488
- for (const edge of edgesForFunction) {
489
- if (stack.has(edge.caller)) continue;
490
- advanced = true;
491
- const nextCalls = new Map(calls);
492
- nextCalls.set(fn, edge.site);
493
- const nextStack = new Set(stack);
494
- nextStack.add(edge.caller);
495
- trace(edge.caller, nextCalls, nextStack);
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
- if (!advanced) contexts.push(calls);
517
+ return false;
498
518
  };
499
- trace(target, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set([target]));
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) {
@@ -1481,7 +1500,7 @@ const noRenderInstanceSnapshot = createRule({
1481
1500
  changed = true;
1482
1501
  }
1483
1502
  }
1484
- const renderedEdges = localEdges.filter((edge) => rendered.has(edge.caller));
1503
+ const callContexts = createFunctionCallSiteContexts(localEdges.filter((edge) => rendered.has(edge.caller)));
1485
1504
  /** Test whether an expression is a local alias of a Store instance Hook result. */
1486
1505
  const isInstanceDerived = (input, calls, seen = /* @__PURE__ */ new Set()) => {
1487
1506
  const node = unwrapExpression(input);
@@ -1514,7 +1533,7 @@ const noRenderInstanceSnapshot = createRule({
1514
1533
  };
1515
1534
  for (const snapshot of snapshots) {
1516
1535
  if (!snapshot.owner || !rendered.has(snapshot.owner)) continue;
1517
- if (getFunctionCallSiteContexts(snapshot.owner, renderedEdges).some((calls) => {
1536
+ if (callContexts.some(snapshot.owner, (calls) => {
1518
1537
  return snapshot.kind === "instance" ? isInstanceDerived(snapshot.source, calls) : isSnapshotReaderDerived(snapshot.source, calls);
1519
1538
  })) context.report({
1520
1539
  node: snapshot.node,
@@ -1682,9 +1701,10 @@ const noStoreMutation = createRule({
1682
1701
  site: node
1683
1702
  });
1684
1703
  }
1685
- for (const candidate of candidates) if ((candidate.owner ? getFunctionCallSiteContexts(candidate.owner, localEdges) : [/* @__PURE__ */ new Map()]).some((calls) => {
1704
+ const callContexts = createFunctionCallSiteContexts(localEdges);
1705
+ for (const candidate of candidates) if (candidate.owner ? callContexts.some(candidate.owner, (calls) => {
1686
1706
  return isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), calls);
1687
- })) context.report({
1707
+ }) : isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map())) context.report({
1688
1708
  node: candidate.node,
1689
1709
  messageId: "mutation"
1690
1710
  });
@@ -2451,7 +2471,7 @@ const rules = {
2451
2471
  const plugin = {
2452
2472
  meta: {
2453
2473
  name: "@violetflux/eslint-plugin-kerros",
2454
- version: "0.3.0"
2474
+ version: "0.3.1"
2455
2475
  },
2456
2476
  rules
2457
2477
  };
package/dist/index.mjs CHANGED
@@ -444,32 +444,51 @@ const setMutationMethods = /* @__PURE__ */ new Set([
444
444
  "clear",
445
445
  "delete"
446
446
  ]);
447
- /** Build separate dynamic call-site contexts for one local function. */
448
- function getFunctionCallSiteContexts(target, edges) {
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
- const contexts = [];
456
- /** Trace callers independently so states from different invocation paths never merge globally. */
457
- const trace = (fn, calls, stack) => {
458
- const edgesForFunction = incoming.get(fn) ?? [];
459
- let advanced = false;
460
- for (const edge of edgesForFunction) {
461
- if (stack.has(edge.caller)) continue;
462
- advanced = true;
463
- const nextCalls = new Map(calls);
464
- nextCalls.set(fn, edge.site);
465
- const nextStack = new Set(stack);
466
- nextStack.add(edge.caller);
467
- trace(edge.caller, nextCalls, nextStack);
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
- if (!advanced) contexts.push(calls);
489
+ return false;
470
490
  };
471
- trace(target, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set([target]));
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) {
@@ -1453,7 +1472,7 @@ const noRenderInstanceSnapshot = createRule({
1453
1472
  changed = true;
1454
1473
  }
1455
1474
  }
1456
- const renderedEdges = localEdges.filter((edge) => rendered.has(edge.caller));
1475
+ const callContexts = createFunctionCallSiteContexts(localEdges.filter((edge) => rendered.has(edge.caller)));
1457
1476
  /** Test whether an expression is a local alias of a Store instance Hook result. */
1458
1477
  const isInstanceDerived = (input, calls, seen = /* @__PURE__ */ new Set()) => {
1459
1478
  const node = unwrapExpression(input);
@@ -1486,7 +1505,7 @@ const noRenderInstanceSnapshot = createRule({
1486
1505
  };
1487
1506
  for (const snapshot of snapshots) {
1488
1507
  if (!snapshot.owner || !rendered.has(snapshot.owner)) continue;
1489
- if (getFunctionCallSiteContexts(snapshot.owner, renderedEdges).some((calls) => {
1508
+ if (callContexts.some(snapshot.owner, (calls) => {
1490
1509
  return snapshot.kind === "instance" ? isInstanceDerived(snapshot.source, calls) : isSnapshotReaderDerived(snapshot.source, calls);
1491
1510
  })) context.report({
1492
1511
  node: snapshot.node,
@@ -1654,9 +1673,10 @@ const noStoreMutation = createRule({
1654
1673
  site: node
1655
1674
  });
1656
1675
  }
1657
- for (const candidate of candidates) if ((candidate.owner ? getFunctionCallSiteContexts(candidate.owner, localEdges) : [/* @__PURE__ */ new Map()]).some((calls) => {
1676
+ const callContexts = createFunctionCallSiteContexts(localEdges);
1677
+ for (const candidate of candidates) if (candidate.owner ? callContexts.some(candidate.owner, (calls) => {
1658
1678
  return isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), calls);
1659
- })) context.report({
1679
+ }) : isSnapshotDerived(candidate.expression, maxAliasDepth, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map())) context.report({
1660
1680
  node: candidate.node,
1661
1681
  messageId: "mutation"
1662
1682
  });
@@ -2423,7 +2443,7 @@ const rules = {
2423
2443
  const plugin = {
2424
2444
  meta: {
2425
2445
  name: "@violetflux/eslint-plugin-kerros",
2426
- version: "0.3.0"
2446
+ version: "0.3.1"
2427
2447
  },
2428
2448
  rules
2429
2449
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@violetflux/eslint-plugin-kerros",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Type-aware ESLint rules for Kerros stores.",
5
5
  "repository": {
6
6
  "type": "git",