@sourcescape/ds-cli 0.1.0 → 0.2.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/cli.js +692 -461
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,17 +5,30 @@ import { parseArgs } from "util";
|
|
|
5
5
|
|
|
6
6
|
// src/systems.ts
|
|
7
7
|
import { readFileSync, readdirSync, existsSync, statSync } from "fs";
|
|
8
|
-
import { join, resolve
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
+
}
|
|
13
26
|
}
|
|
14
27
|
function listSystems() {
|
|
15
|
-
return readdirSync(
|
|
28
|
+
return readdirSync(getSystemsRoot(), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
16
29
|
}
|
|
17
30
|
function systemDir(system) {
|
|
18
|
-
return join(
|
|
31
|
+
return join(getSystemsRoot(), system);
|
|
19
32
|
}
|
|
20
33
|
function systemExists(system) {
|
|
21
34
|
return existsSync(systemDir(system));
|
|
@@ -64,12 +77,24 @@ function listComponents(system) {
|
|
|
64
77
|
if (!existsSync(kindDir)) continue;
|
|
65
78
|
const dirs = readdirSync(kindDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
66
79
|
for (const name of dirs) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
+
}
|
|
73
98
|
}
|
|
74
99
|
}
|
|
75
100
|
return results;
|
|
@@ -95,77 +120,67 @@ function allComponentNames(system) {
|
|
|
95
120
|
}
|
|
96
121
|
return results;
|
|
97
122
|
}
|
|
98
|
-
function listTokenFiles(system) {
|
|
99
|
-
const tokensDir = join(systemDir(system), "tokens");
|
|
100
|
-
if (!existsSync(tokensDir)) return [];
|
|
101
|
-
return readdirSync(tokensDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", "")).sort();
|
|
102
|
-
}
|
|
103
|
-
function readTokenFile(system, name) {
|
|
104
|
-
const filePath = join(systemDir(system), "tokens", `${name}.json`);
|
|
105
|
-
if (!existsSync(filePath)) return null;
|
|
106
|
-
return JSON.parse(readFile(filePath));
|
|
107
|
-
}
|
|
108
|
-
function listPrinciples(system) {
|
|
109
|
-
const dir = join(systemDir(system), "principles");
|
|
110
|
-
if (!existsSync(dir)) return [];
|
|
111
|
-
return readdirSync(dir).filter((f) => f.endsWith(".md") && f !== "index.md").map((f) => {
|
|
112
|
-
const name = f.replace(".md", "");
|
|
113
|
-
const title = readFirstHeading(join(dir, f));
|
|
114
|
-
return { name, title: title || name };
|
|
115
|
-
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
116
|
-
}
|
|
117
|
-
function readPrinciple(system, name) {
|
|
118
|
-
const filePath = join(systemDir(system), "principles", `${name}.md`);
|
|
119
|
-
if (!existsSync(filePath)) return null;
|
|
120
|
-
return readFile(filePath);
|
|
121
|
-
}
|
|
122
|
-
function hasArchitecture(system) {
|
|
123
|
-
return existsSync(join(systemDir(system), "architecture"));
|
|
124
|
-
}
|
|
125
|
-
function listArchitecture(system, subpath) {
|
|
126
|
-
const baseDir = join(systemDir(system), "architecture");
|
|
127
|
-
const dir = subpath ? join(baseDir, subpath) : baseDir;
|
|
128
|
-
if (!existsSync(dir)) return { entries: [], indexContent: null };
|
|
129
|
-
const indexPath = join(dir, "index.md");
|
|
130
|
-
const indexContent = existsSync(indexPath) ? readFile(indexPath) : null;
|
|
131
|
-
const entries = readdirSync(dir, { withFileTypes: true }).filter((d) => d.name !== "index.md").map((d) => ({ name: d.name, isDir: d.isDirectory() })).sort((a, b) => {
|
|
132
|
-
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
133
|
-
return a.name.localeCompare(b.name);
|
|
134
|
-
});
|
|
135
|
-
return { entries, indexContent };
|
|
136
|
-
}
|
|
137
|
-
function readArchitectureFile(system, subpath) {
|
|
138
|
-
const filePath = join(systemDir(system), "architecture", subpath);
|
|
139
|
-
if (!existsSync(filePath)) return null;
|
|
140
|
-
if (statSync(filePath).isDirectory()) return null;
|
|
141
|
-
return readFile(filePath);
|
|
142
|
-
}
|
|
143
|
-
function isArchitectureDir(system, subpath) {
|
|
144
|
-
const filePath = join(systemDir(system), "architecture", subpath);
|
|
145
|
-
return existsSync(filePath) && statSync(filePath).isDirectory();
|
|
146
|
-
}
|
|
147
|
-
function listExamples(system) {
|
|
148
|
-
const dir = join(systemDir(system), "examples");
|
|
149
|
-
if (!existsSync(dir)) return [];
|
|
150
|
-
return readdirSync(dir).filter((f) => f.endsWith(".html")).sort();
|
|
151
|
-
}
|
|
152
|
-
function listReferences(system) {
|
|
153
|
-
const dir = join(systemDir(system), "references");
|
|
154
|
-
if (!existsSync(dir)) return [];
|
|
155
|
-
return readdirSync(dir, { withFileTypes: true }).filter((d) => !d.isDirectory()).map((d) => d.name).sort();
|
|
156
|
-
}
|
|
157
123
|
function suggestComponent(system, query) {
|
|
158
124
|
const all = allComponentNames(system);
|
|
159
125
|
const q = query.toLowerCase();
|
|
160
126
|
return all.filter((c) => c.name.includes(q) || q.includes(c.name)).map((c) => `${c.name} (${c.kind})`);
|
|
161
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
|
+
}
|
|
162
177
|
function findComponentSources(system, name) {
|
|
163
178
|
const found = findComponent(system, name);
|
|
164
179
|
if (!found) return { css: [], js: [] };
|
|
165
180
|
const css = [];
|
|
166
181
|
const js = [];
|
|
167
182
|
const stylePath = join(found.dir, "style.css");
|
|
168
|
-
const scriptPath = join(found.dir, "
|
|
183
|
+
const scriptPath = join(found.dir, "component.js");
|
|
169
184
|
if (existsSync(stylePath)) css.push(stylePath);
|
|
170
185
|
if (existsSync(scriptPath)) js.push(scriptPath);
|
|
171
186
|
return { css, js };
|
|
@@ -181,12 +196,6 @@ function subheading(text) {
|
|
|
181
196
|
console.log(`
|
|
182
197
|
${text}`);
|
|
183
198
|
}
|
|
184
|
-
function list(items, indent = 2) {
|
|
185
|
-
const pad = " ".repeat(indent);
|
|
186
|
-
for (const item of items) {
|
|
187
|
-
console.log(`${pad}${item}`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
199
|
function descriptionList(items, indent = 2) {
|
|
191
200
|
if (items.length === 0) return;
|
|
192
201
|
const maxName = Math.max(...items.map((i) => i.name.length));
|
|
@@ -196,9 +205,6 @@ function descriptionList(items, indent = 2) {
|
|
|
196
205
|
console.log(`${pad}${item.name.padEnd(maxName)}${desc}`);
|
|
197
206
|
}
|
|
198
207
|
}
|
|
199
|
-
function countLine(label, count) {
|
|
200
|
-
console.log(` ${label}: ${count}`);
|
|
201
|
-
}
|
|
202
208
|
function error(message, suggestions) {
|
|
203
209
|
console.error(`Error: ${message}`);
|
|
204
210
|
if (suggestions && suggestions.length > 0) {
|
|
@@ -229,82 +235,15 @@ function commandList() {
|
|
|
229
235
|
return 0;
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
// src/tokens.ts
|
|
233
|
-
function isToken(obj) {
|
|
234
|
-
return typeof obj === "object" && obj !== null && "$value" in obj;
|
|
235
|
-
}
|
|
236
|
-
function flattenTokens(obj, prefix = "") {
|
|
237
|
-
const tokens = [];
|
|
238
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
239
|
-
if (key.startsWith("$")) continue;
|
|
240
|
-
const path = prefix ? `${prefix}.${key}` : key;
|
|
241
|
-
if (isToken(value)) {
|
|
242
|
-
tokens.push({
|
|
243
|
-
path,
|
|
244
|
-
$value: value.$value,
|
|
245
|
-
$type: value.$type ?? "unknown"
|
|
246
|
-
});
|
|
247
|
-
} else if (typeof value === "object" && value !== null) {
|
|
248
|
-
tokens.push(...flattenTokens(value, path));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return tokens;
|
|
252
|
-
}
|
|
253
|
-
function groupTokens(tokens) {
|
|
254
|
-
const groups = /* @__PURE__ */ new Map();
|
|
255
|
-
for (const token of tokens) {
|
|
256
|
-
const category = token.path.split(".")[0];
|
|
257
|
-
if (!groups.has(category)) {
|
|
258
|
-
groups.set(category, []);
|
|
259
|
-
}
|
|
260
|
-
groups.get(category).push(token);
|
|
261
|
-
}
|
|
262
|
-
return Array.from(groups.entries()).map(([name, tokens2]) => ({
|
|
263
|
-
name,
|
|
264
|
-
tokens: tokens2
|
|
265
|
-
}));
|
|
266
|
-
}
|
|
267
|
-
function formatValue(value) {
|
|
268
|
-
if (typeof value === "string") return value;
|
|
269
|
-
if (typeof value === "number") return String(value);
|
|
270
|
-
if (typeof value === "object" && value !== null) {
|
|
271
|
-
return JSON.stringify(value);
|
|
272
|
-
}
|
|
273
|
-
return String(value);
|
|
274
|
-
}
|
|
275
|
-
function renderTokens(data) {
|
|
276
|
-
const tokens = flattenTokens(data);
|
|
277
|
-
const groups = groupTokens(tokens);
|
|
278
|
-
for (const group of groups) {
|
|
279
|
-
console.log(`
|
|
280
|
-
${group.name}`);
|
|
281
|
-
console.log("\u2500".repeat(Math.min(group.name.length, 40)));
|
|
282
|
-
const maxPath = Math.max(...group.tokens.map((t) => t.path.length));
|
|
283
|
-
for (const token of group.tokens) {
|
|
284
|
-
const typeTag = token.$type !== "unknown" ? ` [${token.$type}]` : "";
|
|
285
|
-
console.log(` ${token.path.padEnd(maxPath)} ${formatValue(token.$value)}${typeTag}`);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
console.log();
|
|
289
|
-
}
|
|
290
|
-
function renderFlat(data) {
|
|
291
|
-
const tokens = flattenTokens(data);
|
|
292
|
-
for (const token of tokens) {
|
|
293
|
-
console.log(`${token.path} = ${formatValue(token.$value)}`);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
function renderJson(data) {
|
|
297
|
-
console.log(JSON.stringify(data, null, 2));
|
|
298
|
-
}
|
|
299
|
-
function tokenSummary(data) {
|
|
300
|
-
const tokens = flattenTokens(data);
|
|
301
|
-
const types = new Set(tokens.map((t) => t.$type));
|
|
302
|
-
const typeList = Array.from(types).filter((t) => t !== "unknown").sort();
|
|
303
|
-
const typeSuffix = typeList.length > 0 ? ` (${typeList.join(", ")})` : "";
|
|
304
|
-
return `${tokens.length} tokens${typeSuffix}`;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
238
|
// src/commands/show.ts
|
|
239
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
240
|
+
import { join as join2 } from "path";
|
|
241
|
+
function loadManifest(system) {
|
|
242
|
+
const path = join2(systemDir(system), "references", "manifest.json");
|
|
243
|
+
if (!existsSync2(path)) return [];
|
|
244
|
+
const data = JSON.parse(readFileSync2(path, "utf-8"));
|
|
245
|
+
return data.references || [];
|
|
246
|
+
}
|
|
308
247
|
function commandShow(system) {
|
|
309
248
|
if (!systemExists(system)) {
|
|
310
249
|
const available = listSystems();
|
|
@@ -321,48 +260,312 @@ function commandShow(system) {
|
|
|
321
260
|
if (desc) {
|
|
322
261
|
markdown(desc);
|
|
323
262
|
}
|
|
324
|
-
heading("Sections");
|
|
325
263
|
const components = listComponents(system);
|
|
326
264
|
const molecules = components.filter((c) => c.kind === "molecules");
|
|
327
265
|
const cells = components.filter((c) => c.kind === "cells");
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
console.log(
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
266
|
+
console.log("\n## Component Inventory");
|
|
267
|
+
function printTable(label, items) {
|
|
268
|
+
const metas = items.map((c) => readComponentMeta(system, c.name));
|
|
269
|
+
console.log(`
|
|
270
|
+
### ${label} (${items.length} active)
|
|
271
|
+
`);
|
|
272
|
+
console.log("| Component | Tag | Description |");
|
|
273
|
+
console.log("|---|---|---|");
|
|
274
|
+
for (const m of metas) {
|
|
275
|
+
console.log(`| ${m.name} | ${m.tag} | ${m.description} |`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (molecules.length > 0) printTable("Molecules", molecules);
|
|
279
|
+
if (cells.length > 0) printTable("Cells", cells);
|
|
280
|
+
const refs = loadManifest(system);
|
|
281
|
+
if (refs.length > 0) {
|
|
282
|
+
console.log("\n## References\n");
|
|
283
|
+
for (const ref of refs) {
|
|
284
|
+
console.log(`- \`${ref.file}\` \u2014 ${ref.description}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
349
287
|
blank();
|
|
350
288
|
return 0;
|
|
351
289
|
}
|
|
352
290
|
|
|
353
291
|
// src/commands/browse.ts
|
|
354
|
-
import { join as join5 } from "path";
|
|
355
|
-
import { existsSync as existsSync6 } from "fs";
|
|
356
292
|
import { exec } from "child_process";
|
|
357
293
|
|
|
358
294
|
// src/commands/components.ts
|
|
359
|
-
import { join as
|
|
360
|
-
import { existsSync as
|
|
295
|
+
import { join as join4, basename, relative } from "path";
|
|
296
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
|
|
297
|
+
|
|
298
|
+
// src/search/tfidf.ts
|
|
299
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
300
|
+
"a",
|
|
301
|
+
"an",
|
|
302
|
+
"the",
|
|
303
|
+
"and",
|
|
304
|
+
"or",
|
|
305
|
+
"but",
|
|
306
|
+
"in",
|
|
307
|
+
"on",
|
|
308
|
+
"at",
|
|
309
|
+
"to",
|
|
310
|
+
"for",
|
|
311
|
+
"of",
|
|
312
|
+
"with",
|
|
313
|
+
"by",
|
|
314
|
+
"from",
|
|
315
|
+
"is",
|
|
316
|
+
"are",
|
|
317
|
+
"was",
|
|
318
|
+
"were",
|
|
319
|
+
"be",
|
|
320
|
+
"been",
|
|
321
|
+
"has",
|
|
322
|
+
"have",
|
|
323
|
+
"had",
|
|
324
|
+
"do",
|
|
325
|
+
"does",
|
|
326
|
+
"did",
|
|
327
|
+
"will",
|
|
328
|
+
"would",
|
|
329
|
+
"could",
|
|
330
|
+
"should",
|
|
331
|
+
"may",
|
|
332
|
+
"might",
|
|
333
|
+
"can",
|
|
334
|
+
"this",
|
|
335
|
+
"that",
|
|
336
|
+
"these",
|
|
337
|
+
"those",
|
|
338
|
+
"it",
|
|
339
|
+
"its",
|
|
340
|
+
"not",
|
|
341
|
+
"no",
|
|
342
|
+
"as",
|
|
343
|
+
"if",
|
|
344
|
+
"so",
|
|
345
|
+
"than",
|
|
346
|
+
"up",
|
|
347
|
+
"out",
|
|
348
|
+
"about",
|
|
349
|
+
"into",
|
|
350
|
+
"over",
|
|
351
|
+
"after",
|
|
352
|
+
"before",
|
|
353
|
+
"between",
|
|
354
|
+
"under",
|
|
355
|
+
"such",
|
|
356
|
+
"each",
|
|
357
|
+
"all",
|
|
358
|
+
"any",
|
|
359
|
+
"both",
|
|
360
|
+
"more",
|
|
361
|
+
"other",
|
|
362
|
+
"some"
|
|
363
|
+
]);
|
|
364
|
+
function stripMarkdown(text) {
|
|
365
|
+
return text.replace(/^#{1,6}\s+/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[|─┌┐└┘├┤┬┴┼]/g, " ").replace(/<[^>]+>/g, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/!\[[^\]]*\]\([^)]+\)/g, "").replace(/^[-*]\s+/gm, "").replace(/^\d+\.\s+/gm, "").replace(/^>\s+/gm, "");
|
|
366
|
+
}
|
|
367
|
+
function tokenize(text) {
|
|
368
|
+
return text.toLowerCase().replace(/[^a-z0-9-]/g, " ").split(/\s+/).filter((t) => t.length > 1 && !STOP_WORDS.has(t));
|
|
369
|
+
}
|
|
370
|
+
function computeTF(tokens) {
|
|
371
|
+
const counts = {};
|
|
372
|
+
for (const t of tokens) {
|
|
373
|
+
counts[t] = (counts[t] || 0) + 1;
|
|
374
|
+
}
|
|
375
|
+
const len = tokens.length || 1;
|
|
376
|
+
const tf = {};
|
|
377
|
+
for (const [term, count] of Object.entries(counts)) {
|
|
378
|
+
tf[term] = count / len;
|
|
379
|
+
}
|
|
380
|
+
return tf;
|
|
381
|
+
}
|
|
382
|
+
function computeIDF(documents) {
|
|
383
|
+
const N = documents.length || 1;
|
|
384
|
+
const docFreq = {};
|
|
385
|
+
for (const tokens of documents) {
|
|
386
|
+
const seen = new Set(tokens);
|
|
387
|
+
for (const t of seen) {
|
|
388
|
+
docFreq[t] = (docFreq[t] || 0) + 1;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const idf = {};
|
|
392
|
+
for (const [term, df] of Object.entries(docFreq)) {
|
|
393
|
+
idf[term] = Math.log(1 + N / df);
|
|
394
|
+
}
|
|
395
|
+
return idf;
|
|
396
|
+
}
|
|
397
|
+
function computeTFIDF(tf, idf) {
|
|
398
|
+
const tfidf = {};
|
|
399
|
+
for (const [term, tfVal] of Object.entries(tf)) {
|
|
400
|
+
const idfVal = idf[term] ?? Math.log(1 + 1);
|
|
401
|
+
tfidf[term] = tfVal * idfVal;
|
|
402
|
+
}
|
|
403
|
+
return tfidf;
|
|
404
|
+
}
|
|
405
|
+
function cosineSimilarity(a, b) {
|
|
406
|
+
let dot = 0;
|
|
407
|
+
let magA = 0;
|
|
408
|
+
let magB = 0;
|
|
409
|
+
for (const [term, val] of Object.entries(a)) {
|
|
410
|
+
magA += val * val;
|
|
411
|
+
if (term in b) {
|
|
412
|
+
dot += val * b[term];
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const val of Object.values(b)) {
|
|
416
|
+
magB += val * val;
|
|
417
|
+
}
|
|
418
|
+
const denom = Math.sqrt(magA) * Math.sqrt(magB);
|
|
419
|
+
return denom === 0 ? 0 : dot / denom;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/search/indexer.ts
|
|
423
|
+
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
424
|
+
import { join as join3 } from "path";
|
|
425
|
+
var INDEX_FILE = ".search-index.json";
|
|
426
|
+
function indexPath(system) {
|
|
427
|
+
return join3(systemDir(system), INDEX_FILE);
|
|
428
|
+
}
|
|
429
|
+
function buildSearchIndex(system) {
|
|
430
|
+
const components = listComponents(system);
|
|
431
|
+
const docTokens = [];
|
|
432
|
+
const rawEntries = [];
|
|
433
|
+
for (const comp of components) {
|
|
434
|
+
const fullDesc = readFullDescription(system, comp.name, comp.kind);
|
|
435
|
+
const plain = stripMarkdown(fullDesc || comp.description);
|
|
436
|
+
const tokens = tokenize(`${comp.name} ${plain}`);
|
|
437
|
+
const unique = [...new Set(tokens)];
|
|
438
|
+
docTokens.push(unique);
|
|
439
|
+
rawEntries.push({
|
|
440
|
+
name: comp.name,
|
|
441
|
+
kind: comp.kind,
|
|
442
|
+
summary: comp.description.slice(0, 200),
|
|
443
|
+
tokens: unique,
|
|
444
|
+
tf: computeTF(tokens),
|
|
445
|
+
mtime: descriptionMtime(system, comp.name, comp.kind)
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
const idf = computeIDF(docTokens);
|
|
449
|
+
const entries = rawEntries.map((raw) => ({
|
|
450
|
+
name: raw.name,
|
|
451
|
+
kind: raw.kind,
|
|
452
|
+
summary: raw.summary,
|
|
453
|
+
tfidf: computeTFIDF(raw.tf, idf),
|
|
454
|
+
tokens: raw.tokens,
|
|
455
|
+
descriptionMtime: raw.mtime
|
|
456
|
+
}));
|
|
457
|
+
return {
|
|
458
|
+
version: 1,
|
|
459
|
+
buildTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
460
|
+
systemName: system,
|
|
461
|
+
documentCount: entries.length,
|
|
462
|
+
idf,
|
|
463
|
+
entries
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function writeSearchIndex(system, index) {
|
|
467
|
+
try {
|
|
468
|
+
writeFileSync(indexPath(system), JSON.stringify(index, null, 2), "utf-8");
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function loadSearchIndex(system) {
|
|
473
|
+
const path = indexPath(system);
|
|
474
|
+
if (!existsSync3(path)) return null;
|
|
475
|
+
try {
|
|
476
|
+
const data = JSON.parse(readFileSync3(path, "utf-8"));
|
|
477
|
+
if (data.version !== 1) return null;
|
|
478
|
+
return data;
|
|
479
|
+
} catch {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function isStale(system, index) {
|
|
484
|
+
for (const entry of index.entries) {
|
|
485
|
+
const currentMtime = descriptionMtime(system, entry.name, entry.kind);
|
|
486
|
+
if (currentMtime !== entry.descriptionMtime) return true;
|
|
487
|
+
}
|
|
488
|
+
const currentCount = listComponents(system).length;
|
|
489
|
+
if (currentCount !== index.documentCount) return true;
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
function ensureSearchIndex(system) {
|
|
493
|
+
const existing = loadSearchIndex(system);
|
|
494
|
+
if (existing && !isStale(system, existing)) {
|
|
495
|
+
return existing;
|
|
496
|
+
}
|
|
497
|
+
const index = buildSearchIndex(system);
|
|
498
|
+
writeSearchIndex(system, index);
|
|
499
|
+
return index;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/search/search.ts
|
|
503
|
+
var NAME_WEIGHT = 0.4;
|
|
504
|
+
var KEYWORD_WEIGHT = 0.3;
|
|
505
|
+
var TFIDF_WEIGHT = 0.3;
|
|
506
|
+
function nameScore(queryTokens, componentName) {
|
|
507
|
+
const name = componentName.toLowerCase();
|
|
508
|
+
const parts = name.split("-");
|
|
509
|
+
let matched = 0;
|
|
510
|
+
for (const qt of queryTokens) {
|
|
511
|
+
if (name.includes(qt) || parts.some((p) => p.includes(qt) || qt.includes(p))) {
|
|
512
|
+
matched++;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return queryTokens.length === 0 ? 0 : matched / queryTokens.length;
|
|
516
|
+
}
|
|
517
|
+
function keywordScore(queryTokens, docTokens) {
|
|
518
|
+
if (queryTokens.length === 0) return 0;
|
|
519
|
+
const docSet = new Set(docTokens);
|
|
520
|
+
let matched = 0;
|
|
521
|
+
for (const qt of queryTokens) {
|
|
522
|
+
if (docSet.has(qt)) {
|
|
523
|
+
matched++;
|
|
524
|
+
} else {
|
|
525
|
+
for (const dt of docTokens) {
|
|
526
|
+
if (dt.includes(qt) || qt.includes(dt)) {
|
|
527
|
+
matched += 0.5;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return matched / queryTokens.length;
|
|
534
|
+
}
|
|
535
|
+
function searchComponents(system, query) {
|
|
536
|
+
const index = ensureSearchIndex(system);
|
|
537
|
+
const queryTokens = tokenize(query);
|
|
538
|
+
if (queryTokens.length === 0) return [];
|
|
539
|
+
const queryTF = computeTF(queryTokens);
|
|
540
|
+
const queryVec = computeTFIDF(queryTF, index.idf);
|
|
541
|
+
const results = [];
|
|
542
|
+
for (const entry of index.entries) {
|
|
543
|
+
const ns = nameScore(queryTokens, entry.name);
|
|
544
|
+
const ks = keywordScore(queryTokens, entry.tokens);
|
|
545
|
+
const ts = cosineSimilarity(queryVec, entry.tfidf);
|
|
546
|
+
const score = NAME_WEIGHT * ns + KEYWORD_WEIGHT * ks + TFIDF_WEIGHT * ts;
|
|
547
|
+
if (score > 0.01) {
|
|
548
|
+
results.push({
|
|
549
|
+
name: entry.name,
|
|
550
|
+
kind: entry.kind,
|
|
551
|
+
summary: entry.summary,
|
|
552
|
+
score: Math.round(score * 100) / 100
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
results.sort((a, b) => b.score - a.score);
|
|
557
|
+
return results;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// src/commands/components.ts
|
|
361
561
|
async function commandComponents(system, args) {
|
|
362
562
|
const [subcommand, ...rest] = args;
|
|
363
563
|
if (!subcommand || subcommand === "list") {
|
|
364
564
|
return listAllComponents(system);
|
|
365
565
|
}
|
|
566
|
+
if (subcommand === "inventory") {
|
|
567
|
+
return showInventory(system);
|
|
568
|
+
}
|
|
366
569
|
if (subcommand === "details") {
|
|
367
570
|
const name = rest[0];
|
|
368
571
|
if (!name) {
|
|
@@ -371,6 +574,17 @@ async function commandComponents(system, args) {
|
|
|
371
574
|
}
|
|
372
575
|
return showDetails(system, name);
|
|
373
576
|
}
|
|
577
|
+
if (subcommand === "search") {
|
|
578
|
+
const query = rest.join(" ");
|
|
579
|
+
if (!query) {
|
|
580
|
+
error("Usage: ds <system> components search <query>");
|
|
581
|
+
return 1;
|
|
582
|
+
}
|
|
583
|
+
return showSearch(system, query);
|
|
584
|
+
}
|
|
585
|
+
if (subcommand === "index") {
|
|
586
|
+
return rebuildIndex(system);
|
|
587
|
+
}
|
|
374
588
|
return showDetails(system, subcommand);
|
|
375
589
|
}
|
|
376
590
|
function listAllComponents(system) {
|
|
@@ -385,14 +599,20 @@ function listAllComponents(system) {
|
|
|
385
599
|
if (molecules.length > 0) {
|
|
386
600
|
subheading(`Molecules (${molecules.length})`);
|
|
387
601
|
descriptionList(
|
|
388
|
-
molecules.map((c) => ({
|
|
602
|
+
molecules.map((c) => ({
|
|
603
|
+
name: c.tag ? `${c.name} ${c.tag}` : c.name,
|
|
604
|
+
description: c.description
|
|
605
|
+
})),
|
|
389
606
|
4
|
|
390
607
|
);
|
|
391
608
|
}
|
|
392
609
|
if (cells.length > 0) {
|
|
393
610
|
subheading(`Cells (${cells.length})`);
|
|
394
611
|
descriptionList(
|
|
395
|
-
cells.map((c) => ({
|
|
612
|
+
cells.map((c) => ({
|
|
613
|
+
name: c.tag ? `${c.name} ${c.tag}` : c.name,
|
|
614
|
+
description: c.description
|
|
615
|
+
})),
|
|
396
616
|
4
|
|
397
617
|
);
|
|
398
618
|
}
|
|
@@ -408,49 +628,272 @@ function showDetails(system, name) {
|
|
|
408
628
|
}
|
|
409
629
|
const sysDir = systemDir(system);
|
|
410
630
|
const relPath = relative(sysDir, found.dir);
|
|
631
|
+
const json = readComponentJson(found.dir);
|
|
411
632
|
heading(`${name}`);
|
|
412
633
|
console.log(` Kind: ${found.kind}`);
|
|
634
|
+
if (json?.tag) console.log(` Tag: ${json.tag}`);
|
|
413
635
|
console.log(` Path: ${relPath}`);
|
|
414
636
|
const files = readdirSync2(found.dir);
|
|
415
637
|
console.log(` Files: ${files.join(", ")}`);
|
|
416
638
|
blank();
|
|
417
|
-
|
|
418
|
-
if (existsSync2(descPath)) {
|
|
639
|
+
if (json?.description) {
|
|
419
640
|
subheading("Description");
|
|
420
641
|
blank();
|
|
642
|
+
console.log(` ${json.description}`);
|
|
643
|
+
blank();
|
|
644
|
+
}
|
|
645
|
+
if (json?.example) {
|
|
646
|
+
subheading("Example");
|
|
647
|
+
blank();
|
|
648
|
+
console.log("```xml");
|
|
649
|
+
console.log(json.example);
|
|
650
|
+
console.log("```");
|
|
651
|
+
blank();
|
|
652
|
+
}
|
|
653
|
+
const descPath = join4(found.dir, "description.md");
|
|
654
|
+
if (existsSync4(descPath)) {
|
|
655
|
+
subheading("Description (full)");
|
|
656
|
+
blank();
|
|
421
657
|
console.log(readFile(descPath));
|
|
422
658
|
}
|
|
423
659
|
const sources = findComponentSources(system, name);
|
|
424
660
|
if (sources.css.length > 0) {
|
|
425
661
|
for (const cssPath of sources.css) {
|
|
426
|
-
subheading(`Source CSS: ${
|
|
662
|
+
subheading(`Source CSS: ${basename(cssPath)}`);
|
|
427
663
|
blank();
|
|
428
664
|
console.log("```css");
|
|
429
|
-
console.log(
|
|
665
|
+
console.log(readFileSync4(cssPath, "utf-8").trimEnd());
|
|
430
666
|
console.log("```");
|
|
431
667
|
blank();
|
|
432
668
|
}
|
|
433
669
|
}
|
|
434
670
|
if (sources.js.length > 0) {
|
|
435
671
|
for (const jsPath of sources.js) {
|
|
436
|
-
subheading(`Source JS: ${
|
|
672
|
+
subheading(`Source JS: ${basename(jsPath)}`);
|
|
437
673
|
blank();
|
|
438
674
|
console.log("```js");
|
|
439
|
-
console.log(
|
|
675
|
+
console.log(readFileSync4(jsPath, "utf-8").trimEnd());
|
|
440
676
|
console.log("```");
|
|
441
677
|
blank();
|
|
442
678
|
}
|
|
443
679
|
}
|
|
444
680
|
return 0;
|
|
445
681
|
}
|
|
682
|
+
function showInventory(system) {
|
|
683
|
+
const components = listComponents(system);
|
|
684
|
+
if (components.length === 0) {
|
|
685
|
+
console.log(`No components found in ${system}.`);
|
|
686
|
+
return 0;
|
|
687
|
+
}
|
|
688
|
+
const molecules = components.filter((c) => c.kind === "molecules");
|
|
689
|
+
const cells = components.filter((c) => c.kind === "cells");
|
|
690
|
+
const printTable = (items) => {
|
|
691
|
+
console.log("| Name | Tag | Description |");
|
|
692
|
+
console.log("|------|-----|-------------|");
|
|
693
|
+
for (const c of items) {
|
|
694
|
+
const tag = c.tag ? `\`${c.tag}\`` : "";
|
|
695
|
+
console.log(`| ${c.name} | ${tag} | ${c.description} |`);
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
console.log(`# ${system} \u2014 Component Inventory
|
|
699
|
+
`);
|
|
700
|
+
if (molecules.length > 0) {
|
|
701
|
+
console.log(`## Molecules (${molecules.length})
|
|
702
|
+
`);
|
|
703
|
+
printTable(molecules);
|
|
704
|
+
console.log();
|
|
705
|
+
}
|
|
706
|
+
if (cells.length > 0) {
|
|
707
|
+
console.log(`## Cells (${cells.length})
|
|
708
|
+
`);
|
|
709
|
+
printTable(cells);
|
|
710
|
+
console.log();
|
|
711
|
+
}
|
|
712
|
+
return 0;
|
|
713
|
+
}
|
|
714
|
+
function showSearch(system, query) {
|
|
715
|
+
const results = searchComponents(system, query);
|
|
716
|
+
const total = listComponents(system).length;
|
|
717
|
+
console.log(`
|
|
718
|
+
Search: "${query}" (${total} components indexed)`);
|
|
719
|
+
console.log("\u2500".repeat(50));
|
|
720
|
+
if (results.length === 0) {
|
|
721
|
+
console.log("\n No matches found.\n");
|
|
722
|
+
return 0;
|
|
723
|
+
}
|
|
724
|
+
console.log(`
|
|
725
|
+
Results (${results.length} match${results.length === 1 ? "" : "es"})
|
|
726
|
+
`);
|
|
727
|
+
const maxName = Math.max(...results.map((r) => r.name.length));
|
|
728
|
+
const maxKind = Math.max(...results.map((r) => r.kind.length + 2));
|
|
729
|
+
for (const r of results) {
|
|
730
|
+
const name = r.name.padEnd(maxName);
|
|
731
|
+
const kind = `(${r.kind})`.padEnd(maxKind);
|
|
732
|
+
const score = r.score.toFixed(2);
|
|
733
|
+
console.log(` ${name} ${kind} ${score} ${r.summary}`);
|
|
734
|
+
}
|
|
735
|
+
blank();
|
|
736
|
+
return 0;
|
|
737
|
+
}
|
|
738
|
+
function rebuildIndex(system) {
|
|
739
|
+
console.log(`Building search index for ${system}...`);
|
|
740
|
+
const index = buildSearchIndex(system);
|
|
741
|
+
writeSearchIndex(system, index);
|
|
742
|
+
console.log(` Documents: ${index.documentCount}`);
|
|
743
|
+
console.log(` Terms: ${Object.keys(index.idf).length}`);
|
|
744
|
+
console.log(` Written: ${index.buildTime}`);
|
|
745
|
+
return 0;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/commands/references.ts
|
|
749
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync3 } from "fs";
|
|
750
|
+
import { join as join5, extname } from "path";
|
|
751
|
+
function referencesDir(system) {
|
|
752
|
+
return join5(systemDir(system), "references");
|
|
753
|
+
}
|
|
754
|
+
function loadManifest2(system) {
|
|
755
|
+
const path = join5(referencesDir(system), "manifest.json");
|
|
756
|
+
if (!existsSync5(path)) return null;
|
|
757
|
+
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
758
|
+
}
|
|
759
|
+
function listReferenceFiles(system) {
|
|
760
|
+
const dir = referencesDir(system);
|
|
761
|
+
if (!existsSync5(dir)) return [];
|
|
762
|
+
return readdirSync3(dir).filter((f) => f !== "manifest.json").sort();
|
|
763
|
+
}
|
|
764
|
+
function commandReferences(system, segments) {
|
|
765
|
+
const dir = referencesDir(system);
|
|
766
|
+
if (!existsSync5(dir)) {
|
|
767
|
+
error(`No references directory for "${system}"`);
|
|
768
|
+
return 1;
|
|
769
|
+
}
|
|
770
|
+
if (segments.length === 0) {
|
|
771
|
+
const files = listReferenceFiles(system);
|
|
772
|
+
if (files.length === 0) {
|
|
773
|
+
console.log("No reference files.");
|
|
774
|
+
return 0;
|
|
775
|
+
}
|
|
776
|
+
const manifest = loadManifest2(system);
|
|
777
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
778
|
+
if (manifest) {
|
|
779
|
+
for (const entry of manifest.references) {
|
|
780
|
+
lookup.set(entry.file, entry);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
heading(`References (${files.length})`);
|
|
784
|
+
descriptionList(
|
|
785
|
+
files.map((f) => {
|
|
786
|
+
const entry = lookup.get(f);
|
|
787
|
+
return {
|
|
788
|
+
name: f,
|
|
789
|
+
description: entry ? entry.description : ""
|
|
790
|
+
};
|
|
791
|
+
})
|
|
792
|
+
);
|
|
793
|
+
return 0;
|
|
794
|
+
}
|
|
795
|
+
const fileName = segments.join("/");
|
|
796
|
+
const filePath = join5(dir, fileName);
|
|
797
|
+
if (!existsSync5(filePath)) {
|
|
798
|
+
const files = listReferenceFiles(system);
|
|
799
|
+
const suggestions = files.filter(
|
|
800
|
+
(f) => f.includes(fileName) || fileName.includes(f.replace(extname(f), ""))
|
|
801
|
+
);
|
|
802
|
+
error(`Reference "${fileName}" not found`, suggestions);
|
|
803
|
+
return 1;
|
|
804
|
+
}
|
|
805
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
806
|
+
process.stdout.write(content);
|
|
807
|
+
return 0;
|
|
808
|
+
}
|
|
446
809
|
|
|
447
810
|
// src/commands/render.ts
|
|
448
|
-
import { readFileSync as
|
|
811
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
449
812
|
import { resolve as resolve2 } from "path";
|
|
450
813
|
|
|
451
814
|
// src/resolve-deps.ts
|
|
452
|
-
import { readFileSync as
|
|
453
|
-
import { join as
|
|
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
|
|
454
897
|
function buildRegistry(system) {
|
|
455
898
|
const components = listComponents(system);
|
|
456
899
|
const entries = [];
|
|
@@ -458,14 +901,14 @@ function buildRegistry(system) {
|
|
|
458
901
|
for (const comp of components) {
|
|
459
902
|
const found = findComponent(system, comp.name);
|
|
460
903
|
if (!found) continue;
|
|
461
|
-
const scriptPath =
|
|
462
|
-
const stylePath =
|
|
463
|
-
const hasJs =
|
|
464
|
-
const hasCss =
|
|
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);
|
|
465
908
|
const tags = [];
|
|
466
909
|
const dynamicTags = [];
|
|
467
910
|
if (hasJs) {
|
|
468
|
-
const js =
|
|
911
|
+
const js = readFileSync7(scriptPath, "utf-8");
|
|
469
912
|
for (const m of js.matchAll(/customElements\.define\(\s*['"]([^'"]+)['"]/g)) {
|
|
470
913
|
tags.push(m[1]);
|
|
471
914
|
}
|
|
@@ -480,7 +923,7 @@ function buildRegistry(system) {
|
|
|
480
923
|
}
|
|
481
924
|
}
|
|
482
925
|
if (hasCss) {
|
|
483
|
-
const css =
|
|
926
|
+
const css = readFileSync7(stylePath, "utf-8");
|
|
484
927
|
for (const m of css.matchAll(/(?:^|[\s,}>+~])([a-z][a-z0-9]*-[a-z0-9-]*)(?=[\s,{[:.>+~]|$)/gm)) {
|
|
485
928
|
const tag = m[1];
|
|
486
929
|
if (!tags.includes(tag)) tags.push(tag);
|
|
@@ -538,35 +981,43 @@ function resolveMarkup(registry, markup) {
|
|
|
538
981
|
const cssFiles = [];
|
|
539
982
|
const jsFiles = [];
|
|
540
983
|
for (const entry of matched) {
|
|
541
|
-
if (entry.hasCss) cssFiles.push(
|
|
542
|
-
if (entry.hasJs) jsFiles.push(
|
|
984
|
+
if (entry.hasCss) cssFiles.push(join7(entry.dir, "style.css"));
|
|
985
|
+
if (entry.hasJs) jsFiles.push(join7(entry.dir, "component.js"));
|
|
543
986
|
}
|
|
544
987
|
return { cssFiles, jsFiles, unmatchedTags };
|
|
545
988
|
}
|
|
546
|
-
function
|
|
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) {
|
|
547
1003
|
const sysDir = systemDir(system);
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
blocks.push(`/* ${relative2(sysDir, p)} */`);
|
|
557
|
-
blocks.push(readFileSync3(p, "utf-8"));
|
|
558
|
-
}
|
|
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"));
|
|
559
1012
|
}
|
|
560
1013
|
for (const p of deps.cssFiles) {
|
|
561
1014
|
blocks.push(`/* ${relative2(sysDir, p)} */`);
|
|
562
|
-
blocks.push(
|
|
563
|
-
}
|
|
564
|
-
if (existsSync3(utilitiesCss)) {
|
|
565
|
-
blocks.push(`/* ${relative2(sysDir, utilitiesCss)} */`);
|
|
566
|
-
blocks.push(readFileSync3(utilitiesCss, "utf-8"));
|
|
1015
|
+
blocks.push(readFileSync7(p, "utf-8"));
|
|
567
1016
|
}
|
|
568
1017
|
const inlinedCss = blocks.join("\n");
|
|
569
|
-
const scriptTags =
|
|
1018
|
+
const scriptTags = bundledJs ? ` <script>
|
|
1019
|
+
${bundledJs}
|
|
1020
|
+
</script>` : deps.jsFiles.map((p) => ` <script type="module" src="/${relative2(sysDir, p)}"></script>`).join("\n");
|
|
570
1021
|
return `<!DOCTYPE html>
|
|
571
1022
|
<html lang="en">
|
|
572
1023
|
<head>
|
|
@@ -585,9 +1036,9 @@ ${scriptTags}
|
|
|
585
1036
|
|
|
586
1037
|
// src/preview-server.ts
|
|
587
1038
|
import { createServer } from "http";
|
|
588
|
-
import { join as
|
|
1039
|
+
import { join as join8, extname as extname2 } from "path";
|
|
589
1040
|
import { readFile as readFile2 } from "fs/promises";
|
|
590
|
-
import { existsSync as
|
|
1041
|
+
import { existsSync as existsSync7 } from "fs";
|
|
591
1042
|
var MIME_TYPES = {
|
|
592
1043
|
".html": "text/html; charset=utf-8",
|
|
593
1044
|
".css": "text/css; charset=utf-8",
|
|
@@ -613,15 +1064,15 @@ function startPreviewServer(systemDir2, opts) {
|
|
|
613
1064
|
res.end(opts.html);
|
|
614
1065
|
return;
|
|
615
1066
|
}
|
|
616
|
-
const filePath =
|
|
617
|
-
if (!
|
|
1067
|
+
const filePath = join8(systemDir2, decodeURIComponent(url.pathname));
|
|
1068
|
+
if (!existsSync7(filePath)) {
|
|
618
1069
|
res.writeHead(404);
|
|
619
1070
|
res.end("Not found");
|
|
620
1071
|
return;
|
|
621
1072
|
}
|
|
622
1073
|
try {
|
|
623
1074
|
const data = await readFile2(filePath);
|
|
624
|
-
const ext =
|
|
1075
|
+
const ext = extname2(filePath).toLowerCase();
|
|
625
1076
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
626
1077
|
res.writeHead(200, { "Content-Type": contentType });
|
|
627
1078
|
res.end(data);
|
|
@@ -672,18 +1123,19 @@ async function commandRender(system, fileArg, flags) {
|
|
|
672
1123
|
markup = await readStdin();
|
|
673
1124
|
} else {
|
|
674
1125
|
const filePath = resolve2(fileArg);
|
|
675
|
-
if (!
|
|
1126
|
+
if (!existsSync8(filePath)) {
|
|
676
1127
|
error(`File not found: ${filePath}`);
|
|
677
1128
|
return 1;
|
|
678
1129
|
}
|
|
679
|
-
markup =
|
|
1130
|
+
markup = readFileSync8(filePath, "utf-8");
|
|
680
1131
|
}
|
|
681
1132
|
const registry = buildRegistry(system);
|
|
682
1133
|
const deps = resolveMarkup(registry, markup);
|
|
683
1134
|
for (const tag of deps.unmatchedTags) {
|
|
684
1135
|
console.warn(` warn: unmatched custom element <${tag}>`);
|
|
685
1136
|
}
|
|
686
|
-
const
|
|
1137
|
+
const js = await bundleJs(system);
|
|
1138
|
+
const html = buildPreviewHtml(system, markup, deps, js);
|
|
687
1139
|
if (flags.html) {
|
|
688
1140
|
process.stdout.write(html);
|
|
689
1141
|
return 0;
|
|
@@ -720,27 +1172,12 @@ async function commandBrowse(system, path, flags) {
|
|
|
720
1172
|
switch (section) {
|
|
721
1173
|
case "components":
|
|
722
1174
|
return commandComponents(system, segments.slice(1));
|
|
1175
|
+
case "references":
|
|
1176
|
+
return commandReferences(system, segments.slice(1));
|
|
723
1177
|
case "render":
|
|
724
1178
|
return commandRender(system, segments.slice(1).join("/"), flags);
|
|
725
|
-
case "tokens":
|
|
726
|
-
return handleTokens(system, segments.slice(1), flags);
|
|
727
|
-
case "principles":
|
|
728
|
-
return handlePrinciples(system, segments.slice(1));
|
|
729
|
-
case "architecture":
|
|
730
|
-
return handleArchitecture(system, segments.slice(1));
|
|
731
|
-
case "examples":
|
|
732
|
-
return handleExamples(system, segments.slice(1), flags);
|
|
733
|
-
case "references":
|
|
734
|
-
return handleReferences(system, segments.slice(1));
|
|
735
1179
|
default:
|
|
736
|
-
error(`Unknown section "${section}"`, [
|
|
737
|
-
"components",
|
|
738
|
-
"tokens",
|
|
739
|
-
"principles",
|
|
740
|
-
"architecture",
|
|
741
|
-
"examples",
|
|
742
|
-
"references"
|
|
743
|
-
]);
|
|
1180
|
+
error(`Unknown section "${section}"`, ["components", "references"]);
|
|
744
1181
|
return 1;
|
|
745
1182
|
}
|
|
746
1183
|
}
|
|
@@ -748,198 +1185,6 @@ function openInBrowser(target) {
|
|
|
748
1185
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
749
1186
|
exec(`${cmd} "${target}"`);
|
|
750
1187
|
}
|
|
751
|
-
function handleTokens(system, segments, flags) {
|
|
752
|
-
if (segments.length === 0) {
|
|
753
|
-
return listAllTokens(system);
|
|
754
|
-
}
|
|
755
|
-
return showTokenFile(system, segments[0], flags);
|
|
756
|
-
}
|
|
757
|
-
function listAllTokens(system) {
|
|
758
|
-
const files = listTokenFiles(system);
|
|
759
|
-
if (files.length === 0) {
|
|
760
|
-
console.log(`No token files found in ${system}.`);
|
|
761
|
-
return 0;
|
|
762
|
-
}
|
|
763
|
-
heading(`${system} \u2014 Tokens`);
|
|
764
|
-
for (const name of files) {
|
|
765
|
-
const data = readTokenFile(system, name);
|
|
766
|
-
if (!data) continue;
|
|
767
|
-
console.log(` ${name}: ${tokenSummary(data)}`);
|
|
768
|
-
}
|
|
769
|
-
blank();
|
|
770
|
-
return 0;
|
|
771
|
-
}
|
|
772
|
-
function showTokenFile(system, name, flags) {
|
|
773
|
-
const data = readTokenFile(system, name);
|
|
774
|
-
if (!data) {
|
|
775
|
-
const available = listTokenFiles(system);
|
|
776
|
-
const suggestions = available.filter(
|
|
777
|
-
(f) => f.includes(name) || name.includes(f)
|
|
778
|
-
);
|
|
779
|
-
error(
|
|
780
|
-
`Token file "${name}" not found in ${system}`,
|
|
781
|
-
suggestions.length > 0 ? suggestions : available
|
|
782
|
-
);
|
|
783
|
-
return 1;
|
|
784
|
-
}
|
|
785
|
-
if (flags.json) {
|
|
786
|
-
renderJson(data);
|
|
787
|
-
return 0;
|
|
788
|
-
}
|
|
789
|
-
if (flags.flat) {
|
|
790
|
-
renderFlat(data);
|
|
791
|
-
return 0;
|
|
792
|
-
}
|
|
793
|
-
renderTokens(data);
|
|
794
|
-
return 0;
|
|
795
|
-
}
|
|
796
|
-
function handlePrinciples(system, segments) {
|
|
797
|
-
if (segments.length === 0) {
|
|
798
|
-
return listAllPrinciples(system);
|
|
799
|
-
}
|
|
800
|
-
return showPrinciple(system, segments[0]);
|
|
801
|
-
}
|
|
802
|
-
function listAllPrinciples(system) {
|
|
803
|
-
const principles = listPrinciples(system);
|
|
804
|
-
if (principles.length === 0) {
|
|
805
|
-
console.log(`No principles found in ${system}.`);
|
|
806
|
-
return 0;
|
|
807
|
-
}
|
|
808
|
-
heading(`${system} \u2014 Principles`);
|
|
809
|
-
descriptionList(
|
|
810
|
-
principles.map((p) => ({ name: p.name, description: p.title }))
|
|
811
|
-
);
|
|
812
|
-
blank();
|
|
813
|
-
return 0;
|
|
814
|
-
}
|
|
815
|
-
function showPrinciple(system, name) {
|
|
816
|
-
const content = readPrinciple(system, name);
|
|
817
|
-
if (!content) {
|
|
818
|
-
const available = listPrinciples(system);
|
|
819
|
-
const suggestions = available.filter((p) => p.name.includes(name) || name.includes(p.name)).map((p) => p.name);
|
|
820
|
-
error(
|
|
821
|
-
`Principle "${name}" not found in ${system}`,
|
|
822
|
-
suggestions.length > 0 ? suggestions : available.map((p) => p.name)
|
|
823
|
-
);
|
|
824
|
-
return 1;
|
|
825
|
-
}
|
|
826
|
-
markdown(content);
|
|
827
|
-
return 0;
|
|
828
|
-
}
|
|
829
|
-
function handleArchitecture(system, segments) {
|
|
830
|
-
if (!hasArchitecture(system)) {
|
|
831
|
-
console.log(`No architecture section in ${system}.`);
|
|
832
|
-
return 0;
|
|
833
|
-
}
|
|
834
|
-
const subpath = segments.length > 0 ? segments.join("/") : void 0;
|
|
835
|
-
if (subpath) {
|
|
836
|
-
const content = readArchitectureFile(system, subpath);
|
|
837
|
-
if (content !== null) {
|
|
838
|
-
markdown(content);
|
|
839
|
-
return 0;
|
|
840
|
-
}
|
|
841
|
-
const contentMd = readArchitectureFile(system, subpath + ".md");
|
|
842
|
-
if (contentMd !== null) {
|
|
843
|
-
markdown(contentMd);
|
|
844
|
-
return 0;
|
|
845
|
-
}
|
|
846
|
-
if (isArchitectureDir(system, subpath)) {
|
|
847
|
-
return listArchitectureDir(system, subpath);
|
|
848
|
-
}
|
|
849
|
-
error(`Architecture path "${subpath}" not found in ${system}`);
|
|
850
|
-
return 1;
|
|
851
|
-
}
|
|
852
|
-
return listArchitectureDir(system, void 0);
|
|
853
|
-
}
|
|
854
|
-
function listArchitectureDir(system, subpath) {
|
|
855
|
-
const { entries, indexContent } = listArchitecture(system, subpath);
|
|
856
|
-
const label = subpath ? `${system} \u2014 Architecture / ${subpath}` : `${system} \u2014 Architecture`;
|
|
857
|
-
heading(label);
|
|
858
|
-
if (indexContent) {
|
|
859
|
-
markdown(indexContent);
|
|
860
|
-
blank();
|
|
861
|
-
}
|
|
862
|
-
if (entries.length > 0) {
|
|
863
|
-
for (const entry of entries) {
|
|
864
|
-
const suffix = entry.isDir ? "/" : "";
|
|
865
|
-
console.log(` ${entry.name}${suffix}`);
|
|
866
|
-
}
|
|
867
|
-
blank();
|
|
868
|
-
}
|
|
869
|
-
return 0;
|
|
870
|
-
}
|
|
871
|
-
function handleExamples(system, segments, flags) {
|
|
872
|
-
if (segments.length === 0) {
|
|
873
|
-
return listAllExamples(system);
|
|
874
|
-
}
|
|
875
|
-
return showExample(system, segments[0], flags);
|
|
876
|
-
}
|
|
877
|
-
function listAllExamples(system) {
|
|
878
|
-
const examples = listExamples(system);
|
|
879
|
-
if (examples.length === 0) {
|
|
880
|
-
console.log(`No examples found in ${system}.`);
|
|
881
|
-
return 0;
|
|
882
|
-
}
|
|
883
|
-
heading(`${system} \u2014 Examples`);
|
|
884
|
-
list(examples);
|
|
885
|
-
blank();
|
|
886
|
-
return 0;
|
|
887
|
-
}
|
|
888
|
-
function showExample(system, name, flags) {
|
|
889
|
-
let filename = name;
|
|
890
|
-
if (!filename.endsWith(".html")) filename += ".html";
|
|
891
|
-
const filePath = join5(systemDir(system), "examples", filename);
|
|
892
|
-
if (!existsSync6(filePath)) {
|
|
893
|
-
const available = listExamples(system);
|
|
894
|
-
const suggestions = available.filter(
|
|
895
|
-
(e) => e.includes(name) || name.includes(e.replace(".html", ""))
|
|
896
|
-
);
|
|
897
|
-
error(
|
|
898
|
-
`Example "${name}" not found in ${system}`,
|
|
899
|
-
suggestions.length > 0 ? suggestions.map((s) => s.replace(".html", "")) : available.map((s) => s.replace(".html", ""))
|
|
900
|
-
);
|
|
901
|
-
return 1;
|
|
902
|
-
}
|
|
903
|
-
if (flags.open) {
|
|
904
|
-
openInBrowser(filePath);
|
|
905
|
-
console.log(`Opening ${filename} in browser...`);
|
|
906
|
-
return 0;
|
|
907
|
-
}
|
|
908
|
-
console.log(`Example: ${filename}`);
|
|
909
|
-
console.log(`Path: ${filePath}`);
|
|
910
|
-
console.log(`Use --open to view in browser.`);
|
|
911
|
-
return 0;
|
|
912
|
-
}
|
|
913
|
-
function handleReferences(system, segments) {
|
|
914
|
-
if (segments.length === 0) {
|
|
915
|
-
return listAllReferences(system);
|
|
916
|
-
}
|
|
917
|
-
const name = segments.join("/");
|
|
918
|
-
const filePath = join5(systemDir(system), "references", name);
|
|
919
|
-
if (!existsSync6(filePath)) {
|
|
920
|
-
const available = listReferences(system);
|
|
921
|
-
error(`Reference "${name}" not found in ${system}`, available);
|
|
922
|
-
return 1;
|
|
923
|
-
}
|
|
924
|
-
if (name.endsWith(".md")) {
|
|
925
|
-
markdown(readFile(filePath));
|
|
926
|
-
return 0;
|
|
927
|
-
}
|
|
928
|
-
console.log(`Reference: ${name}`);
|
|
929
|
-
console.log(`Path: ${filePath}`);
|
|
930
|
-
return 0;
|
|
931
|
-
}
|
|
932
|
-
function listAllReferences(system) {
|
|
933
|
-
const refs = listReferences(system);
|
|
934
|
-
if (refs.length === 0) {
|
|
935
|
-
console.log(`No references found in ${system}.`);
|
|
936
|
-
return 0;
|
|
937
|
-
}
|
|
938
|
-
heading(`${system} \u2014 References`);
|
|
939
|
-
list(refs);
|
|
940
|
-
blank();
|
|
941
|
-
return 0;
|
|
942
|
-
}
|
|
943
1188
|
|
|
944
1189
|
// src/cli.ts
|
|
945
1190
|
function showHelp() {
|
|
@@ -950,25 +1195,21 @@ Usage:
|
|
|
950
1195
|
design-system list List all design systems
|
|
951
1196
|
design-system <system> Show system overview
|
|
952
1197
|
design-system <system> components list List all components
|
|
1198
|
+
design-system <system> components inventory Generate markdown inventory table
|
|
953
1199
|
design-system <system> components details <n> Component description + source CSS/JS
|
|
954
|
-
design-system <system>
|
|
955
|
-
design-system <system>
|
|
956
|
-
design-system <system>
|
|
957
|
-
design-system <system>
|
|
958
|
-
design-system <system> architecture List architecture areas
|
|
959
|
-
design-system <system> architecture/<path> Navigate architecture tree
|
|
1200
|
+
design-system <system> components search <q> Search components by query
|
|
1201
|
+
design-system <system> components index Rebuild search index
|
|
1202
|
+
design-system <system> references List reference files
|
|
1203
|
+
design-system <system> references <file> Display reference file
|
|
960
1204
|
design-system <system> render <file> Render markup with resolved deps
|
|
961
1205
|
design-system <system> render - Render from stdin
|
|
962
|
-
design-system <system> examples List example files
|
|
963
|
-
design-system <system> references List reference files
|
|
964
1206
|
|
|
965
1207
|
Flags:
|
|
966
|
-
--open Open HTML file in browser (examples)
|
|
967
|
-
--json Output raw JSON (tokens)
|
|
968
|
-
--flat Output flat path = value lines (tokens)
|
|
969
1208
|
--html Dump raw HTML to stdout
|
|
970
|
-
--systems-dir <path> Override default systems directory
|
|
971
1209
|
--help, -h Show this help message
|
|
1210
|
+
|
|
1211
|
+
Environment:
|
|
1212
|
+
DESIGN_SYSTEMS_DIR Path to directory containing design system definitions (required)
|
|
972
1213
|
`);
|
|
973
1214
|
}
|
|
974
1215
|
async function main() {
|
|
@@ -976,18 +1217,11 @@ async function main() {
|
|
|
976
1217
|
args: process.argv.slice(2),
|
|
977
1218
|
options: {
|
|
978
1219
|
help: { type: "boolean", short: "h" },
|
|
979
|
-
|
|
980
|
-
json: { type: "boolean" },
|
|
981
|
-
flat: { type: "boolean" },
|
|
982
|
-
html: { type: "boolean" },
|
|
983
|
-
"systems-dir": { type: "string" }
|
|
1220
|
+
html: { type: "boolean" }
|
|
984
1221
|
},
|
|
985
1222
|
allowPositionals: true,
|
|
986
1223
|
strict: true
|
|
987
1224
|
});
|
|
988
|
-
if (values["systems-dir"]) {
|
|
989
|
-
setSystemsRoot(values["systems-dir"]);
|
|
990
|
-
}
|
|
991
1225
|
if (values.help && positionals.length === 0) {
|
|
992
1226
|
showHelp();
|
|
993
1227
|
process.exit(0);
|
|
@@ -1012,9 +1246,6 @@ async function main() {
|
|
|
1012
1246
|
process.exit(await commandRender(first, rest.slice(1).join(" "), { html: values.html }));
|
|
1013
1247
|
}
|
|
1014
1248
|
const flags = {
|
|
1015
|
-
open: values.open,
|
|
1016
|
-
json: values.json,
|
|
1017
|
-
flat: values.flat,
|
|
1018
1249
|
html: values.html
|
|
1019
1250
|
};
|
|
1020
1251
|
process.exit(await commandBrowse(first, second, flags));
|