basalt-ui 1.19.0 → 1.20.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 +15 -1
- package/CHANGELOG.md +27 -0
- package/MIGRATING.md +228 -0
- package/README.md +53 -9
- package/agent/rules/basalt-app.md +43 -0
- package/agent/rules/basalt-charts.md +26 -14
- package/agent/rules/basalt-mantine.md +25 -10
- package/agent/rules/basalt-router.md +22 -10
- package/agent/rules/basalt-tokens.md +75 -6
- package/agent/skills/basalt-app/SKILL.md +42 -6
- package/configs/check.yml +11 -4
- package/configs/lefthook.yml +21 -2
- package/configs/oxlint-plugin.js +515 -25
- package/configs/oxlint-plugin.test.ts +383 -10
- package/configs/oxlint.json +10 -1
- package/dist/agent/stick-to-bottom.js +1 -1
- package/dist/agent-chat/thread-detail-panel.js +1 -1
- package/dist/agent-chat/thread-message.js +2 -2
- package/dist/agent-chat/tool-chip.js +1 -1
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +1 -1
- package/dist/charts/primitives/ChartLegend.js +2 -2
- package/dist/charts/primitives/ChartTooltip.js +1 -1
- package/dist/cli/index.d.ts +107 -15
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +610 -72
- package/dist/cli/toolchain-wiring.test.d.ts +2 -0
- package/dist/cli/toolchain-wiring.test.d.ts.map +1 -0
- package/dist/dashboard/delta-badge.js +2 -2
- package/dist/dashboard/stat-card.js +1 -1
- package/dist/data/virtual-list.js +5 -5
- package/dist/guard/index.d.ts +56 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +326 -59
- package/dist/guard/types.d.ts +25 -5
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/register.d.ts +25 -0
- package/dist/register.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +14 -14
- package/dist/shell/app-mobile-nav.module.css +44 -11
- package/dist/shell/app-mobile-nav.module.css.d.ts +1 -1
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.js +1 -1
- package/dist/tokens/index.d.ts +3 -3
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +2 -0
- package/dist/tokens/palette.d.ts +35 -27
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +24 -18
- package/dist/tokens.css +19 -17
- package/dist/vite-color-mix.js +2 -2
- package/llms.txt +3 -3
- package/package.json +4 -3
- package/src/agent/stick-to-bottom.tsx +1 -1
- package/src/agent-chat/thread-detail-panel.tsx +1 -1
- package/src/agent-chat/thread-message.tsx +3 -3
- package/src/agent-chat/tool-chip.tsx +1 -1
- package/src/charts/kinds/Donut.tsx +2 -1
- package/src/charts/primitives/ChartLegend.tsx +2 -2
- package/src/charts/primitives/ChartTooltip.tsx +1 -1
- package/src/cli/index.ts +1070 -99
- package/src/cli/init-sync.test.ts +19 -3
- package/src/cli/tokens-css.test.ts +27 -14
- package/src/cli/toolchain-wiring.test.ts +694 -0
- package/src/dashboard/delta-badge.tsx +3 -3
- package/src/dashboard/stat-card.tsx +1 -1
- package/src/data/virtual-list.tsx +8 -8
- package/src/guard/check-source.test.ts +663 -11
- package/src/guard/index.ts +727 -94
- package/src/guard/types.ts +29 -4
- package/src/register.ts +32 -0
- package/src/shell/app-mobile-nav.module.css +44 -11
- package/src/shell/app-mobile-nav.module.css.d.ts +1 -1
- package/src/shell/app-mobile-nav.test.tsx +34 -0
- package/src/shell/app-mobile-nav.tsx +16 -5
- package/src/surfaces.ts +1 -1
- package/src/theme/density-relations.test.ts +8 -5
- package/src/theme/spacing.test.ts +2 -0
- package/src/theme-toggle/index.tsx +2 -2
- package/src/tokens/index.ts +5 -3
- package/src/tokens/palette.ts +24 -18
- package/src/vite-color-mix.ts +2 -2
package/dist/guard/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const HEX = /#[0-9a-fA-F]{3,8}\b/g;
|
|
2
|
-
const FUNC = /\b(?:rgba?|hsla?)\(/g;
|
|
2
|
+
const FUNC = /\b(?:rgba?|hsla?)\((?!\s*\$\{)/g;
|
|
3
3
|
const LOCALSTORAGE_THEME = /localStorage\s*\.\s*getItem\s*\(\s*['"]theme['"]\s*\)/g;
|
|
4
|
-
const RAW_FONT_FAMILY_VAR_ESCAPE = /var\(\s
|
|
4
|
+
const RAW_FONT_FAMILY_VAR_ESCAPE = /var\(\s*--/.source;
|
|
5
5
|
const RAW_FONT_FAMILY_KEYWORD = /(?:inherit|initial|unset|revert)\b/.source;
|
|
6
6
|
const RAW_FONT_FAMILY = new RegExp(
|
|
7
7
|
`\\bfontFamily\\s*[:=]\\s*\\{?(?!['"\`]?\\s*${RAW_FONT_FAMILY_VAR_ESCAPE})(?!['"\`]?\\s*${RAW_FONT_FAMILY_KEYWORD})['"\`][^'"\`]+['"\`]|\\bfont-family\\s*:\\s*(?!['"\`]?\\s*${RAW_FONT_FAMILY_VAR_ESCAPE})(?!['"\`]?\\s*${RAW_FONT_FAMILY_KEYWORD})(?:['"\`][^'"\`]+['"\`]|[A-Za-z][\\w-]*)`,
|
|
@@ -9,14 +9,42 @@ const RAW_FONT_FAMILY = new RegExp(
|
|
|
9
9
|
);
|
|
10
10
|
const SURFACE_BORDER = /\bborder(?:Top|Bottom|Left|Right)?\s*:\s*(?!['"`]?[^'"`]*(?:var\(|\$\{))(?!['"`]?(?:none|transparent|inherit|unset|revert)\b)['"`][^'"`]+['"`]/g;
|
|
11
11
|
const SURFACE_RADIUS = /\bborderRadius\s*:\s*(?:[0-9]+|['"`](?!\s*var\()[^'"`]*[0-9])/g;
|
|
12
|
-
const
|
|
12
|
+
const SHADOW_DECL = /\b(?:boxShadow|box-shadow)\s*:\s*(['"`])((?:[^'"`\\]|\\.)*)\1/g;
|
|
13
|
+
const SHADOW_KEEPS_CARD_DEPTH = /--vx-shadow-|\bVX\.shadow/;
|
|
14
|
+
const SHADOW_IS_COMPOSED = /var\(|\$\{/;
|
|
15
|
+
const CSS_SURFACE_RADIUS = /(?<![\w-])border-radius\s*:\s*([^;}]+)/g;
|
|
16
|
+
const CSS_SURFACE_SHADOW = /(?<![\w-])box-shadow\s*:\s*([^;}]+)/g;
|
|
17
|
+
const MICRO_RADIUS_CEILING_PX = 3;
|
|
18
|
+
const SHAPE_RADIUS_VALUES = /* @__PURE__ */ new Set(["50%", "9999px", "999px", "100%"]);
|
|
19
|
+
function isAllowedCssRadius(value) {
|
|
20
|
+
const rest = value.replace(/var\([^)]*\)/g, " ").trim();
|
|
21
|
+
if (rest === "") return true;
|
|
22
|
+
return rest.split(/\s+/).every((part) => {
|
|
23
|
+
if (SHAPE_RADIUS_VALUES.has(part)) return true;
|
|
24
|
+
const literal = /^[-+]?(\d+(?:\.\d+)?|\.\d+)(px|rem)?$/.exec(part);
|
|
25
|
+
if (literal === null) return false;
|
|
26
|
+
const px = literal[2] === "rem" ? Number(literal[1]) * ROOT_FONT_SIZE_PX : Number(literal[1]);
|
|
27
|
+
return px <= MICRO_RADIUS_CEILING_PX;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const INLINE_FONT_SIZE = /(?<![\w-;])font-?[Ss]ize\s*:\s*['"]?(\d+(?:\.\d+)?)(?:px)?['"]?(?=[,;\s}]|$)/g;
|
|
13
31
|
const CARD_SURFACE_TAG = /<(?:Card|Paper)(?![\w.])(?:=>|[^>])*?>/g;
|
|
14
32
|
const WITH_BORDER_PROP = /\bwithBorder\b(?!\s*=\s*\{\s*false\s*\})/;
|
|
15
33
|
const OFF_SYSTEM_SURFACE_VAR = /var\(--mantine-color-(gray|dark)-\d/g;
|
|
16
34
|
const MANTINE_SHADE_INDEX = /\b(?:color|c|bg|backgroundColor|borderColor)\s*=\s*\{?\s*['"][a-z]+\.\d\b|var\(--mantine-color-(?!gray-|dark-)[a-z]+-\d/g;
|
|
17
|
-
const
|
|
35
|
+
const RAW_HTML_TAG_NAME = "<(?:div|span|section|header|nav|footer|aside|main|article|ul|ol)\\b";
|
|
36
|
+
const RAW_HTML_TAG = new RegExp(RAW_HTML_TAG_NAME);
|
|
37
|
+
const RAW_HTML_LAYOUT_TAG = new RegExp(`${RAW_HTML_TAG_NAME}(?:=>|[^>])*?>`, "g");
|
|
18
38
|
const INLINE_STYLE = /style=\{\{/;
|
|
39
|
+
const STYLE_IDENTIFIER = /style=\{\s*([A-Za-z_$][\w$]*)\s*\}/;
|
|
19
40
|
const LAYOUT_SURFACE_PROP = /\b(?:display|padding|margin|gap|flex|grid|border|background|width|height)\b/;
|
|
41
|
+
function hoistedObjectBody(codeText, name) {
|
|
42
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43
|
+
const decl = new RegExp(`\\b(?:const|let|var)\\s+${escaped}\\b[^=;]*=\\s*\\{([^}]*)\\}`).exec(
|
|
44
|
+
codeText
|
|
45
|
+
);
|
|
46
|
+
return decl?.[1] ?? "";
|
|
47
|
+
}
|
|
20
48
|
const BOX_SIDE = "(?:Top|Bottom|Left|Right|Block|Inline)(?:Start|End)?";
|
|
21
49
|
const BOX_SIDE_KEBAB = "(?:top|bottom|left|right|block|inline)(?:-(?:start|end))?";
|
|
22
50
|
const SPACING_PROP = `(?:padding|margin)(?:${BOX_SIDE}|-${BOX_SIDE_KEBAB})?|gap|rowGap|columnGap|row-gap|column-gap`;
|
|
@@ -77,9 +105,15 @@ const DEFAULT_GUARD_CONFIG = {
|
|
|
77
105
|
exemptRules: {},
|
|
78
106
|
severity: {}
|
|
79
107
|
};
|
|
80
|
-
const GRACE_PERIOD_KINDS = {
|
|
108
|
+
const GRACE_PERIOD_KINDS = {
|
|
109
|
+
"theme-allow-unscoped": "introduced in the round-4 guard minor \u2014 promote to error one minor later. Every consumer has bare `theme-allow` comments today (basalt itself has ~20), and landing this as an error would fail every one of their builds on an upgrade they took for the fixes.",
|
|
110
|
+
"surface-shadow-override": "introduced in the round-4 guard minor \u2014 promote to error one minor later. Catches the shape a TOKEN-FLUENT consumer writes, so the code it rejects is code that looks correct and passed every previous release.",
|
|
111
|
+
"css-raw-surface": 'introduced in the round-4 guard minor \u2014 promote to error one minor later. The surface kinds reaching CSS at all is the "existing kind, new file type" case the doctrine names explicitly.',
|
|
112
|
+
"inline-font-size": "introduced in the round-4 guard minor \u2014 promote to error one minor later. The oxlint plugin has enforced the same thing as `basalt/no-raw-font-size` for a while, so a consumer running both sees nothing new; one running check-theme alone gets a minor of runway.",
|
|
113
|
+
"hidden-inline-style": "introduced in the round-4 guard minor \u2014 promote by DELETING the kind and folding its two scans into `raw-html-layout`, which is where they belong once the widening has had its minor."
|
|
114
|
+
};
|
|
81
115
|
function severityOf(kind, cfg) {
|
|
82
|
-
return cfg.severity?.[kind] ?? (kind
|
|
116
|
+
return cfg.severity?.[kind] ?? (Object.hasOwn(GRACE_PERIOD_KINDS, kind) ? "warn" : "error");
|
|
83
117
|
}
|
|
84
118
|
const FINDING_TEXT_MAX_LENGTH = 100;
|
|
85
119
|
function textForFinding(sourceLine, fallbackToken) {
|
|
@@ -90,18 +124,166 @@ function textForFinding(sourceLine, fallbackToken) {
|
|
|
90
124
|
function isChartFile(relPath) {
|
|
91
125
|
return relPath.includes("/charts/") && !AXIS_WRAPPER_FILE.test(relPath);
|
|
92
126
|
}
|
|
127
|
+
const EXEMPT_RULE_ALIASES = {
|
|
128
|
+
"hidden-inline-style": "raw-html-layout",
|
|
129
|
+
"surface-shadow-override": "raw-surface",
|
|
130
|
+
"css-raw-surface": "raw-surface"
|
|
131
|
+
};
|
|
93
132
|
function isRuleExempt(kind, relPath, exemptRules) {
|
|
133
|
+
const alias = EXEMPT_RULE_ALIASES[kind];
|
|
134
|
+
if (alias !== void 0 && isRuleExempt(alias, relPath, exemptRules)) return true;
|
|
94
135
|
const patterns = exemptRules?.[kind];
|
|
95
136
|
if (patterns === void 0 || patterns.length === 0) return false;
|
|
96
137
|
const segments = relPath.split("/");
|
|
97
138
|
return patterns.some((pattern) => segments.includes(pattern.replace(/\/$/, "")));
|
|
98
139
|
}
|
|
99
|
-
|
|
100
|
-
|
|
140
|
+
const PLUGIN_RULE_IDS = /* @__PURE__ */ new Set([
|
|
141
|
+
"no-raw-font-size",
|
|
142
|
+
"raw-size-literal",
|
|
143
|
+
"card-inset",
|
|
144
|
+
"chart-in-raw-surface",
|
|
145
|
+
"hand-rolled-plot",
|
|
146
|
+
"chart-legend-literal",
|
|
147
|
+
"hand-rolled-shell",
|
|
148
|
+
"shadow-basalt-export",
|
|
149
|
+
"raw-scroll-container",
|
|
150
|
+
"visx-boundary",
|
|
151
|
+
"visx-tooltip",
|
|
152
|
+
"token-layer-boundary",
|
|
153
|
+
// These three honour `basalt-agent-allow`, never `theme-allow` — but they are still REAL ids, so
|
|
154
|
+
// a `theme-allow ai-sdk-major` must parse as a (useless) scoped annotation rather than as prose.
|
|
155
|
+
"agent-resume-guard",
|
|
156
|
+
"agent-no-raw-usechat",
|
|
157
|
+
"ai-sdk-major"
|
|
158
|
+
]);
|
|
159
|
+
const MIN_ALLOW_REASON_LENGTH = 4;
|
|
160
|
+
function parseAllowAnnotation(rest) {
|
|
161
|
+
const rules = [];
|
|
162
|
+
const unknownRules = [];
|
|
163
|
+
let remainder = rest.replace(/^[\s,]+/, "");
|
|
164
|
+
let inIdSlot = true;
|
|
165
|
+
for (; ; ) {
|
|
166
|
+
const token = /^(?:basalt\/)?([a-z][a-z0-9-]*)(?=$|[\s,:—–])/.exec(remainder);
|
|
167
|
+
const id = token?.[1];
|
|
168
|
+
if (token === null || id === void 0) break;
|
|
169
|
+
if (!(Object.hasOwn(GUARD_RULES, id) || PLUGIN_RULE_IDS.has(id))) {
|
|
170
|
+
if (inIdSlot) unknownRules.push(id);
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
rules.push(id);
|
|
174
|
+
const after = remainder.slice(token[0].length);
|
|
175
|
+
remainder = after.replace(/^[\s,]+/, "");
|
|
176
|
+
inIdSlot = /^\s*,/.test(after);
|
|
177
|
+
}
|
|
178
|
+
const reason = remainder.replace(/^(?:—|–|-{1,2}|:)\s*/, "").trim();
|
|
179
|
+
return { rules, unknownRules, hasReason: reason.length >= MIN_ALLOW_REASON_LENGTH };
|
|
180
|
+
}
|
|
181
|
+
const MAX_CSS_CONTINUATION_LINES = 8;
|
|
182
|
+
const COMMENT_OPENER_BEFORE = /(?:\/\/|\/\*|<!--)\s*$/;
|
|
183
|
+
const COMMENT_ONLY_LINE = /^\s*(?:\{\s*\/\*|\/\/|\/\*|\*|<!--)/;
|
|
184
|
+
const ANNOTATION_PREFIX = /(?:^|\/\/|\/\*|<!--|^\s*\*)\s*$/;
|
|
185
|
+
function collectAllowAnnotations(lines, codeLines, allowComment, syntax) {
|
|
186
|
+
const byLine = /* @__PURE__ */ new Map();
|
|
187
|
+
const declared = [];
|
|
188
|
+
const add = (index, annotation) => {
|
|
189
|
+
const list = byLine.get(index) ?? [];
|
|
190
|
+
list.push(annotation);
|
|
191
|
+
byLine.set(index, list);
|
|
192
|
+
};
|
|
193
|
+
for (let i = 0; i < lines.length; i++) {
|
|
194
|
+
const line = lines[i] ?? "";
|
|
195
|
+
const stripped = codeLines[i] ?? "";
|
|
196
|
+
const isCommentOnlyLine = stripped.trim() === "" || COMMENT_ONLY_LINE.test(line);
|
|
197
|
+
let from = 0;
|
|
198
|
+
for (; ; ) {
|
|
199
|
+
const at = line.indexOf(allowComment, from);
|
|
200
|
+
if (at === -1) break;
|
|
201
|
+
from = at + allowComment.length;
|
|
202
|
+
if (stripped[at] !== " " && !COMMENT_OPENER_BEFORE.test(line.slice(0, at))) continue;
|
|
203
|
+
const annotation = parseAllowAnnotation(line.slice(from));
|
|
204
|
+
if (ANNOTATION_PREFIX.test(line.slice(0, at))) declared.push({ line: i + 1, annotation });
|
|
205
|
+
add(i, annotation);
|
|
206
|
+
if (isCommentOnlyLine) {
|
|
207
|
+
add(i + 1, annotation);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (syntax !== "css") continue;
|
|
211
|
+
for (let back = 1; back <= MAX_CSS_CONTINUATION_LINES; back++) {
|
|
212
|
+
const previous = (codeLines[i - back] ?? "").trim();
|
|
213
|
+
if (previous === "" || /[;{}]$/.test(previous)) break;
|
|
214
|
+
add(i - back, annotation);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { waivers: byLine, declared };
|
|
219
|
+
}
|
|
220
|
+
function annotationsCover(annotations, kind) {
|
|
221
|
+
return (annotations ?? []).some(
|
|
222
|
+
(a) => a.rules.length === 0 && a.unknownRules.length === 0 || a.rules.includes(kind)
|
|
223
|
+
);
|
|
101
224
|
}
|
|
102
225
|
function guardSyntaxFor(relPath) {
|
|
103
|
-
|
|
226
|
+
if (relPath.endsWith(".css")) return "css";
|
|
227
|
+
if (/\.(?:html?|webmanifest|json)$/.test(relPath)) return "markup";
|
|
228
|
+
return "ts";
|
|
229
|
+
}
|
|
230
|
+
const MARKUP_KINDS = /* @__PURE__ */ new Set(["raw-hex", "raw-color-fn", "raw-font-family"]);
|
|
231
|
+
function stripMarkupComments(text) {
|
|
232
|
+
return text.replace(/<!--[\s\S]*?-->/g, (m) => m.replace(/[^\n]/g, " "));
|
|
233
|
+
}
|
|
234
|
+
const GENERATED_HEADER_LINE = "/* @generated basalt-ui \u2014 do not edit; regenerate with the command on the next line */";
|
|
235
|
+
const GENERATED_PROVENANCE_LINE = /^\/\* basalt-ui \d+\.\d+\.\d+[^\s]* — `basalt-ui (?:tokens:css|fonts:css)[^`]*` \*\/$/;
|
|
236
|
+
const GENERATED_HEADER_LINES = 2;
|
|
237
|
+
const GENERATED_COMMENT_LINE = /^\/\*(?:[^*]|\*(?!\/))*\*\/$/;
|
|
238
|
+
const GENERATED_SELECTOR_LINE = /^[^{};]*[,{]$/;
|
|
239
|
+
const GENERATED_DECL_LINE = /^(?:--(?:vx|basalt)-[A-Za-z0-9_-]*\s*:[^{};]*;|\})$/;
|
|
240
|
+
const NO_GENERATED_LINES = /* @__PURE__ */ new Set();
|
|
241
|
+
function braceDelta(line) {
|
|
242
|
+
let delta = 0;
|
|
243
|
+
for (const ch of line) {
|
|
244
|
+
if (ch === "{") delta++;
|
|
245
|
+
else if (ch === "}") delta--;
|
|
246
|
+
}
|
|
247
|
+
return delta;
|
|
104
248
|
}
|
|
249
|
+
function generatedTokenLines(relPath, lines, allowComment) {
|
|
250
|
+
if (!relPath.endsWith(".css")) return NO_GENERATED_LINES;
|
|
251
|
+
if ((lines[0] ?? "").trim() !== GENERATED_HEADER_LINE) return NO_GENERATED_LINES;
|
|
252
|
+
if (!GENERATED_PROVENANCE_LINE.test((lines[1] ?? "").trim())) return NO_GENERATED_LINES;
|
|
253
|
+
const skippable = /* @__PURE__ */ new Set([1, 2]);
|
|
254
|
+
let depth = 0;
|
|
255
|
+
for (let i = GENERATED_HEADER_LINES; i < lines.length; i++) {
|
|
256
|
+
const line = lines[i] ?? "";
|
|
257
|
+
const trimmed = line.trim();
|
|
258
|
+
const shaped = trimmed === "" || GENERATED_COMMENT_LINE.test(trimmed) || (depth === 0 ? GENERATED_SELECTOR_LINE : GENERATED_DECL_LINE).test(trimmed);
|
|
259
|
+
if (shaped && !line.includes(allowComment)) skippable.add(i + 1);
|
|
260
|
+
depth += braceDelta(line);
|
|
261
|
+
if (depth < 0) return NO_GENERATED_LINES;
|
|
262
|
+
}
|
|
263
|
+
return skippable;
|
|
264
|
+
}
|
|
265
|
+
const TOKENS_ONLY_DISABLED_KINDS = /* @__PURE__ */ new Set([
|
|
266
|
+
"localstorage-theme",
|
|
267
|
+
"off-identity-accent",
|
|
268
|
+
"mantine-shade-index",
|
|
269
|
+
"raw-spacing",
|
|
270
|
+
"raw-radius",
|
|
271
|
+
"card-with-border",
|
|
272
|
+
"off-system-surface-var",
|
|
273
|
+
"raw-html-layout",
|
|
274
|
+
// Same remedy as raw-html-layout, word for word ("use a Mantine layout primitive") — it is that
|
|
275
|
+
// kind's widening, so it has to share its gate here exactly as it shares it in
|
|
276
|
+
// EXEMPT_RULE_ALIASES. Splitting them told a Mantine-free consumer to import Box/Flex.
|
|
277
|
+
"hidden-inline-style",
|
|
278
|
+
"inline-spacing",
|
|
279
|
+
"inline-display",
|
|
280
|
+
"raw-visx-axis",
|
|
281
|
+
"raw-motion-value",
|
|
282
|
+
"unframed-chart",
|
|
283
|
+
"chart-missing-aria-label",
|
|
284
|
+
"raw-form-control",
|
|
285
|
+
"sub-16-input-font"
|
|
286
|
+
]);
|
|
105
287
|
const REGEX_PRECEDING_PUNCT = /* @__PURE__ */ new Set([
|
|
106
288
|
"=",
|
|
107
289
|
"(",
|
|
@@ -419,6 +601,41 @@ const GUARD_RULES = {
|
|
|
419
601
|
// object literal — both JSX-shaped, neither appears in CSS text.
|
|
420
602
|
appliesTo: (relPath) => !relPath.endsWith(".css"),
|
|
421
603
|
message: "fontSize below 16 on a form control is dead code \u2014 the styles.css iOS floor is `!important` and always wins. Either drop the override, or be honest about 16px in the design."
|
|
604
|
+
},
|
|
605
|
+
"theme-allow-unscoped": {
|
|
606
|
+
kind: "theme-allow-unscoped",
|
|
607
|
+
pattern: /theme-allow/,
|
|
608
|
+
// handled inline (annotation pass); entry keeps the registry complete
|
|
609
|
+
message: "theme-allow without a usable rule id and a reason. Write `theme-allow <rule-id> \u2014 <why>`: the id scopes the exception to that one kind (a bare comment waives EVERY kind on the line, including ones added later), and the reason is what makes it reviewable in a diff. An id that names no rule is a typo, not a blanket waiver \u2014 it suppresses nothing."
|
|
610
|
+
},
|
|
611
|
+
"surface-shadow-override": {
|
|
612
|
+
kind: "surface-shadow-override",
|
|
613
|
+
pattern: SHADOW_DECL,
|
|
614
|
+
// handled inline (value is judged, not just matched)
|
|
615
|
+
enabled: (cfg) => cfg.rawSurface,
|
|
616
|
+
message: "Token-composed boxShadow that REPLACES card depth \u2014 a card whose shadow drops --vx-shadow-card is the one card in the app with no depth. Compose with it (`${VX.shadowCard}, <your ring>`) rather than instead of it."
|
|
617
|
+
},
|
|
618
|
+
"css-raw-surface": {
|
|
619
|
+
kind: "css-raw-surface",
|
|
620
|
+
pattern: CSS_SURFACE_RADIUS,
|
|
621
|
+
// handled inline (value is judged); entry keeps the registry complete
|
|
622
|
+
enabled: (cfg) => cfg.rawSurface,
|
|
623
|
+
// The kebab dialect only exists in CSS text; the camelCase half is `raw-surface`.
|
|
624
|
+
appliesTo: (relPath) => relPath.endsWith(".css"),
|
|
625
|
+
message: "Surface literal in CSS \u2014 use var(--vx-radius-card|--vx-radius-md) for a corner and var(--vx-shadow-card) for depth, the same tokens the TSX side is held to."
|
|
626
|
+
},
|
|
627
|
+
"hidden-inline-style": {
|
|
628
|
+
kind: "hidden-inline-style",
|
|
629
|
+
pattern: RAW_HTML_LAYOUT_TAG,
|
|
630
|
+
// handled inline (full-text tag-scoped scan + hoisted-const lookup)
|
|
631
|
+
enabled: (cfg) => cfg.rawHtmlLayout,
|
|
632
|
+
appliesTo: (relPath) => !isChartFile(relPath) && !relPath.endsWith(".css"),
|
|
633
|
+
message: "Raw HTML element with inline layout/surface styling that the line scan cannot see \u2014 the tag is formatted across lines, or the style object is hoisted to a const. Same violation as raw-html-layout: use a Mantine layout primitive (Box/Flex/Grid/Stack/Group). Its own kind for one minor so the widening lands as a warning; it merges into raw-html-layout at the promotion."
|
|
634
|
+
},
|
|
635
|
+
"inline-font-size": {
|
|
636
|
+
kind: "inline-font-size",
|
|
637
|
+
pattern: INLINE_FONT_SIZE,
|
|
638
|
+
message: "Raw font-size literal \u2014 route through VX.text.* (numbers) / --vx-text-* (CSS), or the Mantine fz token. Same rule the oxlint plugin enforces as basalt/no-raw-font-size; this is the half a check-theme-only CI can see."
|
|
422
639
|
}
|
|
423
640
|
};
|
|
424
641
|
function ruleApplies(kind, relPath) {
|
|
@@ -426,6 +643,8 @@ function ruleApplies(kind, relPath) {
|
|
|
426
643
|
return appliesTo === void 0 || appliesTo(relPath);
|
|
427
644
|
}
|
|
428
645
|
function checkSource(text, relPath, cfg) {
|
|
646
|
+
const lines = text.split("\n");
|
|
647
|
+
const generatedLines = generatedTokenLines(relPath, lines, cfg.allowComment);
|
|
429
648
|
const findings = [];
|
|
430
649
|
const forbiddenAccentRe = GUARD_RULES["off-identity-accent"].pattern(cfg);
|
|
431
650
|
const spacingPropRe = GUARD_RULES["raw-spacing"].pattern(cfg);
|
|
@@ -433,101 +652,157 @@ function checkSource(text, relPath, cfg) {
|
|
|
433
652
|
GUARD_RULES["raw-radius"].pattern.source,
|
|
434
653
|
GUARD_RULES["raw-radius"].pattern.flags
|
|
435
654
|
);
|
|
436
|
-
const lines = text.split("\n");
|
|
437
655
|
const syntax = guardSyntaxFor(relPath);
|
|
438
|
-
const codeText = stripComments(text, syntax);
|
|
656
|
+
const codeText = syntax === "markup" ? stripMarkupComments(text) : stripComments(text, syntax);
|
|
439
657
|
const codeLines = codeText.split("\n");
|
|
658
|
+
const { waivers, declared } = collectAllowAnnotations(lines, codeLines, cfg.allowComment, syntax);
|
|
659
|
+
const isAllowed = (line, kind) => annotationsCover(waivers.get(line - 1), kind);
|
|
660
|
+
const isAllowedInRange = (start, end, kind) => {
|
|
661
|
+
for (let n = start; n <= end; n++) if (isAllowed(n, kind)) return true;
|
|
662
|
+
return false;
|
|
663
|
+
};
|
|
664
|
+
const push = (kind, line, token) => {
|
|
665
|
+
if (isAllowed(line, kind)) return;
|
|
666
|
+
findings.push({ relPath, line, token, kind });
|
|
667
|
+
};
|
|
668
|
+
for (const { line, annotation } of declared) {
|
|
669
|
+
if (annotation.unknownRules.length === 0 && annotation.rules.length > 0 && annotation.hasReason)
|
|
670
|
+
continue;
|
|
671
|
+
const unknown = `unknown rule id '${annotation.unknownRules.join("', '")}'`;
|
|
672
|
+
const missing = annotation.unknownRules.length > 0 ? annotation.rules.length === 0 ? `${unknown} \u2014 waives nothing` : `${unknown} \u2014 not waived` : annotation.rules.length === 0 ? "no rule id" : "no reason";
|
|
673
|
+
findings.push({
|
|
674
|
+
relPath,
|
|
675
|
+
line,
|
|
676
|
+
token: `${cfg.allowComment} (${missing})`,
|
|
677
|
+
kind: "theme-allow-unscoped"
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
const rawHtmlLayoutLines = /* @__PURE__ */ new Set();
|
|
440
681
|
for (let i = 0; i < lines.length; i++) {
|
|
441
682
|
const line = codeLines[i] ?? "";
|
|
442
|
-
if ((lines[i] ?? "").includes(cfg.allowComment)) continue;
|
|
443
683
|
for (const m of line.matchAll(GUARD_RULES["raw-hex"].pattern)) {
|
|
444
|
-
|
|
684
|
+
push("raw-hex", i + 1, m[0]);
|
|
445
685
|
}
|
|
446
686
|
for (const m of line.matchAll(GUARD_RULES["raw-color-fn"].pattern)) {
|
|
447
|
-
|
|
687
|
+
push("raw-color-fn", i + 1, m[0]);
|
|
448
688
|
}
|
|
449
689
|
if (ruleApplies("localstorage-theme", relPath)) {
|
|
450
690
|
for (const m of line.matchAll(GUARD_RULES["localstorage-theme"].pattern)) {
|
|
451
|
-
|
|
691
|
+
push("localstorage-theme", i + 1, m[0]);
|
|
452
692
|
}
|
|
453
693
|
}
|
|
454
694
|
for (const m of line.matchAll(GUARD_RULES["raw-font-family"].pattern)) {
|
|
455
|
-
|
|
695
|
+
push("raw-font-family", i + 1, m[0]);
|
|
696
|
+
}
|
|
697
|
+
for (const m of line.matchAll(GUARD_RULES["inline-font-size"].pattern)) {
|
|
698
|
+
push("inline-font-size", i + 1, m[0]);
|
|
456
699
|
}
|
|
457
700
|
if (ruleApplies("off-identity-accent", relPath)) {
|
|
458
701
|
for (const m of line.matchAll(forbiddenAccentRe)) {
|
|
459
|
-
|
|
702
|
+
push("off-identity-accent", i + 1, m[1] ?? "");
|
|
460
703
|
}
|
|
461
704
|
}
|
|
462
705
|
if (ruleApplies("raw-spacing", relPath)) {
|
|
463
|
-
for (const m of line.matchAll(spacingPropRe))
|
|
464
|
-
findings.push({ relPath, line: i + 1, token: m[0], kind: "raw-spacing" });
|
|
465
|
-
}
|
|
706
|
+
for (const m of line.matchAll(spacingPropRe)) push("raw-spacing", i + 1, m[0]);
|
|
466
707
|
}
|
|
467
708
|
if (GUARD_RULES["raw-radius"].enabled(cfg) && ruleApplies("raw-radius", relPath)) {
|
|
468
|
-
for (const m of line.matchAll(radiusPropRe))
|
|
469
|
-
findings.push({ relPath, line: i + 1, token: m[0], kind: "raw-radius" });
|
|
470
|
-
}
|
|
709
|
+
for (const m of line.matchAll(radiusPropRe)) push("raw-radius", i + 1, m[0]);
|
|
471
710
|
}
|
|
472
711
|
if (GUARD_RULES["raw-surface"].enabled(cfg)) {
|
|
473
|
-
for (const m of line.matchAll(SURFACE_BORDER))
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
712
|
+
for (const m of line.matchAll(SURFACE_BORDER)) push("raw-surface", i + 1, m[0]);
|
|
713
|
+
for (const m of line.matchAll(SURFACE_RADIUS)) push("raw-surface", i + 1, m[0]);
|
|
714
|
+
if (syntax === "css") {
|
|
715
|
+
for (const m of line.matchAll(CSS_SURFACE_RADIUS)) {
|
|
716
|
+
const value = m[1] ?? "";
|
|
717
|
+
if (!/\d/.test(value) || isAllowedCssRadius(value)) continue;
|
|
718
|
+
push("css-raw-surface", i + 1, m[0].trim());
|
|
719
|
+
}
|
|
720
|
+
for (const m of line.matchAll(CSS_SURFACE_SHADOW)) {
|
|
721
|
+
const value = m[1] ?? "";
|
|
722
|
+
if (SHADOW_IS_COMPOSED.test(value) || /^\s*none\s*$/.test(value)) continue;
|
|
723
|
+
push("css-raw-surface", i + 1, m[0].trim());
|
|
724
|
+
}
|
|
725
|
+
} else {
|
|
726
|
+
for (const m of line.matchAll(SHADOW_DECL)) {
|
|
727
|
+
const value = m[2] ?? "";
|
|
728
|
+
if (!SHADOW_IS_COMPOSED.test(value)) {
|
|
729
|
+
push("raw-surface", i + 1, m[0]);
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
if (SHADOW_KEEPS_CARD_DEPTH.test(value)) continue;
|
|
733
|
+
push("surface-shadow-override", i + 1, m[0]);
|
|
734
|
+
}
|
|
481
735
|
}
|
|
482
736
|
}
|
|
483
737
|
if (GUARD_RULES["off-system-surface-var"].enabled(cfg)) {
|
|
484
738
|
for (const m of line.matchAll(GUARD_RULES["off-system-surface-var"].pattern)) {
|
|
485
|
-
|
|
739
|
+
push("off-system-surface-var", i + 1, m[0]);
|
|
486
740
|
}
|
|
487
741
|
}
|
|
488
742
|
if (GUARD_RULES["mantine-shade-index"].enabled(cfg)) {
|
|
489
743
|
for (const m of line.matchAll(GUARD_RULES["mantine-shade-index"].pattern)) {
|
|
490
|
-
|
|
744
|
+
push("mantine-shade-index", i + 1, m[0]);
|
|
491
745
|
}
|
|
492
746
|
}
|
|
493
747
|
if (GUARD_RULES["raw-html-layout"].enabled(cfg) && ruleApplies("raw-html-layout", relPath) && RAW_HTML_TAG.test(line) && INLINE_STYLE.test(line) && LAYOUT_SURFACE_PROP.test(line)) {
|
|
494
|
-
|
|
748
|
+
rawHtmlLayoutLines.add(i + 1);
|
|
749
|
+
push("raw-html-layout", i + 1, "<raw-html style>");
|
|
495
750
|
}
|
|
496
751
|
if (GUARD_RULES["inline-spacing"].enabled(cfg)) {
|
|
497
752
|
for (const m of line.matchAll(GUARD_RULES["inline-spacing"].pattern)) {
|
|
498
753
|
if (syntax === "css" && isSubScaleCssSpacing(line, m.index)) continue;
|
|
499
|
-
|
|
754
|
+
push("inline-spacing", i + 1, m[0]);
|
|
500
755
|
}
|
|
501
756
|
}
|
|
502
757
|
if (GUARD_RULES["inline-display"].enabled(cfg) && ruleApplies("inline-display", relPath)) {
|
|
503
758
|
for (const m of line.matchAll(GUARD_RULES["inline-display"].pattern)) {
|
|
504
|
-
|
|
759
|
+
push("inline-display", i + 1, m[0]);
|
|
505
760
|
}
|
|
506
761
|
}
|
|
507
762
|
if (GUARD_RULES["raw-visx-axis"].enabled(cfg) && GUARD_RULES["raw-visx-axis"].appliesTo(relPath)) {
|
|
508
763
|
for (const m of line.matchAll(GUARD_RULES["raw-visx-axis"].pattern)) {
|
|
509
|
-
|
|
764
|
+
push("raw-visx-axis", i + 1, m[0]);
|
|
510
765
|
}
|
|
511
766
|
}
|
|
512
767
|
if (GUARD_RULES["raw-motion-value"].enabled(cfg) && ruleApplies("raw-motion-value", relPath)) {
|
|
513
|
-
for (const m of line.matchAll(MOTION_TRANSITION_NUMERIC))
|
|
514
|
-
|
|
515
|
-
}
|
|
768
|
+
for (const m of line.matchAll(MOTION_TRANSITION_NUMERIC))
|
|
769
|
+
push("raw-motion-value", i + 1, m[0]);
|
|
516
770
|
for (const m of line.matchAll(MOTION_TRANSITION_EASE_ARRAY)) {
|
|
517
|
-
|
|
771
|
+
push("raw-motion-value", i + 1, m[0]);
|
|
518
772
|
}
|
|
519
773
|
}
|
|
520
774
|
if (GUARD_RULES["raw-form-control"].enabled(cfg) && ruleApplies("raw-form-control", relPath)) {
|
|
521
775
|
for (const m of line.matchAll(GUARD_RULES["raw-form-control"].pattern)) {
|
|
522
|
-
|
|
776
|
+
push("raw-form-control", i + 1, m[0]);
|
|
523
777
|
}
|
|
524
778
|
}
|
|
525
779
|
}
|
|
780
|
+
if (GUARD_RULES["hidden-inline-style"].enabled(cfg) && ruleApplies("hidden-inline-style", relPath)) {
|
|
781
|
+
for (const m of codeText.matchAll(RAW_HTML_LAYOUT_TAG)) {
|
|
782
|
+
const tagText = m[0];
|
|
783
|
+
let styleBody = "";
|
|
784
|
+
if (INLINE_STYLE.test(tagText)) styleBody = tagText;
|
|
785
|
+
else {
|
|
786
|
+
const ident = STYLE_IDENTIFIER.exec(tagText)?.[1];
|
|
787
|
+
if (ident !== void 0) styleBody = hoistedObjectBody(codeText, ident);
|
|
788
|
+
}
|
|
789
|
+
if (styleBody === "" || !LAYOUT_SURFACE_PROP.test(styleBody)) continue;
|
|
790
|
+
const startLine = codeText.slice(0, m.index ?? 0).split("\n").length;
|
|
791
|
+
if (rawHtmlLayoutLines.has(startLine)) continue;
|
|
792
|
+
const endLine = startLine + (tagText.split("\n").length - 1);
|
|
793
|
+
if (isAllowedInRange(startLine, endLine, "hidden-inline-style")) continue;
|
|
794
|
+
findings.push({
|
|
795
|
+
relPath,
|
|
796
|
+
line: startLine,
|
|
797
|
+
token: "<raw-html style>",
|
|
798
|
+
kind: "hidden-inline-style"
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
}
|
|
526
802
|
if (GUARD_RULES["unframed-chart"].enabled(cfg) && ruleApplies("unframed-chart", relPath)) {
|
|
527
803
|
for (const m of codeText.matchAll(RAW_CHART_LEGEND_ARRAY)) {
|
|
528
804
|
const lineNo = codeText.slice(0, (m.index ?? 0) + m[0].length).split("\n").length;
|
|
529
|
-
|
|
530
|
-
findings.push({ relPath, line: lineNo, token: "items={[", kind: "unframed-chart" });
|
|
805
|
+
push("unframed-chart", lineNo, "items={[");
|
|
531
806
|
}
|
|
532
807
|
}
|
|
533
808
|
if (GUARD_RULES["chart-missing-aria-label"].enabled(cfg) && ruleApplies("chart-missing-aria-label", relPath)) {
|
|
@@ -536,14 +811,7 @@ function checkSource(text, relPath, cfg) {
|
|
|
536
811
|
if (HAS_ARIA_LABEL_PROP.test(tagText)) continue;
|
|
537
812
|
const startLine = codeText.slice(0, m.index ?? 0).split("\n").length;
|
|
538
813
|
const endLine = startLine + (tagText.split("\n").length - 1);
|
|
539
|
-
|
|
540
|
-
for (let n = startLine; n <= endLine; n++) {
|
|
541
|
-
if (isSkippedLine(lines[n - 1] ?? "", cfg)) {
|
|
542
|
-
skipped = true;
|
|
543
|
-
break;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
if (skipped) continue;
|
|
814
|
+
if (isAllowedInRange(startLine, endLine, "chart-missing-aria-label")) continue;
|
|
547
815
|
findings.push({
|
|
548
816
|
relPath,
|
|
549
817
|
line: startLine,
|
|
@@ -557,8 +825,7 @@ function checkSource(text, relPath, cfg) {
|
|
|
557
825
|
const withBorder = WITH_BORDER_PROP.exec(m[0]);
|
|
558
826
|
if (withBorder === null) continue;
|
|
559
827
|
const lineNo = codeText.slice(0, (m.index ?? 0) + withBorder.index).split("\n").length;
|
|
560
|
-
|
|
561
|
-
findings.push({ relPath, line: lineNo, token: "withBorder", kind: "card-with-border" });
|
|
828
|
+
push("card-with-border", lineNo, "withBorder");
|
|
562
829
|
}
|
|
563
830
|
}
|
|
564
831
|
if (GUARD_RULES["sub-16-input-font"].enabled(cfg) && ruleApplies("sub-16-input-font", relPath)) {
|
|
@@ -570,8 +837,7 @@ function checkSource(text, relPath, cfg) {
|
|
|
570
837
|
const value = Number.parseFloat(fontSizeMatch[1] ?? "");
|
|
571
838
|
if (!Number.isFinite(value) || value >= 16) continue;
|
|
572
839
|
const lineNo = codeText.slice(0, (m.index ?? 0) + tagText.length).split("\n").length;
|
|
573
|
-
|
|
574
|
-
findings.push({ relPath, line: lineNo, token: fontSizeMatch[0], kind: "sub-16-input-font" });
|
|
840
|
+
push("sub-16-input-font", lineNo, fontSizeMatch[0]);
|
|
575
841
|
}
|
|
576
842
|
for (const m of codeText.matchAll(STYLES_INPUT_PART)) {
|
|
577
843
|
const inputBody = m[1] ?? "";
|
|
@@ -580,11 +846,10 @@ function checkSource(text, relPath, cfg) {
|
|
|
580
846
|
const value = Number.parseFloat(fontSizeMatch[1] ?? "");
|
|
581
847
|
if (!Number.isFinite(value) || value >= 16) continue;
|
|
582
848
|
const lineNo = codeText.slice(0, (m.index ?? 0) + m[0].length).split("\n").length;
|
|
583
|
-
|
|
584
|
-
findings.push({ relPath, line: lineNo, token: fontSizeMatch[0], kind: "sub-16-input-font" });
|
|
849
|
+
push("sub-16-input-font", lineNo, fontSizeMatch[0]);
|
|
585
850
|
}
|
|
586
851
|
}
|
|
587
|
-
return findings.filter((f) => !isRuleExempt(f.kind, f.relPath, cfg.exemptRules)).map((f) => ({
|
|
852
|
+
return findings.filter((f) => !generatedLines.has(f.line)).filter((f) => !isRuleExempt(f.kind, f.relPath, cfg.exemptRules)).filter((f) => syntax !== "markup" || MARKUP_KINDS.has(f.kind)).filter((f) => cfg.profile !== "tokens-only" || !TOKENS_ONLY_DISABLED_KINDS.has(f.kind)).map((f) => ({
|
|
588
853
|
...f,
|
|
589
854
|
text: textForFinding(lines[f.line - 1], f.token),
|
|
590
855
|
severity: severityOf(f.kind, cfg)
|
|
@@ -592,6 +857,8 @@ function checkSource(text, relPath, cfg) {
|
|
|
592
857
|
}
|
|
593
858
|
export {
|
|
594
859
|
DEFAULT_GUARD_CONFIG,
|
|
860
|
+
GENERATED_HEADER_LINE,
|
|
595
861
|
GUARD_RULES,
|
|
862
|
+
TOKENS_ONLY_DISABLED_KINDS,
|
|
596
863
|
checkSource
|
|
597
864
|
};
|
package/dist/guard/types.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Guard types — Mantine-free, dependency-free (zero imports beyond TS types).
|
|
3
3
|
*
|
|
4
|
-
* GuardKind is the closed set of
|
|
4
|
+
* GuardKind is the closed set of 25 violation kinds the theme guard can emit.
|
|
5
5
|
* Finding is the structured result per violation, replacing the old `Violation` shape.
|
|
6
6
|
* GuardConfig is the per-run configuration that drives checkSource.
|
|
7
7
|
*/
|
|
8
|
-
/** The
|
|
9
|
-
export type GuardKind = 'raw-hex' | 'raw-color-fn' | 'localstorage-theme' | 'off-identity-accent' | 'mantine-shade-index' | 'raw-spacing' | 'raw-radius' | 'raw-surface' | 'card-with-border' | 'off-system-surface-var' | 'raw-html-layout' | 'inline-spacing' | 'inline-display' | 'raw-visx-axis' | 'raw-motion-value' | 'unframed-chart' | 'chart-missing-aria-label' | 'raw-form-control' | 'sub-16-input-font' | 'raw-font-family';
|
|
8
|
+
/** The 25 theme-guard violation kinds. */
|
|
9
|
+
export type GuardKind = 'raw-hex' | 'raw-color-fn' | 'localstorage-theme' | 'off-identity-accent' | 'mantine-shade-index' | 'raw-spacing' | 'raw-radius' | 'raw-surface' | 'card-with-border' | 'off-system-surface-var' | 'raw-html-layout' | 'inline-spacing' | 'inline-display' | 'raw-visx-axis' | 'raw-motion-value' | 'unframed-chart' | 'chart-missing-aria-label' | 'raw-form-control' | 'sub-16-input-font' | 'raw-font-family' | 'theme-allow-unscoped' | 'surface-shadow-override' | 'css-raw-surface' | 'inline-font-size' | 'hidden-inline-style';
|
|
10
10
|
/**
|
|
11
11
|
* How hard a finding lands. `error` fails the build; `warn` reports and passes.
|
|
12
12
|
*
|
|
@@ -127,10 +127,30 @@ export type GuardConfig = {
|
|
|
127
127
|
*/
|
|
128
128
|
readonly sub16InputFont: boolean;
|
|
129
129
|
/**
|
|
130
|
-
* Allow-comment policy
|
|
131
|
-
*
|
|
130
|
+
* Allow-comment policy. The token that opens an exception annotation inside a COMMENT, on the
|
|
131
|
+
* finding's own line or the line directly above it (matching the oxlint plugin's placement, and
|
|
132
|
+
* surviving an `oxfmt` reflow that moves the comment off the offending line). Default
|
|
133
|
+
* `'theme-allow'`.
|
|
134
|
+
*
|
|
135
|
+
* The accountable form is `theme-allow <rule-id>[, <rule-id>] — <reason>`: the rule ids scope the
|
|
136
|
+
* exception to those kinds only, and the reason says why. A `theme-allow` missing either half
|
|
137
|
+
* still suppresses (so an upgrade never breaks a build over it) but reports
|
|
138
|
+
* {@link GuardKind} `theme-allow-unscoped` on its own line.
|
|
132
139
|
*/
|
|
133
140
|
readonly allowComment: string;
|
|
141
|
+
/**
|
|
142
|
+
* Which kinds apply to this consumer. `'mantine'` (default) is the full framework guard.
|
|
143
|
+
*
|
|
144
|
+
* `'tokens-only'` is for a consumer that installs the TOKEN LAYER and nothing else — no
|
|
145
|
+
* `@mantine/core`, often no React (`basalt-ui tokens:css` + `basalt-ui/tokens`). Every kind whose
|
|
146
|
+
* remedy is a Mantine component or prop is disabled there, because "use `TextInput` from
|
|
147
|
+
* `@mantine/core`" is not advice, it is a dependency the consumer deliberately does not have.
|
|
148
|
+
* The color/typography kinds stay on — those are the tokens, which is the whole surface they DO
|
|
149
|
+
* consume. See `TOKENS_ONLY_DISABLED_KINDS` in `./index` for the exact set.
|
|
150
|
+
*
|
|
151
|
+
* Optional so a caller built before this field existed keeps compiling; absent means `'mantine'`.
|
|
152
|
+
*/
|
|
153
|
+
readonly profile?: 'mantine' | 'tokens-only';
|
|
134
154
|
/**
|
|
135
155
|
* Per-rule, per-path exemptions — complements whole-file `exempt` (BasaltConfig, which skips
|
|
136
156
|
* ALL rules for a file) and code-level `appliesTo` (hardcoded per-kind path scoping, e.g.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/guard/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,cAAc,GACd,oBAAoB,GACpB,qBAAqB,GACrB,qBAAqB,GACrB,aAAa,GACb,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,wBAAwB,GACxB,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,0BAA0B,GAC1B,kBAAkB,GAClB,mBAAmB,GACnB,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/guard/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,cAAc,GACd,oBAAoB,GACpB,qBAAqB,GACrB,qBAAqB,GACrB,aAAa,GACb,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,wBAAwB,GACxB,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,0BAA0B,GAC1B,kBAAkB,GAClB,mBAAmB,GACnB,iBAAiB,GACjB,sBAAsB,GACtB,yBAAyB,GACzB,iBAAiB,GACjB,kBAAkB,GAClB,qBAAqB,CAAA;AAEzB;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,CAAA;AAE5C;;;;;;;GAOG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;CACjC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,wGAAwG;IACxG,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5C;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAA;IACnC,iGAAiG;IACjG,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC,2GAA2G;IAC3G,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;IACrC,sGAAsG;IACtG,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,4FAA4F;IAC5F,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,yFAAyF;IACzF,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,kGAAkG;IAClG,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAA;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IAC5C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;IAC1D;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;CAC7D,CAAA"}
|
package/dist/register.d.ts
CHANGED
|
@@ -136,4 +136,29 @@ export declare namespace StandardSchemaV1 {
|
|
|
136
136
|
type InferInput<T extends StandardSchemaV1> = NonNullable<T['~standard']['types']>['input'];
|
|
137
137
|
type InferOutput<T extends StandardSchemaV1> = NonNullable<T['~standard']['types']>['output'];
|
|
138
138
|
}
|
|
139
|
+
declare global {
|
|
140
|
+
/**
|
|
141
|
+
* The app's version string, injected as a build-time constant by basalt's own Vite preset —
|
|
142
|
+
* `basaltViteConfig` defines it from `BUILD_VERSION`, falling back to the app package.json
|
|
143
|
+
* `version` (see `../vite`, the `define` block).
|
|
144
|
+
*
|
|
145
|
+
* Declared HERE rather than beside the define because a consumer's `src/**` never loads
|
|
146
|
+
* `basalt-ui/vite`'s types (that subpath is imported from `vite.config.ts`, which most apps keep
|
|
147
|
+
* in a separate tsconfig project). This file is re-exported from the root barrel, so an app that
|
|
148
|
+
* imports from `'basalt-ui'` at all has it in scope with no `/// <reference>` and no
|
|
149
|
+
* `src/vite-env.d.ts` entry — which is the point: basalt shipped the define and no declaration,
|
|
150
|
+
* so every consumer hand-wrote this block, one of them needing an `oxlint-disable` in a file that
|
|
151
|
+
* existed only because basalt asked for it.
|
|
152
|
+
*
|
|
153
|
+
* A consumer importing ONLY a subpath (`basalt-ui/tokens`, `basalt-ui/charts`) does not load this
|
|
154
|
+
* file and does not get the global. That gap is the same set as the applicability gap: reaching
|
|
155
|
+
* for the root barrel is what `BasaltProvider` requires, and an app that never does is not on
|
|
156
|
+
* basalt's Vite preset either, so there is no `__APP_VERSION__` to declare.
|
|
157
|
+
*
|
|
158
|
+
* Precondition: only defined when your Vite config actually goes through `basaltViteConfig` (or
|
|
159
|
+
* you define it yourself). An app that hand-rolls its Vite config and reads this gets a
|
|
160
|
+
* ReferenceError at runtime — TypeScript is trusting the preset, not proving it.
|
|
161
|
+
*/
|
|
162
|
+
const __APP_VERSION__: string;
|
|
163
|
+
}
|
|
139
164
|
//# sourceMappingURL=register.d.ts.map
|