@nadicodeai/ui 0.20.0 → 0.22.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/AGENTS.md +4 -3
- package/README.md +14 -21
- package/dist/agents.d.ts +10 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +9 -0
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/brand-icons/apple.d.ts +18 -0
- package/dist/components/brand-icons/apple.d.ts.map +1 -0
- package/dist/components/brand-icons/apple.js +22 -0
- package/dist/components/brand-icons/brand-icon.d.ts +22 -0
- package/dist/components/brand-icons/brand-icon.d.ts.map +1 -0
- package/dist/components/brand-icons/brand-icon.js +26 -0
- package/dist/components/brand-icons/linux.d.ts +18 -0
- package/dist/components/brand-icons/linux.d.ts.map +1 -0
- package/dist/components/brand-icons/linux.js +25 -0
- package/dist/components/brand-icons/windows.d.ts +18 -0
- package/dist/components/brand-icons/windows.d.ts.map +1 -0
- package/dist/components/brand-icons/windows.js +22 -0
- package/dist/components/brand-icons.d.ts +5 -0
- package/dist/components/brand-icons.d.ts.map +1 -0
- package/dist/components/brand-icons.js +10 -0
- package/dist/components/button.d.ts +1 -1
- package/dist/components/button.js +1 -1
- package/dist/components/card-marketing.js +2 -2
- package/dist/components/card.d.ts +4 -3
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +12 -2
- package/dist/components/chart-breakdown-table.js +1 -1
- package/dist/components/chart-bullet-bar.d.ts +18 -2
- package/dist/components/chart-bullet-bar.d.ts.map +1 -1
- package/dist/components/chart-bullet-bar.js +62 -9
- package/dist/components/chart-date-range-control.d.ts +11 -1
- package/dist/components/chart-date-range-control.d.ts.map +1 -1
- package/dist/components/chart-date-range-control.js +13 -3
- package/dist/components/chart-entity-colors.d.ts +7 -1
- package/dist/components/chart-entity-colors.d.ts.map +1 -1
- package/dist/components/chart-entity-colors.js +7 -1
- package/dist/components/chart-kpi-stat.d.ts.map +1 -1
- package/dist/components/chart-kpi-stat.js +1 -1
- package/dist/components/chart-time-series.d.ts.map +1 -1
- package/dist/components/chart-time-series.js +62 -4
- package/dist/components/chart.js +2 -2
- package/dist/components/code-editor-mockup.js +1 -1
- package/dist/components/nav-bar.js +1 -1
- package/dist/components/radio-group.d.ts +1 -1
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js +2 -2
- package/dist/components/theme-mode-switcher.d.ts +10 -3
- package/dist/components/theme-mode-switcher.d.ts.map +1 -1
- package/dist/components/theme-mode-switcher.js +14 -7
- package/dist/components/use-case-bento.d.ts +2 -4
- package/dist/components/use-case-bento.d.ts.map +1 -1
- package/dist/components/use-case-bento.js +2 -3
- package/dist/eslint/index.js +91 -0
- package/dist/eslint/rules/jsx-class-utility.js +231 -0
- package/dist/eslint/rules/no-deprecated-zod-api.js +156 -0
- package/dist/eslint/rules/no-design-system-src-import.js +11 -0
- package/dist/eslint/rules/no-invalid-token-utility.js +46 -0
- package/dist/eslint/rules/no-native-visible-control.js +86 -0
- package/dist/eslint/rules/no-raw-color-utility.js +109 -0
- package/dist/eslint/rules/no-raw-focus-ring-width.js +30 -0
- package/dist/eslint/rules/no-raw-logo-import.js +41 -0
- package/dist/eslint/rules/no-shadcn-appearance-override.js +191 -0
- package/dist/eslint/rules/no-token-category-bypass.js +53 -0
- package/dist/eslint/rules/no-ui-src-import.js +10 -0
- package/dist/eslint/rules/no-unpinned-vercel-functions-import.js +100 -0
- package/dist/eslint/rules/src-import-boundary.js +207 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +0 -1
- package/dist/index.d.ts +1 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -16
- package/dist/internal/assistant-ui/attachment.d.ts +5 -0
- package/dist/internal/assistant-ui/attachment.d.ts.map +1 -0
- package/dist/internal/assistant-ui/attachment.js +93 -0
- package/dist/internal/assistant-ui/follow-up-suggestions.d.ts +3 -0
- package/dist/internal/assistant-ui/follow-up-suggestions.d.ts.map +1 -0
- package/dist/internal/assistant-ui/follow-up-suggestions.js +8 -0
- package/dist/internal/assistant-ui/markdown-text.d.ts +2 -0
- package/dist/internal/assistant-ui/markdown-text.d.ts.map +1 -0
- package/dist/internal/assistant-ui/markdown-text.js +54 -0
- package/dist/internal/assistant-ui/reasoning.d.ts +31 -0
- package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -0
- package/dist/internal/assistant-ui/reasoning.js +118 -0
- package/dist/internal/assistant-ui/thread.d.ts +26 -0
- package/dist/internal/assistant-ui/thread.d.ts.map +1 -0
- package/dist/internal/assistant-ui/thread.js +118 -0
- package/dist/internal/assistant-ui/tool-fallback.d.ts +10 -0
- package/dist/internal/assistant-ui/tool-fallback.d.ts.map +1 -0
- package/dist/internal/assistant-ui/tool-fallback.js +163 -0
- package/dist/internal/assistant-ui/tool-group.d.ts +18 -0
- package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -0
- package/dist/internal/assistant-ui/tool-group.js +44 -0
- package/dist/internal/assistant-ui/tooltip-icon-button.d.ts +8 -0
- package/dist/internal/assistant-ui/tooltip-icon-button.d.ts.map +1 -0
- package/dist/internal/assistant-ui/tooltip-icon-button.js +10 -0
- package/dist/styles/globals.css +3 -209
- package/docs/agents/nadicodeai-ui.md +4 -6
- package/docs/component-classification.md +1 -1
- package/docs/consuming-cross-repo.md +32 -3
- package/docs/contract.md +16 -6
- package/docs/migration/base-ui-migration.md +0 -11
- package/docs/product-compositions.md +1 -1
- package/llms.txt +5 -6
- package/package.json +17 -29
- package/skills/nadicodeai-ui/SKILL.md +5 -7
- package/dist/ai-elements.d.ts +0 -49
- package/dist/ai-elements.d.ts.map +0 -1
- package/dist/ai-elements.js +0 -48
- package/dist/components/agent-avatar.d.ts +0 -7
- package/dist/components/agent-avatar.d.ts.map +0 -1
- package/dist/components/agent-avatar.js +0 -6
- package/dist/components/agent-chat.d.ts +0 -20
- package/dist/components/agent-chat.d.ts.map +0 -1
- package/dist/components/agent-chat.js +0 -10
- package/dist/components/agent-composer.d.ts +0 -56
- package/dist/components/agent-composer.d.ts.map +0 -1
- package/dist/components/agent-composer.js +0 -56
- package/dist/components/agent-message.d.ts +0 -117
- package/dist/components/agent-message.d.ts.map +0 -1
- package/dist/components/agent-message.js +0 -99
- package/dist/components/agent-thread.d.ts +0 -26
- package/dist/components/agent-thread.d.ts.map +0 -1
- package/dist/components/agent-thread.js +0 -63
- package/dist/components/agent-tool-approval.d.ts +0 -27
- package/dist/components/agent-tool-approval.d.ts.map +0 -1
- package/dist/components/agent-tool-approval.js +0 -43
- package/dist/components/ai-elements/agent.d.ts +0 -27
- package/dist/components/ai-elements/agent.d.ts.map +0 -1
- package/dist/components/ai-elements/agent.js +0 -25
- package/dist/components/ai-elements/artifact.d.ts +0 -24
- package/dist/components/ai-elements/artifact.d.ts.map +0 -1
- package/dist/components/ai-elements/artifact.js +0 -20
- package/dist/components/ai-elements/attachments.d.ts +0 -55
- package/dist/components/ai-elements/attachments.d.ts.map +0 -1
- package/dist/components/ai-elements/attachments.js +0 -125
- package/dist/components/ai-elements/audio-player.d.ts +0 -30
- package/dist/components/ai-elements/audio-player.d.ts.map +0 -1
- package/dist/components/ai-elements/audio-player.js +0 -41
- package/dist/components/ai-elements/canvas.d.ts +0 -8
- package/dist/components/ai-elements/canvas.d.ts.map +0 -1
- package/dist/components/ai-elements/canvas.js +0 -5
- package/dist/components/ai-elements/chain-of-thought.d.ts +0 -30
- package/dist/components/ai-elements/chain-of-thought.d.ts.map +0 -1
- package/dist/components/ai-elements/chain-of-thought.js +0 -49
- package/dist/components/ai-elements/checkpoint.d.ts +0 -12
- package/dist/components/ai-elements/checkpoint.d.ts.map +0 -1
- package/dist/components/ai-elements/checkpoint.js +0 -10
- package/dist/components/ai-elements/code-block.d.ts +0 -53
- package/dist/components/ai-elements/code-block.d.ts.map +0 -1
- package/dist/components/ai-elements/code-block.js +0 -239
- package/dist/components/ai-elements/commit.d.ts +0 -65
- package/dist/components/ai-elements/commit.d.ts.map +0 -1
- package/dist/components/ai-elements/commit.js +0 -83
- package/dist/components/ai-elements/confirmation.d.ts +0 -50
- package/dist/components/ai-elements/confirmation.d.ts.map +0 -1
- package/dist/components/ai-elements/confirmation.js +0 -57
- package/dist/components/ai-elements/connection.d.ts +0 -6
- package/dist/components/ai-elements/connection.d.ts.map +0 -1
- package/dist/components/ai-elements/connection.js +0 -10
- package/dist/components/ai-elements/context.d.ts +0 -33
- package/dist/components/ai-elements/context.d.ts.map +0 -1
- package/dist/components/ai-elements/context.js +0 -147
- package/dist/components/ai-elements/controls.d.ts +0 -5
- package/dist/components/ai-elements/controls.d.ts.map +0 -1
- package/dist/components/ai-elements/controls.js +0 -5
- package/dist/components/ai-elements/conversation.d.ts +0 -24
- package/dist/components/ai-elements/conversation.d.ts.map +0 -1
- package/dist/components/ai-elements/conversation.js +0 -41
- package/dist/components/ai-elements/edge.d.ts +0 -6
- package/dist/components/ai-elements/edge.d.ts.map +0 -1
- package/dist/components/ai-elements/edge.js +0 -88
- package/dist/components/ai-elements/environment-variables.d.ts +0 -39
- package/dist/components/ai-elements/environment-variables.d.ts.map +0 -1
- package/dist/components/ai-elements/environment-variables.js +0 -60
- package/dist/components/ai-elements/file-tree.d.ts +0 -27
- package/dist/components/ai-elements/file-tree.d.ts.map +0 -1
- package/dist/components/ai-elements/file-tree.js +0 -73
- package/dist/components/ai-elements/image.d.ts +0 -7
- package/dist/components/ai-elements/image.d.ts.map +0 -1
- package/dist/components/ai-elements/image.js +0 -3
- package/dist/components/ai-elements/inline-citation.d.ts +0 -39
- package/dist/components/ai-elements/inline-citation.d.ts.map +0 -1
- package/dist/components/ai-elements/inline-citation.js +0 -86
- package/dist/components/ai-elements/jsx-preview.d.ts +0 -30
- package/dist/components/ai-elements/jsx-preview.d.ts.map +0 -1
- package/dist/components/ai-elements/jsx-preview.js +0 -159
- package/dist/components/ai-elements/message.d.ts +0 -38
- package/dist/components/ai-elements/message.d.ts.map +0 -1
- package/dist/components/ai-elements/message.js +0 -91
- package/dist/components/ai-elements/mic-selector.d.ts +0 -46
- package/dist/components/ai-elements/mic-selector.d.ts.map +0 -1
- package/dist/components/ai-elements/mic-selector.js +0 -180
- package/dist/components/ai-elements/model-selector.d.ts +0 -36
- package/dist/components/ai-elements/model-selector.d.ts.map +0 -1
- package/dist/components/ai-elements/model-selector.js +0 -18
- package/dist/components/ai-elements/node.d.ts +0 -22
- package/dist/components/ai-elements/node.d.ts.map +0 -1
- package/dist/components/ai-elements/node.js +0 -12
- package/dist/components/ai-elements/open-in-chat.d.ts +0 -27
- package/dist/components/ai-elements/open-in-chat.d.ts.map +0 -1
- package/dist/components/ai-elements/open-in-chat.js +0 -81
- package/dist/components/ai-elements/package-info.d.ts +0 -30
- package/dist/components/ai-elements/package-info.d.ts.map +0 -1
- package/dist/components/ai-elements/package-info.js +0 -50
- package/dist/components/ai-elements/panel.d.ts +0 -6
- package/dist/components/ai-elements/panel.d.ts.map +0 -1
- package/dist/components/ai-elements/panel.js +0 -5
- package/dist/components/ai-elements/persona.d.ts +0 -49
- package/dist/components/ai-elements/persona.d.ts.map +0 -1
- package/dist/components/ai-elements/persona.js +0 -175
- package/dist/components/ai-elements/plan.d.ts +0 -27
- package/dist/components/ai-elements/plan.d.ts.map +0 -1
- package/dist/components/ai-elements/plan.js +0 -34
- package/dist/components/ai-elements/prompt-input.d.ts +0 -149
- package/dist/components/ai-elements/prompt-input.d.ts.map +0 -1
- package/dist/components/ai-elements/prompt-input.js +0 -704
- package/dist/components/ai-elements/queue.d.ts +0 -62
- package/dist/components/ai-elements/queue.d.ts.map +0 -1
- package/dist/components/ai-elements/queue.js +0 -24
- package/dist/components/ai-elements/reasoning.d.ts +0 -27
- package/dist/components/ai-elements/reasoning.d.ts.map +0 -1
- package/dist/components/ai-elements/reasoning.js +0 -92
- package/dist/components/ai-elements/sandbox.d.ts +0 -25
- package/dist/components/ai-elements/sandbox.d.ts.map +0 -1
- package/dist/components/ai-elements/sandbox.js +0 -15
- package/dist/components/ai-elements/schema-display.d.ts +0 -56
- package/dist/components/ai-elements/schema-display.d.ts.map +0 -1
- package/dist/components/ai-elements/schema-display.js +0 -90
- package/dist/components/ai-elements/shimmer.d.ts +0 -10
- package/dist/components/ai-elements/shimmer.d.ts.map +0 -1
- package/dist/components/ai-elements/shimmer.js +0 -11
- package/dist/components/ai-elements/snippet.d.ts +0 -19
- package/dist/components/ai-elements/snippet.d.ts.map +0 -1
- package/dist/components/ai-elements/snippet.js +0 -32
- package/dist/components/ai-elements/sources.d.ts +0 -13
- package/dist/components/ai-elements/sources.d.ts.map +0 -1
- package/dist/components/ai-elements/sources.js +0 -9
- package/dist/components/ai-elements/speech-input.d.ts +0 -55
- package/dist/components/ai-elements/speech-input.d.ts.map +0 -1
- package/dist/components/ai-elements/speech-input.js +0 -187
- package/dist/components/ai-elements/stack-trace.d.ts +0 -38
- package/dist/components/ai-elements/stack-trace.d.ts.map +0 -1
- package/dist/components/ai-elements/stack-trace.js +0 -181
- package/dist/components/ai-elements/suggestion.d.ts +0 -11
- package/dist/components/ai-elements/suggestion.d.ts.map +0 -1
- package/dist/components/ai-elements/suggestion.js +0 -13
- package/dist/components/ai-elements/task.d.ts +0 -15
- package/dist/components/ai-elements/task.d.ts.map +0 -1
- package/dist/components/ai-elements/task.js +0 -10
- package/dist/components/ai-elements/terminal.d.ts +0 -28
- package/dist/components/ai-elements/terminal.d.ts.map +0 -1
- package/dist/components/ai-elements/terminal.js +0 -55
- package/dist/components/ai-elements/test-results.d.ts +0 -59
- package/dist/components/ai-elements/test-results.d.ts.map +0 -1
- package/dist/components/ai-elements/test-results.js +0 -98
- package/dist/components/ai-elements/tool.d.ts +0 -32
- package/dist/components/ai-elements/tool.d.ts.map +0 -1
- package/dist/components/ai-elements/tool.js +0 -49
- package/dist/components/ai-elements/toolbar.d.ts +0 -6
- package/dist/components/ai-elements/toolbar.d.ts.map +0 -1
- package/dist/components/ai-elements/toolbar.js +0 -5
- package/dist/components/ai-elements/transcription.d.ts +0 -17
- package/dist/components/ai-elements/transcription.d.ts.map +0 -1
- package/dist/components/ai-elements/transcription.js +0 -36
- package/dist/components/ai-elements/voice-selector.d.ts +0 -65
- package/dist/components/ai-elements/voice-selector.d.ts.map +0 -1
- package/dist/components/ai-elements/voice-selector.js +0 -224
- package/dist/components/ai-elements/web-preview.d.ts +0 -35
- package/dist/components/ai-elements/web-preview.d.ts.map +0 -1
- package/dist/components/ai-elements/web-preview.js +0 -66
- package/dist/components/artifact-panel.d.ts +0 -130
- package/dist/components/artifact-panel.d.ts.map +0 -1
- package/dist/components/artifact-panel.js +0 -180
- package/dist/components/attachment.d.ts +0 -24
- package/dist/components/attachment.d.ts.map +0 -1
- package/dist/components/attachment.js +0 -68
- package/dist/components/bubble.d.ts +0 -17
- package/dist/components/bubble.d.ts.map +0 -1
- package/dist/components/bubble.js +0 -59
- package/dist/components/demo-surface.d.ts +0 -34
- package/dist/components/demo-surface.d.ts.map +0 -1
- package/dist/components/demo-surface.js +0 -21
- package/dist/components/files-preview.d.ts +0 -36
- package/dist/components/files-preview.d.ts.map +0 -1
- package/dist/components/files-preview.js +0 -85
- package/dist/components/marker.d.ts +0 -11
- package/dist/components/marker.d.ts.map +0 -1
- package/dist/components/marker.js +0 -34
- package/dist/components/message-scroller.d.ts +0 -17
- package/dist/components/message-scroller.d.ts.map +0 -1
- package/dist/components/message-scroller.js +0 -29
- package/dist/components/message.d.ts +0 -11
- package/dist/components/message.d.ts.map +0 -1
- package/dist/components/message.js +0 -21
- package/dist/components/state-notice.d.ts +0 -13
- package/dist/components/state-notice.d.ts.map +0 -1
- package/dist/components/state-notice.js +0 -8
- package/dist/components/watch-it-work-band.d.ts +0 -12
- package/dist/components/watch-it-work-band.d.ts.map +0 -1
- package/dist/components/watch-it-work-band.js +0 -8
- package/dist/hooks/use-controllable-state.d.ts +0 -9
- package/dist/hooks/use-controllable-state.d.ts.map +0 -1
- package/dist/hooks/use-controllable-state.js +0 -23
- package/dist/lib/agent-attachment.d.ts +0 -11
- package/dist/lib/agent-attachment.d.ts.map +0 -1
- package/dist/lib/agent-attachment.js +0 -19
- package/dist/lib/agent-contracts.d.ts +0 -132
- package/dist/lib/agent-contracts.d.ts.map +0 -1
- package/dist/lib/agent-contracts.js +0 -6
- package/dist/lib/agent-state.d.ts +0 -22
- package/dist/lib/agent-state.d.ts.map +0 -1
- package/dist/lib/agent-state.js +0 -96
- package/dist/lib/agent-work-state.d.ts +0 -36
- package/dist/lib/agent-work-state.d.ts.map +0 -1
- package/dist/lib/agent-work-state.js +0 -36
- package/dist/lib/message-scroller-primitive/components.d.ts +0 -17
- package/dist/lib/message-scroller-primitive/components.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/components.js +0 -204
- package/dist/lib/message-scroller-primitive/geometry.d.ts +0 -51
- package/dist/lib/message-scroller-primitive/geometry.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/geometry.js +0 -199
- package/dist/lib/message-scroller-primitive/index.d.ts +0 -12
- package/dist/lib/message-scroller-primitive/index.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/index.js +0 -12
- package/dist/lib/message-scroller-primitive/stores.d.ts +0 -6
- package/dist/lib/message-scroller-primitive/stores.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/stores.js +0 -53
- package/dist/lib/message-scroller-primitive/types.d.ts +0 -88
- package/dist/lib/message-scroller-primitive/types.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/types.js +0 -40
- package/dist/lib/message-scroller-primitive/use-message-scroller-commands.d.ts +0 -19
- package/dist/lib/message-scroller-primitive/use-message-scroller-commands.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/use-message-scroller-commands.js +0 -188
- package/dist/lib/message-scroller-primitive/use-message-scroller-controller.d.ts +0 -7
- package/dist/lib/message-scroller-primitive/use-message-scroller-controller.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/use-message-scroller-controller.js +0 -506
- package/dist/lib/message-scroller-primitive/use-message-scroller-refs.d.ts +0 -49
- package/dist/lib/message-scroller-primitive/use-message-scroller-refs.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/use-message-scroller-refs.js +0 -90
- package/dist/lib/message-scroller-primitive/use-render.d.ts +0 -22
- package/dist/lib/message-scroller-primitive/use-render.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/use-render.js +0 -113
- package/dist/lib/message-scroller-primitive/utils.d.ts +0 -4
- package/dist/lib/message-scroller-primitive/utils.d.ts.map +0 -1
- package/dist/lib/message-scroller-primitive/utils.js +0 -9
- package/docs/agent-work-surfaces.md +0 -132
- package/docs/migration/ai-elements-vendoring.md +0 -29
- package/docs/terminal-tui-semantics.md +0 -172
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { noDesignSystemSrcImport } from "./rules/no-design-system-src-import.js";
|
|
4
|
+
import { noDeprecatedZodApi } from "./rules/no-deprecated-zod-api.js";
|
|
5
|
+
import { noRawLogoImport } from "./rules/no-raw-logo-import.js";
|
|
6
|
+
import { noRawColorUtility } from "./rules/no-raw-color-utility.js";
|
|
7
|
+
import { noShadcnAppearanceOverride } from "./rules/no-shadcn-appearance-override.js";
|
|
8
|
+
import { noUnpinnedVercelFunctionsImport } from "./rules/no-unpinned-vercel-functions-import.js";
|
|
9
|
+
import { noUiSrcImport } from "./rules/no-ui-src-import.js";
|
|
10
|
+
import { noNativeVisibleControl } from "./rules/no-native-visible-control.js";
|
|
11
|
+
import { noRawFocusRingWidth } from "./rules/no-raw-focus-ring-width.js";
|
|
12
|
+
import { noInvalidTokenUtility } from "./rules/no-invalid-token-utility.js";
|
|
13
|
+
import { noTokenCategoryBypass } from "./rules/no-token-category-bypass.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The plugin carries no version of its own. Its bytes are published inside
|
|
17
|
+
* `@nadicodeai/ui`, whose version the release gate already forces to move in
|
|
18
|
+
* lockstep, so that manifest is the one home of the number and this reads it.
|
|
19
|
+
* The published copy of this file sits at `<ui>/dist/eslint/index.js` and the
|
|
20
|
+
* source sits at `tools/eslint-plugin-nadicodeai/index.js`, which puts the
|
|
21
|
+
* manifest at one of two places relative to here; the `name` field says which
|
|
22
|
+
* candidate is the right one, so the repository root's own manifest — also two
|
|
23
|
+
* levels up — cannot answer by accident.
|
|
24
|
+
*/
|
|
25
|
+
const PUBLISHING_PACKAGE = "@nadicodeai/ui";
|
|
26
|
+
const MANIFEST_CANDIDATES = ["../../package.json", `../../${PUBLISHING_PACKAGE}/package.json`];
|
|
27
|
+
|
|
28
|
+
const version = readPublishedVersion();
|
|
29
|
+
|
|
30
|
+
function readPublishedVersion() {
|
|
31
|
+
for (const candidate of MANIFEST_CANDIDATES) {
|
|
32
|
+
let manifest;
|
|
33
|
+
try {
|
|
34
|
+
manifest = JSON.parse(readFileSync(new URL(candidate, import.meta.url), "utf8"));
|
|
35
|
+
} catch {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (manifest?.name === PUBLISHING_PACKAGE && typeof manifest.version === "string") {
|
|
39
|
+
return manifest.version;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw new Error(
|
|
43
|
+
`@nadicodeai/eslint-plugin: no ${PUBLISHING_PACKAGE} manifest at ${MANIFEST_CANDIDATES.join(" or ")}, so the plugin cannot report the version it ships under.`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const strictDesignSystemRules = {
|
|
48
|
+
"nadicodeai/no-design-system-src-import": "error",
|
|
49
|
+
"nadicodeai/no-invalid-token-utility": "error",
|
|
50
|
+
"nadicodeai/no-native-visible-control": "error",
|
|
51
|
+
"nadicodeai/no-raw-color-utility": "error",
|
|
52
|
+
"nadicodeai/no-raw-focus-ring-width": "error",
|
|
53
|
+
"nadicodeai/no-raw-logo-import": "error",
|
|
54
|
+
"nadicodeai/no-shadcn-appearance-override": "error",
|
|
55
|
+
"nadicodeai/no-token-category-bypass": "error",
|
|
56
|
+
"nadicodeai/no-ui-src-import": "error",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** @type {import('eslint').ESLint.Plugin} */
|
|
60
|
+
const plugin = {
|
|
61
|
+
meta: {
|
|
62
|
+
name: "@nadicodeai/eslint-plugin",
|
|
63
|
+
version,
|
|
64
|
+
},
|
|
65
|
+
configs: {},
|
|
66
|
+
rules: {
|
|
67
|
+
"no-deprecated-zod-api": noDeprecatedZodApi,
|
|
68
|
+
"no-raw-logo-import": noRawLogoImport,
|
|
69
|
+
"no-raw-color-utility": noRawColorUtility,
|
|
70
|
+
"no-shadcn-appearance-override": noShadcnAppearanceOverride,
|
|
71
|
+
"no-native-visible-control": noNativeVisibleControl,
|
|
72
|
+
"no-raw-focus-ring-width": noRawFocusRingWidth,
|
|
73
|
+
"no-unpinned-vercel-functions-import": noUnpinnedVercelFunctionsImport,
|
|
74
|
+
"no-ui-src-import": noUiSrcImport,
|
|
75
|
+
"no-design-system-src-import": noDesignSystemSrcImport,
|
|
76
|
+
"no-invalid-token-utility": noInvalidTokenUtility,
|
|
77
|
+
"no-token-category-bypass": noTokenCategoryBypass,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const strictDesignSystemConfig = [
|
|
82
|
+
{
|
|
83
|
+
name: "nadicodeai/design-system-strict",
|
|
84
|
+
plugins: { nadicodeai: plugin },
|
|
85
|
+
rules: strictDesignSystemRules,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
plugin.configs["flat/strict"] = strictDesignSystemConfig;
|
|
90
|
+
|
|
91
|
+
export default plugin;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const PACKAGE_PRIMITIVE_PATH = "@nadicodeai/ui/src/";
|
|
2
|
+
|
|
3
|
+
export function isPackageOwnedPrimitive(filename) {
|
|
4
|
+
return filename.replaceAll("\\", "/").includes(PACKAGE_PRIMITIVE_PATH);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function variableInitializer(node, context) {
|
|
8
|
+
let scope = context.sourceCode.getScope(node);
|
|
9
|
+
while (scope) {
|
|
10
|
+
const variable = scope.set.get(node.name);
|
|
11
|
+
const definition = variable?.defs.find(
|
|
12
|
+
(candidate) => candidate.type === "Variable" && candidate.node?.type === "VariableDeclarator",
|
|
13
|
+
);
|
|
14
|
+
if (definition?.node.init) return definition.node.init;
|
|
15
|
+
scope = scope.upper;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function collectStaticClassFragments(
|
|
21
|
+
node,
|
|
22
|
+
fragments,
|
|
23
|
+
context,
|
|
24
|
+
seen = new Set(),
|
|
25
|
+
includeObjectKeys = true,
|
|
26
|
+
) {
|
|
27
|
+
if (!node) return;
|
|
28
|
+
|
|
29
|
+
if (seen.has(node)) return;
|
|
30
|
+
seen.add(node);
|
|
31
|
+
|
|
32
|
+
if (node.type === "Literal" && typeof node.value === "string") {
|
|
33
|
+
fragments.push(node.value);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (node.type === "JSXExpressionContainer" || node.type === "ChainExpression") {
|
|
37
|
+
collectStaticClassFragments(node.expression, fragments, context, seen, includeObjectKeys);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (node.type === "TemplateLiteral") {
|
|
41
|
+
for (const quasi of node.quasis) fragments.push(quasi.value.cooked ?? quasi.value.raw);
|
|
42
|
+
for (const expression of node.expressions) {
|
|
43
|
+
collectStaticClassFragments(expression, fragments, context, seen, includeObjectKeys);
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (node.type === "TaggedTemplateExpression") {
|
|
48
|
+
collectStaticClassFragments(node.quasi, fragments, context, seen, includeObjectKeys);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (node.type === "ConditionalExpression") {
|
|
52
|
+
collectStaticClassFragments(node.consequent, fragments, context, seen, includeObjectKeys);
|
|
53
|
+
collectStaticClassFragments(node.alternate, fragments, context, seen, includeObjectKeys);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (node.type === "LogicalExpression" || node.type === "BinaryExpression") {
|
|
57
|
+
collectStaticClassFragments(node.left, fragments, context, seen, includeObjectKeys);
|
|
58
|
+
collectStaticClassFragments(node.right, fragments, context, seen, includeObjectKeys);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (node.type === "CallExpression" || node.type === "NewExpression") {
|
|
62
|
+
for (const argument of node.arguments) {
|
|
63
|
+
collectStaticClassFragments(
|
|
64
|
+
argument.type === "SpreadElement" ? argument.argument : argument,
|
|
65
|
+
fragments,
|
|
66
|
+
context,
|
|
67
|
+
seen,
|
|
68
|
+
includeObjectKeys,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (node.type === "ArrayExpression" || node.type === "SequenceExpression") {
|
|
74
|
+
for (const element of node.elements ?? node.expressions) {
|
|
75
|
+
collectStaticClassFragments(
|
|
76
|
+
element?.type === "SpreadElement" ? element.argument : element,
|
|
77
|
+
fragments,
|
|
78
|
+
context,
|
|
79
|
+
seen,
|
|
80
|
+
includeObjectKeys,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (node.type === "ObjectExpression") {
|
|
86
|
+
for (const property of node.properties) {
|
|
87
|
+
if (property.type === "SpreadElement") {
|
|
88
|
+
collectStaticClassFragments(property.argument, fragments, context, seen, includeObjectKeys);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (includeObjectKeys && !property.computed) {
|
|
92
|
+
collectStaticClassFragments(property.key, fragments, context, seen, includeObjectKeys);
|
|
93
|
+
}
|
|
94
|
+
collectStaticClassFragments(property.value, fragments, context, seen, includeObjectKeys);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (node.type === "Identifier") {
|
|
99
|
+
collectStaticClassFragments(
|
|
100
|
+
variableInitializer(node, context),
|
|
101
|
+
fragments,
|
|
102
|
+
context,
|
|
103
|
+
seen,
|
|
104
|
+
includeObjectKeys,
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (node.type === "MemberExpression") {
|
|
109
|
+
// A dynamic lookup can select any value in a local class table. Resolve the
|
|
110
|
+
// entire object so no palette entry can hide behind `classes[current]`.
|
|
111
|
+
collectStaticClassFragments(node.object, fragments, context, seen, includeObjectKeys);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (
|
|
115
|
+
node.type === "TSAsExpression" ||
|
|
116
|
+
node.type === "TSSatisfiesExpression" ||
|
|
117
|
+
node.type === "TSNonNullExpression" ||
|
|
118
|
+
node.type === "TypeCastExpression"
|
|
119
|
+
) {
|
|
120
|
+
collectStaticClassFragments(node.expression, fragments, context, seen, includeObjectKeys);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function staticPropertyName(property) {
|
|
125
|
+
if (property.key.type === "Identifier" && !property.computed) return property.key.name;
|
|
126
|
+
if (property.key.type === "Literal" && typeof property.key.value === "string") {
|
|
127
|
+
return property.key.value;
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function collectStaticSpreadClassFragments(node, fragments, context, seen = new Set()) {
|
|
133
|
+
if (!node || seen.has(node)) return;
|
|
134
|
+
seen.add(node);
|
|
135
|
+
|
|
136
|
+
if (node.type === "Identifier") {
|
|
137
|
+
collectStaticSpreadClassFragments(variableInitializer(node, context), fragments, context, seen);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (node.type === "ObjectExpression") {
|
|
141
|
+
for (const property of node.properties) {
|
|
142
|
+
if (property.type === "SpreadElement") {
|
|
143
|
+
collectStaticSpreadClassFragments(property.argument, fragments, context, seen);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (staticPropertyName(property) === "className") {
|
|
147
|
+
collectStaticClassFragments(property.value, fragments, context, seen);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (node.type === "ConditionalExpression") {
|
|
153
|
+
collectStaticSpreadClassFragments(node.consequent, fragments, context, seen);
|
|
154
|
+
collectStaticSpreadClassFragments(node.alternate, fragments, context, seen);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (node.type === "LogicalExpression") {
|
|
158
|
+
collectStaticSpreadClassFragments(node.left, fragments, context, seen);
|
|
159
|
+
collectStaticSpreadClassFragments(node.right, fragments, context, seen);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (
|
|
163
|
+
node.type === "TSAsExpression" ||
|
|
164
|
+
node.type === "TSSatisfiesExpression" ||
|
|
165
|
+
node.type === "TSNonNullExpression" ||
|
|
166
|
+
node.type === "TypeCastExpression"
|
|
167
|
+
) {
|
|
168
|
+
collectStaticSpreadClassFragments(node.expression, fragments, context, seen);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function classUtilities(attribute, context) {
|
|
173
|
+
if (attribute?.type !== "JSXAttribute" || attribute.name.name !== "className") return [];
|
|
174
|
+
return staticClassUtilities(attribute.value, context);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function staticStringFragments(node, context) {
|
|
178
|
+
const fragments = [];
|
|
179
|
+
collectStaticClassFragments(node, fragments, context);
|
|
180
|
+
return fragments;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function staticClassUtilities(node, context) {
|
|
184
|
+
return staticStringFragments(node, context).flatMap((fragment) =>
|
|
185
|
+
fragment.split(/\s+/).filter(Boolean),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function staticValueClassUtilities(node, context) {
|
|
190
|
+
const fragments = [];
|
|
191
|
+
collectStaticClassFragments(node, fragments, context, new Set(), false);
|
|
192
|
+
return fragments.flatMap((fragment) => fragment.split(/\s+/).filter(Boolean));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function tokenUtilityVisitors(context, onUtility) {
|
|
196
|
+
const cvaNames = new Set();
|
|
197
|
+
|
|
198
|
+
function visitUtilities(node, utilities) {
|
|
199
|
+
for (const utility of utilities) onUtility(node, utility);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
ImportDeclaration(node) {
|
|
204
|
+
if (node.source.value !== "class-variance-authority") return;
|
|
205
|
+
for (const specifier of node.specifiers) {
|
|
206
|
+
if (specifier.type === "ImportSpecifier" && specifier.imported.name === "cva") {
|
|
207
|
+
cvaNames.add(specifier.local.name);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
JSXAttribute(node) {
|
|
212
|
+
visitUtilities(node, classUtilities(node, context));
|
|
213
|
+
},
|
|
214
|
+
CallExpression(node) {
|
|
215
|
+
if (node.callee.type !== "Identifier" || !cvaNames.has(node.callee.name)) return;
|
|
216
|
+
visitUtilities(node, staticValueClassUtilities(node, context));
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function openingElementClassUtilities(node, context) {
|
|
222
|
+
const fragments = [];
|
|
223
|
+
for (const attribute of node.attributes) {
|
|
224
|
+
if (attribute.type === "JSXAttribute" && attribute.name.name === "className") {
|
|
225
|
+
fragments.push(...staticStringFragments(attribute.value, context));
|
|
226
|
+
} else if (attribute.type === "JSXSpreadAttribute") {
|
|
227
|
+
collectStaticSpreadClassFragments(attribute.argument, fragments, context);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return fragments.flatMap((fragment) => fragment.split(/\s+/).filter(Boolean));
|
|
231
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
const LEGACY_FACTORIES = new Map([
|
|
2
|
+
["nativeEnum", "z.enum()"],
|
|
3
|
+
["promise", "await the value before parsing it"],
|
|
4
|
+
]);
|
|
5
|
+
|
|
6
|
+
const LEGACY_METHODS = {
|
|
7
|
+
number: new Map([
|
|
8
|
+
["finite", "remove it; z.number() already rejects infinite values"],
|
|
9
|
+
["safe", ".int()"],
|
|
10
|
+
["step", ".multipleOf()"],
|
|
11
|
+
]),
|
|
12
|
+
object: new Map([
|
|
13
|
+
["merge", ".extend(other.shape)"],
|
|
14
|
+
["passthrough", "z.looseObject()"],
|
|
15
|
+
["strict", "z.strictObject()"],
|
|
16
|
+
["strip", "z.object(), which strips unknown keys by default"],
|
|
17
|
+
]),
|
|
18
|
+
string: new Map(
|
|
19
|
+
[
|
|
20
|
+
"base64",
|
|
21
|
+
"base64url",
|
|
22
|
+
"cuid",
|
|
23
|
+
"cuid2",
|
|
24
|
+
"e164",
|
|
25
|
+
"email",
|
|
26
|
+
"emoji",
|
|
27
|
+
"guid",
|
|
28
|
+
"ipv4",
|
|
29
|
+
"ipv6",
|
|
30
|
+
"jwt",
|
|
31
|
+
"ksuid",
|
|
32
|
+
"nanoid",
|
|
33
|
+
"ulid",
|
|
34
|
+
"url",
|
|
35
|
+
"uuid",
|
|
36
|
+
"uuidv4",
|
|
37
|
+
"uuidv6",
|
|
38
|
+
"uuidv7",
|
|
39
|
+
"xid",
|
|
40
|
+
].map((name) => [name, `z.${name.startsWith("uuidv") ? "uuid" : name}()`]),
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
for (const name of ["date", "datetime", "duration", "time"]) {
|
|
45
|
+
LEGACY_METHODS.string.set(name, `z.iso.${name}()`);
|
|
46
|
+
}
|
|
47
|
+
for (const name of ["cidrv4", "cidrv6"]) {
|
|
48
|
+
LEGACY_METHODS.string.set(name, `z.${name}()`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function memberName(node) {
|
|
52
|
+
if (!node.computed && node.property.type === "Identifier") {
|
|
53
|
+
return node.property.name;
|
|
54
|
+
}
|
|
55
|
+
if (node.computed && node.property.type === "Literal") {
|
|
56
|
+
return typeof node.property.value === "string" ? node.property.value : undefined;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const noDeprecatedZodApi = {
|
|
62
|
+
meta: {
|
|
63
|
+
type: "problem",
|
|
64
|
+
docs: {
|
|
65
|
+
description: "Disallow deprecated and legacy Zod APIs when a Zod 4 replacement exists",
|
|
66
|
+
},
|
|
67
|
+
schema: [],
|
|
68
|
+
messages: {
|
|
69
|
+
legacyFactory: "{{api}} is deprecated in Zod 4; use {{replacement}}.",
|
|
70
|
+
legacyMethod: "{{api}} is deprecated or legacy in Zod 4; use {{replacement}}.",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
create(context) {
|
|
75
|
+
const sourceCode = context.sourceCode;
|
|
76
|
+
const zodBindings = new Set();
|
|
77
|
+
|
|
78
|
+
function isZodBinding(identifier) {
|
|
79
|
+
for (let scope = sourceCode.getScope(identifier); scope; scope = scope.upper) {
|
|
80
|
+
const variable = scope.set.get(identifier.name);
|
|
81
|
+
if (variable) {
|
|
82
|
+
return zodBindings.has(variable);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function rootFactory(call) {
|
|
89
|
+
if (call.type !== "CallExpression" || call.callee.type !== "MemberExpression") {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const factory = memberName(call.callee);
|
|
93
|
+
if (!factory) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
if (call.callee.object.type === "Identifier" && isZodBinding(call.callee.object)) {
|
|
97
|
+
return factory;
|
|
98
|
+
}
|
|
99
|
+
return rootFactory(call.callee.object);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
ImportDeclaration(node) {
|
|
104
|
+
if (node.source.value !== "zod") {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const specifier of node.specifiers) {
|
|
108
|
+
const importsNamespace = specifier.type === "ImportNamespaceSpecifier";
|
|
109
|
+
const importsZ =
|
|
110
|
+
specifier.type === "ImportSpecifier" &&
|
|
111
|
+
((specifier.imported.type === "Identifier" && specifier.imported.name === "z") ||
|
|
112
|
+
(specifier.imported.type === "Literal" && specifier.imported.value === "z"));
|
|
113
|
+
if (!importsNamespace && !importsZ) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
for (const variable of sourceCode.getDeclaredVariables(specifier)) {
|
|
117
|
+
zodBindings.add(variable);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
CallExpression(node) {
|
|
123
|
+
if (node.callee.type !== "MemberExpression") {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const method = memberName(node.callee);
|
|
127
|
+
if (!method) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (node.callee.object.type === "Identifier" && isZodBinding(node.callee.object)) {
|
|
132
|
+
const replacement = LEGACY_FACTORIES.get(method);
|
|
133
|
+
if (replacement) {
|
|
134
|
+
context.report({
|
|
135
|
+
node: node.callee.property,
|
|
136
|
+
messageId: "legacyFactory",
|
|
137
|
+
data: { api: `z.${method}()`, replacement },
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const factory = rootFactory(node.callee.object);
|
|
144
|
+
const family = factory === "strictObject" || factory === "looseObject" ? "object" : factory;
|
|
145
|
+
const replacement = LEGACY_METHODS[family]?.get(method);
|
|
146
|
+
if (replacement) {
|
|
147
|
+
context.report({
|
|
148
|
+
node: node.callee.property,
|
|
149
|
+
messageId: "legacyMethod",
|
|
150
|
+
data: { api: `z.${factory}().${method}()`, replacement },
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { bannedSourceRootMatcher, createSrcImportRule } from "./src-import-boundary.js";
|
|
2
|
+
|
|
3
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
4
|
+
export const noDesignSystemSrcImport = createSrcImportRule({
|
|
5
|
+
description:
|
|
6
|
+
"Disallow deep imports from @nadicodeai/design-system source paths in apps and UI consumers",
|
|
7
|
+
bannedMessage: "Import @nadicodeai/design-system through published exports only, not {{value}}.",
|
|
8
|
+
// The package dir is `design-system/`, so relative paths carry the shorter
|
|
9
|
+
// `design-system/src` marker rather than the scoped specifier.
|
|
10
|
+
isBanned: bannedSourceRootMatcher("design-system/src"),
|
|
11
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
import { tokenUtilityVisitors } from "./jsx-class-utility.js";
|
|
4
|
+
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const generatedTheme = require("@nadicodeai/design-system/tailwind").theme.extend;
|
|
7
|
+
const typographyUtilities = new Set(
|
|
8
|
+
Object.keys(generatedTheme.fontSize).map((name) => `nc-type-${name}`),
|
|
9
|
+
);
|
|
10
|
+
const colorTokens = new Set(Object.keys(generatedTheme.colors));
|
|
11
|
+
const COLOR_UTILITY =
|
|
12
|
+
/^(?:bg|text|border|outline|ring|fill|stroke|decoration|accent|caret|divide|from|via|to|shadow)-nc-([^/]+)(?:\/.*)?$/;
|
|
13
|
+
|
|
14
|
+
function withoutVariants(utility) {
|
|
15
|
+
return utility.slice(utility.lastIndexOf(":") + 1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isInvalidGeneratedUtility(utility) {
|
|
19
|
+
const unqualified = withoutVariants(utility);
|
|
20
|
+
if (unqualified.startsWith("nc-type-")) {
|
|
21
|
+
return !typographyUtilities.has(unqualified);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const color = COLOR_UTILITY.exec(unqualified);
|
|
25
|
+
return color ? !colorTokens.has(color[1]) : false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const noInvalidTokenUtility = {
|
|
29
|
+
meta: {
|
|
30
|
+
type: "problem",
|
|
31
|
+
docs: {
|
|
32
|
+
description: "Disallow token utilities that are absent from generated design-system output",
|
|
33
|
+
},
|
|
34
|
+
schema: [],
|
|
35
|
+
messages: {
|
|
36
|
+
invalidTokenUtility:
|
|
37
|
+
"{{utility}} is not generated by the installed NadicodeAI design system. Use a utility from @nadicodeai/design-system/tailwind.",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
create(context) {
|
|
41
|
+
return tokenUtilityVisitors(context, (node, utility) => {
|
|
42
|
+
if (!isInvalidGeneratedUtility(utility)) return;
|
|
43
|
+
context.report({ node, messageId: "invalidTokenUtility", data: { utility } });
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { isPackageOwnedPrimitive } from "./jsx-class-utility.js";
|
|
2
|
+
|
|
3
|
+
const NATIVE_CONTROLS = new Set(["button", "input", "select", "textarea"]);
|
|
4
|
+
|
|
5
|
+
function staticValue(node) {
|
|
6
|
+
if (node?.type === "Literal") {
|
|
7
|
+
return node.value;
|
|
8
|
+
}
|
|
9
|
+
if (node?.type === "JSXExpressionContainer") {
|
|
10
|
+
return staticValue(node.expression);
|
|
11
|
+
}
|
|
12
|
+
if (node?.type === "TemplateLiteral" && node.expressions.length === 0) {
|
|
13
|
+
return node.quasis[0]?.value.cooked;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function hiddenInput(node) {
|
|
19
|
+
const typeAttribute = node.attributes.find(
|
|
20
|
+
(attribute) => attribute.type === "JSXAttribute" && attribute.name.name === "type",
|
|
21
|
+
);
|
|
22
|
+
return staticValue(typeAttribute?.value) === "hidden";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isPrimitiveRenderElement(node, packagePrimitiveNames) {
|
|
26
|
+
const expression = node.parent;
|
|
27
|
+
const attribute = expression?.parent;
|
|
28
|
+
const openingElement = attribute?.parent;
|
|
29
|
+
return (
|
|
30
|
+
expression?.type === "JSXExpressionContainer" &&
|
|
31
|
+
attribute?.type === "JSXAttribute" &&
|
|
32
|
+
attribute.name.name === "render" &&
|
|
33
|
+
openingElement?.type === "JSXOpeningElement" &&
|
|
34
|
+
openingElement.name.type === "JSXIdentifier" &&
|
|
35
|
+
packagePrimitiveNames.has(openingElement.name.name)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const noNativeVisibleControl = {
|
|
40
|
+
meta: {
|
|
41
|
+
type: "problem",
|
|
42
|
+
docs: {
|
|
43
|
+
description: "Disallow visible native controls in app source; use @nadicodeai/ui primitives",
|
|
44
|
+
},
|
|
45
|
+
schema: [],
|
|
46
|
+
messages: {
|
|
47
|
+
nativeVisibleControl:
|
|
48
|
+
"Use the @nadicodeai/ui primitive instead of native visible <{{element}}>.",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
create(context) {
|
|
52
|
+
if (isPackageOwnedPrimitive(context.filename)) {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
const packagePrimitiveNames = new Set();
|
|
56
|
+
return {
|
|
57
|
+
ImportDeclaration(node) {
|
|
58
|
+
if (
|
|
59
|
+
typeof node.source.value !== "string" ||
|
|
60
|
+
!node.source.value.startsWith("@nadicodeai/ui")
|
|
61
|
+
) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
for (const specifier of node.specifiers) {
|
|
65
|
+
packagePrimitiveNames.add(specifier.local.name);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
JSXOpeningElement(node) {
|
|
69
|
+
if (node.name.type !== "JSXIdentifier" || !NATIVE_CONTROLS.has(node.name.name)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (node.name.name === "input" && hiddenInput(node)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (isPrimitiveRenderElement(node.parent, packagePrimitiveNames)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
context.report({
|
|
79
|
+
node: node.name,
|
|
80
|
+
messageId: "nativeVisibleControl",
|
|
81
|
+
data: { element: node.name.name },
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|