@vizejs/vite-plugin-musea 0.0.1-alpha.83 → 0.0.1-alpha.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +48 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +493 -4
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,9 @@ interface DesignToken {
|
|
|
23
23
|
type?: string;
|
|
24
24
|
description?: string;
|
|
25
25
|
attributes?: Record<string, unknown>;
|
|
26
|
+
$tier?: "primitive" | "semantic";
|
|
27
|
+
$reference?: string;
|
|
28
|
+
$resolvedValue?: string | number;
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
31
|
* Token category (e.g., colors, spacing, typography).
|
|
@@ -74,6 +77,18 @@ type TokenTransform = (token: DesignToken, path: string[]) => DesignToken;
|
|
|
74
77
|
* Parse Style Dictionary tokens file.
|
|
75
78
|
*/
|
|
76
79
|
declare function parseTokens(tokensPath: string): Promise<TokenCategory[]>;
|
|
80
|
+
/**
|
|
81
|
+
* Flatten nested categories into a flat map keyed by dot-path.
|
|
82
|
+
*/
|
|
83
|
+
declare function buildTokenMap(categories: TokenCategory[], prefix?: string[]): Record<string, DesignToken>;
|
|
84
|
+
/**
|
|
85
|
+
* Resolve references in categories, setting $tier, $reference, and $resolvedValue.
|
|
86
|
+
*/
|
|
87
|
+
declare function resolveReferences(categories: TokenCategory[], tokenMap: Record<string, DesignToken>): void;
|
|
88
|
+
/**
|
|
89
|
+
* Read raw JSON token file.
|
|
90
|
+
*/
|
|
91
|
+
|
|
77
92
|
/**
|
|
78
93
|
* Generate HTML documentation for tokens.
|
|
79
94
|
*/
|
|
@@ -85,13 +100,44 @@ declare function generateTokensMarkdown(categories: TokenCategory[]): string;
|
|
|
85
100
|
/**
|
|
86
101
|
* Style Dictionary plugin for Musea.
|
|
87
102
|
*/
|
|
88
|
-
declare function processStyleDictionary(config: StyleDictionaryConfig): Promise<StyleDictionaryOutput>;
|
|
103
|
+
declare function processStyleDictionary(config: StyleDictionaryConfig): Promise<StyleDictionaryOutput>;
|
|
104
|
+
interface TokenUsageMatch {
|
|
105
|
+
line: number;
|
|
106
|
+
lineContent: string;
|
|
107
|
+
property: string;
|
|
108
|
+
}
|
|
109
|
+
interface TokenUsageEntry {
|
|
110
|
+
artPath: string;
|
|
111
|
+
artTitle: string;
|
|
112
|
+
artCategory?: string;
|
|
113
|
+
matches: TokenUsageMatch[];
|
|
114
|
+
}
|
|
115
|
+
type TokenUsageMap = Record<string, TokenUsageEntry[]>;
|
|
116
|
+
/**
|
|
117
|
+
* Normalize a token value for comparison.
|
|
118
|
+
* - Lowercase, trim
|
|
119
|
+
* - Leading-zero: `.5rem` → `0.5rem`
|
|
120
|
+
* - Short hex: `#fff` → `#ffffff`
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Scan art file sources for token value matches in `<style>` blocks.
|
|
125
|
+
*/
|
|
126
|
+
declare function scanTokenUsage(artFiles: Map<string, {
|
|
127
|
+
path: string;
|
|
128
|
+
metadata: {
|
|
129
|
+
title: string;
|
|
130
|
+
category?: string;
|
|
131
|
+
};
|
|
132
|
+
}>, tokenMap: Record<string, DesignToken>): TokenUsageMap; //#endregion
|
|
89
133
|
//#region src/index.d.ts
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=style-dictionary.d.ts.map
|
|
90
136
|
/**
|
|
91
137
|
* Create Musea Vite plugin.
|
|
92
138
|
*/
|
|
93
139
|
declare function musea(options?: MuseaOptions): Plugin[];
|
|
94
140
|
|
|
95
141
|
//#endregion
|
|
96
|
-
export { A11yOptions, A11yResult, A11ySummary, AnalysisApiResponse, ArtFileInfo, ArtMetadata, ArtVariant, AutogenOptions, AutogenOutput, CaptureConfig, CiConfig, ComparisonConfig, CsfOutput, DesignToken, GeneratedVariant, MuseaA11yRunner, MuseaOptions, MuseaTheme, MuseaThemeColors, MuseaVrtRunner, PaletteApiResponse, PropDefinition, StyleDictionaryConfig, StyleDictionaryOutput, TokenCategory, ViewportConfig, VrtOptions, VrtResult, VrtSummary, musea as default, generateArtFile, generateTokensHtml, generateTokensMarkdown, generateVrtJsonReport, generateVrtReport, musea, parseTokens, processStyleDictionary, writeArtFile };
|
|
142
|
+
export { A11yOptions, A11yResult, A11ySummary, AnalysisApiResponse, ArtFileInfo, ArtMetadata, ArtVariant, AutogenOptions, AutogenOutput, CaptureConfig, CiConfig, ComparisonConfig, CsfOutput, DesignToken, GeneratedVariant, MuseaA11yRunner, MuseaOptions, MuseaTheme, MuseaThemeColors, MuseaVrtRunner, PaletteApiResponse, PropDefinition, StyleDictionaryConfig, StyleDictionaryOutput, TokenCategory, TokenUsageMap, ViewportConfig, VrtOptions, VrtResult, VrtSummary, buildTokenMap, musea as default, generateArtFile, generateTokensHtml, generateTokensMarkdown, generateVrtJsonReport, generateVrtReport, musea, parseTokens, processStyleDictionary, resolveReferences, scanTokenUsage, writeArtFile };
|
|
97
143
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/style-dictionary.ts","../src/index.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;;;;;;;;;UAWiB,WAAA;;;;eAIF;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/style-dictionary.ts","../src/index.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;;;;;;;;;UAWiB,WAAA;;;;eAIF;;EAJE,UAAA,CAAA,EAAA,MAAW;;;;AAa5B;;AAEyB,UAFR,aAAA,CAEQ;EAAW,IAA1B,EAAA,MAAA;EAAM,MACE,EADR,MACQ,CAAA,MAAA,EADO,WACP,CAAA;EAAa,aAAA,CAAA,EAAb,aAAa,EAAA;;;;AAM/B;UAAiB,qBAAA;cACH;;IAWG,IAAA,EAAA,MAAA;;;;AA2BjB;;;;AAAgF,UA3B/D,qBAAA,CA2B+D;;;;EAK1D,UAAA,EAAA,MAAW;EAAA;;;AAA6B;;;;AAuJ9D;;EAA6B,SACf,CAAA,EAAA,MAAA;EAAa;;AAElB;eArKM;;;AAkMf;;AACc,KA7LF,cAAA,GA6LE,CAAA,KAAA,EA7LuB,WA6LvB,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,GA7LuD,WA6LvD;;;AACI;iBAzLI,WAAA,sBAAiC,QAAQ;;;AAsY/D;iBA/OgB,aAAA,aACF,qCAEX,eAAe;;;AA8WlB;iBAjVgB,iBAAA,aACF,2BACF,eAAe;;;AAqX3B;;;;;iBAxKgB,kBAAA,aAA+B;ACjO/C;;;AAAmD,iBDmWnC,sBAAA,CCnWmC,UAAA,EDmWA,aCnWA,EAAA,CAAA,EAAA,MAAA;AAAM;;;iBDyYnC,sBAAA,SACZ,wBACP,QAAQ;UA6CM,eAAA;;;;;UAMA,eAAA;;;;WAIN;;KAGC,aAAA,GAAgB,eAAe;;;;;;;;;;;iBAgC3B,cAAA,WACJ;;;;;;cACA,eAAe,eACxB;;;;AA/rB4B;;;iBCuNf,KAAA,WAAe,eAAoB"}
|
package/dist/index.js
CHANGED
|
@@ -92,12 +92,15 @@ function isTokenValue(value) {
|
|
|
92
92
|
* Normalize token to DesignToken interface.
|
|
93
93
|
*/
|
|
94
94
|
function normalizeToken(raw) {
|
|
95
|
-
|
|
95
|
+
const token = {
|
|
96
96
|
value: raw.value,
|
|
97
97
|
type: raw.type,
|
|
98
98
|
description: raw.description,
|
|
99
99
|
attributes: raw.attributes
|
|
100
100
|
};
|
|
101
|
+
if (raw.$tier === "primitive" || raw.$tier === "semantic") token.$tier = raw.$tier;
|
|
102
|
+
if (typeof raw.$reference === "string") token.$reference = raw.$reference;
|
|
103
|
+
return token;
|
|
101
104
|
}
|
|
102
105
|
/**
|
|
103
106
|
* Format category name for display.
|
|
@@ -106,6 +109,168 @@ function formatCategoryName(name) {
|
|
|
106
109
|
return name.replace(/[-_]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
107
110
|
}
|
|
108
111
|
/**
|
|
112
|
+
* Flatten nested categories into a flat map keyed by dot-path.
|
|
113
|
+
*/
|
|
114
|
+
function buildTokenMap(categories, prefix = []) {
|
|
115
|
+
const map = {};
|
|
116
|
+
for (const cat of categories) {
|
|
117
|
+
const catKey = cat.name.toLowerCase().replace(/\s+/g, "-");
|
|
118
|
+
const catPath = [...prefix, catKey];
|
|
119
|
+
for (const [name, token] of Object.entries(cat.tokens)) {
|
|
120
|
+
const dotPath = [...catPath, name].join(".");
|
|
121
|
+
map[dotPath] = token;
|
|
122
|
+
}
|
|
123
|
+
if (cat.subcategories) {
|
|
124
|
+
const subMap = buildTokenMap(cat.subcategories, catPath);
|
|
125
|
+
Object.assign(map, subMap);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return map;
|
|
129
|
+
}
|
|
130
|
+
const REFERENCE_PATTERN = /^\{(.+)\}$/;
|
|
131
|
+
const MAX_RESOLVE_DEPTH = 10;
|
|
132
|
+
/**
|
|
133
|
+
* Resolve references in categories, setting $tier, $reference, and $resolvedValue.
|
|
134
|
+
*/
|
|
135
|
+
function resolveReferences(categories, tokenMap) {
|
|
136
|
+
for (const cat of categories) {
|
|
137
|
+
for (const token of Object.values(cat.tokens)) resolveTokenReference(token, tokenMap);
|
|
138
|
+
if (cat.subcategories) resolveReferences(cat.subcategories, tokenMap);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function resolveTokenReference(token, tokenMap) {
|
|
142
|
+
if (typeof token.value === "string") {
|
|
143
|
+
const match = token.value.match(REFERENCE_PATTERN);
|
|
144
|
+
if (match) {
|
|
145
|
+
token.$tier = token.$tier ?? "semantic";
|
|
146
|
+
token.$reference = match[1];
|
|
147
|
+
token.$resolvedValue = resolveValue(match[1], tokenMap, 0, new Set());
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
token.$tier = token.$tier ?? "primitive";
|
|
152
|
+
}
|
|
153
|
+
function resolveValue(ref, tokenMap, depth, visited) {
|
|
154
|
+
if (depth >= MAX_RESOLVE_DEPTH || visited.has(ref)) return void 0;
|
|
155
|
+
visited.add(ref);
|
|
156
|
+
const target = tokenMap[ref];
|
|
157
|
+
if (!target) return void 0;
|
|
158
|
+
if (typeof target.value === "string") {
|
|
159
|
+
const match = target.value.match(REFERENCE_PATTERN);
|
|
160
|
+
if (match) return resolveValue(match[1], tokenMap, depth + 1, visited);
|
|
161
|
+
}
|
|
162
|
+
return target.value;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Read raw JSON token file.
|
|
166
|
+
*/
|
|
167
|
+
async function readRawTokenFile(tokensPath) {
|
|
168
|
+
const content = await fs.promises.readFile(tokensPath, "utf-8");
|
|
169
|
+
return JSON.parse(content);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Write raw JSON token file atomically (write tmp, rename).
|
|
173
|
+
*/
|
|
174
|
+
async function writeRawTokenFile(tokensPath, data) {
|
|
175
|
+
const tmpPath = tokensPath + ".tmp";
|
|
176
|
+
await fs.promises.writeFile(tmpPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
177
|
+
await fs.promises.rename(tmpPath, tokensPath);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Set a token at a dot-separated path in the raw JSON structure.
|
|
181
|
+
*/
|
|
182
|
+
function setTokenAtPath(data, dotPath, token) {
|
|
183
|
+
const parts = dotPath.split(".");
|
|
184
|
+
let current = data;
|
|
185
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
186
|
+
const key = parts[i];
|
|
187
|
+
if (typeof current[key] !== "object" || current[key] === null) current[key] = {};
|
|
188
|
+
current = current[key];
|
|
189
|
+
}
|
|
190
|
+
const leafKey = parts[parts.length - 1];
|
|
191
|
+
const raw = { value: token.value };
|
|
192
|
+
if (token.type) raw.type = token.type;
|
|
193
|
+
if (token.description) raw.description = token.description;
|
|
194
|
+
if (token.$tier) raw.$tier = token.$tier;
|
|
195
|
+
if (token.$reference) raw.$reference = token.$reference;
|
|
196
|
+
if (token.attributes) raw.attributes = token.attributes;
|
|
197
|
+
current[leafKey] = raw;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Delete a token at a dot-separated path, cleaning empty parents.
|
|
201
|
+
*/
|
|
202
|
+
function deleteTokenAtPath(data, dotPath) {
|
|
203
|
+
const parts = dotPath.split(".");
|
|
204
|
+
const parents = [];
|
|
205
|
+
let current = data;
|
|
206
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
207
|
+
const key = parts[i];
|
|
208
|
+
if (typeof current[key] !== "object" || current[key] === null) return false;
|
|
209
|
+
parents.push({
|
|
210
|
+
obj: current,
|
|
211
|
+
key
|
|
212
|
+
});
|
|
213
|
+
current = current[key];
|
|
214
|
+
}
|
|
215
|
+
const leafKey = parts[parts.length - 1];
|
|
216
|
+
if (!(leafKey in current)) return false;
|
|
217
|
+
delete current[leafKey];
|
|
218
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
219
|
+
const { obj, key } = parents[i];
|
|
220
|
+
const child = obj[key];
|
|
221
|
+
if (Object.keys(child).length === 0) delete obj[key];
|
|
222
|
+
else break;
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Validate that a semantic reference points to an existing token and has no cycles.
|
|
228
|
+
*/
|
|
229
|
+
function validateSemanticReference(tokenMap, reference, selfPath) {
|
|
230
|
+
if (!tokenMap[reference]) return {
|
|
231
|
+
valid: false,
|
|
232
|
+
error: `Reference target "${reference}" does not exist`
|
|
233
|
+
};
|
|
234
|
+
const visited = new Set();
|
|
235
|
+
if (selfPath) visited.add(selfPath);
|
|
236
|
+
let current = reference;
|
|
237
|
+
let depth = 0;
|
|
238
|
+
while (depth < MAX_RESOLVE_DEPTH) {
|
|
239
|
+
if (visited.has(current)) return {
|
|
240
|
+
valid: false,
|
|
241
|
+
error: `Circular reference detected at "${current}"`
|
|
242
|
+
};
|
|
243
|
+
visited.add(current);
|
|
244
|
+
const target = tokenMap[current];
|
|
245
|
+
if (!target) break;
|
|
246
|
+
if (typeof target.value === "string") {
|
|
247
|
+
const match = target.value.match(REFERENCE_PATTERN);
|
|
248
|
+
if (match) {
|
|
249
|
+
current = match[1];
|
|
250
|
+
depth++;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
if (depth >= MAX_RESOLVE_DEPTH) return {
|
|
257
|
+
valid: false,
|
|
258
|
+
error: "Reference chain too deep (max 10)"
|
|
259
|
+
};
|
|
260
|
+
return { valid: true };
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Find all tokens that reference the given path.
|
|
264
|
+
*/
|
|
265
|
+
function findDependentTokens(tokenMap, targetPath) {
|
|
266
|
+
const dependents = [];
|
|
267
|
+
for (const [path$1, token] of Object.entries(tokenMap)) if (typeof token.value === "string") {
|
|
268
|
+
const match = token.value.match(REFERENCE_PATTERN);
|
|
269
|
+
if (match && match[1] === targetPath) dependents.push(path$1);
|
|
270
|
+
}
|
|
271
|
+
return dependents;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
109
274
|
* Generate HTML documentation for tokens.
|
|
110
275
|
*/
|
|
111
276
|
function generateTokensHtml(categories) {
|
|
@@ -275,6 +440,97 @@ async function processStyleDictionary(config) {
|
|
|
275
440
|
}
|
|
276
441
|
};
|
|
277
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Normalize a token value for comparison.
|
|
445
|
+
* - Lowercase, trim
|
|
446
|
+
* - Leading-zero: `.5rem` → `0.5rem`
|
|
447
|
+
* - Short hex: `#fff` → `#ffffff`
|
|
448
|
+
*/
|
|
449
|
+
function normalizeTokenValue(value) {
|
|
450
|
+
let v = String(value).trim().toLowerCase();
|
|
451
|
+
const shortHex = v.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/);
|
|
452
|
+
if (shortHex) {
|
|
453
|
+
const [, r, g, b, a] = shortHex;
|
|
454
|
+
v = a ? `#${r}${r}${g}${g}${b}${b}${a}${a}` : `#${r}${r}${g}${g}${b}${b}`;
|
|
455
|
+
}
|
|
456
|
+
v = v.replace(/(?<![0-9])\.(\d)/g, "0.$1");
|
|
457
|
+
return v;
|
|
458
|
+
}
|
|
459
|
+
const STYLE_BLOCK_RE = /<style[^>]*>([\s\S]*?)<\/style>/g;
|
|
460
|
+
const CSS_PROPERTY_RE = /^\s*([\w-]+)\s*:\s*(.+?)\s*;?\s*$/;
|
|
461
|
+
/**
|
|
462
|
+
* Scan art file sources for token value matches in `<style>` blocks.
|
|
463
|
+
*/
|
|
464
|
+
function scanTokenUsage(artFiles, tokenMap) {
|
|
465
|
+
const valueLookup = new Map();
|
|
466
|
+
for (const [tokenPath, token] of Object.entries(tokenMap)) {
|
|
467
|
+
const rawValue = token.$resolvedValue ?? token.value;
|
|
468
|
+
const normalized = normalizeTokenValue(rawValue);
|
|
469
|
+
if (!normalized) continue;
|
|
470
|
+
const existing = valueLookup.get(normalized);
|
|
471
|
+
if (existing) existing.push(tokenPath);
|
|
472
|
+
else valueLookup.set(normalized, [tokenPath]);
|
|
473
|
+
}
|
|
474
|
+
const usageMap = {};
|
|
475
|
+
for (const [artPath, artInfo] of artFiles) {
|
|
476
|
+
let source;
|
|
477
|
+
try {
|
|
478
|
+
source = fs.readFileSync(artPath, "utf-8");
|
|
479
|
+
} catch {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const allLines = source.split("\n");
|
|
483
|
+
const styleRegions = [];
|
|
484
|
+
let match;
|
|
485
|
+
STYLE_BLOCK_RE.lastIndex = 0;
|
|
486
|
+
while ((match = STYLE_BLOCK_RE.exec(source)) !== null) {
|
|
487
|
+
const beforeMatch = source.slice(0, match.index);
|
|
488
|
+
const startTag = source.slice(match.index, match.index + match[0].indexOf(match[1]));
|
|
489
|
+
const startLine = beforeMatch.split("\n").length + startTag.split("\n").length - 1;
|
|
490
|
+
styleRegions.push({
|
|
491
|
+
startLine,
|
|
492
|
+
content: match[1]
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
for (const region of styleRegions) {
|
|
496
|
+
const lines = region.content.split("\n");
|
|
497
|
+
for (let i = 0; i < lines.length; i++) {
|
|
498
|
+
const line = lines[i];
|
|
499
|
+
const propMatch = line.match(CSS_PROPERTY_RE);
|
|
500
|
+
if (!propMatch) continue;
|
|
501
|
+
const property = propMatch[1];
|
|
502
|
+
const valueStr = propMatch[2];
|
|
503
|
+
const valueParts = valueStr.split(/\s+/);
|
|
504
|
+
for (const part of valueParts) {
|
|
505
|
+
const normalizedPart = normalizeTokenValue(part);
|
|
506
|
+
const matchingTokens = valueLookup.get(normalizedPart);
|
|
507
|
+
if (!matchingTokens) continue;
|
|
508
|
+
const lineNumber = region.startLine + i;
|
|
509
|
+
const lineContent = allLines[lineNumber - 1]?.trim() ?? line.trim();
|
|
510
|
+
for (const tokenPath of matchingTokens) {
|
|
511
|
+
if (!usageMap[tokenPath]) usageMap[tokenPath] = [];
|
|
512
|
+
let entry = usageMap[tokenPath].find((e) => e.artPath === artPath);
|
|
513
|
+
if (!entry) {
|
|
514
|
+
entry = {
|
|
515
|
+
artPath,
|
|
516
|
+
artTitle: artInfo.metadata.title,
|
|
517
|
+
artCategory: artInfo.metadata.category,
|
|
518
|
+
matches: []
|
|
519
|
+
};
|
|
520
|
+
usageMap[tokenPath].push(entry);
|
|
521
|
+
}
|
|
522
|
+
if (!entry.matches.some((m) => m.line === lineNumber && m.property === property)) entry.matches.push({
|
|
523
|
+
line: lineNumber,
|
|
524
|
+
lineContent,
|
|
525
|
+
property
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return usageMap;
|
|
533
|
+
}
|
|
278
534
|
|
|
279
535
|
//#endregion
|
|
280
536
|
//#region src/index.ts
|
|
@@ -498,30 +754,263 @@ function musea(options = {}) {
|
|
|
498
754
|
sendJson(Array.from(artFiles.values()));
|
|
499
755
|
return;
|
|
500
756
|
}
|
|
757
|
+
if (req.url === "/tokens/usage" && req.method === "GET") {
|
|
758
|
+
if (!tokensPath) {
|
|
759
|
+
sendJson({});
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
try {
|
|
763
|
+
const absoluteTokensPath = path.resolve(config.root, tokensPath);
|
|
764
|
+
const categories = await parseTokens(absoluteTokensPath);
|
|
765
|
+
const tokenMap = buildTokenMap(categories);
|
|
766
|
+
resolveReferences(categories, tokenMap);
|
|
767
|
+
const resolvedTokenMap = buildTokenMap(categories);
|
|
768
|
+
const usage = scanTokenUsage(artFiles, resolvedTokenMap);
|
|
769
|
+
sendJson(usage);
|
|
770
|
+
} catch (e) {
|
|
771
|
+
console.error("[musea] Failed to scan token usage:", e);
|
|
772
|
+
sendJson({});
|
|
773
|
+
}
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
501
776
|
if (req.url === "/tokens" && req.method === "GET") {
|
|
502
777
|
if (!tokensPath) {
|
|
503
|
-
sendJson({
|
|
778
|
+
sendJson({
|
|
779
|
+
categories: [],
|
|
780
|
+
tokenMap: {},
|
|
781
|
+
meta: {
|
|
782
|
+
filePath: "",
|
|
783
|
+
tokenCount: 0,
|
|
784
|
+
primitiveCount: 0,
|
|
785
|
+
semanticCount: 0
|
|
786
|
+
}
|
|
787
|
+
});
|
|
504
788
|
return;
|
|
505
789
|
}
|
|
506
790
|
try {
|
|
507
791
|
const absoluteTokensPath = path.resolve(config.root, tokensPath);
|
|
508
792
|
const categories = await parseTokens(absoluteTokensPath);
|
|
509
|
-
|
|
793
|
+
const tokenMap = buildTokenMap(categories);
|
|
794
|
+
resolveReferences(categories, tokenMap);
|
|
795
|
+
const resolvedTokenMap = buildTokenMap(categories);
|
|
796
|
+
let primitiveCount = 0;
|
|
797
|
+
let semanticCount = 0;
|
|
798
|
+
for (const token of Object.values(resolvedTokenMap)) if (token.$tier === "semantic") semanticCount++;
|
|
799
|
+
else primitiveCount++;
|
|
800
|
+
sendJson({
|
|
801
|
+
categories,
|
|
802
|
+
tokenMap: resolvedTokenMap,
|
|
803
|
+
meta: {
|
|
804
|
+
filePath: absoluteTokensPath,
|
|
805
|
+
tokenCount: Object.keys(resolvedTokenMap).length,
|
|
806
|
+
primitiveCount,
|
|
807
|
+
semanticCount
|
|
808
|
+
}
|
|
809
|
+
});
|
|
510
810
|
} catch (e) {
|
|
511
811
|
console.error("[musea] Failed to load tokens:", e);
|
|
512
812
|
sendJson({
|
|
513
813
|
categories: [],
|
|
814
|
+
tokenMap: {},
|
|
514
815
|
error: String(e)
|
|
515
816
|
});
|
|
516
817
|
}
|
|
517
818
|
return;
|
|
518
819
|
}
|
|
820
|
+
if (req.url === "/tokens" && req.method === "POST") {
|
|
821
|
+
if (!tokensPath) {
|
|
822
|
+
sendError("No tokens path configured", 400);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
let body = "";
|
|
826
|
+
req.on("data", (chunk) => {
|
|
827
|
+
body += chunk;
|
|
828
|
+
});
|
|
829
|
+
req.on("end", async () => {
|
|
830
|
+
try {
|
|
831
|
+
const { path: dotPath, token } = JSON.parse(body);
|
|
832
|
+
if (!dotPath || !token || token.value === void 0) {
|
|
833
|
+
sendError("Missing required fields: path, token.value", 400);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const absoluteTokensPath = path.resolve(config.root, tokensPath);
|
|
837
|
+
const rawData = await readRawTokenFile(absoluteTokensPath);
|
|
838
|
+
const currentCategories = await parseTokens(absoluteTokensPath);
|
|
839
|
+
const currentMap = buildTokenMap(currentCategories);
|
|
840
|
+
if (currentMap[dotPath]) {
|
|
841
|
+
sendError(`Token already exists at path "${dotPath}"`, 409);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (token.$reference) {
|
|
845
|
+
const validation = validateSemanticReference(currentMap, token.$reference, dotPath);
|
|
846
|
+
if (!validation.valid) {
|
|
847
|
+
sendError(validation.error, 400);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
token.value = `{${token.$reference}}`;
|
|
851
|
+
token.$tier = "semantic";
|
|
852
|
+
}
|
|
853
|
+
setTokenAtPath(rawData, dotPath, token);
|
|
854
|
+
await writeRawTokenFile(absoluteTokensPath, rawData);
|
|
855
|
+
const categories = await parseTokens(absoluteTokensPath);
|
|
856
|
+
const tokenMap = buildTokenMap(categories);
|
|
857
|
+
resolveReferences(categories, tokenMap);
|
|
858
|
+
const resolvedTokenMap = buildTokenMap(categories);
|
|
859
|
+
sendJson({
|
|
860
|
+
categories,
|
|
861
|
+
tokenMap: resolvedTokenMap
|
|
862
|
+
}, 201);
|
|
863
|
+
} catch (e) {
|
|
864
|
+
sendError(e instanceof Error ? e.message : String(e));
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
if (req.url === "/tokens" && req.method === "PUT") {
|
|
870
|
+
if (!tokensPath) {
|
|
871
|
+
sendError("No tokens path configured", 400);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
let body = "";
|
|
875
|
+
req.on("data", (chunk) => {
|
|
876
|
+
body += chunk;
|
|
877
|
+
});
|
|
878
|
+
req.on("end", async () => {
|
|
879
|
+
try {
|
|
880
|
+
const { path: dotPath, token } = JSON.parse(body);
|
|
881
|
+
if (!dotPath || !token || token.value === void 0) {
|
|
882
|
+
sendError("Missing required fields: path, token.value", 400);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
const absoluteTokensPath = path.resolve(config.root, tokensPath);
|
|
886
|
+
if (token.$reference) {
|
|
887
|
+
const currentCategories = await parseTokens(absoluteTokensPath);
|
|
888
|
+
const currentMap = buildTokenMap(currentCategories);
|
|
889
|
+
const validation = validateSemanticReference(currentMap, token.$reference, dotPath);
|
|
890
|
+
if (!validation.valid) {
|
|
891
|
+
sendError(validation.error, 400);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
token.value = `{${token.$reference}}`;
|
|
895
|
+
token.$tier = "semantic";
|
|
896
|
+
}
|
|
897
|
+
const rawData = await readRawTokenFile(absoluteTokensPath);
|
|
898
|
+
setTokenAtPath(rawData, dotPath, token);
|
|
899
|
+
await writeRawTokenFile(absoluteTokensPath, rawData);
|
|
900
|
+
const categories = await parseTokens(absoluteTokensPath);
|
|
901
|
+
const tokenMap = buildTokenMap(categories);
|
|
902
|
+
resolveReferences(categories, tokenMap);
|
|
903
|
+
const resolvedTokenMap = buildTokenMap(categories);
|
|
904
|
+
sendJson({
|
|
905
|
+
categories,
|
|
906
|
+
tokenMap: resolvedTokenMap
|
|
907
|
+
});
|
|
908
|
+
} catch (e) {
|
|
909
|
+
sendError(e instanceof Error ? e.message : String(e));
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (req.url === "/tokens" && req.method === "DELETE") {
|
|
915
|
+
if (!tokensPath) {
|
|
916
|
+
sendError("No tokens path configured", 400);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
let body = "";
|
|
920
|
+
req.on("data", (chunk) => {
|
|
921
|
+
body += chunk;
|
|
922
|
+
});
|
|
923
|
+
req.on("end", async () => {
|
|
924
|
+
try {
|
|
925
|
+
const { path: dotPath } = JSON.parse(body);
|
|
926
|
+
if (!dotPath) {
|
|
927
|
+
sendError("Missing required field: path", 400);
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
const absoluteTokensPath = path.resolve(config.root, tokensPath);
|
|
931
|
+
const currentCategories = await parseTokens(absoluteTokensPath);
|
|
932
|
+
const currentMap = buildTokenMap(currentCategories);
|
|
933
|
+
const dependents = findDependentTokens(currentMap, dotPath);
|
|
934
|
+
const rawData = await readRawTokenFile(absoluteTokensPath);
|
|
935
|
+
const deleted = deleteTokenAtPath(rawData, dotPath);
|
|
936
|
+
if (!deleted) {
|
|
937
|
+
sendError(`Token not found at path "${dotPath}"`, 404);
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
await writeRawTokenFile(absoluteTokensPath, rawData);
|
|
941
|
+
const categories = await parseTokens(absoluteTokensPath);
|
|
942
|
+
const tokenMap = buildTokenMap(categories);
|
|
943
|
+
resolveReferences(categories, tokenMap);
|
|
944
|
+
const resolvedTokenMap = buildTokenMap(categories);
|
|
945
|
+
sendJson({
|
|
946
|
+
categories,
|
|
947
|
+
tokenMap: resolvedTokenMap,
|
|
948
|
+
dependentsWarning: dependents.length > 0 ? dependents : void 0
|
|
949
|
+
});
|
|
950
|
+
} catch (e) {
|
|
951
|
+
sendError(e instanceof Error ? e.message : String(e));
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
if (req.url?.startsWith("/arts/") && req.method === "PUT") {
|
|
957
|
+
const rest = req.url.slice(6);
|
|
958
|
+
const sourceMatch = rest.match(/^(.+)\/source$/);
|
|
959
|
+
if (sourceMatch) {
|
|
960
|
+
const artPath = decodeURIComponent(sourceMatch[1]);
|
|
961
|
+
const art = artFiles.get(artPath);
|
|
962
|
+
if (!art) {
|
|
963
|
+
sendError("Art not found", 404);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
let body = "";
|
|
967
|
+
req.on("data", (chunk) => {
|
|
968
|
+
body += chunk;
|
|
969
|
+
});
|
|
970
|
+
req.on("end", async () => {
|
|
971
|
+
try {
|
|
972
|
+
const { source } = JSON.parse(body);
|
|
973
|
+
if (typeof source !== "string") {
|
|
974
|
+
sendError("Missing required field: source", 400);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
await fs.promises.writeFile(artPath, source, "utf-8");
|
|
978
|
+
await processArtFile(artPath);
|
|
979
|
+
sendJson({ success: true });
|
|
980
|
+
} catch (e) {
|
|
981
|
+
sendError(e instanceof Error ? e.message : String(e));
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
next();
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
519
989
|
if (req.url?.startsWith("/arts/") && req.method === "GET") {
|
|
520
990
|
const rest = req.url.slice(6);
|
|
991
|
+
const sourceMatch = rest.match(/^(.+)\/source$/);
|
|
521
992
|
const paletteMatch = rest.match(/^(.+)\/palette$/);
|
|
522
993
|
const analysisMatch = rest.match(/^(.+)\/analysis$/);
|
|
523
994
|
const docsMatch = rest.match(/^(.+)\/docs$/);
|
|
524
995
|
const a11yMatch = rest.match(/^(.+)\/variants\/([^/]+)\/a11y$/);
|
|
996
|
+
if (sourceMatch) {
|
|
997
|
+
const artPath$1 = decodeURIComponent(sourceMatch[1]);
|
|
998
|
+
const art$1 = artFiles.get(artPath$1);
|
|
999
|
+
if (!art$1) {
|
|
1000
|
+
sendError("Art not found", 404);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
try {
|
|
1004
|
+
const source = await fs.promises.readFile(artPath$1, "utf-8");
|
|
1005
|
+
sendJson({
|
|
1006
|
+
source,
|
|
1007
|
+
path: artPath$1
|
|
1008
|
+
});
|
|
1009
|
+
} catch (e) {
|
|
1010
|
+
sendError(e instanceof Error ? e.message : String(e));
|
|
1011
|
+
}
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
525
1014
|
if (paletteMatch) {
|
|
526
1015
|
const artPath$1 = decodeURIComponent(paletteMatch[1]);
|
|
527
1016
|
const art$1 = artFiles.get(artPath$1);
|
|
@@ -2267,5 +2756,5 @@ function escapeHtml(str) {
|
|
|
2267
2756
|
var src_default = musea;
|
|
2268
2757
|
|
|
2269
2758
|
//#endregion
|
|
2270
|
-
export { MuseaA11yRunner, MuseaVrtRunner, src_default as default, generateArtFile, generateTokensHtml, generateTokensMarkdown, generateVrtJsonReport, generateVrtReport, musea, parseTokens, processStyleDictionary, writeArtFile };
|
|
2759
|
+
export { MuseaA11yRunner, MuseaVrtRunner, buildTokenMap, src_default as default, generateArtFile, generateTokensHtml, generateTokensMarkdown, generateVrtJsonReport, generateVrtReport, musea, parseTokens, processStyleDictionary, resolveReferences, scanTokenUsage, writeArtFile };
|
|
2271
2760
|
//# sourceMappingURL=index.js.map
|