@monoes/monodesign 1.2.18 → 1.2.22
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/README.md +3 -3
- package/cli/engine/design-system-checks.mjs +381 -0
- package/cli/engine/design-system-parse.mjs +433 -0
- package/cli/engine/design-system.mjs +20 -736
- package/cli/engine/detect-antipatterns-browser.js +12 -1
- package/cli/engine/engines/regex/detect-text-matchers.mjs +371 -0
- package/cli/engine/engines/regex/detect-text-run.mjs +186 -0
- package/cli/engine/engines/regex/detect-text.mjs +11 -523
- package/cli/engine/engines/static-html/css-cascade-color.mjs +219 -0
- package/cli/engine/engines/static-html/css-cascade-static-data.mjs +138 -0
- package/cli/engine/engines/static-html/css-cascade-static-dom.mjs +132 -0
- package/cli/engine/engines/static-html/css-cascade-static-parse.mjs +393 -0
- package/cli/engine/engines/static-html/css-cascade.mjs +24 -837
- package/cli/engine/registry/antipatterns-provider.mjs +60 -0
- package/cli/engine/registry/antipatterns-quality.mjs +218 -0
- package/cli/engine/registry/antipatterns-slop.mjs +226 -0
- package/cli/engine/registry/antipatterns.mjs +7 -494
- package/cli/engine/rules/checks-background.mjs +155 -0
- package/cli/engine/rules/checks-css-values.mjs +137 -0
- package/cli/engine/rules/checks-dom-effects.mjs +243 -0
- package/cli/engine/rules/checks-dom.mjs +148 -0
- package/cli/engine/rules/checks-element.mjs +286 -0
- package/cli/engine/rules/checks-html.mjs +204 -0
- package/cli/engine/rules/checks-overflow.mjs +263 -0
- package/cli/engine/rules/checks-page.mjs +465 -0
- package/cli/engine/rules/checks-pure.mjs +453 -0
- package/cli/engine/rules/checks-quality.mjs +447 -0
- package/cli/engine/rules/checks-stylesheet.mjs +324 -0
- package/cli/engine/rules/checks.mjs +98 -2939
- package/cli/lib/monodesign-config-consent.mjs +93 -0
- package/cli/lib/monodesign-config-core.mjs +37 -0
- package/cli/lib/monodesign-config-detection.mjs +133 -0
- package/cli/lib/monodesign-config-filter.mjs +198 -0
- package/cli/lib/monodesign-config-ignore-value.mjs +212 -0
- package/cli/lib/monodesign-config.mjs +21 -623
- package/package.json +2 -2
- package/skill/scripts/context-fields.mjs +65 -0
- package/skill/scripts/context-resolve.mjs +425 -0
- package/skill/scripts/context-update.mjs +151 -0
- package/skill/scripts/context-utils.mjs +68 -0
- package/skill/scripts/context-workspace.mjs +245 -0
- package/skill/scripts/context.mjs +17 -891
- package/skill/scripts/hook-admin-config.mjs +152 -0
- package/skill/scripts/hook-admin-ignore.mjs +131 -0
- package/skill/scripts/hook-admin-manifest.mjs +292 -0
- package/skill/scripts/hook-admin.mjs +8 -541
- package/skill/scripts/hook-before-edit-shell.mjs +184 -0
- package/skill/scripts/hook-before-edit.mjs +9 -165
- package/skill/scripts/hook-lib-cache.mjs +145 -0
- package/skill/scripts/hook-lib-config.mjs +292 -0
- package/skill/scripts/hook-lib-detector.mjs +72 -0
- package/skill/scripts/hook-lib-events.mjs +182 -0
- package/skill/scripts/hook-lib-findings.mjs +201 -0
- package/skill/scripts/hook-lib-ignore.mjs +205 -0
- package/skill/scripts/hook-lib-render.mjs +273 -0
- package/skill/scripts/hook-lib-run.mjs +281 -0
- package/skill/scripts/hook-lib-scan-targets.mjs +128 -0
- package/skill/scripts/hook-lib.mjs +71 -1747
- package/skill/scripts/lib/design-parser-colors.mjs +182 -0
- package/skill/scripts/lib/design-parser-extract.mjs +333 -0
- package/skill/scripts/lib/design-parser-sections.mjs +214 -0
- package/skill/scripts/lib/design-parser-yaml.mjs +135 -0
- package/skill/scripts/lib/design-parser.mjs +10 -783
- package/skill/scripts/live/manual-apply-chunk.mjs +151 -0
- package/skill/scripts/live/manual-apply-compact.mjs +122 -0
- package/skill/scripts/live/manual-apply-evidence.mjs +115 -0
- package/skill/scripts/live/manual-apply-log.mjs +179 -0
- package/skill/scripts/live/manual-apply-transaction.mjs +122 -0
- package/skill/scripts/live/manual-apply.mjs +65 -640
- package/skill/scripts/live/svelte-component-accept.mjs +248 -0
- package/skill/scripts/live/svelte-component-css.mjs +230 -0
- package/skill/scripts/live/svelte-component-markup.mjs +104 -0
- package/skill/scripts/live/svelte-component-scaffold.mjs +118 -0
- package/skill/scripts/live/svelte-component-session.mjs +158 -0
- package/skill/scripts/live/svelte-component.mjs +32 -792
- package/skill/scripts/live-accept-mutate.mjs +152 -0
- package/skill/scripts/live-accept-parse.mjs +370 -0
- package/skill/scripts/live-accept-scrub.mjs +59 -0
- package/skill/scripts/live-accept-search.mjs +58 -0
- package/skill/scripts/live-accept.mjs +15 -631
- package/skill/scripts/live-commit-manual-edits-paths.mjs +27 -0
- package/skill/scripts/live-commit-manual-edits-repair.mjs +166 -0
- package/skill/scripts/live-commit-manual-edits-rollback.mjs +231 -0
- package/skill/scripts/live-commit-manual-edits-utils.mjs +167 -0
- package/skill/scripts/live-commit-manual-edits-verify.mjs +356 -0
- package/skill/scripts/live-commit-manual-edits.mjs +24 -881
- package/skill/scripts/live-copy-edit-agent.mjs +16 -551
- package/skill/scripts/live-copy-edit-compact.mjs +137 -0
- package/skill/scripts/live-copy-edit-post-apply.mjs +175 -0
- package/skill/scripts/live-copy-edit-runners.mjs +297 -0
- package/skill/scripts/live-inject-csp.mjs +132 -0
- package/skill/scripts/live-inject-files.mjs +229 -0
- package/skill/scripts/live-inject-tag.mjs +114 -0
- package/skill/scripts/live-inject.mjs +13 -440
- package/skill/scripts/live-server-poll.mjs +232 -0
- package/skill/scripts/live-server-routes.mjs +449 -0
- package/skill/scripts/live-server-state.mjs +361 -0
- package/skill/scripts/live-server.mjs +10 -946
- package/skill/scripts/live-wrap-css.mjs +132 -0
- package/skill/scripts/live-wrap-manual-edit.mjs +153 -0
- package/skill/scripts/live-wrap-search.mjs +247 -0
- package/skill/scripts/live-wrap.mjs +21 -484
- package/skill/scripts/palette-seeds.mjs +401 -0
- package/skill/scripts/palette.mjs +1 -395
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
// Design-system source parsing: DESIGN.md/sidecar path resolution,
|
|
2
|
+
// frontmatter/YAML parsing, color/font normalization, and building the
|
|
3
|
+
// normalized designSystem object. Split out of design-system.mjs (file-size
|
|
4
|
+
// sweep — pure move, no behaviour change).
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
import { GENERIC_FONTS } from './shared/constants.mjs';
|
|
10
|
+
import { parseAnyColor, resolveLengthPx } from './rules/checks.mjs';
|
|
11
|
+
|
|
12
|
+
const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md'];
|
|
13
|
+
const FALLBACK_DIRS = ['.agents/context', 'docs'];
|
|
14
|
+
const COLOR_CHANNEL_TOLERANCE = 6;
|
|
15
|
+
const FONT_SIZE_LITERAL_RE = /^-?[\d.]+(?:px|rem)$/;
|
|
16
|
+
|
|
17
|
+
function firstExisting(dir, names) {
|
|
18
|
+
for (const name of names) {
|
|
19
|
+
const abs = path.join(dir, name);
|
|
20
|
+
if (fs.existsSync(abs)) return abs;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveDesignMdPath(cwd = process.cwd()) {
|
|
26
|
+
const root = firstExisting(cwd, DESIGN_NAMES);
|
|
27
|
+
if (root) return { path: root, contextDir: cwd };
|
|
28
|
+
|
|
29
|
+
for (const rel of FALLBACK_DIRS) {
|
|
30
|
+
const dir = path.resolve(cwd, rel);
|
|
31
|
+
const found = firstExisting(dir, DESIGN_NAMES);
|
|
32
|
+
if (found) return { path: found, contextDir: dir };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveDesignSidecarPath(cwd = process.cwd(), contextDir = cwd) {
|
|
39
|
+
const candidates = [
|
|
40
|
+
path.join(cwd, '.monodesign', 'design.json'),
|
|
41
|
+
path.join(cwd, 'DESIGN.json'),
|
|
42
|
+
path.join(contextDir, 'DESIGN.json'),
|
|
43
|
+
];
|
|
44
|
+
return candidates.find((candidate, index) =>
|
|
45
|
+
candidates.indexOf(candidate) === index && fs.existsSync(candidate)
|
|
46
|
+
) || null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseFrontmatter(md) {
|
|
50
|
+
const lines = String(md || '').split(/\r?\n/);
|
|
51
|
+
if (lines[0]?.trim() !== '---') return null;
|
|
52
|
+
let end = -1;
|
|
53
|
+
for (let i = 1; i < lines.length; i++) {
|
|
54
|
+
if (lines[i].trim() === '---') { end = i; break; }
|
|
55
|
+
}
|
|
56
|
+
if (end === -1) return null;
|
|
57
|
+
try {
|
|
58
|
+
return parseYamlSubset(lines.slice(1, end).join('\n'));
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function parseYamlSubset(yaml) {
|
|
65
|
+
const root = {};
|
|
66
|
+
const stack = [{ indent: -1, obj: root }];
|
|
67
|
+
|
|
68
|
+
for (const raw of String(yaml || '').split(/\r?\n/)) {
|
|
69
|
+
if (!raw.trim() || /^\s*#/.test(raw)) continue;
|
|
70
|
+
const indent = raw.match(/^\s*/)[0].length;
|
|
71
|
+
const content = raw.slice(indent);
|
|
72
|
+
const colonIdx = findTopLevelColon(content);
|
|
73
|
+
if (colonIdx === -1) continue;
|
|
74
|
+
|
|
75
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) stack.pop();
|
|
76
|
+
|
|
77
|
+
const key = unquoteYamlKey(content.slice(0, colonIdx).trim());
|
|
78
|
+
const rest = stripInlineYamlComment(content.slice(colonIdx + 1).trim());
|
|
79
|
+
const parent = stack[stack.length - 1].obj;
|
|
80
|
+
|
|
81
|
+
if (rest === '') {
|
|
82
|
+
const obj = {};
|
|
83
|
+
parent[key] = obj;
|
|
84
|
+
stack.push({ indent, obj });
|
|
85
|
+
} else {
|
|
86
|
+
parent[key] = parseScalar(rest);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return root;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function findTopLevelColon(s) {
|
|
94
|
+
let inQuote = null;
|
|
95
|
+
for (let i = 0; i < s.length; i++) {
|
|
96
|
+
const ch = s[i];
|
|
97
|
+
if (inQuote) {
|
|
98
|
+
if (ch === inQuote && s[i - 1] !== '\\') inQuote = null;
|
|
99
|
+
} else if (ch === '"' || ch === "'") {
|
|
100
|
+
inQuote = ch;
|
|
101
|
+
} else if (ch === ':') {
|
|
102
|
+
return i;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return -1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function unquoteYamlKey(key) {
|
|
109
|
+
if ((key.startsWith('"') && key.endsWith('"')) || (key.startsWith("'") && key.endsWith("'"))) {
|
|
110
|
+
return key.slice(1, -1);
|
|
111
|
+
}
|
|
112
|
+
return key;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function stripInlineYamlComment(s) {
|
|
116
|
+
let inQuote = null;
|
|
117
|
+
for (let i = 0; i < s.length; i++) {
|
|
118
|
+
const ch = s[i];
|
|
119
|
+
if (inQuote) {
|
|
120
|
+
if (ch === inQuote && s[i - 1] !== '\\') inQuote = null;
|
|
121
|
+
} else if (ch === '"' || ch === "'") {
|
|
122
|
+
inQuote = ch;
|
|
123
|
+
} else if (ch === '#' && i > 0 && /\s/.test(s[i - 1])) {
|
|
124
|
+
return s.slice(0, i).trimEnd();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return s;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function parseScalar(raw) {
|
|
131
|
+
const s = raw.trim();
|
|
132
|
+
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
|
133
|
+
return s.slice(1, -1);
|
|
134
|
+
}
|
|
135
|
+
if (s === 'true') return true;
|
|
136
|
+
if (s === 'false') return false;
|
|
137
|
+
if (s === 'null' || s === '~') return null;
|
|
138
|
+
if (/^-?\d+$/.test(s)) return Number(s);
|
|
139
|
+
if (/^-?\d*\.\d+$/.test(s)) return Number(s);
|
|
140
|
+
return s;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function safeReadJson(filePath) {
|
|
144
|
+
if (!filePath) return null;
|
|
145
|
+
try {
|
|
146
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function normalizeFontName(value) {
|
|
153
|
+
return String(value || '')
|
|
154
|
+
.trim()
|
|
155
|
+
.replace(/\s*!important\s*$/i, '')
|
|
156
|
+
.trim()
|
|
157
|
+
.replace(/^["']|["']$/g, '')
|
|
158
|
+
.replace(/\+/g, ' ')
|
|
159
|
+
.replace(/\s+/g, ' ')
|
|
160
|
+
.toLowerCase();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function splitFontStack(stack) {
|
|
164
|
+
return String(stack || '')
|
|
165
|
+
.replace(/\s*!important\s*$/i, '')
|
|
166
|
+
.split(',')
|
|
167
|
+
.map(normalizeFontName)
|
|
168
|
+
.filter(Boolean);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function primaryFont(stack) {
|
|
172
|
+
if (!stack || /var\(/i.test(stack) || !isLiteralFontStack(stack)) return '';
|
|
173
|
+
return splitFontStack(stack).find(font => !GENERIC_FONTS.has(font)) || '';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function isLiteralFontStack(stack) {
|
|
177
|
+
const text = String(stack || '');
|
|
178
|
+
return !/[$`{}]|\s\+\s|\|\|/.test(text);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function cssColorLabel(raw) {
|
|
182
|
+
return String(raw || '').trim().replace(/\s+/g, ' ');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function colorKey(color) {
|
|
186
|
+
if (!color) return '';
|
|
187
|
+
return `${color.r},${color.g},${color.b}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function colorsClose(a, b) {
|
|
191
|
+
if (!a || !b) return false;
|
|
192
|
+
return Math.max(
|
|
193
|
+
Math.abs(a.r - b.r),
|
|
194
|
+
Math.abs(a.g - b.g),
|
|
195
|
+
Math.abs(a.b - b.b),
|
|
196
|
+
) <= COLOR_CHANNEL_TOLERANCE;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function hslToRgb(H, S, L, alpha = 1) {
|
|
200
|
+
const h = (((H % 360) + 360) % 360) / 360;
|
|
201
|
+
const s = Math.max(0, Math.min(1, S));
|
|
202
|
+
const l = Math.max(0, Math.min(1, L));
|
|
203
|
+
const hue2rgb = (p, q, t) => {
|
|
204
|
+
if (t < 0) t += 1;
|
|
205
|
+
if (t > 1) t -= 1;
|
|
206
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
207
|
+
if (t < 1 / 2) return q;
|
|
208
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
209
|
+
return p;
|
|
210
|
+
};
|
|
211
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
212
|
+
const p = 2 * l - q;
|
|
213
|
+
return {
|
|
214
|
+
r: Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
|
|
215
|
+
g: Math.round(hue2rgb(p, q, h) * 255),
|
|
216
|
+
b: Math.round(hue2rgb(p, q, h - 1 / 3) * 255),
|
|
217
|
+
a: alpha,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function parseDesignColor(value) {
|
|
222
|
+
const text = String(value || '').trim();
|
|
223
|
+
const parsed = parseAnyColor(text);
|
|
224
|
+
if (parsed) return parsed;
|
|
225
|
+
const hsl = text.match(/hsla?\(\s*([-\d.]+)(?:deg)?\s*,?\s*([\d.]+)%\s*,?\s*([\d.]+)%(?:\s*[,/]\s*([\d.]+))?\s*\)/i);
|
|
226
|
+
if (hsl) {
|
|
227
|
+
return hslToRgb(
|
|
228
|
+
parseFloat(hsl[1]),
|
|
229
|
+
parseFloat(hsl[2]) / 100,
|
|
230
|
+
parseFloat(hsl[3]) / 100,
|
|
231
|
+
hsl[4] !== undefined ? parseFloat(hsl[4]) : 1,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function addDesignColor(out, value, label) {
|
|
238
|
+
const parsed = parseDesignColor(value);
|
|
239
|
+
if (!parsed) return;
|
|
240
|
+
const key = colorKey(parsed);
|
|
241
|
+
if (!out.allowedColorKeys.has(key)) {
|
|
242
|
+
out.allowedColorKeys.set(key, { color: parsed, labels: [] });
|
|
243
|
+
}
|
|
244
|
+
out.allowedColorKeys.get(key).labels.push(label || cssColorLabel(value));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function addColorObject(out, colors, prefix = 'colors') {
|
|
248
|
+
if (!colors || typeof colors !== 'object') return;
|
|
249
|
+
for (const [name, value] of Object.entries(colors)) {
|
|
250
|
+
if (typeof value === 'string') {
|
|
251
|
+
addDesignColor(out, value, `${prefix}.${name}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function addSidecarColors(out, sidecar) {
|
|
257
|
+
const colorMeta = sidecar?.extensions?.colorMeta;
|
|
258
|
+
if (!colorMeta || typeof colorMeta !== 'object') return;
|
|
259
|
+
|
|
260
|
+
for (const [name, meta] of Object.entries(colorMeta)) {
|
|
261
|
+
if (!meta || typeof meta !== 'object') continue;
|
|
262
|
+
if (typeof meta.canonical === 'string') addDesignColor(out, meta.canonical, `sidecar.${name}`);
|
|
263
|
+
if (Array.isArray(meta.tonalRamp)) {
|
|
264
|
+
for (const [index, value] of meta.tonalRamp.entries()) {
|
|
265
|
+
if (typeof value === 'string') addDesignColor(out, value, `sidecar.${name}.tonalRamp[${index}]`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function addTypographyFonts(out, typography) {
|
|
272
|
+
if (!typography || typeof typography !== 'object') return;
|
|
273
|
+
for (const role of Object.values(typography)) {
|
|
274
|
+
if (!role || typeof role !== 'object') continue;
|
|
275
|
+
if (typeof role.fontFamily !== 'string') continue;
|
|
276
|
+
for (const font of splitFontStack(role.fontFamily)) {
|
|
277
|
+
if (!GENERIC_FONTS.has(font)) out.allowedFonts.add(font);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function addTypographySizes(out, typography) {
|
|
283
|
+
if (!typography || typeof typography !== 'object') return;
|
|
284
|
+
for (const role of Object.values(typography)) {
|
|
285
|
+
if (!role || typeof role !== 'object') continue;
|
|
286
|
+
const raw = String(role.fontSize ?? '').trim().toLowerCase();
|
|
287
|
+
if (!FONT_SIZE_LITERAL_RE.test(raw)) continue;
|
|
288
|
+
const px = resolveLengthPx(raw, 16);
|
|
289
|
+
if (px == null || !Number.isFinite(px) || px <= 0) continue;
|
|
290
|
+
out.allowedFontSizes.push({ value: raw, px });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function addRoundedScale(out, rounded) {
|
|
295
|
+
if (!rounded || typeof rounded !== 'object') return;
|
|
296
|
+
for (const [rawName, value] of Object.entries(rounded)) {
|
|
297
|
+
const name = unquoteYamlKey(rawName).toLowerCase();
|
|
298
|
+
addRoundedToken(out, name, value);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function addRoundedToken(out, name, value) {
|
|
303
|
+
if (typeof value !== 'string' && typeof value !== 'number') return;
|
|
304
|
+
const raw = String(value).trim();
|
|
305
|
+
if (!raw || /var\(/i.test(raw) || raw.includes('%')) return;
|
|
306
|
+
const px = resolveLengthPx(raw, 16);
|
|
307
|
+
if (px == null || !Number.isFinite(px)) return;
|
|
308
|
+
out.allowedRadii.push({ name, value: raw, px });
|
|
309
|
+
if (/(^|\.)(full|pill|round|rounded-full)$/.test(name)) out.hasPillRadius = true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function addSidecarRadii(out, sidecar) {
|
|
313
|
+
const roundedMeta = sidecar?.extensions?.roundedMeta;
|
|
314
|
+
if (!roundedMeta || typeof roundedMeta !== 'object') return;
|
|
315
|
+
|
|
316
|
+
for (const [rawName, meta] of Object.entries(roundedMeta)) {
|
|
317
|
+
const name = unquoteYamlKey(rawName).toLowerCase();
|
|
318
|
+
if (typeof meta === 'string' || typeof meta === 'number') {
|
|
319
|
+
addRoundedToken(out, `sidecar.${name}`, meta);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (!meta || typeof meta !== 'object') continue;
|
|
323
|
+
for (const key of ['canonical', 'value']) {
|
|
324
|
+
if (typeof meta[key] === 'string' || typeof meta[key] === 'number') {
|
|
325
|
+
addRoundedToken(out, `sidecar.${name}.${key}`, meta[key]);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
for (const key of ['values', 'aliases']) {
|
|
329
|
+
if (!Array.isArray(meta[key])) continue;
|
|
330
|
+
for (const [index, value] of meta[key].entries()) {
|
|
331
|
+
addRoundedToken(out, `sidecar.${name}.${key}[${index}]`, value);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (/^(full|pill|round|rounded-full)$/.test(name) || /^(full|pill|round)$/i.test(String(meta.role || ''))) {
|
|
335
|
+
out.hasPillRadius = true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function normalizeDesignSystem(input = {}) {
|
|
341
|
+
const frontmatter = input.frontmatter || {};
|
|
342
|
+
const sidecar = input.sidecar || null;
|
|
343
|
+
const out = {
|
|
344
|
+
present: true,
|
|
345
|
+
sourcePath: input.sourcePath || null,
|
|
346
|
+
sidecarPath: input.sidecarPath || null,
|
|
347
|
+
mdNewerThanJson: input.mdNewerThanJson === true,
|
|
348
|
+
allowedFonts: new Set(),
|
|
349
|
+
allowedColorKeys: new Map(),
|
|
350
|
+
allowedRadii: [],
|
|
351
|
+
allowedFontSizes: [],
|
|
352
|
+
hasPillRadius: false,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
addTypographyFonts(out, frontmatter.typography);
|
|
356
|
+
addTypographySizes(out, frontmatter.typography);
|
|
357
|
+
addColorObject(out, frontmatter.colors);
|
|
358
|
+
addSidecarColors(out, sidecar);
|
|
359
|
+
addRoundedScale(out, frontmatter.rounded);
|
|
360
|
+
addSidecarRadii(out, sidecar);
|
|
361
|
+
|
|
362
|
+
out.hasFonts = out.allowedFonts.size > 0;
|
|
363
|
+
out.hasColors = out.allowedColorKeys.size > 0;
|
|
364
|
+
out.hasRadii = out.allowedRadii.length > 0;
|
|
365
|
+
out.hasFontSizes = out.allowedFontSizes.length > 0;
|
|
366
|
+
return out;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function loadDesignSystemForCwd(cwd = process.cwd()) {
|
|
370
|
+
const md = resolveDesignMdPath(cwd);
|
|
371
|
+
if (!md) return null;
|
|
372
|
+
|
|
373
|
+
let frontmatter = null;
|
|
374
|
+
let mdStat = null;
|
|
375
|
+
try {
|
|
376
|
+
mdStat = fs.statSync(md.path);
|
|
377
|
+
frontmatter = parseFrontmatter(fs.readFileSync(md.path, 'utf-8'));
|
|
378
|
+
} catch {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
if (!frontmatter || typeof frontmatter !== 'object') return null;
|
|
382
|
+
|
|
383
|
+
const sidecarPath = resolveDesignSidecarPath(cwd, md.contextDir);
|
|
384
|
+
const sidecar = safeReadJson(sidecarPath);
|
|
385
|
+
let sidecarStat = null;
|
|
386
|
+
try {
|
|
387
|
+
if (sidecarPath) sidecarStat = fs.statSync(sidecarPath);
|
|
388
|
+
} catch {
|
|
389
|
+
sidecarStat = null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return normalizeDesignSystem({
|
|
393
|
+
frontmatter,
|
|
394
|
+
sidecar,
|
|
395
|
+
sourcePath: md.path,
|
|
396
|
+
sidecarPath,
|
|
397
|
+
mdNewerThanJson: !!(mdStat && sidecarStat && mdStat.mtimeMs > sidecarStat.mtimeMs + 1000),
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
export {
|
|
403
|
+
FONT_SIZE_LITERAL_RE,
|
|
404
|
+
firstExisting,
|
|
405
|
+
resolveDesignMdPath,
|
|
406
|
+
resolveDesignSidecarPath,
|
|
407
|
+
parseFrontmatter,
|
|
408
|
+
parseYamlSubset,
|
|
409
|
+
findTopLevelColon,
|
|
410
|
+
unquoteYamlKey,
|
|
411
|
+
stripInlineYamlComment,
|
|
412
|
+
parseScalar,
|
|
413
|
+
safeReadJson,
|
|
414
|
+
normalizeFontName,
|
|
415
|
+
splitFontStack,
|
|
416
|
+
primaryFont,
|
|
417
|
+
isLiteralFontStack,
|
|
418
|
+
cssColorLabel,
|
|
419
|
+
colorKey,
|
|
420
|
+
colorsClose,
|
|
421
|
+
hslToRgb,
|
|
422
|
+
parseDesignColor,
|
|
423
|
+
addDesignColor,
|
|
424
|
+
addColorObject,
|
|
425
|
+
addSidecarColors,
|
|
426
|
+
addTypographyFonts,
|
|
427
|
+
addTypographySizes,
|
|
428
|
+
addRoundedScale,
|
|
429
|
+
addRoundedToken,
|
|
430
|
+
addSidecarRadii,
|
|
431
|
+
normalizeDesignSystem,
|
|
432
|
+
loadDesignSystemForCwd,
|
|
433
|
+
};
|