@vuer-ai/vuer-uikit 0.0.98 → 0.0.100
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/SyncScroll/SyncScroll.cjs +9 -9
- package/dist/SyncScroll/SyncScroll.mjs +2 -2
- package/dist/SyncScroll/index.cjs +9 -9
- package/dist/SyncScroll/index.mjs +2 -2
- package/dist/chunk-AIYM5PFP.cjs +66 -0
- package/dist/chunk-OWEYAVGT.mjs +62 -0
- package/dist/{chunk-7GWDO25E.cjs → chunk-XBTBTLID.cjs} +2 -2
- package/dist/{chunk-TTYSYGVE.mjs → chunk-XGXWOY3U.mjs} +2 -2
- package/dist/dial/DialPanel.cjs +23 -22
- package/dist/dial/DialPanel.mjs +22 -21
- package/dist/dial/index.cjs +39 -38
- package/dist/dial/index.mjs +22 -21
- package/dist/dial/wrapped-inputs/ControlledInputs.cjs +26 -25
- package/dist/dial/wrapped-inputs/ControlledInputs.mjs +22 -21
- package/dist/dial/wrapped-inputs/DialInputs.cjs +33 -32
- package/dist/dial/wrapped-inputs/DialInputs.mjs +22 -21
- package/dist/dial/wrapped-inputs/DialVectorInput.cjs +23 -22
- package/dist/dial/wrapped-inputs/DialVectorInput.mjs +22 -21
- package/dist/dial/wrapped-inputs/index.cjs +38 -37
- package/dist/dial/wrapped-inputs/index.mjs +22 -21
- package/dist/highlight-cursor/cursor-provider.cjs +3 -3
- package/dist/highlight-cursor/cursor-provider.mjs +2 -2
- package/dist/highlight-cursor/enhanced-components.cjs +1 -1
- package/dist/highlight-cursor/enhanced-components.mjs +1 -1
- package/dist/highlight-cursor/index.cjs +3 -3
- package/dist/highlight-cursor/index.mjs +2 -2
- package/dist/index.cjs +151 -138
- package/dist/index.css +7 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +22 -21
- package/dist/ui/DialBadge.cjs +28 -0
- package/dist/ui/DialBadge.d.cts +33 -0
- package/dist/ui/DialBadge.d.ts +33 -0
- package/dist/ui/DialBadge.mjs +11 -0
- package/dist/ui/UIKitBadge.cjs +5 -5
- package/dist/ui/UIKitBadge.mjs +1 -1
- package/dist/ui/badge.d.cts +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/index.cjs +77 -64
- package/dist/ui/index.d.cts +1 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.mjs +18 -17
- package/dist/ui/inputs/index.cjs +15 -15
- package/dist/ui/inputs/index.mjs +3 -3
- package/dist/ui/inputs/input.d.cts +1 -1
- package/dist/ui/inputs/input.d.ts +1 -1
- package/dist/ui/inputs/number-inputs/index.cjs +10 -10
- package/dist/ui/inputs/number-inputs/index.mjs +2 -2
- package/dist/ui/select.d.cts +1 -1
- package/dist/ui/select.d.ts +1 -1
- package/dist/ui/textarea.d.cts +1 -1
- package/dist/ui/textarea.d.ts +1 -1
- package/dist/ui/tree-view-legacy.cjs +8 -8
- package/dist/ui/tree-view-legacy.mjs +4 -4
- package/dist/ui/waterfall/index.cjs +6 -6
- package/dist/ui/waterfall/index.mjs +5 -5
- package/package.json +2 -7
- package/src/ui/DialBadge.tsx +97 -0
- package/src/ui/index.ts +1 -0
- package/cli/dial-cli.js +0 -833
- package/dist/chunk-4KWGGESI.cjs +0 -494
- package/dist/chunk-A5LCX2UQ.cjs +0 -208
- package/dist/chunk-BEJIZ56L.mjs +0 -300
- package/dist/chunk-C7VGRU3O.mjs +0 -283
- package/dist/chunk-O66RESRR.cjs +0 -285
- package/dist/chunk-VA3PEYFM.mjs +0 -489
- package/dist/chunk-VBBJSIY7.cjs +0 -308
- package/dist/chunk-WWGF6TBZ.mjs +0 -206
- package/dist/chunk-ZGN4UEJR.cjs +0 -679
- package/dist/chunk-ZQLRMOUW.mjs +0 -661
- package/src/cli/dial-cli.ts +0 -1133
- package/dist/{chunk-XMUP5MIM.mjs → chunk-G3EIVAVR.mjs} +0 -0
- package/dist/{chunk-7IS37C3P.cjs → chunk-K4I4YU6N.cjs} +1 -1
- package/dist/{chunk-OX2U5RAG.cjs → chunk-KFPS5CCR.cjs} +0 -0
- package/dist/{chunk-2OZK5DY5.mjs → chunk-KXKEZ3MH.mjs} +1 -1
- package/dist/{chunk-LJMNHTTG.cjs → chunk-OEI7NCF6.cjs} +4 -4
- package/dist/{chunk-W4JCKCW7.mjs → chunk-QHPFLC2O.mjs} +4 -4
package/src/cli/dial-cli.ts
DELETED
|
@@ -1,1133 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
-
import { parseArgs } from "node:util";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { execSync } from "node:child_process";
|
|
6
|
-
import * as docgen from "react-docgen-typescript";
|
|
7
|
-
import * as ts from "typescript";
|
|
8
|
-
|
|
9
|
-
// Get package version and git hash
|
|
10
|
-
function getVersionInfo(): { version: string; gitHash?: string } {
|
|
11
|
-
try {
|
|
12
|
-
// Get the directory of this CLI file
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = dirname(__filename);
|
|
15
|
-
|
|
16
|
-
// Try multiple possible locations for package.json
|
|
17
|
-
// When built, the CLI is in ./cli/dial-cli.js, so package.json is at ../package.json
|
|
18
|
-
// When running from source, it's in ./src/cli/, so package.json is at ../../package.json
|
|
19
|
-
const possiblePaths = [
|
|
20
|
-
resolve(__dirname, '../package.json'), // Built version: cli/dial-cli.js -> package.json
|
|
21
|
-
resolve(__dirname, '../../package.json'), // Source version: src/cli/dial-cli.ts -> package.json
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
let packageJson: any = null;
|
|
25
|
-
for (const path of possiblePaths) {
|
|
26
|
-
try {
|
|
27
|
-
if (existsSync(path)) {
|
|
28
|
-
packageJson = JSON.parse(readFileSync(path, 'utf-8'));
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
} catch {
|
|
32
|
-
// Continue to next path
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const version = packageJson?.version || 'unknown';
|
|
37
|
-
|
|
38
|
-
// Try to get git hash
|
|
39
|
-
let gitHash: string | undefined;
|
|
40
|
-
try {
|
|
41
|
-
gitHash = execSync('git rev-parse --short HEAD', { encoding: 'utf-8', cwd: dirname(__dirname) }).trim();
|
|
42
|
-
} catch {
|
|
43
|
-
// Git not available or not a git repo
|
|
44
|
-
gitHash = undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return { version, gitHash };
|
|
48
|
-
} catch (error) {
|
|
49
|
-
// Fallback if we can't read package.json
|
|
50
|
-
return { version: 'unknown' };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface DialTag {
|
|
55
|
-
grouping?: string;
|
|
56
|
-
min?: number;
|
|
57
|
-
max?: number;
|
|
58
|
-
step?: number;
|
|
59
|
-
options?: unknown;
|
|
60
|
-
icon?: string;
|
|
61
|
-
placeholders?: string[];
|
|
62
|
-
tooltips?: string[];
|
|
63
|
-
dtypes?: string[];
|
|
64
|
-
mins?: number[];
|
|
65
|
-
maxs?: number[];
|
|
66
|
-
steps?: number[];
|
|
67
|
-
column?: boolean;
|
|
68
|
-
rowCount?: number;
|
|
69
|
-
colCount?: number;
|
|
70
|
-
labelPosition?: string;
|
|
71
|
-
dtype?: string;
|
|
72
|
-
type?: string;
|
|
73
|
-
default?: unknown;
|
|
74
|
-
noWrap?: boolean;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
interface ElementData {
|
|
78
|
-
index: number;
|
|
79
|
-
raw: string;
|
|
80
|
-
name?: string;
|
|
81
|
-
type?: string;
|
|
82
|
-
comment?: string;
|
|
83
|
-
min?: number;
|
|
84
|
-
max?: number;
|
|
85
|
-
step?: number;
|
|
86
|
-
dtype?: string;
|
|
87
|
-
dialTags?: DialTag;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
interface TypeDefinition {
|
|
91
|
-
name: string;
|
|
92
|
-
kind: string;
|
|
93
|
-
raw: string;
|
|
94
|
-
dialTags?: DialTag;
|
|
95
|
-
jsDoc?: string;
|
|
96
|
-
type?: string;
|
|
97
|
-
elements?: ElementData[];
|
|
98
|
-
elementType?: string;
|
|
99
|
-
typeNode?: string;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
interface PropertySchema {
|
|
103
|
-
name: string;
|
|
104
|
-
dtype: string;
|
|
105
|
-
min?: number;
|
|
106
|
-
max?: number;
|
|
107
|
-
step?: number;
|
|
108
|
-
icon?: string;
|
|
109
|
-
value?: unknown;
|
|
110
|
-
options?: unknown;
|
|
111
|
-
placeholders?: string[];
|
|
112
|
-
tooltips?: string[];
|
|
113
|
-
dtypes?: string[];
|
|
114
|
-
mins?: number[];
|
|
115
|
-
maxs?: number[];
|
|
116
|
-
steps?: number[];
|
|
117
|
-
tags?: Record<string, unknown>;
|
|
118
|
-
typeDefinition?: TypeDefinition;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Parse type alias definition from TypeScript source
|
|
122
|
-
function parseTypeAlias(sourceFile: ts.SourceFile, typeName: string): TypeDefinition | null {
|
|
123
|
-
let typeDefinition: TypeDefinition | null = null;
|
|
124
|
-
|
|
125
|
-
function visit(node: ts.Node): void {
|
|
126
|
-
if (ts.isTypeAliasDeclaration(node) && node.name.text === typeName) {
|
|
127
|
-
typeDefinition = {
|
|
128
|
-
name: typeName,
|
|
129
|
-
kind: "typeAlias",
|
|
130
|
-
raw: node.getFullText().trim(),
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// Parse JSDoc comments for the type alias
|
|
134
|
-
const fullText = node.getFullText();
|
|
135
|
-
const jsDocMatch = fullText.match(/\/\*\*([\s\S]*?)\*\//);
|
|
136
|
-
if (jsDocMatch) {
|
|
137
|
-
const jsDocContent = jsDocMatch[1];
|
|
138
|
-
typeDefinition.dialTags = parseDialTags(jsDocContent);
|
|
139
|
-
typeDefinition.jsDoc = jsDocContent.trim();
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (ts.isTupleTypeNode(node.type)) {
|
|
143
|
-
typeDefinition.type = "tuple";
|
|
144
|
-
typeDefinition.elements = [];
|
|
145
|
-
|
|
146
|
-
node.type.elements.forEach((element, index) => {
|
|
147
|
-
const elementData: ElementData = {
|
|
148
|
-
index,
|
|
149
|
-
raw: element.getFullText().trim(),
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
if (ts.isNamedTupleMember(element)) {
|
|
153
|
-
elementData.name = element.name?.getText();
|
|
154
|
-
elementData.type = element.type?.getText();
|
|
155
|
-
|
|
156
|
-
// Extract inline comment with @dial annotations
|
|
157
|
-
const sourceText = element.getFullText();
|
|
158
|
-
const commentMatch = sourceText.match(/\/\/(.+)/);
|
|
159
|
-
if (commentMatch) {
|
|
160
|
-
elementData.comment = commentMatch[1].trim();
|
|
161
|
-
|
|
162
|
-
// Parse @dial annotations from the comment
|
|
163
|
-
const dialTags: DialTag = {};
|
|
164
|
-
|
|
165
|
-
// Parse @dial-min
|
|
166
|
-
const minMatch = commentMatch[1].match(/@dial-min\s+([\d.]+)/);
|
|
167
|
-
if (minMatch) {
|
|
168
|
-
dialTags.min = parseFloat(minMatch[1]);
|
|
169
|
-
elementData.min = dialTags.min;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Parse @dial-max
|
|
173
|
-
const maxMatch = commentMatch[1].match(/@dial-max\s+([\d.]+)/);
|
|
174
|
-
if (maxMatch) {
|
|
175
|
-
dialTags.max = parseFloat(maxMatch[1]);
|
|
176
|
-
elementData.max = dialTags.max;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Parse @dial-step
|
|
180
|
-
const stepMatch = commentMatch[1].match(/@dial-step\s+([\d.]+)/);
|
|
181
|
-
if (stepMatch) {
|
|
182
|
-
dialTags.step = parseFloat(stepMatch[1]);
|
|
183
|
-
elementData.step = dialTags.step;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Parse @dial-dtype
|
|
187
|
-
const dtypeMatch = commentMatch[1].match(/@dial-dtype\s+(\w+)/);
|
|
188
|
-
if (dtypeMatch) {
|
|
189
|
-
dialTags.dtype = dtypeMatch[1];
|
|
190
|
-
elementData.dtype = dtypeMatch[1];
|
|
191
|
-
} else {
|
|
192
|
-
elementData.dtype = "number";
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
elementData.dialTags = dialTags;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
typeDefinition.elements!.push(elementData);
|
|
200
|
-
});
|
|
201
|
-
} else if (ts.isArrayTypeNode(node.type)) {
|
|
202
|
-
typeDefinition!.type = "array";
|
|
203
|
-
typeDefinition!.elementType = node.type.elementType?.getText();
|
|
204
|
-
} else {
|
|
205
|
-
typeDefinition.type = "other";
|
|
206
|
-
typeDefinition.typeNode = node.type ? node.type.getText() : "unknown";
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
ts.forEachChild(node, visit);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
visit(sourceFile);
|
|
214
|
-
return typeDefinition;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Parse interface properties from TypeScript source
|
|
218
|
-
function parseInterfaceProperties(
|
|
219
|
-
sourceFile: ts.SourceFile,
|
|
220
|
-
interfaceName: string,
|
|
221
|
-
): PropertySchema[] {
|
|
222
|
-
const properties: PropertySchema[] = [];
|
|
223
|
-
let interfaceDialTags: DialTag = {};
|
|
224
|
-
const groupConfigs: Record<string, any> = {};
|
|
225
|
-
|
|
226
|
-
function visit(node: ts.Node) {
|
|
227
|
-
if (ts.isInterfaceDeclaration(node) && node.name.text === interfaceName) {
|
|
228
|
-
// First, get JSDoc comments for the interface itself
|
|
229
|
-
const interfaceJsDocNodes = ts.getJSDocCommentsAndTags(node);
|
|
230
|
-
if (interfaceJsDocNodes && interfaceJsDocNodes.length > 0) {
|
|
231
|
-
const interfaceFullText = interfaceJsDocNodes[0].getFullText();
|
|
232
|
-
interfaceDialTags = parseDialTags(interfaceFullText);
|
|
233
|
-
|
|
234
|
-
// Parse group-level configurations from interface JSDoc
|
|
235
|
-
const lines = interfaceFullText.split('\n');
|
|
236
|
-
lines.forEach(line => {
|
|
237
|
-
// Match patterns like "@dial transform @dial-no-wrap"
|
|
238
|
-
const groupMatch = line.match(/@dial\s+(\w+)(.*)/);
|
|
239
|
-
if (groupMatch) {
|
|
240
|
-
const groupName = groupMatch[1];
|
|
241
|
-
const configStr = groupMatch[2];
|
|
242
|
-
if (configStr.includes('@dial-no-wrap')) {
|
|
243
|
-
groupConfigs[groupName] = { noWrap: true };
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
node.members.forEach((member) => {
|
|
250
|
-
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
251
|
-
const propName = member.name.text;
|
|
252
|
-
|
|
253
|
-
// Get JSDoc comments for the property
|
|
254
|
-
const jsDocNodes = ts.getJSDocCommentsAndTags(member);
|
|
255
|
-
let propertyDialTags: DialTag = {};
|
|
256
|
-
|
|
257
|
-
if (jsDocNodes && jsDocNodes.length > 0) {
|
|
258
|
-
const fullText = jsDocNodes[0].getFullText();
|
|
259
|
-
propertyDialTags = parseDialTags(fullText);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Merge interface-level tags with property-level tags (property-level takes precedence)
|
|
263
|
-
const mergedDialTags = { ...interfaceDialTags, ...propertyDialTags };
|
|
264
|
-
|
|
265
|
-
// Build property schema
|
|
266
|
-
const prop: PropertySchema = {
|
|
267
|
-
name: propName,
|
|
268
|
-
dtype: mergedDialTags.dtype || "number",
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
// Add numeric constraints
|
|
272
|
-
if (mergedDialTags.min !== undefined) prop.min = mergedDialTags.min;
|
|
273
|
-
if (mergedDialTags.max !== undefined) prop.max = mergedDialTags.max;
|
|
274
|
-
if (mergedDialTags.step !== undefined) prop.step = mergedDialTags.step;
|
|
275
|
-
if (mergedDialTags.icon) prop.icon = mergedDialTags.icon;
|
|
276
|
-
|
|
277
|
-
// Add grouping and other tags
|
|
278
|
-
if (mergedDialTags.grouping || mergedDialTags.column || mergedDialTags.rowCount ||
|
|
279
|
-
mergedDialTags.colCount || mergedDialTags.labelPosition || mergedDialTags.noWrap) {
|
|
280
|
-
prop.tags = {};
|
|
281
|
-
if (mergedDialTags.grouping) {
|
|
282
|
-
prop.tags.grouping = mergedDialTags.grouping;
|
|
283
|
-
// Check if this group has noWrap configuration
|
|
284
|
-
if (groupConfigs[mergedDialTags.grouping]?.noWrap) {
|
|
285
|
-
prop.tags.noWrap = true;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
if (mergedDialTags.column) prop.tags.layout = "column";
|
|
289
|
-
if (mergedDialTags.rowCount) prop.tags.row = mergedDialTags.rowCount;
|
|
290
|
-
if (mergedDialTags.colCount) prop.tags.col = mergedDialTags.colCount;
|
|
291
|
-
if (mergedDialTags.labelPosition) prop.tags.labelPosition = mergedDialTags.labelPosition;
|
|
292
|
-
if (mergedDialTags.noWrap) prop.tags.noWrap = true;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
properties.push(prop);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
ts.forEachChild(node, visit);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
visit(sourceFile);
|
|
304
|
-
return properties;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Parse @dial annotations from JSDoc comments
|
|
308
|
-
function parseDialTags(description: string): DialTag & { ignore?: boolean } {
|
|
309
|
-
const dialTags: DialTag & { ignore?: boolean } = {};
|
|
310
|
-
|
|
311
|
-
if (!description) return dialTags;
|
|
312
|
-
|
|
313
|
-
// Check for @dial-ignore first
|
|
314
|
-
if (/@dial-ignore\b/i.test(description)) {
|
|
315
|
-
dialTags.ignore = true;
|
|
316
|
-
return dialTags; // Return early if ignore is set
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Extract @dial grouping tags (without hyphen)
|
|
320
|
-
const groupingPattern = /@dial\s+(transform|visibility|geometry|segments)\b/gm;
|
|
321
|
-
let match;
|
|
322
|
-
|
|
323
|
-
while ((match = groupingPattern.exec(description)) !== null) {
|
|
324
|
-
dialTags.grouping = match[1];
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Extract @dial-* property tags (with hyphen)
|
|
328
|
-
const propertyPattern = /@dial-([\w-]+)(?:\s+([^\n@]+?))?(?:\n|@|$)/gm;
|
|
329
|
-
|
|
330
|
-
while ((match = propertyPattern.exec(description)) !== null) {
|
|
331
|
-
const [, key, value] = match;
|
|
332
|
-
|
|
333
|
-
switch (key) {
|
|
334
|
-
case "options":
|
|
335
|
-
// Parse array-like values
|
|
336
|
-
if (value) {
|
|
337
|
-
try {
|
|
338
|
-
dialTags.options = JSON.parse(value);
|
|
339
|
-
} catch {
|
|
340
|
-
dialTags.options = value;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
break;
|
|
344
|
-
|
|
345
|
-
case "min":
|
|
346
|
-
case "max":
|
|
347
|
-
case "step":
|
|
348
|
-
// Parse numeric values
|
|
349
|
-
if (value) {
|
|
350
|
-
dialTags[key] = parseFloat(value);
|
|
351
|
-
}
|
|
352
|
-
break;
|
|
353
|
-
|
|
354
|
-
case "col":
|
|
355
|
-
case "column":
|
|
356
|
-
// Boolean flag for column layout
|
|
357
|
-
dialTags.column = true;
|
|
358
|
-
break;
|
|
359
|
-
|
|
360
|
-
case "row":
|
|
361
|
-
// Parse row layout
|
|
362
|
-
if (value && /^\d+$/.test(value)) {
|
|
363
|
-
dialTags.rowCount = parseInt(value, 10);
|
|
364
|
-
}
|
|
365
|
-
break;
|
|
366
|
-
|
|
367
|
-
case "col-3":
|
|
368
|
-
case "col-2":
|
|
369
|
-
case "col-4": {
|
|
370
|
-
// Parse column layout with number
|
|
371
|
-
const colMatch = key.match(/col-(\d+)/);
|
|
372
|
-
if (colMatch) {
|
|
373
|
-
dialTags.colCount = parseInt(colMatch[1], 10);
|
|
374
|
-
}
|
|
375
|
-
break;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
case "row-3":
|
|
379
|
-
case "row-2":
|
|
380
|
-
case "row-4": {
|
|
381
|
-
// Parse row layout with number
|
|
382
|
-
const rowMatch = key.match(/row-(\d+)/);
|
|
383
|
-
if (rowMatch) {
|
|
384
|
-
dialTags.rowCount = parseInt(rowMatch[1], 10);
|
|
385
|
-
}
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
case "dtype":
|
|
390
|
-
// Data type
|
|
391
|
-
if (value) {
|
|
392
|
-
dialTags.dtype = value.trim();
|
|
393
|
-
}
|
|
394
|
-
break;
|
|
395
|
-
|
|
396
|
-
case "dtypes":
|
|
397
|
-
// Comma-separated data types for vectors
|
|
398
|
-
if (value) {
|
|
399
|
-
dialTags.dtypes = value.split(",").map((s) => s.trim());
|
|
400
|
-
}
|
|
401
|
-
break;
|
|
402
|
-
|
|
403
|
-
case "type":
|
|
404
|
-
// Type reference
|
|
405
|
-
if (value) {
|
|
406
|
-
dialTags.type = value.trim();
|
|
407
|
-
}
|
|
408
|
-
break;
|
|
409
|
-
|
|
410
|
-
case "icon":
|
|
411
|
-
// Lucide icon name
|
|
412
|
-
if (value) {
|
|
413
|
-
dialTags.icon = value.trim();
|
|
414
|
-
}
|
|
415
|
-
break;
|
|
416
|
-
|
|
417
|
-
case "default":
|
|
418
|
-
// Default value for the property
|
|
419
|
-
if (value) {
|
|
420
|
-
try {
|
|
421
|
-
// Try to parse as JSON first (for arrays, objects, booleans, numbers)
|
|
422
|
-
dialTags.default = JSON.parse(value);
|
|
423
|
-
} catch {
|
|
424
|
-
// If not valid JSON, use as string
|
|
425
|
-
dialTags.default = value.trim();
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
break;
|
|
429
|
-
|
|
430
|
-
case "placeholders":
|
|
431
|
-
// Comma-separated placeholders for vector types
|
|
432
|
-
if (value) {
|
|
433
|
-
dialTags.placeholders = value.split(",").map((s) => s.trim());
|
|
434
|
-
}
|
|
435
|
-
break;
|
|
436
|
-
|
|
437
|
-
case "tooltips":
|
|
438
|
-
// Comma-separated tooltips for vector types
|
|
439
|
-
if (value) {
|
|
440
|
-
dialTags.tooltips = value.split(",").map((s) => s.trim());
|
|
441
|
-
}
|
|
442
|
-
break;
|
|
443
|
-
|
|
444
|
-
case "mins":
|
|
445
|
-
// Comma-separated min values
|
|
446
|
-
if (value) {
|
|
447
|
-
dialTags.mins = value.split(",").map((s) => parseFloat(s.trim()));
|
|
448
|
-
}
|
|
449
|
-
break;
|
|
450
|
-
|
|
451
|
-
case "maxs":
|
|
452
|
-
// Comma-separated max values
|
|
453
|
-
if (value) {
|
|
454
|
-
dialTags.maxs = value.split(",").map((s) => parseFloat(s.trim()));
|
|
455
|
-
}
|
|
456
|
-
break;
|
|
457
|
-
|
|
458
|
-
case "steps":
|
|
459
|
-
// Comma-separated step values
|
|
460
|
-
if (value) {
|
|
461
|
-
dialTags.steps = value.split(",").map((s) => parseFloat(s.trim()));
|
|
462
|
-
}
|
|
463
|
-
break;
|
|
464
|
-
|
|
465
|
-
case "label-left":
|
|
466
|
-
case "left":
|
|
467
|
-
// Label position left
|
|
468
|
-
dialTags.labelPosition = "left";
|
|
469
|
-
break;
|
|
470
|
-
|
|
471
|
-
case "label-right":
|
|
472
|
-
case "right":
|
|
473
|
-
// Label position right
|
|
474
|
-
dialTags.labelPosition = "right";
|
|
475
|
-
break;
|
|
476
|
-
|
|
477
|
-
case "label-center":
|
|
478
|
-
case "label-middle":
|
|
479
|
-
case "center":
|
|
480
|
-
case "middle":
|
|
481
|
-
// Label position center
|
|
482
|
-
dialTags.labelPosition = "center";
|
|
483
|
-
break;
|
|
484
|
-
|
|
485
|
-
case "label-top":
|
|
486
|
-
case "top":
|
|
487
|
-
// Label position top
|
|
488
|
-
dialTags.labelPosition = "top";
|
|
489
|
-
break;
|
|
490
|
-
|
|
491
|
-
case "label-bottom":
|
|
492
|
-
case "bottom":
|
|
493
|
-
// Label position bottom
|
|
494
|
-
dialTags.labelPosition = "bottom";
|
|
495
|
-
break;
|
|
496
|
-
|
|
497
|
-
case "label-inline":
|
|
498
|
-
case "inline":
|
|
499
|
-
// Label position inline
|
|
500
|
-
dialTags.labelPosition = "inline";
|
|
501
|
-
break;
|
|
502
|
-
|
|
503
|
-
case "no-wrap":
|
|
504
|
-
// No wrap flag for preventing line wrapping
|
|
505
|
-
dialTags.noWrap = true;
|
|
506
|
-
break;
|
|
507
|
-
|
|
508
|
-
default:
|
|
509
|
-
// Other key-value pairs could be handled here
|
|
510
|
-
break;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return dialTags;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Convert prop data to dial schema format
|
|
518
|
-
function propToDialSchema(propName: string, propData: Record<string, unknown>, groupConfigs?: Record<string, any>): PropertySchema | null {
|
|
519
|
-
const description = (propData.description as string) || "";
|
|
520
|
-
const dialTags = parseDialTags(description);
|
|
521
|
-
|
|
522
|
-
// Skip properties with @dial-ignore
|
|
523
|
-
if (dialTags.ignore) {
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Determine dtype based on type or dial tags
|
|
528
|
-
let dtype = dialTags.dtype;
|
|
529
|
-
if (!dtype) {
|
|
530
|
-
const type = propData.type as { name?: string } | undefined;
|
|
531
|
-
const typeName = type?.name || "";
|
|
532
|
-
if (typeName === "boolean") {
|
|
533
|
-
dtype = "boolean";
|
|
534
|
-
} else if (typeName === "number") {
|
|
535
|
-
dtype = "number";
|
|
536
|
-
} else if (typeName.includes("[]")) {
|
|
537
|
-
dtype = "array";
|
|
538
|
-
} else {
|
|
539
|
-
dtype = "string";
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Build schema object
|
|
544
|
-
const schema: PropertySchema = {
|
|
545
|
-
name: propName,
|
|
546
|
-
dtype,
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
// Add default value
|
|
550
|
-
// Priority: @dial-default annotation > component default > undefined
|
|
551
|
-
if (dialTags.default !== undefined) {
|
|
552
|
-
schema.value = dialTags.default;
|
|
553
|
-
} else {
|
|
554
|
-
const defaultValue = propData.defaultValue as { value?: string } | undefined;
|
|
555
|
-
if (defaultValue?.value) {
|
|
556
|
-
try {
|
|
557
|
-
schema.value = JSON.parse(defaultValue.value);
|
|
558
|
-
} catch {
|
|
559
|
-
schema.value = defaultValue.value;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Add dial tags
|
|
565
|
-
if (dialTags.min !== undefined) schema.min = dialTags.min;
|
|
566
|
-
if (dialTags.max !== undefined) schema.max = dialTags.max;
|
|
567
|
-
if (dialTags.step !== undefined) schema.step = dialTags.step;
|
|
568
|
-
if (dialTags.options) schema.options = dialTags.options;
|
|
569
|
-
if (dialTags.icon) schema.icon = dialTags.icon;
|
|
570
|
-
|
|
571
|
-
// Add vector-specific metadata
|
|
572
|
-
if (dialTags.placeholders) schema.placeholders = dialTags.placeholders;
|
|
573
|
-
if (dialTags.tooltips) schema.tooltips = dialTags.tooltips;
|
|
574
|
-
if (dialTags.dtypes) schema.dtypes = dialTags.dtypes;
|
|
575
|
-
if (dialTags.mins) schema.mins = dialTags.mins;
|
|
576
|
-
if (dialTags.maxs) schema.maxs = dialTags.maxs;
|
|
577
|
-
if (dialTags.steps) schema.steps = dialTags.steps;
|
|
578
|
-
|
|
579
|
-
// Add tags for grouping and layout
|
|
580
|
-
const tags: Record<string, unknown> = {};
|
|
581
|
-
if (dialTags.grouping) {
|
|
582
|
-
tags.grouping = dialTags.grouping;
|
|
583
|
-
|
|
584
|
-
// Check if this group has noWrap configuration
|
|
585
|
-
if (groupConfigs && groupConfigs[dialTags.grouping]?.noWrap) {
|
|
586
|
-
tags.noWrap = true;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
if (dialTags.column) {
|
|
590
|
-
tags.col = true;
|
|
591
|
-
}
|
|
592
|
-
if (dialTags.rowCount) {
|
|
593
|
-
tags.row = dialTags.rowCount;
|
|
594
|
-
tags.layout = "row";
|
|
595
|
-
}
|
|
596
|
-
if (dialTags.colCount) {
|
|
597
|
-
tags.col = dialTags.colCount;
|
|
598
|
-
tags.layout = "column";
|
|
599
|
-
}
|
|
600
|
-
if (dialTags.labelPosition) {
|
|
601
|
-
tags.labelPosition = dialTags.labelPosition;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
if (Object.keys(tags).length > 0) {
|
|
605
|
-
schema.tags = tags;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
return schema;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// Process a single file
|
|
612
|
-
async function processFile(
|
|
613
|
-
filePath: string,
|
|
614
|
-
outputDir: string,
|
|
615
|
-
options: docgen.ParserOptions,
|
|
616
|
-
flags: { verbose?: boolean; quiet?: boolean; ignoreList?: string[] } = {},
|
|
617
|
-
): Promise<void> {
|
|
618
|
-
if (!flags.quiet) {
|
|
619
|
-
console.log(`\n📦 Processing ${filePath}...`);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Parse with react-docgen-typescript
|
|
623
|
-
const docs = docgen.parse(filePath, options);
|
|
624
|
-
|
|
625
|
-
// Also parse the TypeScript source
|
|
626
|
-
const sourceCode = readFileSync(filePath, "utf-8");
|
|
627
|
-
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);
|
|
628
|
-
|
|
629
|
-
// Process each component
|
|
630
|
-
const enhancedMetadata = docs.map((doc) => {
|
|
631
|
-
const dialSchema: PropertySchema[] = [];
|
|
632
|
-
|
|
633
|
-
// Parse component-level dial configuration from description
|
|
634
|
-
const groupConfigs: Record<string, any> = {};
|
|
635
|
-
if (doc.description) {
|
|
636
|
-
const componentTags = parseDialTags(doc.description);
|
|
637
|
-
// Extract group-specific configuration
|
|
638
|
-
const descLines = doc.description.split('\n');
|
|
639
|
-
descLines.forEach(line => {
|
|
640
|
-
// Match patterns like "@dial transform @dial-no-wrap"
|
|
641
|
-
const groupMatch = line.match(/@dial\s+(\w+)(.*)/);
|
|
642
|
-
if (groupMatch) {
|
|
643
|
-
const groupName = groupMatch[1];
|
|
644
|
-
const configStr = groupMatch[2];
|
|
645
|
-
if (configStr.includes('@dial-no-wrap')) {
|
|
646
|
-
groupConfigs[groupName] = { noWrap: true };
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Also look for the Props interface and parse its JSDoc for group configurations
|
|
653
|
-
// The props interface is typically named [ComponentName]Props
|
|
654
|
-
const propsInterfaceName = doc.displayName + 'Props';
|
|
655
|
-
function findInterfaceJSDoc(node: ts.Node): void {
|
|
656
|
-
if (ts.isInterfaceDeclaration(node) && node.name.text === propsInterfaceName) {
|
|
657
|
-
const interfaceJsDocNodes = ts.getJSDocCommentsAndTags(node);
|
|
658
|
-
if (interfaceJsDocNodes && interfaceJsDocNodes.length > 0) {
|
|
659
|
-
const interfaceFullText = interfaceJsDocNodes[0].getFullText();
|
|
660
|
-
// Parse group-level configurations from interface JSDoc
|
|
661
|
-
const lines = interfaceFullText.split('\n');
|
|
662
|
-
lines.forEach(line => {
|
|
663
|
-
// Match patterns like "@dial transform @dial-no-wrap"
|
|
664
|
-
const groupMatch = line.match(/@dial\s+(\w+)(.*)/);
|
|
665
|
-
if (groupMatch) {
|
|
666
|
-
const groupName = groupMatch[1];
|
|
667
|
-
const configStr = groupMatch[2];
|
|
668
|
-
if (configStr.includes('@dial-no-wrap')) {
|
|
669
|
-
groupConfigs[groupName] = { noWrap: true };
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
ts.forEachChild(node, findInterfaceJSDoc);
|
|
676
|
-
}
|
|
677
|
-
findInterfaceJSDoc(sourceFile);
|
|
678
|
-
|
|
679
|
-
// Convert each prop to dial schema format
|
|
680
|
-
for (const [propName, propData] of Object.entries(doc.props || {})) {
|
|
681
|
-
const propDescription = (propData as { description?: string }).description || "";
|
|
682
|
-
const propDialTags = parseDialTags(propDescription);
|
|
683
|
-
|
|
684
|
-
// Skip properties with @dial-ignore
|
|
685
|
-
if (propDialTags.ignore) {
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
const propType = (propData as { type?: { name?: string } }).type;
|
|
690
|
-
const referencedType = propDialTags.type || propType?.name;
|
|
691
|
-
|
|
692
|
-
// Check if this prop references a type with dial annotations
|
|
693
|
-
if (referencedType) {
|
|
694
|
-
const typeDefinition = parseTypeAlias(sourceFile, referencedType);
|
|
695
|
-
|
|
696
|
-
if (typeDefinition) {
|
|
697
|
-
// Create schema with type definition
|
|
698
|
-
const schema = propToDialSchema(propName, propData as unknown as Record<string, unknown>, groupConfigs);
|
|
699
|
-
|
|
700
|
-
// Skip if the property is ignored
|
|
701
|
-
if (!schema) {
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Handle vector types with element annotations
|
|
706
|
-
if (typeDefinition.elements && typeDefinition.elements.length > 0) {
|
|
707
|
-
schema.dtype = "vector";
|
|
708
|
-
|
|
709
|
-
// Build arrays from individual element annotations
|
|
710
|
-
const mins: number[] = [];
|
|
711
|
-
const maxs: number[] = [];
|
|
712
|
-
const steps: number[] = [];
|
|
713
|
-
const dtypes: string[] = [];
|
|
714
|
-
const placeholders: string[] = [];
|
|
715
|
-
const tooltips: string[] = [];
|
|
716
|
-
|
|
717
|
-
typeDefinition.elements.forEach((element) => {
|
|
718
|
-
if (element.name) {
|
|
719
|
-
mins.push(element.min !== undefined ? element.min : 0);
|
|
720
|
-
maxs.push(element.max !== undefined ? element.max : 100);
|
|
721
|
-
steps.push(element.step !== undefined ? element.step : 1);
|
|
722
|
-
dtypes.push(element.dtype || "number");
|
|
723
|
-
placeholders.push(element.name);
|
|
724
|
-
|
|
725
|
-
// Create descriptive tooltips
|
|
726
|
-
const tooltip = element.name.includes("Segments")
|
|
727
|
-
? `${element.name.replace("Segments", " segments")}`
|
|
728
|
-
: element.name.charAt(0).toUpperCase() + element.name.slice(1);
|
|
729
|
-
tooltips.push(tooltip);
|
|
730
|
-
}
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
if (mins.length > 0) schema.mins = mins;
|
|
734
|
-
if (maxs.length > 0) schema.maxs = maxs;
|
|
735
|
-
if (steps.length > 0) schema.steps = steps;
|
|
736
|
-
if (dtypes.length > 0) schema.dtypes = dtypes;
|
|
737
|
-
if (placeholders.length > 0) schema.placeholders = placeholders;
|
|
738
|
-
if (tooltips.length > 0) schema.tooltips = tooltips;
|
|
739
|
-
|
|
740
|
-
schema.typeDefinition = typeDefinition;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
dialSchema.push(schema);
|
|
744
|
-
|
|
745
|
-
// Also parse interface properties if it's an interface
|
|
746
|
-
const interfaceProps = parseInterfaceProperties(sourceFile, referencedType);
|
|
747
|
-
interfaceProps.forEach((prop) => {
|
|
748
|
-
dialSchema.push({
|
|
749
|
-
...prop,
|
|
750
|
-
name: `${propName}.${prop.name}`,
|
|
751
|
-
tags: {
|
|
752
|
-
grouping: "nested",
|
|
753
|
-
parent: propName,
|
|
754
|
-
hidden: true,
|
|
755
|
-
metadata: true,
|
|
756
|
-
},
|
|
757
|
-
});
|
|
758
|
-
});
|
|
759
|
-
} else {
|
|
760
|
-
// Regular prop without type definition
|
|
761
|
-
const schema = propToDialSchema(propName, propData as unknown as Record<string, unknown>, groupConfigs);
|
|
762
|
-
if (schema) {
|
|
763
|
-
dialSchema.push(schema);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
} else {
|
|
767
|
-
// Regular prop without type reference
|
|
768
|
-
const schema = propToDialSchema(propName, propData as unknown as Record<string, unknown>, groupConfigs);
|
|
769
|
-
if (schema) {
|
|
770
|
-
dialSchema.push(schema);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
return {
|
|
776
|
-
displayName: doc.displayName,
|
|
777
|
-
description: doc.description,
|
|
778
|
-
dialSchema,
|
|
779
|
-
groupConfigs,
|
|
780
|
-
props: doc.props,
|
|
781
|
-
};
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
// Create output directory if it doesn't exist
|
|
785
|
-
if (!existsSync(outputDir)) {
|
|
786
|
-
mkdirSync(outputDir, { recursive: true });
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
const baseName = basename(filePath, ".tsx").toLowerCase();
|
|
790
|
-
|
|
791
|
-
// Write raw docgen output
|
|
792
|
-
writeFileSync(join(outputDir, `${baseName}-raw.json`), JSON.stringify(docs, null, 2));
|
|
793
|
-
if (!flags.quiet) {
|
|
794
|
-
console.log(`📝 Wrote raw metadata to ${baseName}-raw.json`);
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// Write enhanced metadata with dial schema
|
|
798
|
-
writeFileSync(
|
|
799
|
-
join(outputDir, `${baseName}-combined.json`),
|
|
800
|
-
JSON.stringify(enhancedMetadata, null, 2),
|
|
801
|
-
);
|
|
802
|
-
if (!flags.quiet) {
|
|
803
|
-
console.log(`📝 Wrote enhanced metadata to ${baseName}-combined.json`);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// Write just the dial schemas with groups
|
|
807
|
-
const schemas = enhancedMetadata.map((c) => {
|
|
808
|
-
// Convert groupConfigs to groups array
|
|
809
|
-
const groups = Object.entries(c.groupConfigs || {}).map(([name, config]) => ({
|
|
810
|
-
name,
|
|
811
|
-
...config
|
|
812
|
-
}));
|
|
813
|
-
|
|
814
|
-
return {
|
|
815
|
-
component: c.displayName,
|
|
816
|
-
schemas: c.dialSchema,
|
|
817
|
-
groups: groups.length > 0 ? groups : undefined,
|
|
818
|
-
config: c.dialConfig,
|
|
819
|
-
};
|
|
820
|
-
});
|
|
821
|
-
writeFileSync(join(outputDir, `${baseName}-schemas.json`), JSON.stringify(schemas, null, 2));
|
|
822
|
-
if (!flags.quiet) {
|
|
823
|
-
console.log(`📝 Wrote dial schemas to ${baseName}-schemas.json`);
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
// Log sample output in verbose mode
|
|
827
|
-
if (flags.verbose && enhancedMetadata.length > 0) {
|
|
828
|
-
console.log("\n📊 Generated dial schemas:");
|
|
829
|
-
enhancedMetadata.forEach((meta) => {
|
|
830
|
-
console.log(`\n Component: ${meta.displayName}`);
|
|
831
|
-
console.log(` Properties: ${meta.dialSchema.length}`);
|
|
832
|
-
if (meta.dialSchema.length > 0) {
|
|
833
|
-
console.log(` Sample schema:`);
|
|
834
|
-
console.log(JSON.stringify(meta.dialSchema[0], null, 4).split('\n').map(line => ' ' + line).join('\n'));
|
|
835
|
-
}
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
// Help text for the CLI (with dynamic version)
|
|
841
|
-
function getHelpText(): string {
|
|
842
|
-
const { version, gitHash } = getVersionInfo();
|
|
843
|
-
const versionString = gitHash ? `v${version} (${gitHash})` : `v${version}`;
|
|
844
|
-
|
|
845
|
-
return `
|
|
846
|
-
dial-cli - Generate dial metadata from TypeScript/React components
|
|
847
|
-
|
|
848
|
-
SYNOPSIS
|
|
849
|
-
dial-cli [options] <file.tsx> [file2.tsx ...]
|
|
850
|
-
|
|
851
|
-
DESCRIPTION
|
|
852
|
-
The dial-cli tool parses TypeScript and React component files to extract
|
|
853
|
-
@dial annotations from JSDoc comments and generates JSON metadata files
|
|
854
|
-
that can be used to automatically create UI controls.
|
|
855
|
-
|
|
856
|
-
The tool generates three types of output files for each input:
|
|
857
|
-
• *-raw.json - Raw output from react-docgen-typescript
|
|
858
|
-
• *-combined.json - Enhanced metadata with dial schema information
|
|
859
|
-
• *-schemas.json - Just the dial schemas for UI generation
|
|
860
|
-
|
|
861
|
-
OPTIONS
|
|
862
|
-
-o, --output <dir>
|
|
863
|
-
Output directory for generated metadata files (default: ./metadata)
|
|
864
|
-
|
|
865
|
-
-i, --ignore <prop>
|
|
866
|
-
Property names or patterns to exclude from dial schema generation
|
|
867
|
-
Can be specified multiple times: -i prop1 -i prop2
|
|
868
|
-
Or as a comma-separated list: -i prop1,prop2,prop3
|
|
869
|
-
Supports glob patterns: -i "*Style" -i "on*" -i "_*"
|
|
870
|
-
|
|
871
|
-
-h, --help
|
|
872
|
-
Display this help message and exit
|
|
873
|
-
|
|
874
|
-
-v, --version
|
|
875
|
-
Display version information and exit
|
|
876
|
-
|
|
877
|
-
--verbose
|
|
878
|
-
Enable verbose output with detailed processing information
|
|
879
|
-
|
|
880
|
-
--quiet
|
|
881
|
-
Suppress all output except errors
|
|
882
|
-
|
|
883
|
-
DIAL ANNOTATIONS
|
|
884
|
-
The tool recognizes the following @dial annotations in JSDoc comments:
|
|
885
|
-
|
|
886
|
-
Grouping:
|
|
887
|
-
@dial <group> Group related properties (transform, visibility, etc.)
|
|
888
|
-
|
|
889
|
-
Property Configuration:
|
|
890
|
-
@dial-dtype <type> Data type (vector3, euler, boolean, int, etc.)
|
|
891
|
-
@dial-min <number> Minimum value for numeric inputs
|
|
892
|
-
@dial-max <number> Maximum value for numeric inputs
|
|
893
|
-
@dial-step <number> Step increment for numeric inputs
|
|
894
|
-
@dial-default <value> Default value (overrides component default)
|
|
895
|
-
@dial-options [...] Array of preset values
|
|
896
|
-
@dial-icon <name> Lucide icon name for the control
|
|
897
|
-
@dial-ignore Exclude this property from dial schema generation
|
|
898
|
-
|
|
899
|
-
FormLayout:
|
|
900
|
-
@dial-col-<n> Display in column layout with n columns
|
|
901
|
-
@dial-row-<n> Display in row layout with n items per row
|
|
902
|
-
@dial-label-<pos> Label position (left, right, top, bottom, inline)
|
|
903
|
-
|
|
904
|
-
Vector Types:
|
|
905
|
-
@dial-mins <n,n,n> Comma-separated min values for vector elements
|
|
906
|
-
@dial-maxs <n,n,n> Comma-separated max values for vector elements
|
|
907
|
-
@dial-steps <n,n,n> Comma-separated step values for vector elements
|
|
908
|
-
@dial-dtypes <t,t,t> Comma-separated data types for vector elements
|
|
909
|
-
|
|
910
|
-
EXAMPLES
|
|
911
|
-
Generate metadata for a single component:
|
|
912
|
-
$ dial-cli MyComponent.tsx
|
|
913
|
-
|
|
914
|
-
Specify a custom output directory:
|
|
915
|
-
$ dial-cli -o ./docs/metadata MyComponent.tsx
|
|
916
|
-
|
|
917
|
-
Process multiple files:
|
|
918
|
-
$ dial-cli Component1.tsx Component2.tsx Component3.tsx
|
|
919
|
-
|
|
920
|
-
Process all components in a directory:
|
|
921
|
-
$ dial-cli src/components/*.tsx
|
|
922
|
-
|
|
923
|
-
Generate metadata with verbose output:
|
|
924
|
-
$ dial-cli --verbose MyComponent.tsx
|
|
925
|
-
|
|
926
|
-
Exclude specific properties from schema generation:
|
|
927
|
-
$ dial-cli -i className -i style -i children MyComponent.tsx
|
|
928
|
-
$ dial-cli --ignore ref,key,id MyComponent.tsx
|
|
929
|
-
|
|
930
|
-
EXAMPLE COMPONENT
|
|
931
|
-
interface Props {
|
|
932
|
-
/**
|
|
933
|
-
* Position in 3D space
|
|
934
|
-
* @dial transform
|
|
935
|
-
* @dial-dtype vector3
|
|
936
|
-
* @dial-mins -10,-10,-10
|
|
937
|
-
* @dial-maxs 10,10,10
|
|
938
|
-
* @dial-steps 0.1,0.1,0.1
|
|
939
|
-
* @dial-icon Move3d
|
|
940
|
-
*/
|
|
941
|
-
position: [number, number, number];
|
|
942
|
-
|
|
943
|
-
/**
|
|
944
|
-
* Visibility toggle
|
|
945
|
-
* @dial visibility
|
|
946
|
-
* @dial-dtype boolean
|
|
947
|
-
* @dial-icon Eye
|
|
948
|
-
*/
|
|
949
|
-
visible: boolean;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
FILES
|
|
953
|
-
The tool generates the following files in the output directory:
|
|
954
|
-
|
|
955
|
-
<component>-raw.json
|
|
956
|
-
Raw docgen output with all prop information
|
|
957
|
-
|
|
958
|
-
<component>-combined.json
|
|
959
|
-
Enhanced metadata including dial schemas and type definitions
|
|
960
|
-
|
|
961
|
-
<component>-schemas.json
|
|
962
|
-
Extracted dial schemas ready for UI generation
|
|
963
|
-
|
|
964
|
-
ENVIRONMENT
|
|
965
|
-
NODE_ENV
|
|
966
|
-
Set to 'development' for debug output
|
|
967
|
-
|
|
968
|
-
SEE ALSO
|
|
969
|
-
Documentation: https://uikit.vuer.ai/dial
|
|
970
|
-
GitHub: https://github.com/vuer-ai/vuer-uikit
|
|
971
|
-
|
|
972
|
-
VERSION
|
|
973
|
-
dial-cli ${versionString} (part of @vuer-ai/vuer-uikit)
|
|
974
|
-
|
|
975
|
-
AUTHORS
|
|
976
|
-
Vuer AI Team <team@vuer.ai>
|
|
977
|
-
`;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// Main CLI function
|
|
981
|
-
async function main() {
|
|
982
|
-
const { values, positionals } = parseArgs({
|
|
983
|
-
args: process.argv.slice(2),
|
|
984
|
-
options: {
|
|
985
|
-
output: {
|
|
986
|
-
type: "string",
|
|
987
|
-
short: "o",
|
|
988
|
-
default: "./metadata",
|
|
989
|
-
},
|
|
990
|
-
ignore: {
|
|
991
|
-
type: "string",
|
|
992
|
-
short: "i",
|
|
993
|
-
multiple: true,
|
|
994
|
-
default: [],
|
|
995
|
-
},
|
|
996
|
-
help: {
|
|
997
|
-
type: "boolean",
|
|
998
|
-
short: "h",
|
|
999
|
-
},
|
|
1000
|
-
version: {
|
|
1001
|
-
type: "boolean",
|
|
1002
|
-
short: "v",
|
|
1003
|
-
},
|
|
1004
|
-
verbose: {
|
|
1005
|
-
type: "boolean",
|
|
1006
|
-
},
|
|
1007
|
-
quiet: {
|
|
1008
|
-
type: "boolean",
|
|
1009
|
-
},
|
|
1010
|
-
},
|
|
1011
|
-
allowPositionals: true,
|
|
1012
|
-
});
|
|
1013
|
-
|
|
1014
|
-
// Handle version flag
|
|
1015
|
-
if (values.version) {
|
|
1016
|
-
const { version, gitHash } = getVersionInfo();
|
|
1017
|
-
const versionString = gitHash ? `v${version} (${gitHash})` : `v${version}`;
|
|
1018
|
-
console.log(`dial-cli ${versionString} (part of @vuer-ai/vuer-uikit)`);
|
|
1019
|
-
process.exit(0);
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
// Handle help flag or no arguments
|
|
1023
|
-
if (values.help || positionals.length === 0) {
|
|
1024
|
-
console.log(getHelpText());
|
|
1025
|
-
process.exit(0);
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
// Check for man page style help requests
|
|
1029
|
-
if (positionals.length === 1 && (positionals[0] === "man" || positionals[0] === "help")) {
|
|
1030
|
-
console.log(getHelpText());
|
|
1031
|
-
process.exit(0);
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
const outputDir = resolve(process.cwd(), values.output as string);
|
|
1035
|
-
|
|
1036
|
-
// Process ignore list - handle both multiple -i flags and comma-separated values
|
|
1037
|
-
const ignoreList: string[] = [];
|
|
1038
|
-
const ignoreValues = values.ignore as string | string[] | undefined;
|
|
1039
|
-
if (ignoreValues) {
|
|
1040
|
-
if (Array.isArray(ignoreValues)) {
|
|
1041
|
-
ignoreValues.forEach(val => {
|
|
1042
|
-
// Split by comma in case user provided comma-separated list
|
|
1043
|
-
ignoreList.push(...val.split(',').map(s => s.trim()));
|
|
1044
|
-
});
|
|
1045
|
-
} else if (typeof ignoreValues === 'string') {
|
|
1046
|
-
ignoreList.push(...ignoreValues.split(',').map(s => s.trim()));
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
// Parser options for react-docgen-typescript
|
|
1051
|
-
const options: docgen.ParserOptions = {
|
|
1052
|
-
savePropValueAsString: true,
|
|
1053
|
-
shouldExtractLiteralValuesFromEnum: true,
|
|
1054
|
-
shouldRemoveUndefinedFromOptional: true,
|
|
1055
|
-
propFilter: (prop) => {
|
|
1056
|
-
// Include all props except React internals and ignored props
|
|
1057
|
-
if (prop.name === "ref" || prop.name === "key") {
|
|
1058
|
-
return false;
|
|
1059
|
-
}
|
|
1060
|
-
// Check if prop name matches any ignore pattern
|
|
1061
|
-
// Support both exact matches and patterns with wildcards
|
|
1062
|
-
for (const pattern of ignoreList) {
|
|
1063
|
-
if (pattern.includes('*') || pattern.includes('?')) {
|
|
1064
|
-
// Simple glob pattern matching
|
|
1065
|
-
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
|
|
1066
|
-
if (regex.test(prop.name)) {
|
|
1067
|
-
return false;
|
|
1068
|
-
}
|
|
1069
|
-
} else if (prop.name === pattern) {
|
|
1070
|
-
// Exact match
|
|
1071
|
-
return false;
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
return true;
|
|
1075
|
-
},
|
|
1076
|
-
};
|
|
1077
|
-
|
|
1078
|
-
const flags = {
|
|
1079
|
-
verbose: values.verbose as boolean | undefined,
|
|
1080
|
-
quiet: values.quiet as boolean | undefined,
|
|
1081
|
-
ignoreList,
|
|
1082
|
-
};
|
|
1083
|
-
|
|
1084
|
-
if (!flags.quiet) {
|
|
1085
|
-
const { version, gitHash } = getVersionInfo();
|
|
1086
|
-
const versionString = gitHash ? `v${version} (${gitHash})` : `v${version}`;
|
|
1087
|
-
console.log(`🚀 Dial Documentation Generator ${versionString}`);
|
|
1088
|
-
console.log(`📂 Output directory: ${outputDir}`);
|
|
1089
|
-
if (flags.verbose && ignoreList.length > 0) {
|
|
1090
|
-
console.log(`🚫 Ignoring properties: ${ignoreList.join(', ')}`);
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// Process each file
|
|
1095
|
-
let filesProcessed = 0;
|
|
1096
|
-
let filesErrored = 0;
|
|
1097
|
-
|
|
1098
|
-
for (const file of positionals) {
|
|
1099
|
-
const filePath = resolve(process.cwd(), file);
|
|
1100
|
-
|
|
1101
|
-
if (!existsSync(filePath)) {
|
|
1102
|
-
console.error(`❌ File not found: ${filePath}`);
|
|
1103
|
-
filesErrored++;
|
|
1104
|
-
continue;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
try {
|
|
1108
|
-
await processFile(filePath, outputDir, options, flags);
|
|
1109
|
-
filesProcessed++;
|
|
1110
|
-
} catch (error) {
|
|
1111
|
-
console.error(`❌ Error processing ${filePath}:`, error);
|
|
1112
|
-
filesErrored++;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
if (!flags.quiet) {
|
|
1117
|
-
console.log("\n✅ Documentation generation complete!");
|
|
1118
|
-
if (flags.verbose) {
|
|
1119
|
-
console.log(` Files processed: ${filesProcessed}`);
|
|
1120
|
-
if (filesErrored > 0) {
|
|
1121
|
-
console.log(` Files with errors: ${filesErrored}`);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
// Run the CLI - always run when this is the main module
|
|
1128
|
-
main().catch((error) => {
|
|
1129
|
-
console.error("Fatal error:", error);
|
|
1130
|
-
process.exit(1);
|
|
1131
|
-
});
|
|
1132
|
-
|
|
1133
|
-
export { parseDialTags, propToDialSchema, parseTypeAlias, parseInterfaceProperties };
|