@nadicodeai/ui 0.19.2 → 0.21.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.
Files changed (69) hide show
  1. package/AGENTS.md +3 -1
  2. package/README.md +0 -2
  3. package/dist/components/brand-icons/apple.d.ts +18 -0
  4. package/dist/components/brand-icons/apple.d.ts.map +1 -0
  5. package/dist/components/brand-icons/apple.js +22 -0
  6. package/dist/components/brand-icons/brand-icon.d.ts +22 -0
  7. package/dist/components/brand-icons/brand-icon.d.ts.map +1 -0
  8. package/dist/components/brand-icons/brand-icon.js +26 -0
  9. package/dist/components/brand-icons/linux.d.ts +18 -0
  10. package/dist/components/brand-icons/linux.d.ts.map +1 -0
  11. package/dist/components/brand-icons/linux.js +25 -0
  12. package/dist/components/brand-icons/windows.d.ts +18 -0
  13. package/dist/components/brand-icons/windows.d.ts.map +1 -0
  14. package/dist/components/brand-icons/windows.js +22 -0
  15. package/dist/components/brand-icons.d.ts +5 -0
  16. package/dist/components/brand-icons.d.ts.map +1 -0
  17. package/dist/components/brand-icons.js +10 -0
  18. package/dist/components/button.js +1 -1
  19. package/dist/components/card-marketing.js +2 -2
  20. package/dist/components/card.d.ts +4 -3
  21. package/dist/components/card.d.ts.map +1 -1
  22. package/dist/components/card.js +12 -2
  23. package/dist/components/chart-breakdown-table.js +1 -1
  24. package/dist/components/chart-bullet-bar.d.ts +18 -2
  25. package/dist/components/chart-bullet-bar.d.ts.map +1 -1
  26. package/dist/components/chart-bullet-bar.js +62 -9
  27. package/dist/components/chart-date-range-control.d.ts +11 -1
  28. package/dist/components/chart-date-range-control.d.ts.map +1 -1
  29. package/dist/components/chart-date-range-control.js +13 -3
  30. package/dist/components/chart-entity-colors.d.ts +7 -1
  31. package/dist/components/chart-entity-colors.d.ts.map +1 -1
  32. package/dist/components/chart-entity-colors.js +7 -1
  33. package/dist/components/chart-kpi-stat.d.ts.map +1 -1
  34. package/dist/components/chart-kpi-stat.js +1 -1
  35. package/dist/components/chart-time-series.d.ts.map +1 -1
  36. package/dist/components/chart-time-series.js +62 -4
  37. package/dist/components/chart.js +2 -2
  38. package/dist/components/code-editor-mockup.js +1 -1
  39. package/dist/components/nav-bar.js +1 -1
  40. package/dist/components/radio-group.d.ts +1 -1
  41. package/dist/components/radio-group.d.ts.map +1 -1
  42. package/dist/components/radio-group.js +2 -2
  43. package/dist/components/theme-mode-switcher.d.ts +10 -3
  44. package/dist/components/theme-mode-switcher.d.ts.map +1 -1
  45. package/dist/components/theme-mode-switcher.js +14 -7
  46. package/dist/eslint/index.js +91 -0
  47. package/dist/eslint/rules/jsx-class-utility.js +231 -0
  48. package/dist/eslint/rules/no-deprecated-zod-api.js +156 -0
  49. package/dist/eslint/rules/no-design-system-src-import.js +11 -0
  50. package/dist/eslint/rules/no-invalid-token-utility.js +46 -0
  51. package/dist/eslint/rules/no-native-visible-control.js +86 -0
  52. package/dist/eslint/rules/no-raw-color-utility.js +109 -0
  53. package/dist/eslint/rules/no-raw-focus-ring-width.js +30 -0
  54. package/dist/eslint/rules/no-raw-logo-import.js +41 -0
  55. package/dist/eslint/rules/no-shadcn-appearance-override.js +191 -0
  56. package/dist/eslint/rules/no-token-category-bypass.js +53 -0
  57. package/dist/eslint/rules/no-ui-src-import.js +10 -0
  58. package/dist/eslint/rules/no-unpinned-vercel-functions-import.js +100 -0
  59. package/dist/eslint/rules/src-import-boundary.js +207 -0
  60. package/dist/index.d.ts +1 -0
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +1 -0
  63. package/docs/agents/nadicodeai-ui.md +0 -2
  64. package/docs/consuming-cross-repo.md +31 -3
  65. package/docs/contract.md +11 -1
  66. package/llms.txt +0 -1
  67. package/package.json +9 -3
  68. package/skills/nadicodeai-ui/SKILL.md +1 -1
  69. package/docs/migration/nadia-consumer-readiness.md +0 -180
