juxscript 1.1.2 → 1.1.4

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.
Files changed (67) hide show
  1. package/machinery/build3.js +7 -91
  2. package/machinery/compiler3.js +3 -209
  3. package/machinery/config.js +93 -6
  4. package/machinery/serve.js +255 -0
  5. package/machinery/watcher.js +49 -161
  6. package/package.json +19 -5
  7. package/lib/components/alert.ts +0 -200
  8. package/lib/components/app.ts +0 -247
  9. package/lib/components/badge.ts +0 -101
  10. package/lib/components/base/BaseComponent.ts +0 -421
  11. package/lib/components/base/FormInput.ts +0 -227
  12. package/lib/components/button.ts +0 -178
  13. package/lib/components/card.ts +0 -173
  14. package/lib/components/chart.ts +0 -231
  15. package/lib/components/checkbox.ts +0 -242
  16. package/lib/components/code.ts +0 -123
  17. package/lib/components/container.ts +0 -140
  18. package/lib/components/data.ts +0 -135
  19. package/lib/components/datepicker.ts +0 -234
  20. package/lib/components/dialog.ts +0 -172
  21. package/lib/components/divider.ts +0 -100
  22. package/lib/components/dropdown.ts +0 -186
  23. package/lib/components/element.ts +0 -267
  24. package/lib/components/fileupload.ts +0 -309
  25. package/lib/components/grid.ts +0 -291
  26. package/lib/components/guard.ts +0 -92
  27. package/lib/components/heading.ts +0 -96
  28. package/lib/components/helpers.ts +0 -41
  29. package/lib/components/hero.ts +0 -224
  30. package/lib/components/icon.ts +0 -178
  31. package/lib/components/icons.ts +0 -464
  32. package/lib/components/include.ts +0 -410
  33. package/lib/components/input.ts +0 -457
  34. package/lib/components/list.ts +0 -419
  35. package/lib/components/loading.ts +0 -100
  36. package/lib/components/menu.ts +0 -275
  37. package/lib/components/modal.ts +0 -284
  38. package/lib/components/nav.ts +0 -257
  39. package/lib/components/paragraph.ts +0 -97
  40. package/lib/components/progress.ts +0 -159
  41. package/lib/components/radio.ts +0 -278
  42. package/lib/components/req.ts +0 -303
  43. package/lib/components/script.ts +0 -41
  44. package/lib/components/select.ts +0 -252
  45. package/lib/components/sidebar.ts +0 -275
  46. package/lib/components/style.ts +0 -41
  47. package/lib/components/switch.ts +0 -246
  48. package/lib/components/table.ts +0 -1249
  49. package/lib/components/tabs.ts +0 -250
  50. package/lib/components/theme-toggle.ts +0 -293
  51. package/lib/components/tooltip.ts +0 -144
  52. package/lib/components/view.ts +0 -190
  53. package/lib/components/write.ts +0 -272
  54. package/lib/layouts/default.css +0 -260
  55. package/lib/layouts/figma.css +0 -334
  56. package/lib/reactivity/state.ts +0 -78
  57. package/lib/utils/fetch.ts +0 -553
  58. package/machinery/ast.js +0 -347
  59. package/machinery/build.js +0 -466
  60. package/machinery/bundleAssets.js +0 -0
  61. package/machinery/bundleJux.js +0 -0
  62. package/machinery/bundleVendors.js +0 -0
  63. package/machinery/doc-generator.js +0 -136
  64. package/machinery/imports.js +0 -155
  65. package/machinery/server.js +0 -166
  66. package/machinery/ts-shim.js +0 -46
  67. package/machinery/validators/file-validator.js +0 -123
