webnek 0.1.1 → 0.1.3

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 CHANGED
@@ -1,23 +1,15 @@
1
1
  # webnek
2
2
 
3
3
  ```bash
4
- bunx webnek my-app
4
+ bun create webnek@latest my-app
5
5
  cd my-app
6
- bunx webnek dev
6
+ cargo dev
7
7
  ```
8
8
 
9
- That last command installs the Rust CLI the first time, then starts the app.
9
+ Create compiles the app (first time on a machine can take a minute). After that `cargo dev` starts the server.
10
10
 
11
- Generated apps have **no** `package.json`, **no** lockfile, **no** `node_modules`. `Cargo.toml` is the only manifest (`[package.metadata.npm]` for JS packages, cached in `~/.cargo/webnek/cache/`).
11
+ Same create command: `npm create webnek@latest my-app` or `pnpm create webnek@latest my-app`.
12
12
 
13
- Do not `bun add webnek` inside the app.
14
-
15
- ## Commands
16
-
17
- | Command | What it does |
18
- |---|---|
19
- | `bunx webnek [dir]` | Scaffold a project |
20
- | `bunx webnek dev` | Start the app |
21
- | `bunx webnek add <pkg>` | Add a crate or npm package |
13
+ Generated apps have no `package.json` and no `node_modules`. `cargo build` and `cargo test` work as usual.
22
14
 
23
15
  Requires [Rust](https://rustup.rs/).
package/bin/cli.mjs CHANGED
@@ -15,7 +15,7 @@ import { fileURLToPath } from "node:url";
15
15
  const here = dirname(fileURLToPath(import.meta.url));
16
16
  const pkgRoot = resolve(here, "..");
17
17
  const templateRoot = join(pkgRoot, "template");
18
- const GIT = "https://github.com/Grenish/webnek";
18
+ const VERSION = JSON.parse(readFileSync(join(pkgRoot, "package.json"), "utf8")).version;
19
19
 
20
20
  const args = process.argv.slice(2);
21
21
  const cmd = args[0];
@@ -40,16 +40,15 @@ const IGNORE_WHEN_EMPTY = new Set([
40
40
  ]);
41
41
 
42
42
  function help() {
43
- const run = invokeCmd();
44
43
  console.log(`
45
- webnek 0.1.1
44
+ webnek ${VERSION}
46
45
 
47
- ${run} [dir] Scaffold a new app
48
- ${run} new [dir] Same as above
49
- ${run} init Scaffold into the current directory
50
- ${run} dev Start the app
51
- ${run} build Production build
52
- ${run} add <pkg> Add a crate or npm package
46
+ bun create webnek@latest [dir]
47
+ npm create webnek@latest [dir]
48
+ pnpm create webnek@latest [dir]
49
+
50
+ Then:
51
+ cargo dev
53
52
  `);
54
53
  }
55
54
 
@@ -85,11 +84,10 @@ function frameworkDep(appDir) {
85
84
  const fw = resolve(root, "crates/framework");
86
85
  let rel = relative(appDir, fw);
87
86
  if (!rel.startsWith(".") && !rel.startsWith("/")) rel = `./${rel}`;
88
- // Cargo.toml paths work with forward slashes on all platforms.
89
87
  rel = rel.replaceAll("\\", "/");
90
88
  return `webnek = { path = "${rel}", features = ["ssr"] }`;
91
89
  }
92
- return `webnek = { git = "${GIT}", features = ["ssr"] }`;
90
+ return `webnek = { version = "0.1.0", features = ["ssr"] }`;
93
91
  }
94
92
 
