@nadicodeai/ui 12.1.165 → 12.1.166
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/AGENTS.md +8 -9
- package/README.md +2 -17
- package/dist/components/accordion.d.ts.map +1 -1
- package/dist/components/accordion.js +2 -1
- package/dist/components/account-menu.d.ts.map +1 -1
- package/dist/components/account-menu.js +5 -3
- package/dist/components/activity-calendar.d.ts.map +1 -1
- package/dist/components/activity-calendar.js +3 -2
- package/dist/components/agent-card.js +1 -1
- package/dist/components/agent-orb.d.ts.map +1 -1
- package/dist/components/agent-orb.js +1 -0
- package/dist/components/agent-profile.d.ts.map +1 -1
- package/dist/components/agent-profile.js +7 -4
- package/dist/components/alert-dialog.d.ts +1 -1
- package/dist/components/alert-dialog.d.ts.map +1 -1
- package/dist/components/alert-dialog.js +3 -3
- package/dist/components/app-mark.js +1 -1
- package/dist/components/avatar.js +8 -8
- package/dist/components/badge.d.ts +11 -16
- package/dist/components/badge.d.ts.map +1 -1
- package/dist/components/badge.js +7 -43
- package/dist/components/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb.js +3 -2
- package/dist/components/button.d.ts +5 -21
- package/dist/components/button.d.ts.map +1 -1
- package/dist/components/button.js +6 -76
- package/dist/components/calendar.d.ts +1 -1
- package/dist/components/calendar.d.ts.map +1 -1
- package/dist/components/calendar.js +3 -3
- package/dist/components/card-row.js +1 -1
- package/dist/components/card.d.ts +4 -1
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +3 -2
- package/dist/components/catalog-drawer.d.ts.map +1 -1
- package/dist/components/catalog-drawer.js +3 -2
- package/dist/components/chart-axis.d.ts.map +1 -1
- package/dist/components/chart-axis.js +0 -2
- package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
- package/dist/components/chart-bar-horizontal.js +16 -14
- package/dist/components/chart-card.d.ts.map +1 -1
- package/dist/components/chart-card.js +0 -1
- package/dist/components/chart.d.ts.map +1 -1
- package/dist/components/chart.js +18 -32
- package/dist/components/checkbox.d.ts +3 -2
- package/dist/components/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox.js +4 -3
- package/dist/components/choicebox.d.ts +5 -5
- package/dist/components/choicebox.d.ts.map +1 -1
- package/dist/components/choicebox.js +6 -5
- package/dist/components/clearable-input.d.ts.map +1 -1
- package/dist/components/clearable-input.js +2 -2
- package/dist/components/code-block.d.ts.map +1 -1
- package/dist/components/code-block.js +4 -2
- package/dist/components/collapsible.js +1 -1
- package/dist/components/collection-filters.d.ts.map +1 -1
- package/dist/components/collection-filters.js +1 -0
- package/dist/components/collection-page.js +1 -1
- package/dist/components/collection-sort.d.ts.map +1 -1
- package/dist/components/collection-sort.js +1 -4
- package/dist/components/collection-toolbar.js +1 -1
- package/dist/components/combobox.js +4 -4
- package/dist/components/command.d.ts.map +1 -1
- package/dist/components/command.js +1 -1
- package/dist/components/confirm-dialog.js +2 -2
- package/dist/components/context-menu.d.ts +1 -1
- package/dist/components/context-menu.d.ts.map +1 -1
- package/dist/components/copy-button.js +1 -1
- package/dist/components/data-table.d.ts +11 -3
- package/dist/components/data-table.d.ts.map +1 -1
- package/dist/components/data-table.js +12 -4
- package/dist/components/dialog.d.ts +1 -1
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js +2 -2
- package/dist/components/drawer.d.ts +4 -4
- package/dist/components/drawer.d.ts.map +1 -1
- package/dist/components/drawer.js +14 -15
- package/dist/components/dropdown-menu.d.ts +1 -1
- package/dist/components/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu.js +9 -5
- package/dist/components/empty-art.js +1 -1
- package/dist/components/empty-state.js +2 -2
- package/dist/components/entry-scene.d.ts.map +1 -1
- package/dist/components/entry-scene.js +2 -3
- package/dist/components/field.js +3 -3
- package/dist/components/file-diff.d.ts.map +1 -1
- package/dist/components/file-diff.js +5 -2
- package/dist/components/footer.d.ts +1 -1
- package/dist/components/footer.d.ts.map +1 -1
- package/dist/components/footer.js +3 -3
- package/dist/components/form-dialog.d.ts.map +1 -1
- package/dist/components/form-dialog.js +2 -1
- package/dist/components/form-drawer.d.ts +7 -9
- package/dist/components/form-drawer.d.ts.map +1 -1
- package/dist/components/form-drawer.js +5 -5
- package/dist/components/form.d.ts +26 -0
- package/dist/components/form.d.ts.map +1 -0
- package/dist/components/form.js +29 -0
- package/dist/components/gauge.js +2 -2
- package/dist/components/heatmap/calendar-heatmap.d.ts.map +1 -1
- package/dist/components/heatmap/calendar-heatmap.js +15 -13
- package/dist/components/hive-scene.d.ts.map +1 -1
- package/dist/components/hive-scene.js +3 -1
- package/dist/components/hover-card.d.ts +1 -1
- package/dist/components/hover-card.d.ts.map +1 -1
- package/dist/components/hover-card.js +1 -1
- package/dist/components/input-group.d.ts +5 -6
- package/dist/components/input-group.d.ts.map +1 -1
- package/dist/components/input-group.js +8 -11
- package/dist/components/input-otp.d.ts.map +1 -1
- package/dist/components/input-otp.js +2 -2
- package/dist/components/item.d.ts +1 -1
- package/dist/components/item.js +2 -2
- package/dist/components/kbd.d.ts +6 -9
- package/dist/components/kbd.d.ts.map +1 -1
- package/dist/components/kbd.js +7 -3
- package/dist/components/message-notice.js +1 -1
- package/dist/components/middle-truncate.d.ts.map +1 -1
- package/dist/components/middle-truncate.js +1 -0
- package/dist/components/model-identity.d.ts +4 -4
- package/dist/components/model-identity.d.ts.map +1 -1
- package/dist/components/model-identity.js +2 -2
- package/dist/components/model-mark.d.ts.map +1 -1
- package/dist/components/model-mark.js +4 -2
- package/dist/components/multi-select.js +2 -2
- package/dist/components/native-select.d.ts.map +1 -1
- package/dist/components/native-select.js +2 -6
- package/dist/components/notice-banner.js +1 -1
- package/dist/components/object-page.js +2 -2
- package/dist/components/object-workspace.d.ts.map +1 -1
- package/dist/components/object-workspace.js +1 -3
- package/dist/components/overview-page.d.ts +1 -1
- package/dist/components/overview-page.d.ts.map +1 -1
- package/dist/components/overview-page.js +2 -1
- package/dist/components/page-actions.d.ts.map +1 -1
- package/dist/components/page-actions.js +9 -4
- package/dist/components/pairing-code.js +1 -1
- package/dist/components/pending-label.d.ts.map +1 -1
- package/dist/components/pending-label.js +0 -1
- package/dist/components/popover.d.ts +1 -1
- package/dist/components/popover.d.ts.map +1 -1
- package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
- package/dist/components/portal-sidebar-frame.js +1 -1
- package/dist/components/product-message.d.ts.map +1 -1
- package/dist/components/product-message.js +1 -0
- package/dist/components/progress-checklist.d.ts.map +1 -1
- package/dist/components/progress-checklist.js +2 -2
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js +5 -4
- package/dist/components/radio-group.js +2 -2
- package/dist/components/reading-column.js +1 -1
- package/dist/components/record-identity.d.ts.map +1 -1
- package/dist/components/record-identity.js +2 -5
- package/dist/components/record-list.d.ts.map +1 -1
- package/dist/components/record-list.js +4 -3
- package/dist/components/record-sheet.js +1 -1
- package/dist/components/record-time.d.ts.map +1 -1
- package/dist/components/record-time.js +2 -1
- package/dist/components/resizable.d.ts.map +1 -1
- package/dist/components/resizable.js +1 -1
- package/dist/components/row-edit-form.d.ts +2 -1
- package/dist/components/row-edit-form.d.ts.map +1 -1
- package/dist/components/row-edit-form.js +2 -1
- package/dist/components/run-timeline.js +1 -1
- package/dist/components/scope-switcher.js +1 -1
- package/dist/components/segmented-control.js +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/settings-facts.js +1 -1
- package/dist/components/settings-section.js +1 -1
- package/dist/components/sheet.d.ts +1 -1
- package/dist/components/sheet.d.ts.map +1 -1
- package/dist/components/sheet.js +1 -1
- package/dist/components/sidebar.d.ts +2 -2
- package/dist/components/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar.js +16 -18
- package/dist/components/site-menu.d.ts +28 -0
- package/dist/components/site-menu.d.ts.map +1 -0
- package/dist/components/site-menu.js +23 -0
- package/dist/components/slider.d.ts.map +1 -1
- package/dist/components/slider.js +2 -1
- package/dist/components/snippet.d.ts +2 -0
- package/dist/components/snippet.d.ts.map +1 -1
- package/dist/components/snippet.js +13 -6
- package/dist/components/sonner.d.ts.map +1 -1
- package/dist/components/sonner.js +3 -4
- package/dist/components/status-badge.d.ts +1 -1
- package/dist/components/status-badge.d.ts.map +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/table-card.js +1 -1
- package/dist/components/table.js +4 -4
- package/dist/components/tabs-nav.d.ts +52 -0
- package/dist/components/tabs-nav.d.ts.map +1 -0
- package/dist/components/tabs-nav.js +95 -0
- package/dist/components/tabs.d.ts +2 -56
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +2 -91
- package/dist/components/text-link.d.ts +1 -2
- package/dist/components/text-link.d.ts.map +1 -1
- package/dist/components/text-link.js +1 -2
- package/dist/components/textarea.d.ts +5 -2
- package/dist/components/textarea.d.ts.map +1 -1
- package/dist/components/textarea.js +12 -4
- package/dist/components/toast.d.ts +2 -0
- package/dist/components/toast.d.ts.map +1 -1
- package/dist/components/toast.js +4 -0
- package/dist/components/toggle-group.d.ts +3 -1
- package/dist/components/toggle-group.d.ts.map +1 -1
- package/dist/components/toggle-group.js +3 -6
- package/dist/components/toggle.js +1 -1
- package/dist/components/tooltip.d.ts +12 -12
- package/dist/components/tooltip.d.ts.map +1 -1
- package/dist/components/tooltip.js +9 -9
- package/dist/components/trend-badge.js +2 -2
- package/dist/components/update-card.js +1 -1
- package/dist/components/usage-meter.d.ts +1 -1
- package/dist/components/usage-meter.d.ts.map +1 -1
- package/dist/components/usage-meter.js +9 -7
- package/dist/components/waiting-line.js +1 -1
- package/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -7
- package/dist/internal/agent-orb-motion.d.ts +4 -1
- package/dist/internal/agent-orb-motion.d.ts.map +1 -1
- package/dist/internal/agent-orb-motion.js +9 -7
- package/dist/internal/agent-orb.d.ts +4 -1
- package/dist/internal/agent-orb.d.ts.map +1 -1
- package/dist/internal/agent-orb.js +7 -1
- package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
- package/dist/internal/assistant-ui/attachment.js +15 -15
- package/dist/internal/assistant-ui/follow-up-suggestions.js +1 -1
- package/dist/internal/assistant-ui/markdown-text.js +26 -25
- package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
- package/dist/internal/assistant-ui/reasoning.js +13 -16
- package/dist/internal/assistant-ui/thread.d.ts.map +1 -1
- package/dist/internal/assistant-ui/thread.js +22 -16
- package/dist/internal/assistant-ui/tool-fallback.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tool-fallback.js +29 -21
- package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tool-group.js +6 -7
- package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
- package/dist/internal/badge-frame.d.ts +15 -0
- package/dist/internal/badge-frame.d.ts.map +1 -0
- package/dist/internal/badge-frame.js +44 -0
- package/dist/internal/button-classes.d.ts +20 -0
- package/dist/internal/button-classes.d.ts.map +1 -0
- package/dist/internal/button-classes.js +74 -0
- package/dist/internal/catalog-list.d.ts.map +1 -1
- package/dist/internal/catalog-list.js +8 -4
- package/dist/internal/collection-rail.js +3 -3
- package/dist/internal/form-drawer-frame.d.ts +10 -0
- package/dist/internal/form-drawer-frame.d.ts.map +1 -0
- package/dist/internal/form-drawer-frame.js +9 -0
- package/dist/internal/form-submit.d.ts +2 -1
- package/dist/internal/form-submit.d.ts.map +1 -1
- package/dist/internal/settings-save.d.ts.map +1 -1
- package/dist/internal/settings-save.js +4 -2
- package/dist/internal/status-mark.d.ts.map +1 -1
- package/dist/internal/status-mark.js +4 -1
- package/dist/internal/tab-row.d.ts +10 -0
- package/dist/internal/tab-row.d.ts.map +1 -0
- package/dist/internal/tab-row.js +2 -0
- package/dist/internal/table-compact-line.d.ts +1 -1
- package/dist/internal/table-compact-line.d.ts.map +1 -1
- package/dist/internal/table-compact-line.js +1 -1
- package/dist/internal/tooltip-content-frame.d.ts +14 -0
- package/dist/internal/tooltip-content-frame.d.ts.map +1 -0
- package/dist/internal/tooltip-content-frame.js +11 -0
- package/dist/lib/css-variables.d.ts +8 -0
- package/dist/lib/css-variables.d.ts.map +1 -0
- package/dist/lib/css-variables.js +4 -0
- package/dist/lib/merge-tokens.generated.js +1 -1
- package/dist/lib/use-copy-to-clipboard.d.ts.map +1 -1
- package/dist/lib/use-copy-to-clipboard.js +1 -2
- package/dist/showcase/samples.js +1 -1
- package/dist/showcase/specimens/choices.d.ts.map +1 -1
- package/dist/showcase/specimens/choices.js +3 -2
- package/dist/showcase/specimens/compositions.d.ts.map +1 -1
- package/dist/showcase/specimens/compositions.js +7 -5
- package/dist/showcase/specimens/data.d.ts.map +1 -1
- package/dist/showcase/specimens/data.js +1 -1
- package/dist/showcase/specimens/feedback.d.ts.map +1 -1
- package/dist/showcase/specimens/feedback.js +1 -2
- package/dist/showcase/specimens/index.d.ts +2 -0
- package/dist/showcase/specimens/index.d.ts.map +1 -1
- package/dist/showcase/specimens/inputs.d.ts +1 -0
- package/dist/showcase/specimens/inputs.d.ts.map +1 -1
- package/dist/showcase/specimens/inputs.js +5 -2
- package/dist/showcase/specimens/menus.d.ts +1 -0
- package/dist/showcase/specimens/menus.d.ts.map +1 -1
- package/dist/showcase/specimens/menus.js +7 -2
- package/dist/showcase/specimens/navigation.d.ts.map +1 -1
- package/dist/showcase/specimens/navigation.js +6 -5
- package/dist/showcase/specimens/status.d.ts.map +1 -1
- package/dist/showcase/specimens/status.js +4 -4
- package/dist/showcase/usage.generated.d.ts +1 -1
- package/dist/showcase/usage.generated.d.ts.map +1 -1
- package/dist/showcase/usage.generated.js +38 -16
- package/dist/standalone/index.js +1 -1
- package/dist/standalone/orb-page.js +10 -8
- package/dist/styles/components.css +1 -1
- package/package.json +32 -33
- package/dist/eslint/index.js +0 -224
- package/dist/eslint/rules/badge-is-static-label.js +0 -93
- package/dist/eslint/rules/button-icon-slot.js +0 -102
- package/dist/eslint/rules/button-link-for-navigation.js +0 -146
- package/dist/eslint/rules/button-no-aria-label-with-text.js +0 -75
- package/dist/eslint/rules/button-type-roles-only-in-buttons.js +0 -83
- package/dist/eslint/rules/choice-control-has-a-name.js +0 -98
- package/dist/eslint/rules/choice-group-in-fieldset.js +0 -86
- package/dist/eslint/rules/command-shortcut-single-owner.js +0 -118
- package/dist/eslint/rules/confirm-dialog-words.js +0 -63
- package/dist/eslint/rules/data-table-one-unsized-column.js +0 -96
- package/dist/eslint/rules/destructive-action-placement.js +0 -69
- package/dist/eslint/rules/disabled-choice-says-why.js +0 -71
- package/dist/eslint/rules/disabled-control-appearance.js +0 -75
- package/dist/eslint/rules/ellipsis-opens-a-dialog.js +0 -110
- package/dist/eslint/rules/empty-state-owner.js +0 -145
- package/dist/eslint/rules/input-group-owners.js +0 -155
- package/dist/eslint/rules/jsx-class-utility.js +0 -200
- package/dist/eslint/rules/kbd-children-no-modifier-text.js +0 -128
- package/dist/eslint/rules/literal-text.js +0 -41
- package/dist/eslint/rules/menu-owner.js +0 -75
- package/dist/eslint/rules/middle-truncate-placement.js +0 -69
- package/dist/eslint/rules/no-app-feature-component.js +0 -100
- package/dist/eslint/rules/no-bare-button-label.js +0 -55
- package/dist/eslint/rules/no-choice-control-wrapper.js +0 -97
- package/dist/eslint/rules/no-comment.js +0 -77
- package/dist/eslint/rules/no-control-in-read-only-surface.js +0 -106
- package/dist/eslint/rules/no-decorative-icon.js +0 -164
- package/dist/eslint/rules/no-deprecated-zod-api.js +0 -215
- package/dist/eslint/rules/no-global-patch.js +0 -115
- package/dist/eslint/rules/no-hand-built-busy.js +0 -104
- package/dist/eslint/rules/no-hand-built-theme-picker.js +0 -144
- package/dist/eslint/rules/no-hand-built-tile.js +0 -41
- package/dist/eslint/rules/no-hand-formatted-date.js +0 -87
- package/dist/eslint/rules/no-hand-stacked-avatars.js +0 -67
- package/dist/eslint/rules/no-nested-collapse.js +0 -60
- package/dist/eslint/rules/no-package-component-redefinition.js +0 -175
- package/dist/eslint/rules/no-search-in-form.js +0 -47
- package/dist/eslint/rules/no-shadcn-appearance-override.js +0 -305
- package/dist/eslint/rules/no-shell-prompt-in-snippet.js +0 -74
- package/dist/eslint/rules/no-stock-error-words.js +0 -56
- package/dist/eslint/rules/no-subject-derived-expectation.js +0 -183
- package/dist/eslint/rules/no-tooltip-around-labelled-field.js +0 -72
- package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +0 -57
- package/dist/eslint/rules/no-write-in-subscription.js +0 -140
- package/dist/eslint/rules/one-submenu-level.js +0 -43
- package/dist/eslint/rules/one-toast-per-flow.js +0 -91
- package/dist/eslint/rules/option-count.js +0 -167
- package/dist/eslint/rules/page-command-link-not-render.js +0 -43
- package/dist/eslint/rules/page-composition.js +0 -232
- package/dist/eslint/rules/progress-needs-name.js +0 -57
- package/dist/eslint/rules/rendered-import.js +0 -121
- package/dist/eslint/rules/server-action-discipline.js +0 -233
- package/dist/eslint/rules/settings-limit-in-footer.js +0 -94
- package/dist/eslint/rules/skeleton-in-region.js +0 -80
- package/dist/eslint/rules/sortable-heading-uses-sort.js +0 -79
- package/dist/eslint/rules/table-cell-inset-owner.js +0 -107
- package/dist/eslint/rules/tabs-selection-is-controlled.js +0 -47
- package/dist/eslint/rules/test-files-from-one-definition.js +0 -67
- package/dist/eslint/rules/tooltip-only-arrow.js +0 -38
- package/dist/eslint/rules/tooltip-trigger-accessible-name.js +0 -114
- package/dist/eslint/rules/workspace-declares-imports.js +0 -163
- package/dist/eslint/strict-design-system.js +0 -250
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Standard tool checked first: a `no-restricted-syntax` selector such as
|
|
3
|
-
* `CardTitle JSXOpeningElement` matches any element under a title, not the
|
|
4
|
-
* ones bound to an icon module, and cannot stop at a control between the two
|
|
5
|
-
* (`<CardHeader><Button><Plus/></Button></CardHeader>` is allowed). This rule
|
|
6
|
-
* joins the element to its import and reads the nearest container above it.
|
|
7
|
-
*/
|
|
8
|
-
import { renderedImports, sourceMatches, writtenName } from "./rendered-import.js";
|
|
9
|
-
|
|
10
|
-
export const noDecorativeIcon = {
|
|
11
|
-
meta: {
|
|
12
|
-
type: "problem",
|
|
13
|
-
docs: {
|
|
14
|
-
description:
|
|
15
|
-
"A title or a label carries no icon; an icon belongs to a control or a menu row.",
|
|
16
|
-
},
|
|
17
|
-
schema: [
|
|
18
|
-
{
|
|
19
|
-
type: "object",
|
|
20
|
-
properties: {
|
|
21
|
-
iconModules: { type: "array", items: { type: "string" }, uniqueItems: true },
|
|
22
|
-
headings: { type: "array", items: { type: "string" }, uniqueItems: true },
|
|
23
|
-
controls: { type: "array", items: { type: "string" }, uniqueItems: true },
|
|
24
|
-
},
|
|
25
|
-
additionalProperties: false,
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
defaultOptions: [
|
|
29
|
-
{
|
|
30
|
-
iconModules: ["lucide-react", "@nadicodeai/ui/components/house-icons"],
|
|
31
|
-
headings: [
|
|
32
|
-
"h1",
|
|
33
|
-
"h2",
|
|
34
|
-
"h3",
|
|
35
|
-
"h4",
|
|
36
|
-
"h5",
|
|
37
|
-
"h6",
|
|
38
|
-
"legend",
|
|
39
|
-
"dt",
|
|
40
|
-
"th",
|
|
41
|
-
"caption",
|
|
42
|
-
"CardTitle",
|
|
43
|
-
"CardDescription",
|
|
44
|
-
"CardHeader",
|
|
45
|
-
"DialogTitle",
|
|
46
|
-
"SheetTitle",
|
|
47
|
-
"AlertDialogTitle",
|
|
48
|
-
"DrawerTitle",
|
|
49
|
-
"FieldLabel",
|
|
50
|
-
"FieldLegend",
|
|
51
|
-
"Label",
|
|
52
|
-
"ItemTitle",
|
|
53
|
-
"TableHead",
|
|
54
|
-
],
|
|
55
|
-
controls: [
|
|
56
|
-
"button",
|
|
57
|
-
"a",
|
|
58
|
-
"Button",
|
|
59
|
-
"Link",
|
|
60
|
-
"CardAction",
|
|
61
|
-
"TabsTrigger",
|
|
62
|
-
"TabsLink",
|
|
63
|
-
"ToggleGroupItem",
|
|
64
|
-
"Toggle",
|
|
65
|
-
"SidebarMenuButton",
|
|
66
|
-
"InputGroupAddon",
|
|
67
|
-
"InputGroupButton",
|
|
68
|
-
],
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
messages: {
|
|
72
|
-
"beside-heading":
|
|
73
|
-
"A title or a label carries no icon: an identity mark starts a row or a card (IdentityMark), and an icon belongs to a control or a menu row.",
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
create(context) {
|
|
77
|
-
const [{ iconModules, headings, controls }] = context.options;
|
|
78
|
-
const headingNames = new Set(headings);
|
|
79
|
-
const controlNames = new Set(controls);
|
|
80
|
-
const importOf = renderedImports(context);
|
|
81
|
-
const isIconImport = (name) => {
|
|
82
|
-
const imported = importOf({ type: "JSXIdentifier", name });
|
|
83
|
-
return (
|
|
84
|
-
imported !== null &&
|
|
85
|
-
iconModules.some((module) => sourceMatches(imported.source, module, context.filename))
|
|
86
|
-
);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
function constantOf(name, scope) {
|
|
90
|
-
for (let current = scope; current; current = current.upper) {
|
|
91
|
-
const variable = current.set.get(name);
|
|
92
|
-
if (!variable) continue;
|
|
93
|
-
const [definition] = variable.defs;
|
|
94
|
-
const declared =
|
|
95
|
-
definition?.type === "Variable" && definition.parent.kind === "const"
|
|
96
|
-
? definition.node.init
|
|
97
|
-
: null;
|
|
98
|
-
return declared ?? null;
|
|
99
|
-
}
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function holdsAnIcon(value, scope, seen = new Set()) {
|
|
104
|
-
if (!value || seen.has(value)) return false;
|
|
105
|
-
seen.add(value);
|
|
106
|
-
switch (value.type) {
|
|
107
|
-
case "Identifier":
|
|
108
|
-
return (
|
|
109
|
-
isIconImport(value.name) || holdsAnIcon(constantOf(value.name, scope), scope, seen)
|
|
110
|
-
);
|
|
111
|
-
case "MemberExpression":
|
|
112
|
-
return holdsAnIcon(value.object, scope, seen);
|
|
113
|
-
case "ConditionalExpression":
|
|
114
|
-
return [value.consequent, value.alternate].some((branch) =>
|
|
115
|
-
holdsAnIcon(branch, scope, seen),
|
|
116
|
-
);
|
|
117
|
-
case "LogicalExpression":
|
|
118
|
-
return [value.left, value.right].some((side) => holdsAnIcon(side, scope, seen));
|
|
119
|
-
case "ObjectExpression":
|
|
120
|
-
return value.properties.some(
|
|
121
|
-
(property) => property.type === "Property" && holdsAnIcon(property.value, scope, seen),
|
|
122
|
-
);
|
|
123
|
-
case "ArrayExpression":
|
|
124
|
-
return value.elements.some((element) => holdsAnIcon(element, scope, seen));
|
|
125
|
-
default:
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function isIcon(openingElement) {
|
|
131
|
-
const imported = importOf(openingElement.name);
|
|
132
|
-
if (imported) {
|
|
133
|
-
return iconModules.some((module) =>
|
|
134
|
-
sourceMatches(imported.source, module, context.filename),
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
const { name } = openingElement;
|
|
138
|
-
if (name.type !== "JSXIdentifier" || !/^[A-Z]/.test(name.name)) return false;
|
|
139
|
-
const scope = context.sourceCode.getScope(openingElement);
|
|
140
|
-
return holdsAnIcon(constantOf(name.name, scope), scope);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
JSXOpeningElement(node) {
|
|
145
|
-
if (!isIcon(node)) return;
|
|
146
|
-
for (let current = node.parent.parent; current; current = current.parent) {
|
|
147
|
-
if (current.type === "JSXAttribute") {
|
|
148
|
-
if (headingNames.has(writtenName(current.parent.name))) {
|
|
149
|
-
context.report({ node, messageId: "beside-heading" });
|
|
150
|
-
}
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (current.type !== "JSXElement") continue;
|
|
154
|
-
const name = writtenName(current.openingElement.name);
|
|
155
|
-
if (controlNames.has(name)) return;
|
|
156
|
-
if (headingNames.has(name)) {
|
|
157
|
-
context.report({ node, messageId: "beside-heading" });
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
},
|
|
164
|
-
};
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Legacy Zod 3 APIs, in two sets that are enforced from two different places.
|
|
3
|
-
*
|
|
4
|
-
* UNANNOTATED — the three the installed zod 4.6.5 leaves without a
|
|
5
|
-
* `@deprecated` tag: `promise` carries only prose, `.strict()` and `.strip()`
|
|
6
|
-
* carry only a "consider…" hint. No tag means no type-aware rule can fire on
|
|
7
|
-
* them, so they are this rule's own policy and are reported in every
|
|
8
|
-
* workspace.
|
|
9
|
-
*
|
|
10
|
-
* ANNOTATED — the rest, which Zod does tag `@deprecated`, `nativeEnum`
|
|
11
|
-
* included. `@typescript-eslint/no-deprecated` reports those with the
|
|
12
|
-
* library's own replacement text, but only where it is turned on, and it needs
|
|
13
|
-
* type information. In this repository that is `@nadicodeai/ui` alone:
|
|
14
|
-
* switching the Portal on measured 10.4s → 25.8s per lint run and surfaced 15
|
|
15
|
-
* unrelated findings (React's `FormEvent`, vitest's `toThrowError`), so it
|
|
16
|
-
* stays ui-only (#821). The Portal and the website are where this repository
|
|
17
|
-
* writes most of its Zod, so the names stay listed here for them — a second
|
|
18
|
-
* copy of an upstream annotation, kept deliberately because the alternative is
|
|
19
|
-
* no coverage at all in two of three workspaces (#822).
|
|
20
|
-
*
|
|
21
|
-
* The split is a rule option: `upstreamAnnotated: "report"` (default) names
|
|
22
|
-
* them, `"defer"` leaves them to the type-aware rule. `@nadicodeai/ui` selects
|
|
23
|
-
* `"defer"` in the root config, so no tagged API is reported twice there.
|
|
24
|
-
*/
|
|
25
|
-
const LEGACY_FACTORIES = new Map([["promise", "await the value before parsing it"]]);
|
|
26
|
-
|
|
27
|
-
const UPSTREAM_ANNOTATED_FACTORIES = new Map([["nativeEnum", "z.enum()"]]);
|
|
28
|
-
|
|
29
|
-
const LEGACY_METHODS = {
|
|
30
|
-
object: new Map([
|
|
31
|
-
["strict", "z.strictObject()"],
|
|
32
|
-
["strip", "z.object(), which strips unknown keys by default"],
|
|
33
|
-
]),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const UPSTREAM_ANNOTATED_METHODS = {
|
|
37
|
-
number: new Map([
|
|
38
|
-
["finite", "remove it; z.number() already rejects infinite values"],
|
|
39
|
-
["safe", ".int()"],
|
|
40
|
-
["step", ".multipleOf()"],
|
|
41
|
-
]),
|
|
42
|
-
object: new Map([
|
|
43
|
-
["merge", ".extend(other.shape)"],
|
|
44
|
-
["passthrough", "z.looseObject()"],
|
|
45
|
-
]),
|
|
46
|
-
string: new Map([
|
|
47
|
-
...[
|
|
48
|
-
"base64",
|
|
49
|
-
"base64url",
|
|
50
|
-
"cidrv4",
|
|
51
|
-
"cidrv6",
|
|
52
|
-
"cuid",
|
|
53
|
-
"cuid2",
|
|
54
|
-
"e164",
|
|
55
|
-
"email",
|
|
56
|
-
"emoji",
|
|
57
|
-
"guid",
|
|
58
|
-
"ipv4",
|
|
59
|
-
"ipv6",
|
|
60
|
-
"jwt",
|
|
61
|
-
"ksuid",
|
|
62
|
-
"nanoid",
|
|
63
|
-
"ulid",
|
|
64
|
-
"url",
|
|
65
|
-
"uuid",
|
|
66
|
-
"uuidv4",
|
|
67
|
-
"uuidv6",
|
|
68
|
-
"uuidv7",
|
|
69
|
-
"xid",
|
|
70
|
-
].map((name) => [name, `z.${name.startsWith("uuidv") ? "uuid" : name}()`]),
|
|
71
|
-
...["date", "datetime", "duration", "time"].map((name) => [name, `z.iso.${name}()`]),
|
|
72
|
-
]),
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
function defersAnnotated(context) {
|
|
76
|
-
return context.options?.[0]?.upstreamAnnotated === "defer";
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** The factory table a run consults: the unannotated always, the annotated unless deferred. */
|
|
80
|
-
function legacyFactories(context) {
|
|
81
|
-
if (defersAnnotated(context)) return LEGACY_FACTORIES;
|
|
82
|
-
return new Map([...UPSTREAM_ANNOTATED_FACTORIES, ...LEGACY_FACTORIES]);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** The method table a run consults: the unannotated always, the annotated unless deferred. */
|
|
86
|
-
function legacyMethods(context) {
|
|
87
|
-
if (defersAnnotated(context)) return LEGACY_METHODS;
|
|
88
|
-
const merged = {};
|
|
89
|
-
for (const family of new Set([
|
|
90
|
-
...Object.keys(LEGACY_METHODS),
|
|
91
|
-
...Object.keys(UPSTREAM_ANNOTATED_METHODS),
|
|
92
|
-
])) {
|
|
93
|
-
merged[family] = new Map([
|
|
94
|
-
...(UPSTREAM_ANNOTATED_METHODS[family] ?? []),
|
|
95
|
-
...(LEGACY_METHODS[family] ?? []),
|
|
96
|
-
]);
|
|
97
|
-
}
|
|
98
|
-
return merged;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function memberName(node) {
|
|
102
|
-
if (!node.computed && node.property.type === "Identifier") {
|
|
103
|
-
return node.property.name;
|
|
104
|
-
}
|
|
105
|
-
if (node.computed && node.property.type === "Literal") {
|
|
106
|
-
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
|
|
107
|
-
return typeof node.property.value === "string" ? node.property.value : undefined;
|
|
108
|
-
}
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export const noDeprecatedZodApi = {
|
|
113
|
-
meta: {
|
|
114
|
-
type: "problem",
|
|
115
|
-
docs: {
|
|
116
|
-
description: "Disallow the Zod 3 APIs that Zod 4 replaced without marking them @deprecated",
|
|
117
|
-
},
|
|
118
|
-
schema: [
|
|
119
|
-
{
|
|
120
|
-
type: "object",
|
|
121
|
-
properties: { upstreamAnnotated: { enum: ["report", "defer"] } },
|
|
122
|
-
additionalProperties: false,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
messages: {
|
|
126
|
-
legacyFactory: "{{api}} is legacy in Zod 4; use {{replacement}}.",
|
|
127
|
-
legacyMethod: "{{api}} is legacy in Zod 4; use {{replacement}}.",
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
create(context) {
|
|
132
|
-
const sourceCode = context.sourceCode;
|
|
133
|
-
const factories = legacyFactories(context);
|
|
134
|
-
const methods = legacyMethods(context);
|
|
135
|
-
const zodBindings = new Set();
|
|
136
|
-
|
|
137
|
-
function isZodBinding(identifier) {
|
|
138
|
-
for (let scope = sourceCode.getScope(identifier); scope; scope = scope.upper) {
|
|
139
|
-
const variable = scope.set.get(identifier.name);
|
|
140
|
-
if (variable) {
|
|
141
|
-
return zodBindings.has(variable);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function rootFactory(call) {
|
|
148
|
-
if (call.type !== "CallExpression" || call.callee.type !== "MemberExpression") {
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
const factory = memberName(call.callee);
|
|
152
|
-
if (!factory) {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
if (call.callee.object.type === "Identifier" && isZodBinding(call.callee.object)) {
|
|
156
|
-
return factory;
|
|
157
|
-
}
|
|
158
|
-
return rootFactory(call.callee.object);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
ImportDeclaration(node) {
|
|
163
|
-
if (node.source.value !== "zod") {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
for (const specifier of node.specifiers) {
|
|
167
|
-
const importsNamespace = specifier.type === "ImportNamespaceSpecifier";
|
|
168
|
-
const importsZ =
|
|
169
|
-
specifier.type === "ImportSpecifier" &&
|
|
170
|
-
((specifier.imported.type === "Identifier" && specifier.imported.name === "z") ||
|
|
171
|
-
(specifier.imported.type === "Literal" && specifier.imported.value === "z"));
|
|
172
|
-
if (!importsNamespace && !importsZ) {
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
for (const variable of sourceCode.getDeclaredVariables(specifier)) {
|
|
176
|
-
zodBindings.add(variable);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
CallExpression(node) {
|
|
182
|
-
if (node.callee.type !== "MemberExpression") {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const method = memberName(node.callee);
|
|
186
|
-
if (!method) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (node.callee.object.type === "Identifier" && isZodBinding(node.callee.object)) {
|
|
191
|
-
const replacement = factories.get(method);
|
|
192
|
-
if (replacement) {
|
|
193
|
-
context.report({
|
|
194
|
-
node: node.callee.property,
|
|
195
|
-
messageId: "legacyFactory",
|
|
196
|
-
data: { api: `z.${method}()`, replacement },
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const factory = rootFactory(node.callee.object);
|
|
203
|
-
const family = factory === "strictObject" || factory === "looseObject" ? "object" : factory;
|
|
204
|
-
const replacement = methods[family]?.get(method);
|
|
205
|
-
if (replacement) {
|
|
206
|
-
context.report({
|
|
207
|
-
node: node.callee.property,
|
|
208
|
-
messageId: "legacyMethod",
|
|
209
|
-
data: { api: `z.${factory}().${method}()`, replacement },
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
},
|
|
215
|
-
};
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* A product built on another system's runtime must not replace what that
|
|
3
|
-
* runtime calls: assigning `history.pushState`, a property of `window`, or a
|
|
4
|
-
* member of a built-in prototype changes the code the other system runs, behind
|
|
5
|
-
* its back. Nadia's desktop once replaced `history.pushState` and
|
|
6
|
-
* `replaceState` to rewrite Hermes's routes; the product declares its intent to
|
|
7
|
-
* the owning system instead.
|
|
8
|
-
*
|
|
9
|
-
* Standard tool checked first: core `no-restricted-syntax` can match an
|
|
10
|
-
* assignment to `history.pushState` by name, but the route lock wrote it as
|
|
11
|
-
* `const { history } = target; history.pushState = guarded`, and a member
|
|
12
|
-
* reached through a local alias (`const h = window.history`) carries no global
|
|
13
|
-
* name a selector can see. This rule resolves a local constant to what it was
|
|
14
|
-
* initialised from before matching.
|
|
15
|
-
*
|
|
16
|
-
* Which objects count as globals is configuration: the workspace names them
|
|
17
|
-
* where the rule is turned on, and this file holds only the matching.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const PATCHERS = new Set([
|
|
21
|
-
"Object.assign",
|
|
22
|
-
"Object.defineProperty",
|
|
23
|
-
"Object.defineProperties",
|
|
24
|
-
"Reflect.set",
|
|
25
|
-
"Reflect.defineProperty",
|
|
26
|
-
]);
|
|
27
|
-
|
|
28
|
-
function variableOf(scope, name) {
|
|
29
|
-
for (let current = scope; current; current = current.upper) {
|
|
30
|
-
const found = current.set.get(name);
|
|
31
|
-
if (found) return found;
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function aliasOf(context, identifier) {
|
|
37
|
-
const variable = variableOf(context.sourceCode.getScope(identifier), identifier.name);
|
|
38
|
-
const definition = variable?.defs.length === 1 ? variable.defs[0] : null;
|
|
39
|
-
if (definition?.type !== "Variable" || definition.parent?.kind !== "const") return null;
|
|
40
|
-
const { id, init } = definition.node;
|
|
41
|
-
return id.type === "Identifier" && init ? init : null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function pathOf(context, node, seen = new Set()) {
|
|
45
|
-
if (node.type === "Identifier") {
|
|
46
|
-
if (seen.has(node.name)) return node.name;
|
|
47
|
-
const alias = aliasOf(context, node);
|
|
48
|
-
if (alias) return pathOf(context, alias, new Set([...seen, node.name])) ?? node.name;
|
|
49
|
-
return node.name;
|
|
50
|
-
}
|
|
51
|
-
if (node.type === "ChainExpression") return pathOf(context, node.expression, seen);
|
|
52
|
-
if (node.type === "MemberExpression" && !node.computed && node.property.type === "Identifier") {
|
|
53
|
-
const object = pathOf(context, node.object, seen);
|
|
54
|
-
return object === null ? null : `${object}.${node.property.name}`;
|
|
55
|
-
}
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export const noGlobalPatch = {
|
|
60
|
-
meta: {
|
|
61
|
-
type: "problem",
|
|
62
|
-
docs: {
|
|
63
|
-
description: "Disallow replacing a member of a global object or a built-in prototype",
|
|
64
|
-
},
|
|
65
|
-
schema: [
|
|
66
|
-
{
|
|
67
|
-
type: "object",
|
|
68
|
-
properties: {
|
|
69
|
-
globals: { type: "string" },
|
|
70
|
-
reason: { type: "string" },
|
|
71
|
-
},
|
|
72
|
-
required: ["globals"],
|
|
73
|
-
additionalProperties: false,
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
messages: {
|
|
77
|
-
patch:
|
|
78
|
-
"`{{target}}` is a global another system's code runs; replacing it changes that code behind its back. Declare the intent to its owner instead.{{reason}}",
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
create(context) {
|
|
82
|
-
const options = context.options?.[0];
|
|
83
|
-
if (!options) return {};
|
|
84
|
-
const globals = new RegExp(options.globals);
|
|
85
|
-
const reason = options.reason ? ` ${options.reason}` : "";
|
|
86
|
-
|
|
87
|
-
const isGlobal = (path) => {
|
|
88
|
-
const owner = path.split(".").at(-1);
|
|
89
|
-
return owner === "prototype" || globals.test(owner);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const check = (node, target) => {
|
|
93
|
-
if (target.type !== "MemberExpression") return;
|
|
94
|
-
const owner = pathOf(context, target.object);
|
|
95
|
-
if (owner === null || !isGlobal(owner)) return;
|
|
96
|
-
const member = target.computed ? "[…]" : `.${target.property.name}`;
|
|
97
|
-
context.report({ node, messageId: "patch", data: { reason, target: `${owner}${member}` } });
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
AssignmentExpression(node) {
|
|
102
|
-
check(node, node.left);
|
|
103
|
-
},
|
|
104
|
-
CallExpression(node) {
|
|
105
|
-
const callee = pathOf(context, node.callee);
|
|
106
|
-
const [target] = node.arguments;
|
|
107
|
-
if (!callee || !PATCHERS.has(callee) || !target) return;
|
|
108
|
-
const owner = pathOf(context, target);
|
|
109
|
-
if (owner !== null && isGlobal(owner)) {
|
|
110
|
-
context.report({ node, messageId: "patch", data: { reason, target: owner } });
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
};
|
|
@@ -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
|
-
};
|