@sourcescape/ds-cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-U44UTENA.js +492 -0
- package/dist/cli.js +58 -507
- package/dist/index.d.ts +127 -0
- package/dist/index.js +47 -0
- package/package.json +11 -2
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
// src/systems.ts
|
|
2
|
+
import { readFileSync, readdirSync, existsSync, statSync } from "fs";
|
|
3
|
+
import { join, resolve } from "path";
|
|
4
|
+
function getSystemsRoot() {
|
|
5
|
+
const dir = process.env.DESIGN_SYSTEMS_DIR;
|
|
6
|
+
if (!dir) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"DESIGN_SYSTEMS_DIR environment variable is required. Set it to the path containing your design system definitions."
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
return resolve(dir);
|
|
12
|
+
}
|
|
13
|
+
function readComponentJson(componentDir) {
|
|
14
|
+
const jsonPath = join(componentDir, "description.json");
|
|
15
|
+
if (!existsSync(jsonPath)) return null;
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(jsonPath, "utf-8"));
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function listSystems() {
|
|
23
|
+
return readdirSync(getSystemsRoot(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
24
|
+
}
|
|
25
|
+
function systemDir(system) {
|
|
26
|
+
return join(getSystemsRoot(), system);
|
|
27
|
+
}
|
|
28
|
+
function systemExists(system) {
|
|
29
|
+
return existsSync(systemDir(system));
|
|
30
|
+
}
|
|
31
|
+
function readFirstHeading(filePath) {
|
|
32
|
+
if (!existsSync(filePath)) return "";
|
|
33
|
+
const content = readFileSync(filePath, "utf-8");
|
|
34
|
+
const match = content.match(/^#\s+(.+)$/m);
|
|
35
|
+
return match ? match[1].trim() : "";
|
|
36
|
+
}
|
|
37
|
+
function readFirstParagraph(filePath) {
|
|
38
|
+
if (!existsSync(filePath)) return "";
|
|
39
|
+
const lines = readFileSync(filePath, "utf-8").split("\n");
|
|
40
|
+
let pastHeading = false;
|
|
41
|
+
for (const line of lines) {
|
|
42
|
+
if (line.startsWith("# ")) {
|
|
43
|
+
pastHeading = true;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (pastHeading && line.trim().length > 0 && !line.startsWith("#")) {
|
|
47
|
+
return line.trim();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
function readFile(filePath) {
|
|
53
|
+
return readFileSync(filePath, "utf-8");
|
|
54
|
+
}
|
|
55
|
+
function getSystemInfo(system) {
|
|
56
|
+
const descPath = join(systemDir(system), "DESCRIPTION.md");
|
|
57
|
+
return {
|
|
58
|
+
name: system,
|
|
59
|
+
description: readFirstHeading(descPath)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function readDescription(system) {
|
|
63
|
+
const descPath = join(systemDir(system), "DESCRIPTION.md");
|
|
64
|
+
if (!existsSync(descPath)) return "";
|
|
65
|
+
return readFile(descPath);
|
|
66
|
+
}
|
|
67
|
+
function listComponents(system) {
|
|
68
|
+
const compsDir = join(systemDir(system), "components");
|
|
69
|
+
const results = [];
|
|
70
|
+
for (const kind of ["molecules", "cells"]) {
|
|
71
|
+
const kindDir = join(compsDir, kind);
|
|
72
|
+
if (!existsSync(kindDir)) continue;
|
|
73
|
+
const dirs = readdirSync(kindDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
74
|
+
for (const name of dirs) {
|
|
75
|
+
const compDir = join(kindDir, name);
|
|
76
|
+
const json = readComponentJson(compDir);
|
|
77
|
+
if (json) {
|
|
78
|
+
results.push({
|
|
79
|
+
name,
|
|
80
|
+
kind,
|
|
81
|
+
description: json.description,
|
|
82
|
+
tag: json.tag,
|
|
83
|
+
example: json.example
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
const descPath = join(compDir, "description.md");
|
|
87
|
+
results.push({
|
|
88
|
+
name,
|
|
89
|
+
kind,
|
|
90
|
+
description: readFirstParagraph(descPath)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
function findComponent(system, name) {
|
|
98
|
+
const compsDir = join(systemDir(system), "components");
|
|
99
|
+
for (const kind of ["molecules", "cells"]) {
|
|
100
|
+
const dir = join(compsDir, kind, name);
|
|
101
|
+
if (existsSync(dir) && statSync(dir).isDirectory()) {
|
|
102
|
+
return { kind, dir };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function allComponentNames(system) {
|
|
108
|
+
const compsDir = join(systemDir(system), "components");
|
|
109
|
+
const results = [];
|
|
110
|
+
for (const kind of ["molecules", "cells"]) {
|
|
111
|
+
const kindDir = join(compsDir, kind);
|
|
112
|
+
if (!existsSync(kindDir)) continue;
|
|
113
|
+
const dirs = readdirSync(kindDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => ({ name: d.name, kind }));
|
|
114
|
+
results.push(...dirs);
|
|
115
|
+
}
|
|
116
|
+
return results;
|
|
117
|
+
}
|
|
118
|
+
function suggestComponent(system, query) {
|
|
119
|
+
const all = allComponentNames(system);
|
|
120
|
+
const q = query.toLowerCase();
|
|
121
|
+
return all.filter((c) => c.name.includes(q) || q.includes(c.name)).map((c) => `${c.name} (${c.kind})`);
|
|
122
|
+
}
|
|
123
|
+
function readComponentMeta(system, componentName) {
|
|
124
|
+
const found = findComponent(system, componentName);
|
|
125
|
+
if (!found) return { name: componentName, tag: "", description: "" };
|
|
126
|
+
const descPath = join(found.dir, "description.md");
|
|
127
|
+
if (!existsSync(descPath)) return { name: componentName, tag: "", description: "" };
|
|
128
|
+
const lines = readFileSync(descPath, "utf-8").split("\n");
|
|
129
|
+
let name = componentName;
|
|
130
|
+
let tag = "";
|
|
131
|
+
let description = "";
|
|
132
|
+
for (let i = 0; i < lines.length; i++) {
|
|
133
|
+
const line = lines[i];
|
|
134
|
+
if (line.startsWith("# ")) {
|
|
135
|
+
name = line.slice(2).trim();
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (!tag && /^`<.+>`$/.test(line.trim())) {
|
|
139
|
+
tag = line.trim();
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (name && line.trim().length > 0 && !line.startsWith("#") && !line.startsWith("`")) {
|
|
143
|
+
description = line.trim();
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { name, tag: tag || "*(auto)*", description };
|
|
148
|
+
}
|
|
149
|
+
function readFullDescription(system, name, kind) {
|
|
150
|
+
const compDir = join(systemDir(system), "components", kind, name);
|
|
151
|
+
if (!existsSync(compDir)) return "";
|
|
152
|
+
const mdPath = join(compDir, "description.md");
|
|
153
|
+
if (existsSync(mdPath)) {
|
|
154
|
+
return readFileSync(mdPath, "utf-8");
|
|
155
|
+
}
|
|
156
|
+
const json = readComponentJson(compDir);
|
|
157
|
+
return json?.description ?? "";
|
|
158
|
+
}
|
|
159
|
+
function descriptionMtime(system, name, kind) {
|
|
160
|
+
const compDir = join(systemDir(system), "components", kind, name);
|
|
161
|
+
if (!existsSync(compDir)) return 0;
|
|
162
|
+
const mdPath = join(compDir, "description.md");
|
|
163
|
+
if (existsSync(mdPath)) {
|
|
164
|
+
return statSync(mdPath).mtimeMs;
|
|
165
|
+
}
|
|
166
|
+
const jsonPath = join(compDir, "description.json");
|
|
167
|
+
if (existsSync(jsonPath)) {
|
|
168
|
+
return statSync(jsonPath).mtimeMs;
|
|
169
|
+
}
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
function findComponentSources(system, name) {
|
|
173
|
+
const found = findComponent(system, name);
|
|
174
|
+
if (!found) return { css: [], js: [] };
|
|
175
|
+
const css = [];
|
|
176
|
+
const js = [];
|
|
177
|
+
const stylePath = join(found.dir, "style.css");
|
|
178
|
+
const scriptPath = join(found.dir, "component.js");
|
|
179
|
+
if (existsSync(stylePath)) css.push(stylePath);
|
|
180
|
+
if (existsSync(scriptPath)) js.push(scriptPath);
|
|
181
|
+
return { css, js };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/tokens.ts
|
|
185
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
186
|
+
import { join as join2, basename } from "path";
|
|
187
|
+
function loadTokens(system) {
|
|
188
|
+
const dir = join2(systemDir(system), "tokens");
|
|
189
|
+
const files = readdirSync2(dir).filter((f) => f.endsWith(".json")).sort();
|
|
190
|
+
return files.map((f) => ({
|
|
191
|
+
name: basename(f, ".json"),
|
|
192
|
+
data: JSON.parse(readFileSync2(join2(dir, f), "utf-8"))
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
function walkTokens(obj, path = []) {
|
|
196
|
+
const leaves = [];
|
|
197
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
198
|
+
if (key.startsWith("$")) continue;
|
|
199
|
+
const child = val;
|
|
200
|
+
if (child.$type !== void 0 && child.$value !== void 0) {
|
|
201
|
+
leaves.push({ path: [...path, key], type: child.$type, value: child.$value });
|
|
202
|
+
} else if (typeof child === "object" && child !== null) {
|
|
203
|
+
leaves.push(...walkTokens(child, [...path, key]));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return leaves;
|
|
207
|
+
}
|
|
208
|
+
function toCssVarName(path) {
|
|
209
|
+
return `--${path.join("-")}`;
|
|
210
|
+
}
|
|
211
|
+
function formatValue(type, value) {
|
|
212
|
+
switch (type) {
|
|
213
|
+
case "color":
|
|
214
|
+
case "dimension":
|
|
215
|
+
case "gradient":
|
|
216
|
+
case "duration":
|
|
217
|
+
return String(value);
|
|
218
|
+
case "fontFamily": {
|
|
219
|
+
const families = value;
|
|
220
|
+
const generics = ["serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui"];
|
|
221
|
+
return families.map((f) => {
|
|
222
|
+
if (generics.includes(f) || !/\s/.test(f)) return f;
|
|
223
|
+
return f.includes("'") ? `"${f}"` : `'${f}'`;
|
|
224
|
+
}).join(", ");
|
|
225
|
+
}
|
|
226
|
+
// Composite types — skip
|
|
227
|
+
case "typography":
|
|
228
|
+
case "object":
|
|
229
|
+
case "cubicBezier":
|
|
230
|
+
return null;
|
|
231
|
+
default:
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function generateTokensCss(system) {
|
|
236
|
+
const tokenFiles = loadTokens(system);
|
|
237
|
+
const groups = [];
|
|
238
|
+
for (const { name, data } of tokenFiles) {
|
|
239
|
+
const leaves = walkTokens(data);
|
|
240
|
+
const vars = [];
|
|
241
|
+
for (const leaf of leaves) {
|
|
242
|
+
const formatted = formatValue(leaf.type, leaf.value);
|
|
243
|
+
if (formatted !== null) {
|
|
244
|
+
vars.push(` ${toCssVarName(leaf.path)}: ${formatted};`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (vars.length > 0) {
|
|
248
|
+
groups.push({ name, vars });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const lines = ["@layer tokens {", " :root {"];
|
|
252
|
+
for (let i = 0; i < groups.length; i++) {
|
|
253
|
+
const { name, vars } = groups[i];
|
|
254
|
+
if (i > 0) lines.push("");
|
|
255
|
+
lines.push(` /* ${name}.json */`);
|
|
256
|
+
lines.push(...vars);
|
|
257
|
+
}
|
|
258
|
+
lines.push(" }", "}");
|
|
259
|
+
return lines.join("\n") + "\n";
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/resolve-deps.ts
|
|
263
|
+
import { readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
|
|
264
|
+
import { join as join3, relative } from "path";
|
|
265
|
+
function buildRegistry(system) {
|
|
266
|
+
const components = listComponents(system);
|
|
267
|
+
const entries = [];
|
|
268
|
+
const tagMap = /* @__PURE__ */ new Map();
|
|
269
|
+
for (const comp of components) {
|
|
270
|
+
const found = findComponent(system, comp.name);
|
|
271
|
+
if (!found) continue;
|
|
272
|
+
const scriptPath = join3(found.dir, "component.js");
|
|
273
|
+
const stylePath = join3(found.dir, "style.css");
|
|
274
|
+
const hasJs = existsSync2(scriptPath);
|
|
275
|
+
const hasCss = existsSync2(stylePath);
|
|
276
|
+
const tags = [];
|
|
277
|
+
const dynamicTags = [];
|
|
278
|
+
if (hasJs) {
|
|
279
|
+
const js = readFileSync3(scriptPath, "utf-8");
|
|
280
|
+
for (const m of js.matchAll(/customElements\.define\(\s*['"]([^'"]+)['"]/g)) {
|
|
281
|
+
tags.push(m[1]);
|
|
282
|
+
}
|
|
283
|
+
for (const m of js.matchAll(/document\.createElement\(\s*['"]([a-z][a-z0-9]*-[a-z0-9-]*)['"]\s*\)/g)) {
|
|
284
|
+
if (!tags.includes(m[1])) dynamicTags.push(m[1]);
|
|
285
|
+
}
|
|
286
|
+
for (const m of js.matchAll(/<([a-z][a-z0-9]*-[a-z0-9-]*)[>\s/'"]/g)) {
|
|
287
|
+
const tag = m[1];
|
|
288
|
+
if (!tags.includes(tag) && !dynamicTags.includes(tag)) {
|
|
289
|
+
dynamicTags.push(tag);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (hasCss) {
|
|
294
|
+
const css = readFileSync3(stylePath, "utf-8");
|
|
295
|
+
for (const m of css.matchAll(/(?:^|[\s,}>+~])([a-z][a-z0-9]*-[a-z0-9-]*)(?=[\s,{[:.>+~]|$)/gm)) {
|
|
296
|
+
const tag = m[1];
|
|
297
|
+
if (!tags.includes(tag)) tags.push(tag);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const dirName = comp.name;
|
|
301
|
+
if (dirName.includes("-") && !tags.includes(dirName)) {
|
|
302
|
+
tags.push(dirName);
|
|
303
|
+
}
|
|
304
|
+
const prdName = `prd-${dirName}`;
|
|
305
|
+
if (!tags.includes(prdName)) {
|
|
306
|
+
tags.push(prdName);
|
|
307
|
+
}
|
|
308
|
+
const entry = {
|
|
309
|
+
name: comp.name,
|
|
310
|
+
kind: comp.kind,
|
|
311
|
+
dir: found.dir,
|
|
312
|
+
tags,
|
|
313
|
+
dynamicTags,
|
|
314
|
+
hasCss,
|
|
315
|
+
hasJs
|
|
316
|
+
};
|
|
317
|
+
entries.push(entry);
|
|
318
|
+
for (const tag of tags) {
|
|
319
|
+
if (!tagMap.has(tag)) tagMap.set(tag, entry);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return { tagMap, entries };
|
|
323
|
+
}
|
|
324
|
+
function resolveMarkup(registry, markup) {
|
|
325
|
+
const markupTags = /* @__PURE__ */ new Set();
|
|
326
|
+
for (const m of markup.matchAll(/<([a-z][a-z0-9]*-[a-z0-9-]*)/g)) {
|
|
327
|
+
markupTags.add(m[1]);
|
|
328
|
+
}
|
|
329
|
+
const matched = /* @__PURE__ */ new Set();
|
|
330
|
+
const unmatchedTags = [];
|
|
331
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
332
|
+
const queue = [...markupTags];
|
|
333
|
+
while (queue.length > 0) {
|
|
334
|
+
const tag = queue.pop();
|
|
335
|
+
if (resolved.has(tag)) continue;
|
|
336
|
+
resolved.add(tag);
|
|
337
|
+
const entry = registry.tagMap.get(tag);
|
|
338
|
+
if (entry) {
|
|
339
|
+
if (!matched.has(entry)) {
|
|
340
|
+
matched.add(entry);
|
|
341
|
+
for (const dt of entry.dynamicTags) {
|
|
342
|
+
if (!resolved.has(dt)) queue.push(dt);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
unmatchedTags.push(tag);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const cssFiles = [];
|
|
350
|
+
const jsFiles = [];
|
|
351
|
+
for (const entry of matched) {
|
|
352
|
+
if (entry.hasCss) cssFiles.push(join3(entry.dir, "style.css"));
|
|
353
|
+
if (entry.hasJs) jsFiles.push(join3(entry.dir, "component.js"));
|
|
354
|
+
}
|
|
355
|
+
return { cssFiles, jsFiles, unmatchedTags };
|
|
356
|
+
}
|
|
357
|
+
async function bundleJs(system) {
|
|
358
|
+
const entrypoint = join3(systemDir(system), "src", "index.js");
|
|
359
|
+
if (!existsSync2(entrypoint)) return null;
|
|
360
|
+
const { build } = await import("esbuild");
|
|
361
|
+
const result = await build({
|
|
362
|
+
entryPoints: [entrypoint],
|
|
363
|
+
bundle: true,
|
|
364
|
+
write: false,
|
|
365
|
+
format: "esm"
|
|
366
|
+
});
|
|
367
|
+
if (result.outputFiles.length === 0) return null;
|
|
368
|
+
return result.outputFiles[0].text;
|
|
369
|
+
}
|
|
370
|
+
function buildPreviewHtml(system, markup, deps, bundledJs) {
|
|
371
|
+
const sysDir = systemDir(system);
|
|
372
|
+
const blocks = ["@layer tokens, base, components;"];
|
|
373
|
+
const tokensCss = generateTokensCss(system);
|
|
374
|
+
blocks.push("/* tokens (generated) */");
|
|
375
|
+
blocks.push(tokensCss);
|
|
376
|
+
const baseCss = join3(sysDir, "src", "_shared", "_base.css");
|
|
377
|
+
if (existsSync2(baseCss)) {
|
|
378
|
+
blocks.push(`/* ${relative(sysDir, baseCss)} */`);
|
|
379
|
+
blocks.push(readFileSync3(baseCss, "utf-8"));
|
|
380
|
+
}
|
|
381
|
+
for (const p of deps.cssFiles) {
|
|
382
|
+
blocks.push(`/* ${relative(sysDir, p)} */`);
|
|
383
|
+
blocks.push(readFileSync3(p, "utf-8"));
|
|
384
|
+
}
|
|
385
|
+
const inlinedCss = blocks.join("\n");
|
|
386
|
+
const scriptTags = bundledJs ? ` <script>
|
|
387
|
+
${bundledJs}
|
|
388
|
+
</script>` : deps.jsFiles.map((p) => ` <script type="module" src="/${relative(sysDir, p)}"></script>`).join("\n");
|
|
389
|
+
return `<!DOCTYPE html>
|
|
390
|
+
<html lang="en">
|
|
391
|
+
<head>
|
|
392
|
+
<meta charset="UTF-8">
|
|
393
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
394
|
+
<style>
|
|
395
|
+
${inlinedCss}
|
|
396
|
+
</style>
|
|
397
|
+
</head>
|
|
398
|
+
<body>
|
|
399
|
+
${markup}
|
|
400
|
+
${scriptTags}
|
|
401
|
+
</body>
|
|
402
|
+
</html>`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/preview-server.ts
|
|
406
|
+
import { createServer } from "http";
|
|
407
|
+
import { join as join4, extname } from "path";
|
|
408
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
409
|
+
import { existsSync as existsSync3 } from "fs";
|
|
410
|
+
var MIME_TYPES = {
|
|
411
|
+
".html": "text/html; charset=utf-8",
|
|
412
|
+
".css": "text/css; charset=utf-8",
|
|
413
|
+
".js": "application/javascript; charset=utf-8",
|
|
414
|
+
".json": "application/json; charset=utf-8",
|
|
415
|
+
".svg": "image/svg+xml",
|
|
416
|
+
".png": "image/png",
|
|
417
|
+
".jpg": "image/jpeg",
|
|
418
|
+
".jpeg": "image/jpeg",
|
|
419
|
+
".gif": "image/gif",
|
|
420
|
+
".woff": "font/woff",
|
|
421
|
+
".woff2": "font/woff2",
|
|
422
|
+
".ttf": "font/ttf",
|
|
423
|
+
".otf": "font/otf",
|
|
424
|
+
".eot": "application/vnd.ms-fontobject"
|
|
425
|
+
};
|
|
426
|
+
function startPreviewServer(systemDir2, opts) {
|
|
427
|
+
return new Promise((resolve2, reject) => {
|
|
428
|
+
const server = createServer(async (req, res) => {
|
|
429
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
430
|
+
if (url.pathname === "/" && opts.html) {
|
|
431
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
432
|
+
res.end(opts.html);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const filePath = join4(systemDir2, decodeURIComponent(url.pathname));
|
|
436
|
+
if (!existsSync3(filePath)) {
|
|
437
|
+
res.writeHead(404);
|
|
438
|
+
res.end("Not found");
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
const data = await readFile2(filePath);
|
|
443
|
+
const ext = extname(filePath).toLowerCase();
|
|
444
|
+
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
445
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
446
|
+
res.end(data);
|
|
447
|
+
} catch {
|
|
448
|
+
res.writeHead(500);
|
|
449
|
+
res.end("Internal server error");
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
server.listen(0, () => {
|
|
453
|
+
const addr = server.address();
|
|
454
|
+
if (!addr || typeof addr === "string") {
|
|
455
|
+
reject(new Error("Failed to get server address"));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
resolve2({
|
|
459
|
+
url: `http://localhost:${addr.port}`,
|
|
460
|
+
stop: () => server.close()
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
server.on("error", reject);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export {
|
|
468
|
+
readComponentJson,
|
|
469
|
+
listSystems,
|
|
470
|
+
systemDir,
|
|
471
|
+
systemExists,
|
|
472
|
+
readFile,
|
|
473
|
+
getSystemInfo,
|
|
474
|
+
readDescription,
|
|
475
|
+
listComponents,
|
|
476
|
+
findComponent,
|
|
477
|
+
suggestComponent,
|
|
478
|
+
readComponentMeta,
|
|
479
|
+
readFullDescription,
|
|
480
|
+
descriptionMtime,
|
|
481
|
+
findComponentSources,
|
|
482
|
+
loadTokens,
|
|
483
|
+
walkTokens,
|
|
484
|
+
toCssVarName,
|
|
485
|
+
formatValue,
|
|
486
|
+
generateTokensCss,
|
|
487
|
+
buildRegistry,
|
|
488
|
+
resolveMarkup,
|
|
489
|
+
bundleJs,
|
|
490
|
+
buildPreviewHtml,
|
|
491
|
+
startPreviewServer
|
|
492
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,191 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildPreviewHtml,
|
|
4
|
+
buildRegistry,
|
|
5
|
+
bundleJs,
|
|
6
|
+
descriptionMtime,
|
|
7
|
+
findComponent,
|
|
8
|
+
findComponentSources,
|
|
9
|
+
getSystemInfo,
|
|
10
|
+
listComponents,
|
|
11
|
+
listSystems,
|
|
12
|
+
readComponentJson,
|
|
13
|
+
readComponentMeta,
|
|
14
|
+
readDescription,
|
|
15
|
+
readFile,
|
|
16
|
+
readFullDescription,
|
|
17
|
+
resolveMarkup,
|
|
18
|
+
startPreviewServer,
|
|
19
|
+
suggestComponent,
|
|
20
|
+
systemDir,
|
|
21
|
+
systemExists
|
|
22
|
+
} from "./chunk-U44UTENA.js";
|
|
2
23
|
|
|
3
24
|
// src/cli.ts
|
|
4
25
|
import { parseArgs } from "util";
|
|
5
26
|
|
|
6
|
-
// src/systems.ts
|
|
7
|
-
import { readFileSync, readdirSync, existsSync, statSync } from "fs";
|
|
8
|
-
import { join, resolve } from "path";
|
|
9
|
-
function getSystemsRoot() {
|
|
10
|
-
const dir = process.env.DESIGN_SYSTEMS_DIR;
|
|
11
|
-
if (!dir) {
|
|
12
|
-
console.error("Error: DESIGN_SYSTEMS_DIR environment variable is required.");
|
|
13
|
-
console.error("Set it to the path containing your design system definitions.");
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
return resolve(dir);
|
|
17
|
-
}
|
|
18
|
-
function readComponentJson(componentDir) {
|
|
19
|
-
const jsonPath = join(componentDir, "description.json");
|
|
20
|
-
if (!existsSync(jsonPath)) return null;
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(readFileSync(jsonPath, "utf-8"));
|
|
23
|
-
} catch {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function listSystems() {
|
|
28
|
-
return readdirSync(getSystemsRoot(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
29
|
-
}
|
|
30
|
-
function systemDir(system) {
|
|
31
|
-
return join(getSystemsRoot(), system);
|
|
32
|
-
}
|
|
33
|
-
function systemExists(system) {
|
|
34
|
-
return existsSync(systemDir(system));
|
|
35
|
-
}
|
|
36
|
-
function readFirstHeading(filePath) {
|
|
37
|
-
if (!existsSync(filePath)) return "";
|
|
38
|
-
const content = readFileSync(filePath, "utf-8");
|
|
39
|
-
const match = content.match(/^#\s+(.+)$/m);
|
|
40
|
-
return match ? match[1].trim() : "";
|
|
41
|
-
}
|
|
42
|
-
function readFirstParagraph(filePath) {
|
|
43
|
-
if (!existsSync(filePath)) return "";
|
|
44
|
-
const lines = readFileSync(filePath, "utf-8").split("\n");
|
|
45
|
-
let pastHeading = false;
|
|
46
|
-
for (const line of lines) {
|
|
47
|
-
if (line.startsWith("# ")) {
|
|
48
|
-
pastHeading = true;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (pastHeading && line.trim().length > 0 && !line.startsWith("#")) {
|
|
52
|
-
return line.trim();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return "";
|
|
56
|
-
}
|
|
57
|
-
function readFile(filePath) {
|
|
58
|
-
return readFileSync(filePath, "utf-8");
|
|
59
|
-
}
|
|
60
|
-
function getSystemInfo(system) {
|
|
61
|
-
const descPath = join(systemDir(system), "DESCRIPTION.md");
|
|
62
|
-
return {
|
|
63
|
-
name: system,
|
|
64
|
-
description: readFirstHeading(descPath)
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
function readDescription(system) {
|
|
68
|
-
const descPath = join(systemDir(system), "DESCRIPTION.md");
|
|
69
|
-
if (!existsSync(descPath)) return "";
|
|
70
|
-
return readFile(descPath);
|
|
71
|
-
}
|
|
72
|
-
function listComponents(system) {
|
|
73
|
-
const compsDir = join(systemDir(system), "components");
|
|
74
|
-
const results = [];
|
|
75
|
-
for (const kind of ["molecules", "cells"]) {
|
|
76
|
-
const kindDir = join(compsDir, kind);
|
|
77
|
-
if (!existsSync(kindDir)) continue;
|
|
78
|
-
const dirs = readdirSync(kindDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
79
|
-
for (const name of dirs) {
|
|
80
|
-
const compDir = join(kindDir, name);
|
|
81
|
-
const json = readComponentJson(compDir);
|
|
82
|
-
if (json) {
|
|
83
|
-
results.push({
|
|
84
|
-
name,
|
|
85
|
-
kind,
|
|
86
|
-
description: json.description,
|
|
87
|
-
tag: json.tag,
|
|
88
|
-
example: json.example
|
|
89
|
-
});
|
|
90
|
-
} else {
|
|
91
|
-
const descPath = join(compDir, "description.md");
|
|
92
|
-
results.push({
|
|
93
|
-
name,
|
|
94
|
-
kind,
|
|
95
|
-
description: readFirstParagraph(descPath)
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return results;
|
|
101
|
-
}
|
|
102
|
-
function findComponent(system, name) {
|
|
103
|
-
const compsDir = join(systemDir(system), "components");
|
|
104
|
-
for (const kind of ["molecules", "cells"]) {
|
|
105
|
-
const dir = join(compsDir, kind, name);
|
|
106
|
-
if (existsSync(dir) && statSync(dir).isDirectory()) {
|
|
107
|
-
return { kind, dir };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
function allComponentNames(system) {
|
|
113
|
-
const compsDir = join(systemDir(system), "components");
|
|
114
|
-
const results = [];
|
|
115
|
-
for (const kind of ["molecules", "cells"]) {
|
|
116
|
-
const kindDir = join(compsDir, kind);
|
|
117
|
-
if (!existsSync(kindDir)) continue;
|
|
118
|
-
const dirs = readdirSync(kindDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => ({ name: d.name, kind }));
|
|
119
|
-
results.push(...dirs);
|
|
120
|
-
}
|
|
121
|
-
return results;
|
|
122
|
-
}
|
|
123
|
-
function suggestComponent(system, query) {
|
|
124
|
-
const all = allComponentNames(system);
|
|
125
|
-
const q = query.toLowerCase();
|
|
126
|
-
return all.filter((c) => c.name.includes(q) || q.includes(c.name)).map((c) => `${c.name} (${c.kind})`);
|
|
127
|
-
}
|
|
128
|
-
function readComponentMeta(system, componentName) {
|
|
129
|
-
const found = findComponent(system, componentName);
|
|
130
|
-
if (!found) return { name: componentName, tag: "", description: "" };
|
|
131
|
-
const descPath = join(found.dir, "description.md");
|
|
132
|
-
if (!existsSync(descPath)) return { name: componentName, tag: "", description: "" };
|
|
133
|
-
const lines = readFileSync(descPath, "utf-8").split("\n");
|
|
134
|
-
let name = componentName;
|
|
135
|
-
let tag = "";
|
|
136
|
-
let description = "";
|
|
137
|
-
for (let i = 0; i < lines.length; i++) {
|
|
138
|
-
const line = lines[i];
|
|
139
|
-
if (line.startsWith("# ")) {
|
|
140
|
-
name = line.slice(2).trim();
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (!tag && /^`<.+>`$/.test(line.trim())) {
|
|
144
|
-
tag = line.trim();
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (name && line.trim().length > 0 && !line.startsWith("#") && !line.startsWith("`")) {
|
|
148
|
-
description = line.trim();
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return { name, tag: tag || "*(auto)*", description };
|
|
153
|
-
}
|
|
154
|
-
function readFullDescription(system, name, kind) {
|
|
155
|
-
const compDir = join(systemDir(system), "components", kind, name);
|
|
156
|
-
if (!existsSync(compDir)) return "";
|
|
157
|
-
const mdPath = join(compDir, "description.md");
|
|
158
|
-
if (existsSync(mdPath)) {
|
|
159
|
-
return readFileSync(mdPath, "utf-8");
|
|
160
|
-
}
|
|
161
|
-
const json = readComponentJson(compDir);
|
|
162
|
-
return json?.description ?? "";
|
|
163
|
-
}
|
|
164
|
-
function descriptionMtime(system, name, kind) {
|
|
165
|
-
const compDir = join(systemDir(system), "components", kind, name);
|
|
166
|
-
if (!existsSync(compDir)) return 0;
|
|
167
|
-
const mdPath = join(compDir, "description.md");
|
|
168
|
-
if (existsSync(mdPath)) {
|
|
169
|
-
return statSync(mdPath).mtimeMs;
|
|
170
|
-
}
|
|
171
|
-
const jsonPath = join(compDir, "description.json");
|
|
172
|
-
if (existsSync(jsonPath)) {
|
|
173
|
-
return statSync(jsonPath).mtimeMs;
|
|
174
|
-
}
|
|
175
|
-
return 0;
|
|
176
|
-
}
|
|
177
|
-
function findComponentSources(system, name) {
|
|
178
|
-
const found = findComponent(system, name);
|
|
179
|
-
if (!found) return { css: [], js: [] };
|
|
180
|
-
const css = [];
|
|
181
|
-
const js = [];
|
|
182
|
-
const stylePath = join(found.dir, "style.css");
|
|
183
|
-
const scriptPath = join(found.dir, "component.js");
|
|
184
|
-
if (existsSync(stylePath)) css.push(stylePath);
|
|
185
|
-
if (existsSync(scriptPath)) js.push(scriptPath);
|
|
186
|
-
return { css, js };
|
|
187
|
-
}
|
|
188
|
-
|
|
189
27
|
// src/format.ts
|
|
190
28
|
function heading(text) {
|
|
191
29
|
console.log(`
|
|
@@ -236,12 +74,12 @@ function commandList() {
|
|
|
236
74
|
}
|
|
237
75
|
|
|
238
76
|
// src/commands/show.ts
|
|
239
|
-
import { existsSync
|
|
240
|
-
import { join
|
|
77
|
+
import { existsSync, readFileSync } from "fs";
|
|
78
|
+
import { join } from "path";
|
|
241
79
|
function loadManifest(system) {
|
|
242
|
-
const path =
|
|
243
|
-
if (!
|
|
244
|
-
const data = JSON.parse(
|
|
80
|
+
const path = join(systemDir(system), "references", "manifest.json");
|
|
81
|
+
if (!existsSync(path)) return [];
|
|
82
|
+
const data = JSON.parse(readFileSync(path, "utf-8"));
|
|
245
83
|
return data.references || [];
|
|
246
84
|
}
|
|
247
85
|
function commandShow(system) {
|
|
@@ -292,8 +130,8 @@ function commandShow(system) {
|
|
|
292
130
|
import { exec } from "child_process";
|
|
293
131
|
|
|
294
132
|
// src/commands/components.ts
|
|
295
|
-
import { join as
|
|
296
|
-
import { existsSync as
|
|
133
|
+
import { join as join3, basename, relative } from "path";
|
|
134
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync } from "fs";
|
|
297
135
|
|
|
298
136
|
// src/search/tfidf.ts
|
|
299
137
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -420,11 +258,11 @@ function cosineSimilarity(a, b) {
|
|
|
420
258
|
}
|
|
421
259
|
|
|
422
260
|
// src/search/indexer.ts
|
|
423
|
-
import { readFileSync as
|
|
424
|
-
import { join as
|
|
261
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
262
|
+
import { join as join2 } from "path";
|
|
425
263
|
var INDEX_FILE = ".search-index.json";
|
|
426
264
|
function indexPath(system) {
|
|
427
|
-
return
|
|
265
|
+
return join2(systemDir(system), INDEX_FILE);
|
|
428
266
|
}
|
|
429
267
|
function buildSearchIndex(system) {
|
|
430
268
|
const components = listComponents(system);
|
|
@@ -471,9 +309,9 @@ function writeSearchIndex(system, index) {
|
|
|
471
309
|
}
|
|
472
310
|
function loadSearchIndex(system) {
|
|
473
311
|
const path = indexPath(system);
|
|
474
|
-
if (!
|
|
312
|
+
if (!existsSync2(path)) return null;
|
|
475
313
|
try {
|
|
476
|
-
const data = JSON.parse(
|
|
314
|
+
const data = JSON.parse(readFileSync2(path, "utf-8"));
|
|
477
315
|
if (data.version !== 1) return null;
|
|
478
316
|
return data;
|
|
479
317
|
} catch {
|
|
@@ -633,7 +471,7 @@ function showDetails(system, name) {
|
|
|
633
471
|
console.log(` Kind: ${found.kind}`);
|
|
634
472
|
if (json?.tag) console.log(` Tag: ${json.tag}`);
|
|
635
473
|
console.log(` Path: ${relPath}`);
|
|
636
|
-
const files =
|
|
474
|
+
const files = readdirSync(found.dir);
|
|
637
475
|
console.log(` Files: ${files.join(", ")}`);
|
|
638
476
|
blank();
|
|
639
477
|
if (json?.description) {
|
|
@@ -650,8 +488,8 @@ function showDetails(system, name) {
|
|
|
650
488
|
console.log("```");
|
|
651
489
|
blank();
|
|
652
490
|
}
|
|
653
|
-
const descPath =
|
|
654
|
-
if (
|
|
491
|
+
const descPath = join3(found.dir, "description.md");
|
|
492
|
+
if (existsSync3(descPath)) {
|
|
655
493
|
subheading("Description (full)");
|
|
656
494
|
blank();
|
|
657
495
|
console.log(readFile(descPath));
|
|
@@ -662,7 +500,7 @@ function showDetails(system, name) {
|
|
|
662
500
|
subheading(`Source CSS: ${basename(cssPath)}`);
|
|
663
501
|
blank();
|
|
664
502
|
console.log("```css");
|
|
665
|
-
console.log(
|
|
503
|
+
console.log(readFileSync3(cssPath, "utf-8").trimEnd());
|
|
666
504
|
console.log("```");
|
|
667
505
|
blank();
|
|
668
506
|
}
|
|
@@ -672,7 +510,7 @@ function showDetails(system, name) {
|
|
|
672
510
|
subheading(`Source JS: ${basename(jsPath)}`);
|
|
673
511
|
blank();
|
|
674
512
|
console.log("```js");
|
|
675
|
-
console.log(
|
|
513
|
+
console.log(readFileSync3(jsPath, "utf-8").trimEnd());
|
|
676
514
|
console.log("```");
|
|
677
515
|
blank();
|
|
678
516
|
}
|
|
@@ -746,24 +584,24 @@ function rebuildIndex(system) {
|
|
|
746
584
|
}
|
|
747
585
|
|
|
748
586
|
// src/commands/references.ts
|
|
749
|
-
import { existsSync as
|
|
750
|
-
import { join as
|
|
587
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
|
|
588
|
+
import { join as join4, extname } from "path";
|
|
751
589
|
function referencesDir(system) {
|
|
752
|
-
return
|
|
590
|
+
return join4(systemDir(system), "references");
|
|
753
591
|
}
|
|
754
592
|
function loadManifest2(system) {
|
|
755
|
-
const path =
|
|
756
|
-
if (!
|
|
757
|
-
return JSON.parse(
|
|
593
|
+
const path = join4(referencesDir(system), "manifest.json");
|
|
594
|
+
if (!existsSync4(path)) return null;
|
|
595
|
+
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
758
596
|
}
|
|
759
597
|
function listReferenceFiles(system) {
|
|
760
598
|
const dir = referencesDir(system);
|
|
761
|
-
if (!
|
|
762
|
-
return
|
|
599
|
+
if (!existsSync4(dir)) return [];
|
|
600
|
+
return readdirSync2(dir).filter((f) => f !== "manifest.json").sort();
|
|
763
601
|
}
|
|
764
602
|
function commandReferences(system, segments) {
|
|
765
603
|
const dir = referencesDir(system);
|
|
766
|
-
if (!
|
|
604
|
+
if (!existsSync4(dir)) {
|
|
767
605
|
error(`No references directory for "${system}"`);
|
|
768
606
|
return 1;
|
|
769
607
|
}
|
|
@@ -793,8 +631,8 @@ function commandReferences(system, segments) {
|
|
|
793
631
|
return 0;
|
|
794
632
|
}
|
|
795
633
|
const fileName = segments.join("/");
|
|
796
|
-
const filePath =
|
|
797
|
-
if (!
|
|
634
|
+
const filePath = join4(dir, fileName);
|
|
635
|
+
if (!existsSync4(filePath)) {
|
|
798
636
|
const files = listReferenceFiles(system);
|
|
799
637
|
const suggestions = files.filter(
|
|
800
638
|
(f) => f.includes(fileName) || fileName.includes(f.replace(extname(f), ""))
|
|
@@ -802,301 +640,14 @@ function commandReferences(system, segments) {
|
|
|
802
640
|
error(`Reference "${fileName}" not found`, suggestions);
|
|
803
641
|
return 1;
|
|
804
642
|
}
|
|
805
|
-
const content =
|
|
643
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
806
644
|
process.stdout.write(content);
|
|
807
645
|
return 0;
|
|
808
646
|
}
|
|
809
647
|
|
|
810
648
|
// src/commands/render.ts
|
|
811
|
-
import { readFileSync as
|
|
812
|
-
import { resolve
|
|
813
|
-
|
|
814
|
-
// src/resolve-deps.ts
|
|
815
|
-
import { readFileSync as readFileSync7, existsSync as existsSync6 } from "fs";
|
|
816
|
-
import { join as join7, relative as relative2 } from "path";
|
|
817
|
-
|
|
818
|
-
// src/tokens.ts
|
|
819
|
-
import { readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
|
|
820
|
-
import { join as join6, basename as basename2 } from "path";
|
|
821
|
-
function loadTokens(system) {
|
|
822
|
-
const dir = join6(systemDir(system), "tokens");
|
|
823
|
-
const files = readdirSync4(dir).filter((f) => f.endsWith(".json")).sort();
|
|
824
|
-
return files.map((f) => ({
|
|
825
|
-
name: basename2(f, ".json"),
|
|
826
|
-
data: JSON.parse(readFileSync6(join6(dir, f), "utf-8"))
|
|
827
|
-
}));
|
|
828
|
-
}
|
|
829
|
-
function walkTokens(obj, path = []) {
|
|
830
|
-
const leaves = [];
|
|
831
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
832
|
-
if (key.startsWith("$")) continue;
|
|
833
|
-
const child = val;
|
|
834
|
-
if (child.$type !== void 0 && child.$value !== void 0) {
|
|
835
|
-
leaves.push({ path: [...path, key], type: child.$type, value: child.$value });
|
|
836
|
-
} else if (typeof child === "object" && child !== null) {
|
|
837
|
-
leaves.push(...walkTokens(child, [...path, key]));
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
return leaves;
|
|
841
|
-
}
|
|
842
|
-
function toCssVarName(path) {
|
|
843
|
-
return `--${path.join("-")}`;
|
|
844
|
-
}
|
|
845
|
-
function formatValue(type, value) {
|
|
846
|
-
switch (type) {
|
|
847
|
-
case "color":
|
|
848
|
-
case "dimension":
|
|
849
|
-
case "gradient":
|
|
850
|
-
case "duration":
|
|
851
|
-
return String(value);
|
|
852
|
-
case "fontFamily": {
|
|
853
|
-
const families = value;
|
|
854
|
-
const generics = ["serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui"];
|
|
855
|
-
return families.map((f) => {
|
|
856
|
-
if (generics.includes(f) || !/\s/.test(f)) return f;
|
|
857
|
-
return f.includes("'") ? `"${f}"` : `'${f}'`;
|
|
858
|
-
}).join(", ");
|
|
859
|
-
}
|
|
860
|
-
// Composite types — skip
|
|
861
|
-
case "typography":
|
|
862
|
-
case "object":
|
|
863
|
-
case "cubicBezier":
|
|
864
|
-
return null;
|
|
865
|
-
default:
|
|
866
|
-
return null;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
function generateTokensCss(system) {
|
|
870
|
-
const tokenFiles = loadTokens(system);
|
|
871
|
-
const groups = [];
|
|
872
|
-
for (const { name, data } of tokenFiles) {
|
|
873
|
-
const leaves = walkTokens(data);
|
|
874
|
-
const vars = [];
|
|
875
|
-
for (const leaf of leaves) {
|
|
876
|
-
const formatted = formatValue(leaf.type, leaf.value);
|
|
877
|
-
if (formatted !== null) {
|
|
878
|
-
vars.push(` ${toCssVarName(leaf.path)}: ${formatted};`);
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
if (vars.length > 0) {
|
|
882
|
-
groups.push({ name, vars });
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
const lines = ["@layer tokens {", " :root {"];
|
|
886
|
-
for (let i = 0; i < groups.length; i++) {
|
|
887
|
-
const { name, vars } = groups[i];
|
|
888
|
-
if (i > 0) lines.push("");
|
|
889
|
-
lines.push(` /* ${name}.json */`);
|
|
890
|
-
lines.push(...vars);
|
|
891
|
-
}
|
|
892
|
-
lines.push(" }", "}");
|
|
893
|
-
return lines.join("\n") + "\n";
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// src/resolve-deps.ts
|
|
897
|
-
function buildRegistry(system) {
|
|
898
|
-
const components = listComponents(system);
|
|
899
|
-
const entries = [];
|
|
900
|
-
const tagMap = /* @__PURE__ */ new Map();
|
|
901
|
-
for (const comp of components) {
|
|
902
|
-
const found = findComponent(system, comp.name);
|
|
903
|
-
if (!found) continue;
|
|
904
|
-
const scriptPath = join7(found.dir, "component.js");
|
|
905
|
-
const stylePath = join7(found.dir, "style.css");
|
|
906
|
-
const hasJs = existsSync6(scriptPath);
|
|
907
|
-
const hasCss = existsSync6(stylePath);
|
|
908
|
-
const tags = [];
|
|
909
|
-
const dynamicTags = [];
|
|
910
|
-
if (hasJs) {
|
|
911
|
-
const js = readFileSync7(scriptPath, "utf-8");
|
|
912
|
-
for (const m of js.matchAll(/customElements\.define\(\s*['"]([^'"]+)['"]/g)) {
|
|
913
|
-
tags.push(m[1]);
|
|
914
|
-
}
|
|
915
|
-
for (const m of js.matchAll(/document\.createElement\(\s*['"]([a-z][a-z0-9]*-[a-z0-9-]*)['"]\s*\)/g)) {
|
|
916
|
-
if (!tags.includes(m[1])) dynamicTags.push(m[1]);
|
|
917
|
-
}
|
|
918
|
-
for (const m of js.matchAll(/<([a-z][a-z0-9]*-[a-z0-9-]*)[>\s/'"]/g)) {
|
|
919
|
-
const tag = m[1];
|
|
920
|
-
if (!tags.includes(tag) && !dynamicTags.includes(tag)) {
|
|
921
|
-
dynamicTags.push(tag);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
if (hasCss) {
|
|
926
|
-
const css = readFileSync7(stylePath, "utf-8");
|
|
927
|
-
for (const m of css.matchAll(/(?:^|[\s,}>+~])([a-z][a-z0-9]*-[a-z0-9-]*)(?=[\s,{[:.>+~]|$)/gm)) {
|
|
928
|
-
const tag = m[1];
|
|
929
|
-
if (!tags.includes(tag)) tags.push(tag);
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
const dirName = comp.name;
|
|
933
|
-
if (dirName.includes("-") && !tags.includes(dirName)) {
|
|
934
|
-
tags.push(dirName);
|
|
935
|
-
}
|
|
936
|
-
const prdName = `prd-${dirName}`;
|
|
937
|
-
if (!tags.includes(prdName)) {
|
|
938
|
-
tags.push(prdName);
|
|
939
|
-
}
|
|
940
|
-
const entry = {
|
|
941
|
-
name: comp.name,
|
|
942
|
-
kind: comp.kind,
|
|
943
|
-
dir: found.dir,
|
|
944
|
-
tags,
|
|
945
|
-
dynamicTags,
|
|
946
|
-
hasCss,
|
|
947
|
-
hasJs
|
|
948
|
-
};
|
|
949
|
-
entries.push(entry);
|
|
950
|
-
for (const tag of tags) {
|
|
951
|
-
if (!tagMap.has(tag)) tagMap.set(tag, entry);
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
return { tagMap, entries };
|
|
955
|
-
}
|
|
956
|
-
function resolveMarkup(registry, markup) {
|
|
957
|
-
const markupTags = /* @__PURE__ */ new Set();
|
|
958
|
-
for (const m of markup.matchAll(/<([a-z][a-z0-9]*-[a-z0-9-]*)/g)) {
|
|
959
|
-
markupTags.add(m[1]);
|
|
960
|
-
}
|
|
961
|
-
const matched = /* @__PURE__ */ new Set();
|
|
962
|
-
const unmatchedTags = [];
|
|
963
|
-
const resolved = /* @__PURE__ */ new Set();
|
|
964
|
-
const queue = [...markupTags];
|
|
965
|
-
while (queue.length > 0) {
|
|
966
|
-
const tag = queue.pop();
|
|
967
|
-
if (resolved.has(tag)) continue;
|
|
968
|
-
resolved.add(tag);
|
|
969
|
-
const entry = registry.tagMap.get(tag);
|
|
970
|
-
if (entry) {
|
|
971
|
-
if (!matched.has(entry)) {
|
|
972
|
-
matched.add(entry);
|
|
973
|
-
for (const dt of entry.dynamicTags) {
|
|
974
|
-
if (!resolved.has(dt)) queue.push(dt);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
} else {
|
|
978
|
-
unmatchedTags.push(tag);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
const cssFiles = [];
|
|
982
|
-
const jsFiles = [];
|
|
983
|
-
for (const entry of matched) {
|
|
984
|
-
if (entry.hasCss) cssFiles.push(join7(entry.dir, "style.css"));
|
|
985
|
-
if (entry.hasJs) jsFiles.push(join7(entry.dir, "component.js"));
|
|
986
|
-
}
|
|
987
|
-
return { cssFiles, jsFiles, unmatchedTags };
|
|
988
|
-
}
|
|
989
|
-
async function bundleJs(system) {
|
|
990
|
-
const entrypoint = join7(systemDir(system), "src", "index.js");
|
|
991
|
-
if (!existsSync6(entrypoint)) return null;
|
|
992
|
-
const result = await Bun.build({
|
|
993
|
-
entrypoints: [entrypoint],
|
|
994
|
-
bundle: true
|
|
995
|
-
});
|
|
996
|
-
if (!result.success) {
|
|
997
|
-
console.warn("Bundle failed:", result.logs);
|
|
998
|
-
return null;
|
|
999
|
-
}
|
|
1000
|
-
return await result.outputs[0].text();
|
|
1001
|
-
}
|
|
1002
|
-
function buildPreviewHtml(system, markup, deps, bundledJs) {
|
|
1003
|
-
const sysDir = systemDir(system);
|
|
1004
|
-
const blocks = ["@layer tokens, base, components;"];
|
|
1005
|
-
const tokensCss = generateTokensCss(system);
|
|
1006
|
-
blocks.push("/* tokens (generated) */");
|
|
1007
|
-
blocks.push(tokensCss);
|
|
1008
|
-
const baseCss = join7(sysDir, "src", "_shared", "_base.css");
|
|
1009
|
-
if (existsSync6(baseCss)) {
|
|
1010
|
-
blocks.push(`/* ${relative2(sysDir, baseCss)} */`);
|
|
1011
|
-
blocks.push(readFileSync7(baseCss, "utf-8"));
|
|
1012
|
-
}
|
|
1013
|
-
for (const p of deps.cssFiles) {
|
|
1014
|
-
blocks.push(`/* ${relative2(sysDir, p)} */`);
|
|
1015
|
-
blocks.push(readFileSync7(p, "utf-8"));
|
|
1016
|
-
}
|
|
1017
|
-
const inlinedCss = blocks.join("\n");
|
|
1018
|
-
const scriptTags = bundledJs ? ` <script>
|
|
1019
|
-
${bundledJs}
|
|
1020
|
-
</script>` : deps.jsFiles.map((p) => ` <script type="module" src="/${relative2(sysDir, p)}"></script>`).join("\n");
|
|
1021
|
-
return `<!DOCTYPE html>
|
|
1022
|
-
<html lang="en">
|
|
1023
|
-
<head>
|
|
1024
|
-
<meta charset="UTF-8">
|
|
1025
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1026
|
-
<style>
|
|
1027
|
-
${inlinedCss}
|
|
1028
|
-
</style>
|
|
1029
|
-
</head>
|
|
1030
|
-
<body>
|
|
1031
|
-
${markup}
|
|
1032
|
-
${scriptTags}
|
|
1033
|
-
</body>
|
|
1034
|
-
</html>`;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
// src/preview-server.ts
|
|
1038
|
-
import { createServer } from "http";
|
|
1039
|
-
import { join as join8, extname as extname2 } from "path";
|
|
1040
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
1041
|
-
import { existsSync as existsSync7 } from "fs";
|
|
1042
|
-
var MIME_TYPES = {
|
|
1043
|
-
".html": "text/html; charset=utf-8",
|
|
1044
|
-
".css": "text/css; charset=utf-8",
|
|
1045
|
-
".js": "application/javascript; charset=utf-8",
|
|
1046
|
-
".json": "application/json; charset=utf-8",
|
|
1047
|
-
".svg": "image/svg+xml",
|
|
1048
|
-
".png": "image/png",
|
|
1049
|
-
".jpg": "image/jpeg",
|
|
1050
|
-
".jpeg": "image/jpeg",
|
|
1051
|
-
".gif": "image/gif",
|
|
1052
|
-
".woff": "font/woff",
|
|
1053
|
-
".woff2": "font/woff2",
|
|
1054
|
-
".ttf": "font/ttf",
|
|
1055
|
-
".otf": "font/otf",
|
|
1056
|
-
".eot": "application/vnd.ms-fontobject"
|
|
1057
|
-
};
|
|
1058
|
-
function startPreviewServer(systemDir2, opts) {
|
|
1059
|
-
return new Promise((resolve3, reject) => {
|
|
1060
|
-
const server = createServer(async (req, res) => {
|
|
1061
|
-
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
1062
|
-
if (url.pathname === "/" && opts.html) {
|
|
1063
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1064
|
-
res.end(opts.html);
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
const filePath = join8(systemDir2, decodeURIComponent(url.pathname));
|
|
1068
|
-
if (!existsSync7(filePath)) {
|
|
1069
|
-
res.writeHead(404);
|
|
1070
|
-
res.end("Not found");
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
try {
|
|
1074
|
-
const data = await readFile2(filePath);
|
|
1075
|
-
const ext = extname2(filePath).toLowerCase();
|
|
1076
|
-
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
1077
|
-
res.writeHead(200, { "Content-Type": contentType });
|
|
1078
|
-
res.end(data);
|
|
1079
|
-
} catch {
|
|
1080
|
-
res.writeHead(500);
|
|
1081
|
-
res.end("Internal server error");
|
|
1082
|
-
}
|
|
1083
|
-
});
|
|
1084
|
-
server.listen(0, () => {
|
|
1085
|
-
const addr = server.address();
|
|
1086
|
-
if (!addr || typeof addr === "string") {
|
|
1087
|
-
reject(new Error("Failed to get server address"));
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
resolve3({
|
|
1091
|
-
url: `http://localhost:${addr.port}`,
|
|
1092
|
-
stop: () => server.close()
|
|
1093
|
-
});
|
|
1094
|
-
});
|
|
1095
|
-
server.on("error", reject);
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
// src/commands/render.ts
|
|
649
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
|
|
650
|
+
import { resolve } from "path";
|
|
1100
651
|
async function readStdin() {
|
|
1101
652
|
const chunks = [];
|
|
1102
653
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
@@ -1122,12 +673,12 @@ async function commandRender(system, fileArg, flags) {
|
|
|
1122
673
|
if (fileArg === "-") {
|
|
1123
674
|
markup = await readStdin();
|
|
1124
675
|
} else {
|
|
1125
|
-
const filePath =
|
|
1126
|
-
if (!
|
|
676
|
+
const filePath = resolve(fileArg);
|
|
677
|
+
if (!existsSync5(filePath)) {
|
|
1127
678
|
error(`File not found: ${filePath}`);
|
|
1128
679
|
return 1;
|
|
1129
680
|
}
|
|
1130
|
-
markup =
|
|
681
|
+
markup = readFileSync5(filePath, "utf-8");
|
|
1131
682
|
}
|
|
1132
683
|
const registry = buildRegistry(system);
|
|
1133
684
|
const deps = resolveMarkup(registry, markup);
|
|
@@ -1145,14 +696,14 @@ async function commandRender(system, fileArg, flags) {
|
|
|
1145
696
|
console.log(`Preview server running at ${server.url}`);
|
|
1146
697
|
console.log(`Opening in browser... (Ctrl+C to stop)`);
|
|
1147
698
|
openInBrowser(server.url);
|
|
1148
|
-
await new Promise((
|
|
699
|
+
await new Promise((resolve2) => {
|
|
1149
700
|
process.on("SIGINT", () => {
|
|
1150
701
|
server.stop();
|
|
1151
|
-
|
|
702
|
+
resolve2();
|
|
1152
703
|
});
|
|
1153
704
|
process.on("SIGTERM", () => {
|
|
1154
705
|
server.stop();
|
|
1155
|
-
|
|
706
|
+
resolve2();
|
|
1156
707
|
});
|
|
1157
708
|
});
|
|
1158
709
|
return 0;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot rendering convenience.
|
|
3
|
+
*
|
|
4
|
+
* Collapses the full pipeline (buildRegistry → resolveMarkup → bundleJs → buildPreviewHtml)
|
|
5
|
+
* into a single async call.
|
|
6
|
+
*/
|
|
7
|
+
interface RenderResult {
|
|
8
|
+
html: string;
|
|
9
|
+
unmatchedTags: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Render design-system markup to a complete HTML document.
|
|
13
|
+
*
|
|
14
|
+
* @param system - Design system name (e.g. "fw-prd")
|
|
15
|
+
* @param markup - Raw HTML/XML markup containing custom elements
|
|
16
|
+
* @returns Rendered HTML and any unmatched custom element tags
|
|
17
|
+
*/
|
|
18
|
+
declare function renderMarkup(system: string, markup: string): Promise<RenderResult>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Dependency resolution for raw markup rendering.
|
|
22
|
+
*
|
|
23
|
+
* Scans component directories to build a tag→component registry,
|
|
24
|
+
* then resolves which CSS/JS files a given markup document needs.
|
|
25
|
+
*/
|
|
26
|
+
interface ComponentEntry {
|
|
27
|
+
name: string;
|
|
28
|
+
kind: "molecules" | "cells";
|
|
29
|
+
dir: string;
|
|
30
|
+
tags: string[];
|
|
31
|
+
dynamicTags: string[];
|
|
32
|
+
hasCss: boolean;
|
|
33
|
+
hasJs: boolean;
|
|
34
|
+
}
|
|
35
|
+
interface ComponentRegistry {
|
|
36
|
+
tagMap: Map<string, ComponentEntry>;
|
|
37
|
+
entries: ComponentEntry[];
|
|
38
|
+
}
|
|
39
|
+
interface ResolvedDeps {
|
|
40
|
+
cssFiles: string[];
|
|
41
|
+
jsFiles: string[];
|
|
42
|
+
unmatchedTags: string[];
|
|
43
|
+
}
|
|
44
|
+
/** Scan all component directories and build a tag-to-component map. */
|
|
45
|
+
declare function buildRegistry(system: string): ComponentRegistry;
|
|
46
|
+
/** Resolve which components are needed for the given markup. */
|
|
47
|
+
declare function resolveMarkup(registry: ComponentRegistry, markup: string): ResolvedDeps;
|
|
48
|
+
/** Bundle the system's JS entry point in memory using esbuild. */
|
|
49
|
+
declare function bundleJs(system: string): Promise<string | null>;
|
|
50
|
+
/** Assemble a complete HTML document with inlined CSS and module script tags. */
|
|
51
|
+
declare function buildPreviewHtml(system: string, markup: string, deps: ResolvedDeps, bundledJs?: string): string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Token loading and CSS generation.
|
|
55
|
+
*
|
|
56
|
+
* Reads W3C Design Token format JSON files and generates CSS custom properties.
|
|
57
|
+
*/
|
|
58
|
+
interface TokenLeaf {
|
|
59
|
+
path: string[];
|
|
60
|
+
type: string;
|
|
61
|
+
value: unknown;
|
|
62
|
+
}
|
|
63
|
+
/** Load all token JSON files for a design system. */
|
|
64
|
+
declare function loadTokens(system: string): {
|
|
65
|
+
name: string;
|
|
66
|
+
data: Record<string, unknown>;
|
|
67
|
+
}[];
|
|
68
|
+
/** Recursively walk a W3C token tree, returning flat leaf tokens. */
|
|
69
|
+
declare function walkTokens(obj: Record<string, unknown>, path?: string[]): TokenLeaf[];
|
|
70
|
+
/** Convert a path array to a CSS custom property name. */
|
|
71
|
+
declare function toCssVarName(path: string[]): string;
|
|
72
|
+
/** Format a token value for CSS output. Returns null for composite types that should be skipped. */
|
|
73
|
+
declare function formatValue(type: string, value: unknown): string | null;
|
|
74
|
+
/** Generate complete _tokens.css content from a system's token files. */
|
|
75
|
+
declare function generateTokensCss(system: string): string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Filesystem operations for reading design system data.
|
|
79
|
+
*/
|
|
80
|
+
interface SystemInfo {
|
|
81
|
+
name: string;
|
|
82
|
+
description: string;
|
|
83
|
+
}
|
|
84
|
+
interface ComponentInfo {
|
|
85
|
+
name: string;
|
|
86
|
+
kind: "molecules" | "cells";
|
|
87
|
+
description: string;
|
|
88
|
+
tag?: string;
|
|
89
|
+
example?: string;
|
|
90
|
+
}
|
|
91
|
+
/** List all design system names */
|
|
92
|
+
declare function listSystems(): string[];
|
|
93
|
+
/** Get the root directory for a specific system */
|
|
94
|
+
declare function systemDir(system: string): string;
|
|
95
|
+
/** Check if a system exists */
|
|
96
|
+
declare function systemExists(system: string): boolean;
|
|
97
|
+
/** List components for a system, grouped by kind */
|
|
98
|
+
declare function listComponents(system: string): ComponentInfo[];
|
|
99
|
+
/** Find a component by name, searching both molecules and cells */
|
|
100
|
+
declare function findComponent(system: string, name: string): {
|
|
101
|
+
kind: "molecules" | "cells";
|
|
102
|
+
dir: string;
|
|
103
|
+
} | null;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Static file preview server via node:http.
|
|
107
|
+
*
|
|
108
|
+
* Serves the entire system directory so that relative font paths
|
|
109
|
+
* (e.g. assets/fonts/...) resolve correctly.
|
|
110
|
+
*/
|
|
111
|
+
interface PreviewServer {
|
|
112
|
+
url: string;
|
|
113
|
+
stop: () => void;
|
|
114
|
+
}
|
|
115
|
+
interface PreviewServerOpts {
|
|
116
|
+
/** Pre-rendered HTML string to serve at GET / */
|
|
117
|
+
html?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Start a preview server rooted at `systemDir`.
|
|
121
|
+
*
|
|
122
|
+
* When `opts.html` is provided, GET / returns that HTML string directly;
|
|
123
|
+
* all other paths serve static files from systemDir (fonts, images, etc.).
|
|
124
|
+
*/
|
|
125
|
+
declare function startPreviewServer(systemDir: string, opts: PreviewServerOpts): Promise<PreviewServer>;
|
|
126
|
+
|
|
127
|
+
export { type ComponentEntry, type ComponentInfo, type ComponentRegistry, type PreviewServer, type PreviewServerOpts, type RenderResult, type ResolvedDeps, type SystemInfo, buildPreviewHtml, buildRegistry, bundleJs, findComponent, formatValue, generateTokensCss, listComponents, listSystems, loadTokens, renderMarkup, resolveMarkup, startPreviewServer, systemDir, systemExists, toCssVarName, walkTokens };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildPreviewHtml,
|
|
3
|
+
buildRegistry,
|
|
4
|
+
bundleJs,
|
|
5
|
+
findComponent,
|
|
6
|
+
formatValue,
|
|
7
|
+
generateTokensCss,
|
|
8
|
+
listComponents,
|
|
9
|
+
listSystems,
|
|
10
|
+
loadTokens,
|
|
11
|
+
resolveMarkup,
|
|
12
|
+
startPreviewServer,
|
|
13
|
+
systemDir,
|
|
14
|
+
systemExists,
|
|
15
|
+
toCssVarName,
|
|
16
|
+
walkTokens
|
|
17
|
+
} from "./chunk-U44UTENA.js";
|
|
18
|
+
|
|
19
|
+
// src/render.ts
|
|
20
|
+
async function renderMarkup(system, markup) {
|
|
21
|
+
const registry = buildRegistry(system);
|
|
22
|
+
const deps = resolveMarkup(registry, markup);
|
|
23
|
+
const js = await bundleJs(system);
|
|
24
|
+
const html = buildPreviewHtml(system, markup, deps, js ?? void 0);
|
|
25
|
+
return {
|
|
26
|
+
html,
|
|
27
|
+
unmatchedTags: deps.unmatchedTags
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
buildPreviewHtml,
|
|
32
|
+
buildRegistry,
|
|
33
|
+
bundleJs,
|
|
34
|
+
findComponent,
|
|
35
|
+
formatValue,
|
|
36
|
+
generateTokensCss,
|
|
37
|
+
listComponents,
|
|
38
|
+
listSystems,
|
|
39
|
+
loadTokens,
|
|
40
|
+
renderMarkup,
|
|
41
|
+
resolveMarkup,
|
|
42
|
+
startPreviewServer,
|
|
43
|
+
systemDir,
|
|
44
|
+
systemExists,
|
|
45
|
+
toCssVarName,
|
|
46
|
+
walkTokens
|
|
47
|
+
};
|
package/package.json
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sourcescape/ds-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for browsing design system definitions",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
7
13
|
"bin": {
|
|
8
14
|
"design-system": "./dist/cli.js"
|
|
9
15
|
},
|
|
10
16
|
"files": ["dist"],
|
|
11
17
|
"scripts": {
|
|
12
|
-
"build": "tsup src/cli.ts --format esm --dts --clean",
|
|
18
|
+
"build": "tsup src/cli.ts src/index.ts --format esm --dts --clean",
|
|
13
19
|
"prepublishOnly": "npm run build"
|
|
14
20
|
},
|
|
15
21
|
"engines": {
|
|
16
22
|
"node": ">=18"
|
|
17
23
|
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"esbuild": "^0.25.0"
|
|
26
|
+
},
|
|
18
27
|
"devDependencies": {
|
|
19
28
|
"tsup": "^8.0.0",
|
|
20
29
|
"typescript": "^5.0.0"
|