webnek 0.1.2 → 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/README.md +5 -13
- package/bin/cli.mjs +53 -96
- package/package.json +5 -2
- package/template/.cargo/config.toml +2 -0
- package/template/Cargo.toml +2 -1
- package/template/README.md +3 -3
- package/template/build.rs +119 -65
- package/template/gitignore +8 -0
- package/template/src/client/{index.ts → index.js} +5 -5
- package/template/src/routes/page.rs +2 -2
package/README.md
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
# webnek
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
|
-
|
|
4
|
+
bun create webnek@latest my-app
|
|
5
5
|
cd my-app
|
|
6
|
-
|
|
6
|
+
cargo dev
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Create compiles the app (first time on a machine can take a minute). After that `cargo dev` starts the server.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Same create command: `npm create webnek@latest my-app` or `pnpm create webnek@latest my-app`.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Commands
|
|
16
|
-
|
|
17
|
-
| Command | What it does |
|
|
18
|
-
|---|---|
|
|
19
|
-
| `bunx webnek@latest [dir]` | Scaffold a project |
|
|
20
|
-
| `bunx webnek@latest dev` | Start the app |
|
|
21
|
-
| `bunx webnek@latest 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,6 @@ 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";
|
|
19
18
|
const VERSION = JSON.parse(readFileSync(join(pkgRoot, "package.json"), "utf8")).version;
|
|
20
19
|
|
|
21
20
|
const args = process.argv.slice(2);
|
|
@@ -41,16 +40,15 @@ const IGNORE_WHEN_EMPTY = new Set([
|
|
|
41
40
|
]);
|
|
42
41
|
|
|
43
42
|
function help() {
|
|
44
|
-
const run = invokeCmd();
|
|
45
43
|
console.log(`
|
|
46
44
|
webnek ${VERSION}
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
bun create webnek@latest [dir]
|
|
47
|
+
npm create webnek@latest [dir]
|
|
48
|
+
pnpm create webnek@latest [dir]
|
|
49
|
+
|
|
50
|
+
Then:
|
|
51
|
+
cargo dev
|
|
54
52
|
`);
|
|
55
53
|
}
|
|
56
54
|
|
|
@@ -86,7 +84,6 @@ function frameworkDep(appDir) {
|
|
|
86
84
|
const fw = resolve(root, "crates/framework");
|
|
87
85
|
let rel = relative(appDir, fw);
|
|
88
86
|
if (!rel.startsWith(".") && !rel.startsWith("/")) rel = `./${rel}`;
|
|
89
|
-
// Cargo.toml paths work with forward slashes on all platforms.
|
|
90
87
|
rel = rel.replaceAll("\\", "/");
|
|
91
88
|
return `webnek = { path = "${rel}", features = ["ssr"] }`;
|
|
92
89
|
}
|
|
@@ -104,7 +101,8 @@ function copyTemplate(from, to, vars) {
|
|
|
104
101
|
for (const name of readdirSync(from)) {
|
|
105
102
|
if (name === ".gitkeep" && existsSync(join(to, name))) continue;
|
|
106
103
|
const src = join(from, name);
|
|
107
|
-
|
|
104
|
+
let destName = name.endsWith(".tmpl") ? name.slice(0, -5) : name;
|
|
105
|
+
if (name === "gitignore") destName = ".gitignore";
|
|
108
106
|
const dest = join(to, destName);
|
|
109
107
|
if (statSync(src).isDirectory()) {
|
|
110
108
|
copyTemplate(src, dest, vars);
|
|
@@ -138,10 +136,35 @@ function stripNodeManifests(appDir) {
|
|
|
138
136
|
if (existsSync(nm)) rmSync(nm, { recursive: true, force: true });
|
|
139
137
|
}
|
|
140
138
|
|
|
139
|
+
function which(bin) {
|
|
140
|
+
const r = spawnSync("sh", ["-c", `command -v ${bin}`], { encoding: "utf8" });
|
|
141
|
+
return r.status === 0 ? r.stdout.trim() : "";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function cargoBuild(appDir) {
|
|
145
|
+
if (!which("cargo")) {
|
|
146
|
+
console.error(`
|
|
147
|
+
✖ cargo is not installed. Install Rust first:
|
|
148
|
+
|
|
149
|
+
https://rustup.rs/
|
|
150
|
+
`);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
console.log(" webnek compiling (first time on this computer can take a minute) …\n");
|
|
154
|
+
const r = spawnSync("cargo", ["build"], {
|
|
155
|
+
cwd: appDir,
|
|
156
|
+
stdio: "inherit",
|
|
157
|
+
env: { ...process.env, CARGO_TERM_COLOR: process.env.CARGO_TERM_COLOR || "always" },
|
|
158
|
+
});
|
|
159
|
+
return r.status === 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
141
162
|
function scaffold(targetArg) {
|
|
142
163
|
const appDir = resolve(process.cwd(), targetArg || ".");
|
|
143
164
|
const pkgName = rustPackageName(
|
|
144
|
-
appDir === process.cwd()
|
|
165
|
+
appDir === process.cwd()
|
|
166
|
+
? process.cwd().split(/[/\\]/).pop() || "app"
|
|
167
|
+
: targetArg.replace(/\/+$/, "").split(/[/\\]/).pop()
|
|
145
168
|
);
|
|
146
169
|
|
|
147
170
|
if (existsSync(join(appDir, "Cargo.toml"))) {
|
|
@@ -157,101 +180,32 @@ function scaffold(targetArg) {
|
|
|
157
180
|
|
|
158
181
|
mkdirSync(appDir, { recursive: true });
|
|
159
182
|
stripNodeManifests(appDir);
|
|
160
|
-
|
|
183
|
+
copyTemplate(templateRoot, appDir, {
|
|
161
184
|
PACKAGE_NAME: pkgName,
|
|
162
185
|
FRAMEWORK_DEP: frameworkDep(appDir),
|
|
163
186
|
PROJECT_NAME: pkgName,
|
|
164
|
-
|
|
165
|
-
};
|
|
166
|
-
copyTemplate(templateRoot, appDir, vars);
|
|
187
|
+
});
|
|
167
188
|
stripNodeManifests(appDir);
|
|
168
189
|
|
|
169
190
|
const rel = relative(process.cwd(), appDir) || ".";
|
|
170
|
-
const
|
|
171
|
-
const lines = [` webnek ${VERSION} created ${rel}`, ``, ` Next:`];
|
|
172
|
-
if (rel !== ".") {
|
|
173
|
-
lines.push(` cd ${rel}`);
|
|
174
|
-
}
|
|
175
|
-
lines.push(` ${run} dev`);
|
|
176
|
-
console.log(`\n${lines.join("\n")}\n`);
|
|
177
|
-
}
|
|
191
|
+
const built = cargoBuild(appDir);
|
|
178
192
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
function invokeCmd() {
|
|
185
|
-
const ua = process.env.npm_config_user_agent || "";
|
|
186
|
-
const argv0 = process.argv[0] || "";
|
|
187
|
-
const argv1 = process.argv[1] || "";
|
|
188
|
-
if (ua.includes("bun") || argv0.includes("bun") || argv1.includes("bun")) {
|
|
189
|
-
return "bunx webnek@latest";
|
|
190
|
-
}
|
|
191
|
-
if (ua.includes("npx") || argv1.includes("npx") || argv1.includes("/npm/")) {
|
|
192
|
-
return "npx webnek@latest";
|
|
193
|
+
const lines = [` webnek ${VERSION} ${built ? "ready" : "created"} ${rel}`, ``, ` Next:`];
|
|
194
|
+
if (rel !== ".") lines.push(` cd ${rel}`);
|
|
195
|
+
lines.push(` cargo dev`);
|
|
196
|
+
if (!built) {
|
|
197
|
+
lines.push(``, ` cargo build failed. Fix that, then cargo dev.`);
|
|
193
198
|
}
|
|
194
|
-
|
|
195
|
-
return "bunx webnek@latest";
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function cargoWebnekOk() {
|
|
199
|
-
const probe = spawnSync("cargo", ["webnek", "--help"], {
|
|
200
|
-
encoding: "utf8",
|
|
201
|
-
stdio: "pipe",
|
|
202
|
-
});
|
|
203
|
-
return probe.status === 0 || Boolean(which("cargo-webnek"));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function installCargoWebnek() {
|
|
207
|
-
const root = findFrameworkRoot();
|
|
208
|
-
if (root && existsSync(join(root, "crates/cli/Cargo.toml"))) {
|
|
209
|
-
console.log(" webnek installing cargo-webnek …");
|
|
210
|
-
const ins = spawnSync(
|
|
211
|
-
"cargo",
|
|
212
|
-
["install", "--path", join(root, "crates/cli"), "--force"],
|
|
213
|
-
{ stdio: "inherit" }
|
|
214
|
-
);
|
|
215
|
-
return ins.status === 0;
|
|
216
|
-
}
|
|
217
|
-
console.log(" webnek installing cargo-webnek (once) …");
|
|
218
|
-
const ins = spawnSync(
|
|
219
|
-
"cargo",
|
|
220
|
-
["install", "webnek-cli"],
|
|
221
|
-
{ stdio: "inherit" }
|
|
222
|
-
);
|
|
223
|
-
return ins.status === 0;
|
|
199
|
+
console.log(`\n${lines.join("\n")}\n`);
|
|
224
200
|
}
|
|
225
201
|
|
|
226
|
-
function
|
|
227
|
-
if (cargoWebnekOk()) return;
|
|
228
|
-
|
|
202
|
+
function cargo(args) {
|
|
229
203
|
if (!which("cargo")) {
|
|
230
|
-
console.error(
|
|
231
|
-
✖ cargo is not installed. Install Rust first:
|
|
232
|
-
|
|
233
|
-
https://rustup.rs/
|
|
234
|
-
`);
|
|
204
|
+
console.error(" ✖ cargo is not installed. https://rustup.rs/");
|
|
235
205
|
process.exit(1);
|
|
236
206
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
console.error(`
|
|
240
|
-
✖ could not install cargo-webnek.
|
|
241
|
-
|
|
242
|
-
cargo install webnek-cli
|
|
243
|
-
`);
|
|
244
|
-
process.exit(1);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function proxyCargoWeb(forward) {
|
|
249
|
-
ensureCargoWeb();
|
|
250
|
-
const result = spawnSync("cargo", ["webnek", ...forward], {
|
|
251
|
-
stdio: "inherit",
|
|
252
|
-
cwd: process.cwd(),
|
|
253
|
-
});
|
|
254
|
-
process.exit(result.status ?? 1);
|
|
207
|
+
const r = spawnSync("cargo", args, { stdio: "inherit", cwd: process.cwd() });
|
|
208
|
+
process.exit(r.status ?? 1);
|
|
255
209
|
}
|
|
256
210
|
|
|
257
211
|
const createInvoked =
|
|
@@ -269,12 +223,15 @@ if (args.length === 0 && createInvoked) {
|
|
|
269
223
|
help();
|
|
270
224
|
} else if (cmd === "new" || cmd === "create" || cmd === "init") {
|
|
271
225
|
scaffold(cmd === "init" ? args[1] || "." : args[1] || ".");
|
|
272
|
-
} else if (
|
|
273
|
-
|
|
226
|
+
} else if (cmd === "dev") {
|
|
227
|
+
cargo(["run", ...args.slice(1)]);
|
|
228
|
+
} else if (cmd === "build") {
|
|
229
|
+
cargo(["build", "--release", ...args.slice(1)]);
|
|
230
|
+
} else if (cmd === "test") {
|
|
231
|
+
cargo(["test", ...args.slice(1)]);
|
|
274
232
|
} else if (cmd.startsWith("-")) {
|
|
275
233
|
help();
|
|
276
234
|
process.exit(1);
|
|
277
235
|
} else {
|
|
278
|
-
// `webnek my-app`
|
|
279
236
|
scaffold(cmd);
|
|
280
237
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webnek",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Webnek app
|
|
3
|
+
"version": "0.1.4",
|
|
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
|
},
|
package/template/Cargo.toml
CHANGED
|
@@ -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
|
|
19
|
+
# JS packages: bunx <pkg> is not required. Add crates with cargo add.
|
package/template/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
|
-
|
|
4
|
+
cargo dev
|
|
5
5
|
```
|
|
6
6
|
|
|
7
|
-
- Edit `src/styles.css` (Tailwind v4).
|
|
7
|
+
- Edit `src/styles.css` (Tailwind v4).
|
|
8
8
|
- `public/` is for files you add (favicon, images).
|
|
9
|
-
-
|
|
9
|
+
- `cargo build`, `cargo test`, and `cargo run` work as usual.
|
|
10
10
|
|
|
11
11
|
## Deploy
|
|
12
12
|
|
package/template/build.rs
CHANGED
|
@@ -1,107 +1,161 @@
|
|
|
1
|
-
//! Compile CSS and client JS into OUT_DIR
|
|
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`.
|
|
2
|
+
//! Tailwind lives in ~/.cargo/webnek/cache (no project node_modules).
|
|
5
3
|
|
|
6
4
|
use std::env;
|
|
7
5
|
use std::fs;
|
|
8
6
|
use std::path::{Path, PathBuf};
|
|
9
|
-
use std::process::Command;
|
|
7
|
+
use std::process::{Command, Stdio};
|
|
8
|
+
|
|
9
|
+
const TW: &str = "4.3.3";
|
|
10
10
|
|
|
11
11
|
fn main() {
|
|
12
12
|
println!("cargo:rerun-if-changed=src/styles.css");
|
|
13
|
+
println!("cargo:rerun-if-changed=src/client/index.js");
|
|
13
14
|
println!("cargo:rerun-if-changed=src/client/index.ts");
|
|
14
15
|
|
|
16
|
+
let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()));
|
|
15
17
|
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
16
|
-
write_css(&out.join("styles.css"));
|
|
17
|
-
write_js(&out.join("client.js"));
|
|
18
|
+
write_css(&manifest, &out.join("styles.css"));
|
|
19
|
+
write_js(&manifest, &out.join("client.js"));
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
fn
|
|
21
|
-
if
|
|
22
|
-
|
|
22
|
+
fn cache_dir() -> PathBuf {
|
|
23
|
+
if let Ok(h) = env::var("CARGO_HOME") {
|
|
24
|
+
PathBuf::from(h).join("webnek").join("cache")
|
|
25
|
+
} else if let Ok(h) = env::var("HOME") {
|
|
26
|
+
PathBuf::from(h).join(".cargo").join("webnek").join("cache")
|
|
27
|
+
} else {
|
|
28
|
+
PathBuf::from(".webnek-cache")
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fn ensure_tailwind(cache: &Path) -> bool {
|
|
33
|
+
let _ = fs::create_dir_all(cache);
|
|
34
|
+
let pkg = cache.join("package.json");
|
|
35
|
+
if !pkg.exists() {
|
|
36
|
+
let _ = fs::write(&pkg, "{\"name\":\"webnek-cache\",\"private\":true}");
|
|
23
37
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
let cli = cache.join("node_modules/@tailwindcss/cli/dist/index.mjs");
|
|
39
|
+
let tw = cache.join("node_modules/tailwindcss");
|
|
40
|
+
if cli.exists() && tw.exists() {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
let spec_tw = format!("tailwindcss@{TW}");
|
|
44
|
+
let spec_cli = format!("@tailwindcss/cli@{TW}");
|
|
45
|
+
run(
|
|
46
|
+
cache,
|
|
47
|
+
"bun",
|
|
48
|
+
&["add", &spec_tw, &spec_cli],
|
|
49
|
+
None,
|
|
50
|
+
) || run(
|
|
51
|
+
cache,
|
|
52
|
+
"npm",
|
|
53
|
+
&["install", &spec_tw, &spec_cli],
|
|
54
|
+
None,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn link_node_modules(manifest: &Path, cache: &Path) -> bool {
|
|
59
|
+
let dest = manifest.join("node_modules");
|
|
60
|
+
let src = cache.join("node_modules");
|
|
61
|
+
if dest.exists() || !src.exists() {
|
|
62
|
+
return dest.exists();
|
|
63
|
+
}
|
|
64
|
+
#[cfg(unix)]
|
|
65
|
+
{
|
|
66
|
+
std::os::unix::fs::symlink(&src, &dest).is_ok()
|
|
67
|
+
}
|
|
68
|
+
#[cfg(not(unix))]
|
|
69
|
+
{
|
|
70
|
+
std::os::windows::fs::symlink_dir(&src, &dest).is_ok()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn write_css(manifest: &Path, dest: &Path) {
|
|
75
|
+
let cache = cache_dir();
|
|
76
|
+
if !ensure_tailwind(&cache) {
|
|
77
|
+
fail();
|
|
78
|
+
}
|
|
79
|
+
let _ = link_node_modules(manifest, &cache);
|
|
80
|
+
let entry = cache.join("node_modules/@tailwindcss/cli/dist/index.mjs");
|
|
81
|
+
let node_modules = cache.join("node_modules");
|
|
82
|
+
let out = dest.to_string_lossy().into_owned();
|
|
83
|
+
let args = [
|
|
84
|
+
entry.to_str().unwrap_or(""),
|
|
27
85
|
"-i",
|
|
28
86
|
"src/styles.css",
|
|
29
87
|
"-o",
|
|
30
|
-
|
|
88
|
+
&out,
|
|
31
89
|
"--minify",
|
|
32
|
-
]
|
|
90
|
+
];
|
|
91
|
+
let ok = run(manifest, "bun", &args, Some(&node_modules))
|
|
92
|
+
|| run(manifest, "node", &args, Some(&node_modules));
|
|
93
|
+
|
|
94
|
+
let css = fs::read_to_string(dest).unwrap_or_default();
|
|
95
|
+
if ok && compiled(&css) {
|
|
33
96
|
return;
|
|
34
97
|
}
|
|
35
|
-
|
|
36
|
-
"/* styles.css missing — run cargo webnek dev */\n".to_string()
|
|
37
|
-
});
|
|
38
|
-
let _ = fs::write(dest, fallback);
|
|
98
|
+
fail();
|
|
39
99
|
}
|
|
40
100
|
|
|
41
|
-
fn write_js(dest: &Path) {
|
|
42
|
-
|
|
101
|
+
fn write_js(manifest: &Path, dest: &Path) {
|
|
102
|
+
let js = manifest.join("src/client/index.js");
|
|
103
|
+
if js.exists() && fs::copy(&js, dest).is_ok() {
|
|
43
104
|
return;
|
|
44
105
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
106
|
+
let out = dest.to_string_lossy().into_owned();
|
|
107
|
+
if run(
|
|
108
|
+
manifest,
|
|
109
|
+
"bunx",
|
|
110
|
+
&[
|
|
48
111
|
"esbuild",
|
|
49
112
|
"src/client/index.ts",
|
|
50
113
|
"--bundle",
|
|
51
114
|
"--format=esm",
|
|
52
115
|
"--outfile",
|
|
53
|
-
|
|
54
|
-
]
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
116
|
+
&out,
|
|
117
|
+
],
|
|
118
|
+
None,
|
|
119
|
+
) || run(
|
|
120
|
+
manifest,
|
|
121
|
+
"npx",
|
|
122
|
+
&[
|
|
60
123
|
"--yes",
|
|
61
124
|
"esbuild",
|
|
62
125
|
"src/client/index.ts",
|
|
126
|
+
"--bundle",
|
|
63
127
|
"--format=esm",
|
|
64
128
|
"--outfile",
|
|
65
|
-
|
|
66
|
-
]
|
|
67
|
-
|
|
129
|
+
&out,
|
|
130
|
+
],
|
|
131
|
+
None,
|
|
132
|
+
) {
|
|
68
133
|
return;
|
|
69
134
|
}
|
|
70
|
-
let
|
|
71
|
-
.unwrap_or_else(|_| "// client\n".to_string());
|
|
72
|
-
let _ = fs::write(dest, fallback);
|
|
135
|
+
let _ = fs::write(dest, "// client\n");
|
|
73
136
|
}
|
|
74
137
|
|
|
75
|
-
fn
|
|
76
|
-
let
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
138
|
+
fn compiled(css: &str) -> bool {
|
|
139
|
+
let t = css.trim_start();
|
|
140
|
+
!t.is_empty()
|
|
141
|
+
&& !t.starts_with("@import \"tailwindcss\"")
|
|
142
|
+
&& !t.starts_with("@import 'tailwindcss'")
|
|
95
143
|
}
|
|
96
144
|
|
|
97
|
-
fn
|
|
98
|
-
|
|
145
|
+
fn run(dir: &Path, bin: &str, args: &[&str], node_path: Option<&Path>) -> bool {
|
|
146
|
+
let mut cmd = Command::new(bin);
|
|
147
|
+
cmd.args(args)
|
|
148
|
+
.current_dir(dir)
|
|
149
|
+
.stdin(Stdio::null());
|
|
150
|
+
if let Some(p) = node_path {
|
|
151
|
+
cmd.env("NODE_PATH", p);
|
|
152
|
+
}
|
|
153
|
+
cmd.status().map(|s| s.success()).unwrap_or(false)
|
|
99
154
|
}
|
|
100
155
|
|
|
101
|
-
fn
|
|
102
|
-
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
.unwrap_or(false)
|
|
156
|
+
fn fail() -> ! {
|
|
157
|
+
panic!(
|
|
158
|
+
"webnek: could not compile src/styles.css.\n\
|
|
159
|
+
Install bun or npm, then run `cargo build` again."
|
|
160
|
+
);
|
|
107
161
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
console.log("⚡ [webnek] Client island initialized");
|
|
2
|
-
|
|
3
1
|
document.addEventListener("click", (e) => {
|
|
4
|
-
const
|
|
2
|
+
const el = e.target;
|
|
3
|
+
if (!(el instanceof Element)) return;
|
|
4
|
+
const target = el.closest("[data-action]");
|
|
5
5
|
if (!target) return;
|
|
6
6
|
|
|
7
7
|
const action = target.getAttribute("data-action");
|
|
@@ -14,6 +14,6 @@ document.addEventListener("click", (e) => {
|
|
|
14
14
|
if (action === "dec") val -= 1;
|
|
15
15
|
if (action === "reset") val = 0;
|
|
16
16
|
|
|
17
|
-
counterEl.textContent = val
|
|
18
|
-
if (doubleEl) doubleEl.textContent = (val * 2)
|
|
17
|
+
counterEl.textContent = String(val);
|
|
18
|
+
if (doubleEl) doubleEl.textContent = String(val * 2);
|
|
19
19
|
});
|
|
@@ -44,14 +44,14 @@ pub fn home_page() -> View {
|
|
|
44
44
|
<div className="flex gap-2 pb-1">
|
|
45
45
|
<button
|
|
46
46
|
data-action="dec"
|
|
47
|
-
className="w-10 h-10 border border-line bg-paper text-ink text-lg leading-none hover:bg-white transition-colors"
|
|
47
|
+
className="w-10 h-10 border border-line bg-paper text-ink text-lg leading-none cursor-pointer hover:bg-white transition-colors"
|
|
48
48
|
onClick={move || set_count.update(|c| *c -= 1)}
|
|
49
49
|
>
|
|
50
50
|
"−"
|
|
51
51
|
</button>
|
|
52
52
|
<button
|
|
53
53
|
data-action="inc"
|
|
54
|
-
className="w-10 h-10 bg-ink text-paper text-lg leading-none hover:bg-patina transition-colors"
|
|
54
|
+
className="w-10 h-10 bg-ink text-paper text-lg leading-none cursor-pointer hover:bg-patina transition-colors"
|
|
55
55
|
onClick={move || set_count.update(|c| *c += 1)}
|
|
56
56
|
>
|
|
57
57
|
"+"
|