@hypen-space/cli 0.3.8
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 +157 -0
- package/dist/bin/hypen.js +826 -0
- package/dist/bin/hypen.js.map +13 -0
- package/dist/src/dev-bun.js +266 -0
- package/dist/src/dev-bun.js.map +10 -0
- package/dist/src/dev-node.js +313 -0
- package/dist/src/dev-node.js.map +10 -0
- package/dist/src/dev.js +576 -0
- package/dist/src/dev.js.map +12 -0
- package/dist/src/index.js +580 -0
- package/dist/src/index.js.map +13 -0
- package/package.json +58 -0
- package/templates/counter/hypen.json +15 -0
- package/templates/counter/package.json +18 -0
- package/templates/counter/src/app.ts +16 -0
- package/templates/counter/src/components/App/component.hypen +55 -0
- package/templates/counter/src/components/App/component.ts +21 -0
- package/templates/counter/tsconfig.json +26 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
29
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
|
+
|
|
31
|
+
// src/dev-bun.ts
|
|
32
|
+
var exports_dev_bun = {};
|
|
33
|
+
__export(exports_dev_bun, {
|
|
34
|
+
hypen: () => hypen,
|
|
35
|
+
dev: () => dev,
|
|
36
|
+
default: () => dev_bun_default,
|
|
37
|
+
build: () => build
|
|
38
|
+
});
|
|
39
|
+
import { join, resolve } from "path";
|
|
40
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
41
|
+
import {
|
|
42
|
+
watchComponents,
|
|
43
|
+
generateComponentsCode
|
|
44
|
+
} from "@hypen-space/core/discovery";
|
|
45
|
+
function getDefaultHtmlTemplate(entry) {
|
|
46
|
+
return `<!DOCTYPE html>
|
|
47
|
+
<html lang="en">
|
|
48
|
+
<head>
|
|
49
|
+
<meta charset="UTF-8">
|
|
50
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
51
|
+
<title>Hypen App</title>
|
|
52
|
+
<style>
|
|
53
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
54
|
+
body { font-family: system-ui, -apple-system, sans-serif; }
|
|
55
|
+
</style>
|
|
56
|
+
</head>
|
|
57
|
+
<body>
|
|
58
|
+
<div id="app"></div>
|
|
59
|
+
<script type="module" src="/__hypen__/main.js"></script>
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
function generateMainEntry(entry, componentsPath, debug) {
|
|
65
|
+
return `/**
|
|
66
|
+
* Auto-generated Hypen entry point
|
|
67
|
+
*/
|
|
68
|
+
import { renderWithComponents } from "@hypen-space/core";
|
|
69
|
+
import * as components from "${componentsPath}";
|
|
70
|
+
|
|
71
|
+
const app = await renderWithComponents(
|
|
72
|
+
components,
|
|
73
|
+
"${entry}",
|
|
74
|
+
"#app",
|
|
75
|
+
{ debug: ${debug} }
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Hot reload support
|
|
79
|
+
if (import.meta.hot) {
|
|
80
|
+
import.meta.hot.accept();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default app;
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
async function dev(options) {
|
|
87
|
+
const {
|
|
88
|
+
components: componentsDir,
|
|
89
|
+
entry,
|
|
90
|
+
port = 3000,
|
|
91
|
+
hot = true,
|
|
92
|
+
debug = false,
|
|
93
|
+
htmlTemplate,
|
|
94
|
+
outDir = ".hypen",
|
|
95
|
+
onStart,
|
|
96
|
+
onComponentsChange
|
|
97
|
+
} = options;
|
|
98
|
+
const log = debug ? (...args) => console.log("[hypen:dev]", ...args) : () => {};
|
|
99
|
+
const resolvedComponentsDir = resolve(componentsDir);
|
|
100
|
+
const resolvedOutDir = resolve(outDir);
|
|
101
|
+
if (!existsSync(resolvedOutDir)) {
|
|
102
|
+
mkdirSync(resolvedOutDir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
log("Components directory:", resolvedComponentsDir);
|
|
105
|
+
log("Output directory:", resolvedOutDir);
|
|
106
|
+
const generateComponents = async () => {
|
|
107
|
+
log("Generating components...");
|
|
108
|
+
const code = await generateComponentsCode(resolvedComponentsDir, { debug });
|
|
109
|
+
const componentsPath2 = join(resolvedOutDir, "components.generated.ts");
|
|
110
|
+
writeFileSync(componentsPath2, code);
|
|
111
|
+
log("Generated:", componentsPath2);
|
|
112
|
+
return componentsPath2;
|
|
113
|
+
};
|
|
114
|
+
const generateMain = (componentsPath2) => {
|
|
115
|
+
log("Generating main entry...");
|
|
116
|
+
const code = generateMainEntry(entry, componentsPath2, debug);
|
|
117
|
+
const mainPath2 = join(resolvedOutDir, "main.ts");
|
|
118
|
+
writeFileSync(mainPath2, code);
|
|
119
|
+
log("Generated:", mainPath2);
|
|
120
|
+
return mainPath2;
|
|
121
|
+
};
|
|
122
|
+
const componentsPath = await generateComponents();
|
|
123
|
+
const mainPath = generateMain(componentsPath);
|
|
124
|
+
let watcher = null;
|
|
125
|
+
if (hot) {
|
|
126
|
+
watcher = watchComponents(resolvedComponentsDir, {
|
|
127
|
+
debug,
|
|
128
|
+
onChange: async (components) => {
|
|
129
|
+
log("Components changed, regenerating...");
|
|
130
|
+
await generateComponents();
|
|
131
|
+
onComponentsChange?.(components);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const html = htmlTemplate ? readFileSync(htmlTemplate, "utf-8") : getDefaultHtmlTemplate(entry);
|
|
136
|
+
const server = Bun.serve({
|
|
137
|
+
port,
|
|
138
|
+
async fetch(req) {
|
|
139
|
+
const url = new URL(req.url);
|
|
140
|
+
const pathname = url.pathname;
|
|
141
|
+
log("Request:", pathname);
|
|
142
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
143
|
+
return new Response(html, {
|
|
144
|
+
headers: { "Content-Type": "text/html" }
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (pathname.startsWith("/__hypen__/")) {
|
|
148
|
+
const fileName = pathname.replace("/__hypen__/", "");
|
|
149
|
+
const filePath = join(resolvedOutDir, fileName.replace(/\.js$/, ".ts"));
|
|
150
|
+
if (existsSync(filePath)) {
|
|
151
|
+
const transpiler = new Bun.Transpiler({ loader: "ts" });
|
|
152
|
+
const code = readFileSync(filePath, "utf-8");
|
|
153
|
+
const js = transpiler.transformSync(code);
|
|
154
|
+
return new Response(js, {
|
|
155
|
+
headers: { "Content-Type": "application/javascript" }
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (pathname.endsWith(".ts") || pathname.endsWith(".js")) {
|
|
160
|
+
const possiblePaths = [
|
|
161
|
+
join(resolvedComponentsDir, pathname),
|
|
162
|
+
join(process.cwd(), pathname.slice(1))
|
|
163
|
+
];
|
|
164
|
+
for (const filePath of possiblePaths) {
|
|
165
|
+
if (existsSync(filePath)) {
|
|
166
|
+
const transpiler = new Bun.Transpiler({ loader: "ts" });
|
|
167
|
+
const code = readFileSync(filePath, "utf-8");
|
|
168
|
+
const js = transpiler.transformSync(code);
|
|
169
|
+
return new Response(js, {
|
|
170
|
+
headers: { "Content-Type": "application/javascript" }
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return new Response("Not Found", { status: 404 });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
const serverUrl = `http://localhost:${port}`;
|
|
179
|
+
console.log(`
|
|
180
|
+
Hypen Dev Server
|
|
181
|
+
`);
|
|
182
|
+
console.log(` Local: ${serverUrl}`);
|
|
183
|
+
console.log(` Entry: ${entry}`);
|
|
184
|
+
console.log(` Components: ${resolvedComponentsDir}
|
|
185
|
+
`);
|
|
186
|
+
onStart?.(serverUrl);
|
|
187
|
+
return {
|
|
188
|
+
url: serverUrl,
|
|
189
|
+
stop: () => {
|
|
190
|
+
watcher?.stop();
|
|
191
|
+
server.stop();
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
async function build(options) {
|
|
196
|
+
const {
|
|
197
|
+
components: componentsDir,
|
|
198
|
+
entry,
|
|
199
|
+
outDir = "dist",
|
|
200
|
+
minify = true,
|
|
201
|
+
sourcemap = false
|
|
202
|
+
} = options;
|
|
203
|
+
const resolvedComponentsDir = resolve(componentsDir);
|
|
204
|
+
const resolvedOutDir = resolve(outDir);
|
|
205
|
+
console.log(`
|
|
206
|
+
Hypen Build
|
|
207
|
+
`);
|
|
208
|
+
console.log(` Entry: ${entry}`);
|
|
209
|
+
console.log(` Components: ${resolvedComponentsDir}`);
|
|
210
|
+
console.log(` Output: ${resolvedOutDir}
|
|
211
|
+
`);
|
|
212
|
+
if (!existsSync(resolvedOutDir)) {
|
|
213
|
+
mkdirSync(resolvedOutDir, { recursive: true });
|
|
214
|
+
}
|
|
215
|
+
const code = await generateComponentsCode(resolvedComponentsDir);
|
|
216
|
+
const tempDir = join(resolvedOutDir, ".temp");
|
|
217
|
+
if (!existsSync(tempDir)) {
|
|
218
|
+
mkdirSync(tempDir, { recursive: true });
|
|
219
|
+
}
|
|
220
|
+
const componentsPath = join(tempDir, "components.generated.ts");
|
|
221
|
+
writeFileSync(componentsPath, code);
|
|
222
|
+
const mainCode = generateMainEntry(entry, componentsPath, false);
|
|
223
|
+
const mainPath = join(tempDir, "main.ts");
|
|
224
|
+
writeFileSync(mainPath, mainCode);
|
|
225
|
+
const result = await Bun.build({
|
|
226
|
+
entrypoints: [mainPath],
|
|
227
|
+
outdir: resolvedOutDir,
|
|
228
|
+
minify,
|
|
229
|
+
sourcemap: sourcemap ? "external" : "none",
|
|
230
|
+
target: "browser",
|
|
231
|
+
format: "esm"
|
|
232
|
+
});
|
|
233
|
+
if (!result.success) {
|
|
234
|
+
console.error("Build failed:");
|
|
235
|
+
for (const log of result.logs) {
|
|
236
|
+
console.error(log);
|
|
237
|
+
}
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
const html = getDefaultHtmlTemplate(entry).replace("/__hypen__/main.js", "./main.js");
|
|
241
|
+
writeFileSync(join(resolvedOutDir, "index.html"), html);
|
|
242
|
+
console.log(` Build complete!
|
|
243
|
+
`);
|
|
244
|
+
console.log(` Files:`);
|
|
245
|
+
for (const output of result.outputs) {
|
|
246
|
+
console.log(` ${output.path}`);
|
|
247
|
+
}
|
|
248
|
+
console.log();
|
|
249
|
+
}
|
|
250
|
+
var hypen, dev_bun_default;
|
|
251
|
+
var init_dev_bun = __esm(() => {
|
|
252
|
+
hypen = {
|
|
253
|
+
dev,
|
|
254
|
+
build
|
|
255
|
+
};
|
|
256
|
+
dev_bun_default = hypen;
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// src/dev-node.ts
|
|
260
|
+
var exports_dev_node = {};
|
|
261
|
+
__export(exports_dev_node, {
|
|
262
|
+
hypen: () => hypen2,
|
|
263
|
+
dev: () => dev2,
|
|
264
|
+
default: () => dev_node_default,
|
|
265
|
+
build: () => build2
|
|
266
|
+
});
|
|
267
|
+
import { createServer } from "http";
|
|
268
|
+
import { join as join2, resolve as resolve2, extname } from "path";
|
|
269
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, watch } from "fs";
|
|
270
|
+
import {
|
|
271
|
+
discoverComponents as discoverComponents2,
|
|
272
|
+
generateComponentsCode as generateComponentsCode2
|
|
273
|
+
} from "@hypen-space/core/discovery";
|
|
274
|
+
async function getEsbuild() {
|
|
275
|
+
if (!esbuild) {
|
|
276
|
+
try {
|
|
277
|
+
esbuild = await import("esbuild");
|
|
278
|
+
} catch {
|
|
279
|
+
throw new Error("esbuild is required for Node.js support. Install it with: npm install esbuild");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return esbuild;
|
|
283
|
+
}
|
|
284
|
+
function getDefaultHtmlTemplate2(entry) {
|
|
285
|
+
return `<!DOCTYPE html>
|
|
286
|
+
<html lang="en">
|
|
287
|
+
<head>
|
|
288
|
+
<meta charset="UTF-8">
|
|
289
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
290
|
+
<title>Hypen App</title>
|
|
291
|
+
<style>
|
|
292
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
293
|
+
body { font-family: system-ui, -apple-system, sans-serif; }
|
|
294
|
+
</style>
|
|
295
|
+
</head>
|
|
296
|
+
<body>
|
|
297
|
+
<div id="app"></div>
|
|
298
|
+
<script type="module" src="/__hypen__/main.js"></script>
|
|
299
|
+
</body>
|
|
300
|
+
</html>
|
|
301
|
+
`;
|
|
302
|
+
}
|
|
303
|
+
function generateMainEntry2(entry, componentsPath, debug) {
|
|
304
|
+
return `/**
|
|
305
|
+
* Auto-generated Hypen entry point
|
|
306
|
+
*/
|
|
307
|
+
import { renderWithComponents } from "@hypen-space/core";
|
|
308
|
+
import * as components from "${componentsPath}";
|
|
309
|
+
|
|
310
|
+
const app = await renderWithComponents(
|
|
311
|
+
components,
|
|
312
|
+
"${entry}",
|
|
313
|
+
"#app",
|
|
314
|
+
{ debug: ${debug} }
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// Hot reload support
|
|
318
|
+
if (import.meta.hot) {
|
|
319
|
+
import.meta.hot.accept();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export default app;
|
|
323
|
+
`;
|
|
324
|
+
}
|
|
325
|
+
async function transpileTS(code, filename) {
|
|
326
|
+
const es = await getEsbuild();
|
|
327
|
+
const result = await es.transform(code, {
|
|
328
|
+
loader: "ts",
|
|
329
|
+
sourcefile: filename,
|
|
330
|
+
format: "esm",
|
|
331
|
+
target: "es2020"
|
|
332
|
+
});
|
|
333
|
+
return result.code;
|
|
334
|
+
}
|
|
335
|
+
async function dev2(options) {
|
|
336
|
+
const {
|
|
337
|
+
components: componentsDir,
|
|
338
|
+
entry,
|
|
339
|
+
port = 3000,
|
|
340
|
+
hot = true,
|
|
341
|
+
debug = false,
|
|
342
|
+
htmlTemplate,
|
|
343
|
+
outDir = ".hypen",
|
|
344
|
+
onStart,
|
|
345
|
+
onComponentsChange
|
|
346
|
+
} = options;
|
|
347
|
+
await getEsbuild();
|
|
348
|
+
const log = debug ? (...args) => console.log("[hypen:dev]", ...args) : () => {};
|
|
349
|
+
const resolvedComponentsDir = resolve2(componentsDir);
|
|
350
|
+
const resolvedOutDir = resolve2(outDir);
|
|
351
|
+
if (!existsSync2(resolvedOutDir)) {
|
|
352
|
+
mkdirSync2(resolvedOutDir, { recursive: true });
|
|
353
|
+
}
|
|
354
|
+
log("Components directory:", resolvedComponentsDir);
|
|
355
|
+
log("Output directory:", resolvedOutDir);
|
|
356
|
+
const generateComponents = async () => {
|
|
357
|
+
log("Generating components...");
|
|
358
|
+
const code = await generateComponentsCode2(resolvedComponentsDir, { debug });
|
|
359
|
+
const componentsPath2 = join2(resolvedOutDir, "components.generated.ts");
|
|
360
|
+
writeFileSync2(componentsPath2, code);
|
|
361
|
+
log("Generated:", componentsPath2);
|
|
362
|
+
return componentsPath2;
|
|
363
|
+
};
|
|
364
|
+
const generateMain = (componentsPath2) => {
|
|
365
|
+
log("Generating main entry...");
|
|
366
|
+
const code = generateMainEntry2(entry, componentsPath2, debug);
|
|
367
|
+
const mainPath = join2(resolvedOutDir, "main.ts");
|
|
368
|
+
writeFileSync2(mainPath, code);
|
|
369
|
+
log("Generated:", mainPath);
|
|
370
|
+
return mainPath;
|
|
371
|
+
};
|
|
372
|
+
const componentsPath = await generateComponents();
|
|
373
|
+
generateMain(componentsPath);
|
|
374
|
+
let watcher = null;
|
|
375
|
+
if (hot && existsSync2(resolvedComponentsDir)) {
|
|
376
|
+
watcher = watch(resolvedComponentsDir, { recursive: true }, async (eventType, filename) => {
|
|
377
|
+
if (filename && (filename.endsWith(".hypen") || filename.endsWith(".ts"))) {
|
|
378
|
+
log("File changed:", filename);
|
|
379
|
+
await generateComponents();
|
|
380
|
+
const components = await discoverComponents2(resolvedComponentsDir);
|
|
381
|
+
onComponentsChange?.(components);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
const html = htmlTemplate ? readFileSync2(htmlTemplate, "utf-8") : getDefaultHtmlTemplate2(entry);
|
|
386
|
+
const server = createServer(async (req, res) => {
|
|
387
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
388
|
+
const pathname = url.pathname;
|
|
389
|
+
log("Request:", pathname);
|
|
390
|
+
try {
|
|
391
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
392
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
393
|
+
res.end(html);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (pathname.startsWith("/__hypen__/")) {
|
|
397
|
+
const fileName = pathname.replace("/__hypen__/", "");
|
|
398
|
+
const filePath = join2(resolvedOutDir, fileName.replace(/\.js$/, ".ts"));
|
|
399
|
+
if (existsSync2(filePath)) {
|
|
400
|
+
const code = readFileSync2(filePath, "utf-8");
|
|
401
|
+
const js = await transpileTS(code, filePath);
|
|
402
|
+
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
403
|
+
res.end(js);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (pathname.endsWith(".ts") || pathname.endsWith(".js")) {
|
|
408
|
+
const possiblePaths = [
|
|
409
|
+
join2(resolvedComponentsDir, pathname),
|
|
410
|
+
join2(process.cwd(), pathname.slice(1))
|
|
411
|
+
];
|
|
412
|
+
for (const filePath of possiblePaths) {
|
|
413
|
+
if (existsSync2(filePath)) {
|
|
414
|
+
const code = readFileSync2(filePath, "utf-8");
|
|
415
|
+
const js = await transpileTS(code, filePath);
|
|
416
|
+
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
417
|
+
res.end(js);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const ext = extname(pathname);
|
|
423
|
+
if (ext && MIME_TYPES[ext]) {
|
|
424
|
+
const filePath = join2(process.cwd(), pathname.slice(1));
|
|
425
|
+
if (existsSync2(filePath)) {
|
|
426
|
+
const content = readFileSync2(filePath);
|
|
427
|
+
res.writeHead(200, { "Content-Type": MIME_TYPES[ext] });
|
|
428
|
+
res.end(content);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
433
|
+
res.end("Not Found");
|
|
434
|
+
} catch (error) {
|
|
435
|
+
log("Error:", error);
|
|
436
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
437
|
+
res.end(`Server Error: ${error}`);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
server.listen(port);
|
|
441
|
+
const serverUrl = `http://localhost:${port}`;
|
|
442
|
+
console.log(`
|
|
443
|
+
Hypen Dev Server (Node.js)
|
|
444
|
+
`);
|
|
445
|
+
console.log(` Local: ${serverUrl}`);
|
|
446
|
+
console.log(` Entry: ${entry}`);
|
|
447
|
+
console.log(` Components: ${resolvedComponentsDir}
|
|
448
|
+
`);
|
|
449
|
+
onStart?.(serverUrl);
|
|
450
|
+
return {
|
|
451
|
+
url: serverUrl,
|
|
452
|
+
stop: () => {
|
|
453
|
+
watcher?.close();
|
|
454
|
+
server.close();
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
async function build2(options) {
|
|
459
|
+
const {
|
|
460
|
+
components: componentsDir,
|
|
461
|
+
entry,
|
|
462
|
+
outDir = "dist",
|
|
463
|
+
minify = true,
|
|
464
|
+
sourcemap = false
|
|
465
|
+
} = options;
|
|
466
|
+
const es = await getEsbuild();
|
|
467
|
+
const resolvedComponentsDir = resolve2(componentsDir);
|
|
468
|
+
const resolvedOutDir = resolve2(outDir);
|
|
469
|
+
console.log(`
|
|
470
|
+
Hypen Build (Node.js)
|
|
471
|
+
`);
|
|
472
|
+
console.log(` Entry: ${entry}`);
|
|
473
|
+
console.log(` Components: ${resolvedComponentsDir}`);
|
|
474
|
+
console.log(` Output: ${resolvedOutDir}
|
|
475
|
+
`);
|
|
476
|
+
if (!existsSync2(resolvedOutDir)) {
|
|
477
|
+
mkdirSync2(resolvedOutDir, { recursive: true });
|
|
478
|
+
}
|
|
479
|
+
const code = await generateComponentsCode2(resolvedComponentsDir);
|
|
480
|
+
const tempDir = join2(resolvedOutDir, ".temp");
|
|
481
|
+
if (!existsSync2(tempDir)) {
|
|
482
|
+
mkdirSync2(tempDir, { recursive: true });
|
|
483
|
+
}
|
|
484
|
+
const componentsPath = join2(tempDir, "components.generated.ts");
|
|
485
|
+
writeFileSync2(componentsPath, code);
|
|
486
|
+
const mainCode = generateMainEntry2(entry, componentsPath, false);
|
|
487
|
+
const mainPath = join2(tempDir, "main.ts");
|
|
488
|
+
writeFileSync2(mainPath, mainCode);
|
|
489
|
+
try {
|
|
490
|
+
await es.build({
|
|
491
|
+
entryPoints: [mainPath],
|
|
492
|
+
bundle: true,
|
|
493
|
+
outdir: resolvedOutDir,
|
|
494
|
+
minify,
|
|
495
|
+
sourcemap,
|
|
496
|
+
format: "esm",
|
|
497
|
+
target: "es2020",
|
|
498
|
+
platform: "browser"
|
|
499
|
+
});
|
|
500
|
+
} catch (error) {
|
|
501
|
+
console.error("Build failed:", error);
|
|
502
|
+
process.exit(1);
|
|
503
|
+
}
|
|
504
|
+
const html = getDefaultHtmlTemplate2(entry).replace("/__hypen__/main.js", "./main.js");
|
|
505
|
+
writeFileSync2(join2(resolvedOutDir, "index.html"), html);
|
|
506
|
+
console.log(` Build complete!
|
|
507
|
+
`);
|
|
508
|
+
console.log(` Files:`);
|
|
509
|
+
console.log(` ${join2(resolvedOutDir, "main.js")}`);
|
|
510
|
+
console.log(` ${join2(resolvedOutDir, "index.html")}`);
|
|
511
|
+
console.log();
|
|
512
|
+
}
|
|
513
|
+
var esbuild = null, MIME_TYPES, hypen2, dev_node_default;
|
|
514
|
+
var init_dev_node = __esm(() => {
|
|
515
|
+
MIME_TYPES = {
|
|
516
|
+
".html": "text/html",
|
|
517
|
+
".js": "application/javascript",
|
|
518
|
+
".mjs": "application/javascript",
|
|
519
|
+
".ts": "application/javascript",
|
|
520
|
+
".css": "text/css",
|
|
521
|
+
".json": "application/json",
|
|
522
|
+
".wasm": "application/wasm",
|
|
523
|
+
".png": "image/png",
|
|
524
|
+
".jpg": "image/jpeg",
|
|
525
|
+
".svg": "image/svg+xml"
|
|
526
|
+
};
|
|
527
|
+
hypen2 = {
|
|
528
|
+
dev: dev2,
|
|
529
|
+
build: build2
|
|
530
|
+
};
|
|
531
|
+
dev_node_default = hypen2;
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// src/dev.ts
|
|
535
|
+
var exports_dev = {};
|
|
536
|
+
__export(exports_dev, {
|
|
537
|
+
hypen: () => hypen3,
|
|
538
|
+
dev: () => dev3,
|
|
539
|
+
default: () => dev_default,
|
|
540
|
+
build: () => build3
|
|
541
|
+
});
|
|
542
|
+
function isBun() {
|
|
543
|
+
return typeof globalThis.Bun !== "undefined";
|
|
544
|
+
}
|
|
545
|
+
async function getImplementation() {
|
|
546
|
+
if (isBun()) {
|
|
547
|
+
return Promise.resolve().then(() => (init_dev_bun(), exports_dev_bun));
|
|
548
|
+
} else {
|
|
549
|
+
return Promise.resolve().then(() => (init_dev_node(), exports_dev_node));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
async function dev3(options) {
|
|
553
|
+
const impl = await getImplementation();
|
|
554
|
+
return impl.dev(options);
|
|
555
|
+
}
|
|
556
|
+
async function build3(options) {
|
|
557
|
+
const impl = await getImplementation();
|
|
558
|
+
return impl.build(options);
|
|
559
|
+
}
|
|
560
|
+
var hypen3, dev_default;
|
|
561
|
+
var init_dev = __esm(() => {
|
|
562
|
+
hypen3 = {
|
|
563
|
+
dev: dev3,
|
|
564
|
+
build: build3
|
|
565
|
+
};
|
|
566
|
+
dev_default = hypen3;
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// bin/hypen.ts
|
|
570
|
+
import { parseArgs } from "util";
|
|
571
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
572
|
+
import { join as join3, resolve as resolve3 } from "path";
|
|
573
|
+
var isBun2 = typeof globalThis.Bun !== "undefined";
|
|
574
|
+
var VERSION = "0.2.12";
|
|
575
|
+
var HELP = `
|
|
576
|
+
hypen - Declarative UI framework CLI
|
|
577
|
+
|
|
578
|
+
Usage:
|
|
579
|
+
hypen <command> [options]
|
|
580
|
+
|
|
581
|
+
Commands:
|
|
582
|
+
init [name] Create a new Hypen project
|
|
583
|
+
dev Start development server
|
|
584
|
+
build Build for production
|
|
585
|
+
generate Generate component imports
|
|
586
|
+
|
|
587
|
+
Options:
|
|
588
|
+
-h, --help Show this help message
|
|
589
|
+
-v, --version Show version number
|
|
590
|
+
|
|
591
|
+
Examples:
|
|
592
|
+
hypen init my-app
|
|
593
|
+
hypen dev --port 3000
|
|
594
|
+
hypen build --minify
|
|
595
|
+
`;
|
|
596
|
+
async function loadConfig() {
|
|
597
|
+
const configPath = resolve3("hypen.config.ts");
|
|
598
|
+
if (existsSync3(configPath)) {
|
|
599
|
+
const config = await import(configPath);
|
|
600
|
+
return config.default || config;
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
components: "./src/components",
|
|
604
|
+
entry: "App",
|
|
605
|
+
port: 3000,
|
|
606
|
+
outDir: "dist"
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
async function initProject(name) {
|
|
610
|
+
const projectDir = name ? resolve3(name) : process.cwd();
|
|
611
|
+
const projectName = name || "hypen-app";
|
|
612
|
+
console.log(`
|
|
613
|
+
Creating Hypen project: ${projectName}
|
|
614
|
+
`);
|
|
615
|
+
const dirs = [
|
|
616
|
+
projectDir,
|
|
617
|
+
join3(projectDir, "src"),
|
|
618
|
+
join3(projectDir, "src/components"),
|
|
619
|
+
join3(projectDir, "src/components/App")
|
|
620
|
+
];
|
|
621
|
+
for (const dir of dirs) {
|
|
622
|
+
if (!existsSync3(dir)) {
|
|
623
|
+
mkdirSync3(dir, { recursive: true });
|
|
624
|
+
console.log(` Created: ${dir}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
const packageJson = {
|
|
628
|
+
name: projectName,
|
|
629
|
+
version: "0.1.0",
|
|
630
|
+
type: "module",
|
|
631
|
+
scripts: {
|
|
632
|
+
dev: "hypen dev",
|
|
633
|
+
build: "hypen build",
|
|
634
|
+
start: "node dist/main.js"
|
|
635
|
+
},
|
|
636
|
+
dependencies: {
|
|
637
|
+
"@hypen-space/core": "^0.2.12",
|
|
638
|
+
"@hypen-space/web": "^0.2.12"
|
|
639
|
+
},
|
|
640
|
+
devDependencies: {
|
|
641
|
+
"@hypen-space/cli": "^0.2.12",
|
|
642
|
+
esbuild: "^0.20.0",
|
|
643
|
+
typescript: "^5.0.0"
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
writeFileSync3(join3(projectDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
647
|
+
console.log(` Created: package.json`);
|
|
648
|
+
const configTs = `/**
|
|
649
|
+
* Hypen configuration
|
|
650
|
+
*/
|
|
651
|
+
export default {
|
|
652
|
+
components: "./src/components",
|
|
653
|
+
entry: "App",
|
|
654
|
+
port: 3000,
|
|
655
|
+
outDir: "dist",
|
|
656
|
+
};
|
|
657
|
+
`;
|
|
658
|
+
writeFileSync3(join3(projectDir, "hypen.config.ts"), configTs);
|
|
659
|
+
console.log(` Created: hypen.config.ts`);
|
|
660
|
+
const tsConfig = {
|
|
661
|
+
compilerOptions: {
|
|
662
|
+
target: "ESNext",
|
|
663
|
+
module: "ESNext",
|
|
664
|
+
moduleResolution: "bundler",
|
|
665
|
+
strict: true,
|
|
666
|
+
esModuleInterop: true,
|
|
667
|
+
skipLibCheck: true,
|
|
668
|
+
noEmit: true
|
|
669
|
+
},
|
|
670
|
+
include: ["src/**/*", "hypen.config.ts"]
|
|
671
|
+
};
|
|
672
|
+
writeFileSync3(join3(projectDir, "tsconfig.json"), JSON.stringify(tsConfig, null, 2));
|
|
673
|
+
console.log(` Created: tsconfig.json`);
|
|
674
|
+
const appModule = `import { app } from "@hypen-space/core";
|
|
675
|
+
|
|
676
|
+
type AppState = {
|
|
677
|
+
count: number;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
export default app
|
|
681
|
+
.defineState<AppState>({ count: 0 })
|
|
682
|
+
.onAction("increment", ({ state }) => {
|
|
683
|
+
state.count++;
|
|
684
|
+
})
|
|
685
|
+
.onAction("decrement", ({ state }) => {
|
|
686
|
+
state.count--;
|
|
687
|
+
})
|
|
688
|
+
.build();
|
|
689
|
+
`;
|
|
690
|
+
writeFileSync3(join3(projectDir, "src/components/App/component.ts"), appModule);
|
|
691
|
+
console.log(` Created: src/components/App/component.ts`);
|
|
692
|
+
const appTemplate = `module App {
|
|
693
|
+
Column {
|
|
694
|
+
Text("Hypen Counter")
|
|
695
|
+
.fontSize(24)
|
|
696
|
+
.fontWeight("bold")
|
|
697
|
+
.color("#333")
|
|
698
|
+
|
|
699
|
+
Text("Count: \${state.count}")
|
|
700
|
+
.fontSize(48)
|
|
701
|
+
.fontWeight("bold")
|
|
702
|
+
.color("#007bff")
|
|
703
|
+
|
|
704
|
+
Row {
|
|
705
|
+
Button {
|
|
706
|
+
Text("-")
|
|
707
|
+
}
|
|
708
|
+
.onClick("@actions.decrement")
|
|
709
|
+
.padding(16)
|
|
710
|
+
.fontSize(24)
|
|
711
|
+
|
|
712
|
+
Button {
|
|
713
|
+
Text("+")
|
|
714
|
+
}
|
|
715
|
+
.onClick("@actions.increment")
|
|
716
|
+
.padding(16)
|
|
717
|
+
.fontSize(24)
|
|
718
|
+
}
|
|
719
|
+
.gap(16)
|
|
720
|
+
}
|
|
721
|
+
.padding(32)
|
|
722
|
+
.gap(24)
|
|
723
|
+
.alignItems("center")
|
|
724
|
+
}
|
|
725
|
+
`;
|
|
726
|
+
writeFileSync3(join3(projectDir, "src/components/App/component.hypen"), appTemplate);
|
|
727
|
+
console.log(` Created: src/components/App/component.hypen`);
|
|
728
|
+
const pm = isBun2 ? "bun" : "npm";
|
|
729
|
+
console.log(`
|
|
730
|
+
Done! To get started:
|
|
731
|
+
|
|
732
|
+
${name ? `cd ${name}` : ""}
|
|
733
|
+
${pm} install
|
|
734
|
+
${pm} run dev
|
|
735
|
+
|
|
736
|
+
`);
|
|
737
|
+
}
|
|
738
|
+
async function devServer(options) {
|
|
739
|
+
const config = await loadConfig();
|
|
740
|
+
const { dev: dev4 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
|
|
741
|
+
await dev4({
|
|
742
|
+
components: config.components,
|
|
743
|
+
entry: config.entry,
|
|
744
|
+
port: options.port || config.port || 3000,
|
|
745
|
+
debug: options.debug || false,
|
|
746
|
+
hot: true
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
async function buildProject(options) {
|
|
750
|
+
const config = await loadConfig();
|
|
751
|
+
const { build: build4 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
|
|
752
|
+
await build4({
|
|
753
|
+
components: config.components,
|
|
754
|
+
entry: config.entry,
|
|
755
|
+
outDir: options.outDir || config.outDir || "dist",
|
|
756
|
+
minify: options.minify ?? true,
|
|
757
|
+
sourcemap: options.sourcemap ?? false
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
async function generateComponents() {
|
|
761
|
+
const config = await loadConfig();
|
|
762
|
+
const { generateComponentsCode: generateComponentsCode3 } = await import("@hypen-space/core/discovery");
|
|
763
|
+
const code = await generateComponentsCode3(config.components);
|
|
764
|
+
const outputPath = resolve3(".hypen/components.generated.ts");
|
|
765
|
+
const outputDir = resolve3(".hypen");
|
|
766
|
+
if (!existsSync3(outputDir)) {
|
|
767
|
+
mkdirSync3(outputDir, { recursive: true });
|
|
768
|
+
}
|
|
769
|
+
writeFileSync3(outputPath, code);
|
|
770
|
+
console.log(`
|
|
771
|
+
Generated: ${outputPath}
|
|
772
|
+
`);
|
|
773
|
+
}
|
|
774
|
+
var { values, positionals } = parseArgs({
|
|
775
|
+
args: process.argv.slice(2),
|
|
776
|
+
options: {
|
|
777
|
+
help: { type: "boolean", short: "h" },
|
|
778
|
+
version: { type: "boolean", short: "v" },
|
|
779
|
+
port: { type: "string", short: "p" },
|
|
780
|
+
debug: { type: "boolean", short: "d" },
|
|
781
|
+
outDir: { type: "string", short: "o" },
|
|
782
|
+
minify: { type: "boolean", short: "m" },
|
|
783
|
+
sourcemap: { type: "boolean", short: "s" }
|
|
784
|
+
},
|
|
785
|
+
allowPositionals: true
|
|
786
|
+
});
|
|
787
|
+
if (values.help) {
|
|
788
|
+
console.log(HELP);
|
|
789
|
+
process.exit(0);
|
|
790
|
+
}
|
|
791
|
+
if (values.version) {
|
|
792
|
+
console.log(`hypen v${VERSION}`);
|
|
793
|
+
process.exit(0);
|
|
794
|
+
}
|
|
795
|
+
var command = positionals[0];
|
|
796
|
+
switch (command) {
|
|
797
|
+
case "init":
|
|
798
|
+
await initProject(positionals[1]);
|
|
799
|
+
break;
|
|
800
|
+
case "dev":
|
|
801
|
+
await devServer({
|
|
802
|
+
port: values.port ? parseInt(values.port) : undefined,
|
|
803
|
+
debug: values.debug
|
|
804
|
+
});
|
|
805
|
+
break;
|
|
806
|
+
case "build":
|
|
807
|
+
await buildProject({
|
|
808
|
+
outDir: values.outDir,
|
|
809
|
+
minify: values.minify,
|
|
810
|
+
sourcemap: values.sourcemap
|
|
811
|
+
});
|
|
812
|
+
break;
|
|
813
|
+
case "generate":
|
|
814
|
+
await generateComponents();
|
|
815
|
+
break;
|
|
816
|
+
default:
|
|
817
|
+
if (command) {
|
|
818
|
+
console.error(`
|
|
819
|
+
Unknown command: ${command}
|
|
820
|
+
`);
|
|
821
|
+
}
|
|
822
|
+
console.log(HELP);
|
|
823
|
+
process.exit(command ? 1 : 0);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
//# debugId=262C8CA5981C00C264756E2164756E21
|