95
93
  function isEffectivelyEmpty(dir) {
@@ -137,10 +135,35 @@ function stripNodeManifests(appDir) {
137
135
  if (existsSync(nm)) rmSync(nm, { recursive: true, force: true });
138
136
  }
139
137
 
138
+ function which(bin) {
139
+ const r = spawnSync("sh", ["-c", `command -v ${bin}`], { encoding: "utf8" });
140
+ return r.status === 0 ? r.stdout.trim() : "";
141
+ }
142
+
143
+ function cargoBuild(appDir) {
144
+ if (!which("cargo")) {
145
+ console.error(`
146
+ ✖ cargo is not installed. Install Rust first:
147
+
148
+ https://rustup.rs/
149
+ `);
150
+ return false;
151
+ }
152
+ console.log(" webnek compiling (first time on this computer can take a minute) …\n");
153
+ const r = spawnSync("cargo", ["build"], {
154
+ cwd: appDir,
155
+ stdio: "inherit",
156
+ env: { ...process.env, CARGO_TERM_COLOR: process.env.CARGO_TERM_COLOR || "always" },
157
+ });
158
+ return r.status === 0;
159
+ }
160
+
140
161
  function scaffold(targetArg) {
141
162
  const appDir = resolve(process.cwd(), targetArg || ".");
142
163
  const pkgName = rustPackageName(
143
- appDir === process.cwd() ? (process.cwd().split(/[/\\]/).pop() || "app") : targetArg.replace(/\/+$/, "").split(/[/\\]/).pop()
164
+ appDir === process.cwd()
165
+ ? process.cwd().split(/[/\\]/).pop() || "app"
166
+ : targetArg.replace(/\/+$/, "").split(/[/\\]/).pop()
144
167
  );
145
168
 
146
169
  if (existsSync(join(appDir, "Cargo.toml"))) {
@@ -156,101 +179,32 @@ function scaffold(targetArg) {
156
179
 
157
180
  mkdirSync(appDir, { recursive: true });
158
181
  stripNodeManifests(appDir);
159
- const vars = {
182
+ copyTemplate(templateRoot, appDir, {
160
183
  PACKAGE_NAME: pkgName,
161
184
  FRAMEWORK_DEP: frameworkDep(appDir),
162
185
  PROJECT_NAME: pkgName,
163
- START_CMD: invokeCmd(),
164
- };
165
- copyTemplate(templateRoot, appDir, vars);
186
+ });
166
187
  stripNodeManifests(appDir);
167
188
 
168
189
  const rel = relative(process.cwd(), appDir) || ".";
169
- const run = invokeCmd();
170
- const lines = [` webnek created ${rel}`, ``, ` Next:`];
171
- if (rel !== ".") {
172
- lines.push(` cd ${rel}`);
173
- }
174
- lines.push(` ${run} dev`);
175
- console.log(`\n${lines.join("\n")}\n`);
176
- }
190
+ const built = cargoBuild(appDir);
177
191
 
178
- function which(bin) {
179
- const r = spawnSync("sh", ["-c", `command -v ${bin}`], { encoding: "utf8" });
180
- return r.status === 0 ? r.stdout.trim() : "";
181
- }
182
-
183
- function invokeCmd() {
184
- const ua = process.env.npm_config_user_agent || "";
185
- const argv0 = process.argv[0] || "";
186
- const argv1 = process.argv[1] || "";
187
- if (ua.includes("bun") || argv0.includes("bun") || argv1.includes("bun")) {
188
- return "bunx webnek";
189
- }
190
- if (ua.includes("npx") || argv1.includes("npx") || argv1.includes("/npm/")) {
191
- return "npx webnek";
192
+ const lines = [` webnek ${VERSION} ${built ? "ready" : "created"} ${rel}`, ``, ` Next:`];
193
+ if (rel !== ".") lines.push(` cd ${rel}`);
194
+ lines.push(` cargo dev`);
195
+ if (!built) {
196
+ lines.push(``, ` cargo build failed. Fix that, then cargo dev.`);
192
197
  }
193
- if (which("webnek")) return "webnek";
194
- return "bunx webnek";
195
- }
196
-
197
- function cargoWebnekOk() {
198
- const probe = spawnSync("cargo", ["webnek", "--help"], {
199
- encoding: "utf8",
200
- stdio: "pipe",
201
- });
202
- return probe.status === 0 || Boolean(which("cargo-webnek"));
203
- }
204
-
205
- function installCargoWebnek() {
206
- const root = findFrameworkRoot();
207
- if (root && existsSync(join(root, "crates/cli/Cargo.toml"))) {
208
- console.log(" webnek installing cargo-webnek …");
209
- const ins = spawnSync(
210
- "cargo",
211
- ["install", "--path", join(root, "crates/cli"), "--force"],
212
- { stdio: "inherit" }
213
- );
214
- return ins.status === 0;
215
- }
216
- console.log(" webnek installing cargo-webnek (once, from git) …");
217
- const ins = spawnSync(
218
- "cargo",
219
- ["install", "--git", GIT, "webnek-cli"],
220
- { stdio: "inherit" }
221
- );
222
- return ins.status === 0;
198
+ console.log(`\n${lines.join("\n")}\n`);
223
199
  }
224
200
 
225
- function ensureCargoWeb() {
226
- if (cargoWebnekOk()) return;
227
-
201
+ function cargo(args) {
228
202
  if (!which("cargo")) {
229
- console.error(`
230
- ✖ cargo is not installed. Install Rust first:
231
-
232
- https://rustup.rs/
233
- `);
203
+ console.error(" ✖ cargo is not installed. https://rustup.rs/");
234
204
  process.exit(1);
235
205
  }
236
-
237
- if (!installCargoWebnek()) {
238
- console.error(`
239
- ✖ could not install cargo-webnek.
240
-
241
- cargo install --git ${GIT} webnek-cli
242
- `);
243
- process.exit(1);
244
- }
245
- }
246
-
247
- function proxyCargoWeb(forward) {
248
- ensureCargoWeb();
249
- const result = spawnSync("cargo", ["webnek", ...forward], {
250
- stdio: "inherit",
251
- cwd: process.cwd(),
252
- });
253
- process.exit(result.status ?? 1);
206
+ const r = spawnSync("cargo", args, { stdio: "inherit", cwd: process.cwd() });
207
+ process.exit(r.status ?? 1);
254
208
  }
255
209
 
256
210
  const createInvoked =
@@ -268,12 +222,15 @@ if (args.length === 0 && createInvoked) {
268
222
  help();
269
223
  } else if (cmd === "new" || cmd === "create" || cmd === "init") {
270
224
  scaffold(cmd === "init" ? args[1] || "." : args[1] || ".");
271
- } else if (["dev", "build", "add", "export"].includes(cmd)) {
272
- proxyCargoWeb(args);
225
+ } else if (cmd === "dev") {
226
+ cargo(["run", ...args.slice(1)]);
227
+ } else if (cmd === "build") {
228
+ cargo(["build", "--release", ...args.slice(1)]);
229
+ } else if (cmd === "test") {
230
+ cargo(["test", ...args.slice(1)]);
273
231
  } else if (cmd.startsWith("-")) {
274
232
  help();
275
233
  process.exit(1);
276
234
  } else {
277
- // `webnek my-app`
278
235
  scaffold(cmd);
279
236
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "webnek",
3
- "version": "0.1.1",
4
- "description": "Webnek app installer. bunx webnek my-app && bunx webnek dev",
3
+ "version": "0.1.3",
4
+ "description": "Create a Webnek app. bun create webnek@latest my-app && cargo dev",
5
5
  "type": "module",
6
6
  "license": "MIT OR Apache-2.0",
7
7
  "author": "Grenish Rai <mrcoder2033d@gmail.com>",
@@ -22,6 +22,9 @@
22
22
  "framework",
23
23
  "create-webnek"
24
24
  ],
25
+ "exports": {
26
+ "./bin/cli.mjs": "./bin/cli.mjs"
27
+ },
25
28
  "engines": {
26
29
  "node": ">=18"
27
30
  },
@@ -0,0 +1,2 @@
1
+ [alias]
2
+ dev = "run"
@@ -2,6 +2,7 @@
2
2
  name = "{{PACKAGE_NAME}}"
3
3
  version = "0.1.0"
4
4
  edition = "2021"
5
+ default-run = "{{PACKAGE_NAME}}"
5
6
 
6
7
  [workspace]
7
8
 
@@ -15,4 +16,4 @@ path = "api/index.rs"
15
16
 
16
17
  [package.metadata.npm]
17
18
  # Frontend NPM packages installed to ~/.cargo/webnek/cache/
18
- # Add new packages with: cargo webnek add <package> --npm
19
+ # JS packages: bunx <pkg> is not required. Add crates with cargo add.
@@ -1,12 +1,12 @@
1
1
  # {{PROJECT_NAME}}
2
2
 
3
3
  ```bash
4
- {{START_CMD}} dev
4
+ cargo dev
5
5
  ```
6
6
 
7
- - Edit `src/styles.css` (Tailwind v4). Compiled CSS goes to `target/web/`.
7
+ - Edit `src/styles.css` (Tailwind v4).
8
8
  - `public/` is for files you add (favicon, images).
9
- - JS packages: `{{START_CMD}} add <pkg> --npm`
9
+ - `cargo build`, `cargo test`, and `cargo run` work as usual.
10
10
 
11
11
  ## Deploy
12
12
 
package/template/build.rs CHANGED
@@ -1,7 +1,4 @@
1
- //! Compile CSS and client JS into OUT_DIR so the Vercel function can embed them.
2
- //!
3
- //! Local `cargo webnek dev` still serves from `target/web/` (hot reload).
4
- //! On Vercel, `npx` compiles Tailwind and esbuild.
1
+ //! Compile CSS and client JS into OUT_DIR on every `cargo build`.
5
2
 
6
3
  use std::env;
7
4
  use std::fs;
@@ -18,88 +15,68 @@ fn main() {
18
15
  }
19
16
 
20
17
  fn write_css(dest: &Path) {
21
- if copy_generated("styles.css", dest) {
22
- return;
23
- }
24
- if on_vercel() && npx_ok(&[
25
- "--yes",
26
- "@tailwindcss/cli@4.3.3",
27
- "-i",
28
- "src/styles.css",
29
- "-o",
30
- dest.to_str().unwrap_or("styles.css"),
31
- "--minify",
32
- ]) {
18
+ let out = dest.to_str().unwrap_or("styles.css");
19
+ if run(
20
+ "bunx",
21
+ &[
22
+ "@tailwindcss/cli@4.3.3",
23
+ "-i",
24
+ "src/styles.css",
25
+ "-o",
26
+ out,
27
+ "--minify",
28
+ ],
29
+ ) || run(
30
+ "npx",
31
+ &[
32
+ "--yes",
33
+ "@tailwindcss/cli@4.3.3",
34
+ "-i",
35
+ "src/styles.css",
36
+ "-o",
37
+ out,
38
+ "--minify",
39
+ ],
40
+ ) {
33
41
  return;
34
42
  }
35
- let fallback = fs::read_to_string("src/styles.css").unwrap_or_else(|_| {
36
- "/* styles.css missing run cargo webnek dev */\n".to_string()
37
- });
43
+ let fallback = fs::read_to_string("src/styles.css")
44
+ .unwrap_or_else(|_| "/* styles.css: install bun or npx to compile Tailwind */\n".into());
38
45
  let _ = fs::write(dest, fallback);
39
46
  }
40
47
 
41
48
  fn write_js(dest: &Path) {
42
- if copy_generated("pkg/client.js", dest) {
43
- return;
44
- }
45
- if on_vercel()
46
- && npx_ok(&[
47
- "--yes",
49
+ let out = dest.to_str().unwrap_or("client.js");
50
+ if run(
51
+ "bunx",
52
+ &[
48
53
  "esbuild",
49
54
  "src/client/index.ts",
50
55
  "--bundle",
51
56
  "--format=esm",
52
57
  "--outfile",
53
- dest.to_str().unwrap_or("client.js"),
54
- ])
55
- {
56
- return;
57
- }
58
- if on_vercel()
59
- && npx_ok(&[
58
+ out,
59
+ ],
60
+ ) || run(
61
+ "npx",
62
+ &[
60
63
  "--yes",
61
64
  "esbuild",
62
65
  "src/client/index.ts",
66
+ "--bundle",
63
67
  "--format=esm",
64
68
  "--outfile",
65
- dest.to_str().unwrap_or("client.js"),
66
- ])
67
- {
69
+ out,
70
+ ],
71
+ ) {
68
72
  return;
69
73
  }
70
- let fallback = fs::read_to_string("src/client/index.ts")
71
- .unwrap_or_else(|_| "// client\n".to_string());
74
+ let fallback = fs::read_to_string("src/client/index.ts").unwrap_or_else(|_| "// client\n".into());
72
75
  let _ = fs::write(dest, fallback);
73
76
  }
74
77
 
75
- fn copy_generated(relative: &str, dest: &Path) -> bool {
76
- let pkg = match env::var("CARGO_PKG_NAME") {
77
- Ok(p) => p,
78
- Err(_) => return false,
79
- };
80
- let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()));
81
- let mut candidates = Vec::new();
82
- if let Ok(td) = env::var("CARGO_TARGET_DIR") {
83
- candidates.push(PathBuf::from(td).join("web").join(&pkg).join(relative));
84
- }
85
- candidates.push(manifest.join("target/web").join(&pkg).join(relative));
86
- if let Some(parent) = manifest.parent() {
87
- candidates.push(parent.join("target/web").join(&pkg).join(relative));
88
- }
89
- for src in candidates {
90
- if src.is_file() && fs::copy(&src, dest).is_ok() {
91
- return true;
92
- }
93
- }
94
- false
95
- }
96
-
97
- fn on_vercel() -> bool {
98
- env::var_os("VERCEL").is_some()
99
- }
100
-
101
- fn npx_ok(args: &[&str]) -> bool {
102
- Command::new("npx")
78
+ fn run(bin: &str, args: &[&str]) -> bool {
79
+ Command::new(bin)
103
80
  .args(args)
104
81
  .status()
105
82
  .map(|s| s.success())