@@ -1,466 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import esbuild from 'esbuild';
4
- import {
5
- bundleJuxFilesToRouter,
6
- generateRouterIndex
7
- } from './compiler.js';
8
-
9
- /**
10
- * Generate import map script tag
11
- */
12
- function generateImportMapScript() {
13
- return `<script type="importmap">
14
- {
15
- "imports": {
16
- "juxscript": "./lib/jux.js",
17
- "juxscript/": "./lib/",
18
- "juxscript/reactivity": "./lib/reactivity/state.js",
19
- "juxscript/presets/": "./presets/",
20
- "juxscript/components/": "./lib/components/"
21
- }
22
- }
23
- </script>`;
24
- }
25
-
26
- /**
27
- * Compile a .jux file to .js and .html
28
- *
29
- * @param {string} juxFilePath - Path to the .jux file
30
- * @param {Object} options - Compilation options
31
- * @param {string} options.distDir - Output directory
32
- * @param {string} options.projectRoot - Project root directory
33
- * @param {boolean} options.isServe - Whether serving for development
34
- * @returns {Promise<{jsOutputPath: string, htmlOutputPath: string}>}
35
- */
36
- export async function compileJuxFile(juxFilePath, options = {}) {
37
- const { distDir, projectRoot, isServe = false } = options;
38
-
39
- const relativePath = path.relative(projectRoot, juxFilePath);
40
- const parsedPath = path.parse(relativePath);
41
-
42
- // Output paths
43
- const outputDir = path.join(distDir, parsedPath.dir);
44
- const jsOutputPath = path.join(outputDir, `${parsedPath.name}.js`);
45
- const htmlOutputPath = path.join(outputDir, `${parsedPath.name}.html`);
46
-
47
- // Ensure output directory exists
48
- if (!fs.existsSync(outputDir)) {
49
- fs.mkdirSync(outputDir, { recursive: true });
50
- }
51
-
52
- console.log(`📝 Compiling: ${relativePath}`);
53
-
54
- // Read the .jux file
55
- const juxContent = fs.readFileSync(juxFilePath, 'utf-8');
56
-
57
- // Calculate depth for relative paths
58
- const depth = parsedPath.dir.split(path.sep).filter(p => p).length;
59
- const libPath = depth === 0 ? './lib/jux.js' : '../'.repeat(depth) + 'lib/jux.js';
60
-
61
- // Transform imports
62
- let transformedContent = juxContent;
63
-
64
- // Replace common import patterns with calculated path
65
- transformedContent = transformedContent.replace(
66
- /from\s+['"]\.\.?\/lib\/jux\.js['"]/g,
67
- `from '${libPath}'`
68
- );
69
-
70
- // Only inject import if:
71
- // 1. File is not empty (ignoring whitespace and comments)
72
- // 2. File uses 'jux.' but has no import statement
73
- const contentWithoutComments = transformedContent
74
- .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
75
- .replace(/\/\/.*/g, '') // Remove line comments
76
- .trim();
77
-
78
- const hasContent = contentWithoutComments.length > 0;
79
- const usesJux = /\bjux\./g.test(contentWithoutComments);
80
- const hasImport = /import\s+.*from/.test(transformedContent);
81
-
82
- if (hasContent && usesJux && !hasImport) {
83
- transformedContent = `import { jux } from '${libPath}';\n\n${transformedContent}`;
84
- }
85
-
86
- // Write the transformed JS
87
- fs.writeFileSync(jsOutputPath, transformedContent);
88
-
89
- console.log(` ✓ JS: ${path.relative(projectRoot, jsOutputPath)}`);
90
-
91
- // Generate HTML with import map and correct script path
92
- const scriptPath = `./${parsedPath.name}.js`;
93
- const importMapScript = generateImportMapScript();
94
-
95
- const html = `<!DOCTYPE html>
96
- <html lang="en">
97
- <head>
98
- <meta charset="UTF-8">
99
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
100
- <title>${parsedPath.name}</title>
101
- </head>
102
- <body data-theme="">
103
- <!-- App container -->
104
- <div id="app" data-jux-page="${parsedPath.name}"></div>
105
-
106
- ${importMapScript}
107
- <script type="module" src="${scriptPath}"></script>
108
- ${isServe ? `
109
- <!-- Hot reload -->
110
- <script type="module">
111
- const ws = new WebSocket('ws://localhost:3001');
112
- ws.onmessage = (msg) => {
113
- const data = JSON.parse(msg.data);
114
- if (data.type === 'reload') {
115
- console.log('🔄 Reloading page...');
116
- location.reload();
117
- }
118
- };
119
- ws.onerror = () => console.warn('⚠️ WebSocket connection failed');
120
- </script>
121
- ` : ''}
122
- </body>
123
- </html>`;
124
-
125
- fs.writeFileSync(htmlOutputPath, html);
126
- console.log(` ✓ HTML: ${path.relative(projectRoot, htmlOutputPath)}`);
127
-
128
- return { jsOutputPath, htmlOutputPath };
129
- }
130
-
131
- /**
132
- * Copy and build the JUX library from TypeScript to JavaScript
133
- *
134
- * @param {string} projectRoot - Root directory containing lib/
135
- * @param {string} distDir - Destination directory for built files
136
- */
137
- export async function copyLibToOutput(projectRoot, distDir) {
138
- // Simplified lib path resolution
139
- const libSrc = path.resolve(projectRoot, '../lib');
140
-
141
- if (!fs.existsSync(libSrc)) {
142
- throw new Error(`lib/ directory not found at ${libSrc}`);
143
- }
144
-
145
- const libDest = path.join(distDir, 'lib');
146
-
147
- console.log('📦 Building TypeScript library...');
148
- console.log(` From: ${libSrc}`);
149
- console.log(` To: ${libDest}`);
150
-
151
- if (fs.existsSync(libDest)) {
152
- fs.rmSync(libDest, { recursive: true });
153
- }
154
-
155
- fs.mkdirSync(libDest, { recursive: true });
156
-
157
- // Find all TypeScript entry points
158
- const tsFiles = findFiles(libSrc, '.ts');
159
-
160
- if (tsFiles.length === 0) {
161
- console.warn('⚠️ No TypeScript files found in lib/');
162
- return;
163
- }
164
-
165
- console.log(` Found ${tsFiles.length} TypeScript files`);
166
-
167
- // Build all TypeScript files with esbuild
168
- try {
169
- await esbuild.build({
170
- entryPoints: tsFiles,
171
- bundle: false,
172
- format: 'esm',
173
- outdir: libDest,
174
- outbase: libSrc,
175
- platform: 'browser',
176
- target: 'es2020',
177
- loader: {
178
- '.ts': 'ts'
179
- },
180
- logLevel: 'warning'
181
- });
182
-
183
- console.log(' ✓ TypeScript compiled to JavaScript');
184
-
185
- // Copy non-TS files (CSS, HTML, etc.)
186
- console.log(' Copying lib assets...');
187
- copyNonTsFiles(libSrc, libDest);
188
- console.log(' ✓ Lib assets copied');
189
-
190
- } catch (err) {
191
- console.error('❌ Failed to build TypeScript:', err.message);
192
- throw err;
193
- }
194
-
195
- console.log('✅ Library ready\n');
196
- }
197
-
198
- /**
199
- * Copy project assets (CSS, JS, images) from jux/ to dist/
200
- *
201
- * @param {string} projectRoot - Source directory (jux/)
202
- * @param {string} distDir - Destination directory (jux-dist/)
203
- */
204
- export async function copyProjectAssets(projectRoot, distDir) {
205
- console.log('📦 Copying project assets...');
206
-
207
- // Find all CSS and JS files in project root (excluding node_modules, dist, .git)
208
- const allFiles = [];
209
- findProjectFiles(projectRoot, ['.css', '.js'], allFiles, projectRoot);
210
-
211
- console.log(` Found ${allFiles.length} asset file(s)`);
212
-
213
- for (const srcPath of allFiles) {
214
- const relativePath = path.relative(projectRoot, srcPath);
215
- const destPath = path.join(distDir, relativePath);
216
- const destDir = path.dirname(destPath);
217
-
218
- // Create destination directory if needed
219
- if (!fs.existsSync(destDir)) {
220
- fs.mkdirSync(destDir, { recursive: true });
221
- }
222
-
223
- // Copy file
224
- fs.copyFileSync(srcPath, destPath);
225
- console.log(` ✓ ${relativePath}`);
226
- }
227
-
228
- console.log('✅ Project assets copied\n');
229
- }
230
-
231
- /**
232
- * Transpile TypeScript files from jux/ to jux-dist/, preserving folder structure
233
- *
234
- * @param {string} srcDir - Source directory (jux/)
235
- * @param {string} destDir - Destination directory (jux-dist/)
236
- * @example
237
- * // jux/samples/mypage.ts -> jux-dist/samples/mypage.js
238
- * await transpileProjectTypeScript('jux/', 'jux-dist/');
239
- */
240
- export async function transpileProjectTypeScript(srcDir, destDir) {
241
- console.log('🔷 Transpiling TypeScript files...');
242
-
243
- // Find all TypeScript files in the project
244
- const tsFiles = findFiles(srcDir, '.ts');
245
-
246
- if (tsFiles.length === 0) {
247
- console.log(' No TypeScript files found in project');
248
- return;
249
- }
250
-
251
- console.log(` Found ${tsFiles.length} TypeScript file(s)`);
252
-
253
- try {
254
- // Build all TypeScript files with esbuild
255
- await esbuild.build({
256
- entryPoints: tsFiles,
257
- bundle: false,
258
- format: 'esm',
259
- outdir: destDir,
260
- outbase: srcDir,
261
- platform: 'browser',
262
- target: 'es2020',
263
- loader: {
264
- '.ts': 'ts'
265
- },
266
- logLevel: 'warning'
267
- });
268
-
269
- // Log each transpiled file
270
- tsFiles.forEach(tsFile => {
271
- const relativePath = path.relative(srcDir, tsFile);
272
- const jsPath = relativePath.replace(/\.ts$/, '.js');
273
- console.log(` ✓ ${relativePath} → ${jsPath}`);
274
- });
275
-
276
- console.log('✅ TypeScript transpiled\n');
277
-
278
- } catch (err) {
279
- console.error('❌ Failed to transpile TypeScript:', err.message);
280
- throw err;
281
- }
282
- }
283
-
284
- /**
285
- * Copy presets folder from lib to dist
286
- *
287
- * @param {string} packageRoot - Source package root directory
288
- * @param {string} distDir - Destination directory
289
- */
290
- export async function copyPresetsToOutput(packageRoot, distDir) {
291
- console.log('📦 Copying presets...');
292
-
293
- const presetsSrc = path.join(packageRoot, 'presets');
294
- const presetsDest = path.join(distDir, 'presets');
295
-
296
- if (!fs.existsSync(presetsSrc)) {
297
- console.log(' No presets directory found');
298
- return;
299
- }
300
-
301
- if (fs.existsSync(presetsDest)) {
302
- fs.rmSync(presetsDest, { recursive: true });
303
- }
304
-
305
- fs.mkdirSync(presetsDest, { recursive: true });
306
-
307
- // Copy all files in presets directory
308
- const files = fs.readdirSync(presetsSrc);
309
- let copiedCount = 0;
310
-
311
- for (const file of files) {
312
- const srcPath = path.join(presetsSrc, file);
313
- const destPath = path.join(presetsDest, file);
314
-
315
- if (fs.statSync(srcPath).isFile()) {
316
- fs.copyFileSync(srcPath, destPath);
317
- console.log(` ✓ ${file}`);
318
- copiedCount++;
319
- }
320
- }
321
-
322
- console.log(`✅ Copied ${copiedCount} preset file(s)\n`);
323
- }
324
-
325
- /**
326
- * Recursively find files with a specific extension
327
- *
328
- * @param {string} dir - Directory to search
329
- * @param {string} extension - File extension (e.g., '.ts')
330
- * @param {string[]} fileList - Accumulator for found files
331
- * @returns {string[]} Array of file paths
332
- */
333
- async function findFiles(dir, extension, fileList = []) {
334
- const fs = await import('fs');
335
- const path = await import('path');
336
- const files = fs.readdirSync(dir);
337
-
338
- files.forEach(file => {
339
- const filePath = path.join(dir, file);
340
- const stat = fs.statSync(filePath);
341
-
342
- if (stat.isDirectory() && !['node_modules', 'jux-dist', '.git', 'lib'].includes(file)) {
343
- findFiles(filePath, extension, fileList);
344
- } else if (file.endsWith(extension)) {
345
- fileList.push(filePath);
346
- }
347
- });
348
-
349
- return fileList;
350
- }
351
-
352
- /**
353
- * Copy non-TypeScript files (CSS, JSON, JS, SVG, etc.)
354
- *
355
- * @param {string} src - Source directory
356
- * @param {string} dest - Destination directory
357
- */
358
- function copyNonTsFiles(src, dest) {
359
- const entries = fs.readdirSync(src, { withFileTypes: true });
360
-
361
- for (const entry of entries) {
362
- const srcPath = path.join(src, entry.name);
363
- const destPath = path.join(dest, entry.name);
364
-
365
- if (entry.isDirectory()) {
366
- if (!fs.existsSync(destPath)) {
367
- fs.mkdirSync(destPath, { recursive: true });
368
- }
369
- copyNonTsFiles(srcPath, destPath);
370
- } else if (entry.isFile()) {
371
- const ext = path.extname(entry.name);
372
- // Copy CSS, JSON, SVG, and JS files (but not .ts files)
373
- if (['.css', '.json', '.js', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.webp'].includes(ext)) {
374
- fs.copyFileSync(srcPath, destPath);
375
- }
376
- }
377
- }
378
- }
379
-
380
- /**
381
- * Find project files with specific extensions, excluding certain directories
382
- *
383
- * @param {string} dir - Directory to search
384
- * @param {string[]} extensions - File extensions to find
385
- * @param {string[]} fileList - Accumulator for found files
386
- * @param {string} rootDir - Root directory for relative paths
387
- * @param {string[]} excludeDirs - Directories to exclude
388
- * @returns {string[]} Array of file paths
389
- */
390
- function findProjectFiles(dir, extensions, fileList = [], rootDir = dir, excludeDirs = ['node_modules', 'jux-dist', '.git', 'lib']) {
391
- if (!fs.existsSync(dir)) return fileList;
392
-
393
- const entries = fs.readdirSync(dir, { withFileTypes: true });
394
-
395
- for (const entry of entries) {
396
- const fullPath = path.join(dir, entry.name);
397
-
398
- if (entry.isDirectory()) {
399
- // Skip excluded directories
400
- if (excludeDirs.includes(entry.name)) {
401
- continue;
402
- }
403
- findProjectFiles(fullPath, extensions, fileList, rootDir, excludeDirs);
404
- } else {
405
- // Check if file has one of the desired extensions
406
- const hasExtension = extensions.some(ext => entry.name.endsWith(ext));
407
- if (hasExtension) {
408
- fileList.push(fullPath);
409
- }
410
- }
411
- }
412
-
413
- return fileList;
414
- }
415
-
416
- /**
417
- * Build a project
418
- *
419
- * @param {string} projectRoot - Root directory containing lib/
420
- * @param {string} distDir - Destination directory for built files
421
- * @param {Object} options - Compilation options
422
- * @param {string} options.routePrefix - Route prefix
423
- * @returns {Promise<{jsOutputPath: string, htmlOutputPath: string}>}
424
- */
425
- export async function buildProject(options = {}) {
426
- const { projectRoot, distDir } = options;
427
-
428
- // Ensure project root and dist directory exist
429
- if (!fs.existsSync(projectRoot)) {
430
- throw new Error(`Project root directory not found: ${projectRoot}`);
431
- }
432
- if (!fs.existsSync(distDir)) {
433
- fs.mkdirSync(distDir, { recursive: true });
434
- }
435
-
436
- // Option to use router mode
437
- if (options.router) {
438
- console.log('🔀 Building in router mode...\n');
439
-
440
- await bundleJuxFilesToRouter(projectRoot, distDir, {
441
- routePrefix: options.routePrefix || ''
442
- });
443
-
444
- // Get route information for index generation
445
- const juxFiles = findFiles(projectRoot, '.jux');
446
- const routes = juxFiles.map(juxFile => {
447
- const relativePath = path.relative(projectRoot, juxFile);
448
- const parsedPath = path.parse(relativePath);
449
- const functionName = parsedPath.dir
450
- ? `${parsedPath.dir.replace(/\//g, '_')}_${parsedPath.name}`
451
- : parsedPath.name;
452
- const routePath = (options.routePrefix || '') + '/' +
453
- (parsedPath.dir ? `${parsedPath.dir}/` : '') + parsedPath.name;
454
- return {
455
- path: routePath.replace(/\/+/g, '/'),
456
- functionName
457
- };
458
- });
459
-
460
- generateRouterIndex(distDir, routes);
461
- } else {
462
- // ...existing individual page compilation...
463
- }
464
-
465
- // ...existing code...
466
- }
File without changes
File without changes
File without changes
@@ -1,136 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { glob } from 'glob';
4
-
5
- /**
6
- * Generate documentation from TypeScript component files
7
- *
8
- * @param {string} juxLibPath - Absolute path to jux/lib directory
9
- */
10
- export async function generateDocs(juxLibPath) {
11
- const componentsDir = path.join(juxLibPath, 'components');
12
-
13
- if (!fs.existsSync(componentsDir)) {
14
- throw new Error(`Components directory not found: ${componentsDir}`);
15
- }
16
-
17
- console.log(` Scanning: ${componentsDir}`);
18
-
19
- // Find all component TypeScript files
20
- const componentFiles = glob.sync('*.ts', {
21
- cwd: componentsDir,
22
- absolute: true,
23
- ignore: ['reactivity.ts', 'error-handler.ts']
24
- });
25
-
26
- console.log(` Found ${componentFiles.length} component files`);
27
-
28
- const components = [];
29
-
30
- for (const filePath of componentFiles) {
31
- const content = fs.readFileSync(filePath, 'utf-8');
32
- const componentDoc = parseComponentFile(content, filePath);
33
-
34
- if (componentDoc) {
35
- components.push(componentDoc);
36
- console.log(` ✓ ${componentDoc.name}`);
37
- }
38
- }
39
-
40
- // Sort by category and name
41
- components.sort((a, b) => {
42
- const categoryCompare = (a.category || 'ZZZ').localeCompare(b.category || 'ZZZ');
43
- if (categoryCompare !== 0) return categoryCompare;
44
- return a.name.localeCompare(b.name);
45
- });
46
-
47
- // Generate the docs data
48
- const docsData = {
49
- components,
50
- version: '1.0.0',
51
- lastUpdated: new Date().toISOString()
52
- };
53
-
54
- // Write to JSON file
55
- const outputPath = path.join(componentsDir, 'docs-data.json');
56
- fs.writeFileSync(outputPath, JSON.stringify(docsData, null, 2));
57
-
58
- console.log(` Generated: ${outputPath}`);
59
-
60
- return docsData;
61
- }
62
-
63
- /**
64
- * Parse a single component file
65
- */
66
- function parseComponentFile(content, filePath) {
67
- const fileName = path.basename(filePath, '.ts');
68
- const className = fileName.charAt(0).toUpperCase() + fileName.slice(1);
69
-
70
- const category = inferCategory(className, content);
71
- const descMatch = content.match(/\/\*\*\s*\n\s*\*\s*([^\n*]+)/);
72
- const description = descMatch ? descMatch[1].trim() : `${className} component`;
73
- const factoryMatch = content.match(/export function\s+\w+\(([^)]*)\)/);
74
- const constructorSig = factoryMatch
75
- ? `jux.${fileName}(${factoryMatch[1]})`
76
- : `new ${className}()`;
77
- const fluentMethods = extractFluentMethods(content, className);
78
- const exampleMatch = content.match(/\*\s+Usage:\s*\n\s*\*\s+(.+?)(?:\n|$)/);
79
- let example = exampleMatch
80
- ? exampleMatch[1].trim()
81
- : `jux.${fileName}('id').render()`;
82
-
83
- return {
84
- name: className,
85
- category,
86
- description,
87
- constructor: constructorSig,
88
- fluentMethods,
89
- example
90
- };
91
- }
92
-
93
- /**
94
- * Extract fluent API methods from class
95
- */
96
- function extractFluentMethods(content, className) {
97
- const methods = [];
98
- const methodRegex = /^\s*(\w+)\(([^)]*)\):\s*this\s*\{/gm;
99
- let match;
100
-
101
- while ((match = methodRegex.exec(content)) !== null) {
102
- const methodName = match[1];
103
- const params = match[2];
104
- if (methodName.startsWith('_') || methodName === 'constructor') continue;
105
- const cleanParams = params.split(',').map(p => p.trim().split(':')[0].trim()).filter(p => p).join(', ');
106
- methods.push({
107
- name: methodName,
108
- params: cleanParams ? `(${cleanParams})` : '()',
109
- returns: 'this',
110
- description: `Set ${methodName}`
111
- });
112
- }
113
-
114
- const renderMatch = content.match(/^\s*render\(([^)]*)\):\s*(\w+)/m);
115
- if (renderMatch && !methods.find(m => m.name === 'render')) {
116
- methods.push({
117
- name: 'render',
118
- params: renderMatch[1] ? `(${renderMatch[1]})` : '()',
119
- returns: renderMatch[2],
120
- description: 'Render component to DOM'
121
- });
122
- }
123
-
124
- return methods;
125
- }
126
-
127
- /**
128
- * Infer category from component name or content
129
- */
130
- function inferCategory(name, content) {
131
- const dataComponents = ['Table', 'List', 'Chart', 'Data'];
132
- const coreComponents = ['App', 'Layout', 'Theme', 'Style', 'Script', 'Import'];
133
- if (dataComponents.some(dc => name.includes(dc))) return 'Data Components';
134
- if (coreComponents.includes(name)) return 'Core';
135
- return 'UI Components';
136
- }