lucynui 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/cli/create.ts +31 -66
- 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,38 +103,38 @@ 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
|
|
113
|
-
|
|
114
|
-
try {
|
|
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
|
-
}
|
|
119
|
+
const { mkdir } = await import("node:fs/promises");
|
|
120
|
+
await mkdir(dir, { recursive: true });
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
if (!(await Bun.file(manifest).exists())) {
|
|
123
|
-
await cp(join(source, "package.json"), manifest);
|
|
124
|
-
}
|
|
122
|
+
const source = templateDir();
|
|
125
123
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
await
|
|
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);
|
|
129
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"));
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
/** Points the app at this CLI's own version, so template drift can never bite. */
|
|
133
136
|
async function pinFramework(dir: string) {
|
|
134
|
-
const
|
|
135
|
-
const manifest = Bun.file(manifestPath);
|
|
137
|
+
const manifest = Bun.file(join(dir, "package.json"));
|
|
136
138
|
if (!(await manifest.exists())) return;
|
|
137
139
|
|
|
138
140
|
// Running from a checkout rather than an install? Point the app at this copy
|
|
@@ -144,43 +146,6 @@ async function pinFramework(dir: string) {
|
|
|
144
146
|
const pkg = await manifest.json();
|
|
145
147
|
pkg.dependencies = { ...(pkg.dependencies ?? {}), lucynui: spec };
|
|
146
148
|
await Bun.write(manifest, JSON.stringify(pkg, null, 2) + "\n");
|
|
147
|
-
|
|
148
|
-
await patchManifest(manifestPath);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Ensures the scaffolded package.json has the scripts and trustedDependencies
|
|
153
|
-
* that users expect — the template repo ships a minimal manifest intentionally,
|
|
154
|
-
* so we inject these here rather than keeping them in sync in two repos.
|
|
155
|
-
*/
|
|
156
|
-
async function patchManifest(manifestPath: string) {
|
|
157
|
-
const file = Bun.file(manifestPath);
|
|
158
|
-
if (!(await file.exists())) return;
|
|
159
|
-
|
|
160
|
-
const pkg = await file.json();
|
|
161
|
-
|
|
162
|
-
pkg.scripts = {
|
|
163
|
-
dev: "lucyn dev",
|
|
164
|
-
build: "lucyn build",
|
|
165
|
-
...(pkg.scripts ?? {}),
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const trusted: string[] = pkg.trustedDependencies ?? [];
|
|
169
|
-
if (!trusted.includes("lucynui")) trusted.push("lucynui");
|
|
170
|
-
pkg.trustedDependencies = trusted;
|
|
171
|
-
|
|
172
|
-
await Bun.write(file, JSON.stringify(pkg, null, 2) + "\n");
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function fetchTemplate() {
|
|
176
|
-
const tmp = await mkdtemp(join(tmpdir(), "lucyn-template-"));
|
|
177
|
-
await mkdir(tmp, { recursive: true });
|
|
178
|
-
const checkout = join(tmp, "template");
|
|
179
|
-
|
|
180
|
-
await proc.runStep(tmp, ["git", "clone", "--depth", "1", proc.TEMPLATE_REPO, checkout]);
|
|
181
|
-
await rm(join(checkout, ".git"), { recursive: true, force: true });
|
|
182
|
-
|
|
183
|
-
return checkout;
|
|
184
149
|
}
|
|
185
150
|
|
|
186
151
|
/**
|
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
|
+
}
|