@nadicodeai/ui 12.1.165 → 12.1.167

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.
Files changed (367) hide show
  1. package/AGENTS.md +8 -9
  2. package/README.md +2 -17
  3. package/dist/components/accordion.d.ts.map +1 -1
  4. package/dist/components/accordion.js +2 -1
  5. package/dist/components/account-menu.d.ts.map +1 -1
  6. package/dist/components/account-menu.js +5 -3
  7. package/dist/components/activity-calendar.d.ts.map +1 -1
  8. package/dist/components/activity-calendar.js +3 -2
  9. package/dist/components/agent-card.js +1 -1
  10. package/dist/components/agent-orb.d.ts.map +1 -1
  11. package/dist/components/agent-orb.js +1 -0
  12. package/dist/components/agent-profile.d.ts.map +1 -1
  13. package/dist/components/agent-profile.js +7 -4
  14. package/dist/components/alert-dialog.d.ts +1 -1
  15. package/dist/components/alert-dialog.d.ts.map +1 -1
  16. package/dist/components/alert-dialog.js +3 -3
  17. package/dist/components/app-mark.js +1 -1
  18. package/dist/components/avatar.js +8 -8
  19. package/dist/components/badge.d.ts +11 -16
  20. package/dist/components/badge.d.ts.map +1 -1
  21. package/dist/components/badge.js +7 -43
  22. package/dist/components/breadcrumb.d.ts.map +1 -1
  23. package/dist/components/breadcrumb.js +3 -2
  24. package/dist/components/button.d.ts +5 -21
  25. package/dist/components/button.d.ts.map +1 -1
  26. package/dist/components/button.js +6 -76
  27. package/dist/components/calendar.d.ts +1 -1
  28. package/dist/components/calendar.d.ts.map +1 -1
  29. package/dist/components/calendar.js +3 -3
  30. package/dist/components/card-row.js +1 -1
  31. package/dist/components/card.d.ts +4 -1
  32. package/dist/components/card.d.ts.map +1 -1
  33. package/dist/components/card.js +3 -2
  34. package/dist/components/catalog-drawer.d.ts.map +1 -1
  35. package/dist/components/catalog-drawer.js +3 -2
  36. package/dist/components/chart-axis.d.ts.map +1 -1
  37. package/dist/components/chart-axis.js +0 -2
  38. package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
  39. package/dist/components/chart-bar-horizontal.js +16 -14
  40. package/dist/components/chart-card.d.ts.map +1 -1
  41. package/dist/components/chart-card.js +0 -1
  42. package/dist/components/chart.d.ts.map +1 -1
  43. package/dist/components/chart.js +18 -32
  44. package/dist/components/checkbox.d.ts +3 -2
  45. package/dist/components/checkbox.d.ts.map +1 -1
  46. package/dist/components/checkbox.js +4 -3
  47. package/dist/components/choicebox.d.ts +5 -5
  48. package/dist/components/choicebox.d.ts.map +1 -1
  49. package/dist/components/choicebox.js +6 -5
  50. package/dist/components/clearable-input.d.ts.map +1 -1
  51. package/dist/components/clearable-input.js +2 -2
  52. package/dist/components/code-block.d.ts.map +1 -1
  53. package/dist/components/code-block.js +4 -2
  54. package/dist/components/collapsible.js +1 -1
  55. package/dist/components/collection-filters.d.ts.map +1 -1
  56. package/dist/components/collection-filters.js +1 -0
  57. package/dist/components/collection-page.js +1 -1
  58. package/dist/components/collection-sort.d.ts.map +1 -1
  59. package/dist/components/collection-sort.js +1 -4
  60. package/dist/components/collection-toolbar.js +1 -1
  61. package/dist/components/combobox.js +4 -4
  62. package/dist/components/command.d.ts.map +1 -1
  63. package/dist/components/command.js +1 -1
  64. package/dist/components/confirm-dialog.js +2 -2
  65. package/dist/components/context-menu.d.ts +1 -1
  66. package/dist/components/context-menu.d.ts.map +1 -1
  67. package/dist/components/copy-button.js +1 -1
  68. package/dist/components/data-table.d.ts +14 -7
  69. package/dist/components/data-table.d.ts.map +1 -1
  70. package/dist/components/data-table.js +24 -15
  71. package/dist/components/dialog.d.ts +1 -1
  72. package/dist/components/dialog.d.ts.map +1 -1
  73. package/dist/components/dialog.js +2 -2
  74. package/dist/components/drawer.d.ts +4 -4
  75. package/dist/components/drawer.d.ts.map +1 -1
  76. package/dist/components/drawer.js +14 -15
  77. package/dist/components/dropdown-menu.d.ts +1 -1
  78. package/dist/components/dropdown-menu.d.ts.map +1 -1
  79. package/dist/components/dropdown-menu.js +9 -5
  80. package/dist/components/empty-art.js +1 -1
  81. package/dist/components/empty-state.js +2 -2
  82. package/dist/components/entry-scene.d.ts.map +1 -1
  83. package/dist/components/entry-scene.js +2 -3
  84. package/dist/components/field.js +3 -3
  85. package/dist/components/file-diff.d.ts.map +1 -1
  86. package/dist/components/file-diff.js +5 -2
  87. package/dist/components/footer.d.ts +1 -1
  88. package/dist/components/footer.d.ts.map +1 -1
  89. package/dist/components/footer.js +3 -3
  90. package/dist/components/form-dialog.d.ts.map +1 -1
  91. package/dist/components/form-dialog.js +2 -1
  92. package/dist/components/form-drawer.d.ts +7 -9
  93. package/dist/components/form-drawer.d.ts.map +1 -1
  94. package/dist/components/form-drawer.js +5 -5
  95. package/dist/components/form.d.ts +26 -0
  96. package/dist/components/form.d.ts.map +1 -0
  97. package/dist/components/form.js +29 -0
  98. package/dist/components/gauge.js +2 -2
  99. package/dist/components/heatmap/calendar-heatmap.d.ts.map +1 -1
  100. package/dist/components/heatmap/calendar-heatmap.js +15 -13
  101. package/dist/components/hive-scene.d.ts.map +1 -1
  102. package/dist/components/hive-scene.js +3 -1
  103. package/dist/components/hover-card.d.ts +1 -1
  104. package/dist/components/hover-card.d.ts.map +1 -1
  105. package/dist/components/hover-card.js +1 -1
  106. package/dist/components/input-group.d.ts +5 -6
  107. package/dist/components/input-group.d.ts.map +1 -1
  108. package/dist/components/input-group.js +8 -11
  109. package/dist/components/input-otp.d.ts.map +1 -1
  110. package/dist/components/input-otp.js +2 -2
  111. package/dist/components/item.d.ts +1 -1
  112. package/dist/components/item.js +2 -2
  113. package/dist/components/kbd.d.ts +6 -9
  114. package/dist/components/kbd.d.ts.map +1 -1
  115. package/dist/components/kbd.js +7 -3
  116. package/dist/components/message-notice.js +1 -1
  117. package/dist/components/middle-truncate.d.ts.map +1 -1
  118. package/dist/components/middle-truncate.js +1 -0
  119. package/dist/components/model-identity.d.ts +4 -4
  120. package/dist/components/model-identity.d.ts.map +1 -1
  121. package/dist/components/model-identity.js +2 -2
  122. package/dist/components/model-mark.d.ts.map +1 -1
  123. package/dist/components/model-mark.js +4 -2
  124. package/dist/components/multi-select.js +2 -2
  125. package/dist/components/native-select.d.ts.map +1 -1
  126. package/dist/components/native-select.js +2 -6
  127. package/dist/components/notice-banner.js +1 -1
  128. package/dist/components/object-page.js +2 -2
  129. package/dist/components/object-workspace.d.ts.map +1 -1
  130. package/dist/components/object-workspace.js +1 -3
  131. package/dist/components/overview-page.d.ts +1 -1
  132. package/dist/components/overview-page.d.ts.map +1 -1
  133. package/dist/components/overview-page.js +2 -1
  134. package/dist/components/page-actions.d.ts.map +1 -1
  135. package/dist/components/page-actions.js +9 -4
  136. package/dist/components/pairing-code.js +1 -1
  137. package/dist/components/pending-label.d.ts.map +1 -1
  138. package/dist/components/pending-label.js +0 -1
  139. package/dist/components/popover.d.ts +1 -1
  140. package/dist/components/popover.d.ts.map +1 -1
  141. package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
  142. package/dist/components/portal-sidebar-frame.js +1 -1
  143. package/dist/components/product-message.d.ts.map +1 -1
  144. package/dist/components/product-message.js +1 -0
  145. package/dist/components/progress-checklist.d.ts.map +1 -1
  146. package/dist/components/progress-checklist.js +2 -2
  147. package/dist/components/progress.d.ts.map +1 -1
  148. package/dist/components/progress.js +5 -4
  149. package/dist/components/radio-group.js +2 -2
  150. package/dist/components/reading-column.js +1 -1
  151. package/dist/components/record-identity.d.ts.map +1 -1
  152. package/dist/components/record-identity.js +2 -5
  153. package/dist/components/record-list.d.ts +5 -4
  154. package/dist/components/record-list.d.ts.map +1 -1
  155. package/dist/components/record-list.js +31 -17
  156. package/dist/components/record-sheet.js +1 -1
  157. package/dist/components/record-time.d.ts.map +1 -1
  158. package/dist/components/record-time.js +2 -1
  159. package/dist/components/resizable.d.ts.map +1 -1
  160. package/dist/components/resizable.js +1 -1
  161. package/dist/components/row-edit-form.d.ts +2 -1
  162. package/dist/components/row-edit-form.d.ts.map +1 -1
  163. package/dist/components/row-edit-form.js +2 -1
  164. package/dist/components/run-timeline.js +1 -1
  165. package/dist/components/scope-switcher.js +1 -1
  166. package/dist/components/segmented-control.js +1 -1
  167. package/dist/components/select.js +1 -1
  168. package/dist/components/settings-facts.js +1 -1
  169. package/dist/components/settings-section.js +1 -1
  170. package/dist/components/sheet.d.ts +1 -1
  171. package/dist/components/sheet.d.ts.map +1 -1
  172. package/dist/components/sheet.js +1 -1
  173. package/dist/components/sidebar.d.ts +2 -2
  174. package/dist/components/sidebar.d.ts.map +1 -1
  175. package/dist/components/sidebar.js +16 -18
  176. package/dist/components/site-menu.d.ts +28 -0
  177. package/dist/components/site-menu.d.ts.map +1 -0
  178. package/dist/components/site-menu.js +23 -0
  179. package/dist/components/slider.d.ts.map +1 -1
  180. package/dist/components/slider.js +2 -1
  181. package/dist/components/snippet.d.ts +2 -0
  182. package/dist/components/snippet.d.ts.map +1 -1
  183. package/dist/components/snippet.js +13 -6
  184. package/dist/components/sonner.d.ts.map +1 -1
  185. package/dist/components/sonner.js +3 -4
  186. package/dist/components/status-badge.d.ts +1 -1
  187. package/dist/components/status-badge.d.ts.map +1 -1
  188. package/dist/components/switch.js +1 -1
  189. package/dist/components/table-card.js +1 -1
  190. package/dist/components/table-layouts.d.ts +24 -9
  191. package/dist/components/table-layouts.d.ts.map +1 -1
  192. package/dist/components/table-layouts.js +27 -10
  193. package/dist/components/table.js +4 -4
  194. package/dist/components/tabs-nav.d.ts +52 -0
  195. package/dist/components/tabs-nav.d.ts.map +1 -0
  196. package/dist/components/tabs-nav.js +95 -0
  197. package/dist/components/tabs.d.ts +2 -56
  198. package/dist/components/tabs.d.ts.map +1 -1
  199. package/dist/components/tabs.js +2 -91
  200. package/dist/components/text-link.d.ts +1 -2
  201. package/dist/components/text-link.d.ts.map +1 -1
  202. package/dist/components/text-link.js +1 -2
  203. package/dist/components/textarea.d.ts +5 -2
  204. package/dist/components/textarea.d.ts.map +1 -1
  205. package/dist/components/textarea.js +12 -4
  206. package/dist/components/toast.d.ts +2 -0
  207. package/dist/components/toast.d.ts.map +1 -1
  208. package/dist/components/toast.js +4 -0
  209. package/dist/components/toggle-group.d.ts +3 -1
  210. package/dist/components/toggle-group.d.ts.map +1 -1
  211. package/dist/components/toggle-group.js +3 -6
  212. package/dist/components/toggle.js +1 -1
  213. package/dist/components/tooltip.d.ts +12 -12
  214. package/dist/components/tooltip.d.ts.map +1 -1
  215. package/dist/components/tooltip.js +9 -9
  216. package/dist/components/trend-badge.js +2 -2
  217. package/dist/components/update-card.js +1 -1
  218. package/dist/components/usage-meter.d.ts +1 -1
  219. package/dist/components/usage-meter.d.ts.map +1 -1
  220. package/dist/components/usage-meter.js +9 -7
  221. package/dist/components/waiting-line.js +1 -1
  222. package/dist/index.d.ts +3 -7
  223. package/dist/index.d.ts.map +1 -1
  224. package/dist/index.js +3 -7
  225. package/dist/internal/agent-orb-motion.d.ts +4 -1
  226. package/dist/internal/agent-orb-motion.d.ts.map +1 -1
  227. package/dist/internal/agent-orb-motion.js +9 -7
  228. package/dist/internal/agent-orb.d.ts +4 -1
  229. package/dist/internal/agent-orb.d.ts.map +1 -1
  230. package/dist/internal/agent-orb.js +7 -1
  231. package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
  232. package/dist/internal/assistant-ui/attachment.js +15 -15
  233. package/dist/internal/assistant-ui/follow-up-suggestions.js +1 -1
  234. package/dist/internal/assistant-ui/markdown-text.js +26 -25
  235. package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
  236. package/dist/internal/assistant-ui/reasoning.js +13 -16
  237. package/dist/internal/assistant-ui/thread.d.ts.map +1 -1
  238. package/dist/internal/assistant-ui/thread.js +22 -16
  239. package/dist/internal/assistant-ui/tool-fallback.d.ts.map +1 -1
  240. package/dist/internal/assistant-ui/tool-fallback.js +29 -21
  241. package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
  242. package/dist/internal/assistant-ui/tool-group.js +6 -7
  243. package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
  244. package/dist/internal/badge-frame.d.ts +15 -0
  245. package/dist/internal/badge-frame.d.ts.map +1 -0
  246. package/dist/internal/badge-frame.js +44 -0
  247. package/dist/internal/button-classes.d.ts +20 -0
  248. package/dist/internal/button-classes.d.ts.map +1 -0
  249. package/dist/internal/button-classes.js +74 -0
  250. package/dist/internal/catalog-list.d.ts.map +1 -1
  251. package/dist/internal/catalog-list.js +8 -4
  252. package/dist/internal/collection-rail.js +3 -3
  253. package/dist/internal/form-drawer-frame.d.ts +10 -0
  254. package/dist/internal/form-drawer-frame.d.ts.map +1 -0
  255. package/dist/internal/form-drawer-frame.js +9 -0
  256. package/dist/internal/form-submit.d.ts +2 -1
  257. package/dist/internal/form-submit.d.ts.map +1 -1
  258. package/dist/internal/settings-save.d.ts.map +1 -1
  259. package/dist/internal/settings-save.js +4 -2
  260. package/dist/internal/status-mark.d.ts.map +1 -1
  261. package/dist/internal/status-mark.js +4 -1
  262. package/dist/internal/tab-row.d.ts +10 -0
  263. package/dist/internal/tab-row.d.ts.map +1 -0
  264. package/dist/internal/tab-row.js +2 -0
  265. package/dist/internal/table-compact-line.d.ts +1 -1
  266. package/dist/internal/table-compact-line.d.ts.map +1 -1
  267. package/dist/internal/table-compact-line.js +1 -1
  268. package/dist/internal/tooltip-content-frame.d.ts +14 -0
  269. package/dist/internal/tooltip-content-frame.d.ts.map +1 -0
  270. package/dist/internal/tooltip-content-frame.js +11 -0
  271. package/dist/lib/css-variables.d.ts +8 -0
  272. package/dist/lib/css-variables.d.ts.map +1 -0
  273. package/dist/lib/css-variables.js +4 -0
  274. package/dist/lib/merge-tokens.generated.js +1 -1
  275. package/dist/lib/use-copy-to-clipboard.d.ts.map +1 -1
  276. package/dist/lib/use-copy-to-clipboard.js +1 -2
  277. package/dist/showcase/samples.js +1 -1
  278. package/dist/showcase/specimens/choices.d.ts.map +1 -1
  279. package/dist/showcase/specimens/choices.js +3 -2
  280. package/dist/showcase/specimens/compositions.d.ts.map +1 -1
  281. package/dist/showcase/specimens/compositions.js +7 -5
  282. package/dist/showcase/specimens/data.d.ts.map +1 -1
  283. package/dist/showcase/specimens/data.js +1 -1
  284. package/dist/showcase/specimens/feedback.d.ts.map +1 -1
  285. package/dist/showcase/specimens/feedback.js +1 -2
  286. package/dist/showcase/specimens/index.d.ts +2 -0
  287. package/dist/showcase/specimens/index.d.ts.map +1 -1
  288. package/dist/showcase/specimens/inputs.d.ts +1 -0
  289. package/dist/showcase/specimens/inputs.d.ts.map +1 -1
  290. package/dist/showcase/specimens/inputs.js +5 -2
  291. package/dist/showcase/specimens/menus.d.ts +1 -0
  292. package/dist/showcase/specimens/menus.d.ts.map +1 -1
  293. package/dist/showcase/specimens/menus.js +7 -2
  294. package/dist/showcase/specimens/navigation.d.ts.map +1 -1
  295. package/dist/showcase/specimens/navigation.js +6 -5
  296. package/dist/showcase/specimens/status.d.ts.map +1 -1
  297. package/dist/showcase/specimens/status.js +4 -4
  298. package/dist/showcase/usage.generated.d.ts +1 -1
  299. package/dist/showcase/usage.generated.d.ts.map +1 -1
  300. package/dist/showcase/usage.generated.js +40 -18
  301. package/dist/standalone/index.js +2 -2
  302. package/dist/standalone/orb-page.js +10 -8
  303. package/dist/styles/components.css +14 -1
  304. package/package.json +32 -33
  305. package/dist/eslint/index.js +0 -224
  306. package/dist/eslint/rules/badge-is-static-label.js +0 -93
  307. package/dist/eslint/rules/button-icon-slot.js +0 -102
  308. package/dist/eslint/rules/button-link-for-navigation.js +0 -146
  309. package/dist/eslint/rules/button-no-aria-label-with-text.js +0 -75
  310. package/dist/eslint/rules/button-type-roles-only-in-buttons.js +0 -83
  311. package/dist/eslint/rules/choice-control-has-a-name.js +0 -98
  312. package/dist/eslint/rules/choice-group-in-fieldset.js +0 -86
  313. package/dist/eslint/rules/command-shortcut-single-owner.js +0 -118
  314. package/dist/eslint/rules/confirm-dialog-words.js +0 -63
  315. package/dist/eslint/rules/data-table-one-unsized-column.js +0 -96
  316. package/dist/eslint/rules/destructive-action-placement.js +0 -69
  317. package/dist/eslint/rules/disabled-choice-says-why.js +0 -71
  318. package/dist/eslint/rules/disabled-control-appearance.js +0 -75
  319. package/dist/eslint/rules/ellipsis-opens-a-dialog.js +0 -110
  320. package/dist/eslint/rules/empty-state-owner.js +0 -145
  321. package/dist/eslint/rules/input-group-owners.js +0 -155
  322. package/dist/eslint/rules/jsx-class-utility.js +0 -200
  323. package/dist/eslint/rules/kbd-children-no-modifier-text.js +0 -128
  324. package/dist/eslint/rules/literal-text.js +0 -41
  325. package/dist/eslint/rules/menu-owner.js +0 -75
  326. package/dist/eslint/rules/middle-truncate-placement.js +0 -69
  327. package/dist/eslint/rules/no-app-feature-component.js +0 -100
  328. package/dist/eslint/rules/no-bare-button-label.js +0 -55
  329. package/dist/eslint/rules/no-choice-control-wrapper.js +0 -97
  330. package/dist/eslint/rules/no-comment.js +0 -77
  331. package/dist/eslint/rules/no-control-in-read-only-surface.js +0 -106
  332. package/dist/eslint/rules/no-decorative-icon.js +0 -164
  333. package/dist/eslint/rules/no-deprecated-zod-api.js +0 -215
  334. package/dist/eslint/rules/no-global-patch.js +0 -115
  335. package/dist/eslint/rules/no-hand-built-busy.js +0 -104
  336. package/dist/eslint/rules/no-hand-built-theme-picker.js +0 -144
  337. package/dist/eslint/rules/no-hand-built-tile.js +0 -41
  338. package/dist/eslint/rules/no-hand-formatted-date.js +0 -87
  339. package/dist/eslint/rules/no-hand-stacked-avatars.js +0 -67
  340. package/dist/eslint/rules/no-nested-collapse.js +0 -60
  341. package/dist/eslint/rules/no-package-component-redefinition.js +0 -175
  342. package/dist/eslint/rules/no-search-in-form.js +0 -47
  343. package/dist/eslint/rules/no-shadcn-appearance-override.js +0 -305
  344. package/dist/eslint/rules/no-shell-prompt-in-snippet.js +0 -74
  345. package/dist/eslint/rules/no-stock-error-words.js +0 -56
  346. package/dist/eslint/rules/no-subject-derived-expectation.js +0 -183
  347. package/dist/eslint/rules/no-tooltip-around-labelled-field.js +0 -72
  348. package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +0 -57
  349. package/dist/eslint/rules/no-write-in-subscription.js +0 -140
  350. package/dist/eslint/rules/one-submenu-level.js +0 -43
  351. package/dist/eslint/rules/one-toast-per-flow.js +0 -91
  352. package/dist/eslint/rules/option-count.js +0 -167
  353. package/dist/eslint/rules/page-command-link-not-render.js +0 -43
  354. package/dist/eslint/rules/page-composition.js +0 -232
  355. package/dist/eslint/rules/progress-needs-name.js +0 -57
  356. package/dist/eslint/rules/rendered-import.js +0 -121
  357. package/dist/eslint/rules/server-action-discipline.js +0 -233
  358. package/dist/eslint/rules/settings-limit-in-footer.js +0 -94
  359. package/dist/eslint/rules/skeleton-in-region.js +0 -80
  360. package/dist/eslint/rules/sortable-heading-uses-sort.js +0 -79
  361. package/dist/eslint/rules/table-cell-inset-owner.js +0 -107
  362. package/dist/eslint/rules/tabs-selection-is-controlled.js +0 -47
  363. package/dist/eslint/rules/test-files-from-one-definition.js +0 -67
  364. package/dist/eslint/rules/tooltip-only-arrow.js +0 -38
  365. package/dist/eslint/rules/tooltip-trigger-accessible-name.js +0 -114
  366. package/dist/eslint/rules/workspace-declares-imports.js +0 -163
  367. package/dist/eslint/strict-design-system.js +0 -250
