@camunda/design-system 0.63.0 → 0.64.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/dist/src/components/ui/data-table.d.ts +17 -2
- package/dist/src/components/ui/data-table.d.ts.map +1 -1
- package/dist/src/components/ui/data-table.js +16 -7
- package/dist/src/components/ui/data-table.js.map +1 -1
- package/dist/src/components/ui/input.d.ts.map +1 -1
- package/dist/src/components/ui/input.js +7 -4
- package/dist/src/components/ui/input.js.map +1 -1
- package/dist/src/components/ui/select.d.ts.map +1 -1
- package/dist/src/components/ui/select.js +11 -8
- package/dist/src/components/ui/select.js.map +1 -1
- package/dist/src/components/ui/textarea.d.ts.map +1 -1
- package/dist/src/components/ui/textarea.js +8 -5
- package/dist/src/components/ui/textarea.js.map +1 -1
- package/eslint-plugin/index.d.ts +18 -0
- package/eslint-plugin/index.js +102 -0
- package/eslint-rules/button-has-type.js +56 -0
- package/eslint-rules/class-literal.js +20 -0
- package/eslint-rules/ds-imports.js +131 -0
- package/eslint-rules/icon-button-needs-label.js +110 -0
- package/eslint-rules/index.js +31 -0
- package/eslint-rules/navigation.js +295 -0
- package/eslint-rules/no-adhoc-typography.js +77 -0
- package/eslint-rules/no-deep-import.js +59 -0
- package/eslint-rules/no-direct-lucide-import.js +67 -0
- package/eslint-rules/no-hardcoded-i18n-prop.js +78 -0
- package/eslint-rules/no-hardcoded-user-facing-string.js +82 -0
- package/eslint-rules/no-primitive-tailwind-colors.js +36 -0
- package/eslint-rules/no-raw-color-in-classname.js +34 -0
- package/eslint-rules/prefer-link-props.js +107 -0
- package/eslint-rules/require-browsable-link.js +236 -0
- package/eslint-rules/require-shared-sidebar-provider.js +165 -0
- package/package.json +18 -3
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requires real, browsable links (href — hover, Cmd/middle-click, "open in new
|
|
3
|
+
* tab", announced as a link) on the elements where navigation is expected:
|
|
4
|
+
* links in text, navigation items, and tables. Anything else (a generic
|
|
5
|
+
* `<div>`, `Card`, plain `Button`) is never checked.
|
|
6
|
+
*
|
|
7
|
+
* Flags, with no guessing:
|
|
8
|
+
* - links with no destination: `<a onClick>` without `href`; DS `Link` /
|
|
9
|
+
* `BreadcrumbLink` without `href` or `asChild`.
|
|
10
|
+
* - scoped elements whose click handler visibly navigates (see
|
|
11
|
+
* navigation.js): DS `Button variant="link"`, `NavBreadcrumbItem` without
|
|
12
|
+
* `linkProps`, `DropdownMenuItem` without `asChild`, `DataTable` without
|
|
13
|
+
* `rowHref`, `TableRow`/`TableCell`, and DS descriptor objects (sidebar
|
|
14
|
+
* items, breadcrumb segments, switcher entries, notifications) without
|
|
15
|
+
* `linkProps`/`href`.
|
|
16
|
+
*
|
|
17
|
+
* The unclear case — a click prop with no link prop whose intent isn't
|
|
18
|
+
* visible — belongs to `prefer-link-props` (warn), not here.
|
|
19
|
+
*/
|
|
20
|
+
import {
|
|
21
|
+
findAttribute,
|
|
22
|
+
hasActiveAttribute,
|
|
23
|
+
hasSpread,
|
|
24
|
+
staticString,
|
|
25
|
+
trackDsImports,
|
|
26
|
+
} from "./ds-imports.js";
|
|
27
|
+
import {
|
|
28
|
+
attributeNavigation,
|
|
29
|
+
descriptorRoots,
|
|
30
|
+
findNavigation,
|
|
31
|
+
forEachLinklessDescriptor,
|
|
32
|
+
isBuiltinNavigation,
|
|
33
|
+
navigationSchemaProperties,
|
|
34
|
+
resolveFunction,
|
|
35
|
+
} from "./navigation.js";
|
|
36
|
+
|
|
37
|
+
const LINK_COMPONENTS = new Set(["Link", "BreadcrumbLink"]);
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
meta: {
|
|
41
|
+
type: "problem",
|
|
42
|
+
docs: {
|
|
43
|
+
description:
|
|
44
|
+
"Links in text, navigation items and tables must render real links (href), not click-only elements.",
|
|
45
|
+
},
|
|
46
|
+
hasSuggestions: true,
|
|
47
|
+
messages: {
|
|
48
|
+
anchorWithoutHref:
|
|
49
|
+
'<a> with `onClick` but no `href` is not a link: no hover URL, no Cmd/middle-click, no open in new tab. Add `href` (or use a <button type="button"> if it doesn\'t navigate).',
|
|
50
|
+
linkWithoutHref:
|
|
51
|
+
"<{{component}}> has no `href` and no `asChild`, so it goes nowhere. Pass `href`, or `asChild` around your router's link. If it doesn't navigate (select all, expand, …), use <Button variant=\"link\"> instead.",
|
|
52
|
+
buttonLinkNavigates:
|
|
53
|
+
'<Button variant="link"> navigates from `onClick`. Use the DS <Link href> (or <Link asChild> around your router\'s link) so it is a real link.',
|
|
54
|
+
menuItemNavigates:
|
|
55
|
+
"<DropdownMenuItem> navigates from `{{click}}`. Use `asChild` around a link (`<DropdownMenuItem asChild><Link to=…>`) so the entry is a real link.",
|
|
56
|
+
tableNavigates:
|
|
57
|
+
"<{{component}}> navigates from `onClick`. Render the destination as a link in the row's primary cell (or use DataTable `rowHref`).",
|
|
58
|
+
missingLinkProp:
|
|
59
|
+
"<{{component}}> navigates from `{{click}}` but has no `{{link}}`, so it renders a button. Pass `{{link}}`; the link navigates on its own, so remove the navigation from `{{click}}` (keep it only for side effects such as analytics).",
|
|
60
|
+
descriptorNavigates:
|
|
61
|
+
"This item's `{{click}}` navigates but it has no `linkProps`/`href`, so it renders a button. Add the link prop; the link navigates on its own, so remove the navigation from `{{click}}` (keep it only for side effects such as analytics).",
|
|
62
|
+
suggestRowHref:
|
|
63
|
+
"Replace `onRowClick` with `rowHref` pointing at the same destination.",
|
|
64
|
+
},
|
|
65
|
+
schema: [
|
|
66
|
+
{
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
importSources: {
|
|
70
|
+
type: "array",
|
|
71
|
+
items: { type: "string" },
|
|
72
|
+
minItems: 1,
|
|
73
|
+
uniqueItems: true,
|
|
74
|
+
},
|
|
75
|
+
...navigationSchemaProperties,
|
|
76
|
+
},
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
create(context) {
|
|
82
|
+
const ds = trackDsImports(context);
|
|
83
|
+
const source = context.sourceCode;
|
|
84
|
+
|
|
85
|
+
function report(node, messageId, data, suggest) {
|
|
86
|
+
context.report({ node, messageId, data, suggest });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Suggestion for `onRowClick={(row) => navigate(url(row))}`: replace it
|
|
90
|
+
// with `rowHref={(row) => url(row)}`. DataTable's anchor still calls
|
|
91
|
+
// onRowClick, so keeping a navigating handler would navigate twice — the
|
|
92
|
+
// suggestion is offered only when the handler is nothing but the
|
|
93
|
+
// navigation, and only for a plain one-URL push (see below).
|
|
94
|
+
function rowHrefSuggestion(node, attr, target) {
|
|
95
|
+
// A (disabled) rowHref already present would end up duplicated.
|
|
96
|
+
if (findAttribute(node, "rowHref")) return undefined;
|
|
97
|
+
const handler = attr.value.expression;
|
|
98
|
+
if (resolveFunction(context, handler) !== handler) return undefined;
|
|
99
|
+
// App helpers take routes/params, not URLs — no safe rowHref to offer.
|
|
100
|
+
if (!isBuiltinNavigation(target)) return undefined;
|
|
101
|
+
// A plain anchor can only push one URL: no new tab (window.open), no
|
|
102
|
+
// history replace, no router state/options (a second argument).
|
|
103
|
+
if (target.type === "CallExpression") {
|
|
104
|
+
const method = target.callee.property?.name;
|
|
105
|
+
if (method === "open" || method === "replace") return undefined;
|
|
106
|
+
if (target.arguments.length !== 1) return undefined;
|
|
107
|
+
}
|
|
108
|
+
const body = handler.body;
|
|
109
|
+
const onlyNavigation =
|
|
110
|
+
body === target ||
|
|
111
|
+
(body.type === "BlockStatement" &&
|
|
112
|
+
body.body.length === 1 &&
|
|
113
|
+
body.body[0].type === "ExpressionStatement" &&
|
|
114
|
+
body.body[0].expression === target);
|
|
115
|
+
if (!onlyNavigation) return undefined;
|
|
116
|
+
const dest =
|
|
117
|
+
target.type === "AssignmentExpression"
|
|
118
|
+
? target.right
|
|
119
|
+
: target.arguments[0];
|
|
120
|
+
if (!dest || dest.type === "SpreadElement") return undefined;
|
|
121
|
+
const params = handler.params.map((p) => source.getText(p)).join(", ");
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
messageId: "suggestRowHref",
|
|
125
|
+
fix: (fixer) =>
|
|
126
|
+
fixer.replaceText(
|
|
127
|
+
attr,
|
|
128
|
+
`rowHref={(${params}) => ${source.getText(dest)}}`,
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function checkJsx(node) {
|
|
135
|
+
const name = node.name;
|
|
136
|
+
if (name.type === "JSXIdentifier" && name.name === "a") {
|
|
137
|
+
if (
|
|
138
|
+
findAttribute(node, "onClick") &&
|
|
139
|
+
!hasActiveAttribute(node, "href") &&
|
|
140
|
+
!hasSpread(node)
|
|
141
|
+
) {
|
|
142
|
+
report(node, "anchorWithoutHref");
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const component = ds.importedName(name);
|
|
147
|
+
if (!component || hasSpread(node)) return;
|
|
148
|
+
|
|
149
|
+
if (LINK_COMPONENTS.has(component)) {
|
|
150
|
+
if (
|
|
151
|
+
!hasActiveAttribute(node, "href") &&
|
|
152
|
+
!hasActiveAttribute(node, "asChild")
|
|
153
|
+
) {
|
|
154
|
+
report(node, "linkWithoutHref", { component });
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (component === "Button") {
|
|
159
|
+
const variant = staticString(findAttribute(node, "variant")?.value);
|
|
160
|
+
if (
|
|
161
|
+
variant === "link" &&
|
|
162
|
+
!hasActiveAttribute(node, "asChild") &&
|
|
163
|
+
attributeNavigation(context, findAttribute(node, "onClick"))
|
|
164
|
+
) {
|
|
165
|
+
report(node, "buttonLinkNavigates");
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (component === "DropdownMenuItem") {
|
|
170
|
+
if (hasActiveAttribute(node, "asChild")) return;
|
|
171
|
+
for (const click of ["onSelect", "onClick"]) {
|
|
172
|
+
if (attributeNavigation(context, findAttribute(node, click))) {
|
|
173
|
+
report(node, "menuItemNavigates", { click });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (component === "TableRow" || component === "TableCell") {
|
|
180
|
+
if (attributeNavigation(context, findAttribute(node, "onClick"))) {
|
|
181
|
+
report(node, "tableNavigates", { component });
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (component === "NavBreadcrumbItem") {
|
|
186
|
+
if (hasActiveAttribute(node, "linkProps")) return;
|
|
187
|
+
if (attributeNavigation(context, findAttribute(node, "onClick"))) {
|
|
188
|
+
report(node, "missingLinkProp", {
|
|
189
|
+
component,
|
|
190
|
+
click: "onClick",
|
|
191
|
+
link: "linkProps",
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (component === "DataTable") {
|
|
197
|
+
if (hasActiveAttribute(node, "rowHref")) return;
|
|
198
|
+
const attr = findAttribute(node, "onRowClick");
|
|
199
|
+
const target = attributeNavigation(context, attr);
|
|
200
|
+
if (target) {
|
|
201
|
+
report(
|
|
202
|
+
node,
|
|
203
|
+
"missingLinkProp",
|
|
204
|
+
{ component, click: "onRowClick", link: "rowHref" },
|
|
205
|
+
rowHrefSuggestion(node, attr, target),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// One `const items = [...]` can feed several components — report once.
|
|
212
|
+
const reported = new Set();
|
|
213
|
+
function checkDescriptors(node) {
|
|
214
|
+
for (const root of descriptorRoots(ds, node)) {
|
|
215
|
+
forEachLinklessDescriptor(context, root, (_obj, click) => {
|
|
216
|
+
if (reported.has(click)) return;
|
|
217
|
+
if (findNavigation(context, click.value)) {
|
|
218
|
+
reported.add(click);
|
|
219
|
+
report(click, "descriptorNavigates", {
|
|
220
|
+
click: click.key.name ?? click.key.value,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
ImportDeclaration: ds.ImportDeclaration,
|
|
229
|
+
JSXOpeningElement(node) {
|
|
230
|
+
checkJsx(node);
|
|
231
|
+
checkDescriptors(node);
|
|
232
|
+
},
|
|
233
|
+
CallExpression: checkDescriptors,
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flags `<AppHeader>` and `<AppSidebar>` composed in the same file without a
|
|
3
|
+
* shared `<SidebarProvider>` ancestor. Without one, each reads a separate
|
|
4
|
+
* (or missing) sidebar context — `AppHeader`'s `isMobile` check silently
|
|
5
|
+
* reads `false` regardless of viewport, the mobile hamburger never renders,
|
|
6
|
+
* and `AppSidebar` renders its fixed desktop rail on a phone screen instead
|
|
7
|
+
* of the mobile Sheet overlay (issue #470).
|
|
8
|
+
*
|
|
9
|
+
* Either component used alone (no sidebar at all, or a sidebar-only shell)
|
|
10
|
+
* is a legitimate, documented composition and must never warn — this rule
|
|
11
|
+
* only fires when BOTH are present and don't share one provider instance.
|
|
12
|
+
*
|
|
13
|
+
* Same-file heuristic only: it cannot see a provider wrapping either
|
|
14
|
+
* component from a different file (e.g. a router rendering each at a
|
|
15
|
+
* different route level) — that case is covered by AppSidebar's own
|
|
16
|
+
* dev-mode runtime warning instead, not by static analysis.
|
|
17
|
+
*
|
|
18
|
+
* Optional `importSources`: when set, only `AppHeader`/`AppSidebar` imported
|
|
19
|
+
* from those modules count (aliases resolved), so a consumer's own component
|
|
20
|
+
* with the same name never trips it. `SidebarProvider` is still matched by
|
|
21
|
+
* bare name as well — a local re-export of the provider is a valid ancestor.
|
|
22
|
+
* Without the option (DS repo, relative imports) names match bare.
|
|
23
|
+
*/
|
|
24
|
+
import { importSourcesSchema, trackDsImports } from "./ds-imports.js";
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
meta: {
|
|
28
|
+
type: "problem",
|
|
29
|
+
docs: {
|
|
30
|
+
description:
|
|
31
|
+
"AppHeader and AppSidebar must share one SidebarProvider ancestor when both are used.",
|
|
32
|
+
},
|
|
33
|
+
messages: {
|
|
34
|
+
missingSharedProvider:
|
|
35
|
+
"<{{name}}> is composed with <{{other}}> in this file but they don't share a <SidebarProvider> ancestor — the mobile hamburger/Sheet overlay will silently fail to work. Wrap both in one shared <SidebarProvider>.",
|
|
36
|
+
},
|
|
37
|
+
schema: [importSourcesSchema],
|
|
38
|
+
},
|
|
39
|
+
create(context) {
|
|
40
|
+
const scoped = Boolean(context.options[0]?.importSources);
|
|
41
|
+
const ds = trackDsImports(context);
|
|
42
|
+
const NAMES = ["AppHeader", "AppSidebar"];
|
|
43
|
+
const otherName = (name) =>
|
|
44
|
+
name === "AppHeader" ? "AppSidebar" : "AppHeader";
|
|
45
|
+
const FUNCTION_TYPES = new Set([
|
|
46
|
+
"FunctionDeclaration",
|
|
47
|
+
"FunctionExpression",
|
|
48
|
+
"ArrowFunctionExpression",
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
// Grouping key: the nearest enclosing COMPONENT-level function (each
|
|
52
|
+
// component is its own scope — an AppHeader in one component has nothing
|
|
53
|
+
// to do with an AppSidebar in an unrelated one, even in the same file).
|
|
54
|
+
// `null` = module top level, its own scope too.
|
|
55
|
+
// A plain helper function nested inside a component (e.g.
|
|
56
|
+
// `const header = () => <AppHeader />`) is NOT a new scope — it inherits
|
|
57
|
+
// its enclosing component's scope, so JSX it returns still gets grouped
|
|
58
|
+
// with siblings rendered directly in that component.
|
|
59
|
+
// key -> { AppHeader: [{node, providerAncestors}], AppSidebar: [...] }
|
|
60
|
+
const scopes = new Map();
|
|
61
|
+
const providerStack = [];
|
|
62
|
+
const functionStack = [];
|
|
63
|
+
|
|
64
|
+
function elementName(node) {
|
|
65
|
+
const n = node.openingElement.name;
|
|
66
|
+
if (n.type !== "JSXIdentifier") return null;
|
|
67
|
+
return scoped ? ds.importedName(n) : n.name;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isProvider(node) {
|
|
71
|
+
const n = node.openingElement.name;
|
|
72
|
+
return (
|
|
73
|
+
(n.type === "JSXIdentifier" && n.name === "SidebarProvider") ||
|
|
74
|
+
elementName(node) === "SidebarProvider"
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function currentScopeKey() {
|
|
79
|
+
return functionStack.length
|
|
80
|
+
? functionStack[functionStack.length - 1]
|
|
81
|
+
: null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isComponentScope(node) {
|
|
85
|
+
if (
|
|
86
|
+
(node.type === "FunctionDeclaration" ||
|
|
87
|
+
node.type === "FunctionExpression") &&
|
|
88
|
+
node.id &&
|
|
89
|
+
/^[A-Z]/.test(node.id.name)
|
|
90
|
+
) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
const parent = node.parent;
|
|
94
|
+
return (
|
|
95
|
+
parent?.type === "VariableDeclarator" &&
|
|
96
|
+
parent.id.type === "Identifier" &&
|
|
97
|
+
/^[A-Z]/.test(parent.id.name)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function pushFn(node) {
|
|
102
|
+
if (FUNCTION_TYPES.has(node.type) && isComponentScope(node)) {
|
|
103
|
+
functionStack.push(node);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function popFn(node) {
|
|
107
|
+
if (
|
|
108
|
+
FUNCTION_TYPES.has(node.type) &&
|
|
109
|
+
functionStack[functionStack.length - 1] === node
|
|
110
|
+
) {
|
|
111
|
+
functionStack.pop();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
ImportDeclaration: ds.ImportDeclaration,
|
|
117
|
+
FunctionDeclaration: pushFn,
|
|
118
|
+
FunctionExpression: pushFn,
|
|
119
|
+
ArrowFunctionExpression: pushFn,
|
|
120
|
+
"FunctionDeclaration:exit": popFn,
|
|
121
|
+
"FunctionExpression:exit": popFn,
|
|
122
|
+
"ArrowFunctionExpression:exit": popFn,
|
|
123
|
+
|
|
124
|
+
JSXElement(node) {
|
|
125
|
+
const name = elementName(node);
|
|
126
|
+
if (isProvider(node)) providerStack.push(node);
|
|
127
|
+
if (!NAMES.includes(name)) return;
|
|
128
|
+
|
|
129
|
+
const key = currentScopeKey();
|
|
130
|
+
if (!scopes.has(key))
|
|
131
|
+
scopes.set(key, { AppHeader: [], AppSidebar: [] });
|
|
132
|
+
scopes.get(key)[name].push({
|
|
133
|
+
node,
|
|
134
|
+
providerAncestors: new Set(providerStack),
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
"JSXElement:exit"(node) {
|
|
138
|
+
if (isProvider(node)) providerStack.pop();
|
|
139
|
+
},
|
|
140
|
+
"Program:exit"() {
|
|
141
|
+
for (const occurrences of scopes.values()) {
|
|
142
|
+
for (const name of NAMES) {
|
|
143
|
+
const other = otherName(name);
|
|
144
|
+
if (occurrences[other].length === 0) continue; // used alone — fine
|
|
145
|
+
|
|
146
|
+
for (const occ of occurrences[name]) {
|
|
147
|
+
const sharesProvider = occurrences[other].some((otherOcc) =>
|
|
148
|
+
[...occ.providerAncestors].some((p) =>
|
|
149
|
+
otherOcc.providerAncestors.has(p),
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
if (!sharesProvider) {
|
|
153
|
+
context.report({
|
|
154
|
+
node: occ.node,
|
|
155
|
+
messageId: "missingSharedProvider",
|
|
156
|
+
data: { name, other },
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camunda/design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.0",
|
|
4
4
|
"description": "Camunda's AI-first design system. shadcn + Tailwind components, design tokens aligned to Camunda's identity, and AI tooling for component authoring + migration.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -29,17 +29,28 @@
|
|
|
29
29
|
"types": "./dist/carbon-compat/index.d.ts",
|
|
30
30
|
"import": "./dist/carbon-compat/index.js"
|
|
31
31
|
},
|
|
32
|
+
"./eslint-plugin": {
|
|
33
|
+
"types": "./eslint-plugin/index.d.ts",
|
|
34
|
+
"import": "./eslint-plugin/index.js"
|
|
35
|
+
},
|
|
32
36
|
"./styles.css": "./dist/styles.css"
|
|
33
37
|
},
|
|
34
38
|
"typesVersions": {
|
|
35
39
|
"*": {
|
|
36
40
|
"carbon-compat": [
|
|
37
41
|
"./dist/carbon-compat/index.d.ts"
|
|
42
|
+
],
|
|
43
|
+
"eslint-plugin": [
|
|
44
|
+
"./eslint-plugin/index.d.ts"
|
|
38
45
|
]
|
|
39
46
|
}
|
|
40
47
|
},
|
|
41
48
|
"files": [
|
|
42
49
|
"dist",
|
|
50
|
+
"eslint-plugin",
|
|
51
|
+
"!eslint-plugin/*.test.mjs",
|
|
52
|
+
"eslint-rules",
|
|
53
|
+
"!eslint-rules/*.test.mjs",
|
|
43
54
|
"README.md",
|
|
44
55
|
"LICENSE",
|
|
45
56
|
"NOTICE"
|
|
@@ -73,7 +84,7 @@
|
|
|
73
84
|
"audit:conventions": "node scripts/audit-conventions.mjs",
|
|
74
85
|
"mapping:build": "bash skills/migrate/scripts/build-mapping.sh",
|
|
75
86
|
"mapping:check": "bash skills/migrate/scripts/build-mapping.sh --check",
|
|
76
|
-
"test:rules": "node eslint-rules/no-adhoc-typography.test.mjs && node eslint-rules/no-primitive-tailwind-colors.test.mjs && node eslint-rules/no-raw-color-in-classname.test.mjs && node eslint-rules/require-shared-sidebar-provider.test.mjs && node eslint-rules/no-direct-lucide-import.test.mjs && node eslint-rules/no-
|
|
87
|
+
"test:rules": "node eslint-rules/no-adhoc-typography.test.mjs && node eslint-rules/no-primitive-tailwind-colors.test.mjs && node eslint-rules/no-raw-color-in-classname.test.mjs && node eslint-rules/require-shared-sidebar-provider.test.mjs && node eslint-rules/no-hardcoded-user-facing-string.test.mjs && node eslint-rules/no-direct-lucide-import.test.mjs && node eslint-rules/no-deep-import.test.mjs && node eslint-rules/button-has-type.test.mjs && node eslint-rules/icon-button-needs-label.test.mjs && node eslint-rules/prefer-link-props.test.mjs && node eslint-rules/require-browsable-link.test.mjs && node eslint-rules/no-hardcoded-i18n-prop.test.mjs && node --test eslint-plugin/index.test.mjs src/icons/registry.integrity.test.mjs src/lib/trigger-ref.integrity.test.mjs src/lib/i18n/dictionary.test.mjs src/lib/i18n/dictionary-usage.test.mjs src/lib/i18n/context.test.mjs",
|
|
77
88
|
"test:hooks": "python3 plugins/camunda-design-system/scripts/route-request.test.py",
|
|
78
89
|
"prepare": "husky",
|
|
79
90
|
"release": "changeset publish",
|
|
@@ -93,11 +104,15 @@
|
|
|
93
104
|
"peerDependencies": {
|
|
94
105
|
"@carbon/react": "^1.107.0",
|
|
95
106
|
"react": ">=19.0.0 <20",
|
|
96
|
-
"react-dom": ">=19.0.0 <20"
|
|
107
|
+
"react-dom": ">=19.0.0 <20",
|
|
108
|
+
"eslint": ">=9.0.0"
|
|
97
109
|
},
|
|
98
110
|
"peerDependenciesMeta": {
|
|
99
111
|
"@carbon/react": {
|
|
100
112
|
"optional": true
|
|
113
|
+
},
|
|
114
|
+
"eslint": {
|
|
115
|
+
"optional": true
|
|
101
116
|
}
|
|
102
117
|
},
|
|
103
118
|
"dependencies": {
|