juxscript 1.0.1 ā 1.0.3
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/README.md +13 -1
- package/bin/cli.js +132 -31
- package/lib/components/docs-data.json +1 -1
- package/lib/templates/index.juxt +33 -0
- package/machinery/server.js +12 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,19 @@ Have you ever considered the energy requirements to ship chunks of HTML markup l
|
|
|
21
21
|
|
|
22
22
|
## GETTING STARTED
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
```bash
|
|
25
|
+
# New project
|
|
26
|
+
mkdir my-project
|
|
27
|
+
cd my-project
|
|
28
|
+
npm init -y
|
|
29
|
+
npm install juxscript
|
|
30
|
+
|
|
31
|
+
# Initialize (creates jux/ directory)
|
|
32
|
+
npx jux init
|
|
33
|
+
|
|
34
|
+
# Builds jux-dist and serves index.jux
|
|
35
|
+
npx jux serve
|
|
36
|
+
```
|
|
25
37
|
> install
|
|
26
38
|
`npm i juxscript`
|
|
27
39
|
|
package/bin/cli.js
CHANGED
|
@@ -10,30 +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
|
|
13
|
+
// CLEAR PATH CONTRACT - CONVENTIONS
|
|
14
14
|
const PATHS = {
|
|
15
15
|
// Where jux package is installed (in node_modules/juxscript or local dev)
|
|
16
16
|
packageRoot: path.resolve(__dirname, '..'),
|
|
17
17
|
|
|
18
|
-
// Where the user's project is (where they run `npx jux`)
|
|
18
|
+
// Where the user's project root is (where they run `npx jux`)
|
|
19
19
|
projectRoot: process.cwd(),
|
|
20
20
|
|
|
21
|
+
// Where user's .jux source files live (CONVENTION: ./jux/)
|
|
22
|
+
get juxSource() {
|
|
23
|
+
return path.join(this.projectRoot, 'jux');
|
|
24
|
+
},
|
|
25
|
+
|
|
21
26
|
// Where jux lib files are (components, layouts, etc.)
|
|
22
27
|
get juxLib() {
|
|
23
28
|
return path.join(this.packageRoot, 'lib');
|
|
24
29
|
},
|
|
25
30
|
|
|
26
|
-
// Where
|
|
27
|
-
get
|
|
28
|
-
return path.join(this.projectRoot, 'dist');
|
|
31
|
+
// Where frontend build output goes (CONVENTION: ./jux-dist/)
|
|
32
|
+
get frontendDist() {
|
|
33
|
+
return path.join(this.projectRoot, 'jux-dist');
|
|
29
34
|
}
|
|
30
35
|
};
|
|
31
36
|
|
|
32
37
|
console.log('š JUX Paths:');
|
|
33
|
-
console.log(` Package:
|
|
34
|
-
console.log(` Project:
|
|
35
|
-
console.log(`
|
|
36
|
-
console.log(`
|
|
38
|
+
console.log(` Package: ${PATHS.packageRoot}`);
|
|
39
|
+
console.log(` Project: ${PATHS.projectRoot}`);
|
|
40
|
+
console.log(` Source: ${PATHS.juxSource}`);
|
|
41
|
+
console.log(` Output: ${PATHS.frontendDist}`);
|
|
42
|
+
console.log(` Lib: ${PATHS.juxLib}\n`);
|
|
37
43
|
|
|
38
44
|
const command = process.argv[2];
|
|
39
45
|
|
|
@@ -47,7 +53,7 @@ function findJuxFiles(dir, fileList = []) {
|
|
|
47
53
|
const stat = fs.statSync(filePath);
|
|
48
54
|
|
|
49
55
|
if (stat.isDirectory()) {
|
|
50
|
-
if (file !== 'node_modules' && file !== 'dist' && file !== '.git') {
|
|
56
|
+
if (file !== 'node_modules' && file !== 'jux-dist' && file !== '.git' && file !== 'server') {
|
|
51
57
|
findJuxFiles(filePath, fileList);
|
|
52
58
|
}
|
|
53
59
|
} else if (file.endsWith('.jux')) {
|
|
@@ -75,14 +81,21 @@ async function loadConfig() {
|
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
async function buildProject(isServe = false) {
|
|
78
|
-
console.log('šØ Building JUX
|
|
84
|
+
console.log('šØ Building JUX frontend...\n');
|
|
79
85
|
|
|
80
86
|
try {
|
|
81
|
-
//
|
|
82
|
-
if (fs.existsSync(PATHS.
|
|
83
|
-
|
|
87
|
+
// Verify jux source directory exists
|
|
88
|
+
if (!fs.existsSync(PATHS.juxSource)) {
|
|
89
|
+
console.error(`ā Source directory not found: ${PATHS.juxSource}`);
|
|
90
|
+
console.error(` Please create a 'jux/' directory with your .jux files`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Clean and create frontend dist
|
|
95
|
+
if (fs.existsSync(PATHS.frontendDist)) {
|
|
96
|
+
fs.rmSync(PATHS.frontendDist, { recursive: true, force: true });
|
|
84
97
|
}
|
|
85
|
-
fs.mkdirSync(PATHS.
|
|
98
|
+
fs.mkdirSync(PATHS.frontendDist, { recursive: true });
|
|
86
99
|
|
|
87
100
|
// Step 1: Generate documentation from jux lib
|
|
88
101
|
console.log('š Generating documentation...');
|
|
@@ -93,21 +106,21 @@ async function buildProject(isServe = false) {
|
|
|
93
106
|
console.warn('ā ļø Failed to generate docs:', error.message);
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
// Step 2: Copy jux lib to
|
|
97
|
-
await copyLibToOutput(PATHS.juxLib, PATHS.
|
|
109
|
+
// Step 2: Copy jux lib to frontend dist
|
|
110
|
+
await copyLibToOutput(PATHS.juxLib, PATHS.frontendDist);
|
|
98
111
|
|
|
99
|
-
// Step 3: Copy project assets (CSS, JS)
|
|
100
|
-
await copyProjectAssets(PATHS.
|
|
112
|
+
// Step 3: Copy project assets from jux/ (CSS, JS, images)
|
|
113
|
+
await copyProjectAssets(PATHS.juxSource, PATHS.frontendDist);
|
|
101
114
|
|
|
102
|
-
// Step 4: Compile
|
|
103
|
-
const projectJuxFiles = findJuxFiles(PATHS.
|
|
104
|
-
console.log(`Found ${projectJuxFiles.length}
|
|
115
|
+
// Step 4: Compile .jux files from jux/ directory ONLY
|
|
116
|
+
const projectJuxFiles = findJuxFiles(PATHS.juxSource);
|
|
117
|
+
console.log(`Found ${projectJuxFiles.length} .jux file(s) in /jux\n`);
|
|
105
118
|
|
|
106
119
|
for (const file of projectJuxFiles) {
|
|
107
120
|
try {
|
|
108
121
|
await compileJuxFile(file, {
|
|
109
|
-
distDir: PATHS.
|
|
110
|
-
projectRoot: PATHS.
|
|
122
|
+
distDir: PATHS.frontendDist,
|
|
123
|
+
projectRoot: PATHS.juxSource,
|
|
111
124
|
isServe
|
|
112
125
|
});
|
|
113
126
|
} catch (err) {
|
|
@@ -128,7 +141,7 @@ async function buildProject(isServe = false) {
|
|
|
128
141
|
const relPath = path.relative(PATHS.juxLib, file);
|
|
129
142
|
|
|
130
143
|
await compileJuxFile(file, {
|
|
131
|
-
distDir: path.join(PATHS.
|
|
144
|
+
distDir: path.join(PATHS.frontendDist, 'lib'),
|
|
132
145
|
projectRoot: PATHS.juxLib,
|
|
133
146
|
isServe
|
|
134
147
|
});
|
|
@@ -140,7 +153,16 @@ async function buildProject(isServe = false) {
|
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
155
|
|
|
143
|
-
console.log(`\nā
Built ${projectJuxFiles.length}
|
|
156
|
+
console.log(`\nā
Built ${projectJuxFiles.length} file(s) ā ${PATHS.frontendDist}\n`);
|
|
157
|
+
|
|
158
|
+
// Show backend integration examples
|
|
159
|
+
console.log('š¦ Serve from your backend:');
|
|
160
|
+
console.log(` Express: app.use(express.static('jux-dist'))`);
|
|
161
|
+
console.log(` Flask: app = Flask(__name__, static_folder='jux-dist')`);
|
|
162
|
+
console.log(` FastAPI: app.mount("/", StaticFiles(directory="jux-dist"), name="static")`);
|
|
163
|
+
console.log(` Laravel: Route::view('/', 'jux-dist/index.html')`);
|
|
164
|
+
console.log('');
|
|
165
|
+
|
|
144
166
|
} catch (err) {
|
|
145
167
|
console.error('ā Build error:', err.message);
|
|
146
168
|
console.error(err.stack);
|
|
@@ -149,26 +171,105 @@ async function buildProject(isServe = false) {
|
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
(async () => {
|
|
152
|
-
if (command === '
|
|
174
|
+
if (command === 'init') {
|
|
175
|
+
console.log('šØ Initializing JUX project...\n');
|
|
176
|
+
|
|
177
|
+
const juxDir = PATHS.juxSource;
|
|
178
|
+
|
|
179
|
+
if (fs.existsSync(juxDir)) {
|
|
180
|
+
console.error('ā jux/ directory already exists');
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Create structure
|
|
185
|
+
fs.mkdirSync(juxDir, { recursive: true });
|
|
186
|
+
|
|
187
|
+
// Copy template file from lib/templates/index.juxt
|
|
188
|
+
const templatePath = path.join(PATHS.packageRoot, 'lib', 'templates', 'index.juxt');
|
|
189
|
+
const targetPath = path.join(juxDir, 'index.jux');
|
|
190
|
+
|
|
191
|
+
if (fs.existsSync(templatePath)) {
|
|
192
|
+
fs.copyFileSync(templatePath, targetPath);
|
|
193
|
+
console.log('ā
Created jux/index.jux from template');
|
|
194
|
+
} else {
|
|
195
|
+
// Fallback if template doesn't exist
|
|
196
|
+
console.warn('ā ļø Template not found, creating basic index.jux');
|
|
197
|
+
const fallbackContent = `// Welcome to JUX!
|
|
198
|
+
import { jux } from '/lib/jux.js';
|
|
199
|
+
|
|
200
|
+
jux.style('/lib/layouts/default.css');
|
|
201
|
+
jux.theme('light');
|
|
202
|
+
|
|
203
|
+
const header = jux.header('header').render("#app");
|
|
204
|
+
const main = jux.main('main').render("#app");
|
|
205
|
+
const footer = jux.footer('footer').render("#app");
|
|
206
|
+
|
|
207
|
+
jux.hero('hero1', {
|
|
208
|
+
title: 'Welcome to JUX',
|
|
209
|
+
subtitle: 'A JavaScript UX authorship platform'
|
|
210
|
+
}).render('#main');
|
|
211
|
+
`;
|
|
212
|
+
fs.writeFileSync(targetPath, fallbackContent);
|
|
213
|
+
console.log('ā
Created jux/index.jux');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Create .gitignore
|
|
217
|
+
const gitignorePath = path.join(PATHS.projectRoot, '.gitignore');
|
|
218
|
+
const gitignoreContent = `jux-dist/
|
|
219
|
+
node_modules/
|
|
220
|
+
.DS_Store
|
|
221
|
+
`;
|
|
222
|
+
|
|
223
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
224
|
+
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
225
|
+
console.log('ā
Created .gitignore');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
console.log('ā
Created jux/ directory\n');
|
|
229
|
+
console.log('Next steps:');
|
|
230
|
+
console.log(' 1. Edit jux/index.jux');
|
|
231
|
+
console.log(' 2. Run: npx jux build');
|
|
232
|
+
console.log(' 3. Serve jux-dist/ from your backend\n');
|
|
233
|
+
|
|
234
|
+
} else if (command === 'build') {
|
|
153
235
|
await buildProject(false);
|
|
154
|
-
console.log(`ā
Build complete: ${PATHS.
|
|
236
|
+
console.log(`ā
Build complete: ${PATHS.frontendDist}`);
|
|
155
237
|
|
|
156
238
|
} else if (command === 'serve') {
|
|
157
239
|
await buildProject(true);
|
|
240
|
+
|
|
158
241
|
const config = await loadConfig();
|
|
159
|
-
|
|
242
|
+
config.distDir = PATHS.frontendDist; // Pass the correct dist directory
|
|
243
|
+
|
|
244
|
+
const port = parseInt(process.argv[3]) || 3000;
|
|
245
|
+
await start(port, config);
|
|
160
246
|
|
|
161
247
|
} else {
|
|
162
248
|
console.log(`
|
|
163
249
|
JUX CLI - A JavaScript UX authorship platform
|
|
164
250
|
|
|
165
251
|
Usage:
|
|
166
|
-
npx jux
|
|
252
|
+
npx jux init Initialize a new JUX project
|
|
253
|
+
npx jux build Compile .jux files from ./jux/ to ./jux-dist/
|
|
167
254
|
npx jux serve [port] Start dev server with hot reload (default: 3000)
|
|
168
255
|
|
|
256
|
+
Project Structure (Convention):
|
|
257
|
+
my-project/
|
|
258
|
+
āāā jux/ # Your .jux source files (REQUIRED)
|
|
259
|
+
ā āāā index.jux
|
|
260
|
+
ā āāā pages/
|
|
261
|
+
āāā jux-dist/ # Build output (generated, git-ignore this)
|
|
262
|
+
āāā server/ # Your backend (untouched by jux)
|
|
263
|
+
āāā package.json
|
|
264
|
+
|
|
265
|
+
Getting Started:
|
|
266
|
+
1. npx jux init # Create jux/ directory
|
|
267
|
+
2. npx jux build # Build to jux-dist/
|
|
268
|
+
3. Serve jux-dist/ from your backend
|
|
269
|
+
|
|
169
270
|
Examples:
|
|
170
271
|
npx jux build Build for production
|
|
171
|
-
npx jux serve Start dev server
|
|
272
|
+
npx jux serve Start dev server
|
|
172
273
|
npx jux serve 8080 Start on port 8080
|
|
173
274
|
`);
|
|
174
275
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Welcome to JUX!
|
|
2
|
+
import { code } from '/lib/components/code.js';
|
|
3
|
+
import { jux } from '/lib/jux.js';
|
|
4
|
+
|
|
5
|
+
jux.style('/lib/layouts/notion.css');
|
|
6
|
+
jux.theme('light');
|
|
7
|
+
|
|
8
|
+
const appheader = jux.header('appheader', {}).render("#app");
|
|
9
|
+
const appsubheader = jux.header('appsubheader', {}).render("#app");
|
|
10
|
+
const appsidebar = jux.sidebar('appsidebar', {}).render("#app");
|
|
11
|
+
const appmain = jux.main('appmain', {}).render("#app");
|
|
12
|
+
const appaside = jux.sidebar('appaside', {}).render("#app");
|
|
13
|
+
const appfooter = jux.footer('appfooter', {}).render("#app");
|
|
14
|
+
const appmodal = jux.modal('appmodal', {}).render("#app");
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
await jux.hero('hero1', {
|
|
18
|
+
title: 'Welcome to JUX',
|
|
19
|
+
subtitle: 'A JavaScript UX authorship platform'
|
|
20
|
+
}).render();
|
|
21
|
+
|
|
22
|
+
const code1 = code('code1').code(`Serve from your backend:
|
|
23
|
+
Express: app.use(express.static('jux-dist'))
|
|
24
|
+
Flask: app = Flask(__name__, static_folder='jux-dist')
|
|
25
|
+
FastAPI: app.mount("/", StaticFiles(directory="jux-dist"), name="static")
|
|
26
|
+
Laravel: Route::view('/', 'jux-dist/index.html')`)
|
|
27
|
+
.language('bash');
|
|
28
|
+
|
|
29
|
+
code1.render();
|
|
30
|
+
|
|
31
|
+
// note
|
|
32
|
+
// render()
|
|
33
|
+
// renderTo()
|
package/machinery/server.js
CHANGED
|
@@ -12,7 +12,7 @@ const __dirname = path.dirname(__filename);
|
|
|
12
12
|
|
|
13
13
|
let db = null;
|
|
14
14
|
|
|
15
|
-
async function serve(port = 3000, distDir = './dist') {
|
|
15
|
+
async function serve(port = 3000, distDir = './jux-dist') { // Changed default
|
|
16
16
|
const app = express();
|
|
17
17
|
const absoluteDistDir = path.resolve(distDir);
|
|
18
18
|
const projectRoot = path.resolve('.');
|
|
@@ -20,8 +20,8 @@ async function serve(port = 3000, distDir = './dist') {
|
|
|
20
20
|
app.use(express.json());
|
|
21
21
|
|
|
22
22
|
if (!fs.existsSync(absoluteDistDir)) {
|
|
23
|
-
console.error(
|
|
24
|
-
console.
|
|
23
|
+
console.error(`ā Error: ${path.basename(distDir)}/ directory not found at ${absoluteDistDir}`);
|
|
24
|
+
console.error(' Run: npx jux build\n');
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -146,16 +146,23 @@ async function serve(port = 3000, distDir = './dist') {
|
|
|
146
146
|
// Start HTTP server
|
|
147
147
|
server.listen(port, () => {
|
|
148
148
|
console.log(`š JUX dev server running at http://localhost:${port}`);
|
|
149
|
+
console.log(` Serving: ${absoluteDistDir}`);
|
|
150
|
+
console.log(` Press Ctrl+C to stop\n`);
|
|
149
151
|
});
|
|
150
152
|
|
|
151
153
|
// Start file watcher
|
|
152
|
-
|
|
154
|
+
const juxSource = path.join(projectRoot, 'jux');
|
|
155
|
+
if (fs.existsSync(juxSource)) {
|
|
156
|
+
console.log(`š Watching: ${juxSource}\n`);
|
|
157
|
+
startWatcher(juxSource, absoluteDistDir, clients);
|
|
158
|
+
}
|
|
153
159
|
|
|
154
160
|
// Graceful shutdown
|
|
155
161
|
const shutdown = async () => {
|
|
156
162
|
console.log('\n\nš Shutting down server...');
|
|
157
163
|
wss.close();
|
|
158
164
|
server.close();
|
|
165
|
+
if (db) db = null;
|
|
159
166
|
process.exit(0);
|
|
160
167
|
};
|
|
161
168
|
|
|
@@ -181,5 +188,5 @@ async function initDatabase() {
|
|
|
181
188
|
|
|
182
189
|
export async function start(port = 3000, config = {}) {
|
|
183
190
|
await initDatabase();
|
|
184
|
-
return serve(port, config.distDir || './dist');
|
|
191
|
+
return serve(port, config.distDir || './jux-dist'); // Changed default
|
|
185
192
|
}
|