lucynui 0.1.0 → 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/cli/create.ts +31 -37
- package/package.json +2 -1
- package/template/app.js +6 -0
- package/template/app.ts +4 -0
- package/template/index.html +20 -0
- package/template/lucyn.config.ts +8 -0
- package/template/package.json +16 -0
- package/template/style.css +66 -0
- package/template/tsconfig.json +30 -0
package/cli/create.ts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { closeSync, openSync } from "node:fs";
|
|
2
|
-
import { cp
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
2
|
+
import { cp } from "node:fs/promises";
|
|
4
3
|
import { basename, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
5
|
import * as proc from "./proc";
|
|
6
6
|
import * as ui from "./ui";
|
|
7
7
|
|
|
8
8
|
/** Matches the line the framework logs once the server is listening. */
|
|
9
9
|
const SERVER_READY = /dev server running on http:\/\/localhost:(\d+)/;
|
|
10
10
|
|
|
11
|
-
/**
|
|
11
|
+
/** Files copied from the bundled template into a new project. */
|
|
12
12
|
const TEMPLATE_FILES = ["index.html", "style.css", "app.js", "app.ts", "lucyn.config.ts", "tsconfig.json", ".gitignore"];
|
|
13
13
|
|
|
14
14
|
const bun = () => proc.findBun() ?? "bun";
|
|
15
15
|
|
|
16
|
+
/** Absolute path to the bundled template directory inside this package. */
|
|
17
|
+
function templateDir(): string {
|
|
18
|
+
return fileURLToPath(new URL("../template", import.meta.url));
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
/* ------------------------------------------------------------------ commands */
|
|
17
22
|
|
|
18
23
|
export async function dev(): Promise<never> {
|
|
@@ -37,13 +42,11 @@ export async function create(appName: string): Promise<never> {
|
|
|
37
42
|
|
|
38
43
|
const bunPath = await proc.ensureBun();
|
|
39
44
|
await proc.ensureSystemDeps();
|
|
40
|
-
proc.requireGit();
|
|
41
45
|
|
|
42
|
-
ui.step(1, "
|
|
43
|
-
await
|
|
44
|
-
await rm(join(appName, ".git"), { recursive: true, force: true });
|
|
46
|
+
ui.step(1, "scaffolding the template, hang on");
|
|
47
|
+
await scaffoldInto(join(process.cwd(), appName));
|
|
45
48
|
await pinFramework(appName);
|
|
46
|
-
ui.done("
|
|
49
|
+
ui.done("scaffolding complete — fetching the deps and required builds");
|
|
47
50
|
|
|
48
51
|
ui.step(2, "installing modules including lucynui");
|
|
49
52
|
await proc.runStep(appName, [bunPath, "install"]);
|
|
@@ -66,14 +69,13 @@ export async function create(appName: string): Promise<never> {
|
|
|
66
69
|
export async function init(): Promise<never> {
|
|
67
70
|
const bunPath = await proc.ensureBun();
|
|
68
71
|
await proc.ensureSystemDeps();
|
|
69
|
-
proc.requireGit();
|
|
70
72
|
|
|
71
73
|
if (await isProject(process.cwd())) {
|
|
72
74
|
ui.info("This is already a LucyN UI app — starting dev.");
|
|
73
75
|
} else {
|
|
74
|
-
ui.step(1, "
|
|
76
|
+
ui.step(1, "scaffolding the template, hang on");
|
|
75
77
|
await scaffoldInto(process.cwd());
|
|
76
|
-
ui.done("
|
|
78
|
+
ui.done("scaffolding complete — fetching the deps and required builds");
|
|
77
79
|
|
|
78
80
|
ui.step(2, "installing modules including lucynui");
|
|
79
81
|
await proc.runStep(process.cwd(), [bunPath, "install"]);
|
|
@@ -101,30 +103,33 @@ export function isProject(dir: string) {
|
|
|
101
103
|
|
|
102
104
|
/** Runs a build and throws away the binary — the point is proving it compiles. */
|
|
103
105
|
export async function testBuild(dir: string) {
|
|
106
|
+
const { rm } = await import("node:fs/promises");
|
|
104
107
|
const code = await proc.run(dir, [bun(), proc.frameworkPath("build.ts")]);
|
|
105
108
|
await rm(join(dir, "dist"), { recursive: true, force: true });
|
|
106
109
|
|
|
107
110
|
if (code !== 0) throw new Error("the test build failed");
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Copies the bundled template files into `dir`, never overwriting existing
|
|
115
|
+
* files — except `package.json`: `bun add` creates a bare manifest (no scripts)
|
|
116
|
+
* before postinstall runs, so we always replace it with the full template version.
|
|
117
|
+
*/
|
|
111
118
|
export async function scaffoldInto(dir: string) {
|
|
112
|
-
const
|
|
119
|
+
const { mkdir } = await import("node:fs/promises");
|
|
120
|
+
await mkdir(dir, { recursive: true });
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
for (const file of TEMPLATE_FILES) {
|
|
116
|
-
const target = join(dir, file);
|
|
117
|
-
if (await Bun.file(target).exists()) continue;
|
|
118
|
-
await cp(join(source, file), target);
|
|
119
|
-
}
|
|
122
|
+
const source = templateDir();
|
|
120
123
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
} finally {
|
|
126
|
-
await rm(source, { recursive: true, force: true });
|
|
124
|
+
for (const file of TEMPLATE_FILES) {
|
|
125
|
+
const target = join(dir, file);
|
|
126
|
+
if (await Bun.file(target).exists()) continue;
|
|
127
|
+
await cp(join(source, file), target);
|
|
127
128
|
}
|
|
129
|
+
|
|
130
|
+
// Always write the template package.json — the bare one bun creates before
|
|
131
|
+
// postinstall (no scripts, no name, no type) is useless and must be replaced.
|
|
132
|
+
await cp(join(source, "package.json"), join(dir, "package.json"));
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
/** Points the app at this CLI's own version, so template drift can never bite. */
|
|
@@ -143,17 +148,6 @@ async function pinFramework(dir: string) {
|
|
|
143
148
|
await Bun.write(manifest, JSON.stringify(pkg, null, 2) + "\n");
|
|
144
149
|
}
|
|
145
150
|
|
|
146
|
-
async function fetchTemplate() {
|
|
147
|
-
const tmp = await mkdtemp(join(tmpdir(), "lucyn-template-"));
|
|
148
|
-
await mkdir(tmp, { recursive: true });
|
|
149
|
-
const checkout = join(tmp, "template");
|
|
150
|
-
|
|
151
|
-
await proc.runStep(tmp, ["git", "clone", "--depth", "1", proc.TEMPLATE_REPO, checkout]);
|
|
152
|
-
await rm(join(checkout, ".git"), { recursive: true, force: true });
|
|
153
|
-
|
|
154
|
-
return checkout;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
151
|
/**
|
|
158
152
|
* Starts dev in the foreground, forwarding output, and calls `onReady` with the
|
|
159
153
|
* port the server actually bound to.
|
|
@@ -220,4 +214,4 @@ export async function devDetached(dir: string, logPath: string): Promise<number
|
|
|
220
214
|
child.unref();
|
|
221
215
|
closeSync(fd);
|
|
222
216
|
}
|
|
223
|
-
}
|
|
217
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lucynui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Desktop apps from plain HTML, CSS and JS. No React, no Rust, no Electron.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"bin/",
|
|
14
14
|
"cli/",
|
|
15
15
|
"src/",
|
|
16
|
+
"template/",
|
|
16
17
|
"webview-bun/src/",
|
|
17
18
|
"webview-bun/build/"
|
|
18
19
|
],
|
package/template/app.js
ADDED
package/template/app.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>LucyN App</title>
|
|
7
|
+
<link rel="stylesheet" href="/style.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main class="app">
|
|
11
|
+
<p class="brand">LucyN UI</p>
|
|
12
|
+
<h1>Your desktop app starts here</h1>
|
|
13
|
+
<p id="message">
|
|
14
|
+
Edit <code>index.html</code>, <code>style.css</code> or <code>app.js</code> and the window reloads by itself.
|
|
15
|
+
</p>
|
|
16
|
+
<button id="ping">Say hello</button>
|
|
17
|
+
</main>
|
|
18
|
+
<script src="/app.js"></script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lucyn-app",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "lucyn dev",
|
|
7
|
+
"build": "lucyn build"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"lucynui": "^0.1.0"
|
|
11
|
+
},
|
|
12
|
+
"trustedDependencies": ["lucynui"],
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/bun": "latest"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: dark;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
* {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
min-height: 100vh;
|
|
12
|
+
display: grid;
|
|
13
|
+
place-items: center;
|
|
14
|
+
background: radial-gradient(circle at 50% 0%, #1b2133, #0d0f14 60%);
|
|
15
|
+
color: #e8eaf0;
|
|
16
|
+
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.app {
|
|
20
|
+
max-width: 40rem;
|
|
21
|
+
padding: 3rem;
|
|
22
|
+
text-align: center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.brand {
|
|
26
|
+
margin: 0 0 1rem;
|
|
27
|
+
font-size: 0.75rem;
|
|
28
|
+
letter-spacing: 0.22em;
|
|
29
|
+
text-transform: uppercase;
|
|
30
|
+
color: #e0b34d;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
h1 {
|
|
34
|
+
margin: 0 0 1rem;
|
|
35
|
+
font-size: 2rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
p {
|
|
39
|
+
line-height: 1.7;
|
|
40
|
+
color: #a3abbf;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
code {
|
|
44
|
+
padding: 0.15rem 0.4rem;
|
|
45
|
+
border-radius: 5px;
|
|
46
|
+
background: #1d2231;
|
|
47
|
+
color: #e8eaf0;
|
|
48
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
49
|
+
font-size: 0.9em;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
button {
|
|
53
|
+
margin-top: 1rem;
|
|
54
|
+
padding: 0.7rem 1.4rem;
|
|
55
|
+
border: 1px solid #2c3347;
|
|
56
|
+
border-radius: 10px;
|
|
57
|
+
background: #e0b34d;
|
|
58
|
+
color: #14181f;
|
|
59
|
+
font: inherit;
|
|
60
|
+
font-weight: 600;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
button:hover {
|
|
65
|
+
background: #f0c465;
|
|
66
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "Preserve",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"types": ["bun"],
|
|
11
|
+
|
|
12
|
+
// Bundler mode
|
|
13
|
+
"moduleResolution": "bundler",
|
|
14
|
+
"allowImportingTsExtensions": true,
|
|
15
|
+
"verbatimModuleSyntax": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
|
|
18
|
+
// Best practices
|
|
19
|
+
"strict": true,
|
|
20
|
+
"skipLibCheck": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
"noUncheckedIndexedAccess": true,
|
|
23
|
+
"noImplicitOverride": true,
|
|
24
|
+
|
|
25
|
+
// Some stricter flags (disabled by default)
|
|
26
|
+
"noUnusedLocals": false,
|
|
27
|
+
"noUnusedParameters": false,
|
|
28
|
+
"noPropertyAccessFromIndexSignature": false
|
|
29
|
+
}
|
|
30
|
+
}
|