@idealyst/tooling 1.2.4 → 1.2.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/tooling",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Code analysis and validation utilities for Idealyst Framework",
5
5
  "readme": "README.md",
6
6
  "main": "src/index.ts",
@@ -18,6 +18,7 @@ import type {
18
18
  ComponentAnalyzerOptions,
19
19
  ThemeValues,
20
20
  ComponentCategory,
21
+ SampleProps,
21
22
  } from './types';
22
23
  import { analyzeTheme } from './theme-analyzer';
23
24
 
@@ -191,15 +192,153 @@ function analyzeComponentDir(
191
192
  // Determine category
192
193
  const category = inferCategory(componentName);
193
194
 
195
+ // Look for docs.ts to extract sample props
196
+ const sampleProps = extractSampleProps(dir);
197
+
194
198
  return {
195
199
  name: componentName,
196
200
  description,
197
201
  props,
198
202
  category,
199
203
  filePath: path.relative(process.cwd(), dir),
204
+ sampleProps,
200
205
  };
201
206
  }
202
207
 
208
+ /**
209
+ * Extract sample props from docs.ts file if it exists.
210
+ * The docs.ts file should export a `sampleProps` object.
211
+ */
212
+ function extractSampleProps(dir: string): SampleProps | undefined {
213
+ const docsPath = path.join(dir, 'docs.ts');
214
+
215
+ if (!fs.existsSync(docsPath)) {
216
+ return undefined;
217
+ }
218
+
219
+ try {
220
+ const content = fs.readFileSync(docsPath, 'utf-8');
221
+
222
+ // Create a simple TypeScript program to extract the sampleProps export
223
+ const sourceFile = ts.createSourceFile(
224
+ 'docs.ts',
225
+ content,
226
+ ts.ScriptTarget.ES2020,
227
+ true,
228
+ ts.ScriptKind.TS
229
+ );
230
+
231
+ let samplePropsNode: ts.ObjectLiteralExpression | null = null;
232
+
233
+ // Find the sampleProps export
234
+ ts.forEachChild(sourceFile, (node) => {
235
+ // Handle: export const sampleProps = { ... }
236
+ if (ts.isVariableStatement(node)) {
237
+ const isExported = node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword);
238
+ if (isExported) {
239
+ for (const decl of node.declarationList.declarations) {
240
+ if (ts.isIdentifier(decl.name) && decl.name.text === 'sampleProps' && decl.initializer) {
241
+ if (ts.isObjectLiteralExpression(decl.initializer)) {
242
+ samplePropsNode = decl.initializer;
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ });
249
+
250
+ if (!samplePropsNode) {
251
+ return undefined;
252
+ }
253
+
254
+ // Extract the object literal as JSON-compatible structure
255
+ // This is a simplified extraction - it handles basic literals
256
+ const result: SampleProps = {};
257
+
258
+ for (const prop of samplePropsNode.properties) {
259
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
260
+ const propName = prop.name.text;
261
+
262
+ if (propName === 'props' && ts.isObjectLiteralExpression(prop.initializer)) {
263
+ result.props = extractObjectLiteral(prop.initializer, content);
264
+ } else if (propName === 'children') {
265
+ // For children, we store the raw source text
266
+ result.children = prop.initializer.getText(sourceFile);
267
+ } else if (propName === 'state' && ts.isObjectLiteralExpression(prop.initializer)) {
268
+ // Extract state configuration for controlled components
269
+ result.state = extractObjectLiteral(prop.initializer, content);
270
+ }
271
+ }
272
+ }
273
+
274
+ return Object.keys(result).length > 0 ? result : undefined;
275
+ } catch (e) {
276
+ console.warn(`[component-analyzer] Error reading docs.ts in ${dir}:`, e);
277
+ return undefined;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Extract an object literal to a plain object (for simple literal values).
283
+ */
284
+ function extractObjectLiteral(node: ts.ObjectLiteralExpression, sourceContent: string): Record<string, any> {
285
+ const result: Record<string, any> = {};
286
+
287
+ for (const prop of node.properties) {
288
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
289
+ const key = prop.name.text;
290
+ const init = prop.initializer;
291
+
292
+ if (ts.isStringLiteral(init)) {
293
+ result[key] = init.text;
294
+ } else if (ts.isNumericLiteral(init)) {
295
+ result[key] = Number(init.text);
296
+ } else if (init.kind === ts.SyntaxKind.TrueKeyword) {
297
+ result[key] = true;
298
+ } else if (init.kind === ts.SyntaxKind.FalseKeyword) {
299
+ result[key] = false;
300
+ } else if (ts.isArrayLiteralExpression(init)) {
301
+ result[key] = extractArrayLiteral(init, sourceContent);
302
+ } else if (ts.isObjectLiteralExpression(init)) {
303
+ result[key] = extractObjectLiteral(init, sourceContent);
304
+ } else {
305
+ // For complex expressions (JSX, functions), store the raw source
306
+ result[key] = init.getText();
307
+ }
308
+ }
309
+ }
310
+
311
+ return result;
312
+ }
313
+
314
+ /**
315
+ * Extract an array literal to a plain array.
316
+ */
317
+ function extractArrayLiteral(node: ts.ArrayLiteralExpression, sourceContent: string): any[] {
318
+ const result: any[] = [];
319
+
320
+ for (const element of node.elements) {
321
+ if (ts.isStringLiteral(element)) {
322
+ result.push(element.text);
323
+ } else if (ts.isNumericLiteral(element)) {
324
+ result.push(Number(element.text));
325
+ } else if (element.kind === ts.SyntaxKind.TrueKeyword) {
326
+ result.push(true);
327
+ } else if (element.kind === ts.SyntaxKind.FalseKeyword) {
328
+ result.push(false);
329
+ } else if (ts.isObjectLiteralExpression(element)) {
330
+ result.push(extractObjectLiteral(element, sourceContent));
331
+ } else if (ts.isArrayLiteralExpression(element)) {
332
+ result.push(extractArrayLiteral(element, sourceContent));
333
+ } else {
334
+ // For complex expressions, store raw source
335
+ result.push(element.getText());
336
+ }
337
+ }
338
+
339
+ return result;
340
+ }
341
+
203
342
  /**
204
343
  * Analyze a single property symbol.
205
344
  */
@@ -28,6 +28,30 @@ export interface PropDefinition {
28
28
  required: boolean;
29
29
  }
30
30
 
31
+ /**
32
+ * Configuration for a controlled state binding
33
+ */
34
+ export interface ControlledState {
35
+ /** Initial value for the state */
36
+ initial: any;
37
+ /** The prop name that receives the onChange callback */
38
+ onChangeProp: string;
39
+ /** If true, the callback toggles a boolean value instead of receiving a new value */
40
+ toggle?: boolean;
41
+ }
42
+
43
+ /**
44
+ * Sample props for rendering the component in documentation
45
+ */
46
+ export interface SampleProps {
47
+ /** Props needed to render the component (required props, sample data) */
48
+ props?: Record<string, any>;
49
+ /** Default children for the component */
50
+ children?: any;
51
+ /** Controlled state bindings - key is the prop name, value is the state config */
52
+ state?: Record<string, ControlledState>;
53
+ }
54
+
31
55
  /**
32
56
  * Definition of a component in the registry
33
57
  */
@@ -46,6 +70,9 @@ export interface ComponentDefinition {
46
70
 
47
71
  /** Path to the component file (relative) */
48
72
  filePath?: string;
73
+
74
+ /** Sample props for rendering in documentation (from docs.ts) */
75
+ sampleProps?: SampleProps;
49
76
  }
50
77
 
51
78
  /**
package/src/index.ts CHANGED
@@ -27,6 +27,8 @@ export {
27
27
  type PropDefinition,
28
28
  type ThemeValues,
29
29
  type ComponentAnalyzerOptions,
30
+ type SampleProps,
31
+ type ControlledState,
30
32
  } from './analyzer';
31
33
 
32
34
  // Vite Plugin (also available via @idealyst/tooling/vite)