@hashrock/ono 0.1.1 ā 0.1.2
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 +88 -46
- package/package.json +18 -4
- package/src/browser/compiler.js +253 -0
- package/src/browser/unocss.js +29 -0
- package/src/builder.js +317 -0
- package/src/bundler.js +12 -4
- package/src/cli.js +178 -945
- package/src/content.js +272 -0
- package/src/jsx-runtime.js +19 -0
- package/src/renderer.js +11 -4
- package/src/server.js +99 -0
- package/src/unocss.js +28 -2
- package/src/watcher.js +167 -0
package/src/cli.js
CHANGED
|
@@ -1,1014 +1,247 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* CLI
|
|
4
|
+
* Ono CLI - Minimalist SSG framework
|
|
5
5
|
*/
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { copyFile, mkdir, readdir, stat } from "node:fs/promises";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { join, relative } from "node:path";
|
|
10
|
+
import { loadUnoConfig } from "./unocss.js";
|
|
11
|
+
import { createDevServer } from "./server.js";
|
|
12
|
+
import { buildFile, buildFiles, generateUnoCSS } from "./builder.js";
|
|
13
|
+
import { watchFile, watchFiles, createWebSocketServer } from "./watcher.js";
|
|
6
14
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import http from "node:http";
|
|
10
|
-
import net from "node:net";
|
|
11
|
-
import { bundle } from "./bundler.js";
|
|
12
|
-
import { renderToString } from "./renderer.js";
|
|
13
|
-
import { WebSocketServer } from "ws";
|
|
14
|
-
import { generateCSSFromFiles, loadUnoConfig } from "./unocss.js";
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const command = args[0];
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* @param {number} maxAttempts - Maximum number of ports to try
|
|
20
|
-
* @returns {Promise<number>} Available port number
|
|
21
|
-
*/
|
|
22
|
-
async function findAvailablePort(startPort, maxAttempts = 10) {
|
|
23
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
24
|
-
const port = startPort + i;
|
|
25
|
-
try {
|
|
26
|
-
await new Promise((resolve, reject) => {
|
|
27
|
-
const server = net.createServer();
|
|
28
|
-
server.once("error", reject);
|
|
29
|
-
server.once("listening", () => {
|
|
30
|
-
server.close();
|
|
31
|
-
resolve();
|
|
32
|
-
});
|
|
33
|
-
server.listen(port);
|
|
34
|
-
});
|
|
35
|
-
return port;
|
|
36
|
-
} catch (err) {
|
|
37
|
-
if (err.code !== "EADDRINUSE") {
|
|
38
|
-
throw err;
|
|
39
|
-
}
|
|
40
|
-
// Port is in use, try next one
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
throw new Error(`Could not find available port in range ${startPort}-${startPort + maxAttempts - 1}`);
|
|
44
|
-
}
|
|
18
|
+
async function copyPublicFiles(outputDir = "dist") {
|
|
19
|
+
const publicDir = resolve(process.cwd(), "public");
|
|
20
|
+
const outDir = resolve(process.cwd(), outputDir);
|
|
45
21
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
* @param {string[]} args - Command line arguments
|
|
49
|
-
* @returns {object} Parsed options
|
|
50
|
-
*/
|
|
51
|
-
function parseArgs(args) {
|
|
52
|
-
const options = {
|
|
53
|
-
_: [], // Positional arguments
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
for (let i = 0; i < args.length; i++) {
|
|
57
|
-
const arg = args[i];
|
|
58
|
-
|
|
59
|
-
if (arg === "--watch" || arg === "-w") {
|
|
60
|
-
options.watch = true;
|
|
61
|
-
} else if (arg === "--port" || arg === "-p") {
|
|
62
|
-
options.port = parseInt(args[++i]);
|
|
63
|
-
} else if (arg === "--output" || arg === "-o") {
|
|
64
|
-
options.output = args[++i];
|
|
65
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
66
|
-
options.help = true;
|
|
67
|
-
} else if (arg === "--version" || arg === "-v") {
|
|
68
|
-
options.version = true;
|
|
69
|
-
} else if (!arg.startsWith("-")) {
|
|
70
|
-
options._.push(arg);
|
|
71
|
-
}
|
|
22
|
+
if (!existsSync(publicDir)) {
|
|
23
|
+
return;
|
|
72
24
|
}
|
|
73
25
|
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Discover all JSX files in the pages directory
|
|
79
|
-
* @param {string} pagesDir - Path to pages directory
|
|
80
|
-
* @returns {Promise<string[]>} Array of JSX file paths
|
|
81
|
-
*/
|
|
82
|
-
async function discoverPages(pagesDir) {
|
|
83
|
-
const pages = [];
|
|
84
|
-
|
|
85
|
-
async function walk(dir) {
|
|
86
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
26
|
+
async function copyRecursive(src, dest) {
|
|
27
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
87
28
|
|
|
88
29
|
for (const entry of entries) {
|
|
89
|
-
const
|
|
30
|
+
const srcPath = join(src, entry.name);
|
|
31
|
+
const destPath = join(dest, entry.name);
|
|
90
32
|
|
|
91
33
|
if (entry.isDirectory()) {
|
|
92
|
-
await
|
|
93
|
-
|
|
94
|
-
|
|
34
|
+
await mkdir(destPath, { recursive: true });
|
|
35
|
+
await copyRecursive(srcPath, destPath);
|
|
36
|
+
} else {
|
|
37
|
+
await mkdir(dest, { recursive: true });
|
|
38
|
+
await copyFile(srcPath, destPath);
|
|
95
39
|
}
|
|
96
40
|
}
|
|
97
41
|
}
|
|
98
42
|
|
|
99
|
-
|
|
100
|
-
await walk(pagesDir);
|
|
101
|
-
return pages;
|
|
102
|
-
} catch (error) {
|
|
103
|
-
if (error.code === "ENOENT") {
|
|
104
|
-
return [];
|
|
105
|
-
}
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
43
|
+
await copyRecursive(publicDir, outDir);
|
|
108
44
|
}
|
|
109
45
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
*/
|
|
116
|
-
async function generateUnoCSSFile(outputDir, silent = false) {
|
|
117
|
-
const outputDirAbs = path.resolve(process.cwd(), outputDir);
|
|
46
|
+
async function runBuildCommand() {
|
|
47
|
+
// Check for help flag
|
|
48
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
49
|
+
console.log(`
|
|
50
|
+
Usage: ono build [input] [options]
|
|
118
51
|
|
|
119
|
-
|
|
120
|
-
const htmlFiles = [];
|
|
52
|
+
Build JSX files to static HTML
|
|
121
53
|
|
|
122
|
-
|
|
123
|
-
|
|
54
|
+
Arguments:
|
|
55
|
+
input File or directory to build (default: pages)
|
|
124
56
|
|
|
125
|
-
|
|
126
|
-
|
|
57
|
+
Options:
|
|
58
|
+
--output <dir> Output directory (default: dist)
|
|
59
|
+
--help Show this help message
|
|
127
60
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
61
|
+
Examples:
|
|
62
|
+
ono build Build all pages in pages/ directory
|
|
63
|
+
ono build pages/index.jsx Build a single file
|
|
64
|
+
ono build --output build Build to build/ directory
|
|
65
|
+
`);
|
|
66
|
+
process.exit(0);
|
|
134
67
|
}
|
|
135
68
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
69
|
+
// Filter out options to get the input argument
|
|
70
|
+
const nonOptionArgs = args.slice(1).filter(arg => !arg.startsWith("--"));
|
|
71
|
+
const input = nonOptionArgs[0] || "pages";
|
|
72
|
+
const outputDir = args.includes("--output")
|
|
73
|
+
? args[args.indexOf("--output") + 1]
|
|
74
|
+
: "dist";
|
|
142
75
|
|
|
143
|
-
|
|
144
|
-
const unoConfig = await loadUnoConfig(path.resolve(process.cwd(), "uno.config.js"));
|
|
76
|
+
const unocssConfig = await loadUnoConfig();
|
|
145
77
|
|
|
146
|
-
|
|
147
|
-
|
|
78
|
+
// Check if input is a directory or a file
|
|
79
|
+
const inputPath = resolve(process.cwd(), input);
|
|
80
|
+
const inputStat = await stat(inputPath);
|
|
148
81
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
82
|
+
if (inputStat.isDirectory()) {
|
|
83
|
+
// Build all files in directory
|
|
84
|
+
await buildFiles(input, { outputDir, unocssConfig });
|
|
85
|
+
} else {
|
|
86
|
+
// Build single file
|
|
87
|
+
await buildFile(input, { outputDir, unocssConfig });
|
|
88
|
+
}
|
|
153
89
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
90
|
+
// Copy public files
|
|
91
|
+
await copyPublicFiles(outputDir);
|
|
157
92
|
|
|
158
|
-
|
|
159
|
-
|
|
93
|
+
// Generate UnoCSS
|
|
94
|
+
await generateUnoCSS({ outputDir, unocssConfig });
|
|
160
95
|
|
|
161
|
-
|
|
162
|
-
} catch (error) {
|
|
163
|
-
if (!silent) {
|
|
164
|
-
console.error(`Warning: UnoCSS generation failed: ${error.message}`);
|
|
165
|
-
}
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
96
|
+
console.log("\n⨠Build complete!");
|
|
168
97
|
}
|
|
169
98
|
|
|
170
|
-
/**
|
|
171
|
-
* Copy public directory to output directory
|
|
172
|
-
* @param {string} publicDir - Path to public directory
|
|
173
|
-
* @param {string} outputDir - Path to output directory
|
|
174
|
-
* @param {boolean} silent - Suppress console output
|
|
175
|
-
* @returns {Promise<number>} Number of files copied
|
|
176
|
-
*/
|
|
177
|
-
async function copyPublicFiles(publicDir, outputDir, silent = false) {
|
|
178
|
-
const publicDirAbs = path.resolve(process.cwd(), publicDir);
|
|
179
|
-
const outputDirAbs = path.resolve(process.cwd(), outputDir);
|
|
180
99
|
|
|
181
|
-
try {
|
|
182
|
-
// Check if public directory exists
|
|
183
|
-
await fs.access(publicDirAbs);
|
|
184
|
-
} catch (error) {
|
|
185
|
-
// Public directory doesn't exist, skip
|
|
186
|
-
return 0;
|
|
187
|
-
}
|
|
188
100
|
|
|
189
|
-
|
|
101
|
+
async function runDevCommand() {
|
|
102
|
+
const input = args[1] || "pages";
|
|
103
|
+
const port = args.includes("--port")
|
|
104
|
+
? parseInt(args[args.indexOf("--port") + 1])
|
|
105
|
+
: 3000;
|
|
106
|
+
const outputDir = args.includes("--output")
|
|
107
|
+
? args[args.indexOf("--output") + 1]
|
|
108
|
+
: "dist";
|
|
190
109
|
|
|
191
|
-
|
|
192
|
-
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
110
|
+
const unocssConfig = await loadUnoConfig();
|
|
193
111
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
112
|
+
// Initial build
|
|
113
|
+
const inputPath = resolve(process.cwd(), input);
|
|
114
|
+
const inputStat = await stat(inputPath);
|
|
197
115
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
await fs.copyFile(srcPath, destPath);
|
|
203
|
-
fileCount++;
|
|
204
|
-
if (!silent) {
|
|
205
|
-
const relativePath = path.relative(publicDirAbs, srcPath);
|
|
206
|
-
console.log(`š Copied: public/${relativePath}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
116
|
+
if (inputStat.isDirectory()) {
|
|
117
|
+
await buildFiles(input, { outputDir, unocssConfig });
|
|
118
|
+
} else {
|
|
119
|
+
await buildFile(input, { outputDir, unocssConfig });
|
|
210
120
|
}
|
|
211
121
|
|
|
212
|
-
await
|
|
213
|
-
|
|
214
|
-
}
|
|
122
|
+
await copyPublicFiles(outputDir);
|
|
123
|
+
await generateUnoCSS({ outputDir, unocssConfig });
|
|
215
124
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
* @param {string} inputFile - Path to input JSX file
|
|
219
|
-
* @param {object} options - Build options
|
|
220
|
-
* @param {boolean} options.liveReload - Inject live reload script
|
|
221
|
-
* @param {boolean} options.silent - Suppress console output
|
|
222
|
-
* @param {number} options.wsPort - WebSocket port for live reload
|
|
223
|
-
* @param {string} options.outputDir - Custom output directory
|
|
224
|
-
* @param {string} options.pagesDir - Pages directory for relative path calculation
|
|
225
|
-
* @returns {Promise<{outputPath: string, html: string}>}
|
|
226
|
-
*/
|
|
227
|
-
async function buildFile(inputFile, options = {}) {
|
|
228
|
-
const { liveReload = false, silent = false, wsPort = 35729, outputDir = "dist", pagesDir = null } = options;
|
|
229
|
-
const absolutePath = path.resolve(process.cwd(), inputFile);
|
|
125
|
+
// Create WebSocket server for live reload
|
|
126
|
+
const { wss, port: wsPort } = createWebSocketServer();
|
|
230
127
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
128
|
+
// Start dev server
|
|
129
|
+
const mode = inputStat.isDirectory() ? "pages" : "single";
|
|
130
|
+
const indexFile = inputStat.isFile()
|
|
131
|
+
? relative(
|
|
132
|
+
outputDir,
|
|
133
|
+
(await buildFile(input, { outputDir, unocssConfig, silent: true }))
|
|
134
|
+
.outputPath
|
|
135
|
+
)
|
|
136
|
+
: "index.html";
|
|
234
137
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
138
|
+
let serverPort = port;
|
|
139
|
+
try {
|
|
140
|
+
const { server, app, port: actualPort } = await createDevServer({
|
|
141
|
+
outputDir,
|
|
142
|
+
port,
|
|
143
|
+
mode,
|
|
144
|
+
indexFile,
|
|
145
|
+
});
|
|
146
|
+
serverPort = actualPort;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error.code === "EADDRINUSE") {
|
|
149
|
+
serverPort = port + 1;
|
|
150
|
+
console.log(
|
|
151
|
+
`ā¹ļø Port ${port} is busy, using port ${serverPort} instead`
|
|
152
|
+
);
|
|
153
|
+
await createDevServer({
|
|
154
|
+
outputDir,
|
|
155
|
+
port: serverPort,
|
|
156
|
+
mode,
|
|
157
|
+
indexFile,
|
|
158
|
+
});
|
|
250
159
|
} else {
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return result;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function h(tag, props, ...children) {
|
|
258
|
-
return {
|
|
259
|
-
tag,
|
|
260
|
-
props: props || {},
|
|
261
|
-
children: flattenChildren(children)
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
${bundledCode}
|
|
266
|
-
`;
|
|
267
|
-
|
|
268
|
-
// Write to temporary file
|
|
269
|
-
const tmpFile = path.join(process.cwd(), ".mini-jsx-tmp.js");
|
|
270
|
-
await fs.writeFile(tmpFile, codeWithRuntime);
|
|
271
|
-
|
|
272
|
-
// Import and execute
|
|
273
|
-
const moduleUrl = `${tmpFile}?t=${Date.now()}`;
|
|
274
|
-
const module = await import(moduleUrl);
|
|
275
|
-
const App = module.default;
|
|
276
|
-
|
|
277
|
-
if (!App) {
|
|
278
|
-
throw new Error("No default export found in entry file");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Render to HTML
|
|
282
|
-
const vnode = typeof App === "function" ? App({}) : App;
|
|
283
|
-
let html = renderToString(vnode);
|
|
284
|
-
|
|
285
|
-
// Inject live reload script if requested
|
|
286
|
-
if (liveReload) {
|
|
287
|
-
const liveReloadScript = `
|
|
288
|
-
<script>
|
|
289
|
-
(function() {
|
|
290
|
-
const ws = new WebSocket('ws://localhost:${wsPort}');
|
|
291
|
-
ws.onmessage = function(event) {
|
|
292
|
-
if (event.data === 'reload') {
|
|
293
|
-
console.log('Reloading...');
|
|
294
|
-
window.location.reload();
|
|
160
|
+
throw error;
|
|
295
161
|
}
|
|
296
|
-
};
|
|
297
|
-
ws.onclose = function() {
|
|
298
|
-
console.log('Live reload disconnected. Retrying...');
|
|
299
|
-
setTimeout(function() { window.location.reload(); }, 1000);
|
|
300
|
-
};
|
|
301
|
-
})();
|
|
302
|
-
</script>`;
|
|
303
|
-
html = html.replace("</body>", `${liveReloadScript}\n</body>`);
|
|
304
162
|
}
|
|
305
163
|
|
|
306
|
-
//
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (pagesDir) {
|
|
318
|
-
// Preserve the directory structure from pages folder
|
|
319
|
-
const pagesDirAbs = path.resolve(process.cwd(), pagesDir);
|
|
320
|
-
const relativePath = path.relative(pagesDirAbs, absolutePath);
|
|
321
|
-
const outputRelative = relativePath.replace(/\.jsx$/, ".html");
|
|
322
|
-
outputPath = path.join(outDir, outputRelative);
|
|
323
|
-
|
|
324
|
-
// Create subdirectories if needed
|
|
325
|
-
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
326
|
-
|
|
327
|
-
relativeOutput = path.relative(process.cwd(), outputPath);
|
|
164
|
+
// Watch for changes
|
|
165
|
+
if (inputStat.isDirectory()) {
|
|
166
|
+
await watchFiles(input, {
|
|
167
|
+
outputDir,
|
|
168
|
+
unocssConfig,
|
|
169
|
+
wss,
|
|
170
|
+
onRebuild: async () => {
|
|
171
|
+
await copyPublicFiles(outputDir);
|
|
172
|
+
},
|
|
173
|
+
});
|
|
328
174
|
} else {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (!silent) {
|
|
342
|
-
console.log(`ā Built successfully: ${relativeOutput}`);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return { outputPath, html: fullHtml };
|
|
175
|
+
await watchFile(input, {
|
|
176
|
+
outputDir,
|
|
177
|
+
unocssConfig,
|
|
178
|
+
wss,
|
|
179
|
+
onRebuild: async () => {
|
|
180
|
+
await copyPublicFiles(outputDir);
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
console.log(`\nš Server running at http://localhost:${serverPort}`);
|
|
186
|
+
console.log(`š Serving: ${input}/ ā ${outputDir}/`);
|
|
346
187
|
}
|
|
347
188
|
|
|
348
|
-
async function main() {
|
|
349
|
-
const args = process.argv.slice(2);
|
|
350
|
-
const opts = parseArgs(args);
|
|
351
189
|
|
|
352
|
-
// Show version
|
|
353
|
-
if (opts.version) {
|
|
354
|
-
const pkg = JSON.parse(
|
|
355
|
-
await fs.readFile(new URL("../package.json", import.meta.url), "utf-8")
|
|
356
|
-
);
|
|
357
|
-
console.log(`ono v${pkg.version}`);
|
|
358
|
-
process.exit(0);
|
|
359
|
-
}
|
|
360
190
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
Ono - A lightweight JSX library for static site generation
|
|
191
|
+
function showHelp() {
|
|
192
|
+
console.log(`
|
|
193
|
+
Ono - Minimalist SSG Framework
|
|
365
194
|
|
|
366
195
|
Usage:
|
|
367
|
-
ono
|
|
368
|
-
ono build <file|dir> [options] Build JSX file(s) to HTML
|
|
369
|
-
ono dev <file|dir> [options] Start dev server with live reload
|
|
196
|
+
ono <command> [options]
|
|
370
197
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
198
|
+
Commands:
|
|
199
|
+
build [input] Build JSX files to static HTML
|
|
200
|
+
input: file or directory (default: pages)
|
|
201
|
+
|
|
202
|
+
dev [input] Build, watch, and serve with live reload
|
|
203
|
+
input: file or directory (default: pages)
|
|
375
204
|
|
|
376
205
|
Options:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
-h, --help Show this help message
|
|
381
|
-
-v, --version Show version number
|
|
206
|
+
--output <dir> Output directory (default: dist)
|
|
207
|
+
--port <number> Server port (default: 3000)
|
|
208
|
+
--help Show this help message
|
|
382
209
|
|
|
383
210
|
Examples:
|
|
384
|
-
|
|
385
|
-
ono
|
|
386
|
-
ono
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
ono build example/index.jsx
|
|
390
|
-
ono build example/index.jsx --watch
|
|
391
|
-
ono dev example/index.jsx
|
|
392
|
-
|
|
393
|
-
# Pages mode (build all .jsx files in directory)
|
|
394
|
-
ono build pages
|
|
395
|
-
ono build pages --watch
|
|
396
|
-
ono dev pages
|
|
397
|
-
ono dev # Same as: ono dev pages
|
|
398
|
-
|
|
399
|
-
# Custom options
|
|
400
|
-
ono build pages -o public
|
|
401
|
-
ono dev pages -p 8080
|
|
402
|
-
`);
|
|
403
|
-
process.exit(0);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
const command = opts._[0];
|
|
407
|
-
const inputFile = opts._[1];
|
|
408
|
-
|
|
409
|
-
if (command === "init") {
|
|
410
|
-
const projectDir = inputFile || ".";
|
|
411
|
-
const projectPath = path.resolve(process.cwd(), projectDir);
|
|
412
|
-
|
|
413
|
-
try {
|
|
414
|
-
// Check if directory exists and is not empty
|
|
415
|
-
try {
|
|
416
|
-
const files = await fs.readdir(projectPath);
|
|
417
|
-
if (files.length > 0 && projectDir !== ".") {
|
|
418
|
-
console.error(`Error: Directory ${projectDir} is not empty`);
|
|
419
|
-
process.exit(1);
|
|
420
|
-
}
|
|
421
|
-
} catch (error) {
|
|
422
|
-
if (error.code === "ENOENT") {
|
|
423
|
-
// Directory doesn't exist, create it
|
|
424
|
-
await fs.mkdir(projectPath, { recursive: true });
|
|
425
|
-
} else {
|
|
426
|
-
throw error;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
console.log(`\nš Initializing Ono project in ${projectDir === "." ? "current directory" : projectDir}...\n`);
|
|
431
|
-
|
|
432
|
-
// Create directory structure
|
|
433
|
-
await fs.mkdir(path.join(projectPath, "pages"), { recursive: true });
|
|
434
|
-
await fs.mkdir(path.join(projectPath, "components"), { recursive: true });
|
|
435
|
-
await fs.mkdir(path.join(projectPath, "public", "css"), { recursive: true });
|
|
436
|
-
|
|
437
|
-
// Create Layout.jsx
|
|
438
|
-
const layoutContent = `// Layout component with slot support
|
|
439
|
-
export default function Layout(props) {
|
|
440
|
-
return (
|
|
441
|
-
<html lang="en">
|
|
442
|
-
<head>
|
|
443
|
-
<meta charset="UTF-8" />
|
|
444
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
445
|
-
<title>{props.title || "Ono Site"}</title>
|
|
446
|
-
<link rel="stylesheet" href="/uno.css" />
|
|
447
|
-
<link rel="stylesheet" href="/css/style.css" />
|
|
448
|
-
</head>
|
|
449
|
-
<body class="font-sans max-w-800px mx-auto px-8 py-8 leading-relaxed">
|
|
450
|
-
<header class="mb-8 pb-4 border-b-2 border-gray-200">
|
|
451
|
-
<h1 class="text-3xl font-bold">{props.title}</h1>
|
|
452
|
-
{props.header}
|
|
453
|
-
</header>
|
|
454
|
-
<main>
|
|
455
|
-
{props.children}
|
|
456
|
-
</main>
|
|
457
|
-
<footer class="mt-12 pt-4 border-t border-gray-200 text-secondary text-sm">
|
|
458
|
-
{props.footer || <p>Ā© 2025 Ono</p>}
|
|
459
|
-
</footer>
|
|
460
|
-
</body>
|
|
461
|
-
</html>
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
`;
|
|
465
|
-
await fs.writeFile(path.join(projectPath, "components", "Layout.jsx"), layoutContent);
|
|
466
|
-
|
|
467
|
-
// Create Hello.jsx component
|
|
468
|
-
const helloContent = `export default function Hello(props) {
|
|
469
|
-
return (
|
|
470
|
-
<div class="p-6 bg-blue-50 rounded-lg border-2 border-blue-200">
|
|
471
|
-
<h2 class="text-2xl font-semibold text-blue-800 mb-2">
|
|
472
|
-
Hello, {props.name || "World"}!
|
|
473
|
-
</h2>
|
|
474
|
-
<p class="text-blue-600">
|
|
475
|
-
This is a reusable component. Edit components/Hello.jsx to customize it.
|
|
476
|
-
</p>
|
|
477
|
-
</div>
|
|
478
|
-
);
|
|
211
|
+
ono build Build all pages in pages/ directory
|
|
212
|
+
ono build pages/index.jsx Build a single file
|
|
213
|
+
ono dev Start dev server with live reload
|
|
214
|
+
ono dev --port 8080 Start dev server on port 8080
|
|
215
|
+
`);
|
|
479
216
|
}
|
|
480
|
-
`;
|
|
481
|
-
await fs.writeFile(path.join(projectPath, "components", "Hello.jsx"), helloContent);
|
|
482
|
-
|
|
483
|
-
// Create index.jsx
|
|
484
|
-
const indexContent = `import Layout from "../components/Layout.jsx";
|
|
485
|
-
import Hello from "../components/Hello.jsx";
|
|
486
|
-
|
|
487
|
-
export default function Home() {
|
|
488
|
-
return (
|
|
489
|
-
<Layout title="Welcome to Ono">
|
|
490
|
-
<Hello name="Developer" />
|
|
491
|
-
|
|
492
|
-
<div class="mt-8 space-y-6">
|
|
493
|
-
<section>
|
|
494
|
-
<h2 class="text-2xl font-bold mb-4">Getting Started</h2>
|
|
495
|
-
<p class="mb-4 text-lg">
|
|
496
|
-
You've successfully initialized an Ono project! Here's what you can do next:
|
|
497
|
-
</p>
|
|
498
|
-
<ul class="list-disc ml-6 space-y-2">
|
|
499
|
-
<li>Edit <code class="bg-gray-100 px-2 py-1 rounded text-sm">pages/index.jsx</code> to customize this page</li>
|
|
500
|
-
<li>Create new pages in the <code class="bg-gray-100 px-2 py-1 rounded text-sm">pages/</code> directory</li>
|
|
501
|
-
<li>Add components to <code class="bg-gray-100 px-2 py-1 rounded text-sm">components/</code></li>
|
|
502
|
-
<li>Put static assets in <code class="bg-gray-100 px-2 py-1 rounded text-sm">public/</code></li>
|
|
503
|
-
</ul>
|
|
504
|
-
</section>
|
|
505
|
-
|
|
506
|
-
<section>
|
|
507
|
-
<h2 class="text-2xl font-bold mb-4">Commands</h2>
|
|
508
|
-
<div class="space-y-2">
|
|
509
|
-
<div class="p-4 bg-gray-50 rounded border border-gray-200">
|
|
510
|
-
<code class="text-green-600 font-semibold">ono dev</code>
|
|
511
|
-
<p class="text-sm text-secondary mt-1">Start development server with live reload</p>
|
|
512
|
-
</div>
|
|
513
|
-
<div class="p-4 bg-gray-50 rounded border border-gray-200">
|
|
514
|
-
<code class="text-green-600 font-semibold">ono build pages</code>
|
|
515
|
-
<p class="text-sm text-secondary mt-1">Build your site to the dist/ directory</p>
|
|
516
|
-
</div>
|
|
517
|
-
</div>
|
|
518
|
-
</section>
|
|
519
|
-
|
|
520
|
-
<section>
|
|
521
|
-
<h2 class="text-2xl font-bold mb-4">Features</h2>
|
|
522
|
-
<div class="grid grid-cols-2 gap-4">
|
|
523
|
-
<div class="p-4 border border-gray-200 rounded">
|
|
524
|
-
<h3 class="font-semibold mb-2">šØ UnoCSS</h3>
|
|
525
|
-
<p class="text-sm text-secondary">Atomic CSS with Tailwind-compatible utilities</p>
|
|
526
|
-
</div>
|
|
527
|
-
<div class="p-4 border border-gray-200 rounded">
|
|
528
|
-
<h3 class="font-semibold mb-2">ā” Live Reload</h3>
|
|
529
|
-
<p class="text-sm text-secondary">Instant updates during development</p>
|
|
530
|
-
</div>
|
|
531
|
-
<div class="p-4 border border-gray-200 rounded">
|
|
532
|
-
<h3 class="font-semibold mb-2">š¦ Component Based</h3>
|
|
533
|
-
<p class="text-sm text-secondary">Reusable JSX components</p>
|
|
534
|
-
</div>
|
|
535
|
-
<div class="p-4 border border-gray-200 rounded">
|
|
536
|
-
<h3 class="font-semibold mb-2">š Static Output</h3>
|
|
537
|
-
<p class="text-sm text-secondary">Fast, SEO-friendly HTML</p>
|
|
538
|
-
</div>
|
|
539
|
-
</div>
|
|
540
|
-
</section>
|
|
541
|
-
</div>
|
|
542
|
-
</Layout>
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
`;
|
|
546
|
-
await fs.writeFile(path.join(projectPath, "pages", "index.jsx"), indexContent);
|
|
547
217
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
218
|
+
// Main CLI handler
|
|
219
|
+
(async () => {
|
|
220
|
+
try {
|
|
221
|
+
if (!command || command === "--help" || command === "-h") {
|
|
222
|
+
showHelp();
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
551
225
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
226
|
+
switch (command) {
|
|
227
|
+
case "build":
|
|
228
|
+
await runBuildCommand();
|
|
229
|
+
break;
|
|
556
230
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
// Create .gitignore
|
|
562
|
-
const gitignoreContent = `# Build output
|
|
563
|
-
dist/
|
|
564
|
-
.mini-jsx-tmp.js
|
|
565
|
-
|
|
566
|
-
# Dependencies
|
|
567
|
-
node_modules/
|
|
568
|
-
|
|
569
|
-
# Environment variables
|
|
570
|
-
.env
|
|
571
|
-
.env.local
|
|
572
|
-
`;
|
|
573
|
-
await fs.writeFile(path.join(projectPath, ".gitignore"), gitignoreContent);
|
|
574
|
-
|
|
575
|
-
// Create package.json
|
|
576
|
-
const projectName = projectDir === "." ? path.basename(projectPath) : projectDir;
|
|
577
|
-
const packageJsonContent = {
|
|
578
|
-
name: projectName,
|
|
579
|
-
version: "0.1.0",
|
|
580
|
-
type: "module",
|
|
581
|
-
scripts: {
|
|
582
|
-
dev: "ono dev",
|
|
583
|
-
build: "ono build pages",
|
|
584
|
-
"build:watch": "ono build pages --watch"
|
|
585
|
-
},
|
|
586
|
-
devDependencies: {}
|
|
587
|
-
};
|
|
588
|
-
await fs.writeFile(
|
|
589
|
-
path.join(projectPath, "package.json"),
|
|
590
|
-
JSON.stringify(packageJsonContent, null, 2) + "\n"
|
|
591
|
-
);
|
|
231
|
+
case "dev":
|
|
232
|
+
await runDevCommand();
|
|
233
|
+
break;
|
|
592
234
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
console.log(" components/");
|
|
597
|
-
console.log(" āāā Layout.jsx");
|
|
598
|
-
console.log(" āāā Hello.jsx");
|
|
599
|
-
console.log(" public/");
|
|
600
|
-
console.log(" āāā css/");
|
|
601
|
-
console.log(" ā āāā style.css");
|
|
602
|
-
console.log(" package.json");
|
|
603
|
-
console.log(" .gitignore");
|
|
604
|
-
console.log("");
|
|
605
|
-
console.log("š Project initialized successfully!");
|
|
606
|
-
console.log("");
|
|
607
|
-
console.log("Next steps:");
|
|
608
|
-
if (projectDir !== ".") {
|
|
609
|
-
console.log(` cd ${projectDir}`);
|
|
610
|
-
}
|
|
611
|
-
console.log(" npm run dev # Start development server");
|
|
612
|
-
console.log(" npm run build # Build for production");
|
|
613
|
-
console.log("");
|
|
614
|
-
console.log("Or use ono directly:");
|
|
615
|
-
console.log(" ono dev # Start development server");
|
|
616
|
-
console.log(" ono build pages # Build for production");
|
|
617
|
-
console.log("");
|
|
618
|
-
} catch (error) {
|
|
619
|
-
console.error(`Error: ${error.message}`);
|
|
620
|
-
if (error.stack) {
|
|
621
|
-
console.error(error.stack);
|
|
622
|
-
}
|
|
623
|
-
process.exit(1);
|
|
624
|
-
}
|
|
625
|
-
} else if (command === "build") {
|
|
626
|
-
const buildOptions = {
|
|
627
|
-
outputDir: opts.output || "dist",
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
try {
|
|
631
|
-
// Check if inputFile is a directory (pages mode)
|
|
632
|
-
const isDirectory = inputFile && (await fs.stat(path.resolve(process.cwd(), inputFile)).catch(() => null))?.isDirectory();
|
|
633
|
-
|
|
634
|
-
if (isDirectory || inputFile === "pages" || !inputFile) {
|
|
635
|
-
// Pages mode - build all pages in directory
|
|
636
|
-
const pagesDir = inputFile || "pages";
|
|
637
|
-
const pagesDirAbs = path.resolve(process.cwd(), pagesDir);
|
|
638
|
-
|
|
639
|
-
const pages = await discoverPages(pagesDirAbs);
|
|
640
|
-
|
|
641
|
-
if (pages.length === 0) {
|
|
642
|
-
console.error(`Error: No JSX files found in ${pagesDir}/`);
|
|
643
|
-
process.exit(1);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
console.log(`Found ${pages.length} page(s) in ${pagesDir}/\n`);
|
|
647
|
-
|
|
648
|
-
if (opts.watch) {
|
|
649
|
-
// Initial build all pages
|
|
650
|
-
for (const page of pages) {
|
|
651
|
-
await buildFile(page, { ...buildOptions, pagesDir: pagesDirAbs });
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Copy public files
|
|
655
|
-
const publicCount = await copyPublicFiles("public", buildOptions.outputDir);
|
|
656
|
-
if (publicCount > 0) {
|
|
657
|
-
console.log(`\nš¦ Copied ${publicCount} file(s) from public/\n`);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// Generate UnoCSS
|
|
661
|
-
await generateUnoCSSFile(buildOptions.outputDir);
|
|
662
|
-
|
|
663
|
-
console.log(`š Watching for changes in ${pagesDir}/ and public/...`);
|
|
664
|
-
console.log("Press Ctrl+C to stop\n");
|
|
665
|
-
|
|
666
|
-
const { watch } = await import("node:fs");
|
|
667
|
-
|
|
668
|
-
// Watch pages directory
|
|
669
|
-
watch(pagesDirAbs, { recursive: true }, async (_eventType, filename) => {
|
|
670
|
-
if (filename && filename.endsWith(".jsx")) {
|
|
671
|
-
const changedFile = path.join(pagesDirAbs, filename);
|
|
672
|
-
try {
|
|
673
|
-
await buildFile(changedFile, { ...buildOptions, pagesDir: pagesDirAbs, silent: true });
|
|
674
|
-
await generateUnoCSSFile(buildOptions.outputDir, true);
|
|
675
|
-
console.log(`ā Rebuilt: ${filename}`);
|
|
676
|
-
} catch (error) {
|
|
677
|
-
console.error(`ā Build error: ${error.message}`);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
|
|
682
|
-
// Watch public directory
|
|
683
|
-
const publicDirAbs = path.resolve(process.cwd(), "public");
|
|
684
|
-
try {
|
|
685
|
-
await fs.access(publicDirAbs);
|
|
686
|
-
watch(publicDirAbs, { recursive: true }, async (_eventType, filename) => {
|
|
687
|
-
if (filename) {
|
|
688
|
-
try {
|
|
689
|
-
await copyPublicFiles("public", buildOptions.outputDir, true);
|
|
690
|
-
console.log(`ā Copied: public/${filename}`);
|
|
691
|
-
} catch (error) {
|
|
692
|
-
console.error(`ā Copy error: ${error.message}`);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
});
|
|
696
|
-
} catch {
|
|
697
|
-
// Public directory doesn't exist, skip watching
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
process.on("SIGINT", () => {
|
|
701
|
-
console.log("\n\nš Shutting down...");
|
|
702
|
-
process.exit(0);
|
|
703
|
-
});
|
|
704
|
-
} else {
|
|
705
|
-
// Build all pages
|
|
706
|
-
for (const page of pages) {
|
|
707
|
-
await buildFile(page, { ...buildOptions, pagesDir: pagesDirAbs });
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// Copy public files
|
|
711
|
-
const publicCount = await copyPublicFiles("public", buildOptions.outputDir);
|
|
712
|
-
if (publicCount > 0) {
|
|
713
|
-
console.log(`\nš¦ Copied ${publicCount} file(s) from public/`);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// Generate UnoCSS
|
|
717
|
-
await generateUnoCSSFile(buildOptions.outputDir);
|
|
718
|
-
}
|
|
719
|
-
} else if (inputFile) {
|
|
720
|
-
// Single file mode
|
|
721
|
-
if (opts.watch) {
|
|
722
|
-
// Initial build
|
|
723
|
-
await buildFile(inputFile, buildOptions);
|
|
724
|
-
|
|
725
|
-
// Generate UnoCSS
|
|
726
|
-
await generateUnoCSSFile(buildOptions.outputDir);
|
|
727
|
-
|
|
728
|
-
// Watch for changes
|
|
729
|
-
const absolutePath = path.resolve(process.cwd(), inputFile);
|
|
730
|
-
const watchDir = path.dirname(absolutePath);
|
|
731
|
-
|
|
732
|
-
console.log(`\nš Watching for changes in ${watchDir}...`);
|
|
733
|
-
console.log("Press Ctrl+C to stop\n");
|
|
734
|
-
|
|
735
|
-
const { watch } = await import("node:fs");
|
|
736
|
-
watch(watchDir, { recursive: true }, async (_eventType, filename) => {
|
|
737
|
-
if (filename && filename.endsWith(".jsx")) {
|
|
738
|
-
try {
|
|
739
|
-
await buildFile(inputFile, { ...buildOptions, silent: true });
|
|
740
|
-
await generateUnoCSSFile(buildOptions.outputDir, true);
|
|
741
|
-
console.log(`ā Rebuilt: ${filename}`);
|
|
742
|
-
} catch (error) {
|
|
743
|
-
console.error(`ā Build error: ${error.message}`);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
});
|
|
747
|
-
|
|
748
|
-
process.on("SIGINT", () => {
|
|
749
|
-
console.log("\n\nš Shutting down...");
|
|
750
|
-
process.exit(0);
|
|
751
|
-
});
|
|
752
|
-
} else {
|
|
753
|
-
// Single build
|
|
754
|
-
await buildFile(inputFile, buildOptions);
|
|
755
|
-
|
|
756
|
-
// Generate UnoCSS
|
|
757
|
-
await generateUnoCSSFile(buildOptions.outputDir);
|
|
758
|
-
}
|
|
759
|
-
} else {
|
|
760
|
-
console.error("Error: Please specify a file or pages directory to build");
|
|
761
|
-
console.error("Usage: mini-jsx build <file|pages> [options]");
|
|
235
|
+
default:
|
|
236
|
+
console.error(`Unknown command: ${command}`);
|
|
237
|
+
console.error('Run "ono --help" for usage information');
|
|
762
238
|
process.exit(1);
|
|
763
|
-
}
|
|
764
|
-
} catch (error) {
|
|
765
|
-
console.error(`Error: ${error.message}`);
|
|
766
|
-
if (error.stack) {
|
|
767
|
-
console.error(error.stack);
|
|
768
|
-
}
|
|
769
|
-
process.exit(1);
|
|
770
239
|
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
try {
|
|
776
|
-
// Check if inputFile is a directory (pages mode)
|
|
777
|
-
const isDirectory = inputFile && (await fs.stat(path.resolve(process.cwd(), inputFile)).catch(() => null))?.isDirectory();
|
|
778
|
-
const isPages = isDirectory || inputFile === "pages" || !inputFile;
|
|
779
|
-
|
|
780
|
-
// Find available ports
|
|
781
|
-
const wsPort = await findAvailablePort(35729);
|
|
782
|
-
const httpPort = await findAvailablePort(requestedPort);
|
|
783
|
-
|
|
784
|
-
if (wsPort !== 35729) {
|
|
785
|
-
console.log(`ā¹ļø WebSocket port 35729 is busy, using port ${wsPort} instead`);
|
|
786
|
-
}
|
|
787
|
-
if (httpPort !== requestedPort) {
|
|
788
|
-
console.log(`ā¹ļø Port ${requestedPort} is busy, using port ${httpPort} instead`);
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
if (isPages) {
|
|
792
|
-
// Pages mode
|
|
793
|
-
const pagesDir = inputFile || "pages";
|
|
794
|
-
const pagesDirAbs = path.resolve(process.cwd(), pagesDir);
|
|
795
|
-
const pages = await discoverPages(pagesDirAbs);
|
|
796
|
-
|
|
797
|
-
if (pages.length === 0) {
|
|
798
|
-
console.error(`Error: No JSX files found in ${pagesDir}/`);
|
|
799
|
-
process.exit(1);
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
console.log(`Found ${pages.length} page(s) in ${pagesDir}/\n`);
|
|
803
|
-
|
|
804
|
-
// Initial build all pages
|
|
805
|
-
for (const page of pages) {
|
|
806
|
-
await buildFile(page, { liveReload: true, wsPort, outputDir, pagesDir: pagesDirAbs });
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// Copy public files
|
|
810
|
-
const publicCount = await copyPublicFiles("public", outputDir);
|
|
811
|
-
if (publicCount > 0) {
|
|
812
|
-
console.log(`\nš¦ Copied ${publicCount} file(s) from public/\n`);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
// Generate UnoCSS
|
|
816
|
-
await generateUnoCSSFile(outputDir);
|
|
817
|
-
|
|
818
|
-
// Setup WebSocket server
|
|
819
|
-
const wss = new WebSocketServer({ port: wsPort });
|
|
820
|
-
const clients = new Set();
|
|
821
|
-
|
|
822
|
-
wss.on("connection", (ws) => {
|
|
823
|
-
clients.add(ws);
|
|
824
|
-
ws.on("close", () => clients.delete(ws));
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
function notifyClients() {
|
|
828
|
-
clients.forEach((client) => {
|
|
829
|
-
if (client.readyState === 1) {
|
|
830
|
-
client.send("reload");
|
|
831
|
-
}
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
// Watch for changes
|
|
836
|
-
console.log(`š Watching for changes in ${pagesDir}/ and public/...`);
|
|
837
|
-
|
|
838
|
-
const { watch } = await import("node:fs");
|
|
839
|
-
|
|
840
|
-
// Watch pages directory
|
|
841
|
-
watch(pagesDirAbs, { recursive: true }, async (_eventType, filename) => {
|
|
842
|
-
if (filename && filename.endsWith(".jsx")) {
|
|
843
|
-
const changedFile = path.join(pagesDirAbs, filename);
|
|
844
|
-
try {
|
|
845
|
-
await buildFile(changedFile, { liveReload: true, silent: true, wsPort, outputDir, pagesDir: pagesDirAbs });
|
|
846
|
-
await generateUnoCSSFile(outputDir, true);
|
|
847
|
-
console.log(`ā Rebuilt: ${filename}`);
|
|
848
|
-
notifyClients();
|
|
849
|
-
} catch (error) {
|
|
850
|
-
console.error(`ā Build error: ${error.message}`);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
|
|
855
|
-
// Watch public directory
|
|
856
|
-
const publicDirAbs = path.resolve(process.cwd(), "public");
|
|
857
|
-
try {
|
|
858
|
-
await fs.access(publicDirAbs);
|
|
859
|
-
watch(publicDirAbs, { recursive: true }, async (_eventType, filename) => {
|
|
860
|
-
if (filename) {
|
|
861
|
-
try {
|
|
862
|
-
await copyPublicFiles("public", outputDir, true);
|
|
863
|
-
console.log(`ā Copied: public/${filename}`);
|
|
864
|
-
notifyClients();
|
|
865
|
-
} catch (error) {
|
|
866
|
-
console.error(`ā Copy error: ${error.message}`);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
});
|
|
870
|
-
} catch {
|
|
871
|
-
// Public directory doesn't exist, skip watching
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// Create HTTP server
|
|
875
|
-
const outDir = path.resolve(process.cwd(), outputDir);
|
|
876
|
-
|
|
877
|
-
const server = http.createServer(async (req, res) => {
|
|
878
|
-
let requestPath = req.url === "/" ? "/index.html" : req.url;
|
|
879
|
-
let filePath = path.join(outDir, requestPath);
|
|
880
|
-
|
|
881
|
-
try {
|
|
882
|
-
const content = await fs.readFile(filePath);
|
|
883
|
-
const ext = path.extname(filePath);
|
|
884
|
-
const contentTypes = {
|
|
885
|
-
".html": "text/html",
|
|
886
|
-
".css": "text/css",
|
|
887
|
-
".js": "text/javascript",
|
|
888
|
-
".json": "application/json",
|
|
889
|
-
".png": "image/png",
|
|
890
|
-
".jpg": "image/jpeg",
|
|
891
|
-
".gif": "image/gif",
|
|
892
|
-
".svg": "image/svg+xml",
|
|
893
|
-
};
|
|
894
|
-
|
|
895
|
-
res.writeHead(200, { "Content-Type": contentTypes[ext] || "text/plain" });
|
|
896
|
-
res.end(content);
|
|
897
|
-
} catch (error) {
|
|
898
|
-
res.writeHead(404);
|
|
899
|
-
res.end("Not found");
|
|
900
|
-
}
|
|
901
|
-
});
|
|
902
|
-
|
|
903
|
-
server.listen(httpPort, () => {
|
|
904
|
-
console.log(`\nš Server running at http://localhost:${httpPort}`);
|
|
905
|
-
console.log(`š Serving: ${pagesDir}/ ā ${outputDir}/\n`);
|
|
906
|
-
});
|
|
907
|
-
} else {
|
|
908
|
-
// Single file mode
|
|
909
|
-
if (!inputFile) {
|
|
910
|
-
console.error("Error: Please specify a file or pages directory to serve");
|
|
911
|
-
console.error("Usage: mini-jsx dev <file|pages> [options]");
|
|
912
|
-
process.exit(1);
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
// Initial build with live reload
|
|
916
|
-
await buildFile(inputFile, { liveReload: true, wsPort, outputDir });
|
|
917
|
-
|
|
918
|
-
// Generate UnoCSS
|
|
919
|
-
await generateUnoCSSFile(outputDir);
|
|
920
|
-
|
|
921
|
-
// Setup WebSocket server for live reload
|
|
922
|
-
const wss = new WebSocketServer({ port: wsPort });
|
|
923
|
-
const clients = new Set();
|
|
924
|
-
|
|
925
|
-
wss.on("connection", (ws) => {
|
|
926
|
-
clients.add(ws);
|
|
927
|
-
ws.on("close", () => clients.delete(ws));
|
|
928
|
-
});
|
|
929
|
-
|
|
930
|
-
function notifyClients() {
|
|
931
|
-
clients.forEach((client) => {
|
|
932
|
-
if (client.readyState === 1) {
|
|
933
|
-
client.send("reload");
|
|
934
|
-
}
|
|
935
|
-
});
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
// Watch for changes
|
|
939
|
-
const absolutePath = path.resolve(process.cwd(), inputFile);
|
|
940
|
-
const watchDir = path.dirname(absolutePath);
|
|
941
|
-
|
|
942
|
-
console.log(`\nš Watching for changes in ${watchDir}...`);
|
|
943
|
-
|
|
944
|
-
const { watch } = await import("node:fs");
|
|
945
|
-
watch(watchDir, { recursive: true }, async (_eventType, filename) => {
|
|
946
|
-
if (filename && filename.endsWith(".jsx")) {
|
|
947
|
-
try {
|
|
948
|
-
await buildFile(inputFile, { liveReload: true, silent: true, wsPort, outputDir });
|
|
949
|
-
await generateUnoCSSFile(outputDir, true);
|
|
950
|
-
console.log(`ā Rebuilt: ${filename}`);
|
|
951
|
-
notifyClients();
|
|
952
|
-
} catch (error) {
|
|
953
|
-
console.error(`ā Build error: ${error.message}`);
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
});
|
|
957
|
-
|
|
958
|
-
// Create HTTP server
|
|
959
|
-
const outDir = path.resolve(process.cwd(), outputDir);
|
|
960
|
-
const inputBasename = path.basename(inputFile, ".jsx");
|
|
961
|
-
const outputFilename = `${inputBasename}.html`;
|
|
962
|
-
const relativeOutput = path.relative(process.cwd(), path.join(outDir, outputFilename));
|
|
963
|
-
|
|
964
|
-
const server = http.createServer(async (req, res) => {
|
|
965
|
-
let filePath = path.join(outDir, req.url === "/" ? outputFilename : req.url);
|
|
966
|
-
|
|
967
|
-
try {
|
|
968
|
-
const content = await fs.readFile(filePath);
|
|
969
|
-
const ext = path.extname(filePath);
|
|
970
|
-
const contentTypes = {
|
|
971
|
-
".html": "text/html",
|
|
972
|
-
".css": "text/css",
|
|
973
|
-
".js": "text/javascript",
|
|
974
|
-
".json": "application/json",
|
|
975
|
-
".png": "image/png",
|
|
976
|
-
".jpg": "image/jpeg",
|
|
977
|
-
".gif": "image/gif",
|
|
978
|
-
".svg": "image/svg+xml",
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
res.writeHead(200, { "Content-Type": contentTypes[ext] || "text/plain" });
|
|
982
|
-
res.end(content);
|
|
983
|
-
} catch (error) {
|
|
984
|
-
res.writeHead(404);
|
|
985
|
-
res.end("Not found");
|
|
986
|
-
}
|
|
987
|
-
});
|
|
988
|
-
|
|
989
|
-
server.listen(httpPort, () => {
|
|
990
|
-
console.log(`\nš Server running at http://localhost:${httpPort}`);
|
|
991
|
-
console.log(`š Serving: ${relativeOutput}\n`);
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
// Keep process running
|
|
996
|
-
process.on("SIGINT", () => {
|
|
997
|
-
console.log("\n\nš Shutting down...");
|
|
998
|
-
process.exit(0);
|
|
999
|
-
});
|
|
1000
|
-
} catch (error) {
|
|
1001
|
-
console.error(`Error: ${error.message}`);
|
|
1002
|
-
if (error.stack) {
|
|
1003
|
-
console.error(error.stack);
|
|
1004
|
-
}
|
|
1005
|
-
process.exit(1);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error("Error:", error.message);
|
|
242
|
+
if (process.env.DEBUG) {
|
|
243
|
+
console.error(error);
|
|
1006
244
|
}
|
|
1007
|
-
} else {
|
|
1008
|
-
console.error(`Unknown command: ${command}`);
|
|
1009
|
-
console.error('Run "mini-jsx --help" for usage information');
|
|
1010
245
|
process.exit(1);
|
|
1011
246
|
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
main();
|
|
247
|
+
})();
|