@@ -1,104 +0,0 @@
1
- /*
2
- * Standard tool checked first: `no-restricted-imports` refuses an import by
3
- * module, but not the same export by module and file area (PendingLabel is
4
- * allowed inside packages/ui), and `no-restricted-syntax` cannot join a
5
- * PendingLabel element to the Button it sits under through a renamed import.
6
- */
7
- import { openingElementClassUtilities } from "./jsx-class-utility.js";
8
- import { PACKAGE_BARREL, renderedImports } from "./rendered-import.js";
9
-
10
- const PENDING_LABEL = "PendingLabel";
11
- const PENDING_LABEL_MODULES = new Set([
12
- PACKAGE_BARREL,
13
- `${PACKAGE_BARREL}/components/pending-label`,
14
- ]);
15
- const BUTTONS = new Set(["Button", "ButtonLink"]);
16
- const SPINNER_ICONS = new Set([
17
- "Loader",
18
- "LoaderIcon",
19
- "Loader2",
20
- "Loader2Icon",
21
- "LoaderCircle",
22
- "LoaderCircleIcon",
23
- "LoaderPinwheel",
24
- ]);
25
- const ICON_MODULE = "lucide-react";
26
- const SPIN = "animate-spin";
27
- const FUNCTIONS = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
28
-
29
- const PACKAGE_PATH = "/packages/ui/";
30
- const PORTAL_PATH = "/apps/portal/";
31
-
32
- const PENDING_LABEL_ONLY = new Set([PENDING_LABEL]);
33
-
34
- const SOURCE_SIDE = { ImportSpecifier: "imported", ExportSpecifier: "local" };
35
-
36
- const sourceName = (specifier) => {
37
- const named = specifier[SOURCE_SIDE[specifier.type]];
38
- return named?.name ?? named?.value;
39
- };
40
-
41
- const isSpinToken = (utility) => utility.split(":").pop().replace(/^!/, "") === SPIN;
42
-
43
- function insideAButton(element, importOf) {
44
- for (let current = element.parent; current; current = current.parent) {
45
- if (FUNCTIONS.has(current.type)) return false;
46
- if (current.type === "JSXElement" && BUTTONS.has(importOf(current.openingElement.name)?.name)) {
47
- return true;
48
- }
49
- }
50
- return false;
51
- }
52
-
53
- export const noHandBuiltBusy = {
54
- meta: {
55
- type: "problem",
56
- docs: {
57
- description:
58
- "A working button takes loading and loadingLabel; no product hand-builds a busy label or a spinner.",
59
- },
60
- schema: [],
61
- messages: {
62
- "hand-built-busy":
63
- "A working button takes loading and loadingLabel: it keeps its place and focus, says its loadingLabel and shows no spinner. A wait past a second is WaitingLine.",
64
- },
65
- },
66
- create(context) {
67
- const filename = context.filename.replaceAll("\\", "/");
68
- const inPackage = filename.includes(PACKAGE_PATH);
69
- const inPortal = filename.includes(PORTAL_PATH);
70
- const importOf = renderedImports(context);
71
-
72
- const report = (node) => context.report({ node, messageId: "hand-built-busy" });
73
-
74
- function bannedFrom(source) {
75
- if (source === ICON_MODULE) return SPINNER_ICONS;
76
- if (!inPackage && PENDING_LABEL_MODULES.has(source)) return PENDING_LABEL_ONLY;
77
- return null;
78
- }
79
-
80
- function checkImport(node) {
81
- if (node.importKind === "type" || node.exportKind === "type") return;
82
- const source = node.source?.value;
83
- const bannedNames = bannedFrom(source);
84
- if (!bannedNames) return;
85
- for (const specifier of node.specifiers) {
86
- if (specifier.importKind === "type" || specifier.exportKind === "type") continue;
87
- if (bannedNames.has(sourceName(specifier))) report(specifier);
88
- }
89
- }
90
-
91
- return {
92
- ImportDeclaration: checkImport,
93
- ExportNamedDeclaration: checkImport,
94
- JSXElement(node) {
95
- if (importOf(node.openingElement.name)?.name !== PENDING_LABEL) return;
96
- if (insideAButton(node, importOf)) report(node.openingElement);
97
- },
98
- JSXOpeningElement(node) {
99
- if (inPortal) return;
100
- if (openingElementClassUtilities(node, context).some(isSpinToken)) report(node);
101
- },
102
- };
103
- },
104
- };
@@ -1,144 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
- * `Sun` element under a `RadioGroup`, but not the same icon named through a
4
- * renamed import, nor an option table declared in the same file, and it cannot
5
- * join the icons of two sibling Buttons.
6
- */
7
- import { renderedImports, walk } from "./rendered-import.js";
8
-
9
- const ICON_MODULE = "lucide-react";
10
- const PICKERS = new Set(["RadioGroup", "ToggleGroup", "SegmentedControl", "Switch"]);
11
- const BUTTONS = new Set(["Button", "ButtonLink"]);
12
- const THEME_ICONS = new Map([
13
- ["Sun", "light"],
14
- ["SunIcon", "light"],
15
- ["Moon", "dark"],
16
- ["MoonIcon", "dark"],
17
- ["Monitor", "system"],
18
- ["MonitorIcon", "system"],
19
- ]);
20
- const THEME_VALUES = new Set(["light", "dark"]);
21
-
22
- const keyName = (property) => property.key.name ?? property.key.value;
23
-
24
- function isThemeValue(literal) {
25
- const { parent: container } = literal;
26
- const parent = container.type === "JSXExpressionContainer" ? container.parent : container;
27
- const isItemValue =
28
- (parent.type === "JSXAttribute" && parent.name.name === "value") ||
29
- (parent.type === "Property" && parent.value === literal && keyName(parent) === "value") ||
30
- parent.type === "ArrayExpression" ||
31
- parent.type === "CallExpression";
32
- return isItemValue && THEME_VALUES.has(literal.value);
33
- }
34
-
35
- const isPropertyName = (identifier) => {
36
- const { parent } = identifier;
37
- return (
38
- (parent.type === "Property" && parent.key === identifier && !parent.computed) ||
39
- (parent.type === "MemberExpression" && parent.property === identifier && !parent.computed)
40
- );
41
- };
42
-
43
- export const noHandBuiltThemePicker = {
44
- meta: {
45
- type: "problem",
46
- docs: {
47
- description:
48
- "The appearance is chosen with ThemeModeSwitcher or ThemeModeMenu, never a hand-built picker.",
49
- },
50
- schema: [],
51
- messages: {
52
- "hand-built-theme-picker":
53
- "The appearance is chosen with ThemeModeSwitcher on a settings page or ThemeModeMenu in the account menu; do not rebuild it.",
54
- },
55
- },
56
- create(context) {
57
- const { sourceCode } = context;
58
- const { visitorKeys } = sourceCode;
59
- const importOf = renderedImports(context);
60
-
61
- const iconsByLocalName = new Map();
62
- for (const statement of sourceCode.ast.body) {
63
- if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
64
- if (statement.source.value !== ICON_MODULE) continue;
65
- for (const specifier of statement.specifiers) {
66
- if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type") continue;
67
- const kind = THEME_ICONS.get(specifier.imported.name ?? specifier.imported.value);
68
- if (kind) iconsByLocalName.set(specifier.local.name, kind);
69
- }
70
- }
71
-
72
- const sameFileConstant = (identifier) => {
73
- for (let scope = sourceCode.getScope(identifier); scope; scope = scope.upper) {
74
- const variable = scope.set.get(identifier.name);
75
- if (!variable) continue;
76
- const declarator = variable.defs[0]?.node;
77
- const isConstant =
78
- declarator?.type === "VariableDeclarator" && declarator.parent.kind === "const";
79
- return isConstant ? declarator.init : null;
80
- }
81
- return null;
82
- };
83
-
84
- function collect(root, held, followNames) {
85
- const visit = (child) => {
86
- if (child.type === "JSXOpeningElement") {
87
- const imported = importOf(child.name);
88
- const kind = imported?.source === ICON_MODULE ? THEME_ICONS.get(imported.name) : null;
89
- if (kind) held.icons.add(kind);
90
- } else if (child.type === "Identifier" && !isPropertyName(child)) {
91
- const kind = iconsByLocalName.get(child.name);
92
- if (kind) held.icons.add(kind);
93
- else if (followNames) {
94
- const init = sameFileConstant(child);
95
- if (init) collect(init, held, false);
96
- }
97
- } else if (child.type === "Literal" && isThemeValue(child)) {
98
- held.values.add(child.value);
99
- }
100
- return true;
101
- };
102
- visit(root);
103
- walk(root, visitorKeys, visit);
104
- }
105
-
106
- const holdsAThemeChoice = ({ icons, values }, needsBoth) =>
107
- needsBoth ? icons.size >= 2 || values.size === 2 : icons.size >= 1 || values.size === 2;
108
-
109
- const heldBy = (nodes) => {
110
- const held = { icons: new Set(), values: new Set() };
111
- for (const node of nodes) collect(node, held, true);
112
- return held;
113
- };
114
-
115
- const buttonsIn = (parent) =>
116
- parent.children.flatMap((child) => {
117
- if (child.type === "JSXFragment") return buttonsIn(child);
118
- const isButton =
119
- child.type === "JSXElement" && BUTTONS.has(importOf(child.openingElement.name)?.name);
120
- return isButton ? [child] : [];
121
- });
122
-
123
- const report = (node) => context.report({ node, messageId: "hand-built-theme-picker" });
124
-
125
- return {
126
- JSXElement(node) {
127
- if (PICKERS.has(importOf(node.openingElement.name)?.name)) {
128
- const held = heldBy([node]);
129
- if (holdsAThemeChoice(held, false)) report(node.openingElement);
130
- return;
131
- }
132
- const buttons = buttonsIn(node);
133
- if (buttons.length < 2) return;
134
- if (holdsAThemeChoice(heldBy(buttons), true)) report(node.openingElement);
135
- },
136
- JSXFragment(node) {
137
- if (node.parent.type === "JSXElement" || node.parent.type === "JSXFragment") return;
138
- const buttons = buttonsIn(node);
139
- if (buttons.length < 2) return;
140
- if (holdsAThemeChoice(heldBy(buttons), true)) report(node);
141
- },
142
- };
143
- },
144
- };
@@ -1,41 +0,0 @@
1
- import { findBelow, renderedImports, walk } from "./rendered-import.js";
2
-
3
- const FIELD_LABEL = "FieldLabel";
4
- const FIELD = "Field";
5
- const CONTROLS = new Set(["RadioGroupItem", "Checkbox"]);
6
-
7
- export const noHandBuiltTile = {
8
- meta: {
9
- type: "problem",
10
- docs: {
11
- description:
12
- "A bordered selectable tile is a ChoiceboxGroup, never a FieldLabel around a Field with a radio or a checkbox.",
13
- },
14
- schema: [],
15
- messages: {
16
- "hand-built-tile":
17
- "A bordered selectable tile is a ChoiceboxGroup with ChoiceboxGroupItem. Options without a description are a plain RadioGroup with a Label for each.",
18
- },
19
- },
20
- create(context) {
21
- const importOf = renderedImports(context);
22
- const { visitorKeys } = context.sourceCode;
23
- const isRendered = (name) => (node) =>
24
- node.type === "JSXElement" && name(importOf(node.openingElement.name)?.name);
25
- const isField = isRendered((name) => name === FIELD);
26
- const isControl = isRendered((name) => CONTROLS.has(name));
27
-
28
- return {
29
- JSXElement(node) {
30
- if (importOf(node.openingElement.name)?.name !== FIELD_LABEL) return;
31
- let handBuilt = false;
32
- walk(node, visitorKeys, (child) => {
33
- if (!isField(child) || !findBelow(child, visitorKeys, isControl)) return true;
34
- handBuilt = true;
35
- return "stop";
36
- });
37
- if (handBuilt) context.report({ node: node.openingElement, messageId: "hand-built-tile" });
38
- },
39
- };
40
- },
41
- };
@@ -1,87 +0,0 @@
1
- /*
2
- * Standard tool checked first: `no-restricted-syntax` can match a member named
3
- * `toLocaleDateString` or `new Intl.DateTimeFormat`, but `toLocaleString` is
4
- * also how a number is written, so it is a date only when its options object
5
- * names a date or time part, which a selector cannot read. It reports only a
6
- * date written into markup (inside a JSX expression of the same function): a
7
- * helper that returns a chart label, a period name or an email's text has no
8
- * element to put a RecordTime in.
9
- */
10
- const ALWAYS_DATES = new Set(["toLocaleDateString", "toLocaleTimeString"]);
11
- const DATE_PARTS = new Set([
12
- "dateStyle",
13
- "timeStyle",
14
- "weekday",
15
- "era",
16
- "year",
17
- "month",
18
- "day",
19
- "dayPeriod",
20
- "hour",
21
- "minute",
22
- "second",
23
- "fractionalSecondDigits",
24
- "timeZoneName",
25
- "hour12",
26
- "hourCycle",
27
- ]);
28
-
29
- const isIntlDateTimeFormat = (callee) =>
30
- callee.type === "MemberExpression" &&
31
- !callee.computed &&
32
- callee.object.type === "Identifier" &&
33
- callee.object.name === "Intl" &&
34
- callee.property.name === "DateTimeFormat";
35
-
36
- const namesADatePart = (options) =>
37
- options?.type === "ObjectExpression" &&
38
- options.properties.some(
39
- (property) =>
40
- property.type === "Property" &&
41
- !property.computed &&
42
- property.key.type === "Identifier" &&
43
- DATE_PARTS.has(property.key.name),
44
- );
45
-
46
- const FUNCTIONS = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
47
-
48
- function writtenIntoMarkup(node) {
49
- for (let current = node.parent; current; current = current.parent) {
50
- if (current.type === "JSXExpressionContainer") return true;
51
- if (FUNCTIONS.has(current.type)) return false;
52
- }
53
- return false;
54
- }
55
-
56
- export const noHandFormattedDate = {
57
- meta: {
58
- type: "problem",
59
- docs: {
60
- description: "An app shows a moment through RecordTime, never a hand-formatted date.",
61
- },
62
- schema: [],
63
- messages: {
64
- "hand-formatted-date":
65
- "Show a moment with RecordTime from @nadicodeai/ui/components/record-time: it reads relative within a week, then the date, in the viewer's zone and language, with the exact time in a card. A date formatted by hand drifts from the other screens and shows the server's zone.",
66
- },
67
- },
68
- create(context) {
69
- function check(node) {
70
- if (!writtenIntoMarkup(node)) return;
71
- const { callee } = node;
72
- if (isIntlDateTimeFormat(callee)) {
73
- context.report({ node, messageId: "hand-formatted-date" });
74
- return;
75
- }
76
- if (callee.type !== "MemberExpression" || callee.computed) return;
77
- const name = callee.property.name;
78
- if (
79
- ALWAYS_DATES.has(name) ||
80
- (name === "toLocaleString" && namesADatePart(node.arguments[1]))
81
- ) {
82
- context.report({ node, messageId: "hand-formatted-date" });
83
- }
84
- }
85
- return { CallExpression: check, NewExpression: check };
86
- },
87
- };
@@ -1,67 +0,0 @@
1
- import { openingElementClassUtilities } from "./jsx-class-utility.js";
2
- import { renderedImports } from "./rendered-import.js";
3
-
4
- const AVATAR = "Avatar";
5
- const OVERLAP = /^-(?:space-x-|ml-|ms-|mx-)/;
6
-
7
- const hasOverlapClass = (node, context) =>
8
- openingElementClassUtilities(node, context).some((utility) =>
9
- OVERLAP.test(utility.slice(utility.lastIndexOf(":") + 1).replace(/^!|!$/g, "")),
10
- );
11
-
12
- function returnedElement(callback) {
13
- if (callback?.type !== "ArrowFunctionExpression" && callback?.type !== "FunctionExpression") {
14
- return null;
15
- }
16
- if (callback.body.type !== "BlockStatement") return callback.body;
17
- return callback.body.body.find((statement) => statement.type === "ReturnStatement")?.argument;
18
- }
19
-
20
- export const noHandStackedAvatars = {
21
- meta: {
22
- type: "problem",
23
- docs: {
24
- description:
25
- "Stacked avatars are one AvatarGroup, never sibling Avatars or an overlap written on a parent or an Avatar.",
26
- },
27
- schema: [],
28
- messages: {
29
- "hand-stacked-avatars":
30
- "Stacked avatars are one <AvatarGroup members={...} label={...} />, which owns the overlap, the order, the +N count and the group's label.",
31
- },
32
- },
33
- create(context) {
34
- const importOf = renderedImports(context);
35
- const reported = new WeakSet();
36
- const isAvatar = (node) =>
37
- node?.type === "JSXElement" && importOf(node.openingElement.name)?.name === AVATAR;
38
-
39
- function report(avatar) {
40
- if (reported.has(avatar)) return;
41
- reported.add(avatar);
42
- context.report({ node: avatar.openingElement, messageId: "hand-stacked-avatars" });
43
- }
44
-
45
- return {
46
- JSXElement(node) {
47
- if (isAvatar(node) && hasOverlapClass(node.openingElement, context)) report(node);
48
- const avatars = node.children.filter(isAvatar);
49
- if (avatars.length >= 2) report(avatars[1]);
50
- },
51
- JSXFragment(node) {
52
- const avatars = node.children.filter(isAvatar);
53
- if (avatars.length >= 2) report(avatars[1]);
54
- },
55
- CallExpression(node) {
56
- const { callee, parent } = node;
57
- const isMap =
58
- callee.type === "MemberExpression" && !callee.computed && callee.property.name === "map";
59
- if (!isMap) return;
60
- if (parent.type !== "JSXExpressionContainer" || parent.parent.type !== "JSXElement") return;
61
- if (!hasOverlapClass(parent.parent.openingElement, context)) return;
62
- const returned = returnedElement(node.arguments[0]);
63
- if (isAvatar(returned)) report(returned);
64
- },
65
- };
66
- },
67
- };
@@ -1,60 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` descendant selector
3
- * matches by element name, so it cannot tell the package's Accordion from a
4
- * local component of the same name, a renamed import, or a Disclosure's
5
- * `label` (which is not its content) from its children.
6
- */
7
- import { packageModules, partOf, renderedImports } from "./rendered-import.js";
8
-
9
- const PARTS = [
10
- ["accordion", "Accordion"],
11
- ["accordion", "AccordionContent"],
12
- ["collapsible", "Collapsible"],
13
- ["collapsible", "CollapsibleContent"],
14
- ["disclosure", "Disclosure"],
15
- ];
16
-
17
- const PART_EXPORTERS = new Map(
18
- PARTS.map(([module, part]) => [
19
- part,
20
- [...packageModules(module), `@/components/${module}`].map((from) => ({ from, part })),
21
- ]),
22
- );
23
-
24
- const COLLAPSES = new Set(["Accordion", "Collapsible", "Disclosure"]);
25
- const COLLAPSED_CONTENT = new Set(["AccordionContent", "CollapsibleContent", "Disclosure"]);
26
-
27
- export const noNestedCollapse = {
28
- meta: {
29
- type: "problem",
30
- docs: {
31
- description: "An Accordion, Collapsible or Disclosure never sits in another's content.",
32
- },
33
- schema: [],
34
- messages: {
35
- "nested-collapse":
36
- "A collapse never sits in another: two levels hide too much and break the keyboard path. Make the inner one a section of the page, or flatten the outer.",
37
- },
38
- },
39
- create(context) {
40
- const importOf = renderedImports(context);
41
- const partRendered = (element) =>
42
- partOf(importOf(element.openingElement.name), PART_EXPORTERS, context.filename);
43
-
44
- return {
45
- JSXElement(node) {
46
- if (!COLLAPSES.has(partRendered(node))) return;
47
- let child = node;
48
- for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
49
- const holdsAsContent =
50
- ancestor.type === "JSXElement" && ancestor.children.includes(child);
51
- if (holdsAsContent && COLLAPSED_CONTENT.has(partRendered(ancestor))) {
52
- context.report({ node, messageId: "nested-collapse" });
53
- return;
54
- }
55
- child = ancestor;
56
- }
57
- },
58
- };
59
- },
60
- };
@@ -1,175 +0,0 @@
1
- /*
2
- * A route does not re-declare a component the package already ships.
3
- *
4
- * `@nadicodeai/ui` is the website's component surface. A route that declares
5
- * its own `Footer`, `Card` or `Badge` forks a shipped component into a page,
6
- * where the package's next change does not reach it — and the two are then
7
- * told apart only by their import paths.
8
- *
9
- * The names are DERIVED from the package's build surface at lint time: the
10
- * `types` target of its `exports` map, followed through the barrel's
11
- * `export *` chain. There is no list here to keep in step with the package;
12
- * a component added or removed by the package changes this rule's answer with
13
- * no edit at all.
14
- *
15
- * Standard tool checked first: core `no-redeclare` and
16
- * `no-shadow` answer only inside one scope chain, and `no-restricted-syntax`
17
- * would need every name spelled into a selector — the hand list this derivation
18
- * exists to avoid. Deferred here from #819 for exactly that reason.
19
- */
20
- import { existsSync, readFileSync } from "node:fs";
21
- import { createRequire } from "node:module";
22
- import { dirname, join } from "node:path";
23
- import { fileURLToPath } from "node:url";
24
-
25
- const PACKAGE = "@nadicodeai/ui";
26
-
27
- /*
28
- * TypeScript is loaded when this rule first runs, not when the plugin loads.
29
- * The plugin's bytes are published inside `@nadicodeai/ui`, which does not
30
- * depend on the compiler; a consumer who never enables this rule never asks
31
- * for it, and one who does is already linting TypeScript.
32
- */
33
- let ts;
34
- function typescript() {
35
- ts ??= createRequire(import.meta.url)("typescript");
36
- return ts;
37
- }
38
-
39
- /** The `.d.ts` the package's `exports` map names as its root type surface. */
40
- function typeSurfaceEntry() {
41
- let directory = dirname(fileURLToPath(import.meta.resolve(PACKAGE)));
42
- for (;;) {
43
- const manifestPath = join(directory, "package.json");
44
- let manifest;
45
- try {
46
- manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
47
- } catch {
48
- const parent = dirname(directory);
49
- if (parent === directory) {
50
- throw new Error(`${PACKAGE}: no manifest above its resolved entry point`);
51
- }
52
- directory = parent;
53
- continue;
54
- }
55
- if (manifest.name !== PACKAGE) {
56
- throw new Error(`${manifestPath} is not ${PACKAGE}`);
57
- }
58
- const entry = join(directory, manifest.exports["."].types);
59
- if (!existsSync(entry)) {
60
- throw new Error(
61
- `${PACKAGE} has no built type surface at ${entry}: build the package before linting.`,
62
- );
63
- }
64
- return entry;
65
- }
66
- }
67
-
68
- /*
69
- * A type is not a component. `export type ChartConfig` names a shape a route
70
- * may legitimately declare its own version of, and it cannot be imported as a
71
- * component at all, so the derived set holds values only: type aliases,
72
- * interfaces, and the `type` marker on an export clause are dropped.
73
- */
74
- function isTypeOnlyStatement(ts, statement) {
75
- return (
76
- ts.isTypeAliasDeclaration(statement) ||
77
- ts.isInterfaceDeclaration(statement) ||
78
- statement.isTypeOnly === true
79
- );
80
- }
81
-
82
- function exportedNames(file, names = new Set(), seen = new Set()) {
83
- if (seen.has(file)) return names;
84
- seen.add(file);
85
- const ts = typescript();
86
- const source = ts.createSourceFile(
87
- file,
88
- readFileSync(file, "utf8"),
89
- ts.ScriptTarget.Latest,
90
- false,
91
- ts.ScriptKind.TS,
92
- );
93
- for (const statement of source.statements) {
94
- if (ts.isExportDeclaration(statement)) {
95
- if (statement.moduleSpecifier && !statement.exportClause) {
96
- exportedNames(
97
- join(dirname(file), statement.moduleSpecifier.text.replace(/\.js$/, ".d.ts")),
98
- names,
99
- seen,
100
- );
101
- } else if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
102
- if (isTypeOnlyStatement(ts, statement)) continue;
103
- for (const element of statement.exportClause.elements) {
104
- if (element.isTypeOnly) continue;
105
- names.add(element.name.text);
106
- }
107
- }
108
- continue;
109
- }
110
- const modifiers = ts.canHaveModifiers(statement) ? (ts.getModifiers(statement) ?? []) : [];
111
- if (!modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue;
112
- if (isTypeOnlyStatement(ts, statement)) continue;
113
- if (ts.isVariableStatement(statement)) {
114
- for (const declaration of statement.declarationList.declarations) {
115
- if (ts.isIdentifier(declaration.name)) names.add(declaration.name.text);
116
- }
117
- } else if (statement.name && ts.isIdentifier(statement.name)) {
118
- names.add(statement.name.text);
119
- }
120
- }
121
- return names;
122
- }
123
-
124
- let packageExports;
125
-
126
- /** Read once per lint process; the built surface does not move under it. */
127
- function packageExportNames() {
128
- packageExports ??= exportedNames(typeSurfaceEntry());
129
- return packageExports;
130
- }
131
-
132
- const COMPONENT_NAME = /^[A-Z]/;
133
-
134
- function isFunctionLike(node) {
135
- return (
136
- node?.type === "ArrowFunctionExpression" ||
137
- node?.type === "FunctionExpression" ||
138
- node?.type === "ClassExpression" ||
139
- node?.type === "CallExpression"
140
- );
141
- }
142
-
143
- export const noPackageComponentRedefinition = {
144
- meta: {
145
- type: "problem",
146
- docs: {
147
- description: `Disallow declaring a component whose name ${PACKAGE} already exports`,
148
- },
149
- schema: [],
150
- messages: {
151
- redefined: `{{name}} is exported by ${PACKAGE}: import it instead of declaring a second one here.`,
152
- },
153
- },
154
- create(context) {
155
- const report = (node, name) => {
156
- if (!COMPONENT_NAME.test(name) || !packageExportNames().has(name)) return;
157
- context.report({ node, messageId: "redefined", data: { name } });
158
- };
159
-
160
- return {
161
- FunctionDeclaration(node) {
162
- if (node.id) report(node.id, node.id.name);
163
- },
164
- // A class is the third way to write a component; React still renders one.
165
- ClassDeclaration(node) {
166
- if (node.id) report(node.id, node.id.name);
167
- },
168
- VariableDeclarator(node) {
169
- if (node.id.type === "Identifier" && isFunctionLike(node.init)) {
170
- report(node.id, node.id.name);
171
- }
172
- },
173
- };
174
- },
175
- };