@starci/eslint-canon-fe 1.4.5 → 3.0.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/the-split.mjs CHANGED
@@ -73,8 +73,10 @@ export const connectedBlockHasPresentationalTwin = {
73
73
  "`{{block}}` reads the world, so it must import exact `{{twin}}` from `./component`. A single leaf, one tree, or no local state is not an exception.",
74
74
  bypass:
75
75
  "Connected `{{block}}` renders `<{{rendered}}>` directly. Resolve the world here, then render only `<{{twin}}>`; component.tsx owns the complete presentation.",
76
- unused:
77
- "Connected `{{block}}` imports `{{twin}}` but never renders it. Every connected render path must cross the pure twin.",
76
+ unused:
77
+ "Connected `{{block}}` imports `{{twin}}` but never renders it. Every connected render path must cross the pure twin.",
78
+ called:
79
+ "Connected `{{block}}` calls `{{twin}}(...)` as an ordinary function. Render `<{{twin}} />` so React owns the twin's hook lifecycle and every render keeps one stable component boundary.",
78
80
  },
79
81
  },
80
82
  create(context) {
@@ -82,8 +84,9 @@ export const connectedBlockHasPresentationalTwin = {
82
84
  if (!block) return {}
83
85
 
84
86
  const twin = `${block}Base`
85
- const worldBindings = new Set()
86
- const rendered = []
87
+ const worldBindings = new Set()
88
+ const rendered = []
89
+ const calledTwin = []
87
90
  let importsTwin = false
88
91
  let readsWorld = false
89
92
  let rendersTwin = false
@@ -98,9 +101,10 @@ export const connectedBlockHasPresentationalTwin = {
98
101
  if (imported && local && REACHES_FOR_THE_WORLD.test(imported)) worldBindings.add(local)
99
102
  }
100
103
  },
101
- CallExpression(node) {
102
- if (node.callee?.type !== "Identifier") return
103
- if (worldBindings.has(node.callee.name) || REACHES_FOR_THE_WORLD.test(node.callee.name)) {
104
+ CallExpression(node) {
105
+ if (node.callee?.type !== "Identifier") return
106
+ if (node.callee.name === twin) calledTwin.push(node)
107
+ if (worldBindings.has(node.callee.name) || REACHES_FOR_THE_WORLD.test(node.callee.name)) {
104
108
  readsWorld = true
105
109
  }
106
110
  },
@@ -115,7 +119,7 @@ export const connectedBlockHasPresentationalTwin = {
115
119
  context.report({ node, messageId: "missing", data: { block, twin } })
116
120
  return
117
121
  }
118
- for (const entry of rendered) {
122
+ for (const entry of rendered) {
119
123
  if (entry.name !== twin) {
120
124
  context.report({
121
125
  node: entry.node,
@@ -123,8 +127,11 @@ export const connectedBlockHasPresentationalTwin = {
123
127
  data: { block, rendered: entry.name, twin },
124
128
  })
125
129
  }
126
- }
127
- if (!rendersTwin) context.report({ node, messageId: "unused", data: { block, twin } })
130
+ }
131
+ for (const call of calledTwin) {
132
+ context.report({ node: call, messageId: "called", data: { block, twin } })
133
+ }
134
+ if (!rendersTwin) context.report({ node, messageId: "unused", data: { block, twin } })
128
135
  },
129
136
  }
130
137
  },