@ape-egg/vibe-compiler 4.0.0 → 4.1.1

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.
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ape-egg/vibe-compiler",
3
- "version": "4.0.0",
3
+ "version": "4.1.1",
4
4
  "type": "module",
5
5
  "description": "Optional Rust compiler for Vibe — pre-renders, inlines components, and optimizes iterations. The runtime never needs it.",
6
6
  "homepage": "https://vibe.korte.kim",
package/src/Cargo.lock CHANGED
@@ -1599,7 +1599,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
1599
1599
 
1600
1600
  [[package]]
1601
1601
  name = "vibe-compiler"
1602
- version = "4.0.0"
1602
+ version = "4.1.1"
1603
1603
  dependencies = [
1604
1604
  "clap",
1605
1605
  "colored",
package/src/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "vibe-compiler"
3
- version = "4.0.0"
3
+ version = "4.1.1"
4
4
  edition = "2021"
5
5
  description = "Vibe framework compiler - compiles Vibe source files into optimized output"
6
6
  authors = ["Kim Korte"]
@@ -62,6 +62,53 @@ pub(crate) fn atomic_write(path: &Path, contents: &str) -> std::io::Result<()> {
62
62
  // Can be used as class (.vibe-fouc) or attribute ([vibe-fouc])
63
63
  const FOUC_CLASS_OR_ATTR: &str = "vibe-fouc";
64
64
 
65
+ // vibe's own CSS, emitted inline into every compiled page's <head> so it
66
+ // applies at parse time regardless of which stylesheets the consumer links.
67
+ // The rules are EXTRACTED from vibe.css itself at build time — the file
68
+ // (shipped as a sibling package in every layout, and next to this source in
69
+ // the repo) is the single source of truth; no CSS text lives here to drift.
70
+ // Split on the optional marker into two sheets:
71
+ // - the cover half is load-bearing and always emitted: [vibe-staged] gates
72
+ // route/key remount staging on every page, and the fouc/animation rules
73
+ // cover pages that keep the marker (--fouc-as-is);
74
+ // - the layout half is vibe's base styling (layout-transparent component
75
+ // wrappers), skipped by disableVibeCss.
76
+ // Comments and newlines are stripped at emission: style content survives
77
+ // minification verbatim, so the emitted form must already be newline-free.
78
+ const VIBE_CSS_FILE: &str = include_str!("../../../vibe/vibe.css");
79
+ // The marker literal is the one string shared with the file (and the vite
80
+ // plugin) — scripts/check-release.js holds it in lockstep.
81
+ const VIBE_CSS_OPTIONAL_MARKER: &str =
82
+ "/* vibe:optional — everything below is skipped by disableVibeCss */";
83
+
84
+ fn strip_css_comments(css: &str) -> String {
85
+ let mut out = String::with_capacity(css.len());
86
+ let mut rest = css;
87
+ while let Some(start) = rest.find("/*") {
88
+ out.push_str(&rest[..start]);
89
+ rest = match rest[start..].find("*/") {
90
+ Some(end) => &rest[start + end + 2..],
91
+ None => "",
92
+ };
93
+ }
94
+ out.push_str(rest);
95
+ out
96
+ }
97
+
98
+ fn single_line_css(css: &str) -> String {
99
+ strip_css_comments(css)
100
+ .split_whitespace()
101
+ .collect::<Vec<_>>()
102
+ .join(" ")
103
+ }
104
+
105
+ fn vibe_css_sheets() -> (String, String) {
106
+ let (cover, layout) = VIBE_CSS_FILE
107
+ .split_once(VIBE_CSS_OPTIONAL_MARKER)
108
+ .expect("vibe.css lost its optional marker");
109
+ (single_line_css(cover), single_line_css(layout))
110
+ }
111
+
65
112
  const MIRROR_EXTENSIONS: &[&str] = &[
66
113
  "css", "js",
67
114
  // Fonts
@@ -246,6 +293,34 @@ fn remove_fouc_prevention(html: String) -> String {
246
293
  result
247
294
  }
248
295
 
296
+ /// Inject vibe's CSS right after <head> — ahead of consumer stylesheets, so
297
+ /// their equal-specificity overrides still win the cascade. Idempotent
298
+ /// (recompiles and already-covered sources pass through); fragments without a
299
+ /// <head> are not pages and are left untouched. The cover sheet always emits;
300
+ /// the layout sheet honors disableVibeCss.
301
+ pub fn inject_vibe_css(html: String, disable_vibe_css: bool) -> String {
302
+ if html.contains("data-vibe-cover") {
303
+ return html;
304
+ }
305
+ let open_end = match html.find("<head") {
306
+ Some(at) => match html[at..].find('>') {
307
+ Some(close) => at + close + 1,
308
+ None => return html,
309
+ },
310
+ None => return html,
311
+ };
312
+ let (cover_css, layout_css) = vibe_css_sheets();
313
+ let mut style = format!("<style data-vibe-cover>{}</style>", cover_css);
314
+ if !disable_vibe_css {
315
+ style.push_str(&format!("<style data-vibe-css>{}</style>", layout_css));
316
+ }
317
+ let mut result = String::with_capacity(html.len() + style.len());
318
+ result.push_str(&html[..open_end]);
319
+ result.push_str(&style);
320
+ result.push_str(&html[open_end..]);
321
+ result
322
+ }
323
+
249
324
  #[derive(Error, Debug)]
250
325
  pub enum CompileError {
251
326
  #[error("Source directory not found: {0}")]
@@ -1454,6 +1529,9 @@ impl Compiler {
1454
1529
  output = remove_fouc_prevention(output);
1455
1530
  }
1456
1531
 
1532
+ // vibe's CSS rides every compiled page, present at parse time
1533
+ output = inject_vibe_css(output, self.config.disable_vibe_css);
1534
+
1457
1535
  // Write to output
1458
1536
  let output_path = self.get_output_path(path, relative_path)?;
1459
1537
  atomic_write(&output_path, &output).map_err(|e| CompileError::WriteError {
@@ -1739,6 +1817,9 @@ impl Compiler {
1739
1817
  } else {
1740
1818
  shell_processed
1741
1819
  };
1820
+ // Same sheets the page pipeline emits — the shell serves every route,
1821
+ // and [vibe-staged] gates its remount staging.
1822
+ let shell_html = inject_vibe_css(shell_html, self.config.disable_vibe_css);
1742
1823
  let shell_path = self.config.output.join("index.html");
1743
1824
  atomic_write(&shell_path, &shell_html).map_err(|e| CompileError::WriteError {
1744
1825
  path: shell_path.display().to_string(),
@@ -2825,6 +2906,7 @@ mod tests {
2825
2906
  iterations_as_is: false,
2826
2907
  no_clean: false,
2827
2908
  fouc_as_is: false,
2909
+ disable_vibe_css: false,
2828
2910
  spa: true,
2829
2911
  working_dir: dir.clone(),
2830
2912
  };
@@ -2913,6 +2995,7 @@ mod tests {
2913
2995
  iterations_as_is: false,
2914
2996
  no_clean: false,
2915
2997
  fouc_as_is: false,
2998
+ disable_vibe_css: false,
2916
2999
  spa: true,
2917
3000
  working_dir: dir.clone(),
2918
3001
  };
@@ -3025,6 +3108,7 @@ mod tests {
3025
3108
  iterations_as_is: false,
3026
3109
  no_clean: false,
3027
3110
  fouc_as_is: false,
3111
+ disable_vibe_css: false,
3028
3112
  spa: true,
3029
3113
  working_dir: dir.clone(),
3030
3114
  };
@@ -3096,6 +3180,7 @@ mod tests {
3096
3180
  iterations_as_is: false,
3097
3181
  no_clean: false,
3098
3182
  fouc_as_is: false,
3183
+ disable_vibe_css: false,
3099
3184
  spa: false,
3100
3185
  working_dir: dir.clone(),
3101
3186
  };
package/src/config.rs CHANGED
@@ -48,6 +48,8 @@ pub struct VibeCompilerConfig {
48
48
  pub no_clean: bool,
49
49
  #[serde(default)]
50
50
  pub fouc_as_is: bool,
51
+ #[serde(default)]
52
+ pub disable_vibe_css: bool,
51
53
  #[serde(default, deserialize_with = "deserialize_spa")]
52
54
  pub spa: bool,
53
55
  }
@@ -121,6 +123,7 @@ impl Default for VibeCompilerConfig {
121
123
  iterations_as_is: false,
122
124
  no_clean: false,
123
125
  fouc_as_is: false,
126
+ disable_vibe_css: false,
124
127
  spa: false,
125
128
  }
126
129
  }
@@ -147,6 +150,7 @@ pub struct Config {
147
150
  pub iterations_as_is: bool,
148
151
  pub no_clean: bool,
149
152
  pub fouc_as_is: bool,
153
+ pub disable_vibe_css: bool,
150
154
  pub spa: bool,
151
155
  pub working_dir: PathBuf,
152
156
  }
@@ -209,6 +213,7 @@ impl Config {
209
213
  iterations_as_is: config.iterations_as_is,
210
214
  no_clean: config.no_clean,
211
215
  fouc_as_is: config.fouc_as_is,
216
+ disable_vibe_css: config.disable_vibe_css,
212
217
  spa: config.spa,
213
218
  working_dir,
214
219
  }
package/src/main.rs CHANGED
@@ -89,6 +89,10 @@ struct Args {
89
89
  #[arg(long, name = "fouc-as-is")]
90
90
  fouc_as_is: bool,
91
91
 
92
+ /// Skip emitting vibe's optional layout CSS (the cover rules always emit)
93
+ #[arg(long, name = "disable-vibe-css")]
94
+ disable_vibe_css: bool,
95
+
92
96
  /// Compile the pages tree to SPA output: fragments + route table + shell
93
97
  #[arg(long)]
94
98
  spa: bool,
@@ -171,6 +175,9 @@ fn main() {
171
175
  if args.fouc_as_is {
172
176
  config.fouc_as_is = true;
173
177
  }
178
+ if args.disable_vibe_css {
179
+ config.disable_vibe_css = true;
180
+ }
174
181
  if args.spa {
175
182
  overrides.spa = !original_spa;
176
183
  config.spa = true;