@sourcescape/ds-cli 0.1.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 +494 -712
- 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
|
+
};
|