lucynui 0.1.3 → 0.1.4

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 CHANGED
@@ -1,19 +1,14 @@
1
- import { closeSync, openSync } from "node:fs";
2
1
  import { cp } from "node:fs/promises";
3
2
  import { basename, join } from "node:path";
4
3
  import { fileURLToPath } from "node:url";
5
4
  import * as proc from "./proc";
6
5
  import * as ui from "./ui";
7
6
 
8
- /** Matches the line the framework logs once the server is listening. */
9
7
  const SERVER_READY = /dev server running on http:\/\/localhost:(\d+)/;
10
-
11
- /** Files copied from the bundled template into a new project. */
12
- const TEMPLATE_FILES = ["index.html", "style.css", "app.js", "app.ts", "lucyn.config.ts", "tsconfig.json", ".gitignore"];
8
+ const TEMPLATE_FILES = ["index.html", "style.css", "app.js", "app.ts", "lucyn.config.ts", "tsconfig.json", ".gitignore", "package.json"];
13
9
 
14
10
  const bun = () => proc.findBun() ?? "bun";
15
11
 
16
- /** Absolute path to the bundled template directory inside this package. */
17
12
  function templateDir(): string {
18
13
  return fileURLToPath(new URL("../template", import.meta.url));
19
14
  }
@@ -28,71 +23,51 @@ export async function dev(): Promise<never> {
28
23
  export async function build(): Promise<never> {
29
24
  proc.requireProject();
30
25
  ui.info("Building your app...");
31
-
32
26
  const code = await proc.run(process.cwd(), [bun(), proc.frameworkPath("build.ts")]);
33
27
  if (code !== 0) process.exit(code);
34
-
35
28
  ui.ok("Build complete! Check ./dist");
36
29
  process.exit(0);
37
30
  }
38
31
 
39
- /** `lucyn create-ui <appname>` — the whole zero-to-running-app pipeline. */
32
+ /** `lucyn create-ui <appname>` */
40
33
  export async function create(appName: string): Promise<never> {
41
34
  if (process.platform === "win32") ui.warn(ui.WINDOWS_NOTICE);
42
-
43
- const bunPath = await proc.ensureBun();
35
+ await proc.ensureBun();
44
36
  await proc.ensureSystemDeps();
45
37
 
46
- ui.step(1, "scaffolding the template, hang on");
38
+ ui.step(1, "copying template files");
47
39
  await scaffoldInto(join(process.cwd(), appName));
48
- await pinFramework(appName);
49
- ui.done("scaffolding complete — fetching the deps and required builds");
50
-
51
- ui.step(2, "installing modules including lucynui");
52
- await proc.runStep(appName, [bunPath, "install"]);
53
- ui.done("modules installed");
40
+ ui.done(`app created in ./${appName}`);
54
41
 
55
- ui.step(3, "testing build");
56
- await testBuild(appName);
57
- ui.done("build was successful");
42
+ ui.line();
43
+ ui.info(` cd ${appName}`);
44
+ ui.info(" bun install");
45
+ ui.info(" bun run dev");
46
+ ui.line();
58
47
 
59
- ui.step(4, "starting dev with hmr");
60
- const code = await runDev(appName, (port) => {
61
- ui.done(`dev is up and running on port ${port}`);
62
- ui.ready(basename(appName));
63
- });
64
-
65
- process.exit(code);
48
+ process.exit(0);
66
49
  }
67
50
 
68
- /** `lucyn` / `lucyn init` — scaffold into the current folder, then run it. */
51
+ /** `lucyn` / `lucyn init` */
69
52
  export async function init(): Promise<never> {
70
- const bunPath = await proc.ensureBun();
53
+ await proc.ensureBun();
71
54
  await proc.ensureSystemDeps();
72
55
 
73
56
  if (await isProject(process.cwd())) {
74
- ui.info("This is already a LucyN UI app — starting dev.");
75
- } else {
76
- ui.step(1, "scaffolding the template, hang on");
77
- await scaffoldInto(process.cwd());
78
- ui.done("scaffolding complete — fetching the deps and required builds");
79
-
80
- ui.step(2, "installing modules including lucynui");
81
- await proc.runStep(process.cwd(), [bunPath, "install"]);
82
- ui.done("modules installed");
83
-
84
- ui.step(3, "testing build");
85
- await testBuild(process.cwd());
86
- ui.done("build was successful");
57
+ ui.info("Already a LucyN UI app. Run: bun run dev");
58
+ process.exit(0);
87
59
  }
88
60
 
89
- ui.step(4, "starting dev with hmr");
90
- const code = await runDev(process.cwd(), (port) => {
91
- ui.done(`dev is up and running on port ${port}`);
92
- ui.ready(basename(process.cwd()));
93
- });
61
+ ui.step(1, "copying template files");
62
+ await scaffoldInto(process.cwd());
63
+ ui.done("done");
94
64
 
95
- process.exit(code);
65
+ ui.line();
66
+ ui.info(" bun install");
67
+ ui.info(" bun run dev");
68
+ ui.line();
69
+
70
+ process.exit(0);
96
71
  }
97
72
 
98
73
  /* ------------------------------------------------------------------- helpers */
@@ -101,20 +76,6 @@ export function isProject(dir: string) {
101
76
  return Bun.file(join(dir, proc.CONFIG_FILE)).exists();
102
77
  }
103
78
 
104
- /** Runs a build and throws away the binary — the point is proving it compiles. */
105
- export async function testBuild(dir: string) {
106
- const { rm } = await import("node:fs/promises");
107
- const code = await proc.run(dir, [bun(), proc.frameworkPath("build.ts")]);
108
- await rm(join(dir, "dist"), { recursive: true, force: true });
109
-
110
- if (code !== 0) throw new Error("the test build failed");
111
- }
112
-
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
- */
118
79
  export async function scaffoldInto(dir: string) {
119
80
  const { mkdir } = await import("node:fs/promises");
120
81
  await mkdir(dir, { recursive: true });
@@ -123,35 +84,11 @@ export async function scaffoldInto(dir: string) {
123
84
 
124
85
  for (const file of TEMPLATE_FILES) {
125
86
  const target = join(dir, file);
126
- if (await Bun.file(target).exists()) continue;
87
+ if (file !== "package.json" && await Bun.file(target).exists()) continue;
127
88
  await cp(join(source, file), target);
128
89
  }
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"));
133
90
  }
134
91
 
135
- /** Points the app at this CLI's own version, so template drift can never bite. */
136
- async function pinFramework(dir: string) {
137
- const manifest = Bun.file(join(dir, "package.json"));
138
- if (!(await manifest.exists())) return;
139
-
140
- // Running from a checkout rather than an install? Point the app at this copy
141
- // so the whole flow can be tried out before the package is published.
142
- const spec = import.meta.dir.includes("node_modules")
143
- ? `^${await proc.packageVersion()}`
144
- : `file:${proc.packageRoot()}`;
145
-
146
- const pkg = await manifest.json();
147
- pkg.dependencies = { ...(pkg.dependencies ?? {}), lucynui: spec };
148
- await Bun.write(manifest, JSON.stringify(pkg, null, 2) + "\n");
149
- }
150
-
151
- /**
152
- * Starts dev in the foreground, forwarding output, and calls `onReady` with the
153
- * port the server actually bound to.
154
- */
155
92
  async function runDev(dir: string, onReady: (port: number) => void): Promise<number> {
156
93
  const child = Bun.spawn([bun(), proc.frameworkPath("lucyn.ts"), "--dev"], {
157
94
  cwd: dir,
@@ -167,7 +104,6 @@ async function runDev(dir: string, onReady: (port: number) => void): Promise<num
167
104
  for await (const chunk of child.stdout) {
168
105
  process.stdout.write(chunk);
169
106
  if (announced) continue;
170
-
171
107
  seen += decoder.decode(chunk, { stream: true });
172
108
  const port = SERVER_READY.exec(seen)?.[1];
173
109
  if (port) {
@@ -178,40 +114,3 @@ async function runDev(dir: string, onReady: (port: number) => void): Promise<num
178
114
 
179
115
  return await child.exited;
180
116
  }
181
-
182
- /**
183
- * Starts dev detached so the caller can exit, writing output to `logPath`.
184
- * Returns the port, or null when the window never came up.
185
- */
186
- export async function devDetached(dir: string, logPath: string): Promise<number | null> {
187
- const fd = openSync(logPath, "a");
188
-
189
- const child = Bun.spawn([bun(), proc.frameworkPath("lucyn.ts"), "--dev"], {
190
- cwd: dir,
191
- stdin: "ignore",
192
- stdout: fd,
193
- stderr: fd,
194
- });
195
-
196
- try {
197
- for (let attempt = 0; attempt < 80; attempt++) {
198
- await Bun.sleep(250);
199
-
200
- const log = await Bun.file(logPath).text();
201
- const port = SERVER_READY.exec(log)?.[1];
202
-
203
- if (port) {
204
- // Give it a moment to prove it survives the window actually opening.
205
- await Bun.sleep(500);
206
- return child.exitCode === null ? Number(port) : null;
207
- }
208
-
209
- if (child.exitCode !== null) return null;
210
- }
211
-
212
- return null;
213
- } finally {
214
- child.unref();
215
- closeSync(fd);
216
- }
217
- }
@@ -1,64 +1,26 @@
1
- import { existsSync } from "node:fs";
1
+ import { cp } from "node:fs/promises";
2
2
  import { join, sep } from "node:path";
3
- import * as create from "./create";
4
- import * as ui from "./ui";
3
+ import { fileURLToPath } from "node:url";
5
4
 
6
- /**
7
- * Runs after `bun add lucynui --trust` / `npm i lucynui`.
8
- *
9
- * When the folder is empty it scaffolds the starter app, proves the build works
10
- * and leaves a dev server running before the install command returns. Any folder
11
- * that already looks like a project is left completely alone.
12
- */
13
- export async function postinstall(): Promise<void> {
14
- // Only when installed as a dependency — not while developing this package.
15
- if (!import.meta.dir.includes("node_modules")) return;
16
- if (process.env.LUCYN_SKIP_POSTINSTALL) return;
5
+ if (!import.meta.dir.includes("node_modules")) process.exit(0);
6
+ if (process.env.LUCYN_SKIP_POSTINSTALL) process.exit(0);
17
7
 
18
- const dir = projectDir();
19
- if (!dir) return;
8
+ const projectDir = process.env.INIT_CWD
9
+ ?? process.env.npm_config_local_prefix
10
+ ?? (() => {
11
+ const i = import.meta.dir.lastIndexOf(`${sep}node_modules${sep}`);
12
+ return i === -1 ? null : import.meta.dir.slice(0, i);
13
+ })();
20
14
 
21
- if (await create.isProject(dir)) return;
22
- if (existsSync(join(dir, "index.html")) || existsSync(join(dir, "src"))) return;
15
+ if (!projectDir) process.exit(0);
23
16
 
24
- ui.useTerminal();
25
- ui.info("LucyN UI found an empty project and is setting it up.");
26
- ui.line();
17
+ const template = fileURLToPath(new URL("../template", import.meta.url));
18
+ const FILES = ["index.html", "style.css", "app.js", "app.ts", "lucyn.config.ts", "tsconfig.json", ".gitignore", "package.json"];
27
19
 
28
- ui.step(1, "cloning the template, hang on");
29
- await create.scaffoldInto(dir);
30
- ui.done("cloning complete — fetching the deps and required builds");
31
-
32
- ui.step(2, "installing modules including lucynui");
33
- ui.done("modules installed");
34
-
35
- ui.step(3, "testing build");
36
- await create.testBuild(dir);
37
- ui.done("build was successful");
38
-
39
- ui.step(4, "starting dev with hmr");
40
- const logPath = join(dir, ".lucyn-dev.log");
41
- const port = await create.devDetached(dir, logPath);
42
-
43
- if (port === null) {
44
- ui.warn(`The window did not come up. Run lucyn dev yourself — log: ${logPath}`);
45
- return;
46
- }
47
-
48
- ui.done(`dev is up and running on port ${port}`);
49
- ui.line();
50
- ui.line(` ${ui.accent("Log:")} ${ui.text(logPath)}`);
51
- ui.line();
52
- }
53
-
54
- /** The folder the user ran `bun add` in — the one holding `node_modules`. */
55
- function projectDir(): string | null {
56
- const fromEnv = process.env.INIT_CWD ?? process.env.npm_config_local_prefix;
57
- if (fromEnv) return fromEnv;
58
-
59
- const marker = `${sep}node_modules${sep}`;
60
- const index = import.meta.dir.lastIndexOf(marker);
61
- return index === -1 ? null : import.meta.dir.slice(0, index);
20
+ for (const file of FILES) {
21
+ const target = join(projectDir, file);
22
+ if (file !== "package.json" && await Bun.file(target).exists()) continue;
23
+ await cp(join(template, file), target);
62
24
  }
63
25
 
64
- if (import.meta.main) await postinstall();
26
+ console.log("\n LucyN UI ready.\n\n bun install\n bun run dev\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucynui",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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",