webnek 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/bin/cli.mjs CHANGED
@@ -101,7 +101,8 @@ function copyTemplate(from, to, vars) {
101
101
  for (const name of readdirSync(from)) {
102
102
  if (name === ".gitkeep" && existsSync(join(to, name))) continue;
103
103
  const src = join(from, name);
104
- const destName = name.endsWith(".tmpl") ? name.slice(0, -5) : name;
104
+ let destName = name.endsWith(".tmpl") ? name.slice(0, -5) : name;
105
+ if (name === "gitignore") destName = ".gitignore";
105
106
  const dest = join(to, destName);
106
107
  if (statSync(src).isDirectory()) {
107
108
  copyTemplate(src, dest, vars);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webnek",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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",
package/template/build.rs CHANGED
@@ -1,53 +1,111 @@
1
1
  //! Compile CSS and client JS into OUT_DIR on every `cargo build`.
2
+ //! Tailwind lives in ~/.cargo/webnek/cache (no project node_modules).
2
3
 
3
4
  use std::env;
4
5
  use std::fs;
5
6
  use std::path::{Path, PathBuf};
6
- use std::process::Command;
7
+ use std::process::{Command, Stdio};
8
+
9
+ const TW: &str = "4.3.3";
7
10
 
8
11
  fn main() {
9
12
  println!("cargo:rerun-if-changed=src/styles.css");
13
+ println!("cargo:rerun-if-changed=src/client/index.js");
10
14
  println!("cargo:rerun-if-changed=src/client/index.ts");
11
15
 
16
+ let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()));
12
17
  let out = PathBuf::from(env::var("OUT_DIR").unwrap());
13
- write_css(&out.join("styles.css"));
14
- write_js(&out.join("client.js"));
18
+ write_css(&manifest, &out.join("styles.css"));
19
+ write_js(&manifest, &out.join("client.js"));
15
20
  }
16
21
 
17
- fn write_css(dest: &Path) {
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
- ],
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}");
37
+ }
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,
29
50
  ) || 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
- ) {
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(""),
85
+ "-i",
86
+ "src/styles.css",
87
+ "-o",
88
+ &out,
89
+ "--minify",
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) {
41
96
  return;
42
97
  }
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());
45
- let _ = fs::write(dest, fallback);
98
+ fail();
46
99
  }
47
100
 
48
- fn write_js(dest: &Path) {
49
- let out = dest.to_str().unwrap_or("client.js");
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() {
104
+ return;
105
+ }
106
+ let out = dest.to_string_lossy().into_owned();
50
107
  if run(
108
+ manifest,
51
109
  "bunx",
52
110
  &[
53
111
  "esbuild",
@@ -55,9 +113,11 @@ fn write_js(dest: &Path) {
55
113
  "--bundle",
56
114
  "--format=esm",
57
115
  "--outfile",
58
- out,
116
+ &out,
59
117
  ],
118
+ None,
60
119
  ) || run(
120
+ manifest,
61
121
  "npx",
62
122
  &[
63
123
  "--yes",
@@ -66,19 +126,36 @@ fn write_js(dest: &Path) {
66
126
  "--bundle",
67
127
  "--format=esm",
68
128
  "--outfile",
69
- out,
129
+ &out,
70
130
  ],
131
+ None,
71
132
  ) {
72
133
  return;
73
134
  }
74
- let fallback = fs::read_to_string("src/client/index.ts").unwrap_or_else(|_| "// client\n".into());
75
- let _ = fs::write(dest, fallback);
135
+ let _ = fs::write(dest, "// client\n");
136
+ }
137
+
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'")
143
+ }
144
+
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)
76
154
  }
77
155
 
78
- fn run(bin: &str, args: &[&str]) -> bool {
79
- Command::new(bin)
80
- .args(args)
81
- .status()
82
- .map(|s| s.success())
83
- .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
+ );
84
161
  }
@@ -0,0 +1,8 @@
1
+ /target
2
+ /node_modules
3
+ /.vercel
4
+ .env
5
+ .env.*
6
+ !.env.example
7
+ .DS_Store
8
+ *.rs.bk
@@ -1,7 +1,7 @@
1
- console.log("⚡ [webnek] Client island initialized");
2
-
3
1
  document.addEventListener("click", (e) => {
4
- const target = (e.target as HTMLElement).closest("[data-action]");
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.toString();
18
- if (doubleEl) doubleEl.textContent = (val * 2).toString();
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
  "+"