@@ -0,0 +1,41 @@
1
+ import { createSrcImportRule } from "./src-import-boundary.js";
2
+
3
+ const BANNED_PATTERNS = [
4
+ "@nadicodeai/design-system/assets/logo-mark",
5
+ "@nadicodeai/design-system/assets/logo-wordmark",
6
+ "@nadicodeai/design-system/assets/logo-geometry",
7
+ "logo-mark.svg",
8
+ "logo-wordmark.svg",
9
+ ];
10
+
11
+ // Only the single brand adapter in @nadicodeai/ui may import the raw logo
12
+ // assets. A bare "/components/brand.tsx" suffix would also whitelist a future
13
+ // website/portal `components/brand.tsx`, letting an app bypass BrandMark —
14
+ // pin the full package-relative path instead.
15
+ const ALLOWED_SUFFIXES = ["@nadicodeai/ui/src/components/brand.tsx"];
16
+
17
+ function isAllowedFile(filename) {
18
+ const normalized = filename.replaceAll("\\", "/");
19
+ return ALLOWED_SUFFIXES.some((suffix) => normalized.endsWith(suffix));
20
+ }
21
+
22
+ function matchesBanned(value) {
23
+ return typeof value === "string" && BANNED_PATTERNS.some((pattern) => value.includes(pattern));
24
+ }
25
+
26
+ const rawLogoImportRule = createSrcImportRule({
27
+ description: "Disallow direct logo asset imports outside the BrandMark/BrandWordmark adapter",
28
+ bannedMessage:
29
+ "Import logo assets only through BrandMark/BrandWordmark from @nadicodeai/ui/components/brand (found {{value}}).",
30
+ isBanned: matchesBanned,
31
+ });
32
+
33
+ export const noRawLogoImport = {
34
+ ...rawLogoImportRule,
35
+ create(context) {
36
+ if (isAllowedFile(context.filename)) {
37
+ return {};
38
+ }
39
+ return rawLogoImportRule.create(context);
40
+ },
41
+ };
@@ -0,0 +1,191 @@
1
+ import {
2
+ classUtilities,
3
+ isPackageOwnedPrimitive,
4
+ openingElementClassUtilities,
5
+ } from "./jsx-class-utility.js";
6
+
7
+ function withoutVariants(utility) {
8
+ return utility.slice(utility.lastIndexOf(":") + 1);
9
+ }
10
+
11
+ function isTypographySize(utility) {
12
+ return /^text-(?:xs|sm|base|lg|xl|[2-9]xl|\[[\d.]+(?:px|rem|em|%|lh)\])$/.test(utility);
13
+ }
14
+
15
+ function isTextLayoutUtility(utility) {
16
+ return /^text-(?:left|center|right|justify|start|end|balance|pretty|clip|ellipsis)$/.test(
17
+ utility,
18
+ );
19
+ }
20
+
21
+ const COLOR_NAMES = new Set([
22
+ "black",
23
+ "white",
24
+ "slate",
25
+ "gray",
26
+ "zinc",
27
+ "neutral",
28
+ "stone",
29
+ "red",
30
+ "orange",
31
+ "amber",
32
+ "yellow",
33
+ "lime",
34
+ "green",
35
+ "emerald",
36
+ "teal",
37
+ "cyan",
38
+ "sky",
39
+ "blue",
40
+ "indigo",
41
+ "violet",
42
+ "purple",
43
+ "fuchsia",
44
+ "pink",
45
+ "rose",
46
+ "nc",
47
+ "background",
48
+ "foreground",
49
+ "card",
50
+ "popover",
51
+ "primary",
52
+ "secondary",
53
+ "muted",
54
+ "accent",
55
+ "destructive",
56
+ "border",
57
+ "input",
58
+ "ring",
59
+ "focus",
60
+ ]);
61
+
62
+ function hasColorValue(utility, prefix) {
63
+ if (!utility.startsWith(prefix)) {
64
+ return false;
65
+ }
66
+ const value = utility.slice(prefix.length).split("/")[0];
67
+ return value.startsWith("[") || COLOR_NAMES.has(value.split("-")[0]);
68
+ }
69
+
70
+ function isBorderMechanic(utility) {
71
+ return /^(?:border(?:-(?:[xy]|[trblse]{1,2}|[0-9]+|px|dashed|dotted|double|hidden|none))?)$/.test(
72
+ utility,
73
+ );
74
+ }
75
+
76
+ function isBorderRemoval(utility) {
77
+ return /^border(?:-[xytrblse]{1,2})?-(?:0|none|\[(?:length:)?0(?:px|rem|em|%)?\])$/.test(utility);
78
+ }
79
+
80
+ function isRingOrOutlineMechanic(utility) {
81
+ return /^(?:ring(?:-(?:[0-9]+|inset|offset(?:-[0-9]+)?))?|outline(?:-(?:none|[0-9]+|offset-[0-9]+))?)$/.test(
82
+ utility,
83
+ );
84
+ }
85
+
86
+ function isAppearanceUtility(utility) {
87
+ const base = withoutVariants(utility);
88
+ return (
89
+ /^appearance-/.test(base) ||
90
+ /^bg-/.test(base) ||
91
+ isBorderRemoval(base) ||
92
+ (base.startsWith("border") && !isBorderMechanic(base) && hasColorValue(base, "border-")) ||
93
+ /^shadow(?:-|$)/.test(base) ||
94
+ /^rounded(?:-|$)/.test(base) ||
95
+ ((base.startsWith("ring") || base.startsWith("outline")) &&
96
+ !isRingOrOutlineMechanic(base) &&
97
+ (hasColorValue(base, "ring-") || hasColorValue(base, "outline-"))) ||
98
+ /^opacity-/.test(base) ||
99
+ (/^text-/.test(base) && !isTypographySize(base) && !isTextLayoutUtility(base))
100
+ );
101
+ }
102
+
103
+ function isProhibitedAppearanceUtility(utility, { cardRoot = false } = {}) {
104
+ const base = withoutVariants(utility);
105
+ return isAppearanceUtility(utility) && (!isBorderRemoval(base) || cardRoot);
106
+ }
107
+
108
+ function targetsSharedSlot(utility) {
109
+ return utility.includes("data-slot") || utility.includes("data-[slot");
110
+ }
111
+
112
+ function targetsCardSlot(utility) {
113
+ return (
114
+ /data-slot=(?:card|["']card["'])(?=\])/.test(utility) ||
115
+ /data-\[slot=(?:card|["']card["'])\]/.test(utility)
116
+ );
117
+ }
118
+
119
+ export const noShadcnAppearanceOverride = {
120
+ meta: {
121
+ type: "problem",
122
+ docs: {
123
+ description:
124
+ "Disallow app callers repainting imported shadcn primitives; compose their structural layout only",
125
+ },
126
+ schema: [],
127
+ messages: {
128
+ appearanceOverride:
129
+ "Do not repaint an imported @nadicodeai/ui primitive with {{utility}}; use its semantic variant or package primitive.",
130
+ },
131
+ },
132
+ create(context) {
133
+ if (isPackageOwnedPrimitive(context.filename)) {
134
+ return {};
135
+ }
136
+
137
+ const sharedPrimitiveNames = new Set();
138
+ const sharedCardRootNames = new Set();
139
+ const isSharedPrimitive = (node) =>
140
+ node?.type === "JSXOpeningElement" &&
141
+ node.name.type === "JSXIdentifier" &&
142
+ sharedPrimitiveNames.has(node.name.name);
143
+ const isSharedCardRoot = (node) =>
144
+ node?.type === "JSXOpeningElement" &&
145
+ node.name.type === "JSXIdentifier" &&
146
+ sharedCardRootNames.has(node.name.name);
147
+ return {
148
+ ImportDeclaration(node) {
149
+ if (
150
+ typeof node.source.value !== "string" ||
151
+ !node.source.value.startsWith("@nadicodeai/ui/components/")
152
+ ) {
153
+ return;
154
+ }
155
+ for (const specifier of node.specifiers) {
156
+ sharedPrimitiveNames.add(specifier.local.name);
157
+ if (
158
+ node.source.value === "@nadicodeai/ui/components/card" &&
159
+ specifier.type === "ImportSpecifier" &&
160
+ specifier.imported.name === "Card"
161
+ ) {
162
+ sharedCardRootNames.add(specifier.local.name);
163
+ }
164
+ }
165
+ },
166
+ JSXOpeningElement(node) {
167
+ if (!isSharedPrimitive(node)) return;
168
+ for (const utility of openingElementClassUtilities(node, context)) {
169
+ if (
170
+ utility &&
171
+ isProhibitedAppearanceUtility(utility, { cardRoot: isSharedCardRoot(node) })
172
+ ) {
173
+ context.report({ node, messageId: "appearanceOverride", data: { utility } });
174
+ }
175
+ }
176
+ },
177
+ JSXAttribute(node) {
178
+ if (isSharedPrimitive(node.parent)) return;
179
+ for (const utility of classUtilities(node, context)) {
180
+ if (
181
+ !targetsSharedSlot(utility) ||
182
+ !isProhibitedAppearanceUtility(utility, { cardRoot: targetsCardSlot(utility) })
183
+ ) {
184
+ continue;
185
+ }
186
+ context.report({ node, messageId: "appearanceOverride", data: { utility } });
187
+ }
188
+ },
189
+ };
190
+ },
191
+ };
@@ -0,0 +1,53 @@
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 borderRadius = require("@nadicodeai/design-system/tailwind").theme.extend.borderRadius;
7
+ const radiusNameByValue = new Map(
8
+ Object.entries(borderRadius).map(([name, value]) => [value, name]),
9
+ );
10
+ const ARBITRARY_RADIUS = /^(rounded(?:-(?:t|r|b|l|s|e|ss|se|ee|es|tl|tr|br|bl))?)-\[(.+)\]$/;
11
+ const CANONICAL_RADIUS_VARIABLE = /^var\(--(?:nc-rounded|radius)-([a-z0-9-]+)\)$/;
12
+
13
+ function canonicalRadiusReplacement(utility) {
14
+ const variantBoundary = utility.lastIndexOf(":") + 1;
15
+ const variants = utility.slice(0, variantBoundary);
16
+ const unqualified = utility.slice(variantBoundary);
17
+ const match = ARBITRARY_RADIUS.exec(unqualified);
18
+ if (!match) return undefined;
19
+
20
+ const [, prefix, rawValue] = match;
21
+ const value = rawValue.trim();
22
+ const variable = CANONICAL_RADIUS_VARIABLE.exec(value);
23
+ const token = variable?.[1] ?? radiusNameByValue.get(value);
24
+ if (!token || !(token in borderRadius)) return undefined;
25
+
26
+ return `${variants}${prefix}-${token}`;
27
+ }
28
+
29
+ export const noTokenCategoryBypass = {
30
+ meta: {
31
+ type: "problem",
32
+ docs: {
33
+ description:
34
+ "Disallow arbitrary radius values when the generated design system provides an exact utility",
35
+ },
36
+ schema: [],
37
+ messages: {
38
+ canonicalTokenUtility:
39
+ "Replace {{utility}} with the generated design-system utility {{replacement}}.",
40
+ },
41
+ },
42
+ create(context) {
43
+ return tokenUtilityVisitors(context, (node, utility) => {
44
+ const replacement = canonicalRadiusReplacement(utility);
45
+ if (!replacement) return;
46
+ context.report({
47
+ node,
48
+ messageId: "canonicalTokenUtility",
49
+ data: { replacement, utility },
50
+ });
51
+ });
52
+ },
53
+ };
@@ -0,0 +1,10 @@
1
+ import { bannedSourceRootMatcher, createSrcImportRule } from "./src-import-boundary.js";
2
+
3
+ /** @type {import('eslint').Rule.RuleModule} */
4
+ export const noUiSrcImport = createSrcImportRule({
5
+ description: "Disallow deep imports from @nadicodeai/ui/src/* in consumer apps",
6
+ bannedMessage: "Import @nadicodeai/ui through package exports only, not {{value}}.",
7
+ // The package sits at a repo-root dir literally named `@nadicodeai/ui/`, so
8
+ // relative paths carry the full `@nadicodeai/ui/src` marker too.
9
+ isBanned: bannedSourceRootMatcher("@nadicodeai/ui/src"),
10
+ });
@@ -0,0 +1,100 @@
1
+ const PACKAGE_NAME = "@vercel/functions";
2
+
3
+ // Third-party helpers stay behind owned, single-purpose capability seams. Pin
4
+ // the full workspace-relative suffix so a same-named file in another workspace
5
+ // cannot acquire a platform dependency by shadowing an allowed module.
6
+ const ALLOWED_IMPORTS = new Map([
7
+ ["portal/src/lib/server/db.ts", new Set(["attachDatabasePool"])],
8
+ ["portal/src/lib/server/deferred-work.ts", new Set(["waitUntil"])],
9
+ ]);
10
+
11
+ function matchesPackage(value) {
12
+ return (
13
+ typeof value === "string" && (value === PACKAGE_NAME || value.startsWith(`${PACKAGE_NAME}/`))
14
+ );
15
+ }
16
+
17
+ function allowedSymbolsFor(filename) {
18
+ const normalized = filename.replaceAll("\\", "/");
19
+ for (const [suffix, symbols] of ALLOWED_IMPORTS) {
20
+ if (normalized.endsWith(suffix)) {
21
+ return symbols;
22
+ }
23
+ }
24
+ return undefined;
25
+ }
26
+
27
+ function importedName(specifier) {
28
+ if (specifier.type !== "ImportSpecifier") {
29
+ return undefined;
30
+ }
31
+ return specifier.imported.type === "Identifier"
32
+ ? specifier.imported.name
33
+ : specifier.imported.value;
34
+ }
35
+
36
+ /** @type {import('eslint').Rule.RuleModule} */
37
+ export const noUnpinnedVercelFunctionsImport = {
38
+ meta: {
39
+ type: "problem",
40
+ docs: {
41
+ description: "Confine @vercel/functions imports to pinned edge modules and symbols",
42
+ },
43
+ messages: {
44
+ banned:
45
+ "@vercel/functions duck-types its inputs and ipAddress() crashed production once (docs/solutions/portal-device-authorize-vercel-headers-crash.md). Its use is pinned per module and per symbol; amend the allowlist deliberately or keep the dependency out (found {{value}}).",
46
+ },
47
+ schema: [],
48
+ },
49
+ create(context) {
50
+ const allowedSymbols = allowedSymbolsFor(context.filename);
51
+
52
+ function report(node, value) {
53
+ context.report({
54
+ node,
55
+ messageId: "banned",
56
+ data: { value },
57
+ });
58
+ }
59
+
60
+ return {
61
+ ImportDeclaration(node) {
62
+ if (!matchesPackage(node.source.value)) {
63
+ return;
64
+ }
65
+
66
+ const importsOnlyPinnedSymbols =
67
+ allowedSymbols &&
68
+ node.specifiers.length > 0 &&
69
+ node.specifiers.every((specifier) => {
70
+ const name = importedName(specifier);
71
+ return name !== undefined && allowedSymbols.has(name);
72
+ });
73
+
74
+ if (!importsOnlyPinnedSymbols) {
75
+ report(node, node.source.value);
76
+ }
77
+ },
78
+ // Everything except a static import is judged by the quoted reference
79
+ // itself, whatever expression carries it: dynamic import(), require in
80
+ // any callee shape (bare, module.require, createRequire), export ...
81
+ // from, or a bare string that smuggles the specifier to a loader. Only
82
+ // static named imports can prove which symbols they bind, so only they
83
+ // get the allowlist path above.
84
+ Literal(node) {
85
+ if (!matchesPackage(node.value)) {
86
+ return;
87
+ }
88
+ if (node.parent.type === "ImportDeclaration") {
89
+ return;
90
+ }
91
+ report(node, node.value);
92
+ },
93
+ TemplateLiteral(node) {
94
+ if (node.expressions.length === 0 && matchesPackage(node.quasis[0]?.value.cooked)) {
95
+ report(node, node.quasis[0].value.cooked);
96
+ }
97
+ },
98
+ };
99
+ },
100
+ };
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Shared machinery for the source-import boundary rules
3
+ * (`no-ui-src-import`, `no-design-system-src-import`). Both resolve a module
4
+ * specifier — static string, template/concatenation, const-propagated
5
+ * identifier, TS type assertion — and report when a banned package source
6
+ * path appears in it, across static imports, dynamic `import()`,
7
+ * `require()`/`module.require()`, and TypeScript `import type`/`import =`.
8
+ * Factoring it here keeps the two rules from drifting apart.
9
+ */
10
+
11
+ /**
12
+ * Match a banned source root anywhere in the specifier, at a path boundary:
13
+ * the root followed by `/` (a deep import) or at the end of the string (the
14
+ * root itself). Substring-anywhere so relative paths (`../../@scope/pkg/src/x`),
15
+ * node_modules paths, and loader-prefixed specifiers (`!!raw-loader!…/src/x`)
16
+ * are all caught.
17
+ * @param {string} root
18
+ * @returns {(value: string) => boolean}
19
+ */
20
+ export function bannedSourceRootMatcher(root) {
21
+ return (value) => {
22
+ const index = value.indexOf(root);
23
+ if (index === -1) {
24
+ return false;
25
+ }
26
+ const nextChar = value[index + root.length];
27
+ return nextChar === undefined || nextChar === "/";
28
+ };
29
+ }
30
+
31
+ /**
32
+ * @param {object} options
33
+ * @param {string} options.description
34
+ * @param {string} options.bannedMessage
35
+ * @param {(value: string) => boolean} options.isBanned
36
+ * @returns {import('eslint').Rule.RuleModule}
37
+ */
38
+ export function createSrcImportRule({ description, bannedMessage, isBanned }) {
39
+ return {
40
+ meta: {
41
+ type: "problem",
42
+ docs: { description },
43
+ messages: { banned: bannedMessage },
44
+ schema: [],
45
+ },
46
+ create(context) {
47
+ const sourceCode = context.sourceCode;
48
+
49
+ function findVariable(node, name) {
50
+ for (let scope = sourceCode.getScope(node); scope; scope = scope.upper) {
51
+ const variable = scope.set.get(name);
52
+ if (variable) {
53
+ return variable;
54
+ }
55
+ }
56
+ return undefined;
57
+ }
58
+
59
+ function isUnshadowed(node, name) {
60
+ const variable = findVariable(node, name);
61
+ return !variable || variable.defs.length === 0;
62
+ }
63
+
64
+ // `const require = createRequire(import.meta.url)` is the only real
65
+ // require in this all-ESM repo, so it must not be treated as a shadow.
66
+ function isCreateRequireInit(init) {
67
+ if (init?.type !== "CallExpression") {
68
+ return false;
69
+ }
70
+ const callee = init.callee;
71
+ if (callee.type === "Identifier") {
72
+ return callee.name === "createRequire";
73
+ }
74
+ return (
75
+ callee.type === "MemberExpression" &&
76
+ !callee.computed &&
77
+ callee.property.type === "Identifier" &&
78
+ callee.property.name === "createRequire"
79
+ );
80
+ }
81
+
82
+ function isRequire(node, name) {
83
+ const variable = findVariable(node, name);
84
+ if (!variable || variable.defs.length === 0) {
85
+ return true;
86
+ }
87
+ if (variable.defs.length === 1) {
88
+ const definition = variable.defs[0];
89
+ if (
90
+ definition.type === "Variable" &&
91
+ definition.parent?.kind === "const" &&
92
+ definition.node.id?.type === "Identifier" &&
93
+ isCreateRequireInit(definition.node.init)
94
+ ) {
95
+ return true;
96
+ }
97
+ }
98
+ return false;
99
+ }
100
+
101
+ function staticSpecifier(node, seenVariables = new Set()) {
102
+ if (node?.type === "Literal" && typeof node.value === "string") {
103
+ return node.value;
104
+ }
105
+ if (node?.type === "TemplateLiteral") {
106
+ let value = node.quasis[0]?.value.cooked ?? "";
107
+ for (let index = 0; index < node.expressions.length; index += 1) {
108
+ const expressionValue = staticSpecifier(node.expressions[index], seenVariables);
109
+ const followingText = node.quasis[index + 1]?.value.cooked;
110
+ if (expressionValue === undefined || followingText === undefined) {
111
+ return undefined;
112
+ }
113
+ value += expressionValue + followingText;
114
+ }
115
+ return value;
116
+ }
117
+ if (node?.type === "BinaryExpression" && node.operator === "+") {
118
+ const left = staticSpecifier(node.left, seenVariables);
119
+ const right = staticSpecifier(node.right, seenVariables);
120
+ return left === undefined || right === undefined ? undefined : left + right;
121
+ }
122
+ if (node?.type === "Identifier") {
123
+ const variable = findVariable(node, node.name);
124
+ const definition = variable?.defs.length === 1 ? variable.defs[0] : undefined;
125
+ if (
126
+ !variable ||
127
+ seenVariables.has(variable) ||
128
+ definition?.type !== "Variable" ||
129
+ definition.parent?.kind !== "const" ||
130
+ definition.node.id?.type !== "Identifier" ||
131
+ !definition.node.init
132
+ ) {
133
+ return undefined;
134
+ }
135
+
136
+ const nextSeenVariables = new Set(seenVariables);
137
+ nextSeenVariables.add(variable);
138
+ return staticSpecifier(definition.node.init, nextSeenVariables);
139
+ }
140
+ if (
141
+ node?.type === "TSAsExpression" ||
142
+ node?.type === "TSSatisfiesExpression" ||
143
+ node?.type === "TSNonNullExpression"
144
+ ) {
145
+ return staticSpecifier(node.expression, seenVariables);
146
+ }
147
+ return undefined;
148
+ }
149
+
150
+ function checkValue(node, value) {
151
+ if (typeof value === "string" && isBanned(value)) {
152
+ context.report({
153
+ node,
154
+ messageId: "banned",
155
+ data: { value },
156
+ });
157
+ }
158
+ }
159
+
160
+ function check(node) {
161
+ checkValue(node, staticSpecifier(node.source));
162
+ }
163
+
164
+ function memberPropertyName(node) {
165
+ if (!node.computed && node.property.type === "Identifier") {
166
+ return node.property.name;
167
+ }
168
+ return staticSpecifier(node.property);
169
+ }
170
+
171
+ function checkRequire(node) {
172
+ const isBareRequire =
173
+ node.callee.type === "Identifier" &&
174
+ node.callee.name === "require" &&
175
+ isRequire(node.callee, "require");
176
+ const isModuleRequire =
177
+ node.callee.type === "MemberExpression" &&
178
+ node.callee.object.type === "Identifier" &&
179
+ node.callee.object.name === "module" &&
180
+ isUnshadowed(node.callee.object, "module") &&
181
+ memberPropertyName(node.callee) === "require";
182
+
183
+ if (isBareRequire || isModuleRequire) {
184
+ checkValue(node, staticSpecifier(node.arguments[0]));
185
+ }
186
+ }
187
+
188
+ function checkTypeScriptImportEquals(node) {
189
+ if (node.moduleReference.type === "TSExternalModuleReference") {
190
+ checkValue(node, staticSpecifier(node.moduleReference.expression));
191
+ }
192
+ }
193
+
194
+ return {
195
+ ImportDeclaration: check,
196
+ ImportExpression: check,
197
+ CallExpression: checkRequire,
198
+ // `export { X } from "…/src/…"` and `export * from …` are consumer deep
199
+ // imports too; a source-less `export {}` has no node.source.
200
+ ExportNamedDeclaration: check,
201
+ ExportAllDeclaration: check,
202
+ TSImportType: check,
203
+ TSImportEqualsDeclaration: checkTypeScriptImportEquals,
204
+ };
205
+ },
206
+ };
207
+ }
package/dist/index.d.ts CHANGED
@@ -19,6 +19,7 @@ export * from "./components/benefits-bento";
19
19
  export * from "./components/bento";
20
20
  export * from "./components/brand";
21
21
  export * from "./components/brand-field";
22
+ export * from "./components/brand-icons";
22
23
  export * from "./components/breadcrumb";
23
24
  export * from "./components/bubble";
24
25
  export * from "./components/button";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC;AAChD,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mCAAmC,CAAC;AAClD,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ export * from "./components/benefits-bento.js";
19
19
  export * from "./components/bento.js";
20
20
  export * from "./components/brand.js";
21
21
  export * from "./components/brand-field.js";
22
+ export * from "./components/brand-icons.js";
22
23
  export * from "./components/breadcrumb.js";
23
24
  export * from "./components/bubble.js";
24
25
  export * from "./components/button.js";
@@ -24,8 +24,6 @@ in agent prose. Follow the owning source or document above.
24
24
  - Product compositions and sections:
25
25
  [`../product-compositions.md`](../product-compositions.md) and
26
26
  [`../product-sections.md`](../product-sections.md).
27
- - Consumer order, fixture redaction, and readiness gates:
28
- [`../migration/nadia-consumer-readiness.md`](../migration/nadia-consumer-readiness.md).
29
27
  - Terminal and TUI semantics:
30
28
  [`../terminal-tui-semantics.md`](../terminal-tui-semantics.md).
31
29