@karni/ui 0.0.0-stage → 0.4.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/LICENSE +201 -0
- package/README.md +928 -2
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +8699 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +102 -0
- package/dist/mockServiceWorker.js +361 -0
- package/dist/script-BVJ9BbPm.cjs +2 -0
- package/dist/script-BVJ9BbPm.cjs.map +1 -0
- package/dist/script-D1fn4Dm4.js +6 -0
- package/dist/script-D1fn4Dm4.js.map +1 -0
- package/dist/src/auth/AuthGuard.d.ts +77 -0
- package/dist/src/auth/LoginForm.d.ts +63 -0
- package/dist/src/auth/engine.d.ts +101 -0
- package/dist/src/auth/provider.d.ts +43 -0
- package/dist/src/auth/storage.d.ts +26 -0
- package/dist/src/auth/types.d.ts +32 -0
- package/dist/src/auth/usePermission.d.ts +7 -0
- package/dist/src/components/brand/karni-mark.d.ts +21 -0
- package/dist/src/components/brand/mark-paths.d.ts +8 -0
- package/dist/src/components/chart/data-chart.d.ts +129 -0
- package/dist/src/components/empty-state/empty-state.d.ts +40 -0
- package/dist/src/components/file-upload/FileUpload.d.ts +108 -0
- package/dist/src/components/layout/layout.d.ts +150 -0
- package/dist/src/components/layout/route-focus.d.ts +43 -0
- package/dist/src/components/modal/modal.d.ts +86 -0
- package/dist/src/components/page/page.d.ts +60 -0
- package/dist/src/components/primitives/button.d.ts +45 -0
- package/dist/src/components/primitives/checkbox.d.ts +8 -0
- package/dist/src/components/primitives/dropdown-menu.d.ts +24 -0
- package/dist/src/components/primitives/icons.d.ts +19 -0
- package/dist/src/components/primitives/input.d.ts +7 -0
- package/dist/src/components/primitives/label.d.ts +8 -0
- package/dist/src/components/primitives/select.d.ts +94 -0
- package/dist/src/components/primitives/skeleton.d.ts +7 -0
- package/dist/src/components/primitives/switch.d.ts +8 -0
- package/dist/src/components/primitives/tabs.d.ts +10 -0
- package/dist/src/components/primitives/textarea.d.ts +7 -0
- package/dist/src/components/resource/ResourceDetail.d.ts +7 -0
- package/dist/src/components/resource/ResourceForm.d.ts +8 -0
- package/dist/src/components/resource/ResourceSelect.d.ts +9 -0
- package/dist/src/components/resource/ResourceTable.d.ts +17 -0
- package/dist/src/components/resource/audit-fields.d.ts +17 -0
- package/dist/src/components/resource/detail/ResourceDetail.d.ts +128 -0
- package/dist/src/components/resource/displayLabel.d.ts +12 -0
- package/dist/src/components/resource/empty-value.d.ts +17 -0
- package/dist/src/components/resource/form/ResourceForm.d.ts +109 -0
- package/dist/src/components/resource/form/field-inputs.d.ts +34 -0
- package/dist/src/components/resource/form/fixtures.d.ts +24 -0
- package/dist/src/components/resource/form/form-states.d.ts +87 -0
- package/dist/src/components/resource/form/prepare.d.ts +54 -0
- package/dist/src/components/resource/form/server-errors.d.ts +14 -0
- package/dist/src/components/resource/history/ChangeHistory.d.ts +60 -0
- package/dist/src/components/resource/import/ResourceImport.d.ts +84 -0
- package/dist/src/components/resource/nouns.d.ts +12 -0
- package/dist/src/components/resource/query-state.d.ts +20 -0
- package/dist/src/components/resource/resource-names.d.ts +23 -0
- package/dist/src/components/resource/select/ResourceSelect.d.ts +67 -0
- package/dist/src/components/resource/select/select-states.d.ts +68 -0
- package/dist/src/components/resource/select/use-debounced-value.d.ts +2 -0
- package/dist/src/components/resource/select/use-option-pages.d.ts +37 -0
- package/dist/src/components/resource/select/venue-backend.d.ts +144 -0
- package/dist/src/components/resource/table/BulkActionsBar.d.ts +77 -0
- package/dist/src/components/resource/table/CardList.d.ts +31 -0
- package/dist/src/components/resource/table/ConfirmDialog.d.ts +28 -0
- package/dist/src/components/resource/table/ExportButtons.d.ts +17 -0
- package/dist/src/components/resource/table/FilterBar.d.ts +29 -0
- package/dist/src/components/resource/table/PaginationControls.d.ts +31 -0
- package/dist/src/components/resource/table/ResourceTable.d.ts +137 -0
- package/dist/src/components/resource/table/RowCard.d.ts +31 -0
- package/dist/src/components/resource/table/TableView.d.ts +29 -0
- package/dist/src/components/resource/table/ViewOptions.d.ts +19 -0
- package/dist/src/components/resource/table/access.d.ts +12 -0
- package/dist/src/components/resource/table/demo-fixtures.d.ts +25 -0
- package/dist/src/components/resource/table/derive-columns.d.ts +33 -0
- package/dist/src/components/resource/table/grid-navigation.d.ts +23 -0
- package/dist/src/components/resource/table/layout.d.ts +37 -0
- package/dist/src/components/resource/table/preferences.d.ts +12 -0
- package/dist/src/components/resource/table/slots.d.ts +41 -0
- package/dist/src/components/resource/table/states.d.ts +91 -0
- package/dist/src/components/resource/table/story-fixtures.d.ts +25 -0
- package/dist/src/components/resource/table/structural-columns.d.ts +25 -0
- package/dist/src/components/resource/table/table-icons.d.ts +16 -0
- package/dist/src/components/resource/table/table-utils.d.ts +48 -0
- package/dist/src/components/resource/table/url-state.d.ts +67 -0
- package/dist/src/components/resource/table/use-container-width.d.ts +10 -0
- package/dist/src/components/resource/table/use-debounced-value.d.ts +1 -0
- package/dist/src/components/resource/testapp-fixtures.d.ts +78 -0
- package/dist/src/components/stat/stat.d.ts +54 -0
- package/dist/src/components/toast/toast-store.d.ts +36 -0
- package/dist/src/components/toast/toaster.d.ts +66 -0
- package/dist/src/components/toast/use-toast.d.ts +24 -0
- package/dist/src/hooks/keys.d.ts +24 -0
- package/dist/src/hooks/multipart.d.ts +44 -0
- package/dist/src/hooks/params.d.ts +25 -0
- package/dist/src/hooks/types.d.ts +41 -0
- package/dist/src/hooks/useChangeHistory.d.ts +30 -0
- package/dist/src/hooks/useResourceImport.d.ts +107 -0
- package/dist/src/hooks/useResourceItem.d.ts +4 -0
- package/dist/src/hooks/useResourceList.d.ts +4 -0
- package/dist/src/hooks/useResourceMutations.d.ts +13 -0
- package/dist/src/hooks/useUnsavedChangesGuard.d.ts +31 -0
- package/dist/src/index.d.ts +113 -0
- package/dist/src/lib/cn.d.ts +8 -0
- package/dist/src/lib/roles.d.ts +45 -0
- package/dist/src/lib/variants.d.ts +33 -0
- package/dist/src/primitives/registry.d.ts +81 -0
- package/dist/src/schema/client.d.ts +90 -0
- package/dist/src/schema/parse.d.ts +101 -0
- package/dist/src/schema/provider.d.ts +20 -0
- package/dist/src/schema/types.d.ts +93 -0
- package/dist/src/schema/useSchema.d.ts +21 -0
- package/dist/src/schema/zod.d.ts +4 -0
- package/dist/src/states/ErrorState.d.ts +43 -0
- package/dist/src/states/PendingButton.d.ts +16 -0
- package/dist/src/states/describe-error.d.ts +25 -0
- package/dist/src/states/hooks.d.ts +10 -0
- package/dist/src/theme/script.d.ts +12 -0
- package/dist/src/theme/theme.d.ts +45 -0
- package/dist/styles.css +248 -0
- package/dist/tailwind-preset.cjs +2 -0
- package/dist/tailwind-preset.cjs.map +1 -0
- package/dist/tailwind-preset.d.ts +140 -0
- package/dist/tailwind-preset.js +183 -0
- package/dist/tailwind-preset.js.map +1 -0
- package/dist/tailwind-v4.css +31 -0
- package/dist/vite-plugin.cjs +2 -0
- package/dist/vite-plugin.cjs.map +1 -0
- package/dist/vite-plugin.d.ts +19 -0
- package/dist/vite-plugin.js +16 -0
- package/dist/vite-plugin.js.map +1 -0
- package/eslint-plugin/index.js +76 -0
- package/eslint-plugin/lib/docs.js +11 -0
- package/eslint-plugin/lib/glob.js +51 -0
- package/eslint-plugin/rules/max-page-lines.js +65 -0
- package/eslint-plugin/rules/no-deep-imports.js +61 -0
- package/eslint-plugin/rules/no-direct-http.js +128 -0
- package/eslint-plugin/rules/no-raw-elements.js +65 -0
- package/eslint-plugin/rules/no-style-literals.js +222 -0
- package/eslint-plugin/rules.json +32 -0
- package/package.json +156 -4
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
//#region tailwind-preset.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
return `color-mix(in srgb, var(--karni-color-${e}) calc(<alpha-value> * 100%), transparent)`;
|
|
4
|
+
}
|
|
5
|
+
var t = [
|
|
6
|
+
.5,
|
|
7
|
+
1,
|
|
8
|
+
1.5,
|
|
9
|
+
2,
|
|
10
|
+
2.5,
|
|
11
|
+
3,
|
|
12
|
+
3.5,
|
|
13
|
+
4,
|
|
14
|
+
5,
|
|
15
|
+
6,
|
|
16
|
+
7,
|
|
17
|
+
8,
|
|
18
|
+
9,
|
|
19
|
+
10,
|
|
20
|
+
11,
|
|
21
|
+
12,
|
|
22
|
+
14,
|
|
23
|
+
16,
|
|
24
|
+
20,
|
|
25
|
+
24,
|
|
26
|
+
28,
|
|
27
|
+
32,
|
|
28
|
+
36,
|
|
29
|
+
40,
|
|
30
|
+
44,
|
|
31
|
+
48,
|
|
32
|
+
52,
|
|
33
|
+
56,
|
|
34
|
+
60,
|
|
35
|
+
64,
|
|
36
|
+
72,
|
|
37
|
+
80,
|
|
38
|
+
96,
|
|
39
|
+
128
|
|
40
|
+
];
|
|
41
|
+
function n() {
|
|
42
|
+
let e = {
|
|
43
|
+
0: "0px",
|
|
44
|
+
px: "var(--karni-space-px, 1px)"
|
|
45
|
+
};
|
|
46
|
+
for (let n of t) {
|
|
47
|
+
let t = String(n).replace(".", "-");
|
|
48
|
+
e[String(n)] = `var(--karni-space-${t}, calc(var(--karni-space-unit, 0.25rem) * ${n}))`;
|
|
49
|
+
}
|
|
50
|
+
return e;
|
|
51
|
+
}
|
|
52
|
+
var r = {
|
|
53
|
+
xs: ["0.75rem", "1rem"],
|
|
54
|
+
sm: ["0.875rem", "1.25rem"],
|
|
55
|
+
base: ["1rem", "1.5rem"],
|
|
56
|
+
lg: ["1.125rem", "1.75rem"],
|
|
57
|
+
xl: ["1.25rem", "1.75rem"],
|
|
58
|
+
"2xl": ["1.5rem", "2rem"]
|
|
59
|
+
};
|
|
60
|
+
function i() {
|
|
61
|
+
return Object.fromEntries(Object.entries(r).map(([e, [t, n]]) => [e, [`var(--karni-font-size-${e}, ${t})`, { lineHeight: `var(--karni-font-leading-${e}, ${n})` }]]));
|
|
62
|
+
}
|
|
63
|
+
var a = {
|
|
64
|
+
darkMode: "class",
|
|
65
|
+
theme: { extend: {
|
|
66
|
+
colors: {
|
|
67
|
+
background: e("background"),
|
|
68
|
+
foreground: e("foreground"),
|
|
69
|
+
card: {
|
|
70
|
+
DEFAULT: e("card"),
|
|
71
|
+
foreground: e("card-foreground")
|
|
72
|
+
},
|
|
73
|
+
popover: {
|
|
74
|
+
DEFAULT: e("popover"),
|
|
75
|
+
foreground: e("popover-foreground")
|
|
76
|
+
},
|
|
77
|
+
primary: {
|
|
78
|
+
DEFAULT: e("primary"),
|
|
79
|
+
foreground: e("primary-foreground"),
|
|
80
|
+
hover: e("primary-hover")
|
|
81
|
+
},
|
|
82
|
+
secondary: {
|
|
83
|
+
DEFAULT: e("secondary"),
|
|
84
|
+
foreground: e("secondary-foreground"),
|
|
85
|
+
hover: e("secondary-hover")
|
|
86
|
+
},
|
|
87
|
+
muted: {
|
|
88
|
+
DEFAULT: e("muted"),
|
|
89
|
+
foreground: e("muted-foreground"),
|
|
90
|
+
subtle: e("muted-subtle")
|
|
91
|
+
},
|
|
92
|
+
accent: {
|
|
93
|
+
DEFAULT: e("accent"),
|
|
94
|
+
foreground: e("accent-foreground")
|
|
95
|
+
},
|
|
96
|
+
destructive: {
|
|
97
|
+
DEFAULT: e("destructive"),
|
|
98
|
+
foreground: e("destructive-foreground"),
|
|
99
|
+
hover: e("destructive-hover"),
|
|
100
|
+
subtle: e("destructive-subtle"),
|
|
101
|
+
border: e("destructive-border")
|
|
102
|
+
},
|
|
103
|
+
border: e("border"),
|
|
104
|
+
input: e("input"),
|
|
105
|
+
ring: e("ring"),
|
|
106
|
+
overlay: e("overlay"),
|
|
107
|
+
chart: {
|
|
108
|
+
1: e("chart-1"),
|
|
109
|
+
2: e("chart-2"),
|
|
110
|
+
3: e("chart-3"),
|
|
111
|
+
4: e("chart-4"),
|
|
112
|
+
5: e("chart-5")
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
borderColor: { DEFAULT: e("border") },
|
|
116
|
+
spacing: n(),
|
|
117
|
+
borderRadius: {
|
|
118
|
+
DEFAULT: "var(--karni-radius-sm, 0.25rem)",
|
|
119
|
+
sm: "var(--karni-radius-sm, 0.25rem)",
|
|
120
|
+
md: "var(--karni-radius-md, 0.375rem)",
|
|
121
|
+
lg: "var(--karni-radius-lg, 0.5rem)",
|
|
122
|
+
xl: "var(--karni-radius-xl, 0.75rem)",
|
|
123
|
+
full: "var(--karni-radius-full, 9999px)"
|
|
124
|
+
},
|
|
125
|
+
fontFamily: {
|
|
126
|
+
sans: "var(--karni-font-sans, ui-sans-serif, system-ui, sans-serif)",
|
|
127
|
+
mono: "var(--karni-font-mono, ui-monospace, monospace)"
|
|
128
|
+
},
|
|
129
|
+
fontSize: i(),
|
|
130
|
+
fontWeight: {
|
|
131
|
+
normal: "var(--karni-font-weight-normal, 400)",
|
|
132
|
+
medium: "var(--karni-font-weight-medium, 500)",
|
|
133
|
+
semibold: "var(--karni-font-weight-semibold, 600)",
|
|
134
|
+
bold: "var(--karni-font-weight-bold, 700)"
|
|
135
|
+
},
|
|
136
|
+
lineHeight: { none: "var(--karni-font-leading-none, 1)" },
|
|
137
|
+
textUnderlineOffset: { 4: "var(--karni-font-underline-offset, 4px)" },
|
|
138
|
+
letterSpacing: {
|
|
139
|
+
tight: "var(--karni-font-tracking-tight, -0.025em)",
|
|
140
|
+
wide: "var(--karni-font-tracking-wide, 0.025em)"
|
|
141
|
+
},
|
|
142
|
+
boxShadow: {
|
|
143
|
+
sm: "var(--karni-shadow-sm, 0 1px 3px 0 rgb(0 0 0 / 0.1))",
|
|
144
|
+
DEFAULT: "var(--karni-shadow, 0 1px 3px 0 rgb(0 0 0 / 0.1))",
|
|
145
|
+
md: "var(--karni-shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1))",
|
|
146
|
+
lg: "var(--karni-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1))"
|
|
147
|
+
},
|
|
148
|
+
borderWidth: {
|
|
149
|
+
DEFAULT: "var(--karni-border-width, 1px)",
|
|
150
|
+
2: "var(--karni-border-width-2, 2px)"
|
|
151
|
+
},
|
|
152
|
+
divideWidth: { DEFAULT: "var(--karni-border-width, 1px)" },
|
|
153
|
+
outlineWidth: { DEFAULT: "var(--karni-border-width, 1px)" },
|
|
154
|
+
ringWidth: {
|
|
155
|
+
1: "var(--karni-ring-width-1, 1px)",
|
|
156
|
+
2: "var(--karni-ring-width, 2px)"
|
|
157
|
+
},
|
|
158
|
+
ringOffsetWidth: { 2: "var(--karni-ring-offset-width, 2px)" },
|
|
159
|
+
zIndex: {
|
|
160
|
+
raised: "var(--karni-z-raised, 10)",
|
|
161
|
+
drawer: "var(--karni-z-drawer, 40)",
|
|
162
|
+
popover: "var(--karni-z-popover, 50)",
|
|
163
|
+
modal: "var(--karni-z-modal, 50)",
|
|
164
|
+
toast: "var(--karni-z-toast, 100)"
|
|
165
|
+
},
|
|
166
|
+
opacity: {
|
|
167
|
+
50: "var(--karni-opacity-50, 0.5)",
|
|
168
|
+
60: "var(--karni-opacity-60, 0.6)",
|
|
169
|
+
70: "var(--karni-opacity-70, 0.7)",
|
|
170
|
+
90: "var(--karni-opacity-90, 0.9)"
|
|
171
|
+
},
|
|
172
|
+
transitionDuration: { DEFAULT: "var(--karni-duration, 150ms)" },
|
|
173
|
+
transitionTimingFunction: { DEFAULT: "var(--karni-ease, cubic-bezier(0.4, 0, 0.2, 1))" },
|
|
174
|
+
animation: {
|
|
175
|
+
pulse: "pulse var(--karni-duration-pulse, 2s) cubic-bezier(0.4, 0, 0.6, 1) infinite",
|
|
176
|
+
spin: "spin var(--karni-duration-spin, 1s) linear infinite"
|
|
177
|
+
}
|
|
178
|
+
} }
|
|
179
|
+
};
|
|
180
|
+
//#endregion
|
|
181
|
+
export { t as SPACE_STEPS, a as default };
|
|
182
|
+
|
|
183
|
+
//# sourceMappingURL=tailwind-preset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tailwind-preset.js","names":["color","name","SPACE_STEPS","spacing","scale","step","FONT_SIZES","fontSize","size","leading","karniPreset"],"sources":["../tailwind-preset.ts"],"sourcesContent":["/**\n * Tailwind preset for @karni/ui consumers (Tailwind 3, and 4 through @config).\n *\n * Usage (tailwind.config.ts of the consuming app):\n *\n * ```ts\n * import karniPreset from \"@karni/ui/tailwind-preset\";\n *\n * export default {\n * presets: [karniPreset],\n * content: [\n * \"./src/**\\/*.{ts,tsx}\",\n * \"./node_modules/@karni/ui/dist/**\\/*.js\",\n * ],\n * };\n * ```\n *\n * Also import \"@karni/ui/styles.css\" once (it defines the --karni-* tokens).\n * Tailwind v4 loads the same config through `@config` in CSS, next to\n * `@import \"@karni/ui/tailwind-v4.css\"` (see the README).\n */\nimport type { Config } from \"tailwindcss\";\n\n/**\n * A colour token as a Tailwind colour. Wrapping it in color-mix with\n * `<alpha-value>` keeps opacity modifiers (`bg-primary/50`) working in\n * Tailwind 3 as well as 4, for any colour format the token holds.\n */\nfunction color(name: string): string {\n return `color-mix(in srgb, var(--karni-color-${name}) calc(<alpha-value> * 100%), transparent)`;\n}\n\n/** The spacing scale: each step reads --karni-space-<n>, else n × --karni-space-unit. */\nexport const SPACE_STEPS = [\n 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48,\n 52, 56, 60, 64, 72, 80, 96, 128,\n] as const;\n\nfunction spacing(): Record<string, string> {\n const scale: Record<string, string> = {\n 0: \"0px\",\n px: \"var(--karni-space-px, 1px)\",\n };\n for (const step of SPACE_STEPS) {\n const name = String(step).replace(\".\", \"-\");\n scale[String(step)] =\n `var(--karni-space-${name}, calc(var(--karni-space-unit, 0.25rem) * ${step}))`;\n }\n return scale;\n}\n\nconst FONT_SIZES = {\n xs: [\"0.75rem\", \"1rem\"],\n sm: [\"0.875rem\", \"1.25rem\"],\n base: [\"1rem\", \"1.5rem\"],\n lg: [\"1.125rem\", \"1.75rem\"],\n xl: [\"1.25rem\", \"1.75rem\"],\n \"2xl\": [\"1.5rem\", \"2rem\"],\n} as const;\n\nfunction fontSize(): Record<string, [string, { lineHeight: string }]> {\n return Object.fromEntries(\n Object.entries(FONT_SIZES).map(([name, [size, leading]]) => [\n name,\n [\n `var(--karni-font-size-${name}, ${size})`,\n { lineHeight: `var(--karni-font-leading-${name}, ${leading})` },\n ],\n ]),\n );\n}\n\nconst karniPreset = {\n // `as const` keeps the literal type so the preset stays assignable to\n // Config[\"presets\"] when consumers spread it into their own config.\n darkMode: \"class\" as const,\n theme: {\n extend: {\n colors: {\n background: color(\"background\"),\n foreground: color(\"foreground\"),\n card: { DEFAULT: color(\"card\"), foreground: color(\"card-foreground\") },\n popover: { DEFAULT: color(\"popover\"), foreground: color(\"popover-foreground\") },\n primary: {\n DEFAULT: color(\"primary\"),\n foreground: color(\"primary-foreground\"),\n hover: color(\"primary-hover\"),\n },\n secondary: {\n DEFAULT: color(\"secondary\"),\n foreground: color(\"secondary-foreground\"),\n hover: color(\"secondary-hover\"),\n },\n muted: {\n DEFAULT: color(\"muted\"),\n foreground: color(\"muted-foreground\"),\n subtle: color(\"muted-subtle\"),\n },\n accent: { DEFAULT: color(\"accent\"), foreground: color(\"accent-foreground\") },\n destructive: {\n DEFAULT: color(\"destructive\"),\n foreground: color(\"destructive-foreground\"),\n hover: color(\"destructive-hover\"),\n subtle: color(\"destructive-subtle\"),\n border: color(\"destructive-border\"),\n },\n border: color(\"border\"),\n input: color(\"input\"),\n ring: color(\"ring\"),\n overlay: color(\"overlay\"),\n chart: {\n \"1\": color(\"chart-1\"),\n \"2\": color(\"chart-2\"),\n \"3\": color(\"chart-3\"),\n \"4\": color(\"chart-4\"),\n \"5\": color(\"chart-5\"),\n },\n },\n borderColor: {\n // A bare `border` class uses the token, never Tailwind's default gray.\n DEFAULT: color(\"border\"),\n },\n spacing: spacing(),\n borderRadius: {\n DEFAULT: \"var(--karni-radius-sm, 0.25rem)\",\n sm: \"var(--karni-radius-sm, 0.25rem)\",\n md: \"var(--karni-radius-md, 0.375rem)\",\n lg: \"var(--karni-radius-lg, 0.5rem)\",\n xl: \"var(--karni-radius-xl, 0.75rem)\",\n full: \"var(--karni-radius-full, 9999px)\",\n },\n fontFamily: {\n sans: \"var(--karni-font-sans, ui-sans-serif, system-ui, sans-serif)\",\n mono: \"var(--karni-font-mono, ui-monospace, monospace)\",\n },\n fontSize: fontSize(),\n fontWeight: {\n normal: \"var(--karni-font-weight-normal, 400)\",\n medium: \"var(--karni-font-weight-medium, 500)\",\n semibold: \"var(--karni-font-weight-semibold, 600)\",\n bold: \"var(--karni-font-weight-bold, 700)\",\n },\n lineHeight: { none: \"var(--karni-font-leading-none, 1)\" },\n textUnderlineOffset: { \"4\": \"var(--karni-font-underline-offset, 4px)\" },\n letterSpacing: {\n tight: \"var(--karni-font-tracking-tight, -0.025em)\",\n wide: \"var(--karni-font-tracking-wide, 0.025em)\",\n },\n boxShadow: {\n sm: \"var(--karni-shadow-sm, 0 1px 3px 0 rgb(0 0 0 / 0.1))\",\n DEFAULT: \"var(--karni-shadow, 0 1px 3px 0 rgb(0 0 0 / 0.1))\",\n md: \"var(--karni-shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1))\",\n lg: \"var(--karni-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1))\",\n },\n borderWidth: {\n DEFAULT: \"var(--karni-border-width, 1px)\",\n \"2\": \"var(--karni-border-width-2, 2px)\",\n },\n divideWidth: { DEFAULT: \"var(--karni-border-width, 1px)\" },\n outlineWidth: { DEFAULT: \"var(--karni-border-width, 1px)\" },\n ringWidth: {\n \"1\": \"var(--karni-ring-width-1, 1px)\",\n \"2\": \"var(--karni-ring-width, 2px)\",\n },\n ringOffsetWidth: { \"2\": \"var(--karni-ring-offset-width, 2px)\" },\n zIndex: {\n raised: \"var(--karni-z-raised, 10)\",\n drawer: \"var(--karni-z-drawer, 40)\",\n popover: \"var(--karni-z-popover, 50)\",\n modal: \"var(--karni-z-modal, 50)\",\n toast: \"var(--karni-z-toast, 100)\",\n },\n opacity: {\n \"50\": \"var(--karni-opacity-50, 0.5)\",\n \"60\": \"var(--karni-opacity-60, 0.6)\",\n \"70\": \"var(--karni-opacity-70, 0.7)\",\n \"90\": \"var(--karni-opacity-90, 0.9)\",\n },\n transitionDuration: { DEFAULT: \"var(--karni-duration, 150ms)\" },\n transitionTimingFunction: { DEFAULT: \"var(--karni-ease, cubic-bezier(0.4, 0, 0.2, 1))\" },\n animation: {\n pulse: \"pulse var(--karni-duration-pulse, 2s) cubic-bezier(0.4, 0, 0.6, 1) infinite\",\n spin: \"spin var(--karni-duration-spin, 1s) linear infinite\",\n },\n },\n },\n} satisfies Omit<Config, \"content\">;\n\nexport default karniPreset;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* @karni/ui under Tailwind 4: import once, after Tailwind itself.
|
|
2
|
+
*
|
|
3
|
+
* @import "tailwindcss";
|
|
4
|
+
* @import "@karni/ui/tailwind-v4.css";
|
|
5
|
+
* @config "../tailwind.config.ts"; (the @karni/ui preset)
|
|
6
|
+
*
|
|
7
|
+
* Tailwind 3 projects do not import this file: Tailwind 3 rejects an
|
|
8
|
+
* `@layer base` outside its own stylesheet, and needs nothing here.
|
|
9
|
+
*
|
|
10
|
+
* A bare `border` uses the --karni-color-border token, as under Tailwind 3, whose
|
|
11
|
+
* preflight applies the preset's default border color. Tailwind 4 defaults
|
|
12
|
+
* borders to currentColor. In the base layer this rule sits below every
|
|
13
|
+
* utility, so `border-input`, `border-destructive` and the rest still win;
|
|
14
|
+
* `:root` lifts it just above Tailwind's own reset. */
|
|
15
|
+
@layer base {
|
|
16
|
+
:root *,
|
|
17
|
+
:root ::before,
|
|
18
|
+
:root ::after,
|
|
19
|
+
:root ::backdrop,
|
|
20
|
+
:root ::file-selector-button {
|
|
21
|
+
border-color: var(--karni-color-border);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Tailwind 4 reads transition timing from its own theme variables rather
|
|
26
|
+
* than the preset; point them at the motion tokens (which drop to 0ms under
|
|
27
|
+
* prefers-reduced-motion). */
|
|
28
|
+
@theme {
|
|
29
|
+
--default-transition-duration: var(--karni-duration);
|
|
30
|
+
--default-transition-timing-function: var(--karni-ease);
|
|
31
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./script-BVJ9BbPm.cjs");function t(){return{name:`karni-theme`,transformIndexHtml:()=>[{tag:`script`,children:e.t,injectTo:`head-prepend`}]}}exports.karniTheme=t;
|
|
2
|
+
//# sourceMappingURL=vite-plugin.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin.cjs","names":["KARNI_THEME_SCRIPT","require_script","karniTheme"],"sources":["../vite-plugin.ts"],"sourcesContent":["/**\n * `@karni/ui/vite`: what a Vite app needs from @karni/ui at build time.\n *\n * import { karniTheme } from \"@karni/ui/vite\";\n * export default defineConfig({ plugins: [react(), karniTheme()] });\n *\n * Typed structurally, so it needs no `vite` types of its own.\n */\nimport { KARNI_THEME_SCRIPT } from \"./src/theme/script\";\n\n/** One tag Vite's `transformIndexHtml` adds to index.html. */\nexport interface KarniHtmlTag {\n tag: \"script\";\n children: string;\n injectTo: \"head-prepend\";\n}\n\n/** The plugin object `karniTheme()` returns (a Vite `Plugin`). */\nexport interface KarniThemePlugin {\n name: \"karni-theme\";\n transformIndexHtml: () => KarniHtmlTag[];\n}\n\n/**\n * Puts KARNI_THEME_SCRIPT first in index.html's <head>, so the page paints\n * in the user's theme (stored, else the system's) before React starts.\n * Pair it with <ThemeProvider> around the app. Escape hatch: leave the\n * plugin out and inline KARNI_THEME_SCRIPT yourself (another storage key,\n * another framework).\n */\nexport function karniTheme(): KarniThemePlugin {\n return {\n name: \"karni-theme\",\n transformIndexHtml: () => [\n { tag: \"script\", children: KARNI_THEME_SCRIPT, injectTo: \"head-prepend\" },\n ],\n };\n}\n"],"mappings":"A,4GA8BA,SAAgBE,GAA+B,CAC7C,MAAO,CACL,KAAM,cACN,uBAA0B,CACxB,CAAE,IAAK,SAAU,SAAUF,EAAAA,EAAoB,SAAU,cAAe,CAC1E,CACF,CACF,C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** One tag Vite's `transformIndexHtml` adds to index.html. */
|
|
2
|
+
export interface KarniHtmlTag {
|
|
3
|
+
tag: "script";
|
|
4
|
+
children: string;
|
|
5
|
+
injectTo: "head-prepend";
|
|
6
|
+
}
|
|
7
|
+
/** The plugin object `karniTheme()` returns (a Vite `Plugin`). */
|
|
8
|
+
export interface KarniThemePlugin {
|
|
9
|
+
name: "karni-theme";
|
|
10
|
+
transformIndexHtml: () => KarniHtmlTag[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Puts KARNI_THEME_SCRIPT first in index.html's <head>, so the page paints
|
|
14
|
+
* in the user's theme (stored, else the system's) before React starts.
|
|
15
|
+
* Pair it with <ThemeProvider> around the app. Escape hatch: leave the
|
|
16
|
+
* plugin out and inline KARNI_THEME_SCRIPT yourself (another storage key,
|
|
17
|
+
* another framework).
|
|
18
|
+
*/
|
|
19
|
+
export declare function karniTheme(): KarniThemePlugin;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { t as e } from "./script-D1fn4Dm4.js";
|
|
2
|
+
//#region vite-plugin.ts
|
|
3
|
+
function t() {
|
|
4
|
+
return {
|
|
5
|
+
name: "karni-theme",
|
|
6
|
+
transformIndexHtml: () => [{
|
|
7
|
+
tag: "script",
|
|
8
|
+
children: e,
|
|
9
|
+
injectTo: "head-prepend"
|
|
10
|
+
}]
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { t as karniTheme };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=vite-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin.js","names":["KARNI_THEME_SCRIPT","karniTheme"],"sources":["../vite-plugin.ts"],"sourcesContent":["/**\n * `@karni/ui/vite`: what a Vite app needs from @karni/ui at build time.\n *\n * import { karniTheme } from \"@karni/ui/vite\";\n * export default defineConfig({ plugins: [react(), karniTheme()] });\n *\n * Typed structurally, so it needs no `vite` types of its own.\n */\nimport { KARNI_THEME_SCRIPT } from \"./src/theme/script\";\n\n/** One tag Vite's `transformIndexHtml` adds to index.html. */\nexport interface KarniHtmlTag {\n tag: \"script\";\n children: string;\n injectTo: \"head-prepend\";\n}\n\n/** The plugin object `karniTheme()` returns (a Vite `Plugin`). */\nexport interface KarniThemePlugin {\n name: \"karni-theme\";\n transformIndexHtml: () => KarniHtmlTag[];\n}\n\n/**\n * Puts KARNI_THEME_SCRIPT first in index.html's <head>, so the page paints\n * in the user's theme (stored, else the system's) before React starts.\n * Pair it with <ThemeProvider> around the app. Escape hatch: leave the\n * plugin out and inline KARNI_THEME_SCRIPT yourself (another storage key,\n * another framework).\n */\nexport function karniTheme(): KarniThemePlugin {\n return {\n name: \"karni-theme\",\n transformIndexHtml: () => [\n { tag: \"script\", children: KARNI_THEME_SCRIPT, injectTo: \"head-prepend\" },\n ],\n };\n}\n"],"mappings":";;AA8BA,SAAgBC,IAA+B;CAC7C,OAAO;EACL,MAAM;EACN,0BAA0B,CACxB;GAAE,KAAK;GAAU,UAAUD;GAAoB,UAAU;EAAe,CAC1E;CACF;AACF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @karni/ui/eslint-plugin — Karni's frontend enforcement rules.
|
|
3
|
+
*
|
|
4
|
+
* Plain ESM, no build step; ships inside the @karni/ui package (enforcement
|
|
5
|
+
* contract §2 + F1, F15) and is consumed from flat config (ESLint >= 9):
|
|
6
|
+
*
|
|
7
|
+
* // eslint.config.js
|
|
8
|
+
* import karni from "@karni/ui/eslint-plugin";
|
|
9
|
+
* export default [...karni.configs.recommended];
|
|
10
|
+
*
|
|
11
|
+
* Every rule fails the build ("error", never "warn"), and every rule's
|
|
12
|
+
* documented opt-out is ESLint's own disable comment WITH a required reason:
|
|
13
|
+
*
|
|
14
|
+
* // eslint-disable-next-line karni/no-direct-http -- <reason>
|
|
15
|
+
*
|
|
16
|
+
* The reason requirement is enforced by
|
|
17
|
+
* @eslint-community/eslint-comments/require-description (a regular dependency
|
|
18
|
+
* of @karni/ui — eslint itself is only a peer), and unused disable directives
|
|
19
|
+
* are themselves errors (linterOptions.reportUnusedDisableDirectives).
|
|
20
|
+
*/
|
|
21
|
+
import eslintComments from "@eslint-community/eslint-plugin-eslint-comments";
|
|
22
|
+
|
|
23
|
+
import maxPageLines from "./rules/max-page-lines.js";
|
|
24
|
+
import noDeepImports from "./rules/no-deep-imports.js";
|
|
25
|
+
import noDirectHttp from "./rules/no-direct-http.js";
|
|
26
|
+
import noRawElements from "./rules/no-raw-elements.js";
|
|
27
|
+
import noStyleLiterals from "./rules/no-style-literals.js";
|
|
28
|
+
|
|
29
|
+
const plugin = {
|
|
30
|
+
meta: {
|
|
31
|
+
name: "@karni/ui/eslint-plugin",
|
|
32
|
+
version: "0.1.0",
|
|
33
|
+
},
|
|
34
|
+
rules: {
|
|
35
|
+
"no-raw-elements": noRawElements,
|
|
36
|
+
"max-page-lines": maxPageLines,
|
|
37
|
+
"no-deep-imports": noDeepImports,
|
|
38
|
+
"no-direct-http": noDirectHttp,
|
|
39
|
+
// Not in `recommended`: a project with its own design system uses its own
|
|
40
|
+
// palette. @karni/ui enables it for its own source; projects may opt in.
|
|
41
|
+
"no-style-literals": noStyleLiterals,
|
|
42
|
+
},
|
|
43
|
+
// Filled below so the config entries can reference the plugin object itself.
|
|
44
|
+
configs: {},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
plugin.configs.recommended = [
|
|
48
|
+
{
|
|
49
|
+
name: "karni/recommended",
|
|
50
|
+
plugins: {
|
|
51
|
+
karni: plugin,
|
|
52
|
+
"@eslint-community/eslint-comments": eslintComments,
|
|
53
|
+
},
|
|
54
|
+
linterOptions: {
|
|
55
|
+
reportUnusedDisableDirectives: "error",
|
|
56
|
+
},
|
|
57
|
+
rules: {
|
|
58
|
+
"karni/no-raw-elements": "error",
|
|
59
|
+
"karni/max-page-lines": "error",
|
|
60
|
+
"karni/no-deep-imports": "error",
|
|
61
|
+
"karni/no-direct-http": "error",
|
|
62
|
+
// Every eslint-disable needs a `-- reason`; a bare opt-out is a failure.
|
|
63
|
+
"@eslint-community/eslint-comments/require-description": "error",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
// Tests, mocks and vitest setup files stub the network by design.
|
|
68
|
+
name: "karni/recommended-test-files",
|
|
69
|
+
files: ["**/*.test.*", "**/mocks/**", "**/vitest.setup.*"],
|
|
70
|
+
rules: {
|
|
71
|
+
"karni/no-direct-http": "off",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export default plugin;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docs links for the rules. DOCS_BASE_URL must equal karni.errorcodes.DOCS_BASE_URL
|
|
3
|
+
* (packages/karni/tests/test_errorcodes.py checks it), and each rule's anchor is
|
|
4
|
+
* generated into docs/errors.md from ../rules.json.
|
|
5
|
+
*/
|
|
6
|
+
export const DOCS_BASE_URL = "https://cbsshekhawat18-lab.github.io/karni/";
|
|
7
|
+
|
|
8
|
+
/** @param {string} rule rule name without the plugin prefix, e.g. "no-raw-elements" */
|
|
9
|
+
export function docsUrl(rule) {
|
|
10
|
+
return `${DOCS_BASE_URL}errors/#karni-${rule}`;
|
|
11
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal glob matching for rule file-path options (no runtime dependency).
|
|
3
|
+
*
|
|
4
|
+
* Supported syntax (enough for the shipped defaults and documented options):
|
|
5
|
+
* - `**` matches any number of path segments (crosses `/`).
|
|
6
|
+
* - `*` matches within a single path segment.
|
|
7
|
+
* - `?` matches a single character within a segment.
|
|
8
|
+
*
|
|
9
|
+
* Paths are normalized to posix separators before matching, and a pattern
|
|
10
|
+
* matches if it matches the full path or any trailing sub-path, so a
|
|
11
|
+
* "**" + "/pages/" + "**" pattern works for absolute and relative filenames.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const SPECIALS = /[.+^${}()|[\]\\]/g;
|
|
15
|
+
|
|
16
|
+
/** @param {string} glob @returns {RegExp} */
|
|
17
|
+
export function globToRegExp(glob) {
|
|
18
|
+
let out = "";
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < glob.length) {
|
|
21
|
+
const ch = glob[i];
|
|
22
|
+
if (ch === "*") {
|
|
23
|
+
if (glob[i + 1] === "*") {
|
|
24
|
+
if (glob[i + 2] === "/") {
|
|
25
|
+
// `**/` — zero or more whole segments.
|
|
26
|
+
out += "(?:.*/)?";
|
|
27
|
+
i += 3;
|
|
28
|
+
} else {
|
|
29
|
+
out += ".*";
|
|
30
|
+
i += 2;
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
out += "[^/]*";
|
|
34
|
+
i += 1;
|
|
35
|
+
}
|
|
36
|
+
} else if (ch === "?") {
|
|
37
|
+
out += "[^/]";
|
|
38
|
+
i += 1;
|
|
39
|
+
} else {
|
|
40
|
+
out += ch.replace(SPECIALS, "\\$&");
|
|
41
|
+
i += 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return new RegExp(`(?:^|/)${out}$`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @param {string} filename @param {readonly string[]} patterns @returns {boolean} */
|
|
48
|
+
export function matchesAny(filename, patterns) {
|
|
49
|
+
const path = filename.replaceAll("\\", "/");
|
|
50
|
+
return patterns.some((pattern) => globToRegExp(pattern).test(path));
|
|
51
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* karni/max-page-lines — page files cap at 50 physical lines. A longer page
|
|
3
|
+
* is rebuilding something @karni/ui ships; reach for the component's props
|
|
4
|
+
* (columns, fields, slots) instead.
|
|
5
|
+
*/
|
|
6
|
+
import { docsUrl } from "../lib/docs.js";
|
|
7
|
+
import { matchesAny } from "../lib/glob.js";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_MAX_LINES = 50;
|
|
10
|
+
const DEFAULT_FILES = ["**/pages/**"];
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
meta: {
|
|
14
|
+
type: "problem",
|
|
15
|
+
docs: {
|
|
16
|
+
url: docsUrl("max-page-lines"),
|
|
17
|
+
description:
|
|
18
|
+
"Fail page files (default **/pages/**) with more than 50 physical lines: pages compose @karni/ui components, they do not reimplement them.",
|
|
19
|
+
},
|
|
20
|
+
schema: [
|
|
21
|
+
{
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
maxLines: { type: "integer", minimum: 1 },
|
|
25
|
+
files: {
|
|
26
|
+
type: "array",
|
|
27
|
+
items: { type: "string" },
|
|
28
|
+
description: "Glob patterns of the files the cap applies to.",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
messages: {
|
|
35
|
+
tooLong:
|
|
36
|
+
"Page file is {{count}} physical lines (max {{max}}). Past the cap you are rebuilding " +
|
|
37
|
+
"something @karni/ui ships. Compose Page/ResourceTable/ResourceForm/ResourceDetail and " +
|
|
38
|
+
"their props, or move logic into hooks/components. " +
|
|
39
|
+
"Opt out only with an explained disable: /* eslint-disable karni/max-page-lines -- <reason> */",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
create(context) {
|
|
44
|
+
const options = context.options[0] ?? {};
|
|
45
|
+
const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
|
|
46
|
+
const files = options.files ?? DEFAULT_FILES;
|
|
47
|
+
if (!matchesAny(context.filename, files)) {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
Program(node) {
|
|
52
|
+
const lines = context.sourceCode.lines;
|
|
53
|
+
// A trailing newline produces one empty final element — not a line.
|
|
54
|
+
const count = lines.at(-1) === "" ? lines.length - 1 : lines.length;
|
|
55
|
+
if (count <= maxLines) return;
|
|
56
|
+
context.report({
|
|
57
|
+
node,
|
|
58
|
+
loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
59
|
+
messageId: "tooLong",
|
|
60
|
+
data: { count: String(count), max: String(maxLines) },
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* karni/no-deep-imports — importing @karni/ui internals (dist/, src/) is
|
|
3
|
+
* banned. Deep imports are how component code gets vendored and forked;
|
|
4
|
+
* compose the public API instead, and if it does not fit, extend it through
|
|
5
|
+
* its props/overrides (or upstream the change).
|
|
6
|
+
*/
|
|
7
|
+
import { docsUrl } from "../lib/docs.js";
|
|
8
|
+
const DEEP_IMPORT = /^@karni\/ui\/(?:dist|src)(?:\/|$)/;
|
|
9
|
+
|
|
10
|
+
function checkSource(context, sourceNode) {
|
|
11
|
+
if (!sourceNode || sourceNode.type !== "Literal") return;
|
|
12
|
+
if (typeof sourceNode.value !== "string") return;
|
|
13
|
+
if (!DEEP_IMPORT.test(sourceNode.value)) return;
|
|
14
|
+
context.report({
|
|
15
|
+
node: sourceNode,
|
|
16
|
+
messageId: "deepImport",
|
|
17
|
+
data: { source: sourceNode.value },
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
meta: {
|
|
23
|
+
type: "problem",
|
|
24
|
+
docs: {
|
|
25
|
+
url: docsUrl("no-deep-imports"),
|
|
26
|
+
description:
|
|
27
|
+
"Ban imports from @karni/ui/dist/* and @karni/ui/src/*: compose the public API, never vendor a copy of an internal.",
|
|
28
|
+
},
|
|
29
|
+
schema: [],
|
|
30
|
+
messages: {
|
|
31
|
+
deepImport:
|
|
32
|
+
'Deep import "{{source}}" reaches into @karni/ui internals. Import from the public ' +
|
|
33
|
+
'barrel "@karni/ui" and compose/extend the exported component. Vendoring an internal ' +
|
|
34
|
+
"copy forks it. " +
|
|
35
|
+
"Opt out only with an explained disable: " +
|
|
36
|
+
"// eslint-disable-next-line karni/no-deep-imports -- <reason>",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
create(context) {
|
|
41
|
+
return {
|
|
42
|
+
ImportDeclaration(node) {
|
|
43
|
+
checkSource(context, node.source);
|
|
44
|
+
},
|
|
45
|
+
ExportNamedDeclaration(node) {
|
|
46
|
+
checkSource(context, node.source);
|
|
47
|
+
},
|
|
48
|
+
ExportAllDeclaration(node) {
|
|
49
|
+
checkSource(context, node.source);
|
|
50
|
+
},
|
|
51
|
+
ImportExpression(node) {
|
|
52
|
+
checkSource(context, node.source);
|
|
53
|
+
},
|
|
54
|
+
CallExpression(node) {
|
|
55
|
+
if (node.callee.type === "Identifier" && node.callee.name === "require") {
|
|
56
|
+
checkSource(context, node.arguments[0]);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
};
|