juxscript 1.0.20 → 1.0.22
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 +166 -114
- package/lib/components/alert.ts +143 -92
- package/lib/components/badge.ts +93 -94
- package/lib/components/base/BaseComponent.ts +397 -0
- package/lib/components/base/FormInput.ts +322 -0
- package/lib/components/button.ts +40 -131
- package/lib/components/card.ts +57 -79
- package/lib/components/charts/areachart.ts +315 -0
- package/lib/components/charts/barchart.ts +421 -0
- package/lib/components/charts/doughnutchart.ts +263 -0
- package/lib/components/charts/lib/BaseChart.ts +402 -0
- package/lib/components/{chart-types.ts → charts/lib/chart-types.ts} +1 -1
- package/lib/components/{chart-utils.ts → charts/lib/chart-utils.ts} +1 -1
- package/lib/components/{chart.ts → charts/lib/chart.ts} +3 -3
- package/lib/components/checkbox.ts +255 -204
- package/lib/components/code.ts +31 -78
- package/lib/components/container.ts +113 -130
- package/lib/components/data.ts +37 -5
- package/lib/components/datepicker.ts +180 -147
- package/lib/components/dialog.ts +218 -221
- package/lib/components/divider.ts +63 -87
- package/lib/components/docs-data.json +498 -2404
- package/lib/components/dropdown.ts +191 -236
- package/lib/components/element.ts +196 -145
- package/lib/components/fileupload.ts +253 -167
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.ts +31 -97
- package/lib/components/helpers.ts +13 -6
- package/lib/components/hero.ts +51 -114
- package/lib/components/icon.ts +33 -120
- package/lib/components/icons.ts +2 -1
- package/lib/components/include.ts +76 -3
- package/lib/components/input.ts +155 -407
- package/lib/components/kpicard.ts +16 -16
- package/lib/components/list.ts +358 -261
- package/lib/components/loading.ts +142 -211
- package/lib/components/menu.ts +63 -152
- package/lib/components/modal.ts +42 -129
- package/lib/components/nav.ts +79 -101
- package/lib/components/paragraph.ts +38 -102
- package/lib/components/progress.ts +108 -166
- package/lib/components/radio.ts +283 -234
- package/lib/components/script.ts +19 -87
- package/lib/components/select.ts +189 -199
- package/lib/components/sidebar.ts +110 -141
- package/lib/components/style.ts +19 -82
- package/lib/components/switch.ts +254 -183
- package/lib/components/table.ts +1078 -208
- package/lib/components/tabs.ts +42 -106
- package/lib/components/theme-toggle.ts +73 -165
- package/lib/components/tooltip.ts +85 -316
- package/lib/components/write.ts +108 -127
- package/lib/jux.ts +67 -41
- package/machinery/build.js +466 -0
- package/machinery/compiler.js +354 -105
- package/machinery/server.js +50 -103
- package/machinery/watcher.js +153 -130
- package/package.json +1 -1
- package/presets/hey.jux +46 -0
- package/presets/styles/base.css +1380 -0
- package/presets/styles/notion.css +127 -0
- package/lib/adapters/base-adapter.js +0 -35
- package/lib/adapters/index.js +0 -33
- package/lib/adapters/mysql-adapter.js +0 -65
- package/lib/adapters/postgres-adapter.js +0 -70
- package/lib/adapters/sqlite-adapter.js +0 -56
- package/lib/components/areachart.ts +0 -1128
- package/lib/components/areachartsmooth.ts +0 -1380
- package/lib/components/barchart.ts +0 -1322
- package/lib/components/doughnutchart.ts +0 -1259
- package/lib/components/footer.ts +0 -165
- package/lib/components/header.ts +0 -187
- package/lib/components/layout.ts +0 -239
- package/lib/components/main.ts +0 -137
- package/lib/layouts/default.jux +0 -8
- package/lib/layouts/figma.jux +0 -0
- package/presets/notion.css +0 -2100
- /package/lib/{themes → components/charts/lib}/charts.js +0 -0
package/bin/cli.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
copyLibToOutput,
|
|
5
|
+
copyProjectAssets,
|
|
6
|
+
transpileProjectTypeScript,
|
|
7
|
+
copyPresetsToOutput,
|
|
8
|
+
bundleJuxFilesToRouter,
|
|
9
|
+
generateIndexHtml
|
|
10
|
+
} from '../machinery/compiler.js';
|
|
4
11
|
import { generateDocs } from '../machinery/doc-generator.js';
|
|
5
12
|
import { start } from '../machinery/server.js';
|
|
6
13
|
import path from 'path';
|
|
@@ -42,13 +49,12 @@ console.log(` Output: ${PATHS.frontendDist}`);
|
|
|
42
49
|
console.log(` Lib: ${PATHS.juxLib}\n`);
|
|
43
50
|
|
|
44
51
|
const command = process.argv[2];
|
|
52
|
+
const subCommand = process.argv[3]; // For serve <pagename>
|
|
53
|
+
const watchMode = process.argv.includes('--watch');
|
|
54
|
+
const bundleMode = process.argv.includes('--bundle');
|
|
45
55
|
|
|
46
56
|
/**
|
|
47
57
|
* Recursively find .jux files in a directory
|
|
48
|
-
*
|
|
49
|
-
* @param {string} dir - Directory to search
|
|
50
|
-
* @param {string[]} fileList - Accumulator for found files
|
|
51
|
-
* @returns {string[]} Array of .jux file paths
|
|
52
58
|
*/
|
|
53
59
|
function findJuxFiles(dir, fileList = []) {
|
|
54
60
|
if (!fs.existsSync(dir)) return fileList;
|
|
@@ -72,11 +78,12 @@ function findJuxFiles(dir, fileList = []) {
|
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
/**
|
|
75
|
-
* Build the entire JUX project
|
|
81
|
+
* Build the entire JUX project (ALWAYS uses router bundle)
|
|
76
82
|
*
|
|
77
|
-
* @param {boolean} isServe - Whether building for dev server
|
|
83
|
+
* @param {boolean} isServe - Whether building for dev server
|
|
78
84
|
*/
|
|
79
85
|
async function buildProject(isServe = false) {
|
|
86
|
+
const buildStartTime = performance.now();
|
|
80
87
|
console.log('🔨 Building JUX frontend...\n');
|
|
81
88
|
|
|
82
89
|
try {
|
|
@@ -93,80 +100,121 @@ async function buildProject(isServe = false) {
|
|
|
93
100
|
}
|
|
94
101
|
fs.mkdirSync(PATHS.frontendDist, { recursive: true });
|
|
95
102
|
|
|
96
|
-
// Step 1: Generate documentation
|
|
103
|
+
// Step 1: Generate documentation
|
|
104
|
+
const docsStartTime = performance.now();
|
|
105
|
+
let docsTime = 0; // ✅ Declare with default value
|
|
97
106
|
console.log('📚 Generating documentation...');
|
|
98
107
|
try {
|
|
99
108
|
await generateDocs(PATHS.juxLib);
|
|
100
|
-
|
|
109
|
+
docsTime = performance.now() - docsStartTime;
|
|
110
|
+
console.log(`✅ Documentation generated (${docsTime.toFixed(0)}ms)\n`);
|
|
101
111
|
} catch (error) {
|
|
102
|
-
|
|
112
|
+
docsTime = performance.now() - docsStartTime; // ✅ Still calculate time even on error
|
|
113
|
+
console.warn(`⚠️ Failed to generate docs (${docsTime.toFixed(0)}ms):`, error.message);
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
// Step 2: Copy jux lib to frontend dist
|
|
117
|
+
const libStartTime = performance.now();
|
|
106
118
|
await copyLibToOutput(PATHS.juxLib, PATHS.frontendDist);
|
|
119
|
+
const libTime = performance.now() - libStartTime;
|
|
120
|
+
console.log(`⏱️ Lib copy time: ${libTime.toFixed(0)}ms\n`);
|
|
107
121
|
|
|
108
|
-
// Step
|
|
122
|
+
// Step 3: Copy presets folder
|
|
123
|
+
const presetsStartTime = performance.now();
|
|
109
124
|
await copyPresetsToOutput(PATHS.packageRoot, PATHS.frontendDist);
|
|
125
|
+
const presetsTime = performance.now() - presetsStartTime;
|
|
126
|
+
console.log(`⏱️ Presets copy time: ${presetsTime.toFixed(0)}ms\n`);
|
|
110
127
|
|
|
111
|
-
// Step
|
|
128
|
+
// Step 4: Copy project assets (CSS, JS, images)
|
|
129
|
+
const assetsStartTime = performance.now();
|
|
112
130
|
await copyProjectAssets(PATHS.juxSource, PATHS.frontendDist);
|
|
131
|
+
const assetsTime = performance.now() - assetsStartTime;
|
|
132
|
+
console.log(`⏱️ Assets copy time: ${assetsTime.toFixed(0)}ms\n`);
|
|
113
133
|
|
|
114
|
-
// Step
|
|
134
|
+
// Step 5: Transpile TypeScript files
|
|
135
|
+
const tsStartTime = performance.now();
|
|
115
136
|
await transpileProjectTypeScript(PATHS.juxSource, PATHS.frontendDist);
|
|
137
|
+
const tsTime = performance.now() - tsStartTime;
|
|
138
|
+
console.log(`⏱️ TypeScript transpile time: ${tsTime.toFixed(0)}ms\n`);
|
|
116
139
|
|
|
117
|
-
// Step
|
|
140
|
+
// Step 6: Bundle all .jux files into router
|
|
118
141
|
const projectJuxFiles = findJuxFiles(PATHS.juxSource);
|
|
119
142
|
console.log(`📝 Found ${projectJuxFiles.length} .jux file(s) in /jux\n`);
|
|
120
143
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
distDir: PATHS.frontendDist,
|
|
125
|
-
projectRoot: PATHS.juxSource,
|
|
126
|
-
isServe
|
|
127
|
-
});
|
|
128
|
-
} catch (err) {
|
|
129
|
-
console.error(`Error compiling ${file}:`, err.message);
|
|
130
|
-
}
|
|
144
|
+
if (projectJuxFiles.length === 0) {
|
|
145
|
+
console.warn('⚠️ No .jux files found to bundle');
|
|
146
|
+
process.exit(1);
|
|
131
147
|
}
|
|
132
148
|
|
|
133
|
-
//
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
149
|
+
// ✅ Bundle and get the generated filename
|
|
150
|
+
const bundleStartTime = performance.now();
|
|
151
|
+
const mainJsFilename = await bundleJuxFilesToRouter(PATHS.juxSource, PATHS.frontendDist, {
|
|
152
|
+
routePrefix: ''
|
|
153
|
+
});
|
|
154
|
+
const bundleTime = performance.now() - bundleStartTime;
|
|
155
|
+
|
|
156
|
+
// Generate routes for index.html
|
|
157
|
+
const routes = projectJuxFiles.map(juxFile => {
|
|
158
|
+
const relativePath = path.relative(PATHS.juxSource, juxFile);
|
|
159
|
+
const parsedPath = path.parse(relativePath);
|
|
160
|
+
|
|
161
|
+
const rawFunctionName = parsedPath.dir
|
|
162
|
+
? `${parsedPath.dir.replace(/\//g, '_')}_${parsedPath.name}`
|
|
163
|
+
: parsedPath.name;
|
|
164
|
+
|
|
165
|
+
const functionName = rawFunctionName
|
|
166
|
+
.replace(/[-_]/g, ' ')
|
|
167
|
+
.split(' ')
|
|
168
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
169
|
+
.join('');
|
|
170
|
+
|
|
171
|
+
const routePath = '/' + (parsedPath.dir ? `${parsedPath.dir}/` : '') + parsedPath.name;
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
path: routePath.replace(/\/+/g, '/'),
|
|
175
|
+
functionName
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// ✅ Generate unified index.html
|
|
180
|
+
const indexStartTime = performance.now();
|
|
181
|
+
generateIndexHtml(PATHS.frontendDist, routes, mainJsFilename);
|
|
182
|
+
const indexTime = performance.now() - indexStartTime;
|
|
183
|
+
|
|
184
|
+
const totalBuildTime = performance.now() - buildStartTime;
|
|
185
|
+
|
|
186
|
+
console.log(`\n✅ Bundled ${projectJuxFiles.length} page(s) → ${PATHS.frontendDist}/${mainJsFilename}\n`);
|
|
187
|
+
|
|
188
|
+
// ✅ Build summary with timing breakdown
|
|
189
|
+
console.log(`📊 Build Summary:`);
|
|
190
|
+
console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
|
191
|
+
console.log(` Documentation: ${docsTime.toFixed(0)}ms`);
|
|
192
|
+
console.log(` Library copy: ${libTime.toFixed(0)}ms`);
|
|
193
|
+
console.log(` Presets copy: ${presetsTime.toFixed(0)}ms`);
|
|
194
|
+
console.log(` Assets copy: ${assetsTime.toFixed(0)}ms`);
|
|
195
|
+
console.log(` TypeScript: ${tsTime.toFixed(0)}ms`);
|
|
196
|
+
console.log(` Router bundle: ${bundleTime.toFixed(0)}ms`);
|
|
197
|
+
console.log(` Index generation: ${indexTime.toFixed(0)}ms`);
|
|
198
|
+
console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
|
199
|
+
console.log(` Total build time: ${totalBuildTime.toFixed(0)}ms\n`);
|
|
200
|
+
|
|
201
|
+
// Show usage
|
|
202
|
+
if (!isServe) {
|
|
203
|
+
console.log('📦 Serve from your backend:');
|
|
204
|
+
console.log(` Express: app.use(express.static('jux-dist'))`);
|
|
205
|
+
console.log(` Flask: app = Flask(__name__, static_folder='jux-dist')`);
|
|
206
|
+
console.log(` FastAPI: app.mount("/", StaticFiles(directory="jux-dist"), name="static")`);
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log('📍 Available routes:');
|
|
209
|
+
routes.forEach(r => {
|
|
210
|
+
console.log(` ${r.path}`);
|
|
211
|
+
});
|
|
212
|
+
console.log('');
|
|
156
213
|
}
|
|
157
214
|
|
|
158
|
-
console.log(`\n✅ Built ${projectJuxFiles.length} file(s) → ${PATHS.frontendDist}\n`);
|
|
159
|
-
|
|
160
|
-
// Show backend integration examples
|
|
161
|
-
console.log('📦 Serve from your backend:');
|
|
162
|
-
console.log(` Express: app.use(express.static('jux-dist'))`);
|
|
163
|
-
console.log(` Flask: app = Flask(__name__, static_folder='jux-dist')`);
|
|
164
|
-
console.log(` FastAPI: app.mount("/", StaticFiles(directory="jux-dist"), name="static")`);
|
|
165
|
-
console.log(` Laravel: Route::view('/', 'jux-dist/index.html')`);
|
|
166
|
-
console.log('');
|
|
167
|
-
|
|
168
215
|
} catch (err) {
|
|
169
|
-
|
|
216
|
+
const failTime = performance.now() - buildStartTime;
|
|
217
|
+
console.error(`❌ Build error after ${failTime.toFixed(0)}ms:`, err.message);
|
|
170
218
|
console.error(err.stack);
|
|
171
219
|
process.exit(1);
|
|
172
220
|
}
|
|
@@ -186,41 +234,34 @@ async function buildProject(isServe = false) {
|
|
|
186
234
|
// Create structure
|
|
187
235
|
fs.mkdirSync(juxDir, { recursive: true });
|
|
188
236
|
|
|
189
|
-
//
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
.gap(20)
|
|
200
|
-
.style('padding: 40px;')
|
|
201
|
-
.render('body');
|
|
202
|
-
|
|
203
|
-
jux.hero('welcome-hero', {
|
|
204
|
-
title: 'Welcome to JUX',
|
|
205
|
-
subtitle: 'A JavaScript UX authorship platform'
|
|
206
|
-
}).render('#app-container');
|
|
207
|
-
|
|
208
|
-
jux.divider({}).render('#app-container');
|
|
209
|
-
|
|
210
|
-
jux.write(\`
|
|
211
|
-
<h2>Getting Started</h2>
|
|
212
|
-
<p>Edit <code>jux/index.jux</code> to build your app.</p>
|
|
213
|
-
<ul>
|
|
214
|
-
<li>Run <code>npx jux build</code> to compile</li>
|
|
215
|
-
<li>Run <code>npx jux serve</code> for dev mode</li>
|
|
216
|
-
<li>Serve <code>jux-dist/</code> from your backend</li>
|
|
217
|
-
</ul>
|
|
218
|
-
\`).render('#app-container');
|
|
219
|
-
`;
|
|
237
|
+
// Copy hey.jux from presets as starter template
|
|
238
|
+
const heyTemplatePath = path.join(PATHS.packageRoot, 'presets', 'hey.jux');
|
|
239
|
+
const heyTargetPath = path.join(juxDir, 'hey.jux');
|
|
240
|
+
|
|
241
|
+
if (fs.existsSync(heyTemplatePath)) {
|
|
242
|
+
fs.copyFileSync(heyTemplatePath, heyTargetPath);
|
|
243
|
+
console.log('+ Created jux/hey.jux (starter template)');
|
|
244
|
+
} else {
|
|
245
|
+
console.warn('⚠️ hey.jux template not found in presets/');
|
|
246
|
+
}
|
|
220
247
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
248
|
+
// Copy over presets/*.css as styles/ to jux/
|
|
249
|
+
const presetsSrc = path.join(PATHS.packageRoot, 'presets');
|
|
250
|
+
const stylesDest = path.join(juxDir, 'styles');
|
|
251
|
+
|
|
252
|
+
if (fs.existsSync(presetsSrc)) {
|
|
253
|
+
fs.mkdirSync(stylesDest, { recursive: true });
|
|
254
|
+
|
|
255
|
+
const presetFiles = fs.readdirSync(presetsSrc);
|
|
256
|
+
presetFiles.forEach(file => {
|
|
257
|
+
if (file.endsWith('.css')) {
|
|
258
|
+
const srcFile = path.join(presetsSrc, file);
|
|
259
|
+
const destFile = path.join(stylesDest, file);
|
|
260
|
+
fs.copyFileSync(srcFile, destFile);
|
|
261
|
+
console.log(`+ Copied preset style: styles/${file}`);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
224
265
|
|
|
225
266
|
// Create package.json if it doesn't exist
|
|
226
267
|
const pkgPath = path.join(PATHS.projectRoot, 'package.json');
|
|
@@ -250,25 +291,33 @@ node_modules/
|
|
|
250
291
|
|
|
251
292
|
if (!fs.existsSync(gitignorePath)) {
|
|
252
293
|
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
253
|
-
console.log('
|
|
294
|
+
console.log('+ Created .gitignore');
|
|
254
295
|
}
|
|
255
296
|
|
|
256
|
-
console.log('
|
|
297
|
+
console.log('+ Created jux/ directory\n');
|
|
257
298
|
console.log('Next steps:');
|
|
258
|
-
console.log('
|
|
259
|
-
console.log('
|
|
260
|
-
console.log(' 3. npx jux build # Compile to jux-dist/');
|
|
261
|
-
console.log(' 4. Serve jux-dist/ from your backend\n');
|
|
299
|
+
console.log(' npx jux serve hey # Start dev server for hey.jux\n');
|
|
300
|
+
console.log('Check out the docs: https://juxscript.com/docs\n');
|
|
262
301
|
|
|
263
302
|
} else if (command === 'build') {
|
|
303
|
+
// ✅ Always builds router bundle
|
|
264
304
|
await buildProject(false);
|
|
265
305
|
console.log(`✅ Build complete: ${PATHS.frontendDist}`);
|
|
266
306
|
|
|
267
307
|
} else if (command === 'serve') {
|
|
308
|
+
// ✅ Serve with optional page parameter
|
|
309
|
+
const pageName = subCommand; // e.g., "hey" from "npx jux serve hey"
|
|
310
|
+
|
|
311
|
+
if (pageName) {
|
|
312
|
+
console.log(`🎯 Serving specific page: ${pageName}\n`);
|
|
313
|
+
}
|
|
314
|
+
|
|
268
315
|
await buildProject(true);
|
|
269
316
|
|
|
270
|
-
const port = parseInt(
|
|
271
|
-
|
|
317
|
+
const port = pageName ? 3000 : (parseInt(subCommand) || 3000);
|
|
318
|
+
|
|
319
|
+
// Start server
|
|
320
|
+
await start(port, pageName);
|
|
272
321
|
|
|
273
322
|
} else {
|
|
274
323
|
console.log(`
|
|
@@ -276,16 +325,17 @@ JUX CLI - A JavaScript UX authorship platform
|
|
|
276
325
|
|
|
277
326
|
Usage:
|
|
278
327
|
npx jux init Initialize a new JUX project
|
|
279
|
-
npx jux build
|
|
280
|
-
npx jux serve [
|
|
328
|
+
npx jux build Build router bundle to ./jux-dist/
|
|
329
|
+
npx jux serve [page] Start dev server (optionally for specific page)
|
|
330
|
+
npx jux serve [port] Start dev server on custom port
|
|
281
331
|
|
|
282
332
|
Project Structure:
|
|
283
333
|
my-project/
|
|
284
|
-
├── jux/
|
|
285
|
-
│ ├──
|
|
286
|
-
│ └── pages/
|
|
287
|
-
├── jux-dist/
|
|
288
|
-
├── server/
|
|
334
|
+
├── jux/ # Your .jux source files
|
|
335
|
+
│ ├── hey.jux # Starter page (created by init)
|
|
336
|
+
│ └── pages/ # Additional pages
|
|
337
|
+
├── jux-dist/ # Build output (git-ignore this)
|
|
338
|
+
├── server/ # Your backend
|
|
289
339
|
└── package.json
|
|
290
340
|
|
|
291
341
|
Import Style:
|
|
@@ -294,15 +344,17 @@ Import Style:
|
|
|
294
344
|
import 'juxscript/presets/notion.js';
|
|
295
345
|
|
|
296
346
|
Getting Started:
|
|
297
|
-
1. npx jux init
|
|
298
|
-
2. npm install
|
|
299
|
-
3. npx jux
|
|
300
|
-
4.
|
|
347
|
+
1. npx jux init # Create project structure
|
|
348
|
+
2. npm install # Install dependencies
|
|
349
|
+
3. npx jux serve hey # Dev server for hey.jux at localhost:3000/hey
|
|
350
|
+
4. npx jux serve # Dev server for all pages
|
|
351
|
+
5. Serve jux-dist/ from your backend
|
|
301
352
|
|
|
302
353
|
Examples:
|
|
303
|
-
npx jux build
|
|
304
|
-
npx jux serve
|
|
305
|
-
npx jux serve
|
|
354
|
+
npx jux build Build production bundle
|
|
355
|
+
npx jux serve Start dev server on port 3000 (all pages)
|
|
356
|
+
npx jux serve hey Start dev server at localhost:3000/hey
|
|
357
|
+
npx jux serve 8080 Start dev server on port 8080
|
|
306
358
|
`);
|
|
307
359
|
}
|
|
308
360
|
})();
|