juxscript 1.0.0 ā 1.0.1
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/bin/cli.js +53 -27
- package/lib/components/docs-data.json +1 -1
- package/machinery/doc-generator.js +12 -36
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -10,11 +10,36 @@ import { fileURLToPath } from 'url';
|
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
11
|
const __dirname = path.dirname(__filename);
|
|
12
12
|
|
|
13
|
+
// CLEAR PATH CONTRACT
|
|
14
|
+
const PATHS = {
|
|
15
|
+
// Where jux package is installed (in node_modules/juxscript or local dev)
|
|
16
|
+
packageRoot: path.resolve(__dirname, '..'),
|
|
17
|
+
|
|
18
|
+
// Where the user's project is (where they run `npx jux`)
|
|
19
|
+
projectRoot: process.cwd(),
|
|
20
|
+
|
|
21
|
+
// Where jux lib files are (components, layouts, etc.)
|
|
22
|
+
get juxLib() {
|
|
23
|
+
return path.join(this.packageRoot, 'lib');
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// Where user's dist output goes
|
|
27
|
+
get projectDist() {
|
|
28
|
+
return path.join(this.projectRoot, 'dist');
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
console.log('š JUX Paths:');
|
|
33
|
+
console.log(` Package: ${PATHS.packageRoot}`);
|
|
34
|
+
console.log(` Project: ${PATHS.projectRoot}`);
|
|
35
|
+
console.log(` Lib: ${PATHS.juxLib}`);
|
|
36
|
+
console.log(` Dist: ${PATHS.projectDist}\n`);
|
|
37
|
+
|
|
13
38
|
const command = process.argv[2];
|
|
14
|
-
const projectRoot = process.cwd();
|
|
15
|
-
const distDir = path.join(projectRoot, 'dist');
|
|
16
39
|
|
|
17
40
|
function findJuxFiles(dir, fileList = []) {
|
|
41
|
+
if (!fs.existsSync(dir)) return fileList;
|
|
42
|
+
|
|
18
43
|
const files = fs.readdirSync(dir);
|
|
19
44
|
|
|
20
45
|
files.forEach(file => {
|
|
@@ -34,7 +59,7 @@ function findJuxFiles(dir, fileList = []) {
|
|
|
34
59
|
}
|
|
35
60
|
|
|
36
61
|
async function loadConfig() {
|
|
37
|
-
const configPath = path.join(projectRoot, 'jux.config.js');
|
|
62
|
+
const configPath = path.join(PATHS.projectRoot, 'jux.config.js');
|
|
38
63
|
|
|
39
64
|
if (fs.existsSync(configPath)) {
|
|
40
65
|
try {
|
|
@@ -53,41 +78,45 @@ async function buildProject(isServe = false) {
|
|
|
53
78
|
console.log('šØ Building JUX project...\n');
|
|
54
79
|
|
|
55
80
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
81
|
+
// Clean and create dist
|
|
82
|
+
if (fs.existsSync(PATHS.projectDist)) {
|
|
83
|
+
fs.rmSync(PATHS.projectDist, { recursive: true, force: true });
|
|
58
84
|
}
|
|
59
|
-
fs.mkdirSync(
|
|
85
|
+
fs.mkdirSync(PATHS.projectDist, { recursive: true });
|
|
60
86
|
|
|
61
|
-
// Step 1: Generate documentation
|
|
87
|
+
// Step 1: Generate documentation from jux lib
|
|
62
88
|
console.log('š Generating documentation...');
|
|
63
89
|
try {
|
|
64
|
-
await generateDocs(
|
|
90
|
+
await generateDocs(PATHS.juxLib);
|
|
65
91
|
console.log('ā
Documentation generated\n');
|
|
66
92
|
} catch (error) {
|
|
67
93
|
console.warn('ā ļø Failed to generate docs:', error.message);
|
|
68
94
|
}
|
|
69
95
|
|
|
70
|
-
// Step 2: Copy lib
|
|
71
|
-
await copyLibToOutput(
|
|
96
|
+
// Step 2: Copy jux lib to project dist
|
|
97
|
+
await copyLibToOutput(PATHS.juxLib, PATHS.projectDist);
|
|
72
98
|
|
|
73
|
-
// Step 3: Copy project assets (CSS, JS)
|
|
74
|
-
await copyProjectAssets(projectRoot,
|
|
99
|
+
// Step 3: Copy project assets (CSS, JS)
|
|
100
|
+
await copyProjectAssets(PATHS.projectRoot, PATHS.projectDist);
|
|
75
101
|
|
|
76
|
-
// Step 4:
|
|
77
|
-
const projectJuxFiles = findJuxFiles(projectRoot);
|
|
102
|
+
// Step 4: Compile project .jux files
|
|
103
|
+
const projectJuxFiles = findJuxFiles(PATHS.projectRoot);
|
|
78
104
|
console.log(`Found ${projectJuxFiles.length} project .jux file(s)\n`);
|
|
79
105
|
|
|
80
106
|
for (const file of projectJuxFiles) {
|
|
81
107
|
try {
|
|
82
|
-
await compileJuxFile(file, {
|
|
108
|
+
await compileJuxFile(file, {
|
|
109
|
+
distDir: PATHS.projectDist,
|
|
110
|
+
projectRoot: PATHS.projectRoot,
|
|
111
|
+
isServe
|
|
112
|
+
});
|
|
83
113
|
} catch (err) {
|
|
84
114
|
console.error(`Error compiling ${file}:`, err.message);
|
|
85
115
|
}
|
|
86
116
|
}
|
|
87
117
|
|
|
88
|
-
// Step 5:
|
|
89
|
-
const
|
|
90
|
-
const layoutsDir = path.join(libRoot, 'layouts');
|
|
118
|
+
// Step 5: Compile vendor layouts
|
|
119
|
+
const layoutsDir = path.join(PATHS.juxLib, 'layouts');
|
|
91
120
|
|
|
92
121
|
if (fs.existsSync(layoutsDir)) {
|
|
93
122
|
console.log('\nš Compiling vendor layouts...');
|
|
@@ -96,11 +125,11 @@ async function buildProject(isServe = false) {
|
|
|
96
125
|
|
|
97
126
|
for (const file of vendorJuxFiles) {
|
|
98
127
|
try {
|
|
99
|
-
const relPath = path.relative(
|
|
128
|
+
const relPath = path.relative(PATHS.juxLib, file);
|
|
100
129
|
|
|
101
130
|
await compileJuxFile(file, {
|
|
102
|
-
distDir: path.join(
|
|
103
|
-
projectRoot:
|
|
131
|
+
distDir: path.join(PATHS.projectDist, 'lib'),
|
|
132
|
+
projectRoot: PATHS.juxLib,
|
|
104
133
|
isServe
|
|
105
134
|
});
|
|
106
135
|
|
|
@@ -114,6 +143,7 @@ async function buildProject(isServe = false) {
|
|
|
114
143
|
console.log(`\nā
Built ${projectJuxFiles.length} project file(s) + layouts\n`);
|
|
115
144
|
} catch (err) {
|
|
116
145
|
console.error('ā Build error:', err.message);
|
|
146
|
+
console.error(err.stack);
|
|
117
147
|
process.exit(1);
|
|
118
148
|
}
|
|
119
149
|
}
|
|
@@ -121,13 +151,10 @@ async function buildProject(isServe = false) {
|
|
|
121
151
|
(async () => {
|
|
122
152
|
if (command === 'build') {
|
|
123
153
|
await buildProject(false);
|
|
124
|
-
console.log(`ā
Build complete: ${
|
|
154
|
+
console.log(`ā
Build complete: ${PATHS.projectDist}`);
|
|
125
155
|
|
|
126
156
|
} else if (command === 'serve') {
|
|
127
|
-
|
|
128
|
-
await buildProject(true); // isServe = true
|
|
129
|
-
|
|
130
|
-
// Start server with watcher
|
|
157
|
+
await buildProject(true);
|
|
131
158
|
const config = await loadConfig();
|
|
132
159
|
await start(3000, config);
|
|
133
160
|
|
|
@@ -138,7 +165,6 @@ JUX CLI - A JavaScript UX authorship platform
|
|
|
138
165
|
Usage:
|
|
139
166
|
npx jux build Compile all .jux files to HTML/CSS/JS
|
|
140
167
|
npx jux serve [port] Start dev server with hot reload (default: 3000)
|
|
141
|
-
Builds automatically if dist/ doesn't exist
|
|
142
168
|
|
|
143
169
|
Examples:
|
|
144
170
|
npx jux build Build for production
|
|
@@ -4,10 +4,15 @@ import { glob } from 'glob';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Generate documentation from TypeScript component files
|
|
7
|
+
*
|
|
8
|
+
* @param {string} juxLibPath - Absolute path to jux/lib directory
|
|
7
9
|
*/
|
|
8
|
-
export async function generateDocs(
|
|
9
|
-
const
|
|
10
|
-
|
|
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
|
+
}
|
|
11
16
|
|
|
12
17
|
console.log(` Scanning: ${componentsDir}`);
|
|
13
18
|
|
|
@@ -15,7 +20,7 @@ export async function generateDocs(projectRoot) {
|
|
|
15
20
|
const componentFiles = glob.sync('*.ts', {
|
|
16
21
|
cwd: componentsDir,
|
|
17
22
|
absolute: true,
|
|
18
|
-
ignore: ['reactivity.
|
|
23
|
+
ignore: ['reactivity.ts', 'error-handler.ts']
|
|
19
24
|
});
|
|
20
25
|
|
|
21
26
|
console.log(` Found ${componentFiles.length} component files`);
|
|
@@ -62,23 +67,14 @@ function parseComponentFile(content, filePath) {
|
|
|
62
67
|
const fileName = path.basename(filePath, '.ts');
|
|
63
68
|
const className = fileName.charAt(0).toUpperCase() + fileName.slice(1);
|
|
64
69
|
|
|
65
|
-
// Extract category from file content or infer
|
|
66
70
|
const category = inferCategory(className, content);
|
|
67
|
-
|
|
68
|
-
// Extract description from class JSDoc
|
|
69
71
|
const descMatch = content.match(/\/\*\*\s*\n\s*\*\s*([^\n*]+)/);
|
|
70
72
|
const description = descMatch ? descMatch[1].trim() : `${className} component`;
|
|
71
|
-
|
|
72
|
-
// Extract constructor/factory pattern
|
|
73
73
|
const factoryMatch = content.match(/export function\s+\w+\(([^)]*)\)/);
|
|
74
74
|
const constructorSig = factoryMatch
|
|
75
75
|
? `jux.${fileName}(${factoryMatch[1]})`
|
|
76
76
|
: `new ${className}()`;
|
|
77
|
-
|
|
78
|
-
// Extract fluent methods
|
|
79
77
|
const fluentMethods = extractFluentMethods(content, className);
|
|
80
|
-
|
|
81
|
-
// Extract usage example from JSDoc
|
|
82
78
|
const exampleMatch = content.match(/\*\s+Usage:\s*\n\s*\*\s+(.+?)(?:\n|$)/);
|
|
83
79
|
let example = exampleMatch
|
|
84
80
|
? exampleMatch[1].trim()
|
|
@@ -99,28 +95,14 @@ function parseComponentFile(content, filePath) {
|
|
|
99
95
|
*/
|
|
100
96
|
function extractFluentMethods(content, className) {
|
|
101
97
|
const methods = [];
|
|
102
|
-
|
|
103
|
-
// Match method patterns: methodName(params): this
|
|
104
98
|
const methodRegex = /^\s*(\w+)\(([^)]*)\):\s*this\s*\{/gm;
|
|
105
99
|
let match;
|
|
106
100
|
|
|
107
101
|
while ((match = methodRegex.exec(content)) !== null) {
|
|
108
102
|
const methodName = match[1];
|
|
109
103
|
const params = match[2];
|
|
110
|
-
|
|
111
|
-
// Skip private methods and constructor
|
|
112
104
|
if (methodName.startsWith('_') || methodName === 'constructor') continue;
|
|
113
|
-
|
|
114
|
-
// Clean up params
|
|
115
|
-
const cleanParams = params
|
|
116
|
-
.split(',')
|
|
117
|
-
.map(p => {
|
|
118
|
-
const parts = p.trim().split(':');
|
|
119
|
-
return parts[0].trim();
|
|
120
|
-
})
|
|
121
|
-
.filter(p => p)
|
|
122
|
-
.join(', ');
|
|
123
|
-
|
|
105
|
+
const cleanParams = params.split(',').map(p => p.trim().split(':')[0].trim()).filter(p => p).join(', ');
|
|
124
106
|
methods.push({
|
|
125
107
|
name: methodName,
|
|
126
108
|
params: cleanParams ? `(${cleanParams})` : '()',
|
|
@@ -129,7 +111,6 @@ function extractFluentMethods(content, className) {
|
|
|
129
111
|
});
|
|
130
112
|
}
|
|
131
113
|
|
|
132
|
-
// Also look for render methods
|
|
133
114
|
const renderMatch = content.match(/^\s*render\(([^)]*)\):\s*(\w+)/m);
|
|
134
115
|
if (renderMatch && !methods.find(m => m.name === 'render')) {
|
|
135
116
|
methods.push({
|
|
@@ -149,12 +130,7 @@ function extractFluentMethods(content, className) {
|
|
|
149
130
|
function inferCategory(name, content) {
|
|
150
131
|
const dataComponents = ['Table', 'List', 'Chart', 'Data'];
|
|
151
132
|
const coreComponents = ['App', 'Layout', 'Theme', 'Style', 'Script', 'Import'];
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
return 'Data Components';
|
|
155
|
-
}
|
|
156
|
-
if (coreComponents.includes(name)) {
|
|
157
|
-
return 'Core';
|
|
158
|
-
}
|
|
133
|
+
if (dataComponents.some(dc => name.includes(dc))) return 'Data Components';
|
|
134
|
+
if (coreComponents.includes(name)) return 'Core';
|
|
159
135
|
return 'UI Components';
|
|
160
136
|
}
|