@ape-egg/vibe 1.6.1 → 1.7.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.
@@ -328,7 +328,7 @@ pub fn watch(config: Config, verbose: bool) -> std::result::Result<(), Box<dyn s
328
328
 
329
329
  // Keep compiler and parser alive to reuse component cache across incremental compilations
330
330
  let mut watch_compiler = Compiler::new(config.clone(), false);
331
- let parser = {
331
+ let mut parser = {
332
332
  use crate::parser::HtmlParser;
333
333
  let mut p = HtmlParser::new(config.components_path());
334
334
  if let Err(e) = p.load_elements() {
@@ -396,10 +396,52 @@ pub fn watch(config: Config, verbose: bool) -> std::result::Result<(), Box<dyn s
396
396
  let dependent_pages = graph.get_all_dependent_pages(&path_canonical);
397
397
  if !dependent_pages.is_empty() {
398
398
  println!("{} {} changed", "[watch]".cyan(), relative_path.display());
399
+
400
+ // Invalidate component cache so stale content isn't used
401
+ watch_compiler.clear_component_cache();
402
+
403
+ // Reload the changed component in the parser's element cache
404
+ // (used for custom element syntax like <Layout>)
405
+ if path.exists() {
406
+ let _ = parser.reload_element(path);
407
+ }
408
+
399
409
  pages_to_recompile.extend(dependent_pages);
400
410
  }
401
411
  } else if let Some(ext) = path.extension() {
402
412
  if ext == "html" {
413
+ // Check if file still exists (handle deletions)
414
+ if !path.exists() {
415
+ // File was deleted - clean up dependency graph
416
+ println!("{} {} deleted", "[watch]".yellow(), relative_path.display());
417
+
418
+ if let Some(old_deps) = graph.page_to_components.remove(path) {
419
+ for dep in old_deps {
420
+ if let Some(pages) = graph.component_to_pages.get_mut(&dep) {
421
+ pages.remove(path);
422
+ }
423
+ }
424
+ }
425
+
426
+ // Delete corresponding compiled output
427
+ let output_path = canonical_output.join(relative_path);
428
+ if output_path.exists() {
429
+ if let Err(e) = std::fs::remove_file(&output_path) {
430
+ eprintln!("{}: Failed to delete {}: {}", "Warning".yellow(), output_path.display(), e);
431
+ }
432
+ }
433
+
434
+ // Delete corresponding manifest
435
+ let manifest_path = canonical_output.join("vibe-hyperspeed").join(format!("{}.manifest.js", relative_path.display()));
436
+ if manifest_path.exists() {
437
+ if let Err(e) = std::fs::remove_file(&manifest_path) {
438
+ eprintln!("{}: Failed to delete manifest {}: {}", "Warning".yellow(), manifest_path.display(), e);
439
+ }
440
+ }
441
+
442
+ continue;
443
+ }
444
+
403
445
  // Page changed - recompile just this page
404
446
  println!("{} {} changed", "[watch]".cyan(), relative_path.display());
405
447
  pages_to_recompile.insert(path.clone());
@@ -431,7 +473,10 @@ pub fn watch(config: Config, verbose: bool) -> std::result::Result<(), Box<dyn s
431
473
  }
432
474
 
433
475
  // Separate HTML files (from dependency graph) and CSS/JS assets (from changed_paths)
434
- let html_files: Vec<PathBuf> = pages_to_recompile.into_iter().collect();
476
+ // Filter out deleted files
477
+ let html_files: Vec<PathBuf> = pages_to_recompile.into_iter()
478
+ .filter(|p| p.exists())
479
+ .collect();
435
480
 
436
481
  let mut asset_files: Vec<PathBuf> = Vec::new();
437
482
  for path in &changed_paths {
@@ -446,6 +491,24 @@ pub fn watch(config: Config, verbose: bool) -> std::result::Result<(), Box<dyn s
446
491
  continue;
447
492
  }
448
493
 
494
+ // Handle deleted asset files
495
+ if !path.exists() {
496
+ if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
497
+ if ext == "css" || ext == "js" {
498
+ let relative_path = path.strip_prefix(&config.source).unwrap_or(path);
499
+ let output_path = canonical_output.join(relative_path);
500
+ if output_path.exists() {
501
+ if let Err(e) = std::fs::remove_file(&output_path) {
502
+ eprintln!("{}: Failed to delete {}: {}", "Warning".yellow(), output_path.display(), e);
503
+ } else {
504
+ println!("{} {} deleted", "[watch]".yellow(), relative_path.display());
505
+ }
506
+ }
507
+ }
508
+ }
509
+ continue;
510
+ }
511
+
449
512
  if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
450
513
  if ext == "css" || ext == "js" {
451
514
  asset_files.push(path.clone());
@@ -482,6 +545,10 @@ pub fn watch(config: Config, verbose: bool) -> std::result::Result<(), Box<dyn s
482
545
 
483
546
  // Compile HTML files
484
547
  if !html_files.is_empty() {
548
+ // Always clear component cache before recompiling so components
549
+ // are read fresh from disk (prevents stale cache from previous triggers)
550
+ watch_compiler.clear_component_cache();
551
+
485
552
  match watch_compiler.compile_specific_html_files(&html_files, &parser) {
486
553
  Ok(stats) => {
487
554
  total_stats.files_compiled = stats.files_compiled;
@@ -31,8 +31,6 @@ pub struct VibeCompilerConfig {
31
31
  #[serde(default)]
32
32
  pub elements_as_is: bool,
33
33
  #[serde(default)]
34
- pub validate: bool,
35
- #[serde(default)]
36
34
  pub source_maps: bool,
37
35
  #[serde(default)]
38
36
  pub reserved_elements: Vec<String>,
@@ -44,6 +42,10 @@ pub struct VibeCompilerConfig {
44
42
  pub runtime_as_is: bool,
45
43
  #[serde(default)]
46
44
  pub iterations_as_is: bool,
45
+ #[serde(default)]
46
+ pub no_clean: bool,
47
+ #[serde(default)]
48
+ pub fouc_as_is: bool,
47
49
  }
48
50
 
49
51
  fn default_source() -> String { "./".to_string() }
@@ -92,13 +94,14 @@ impl Default for VibeCompilerConfig {
92
94
  root: None,
93
95
  minify: false,
94
96
  elements_as_is: false,
95
- validate: false,
96
97
  source_maps: false,
97
98
  reserved_elements: vec![],
98
99
  node_modules_as_is: false,
99
100
  components_as_is: false,
100
101
  runtime_as_is: false,
101
102
  iterations_as_is: false,
103
+ no_clean: false,
104
+ fouc_as_is: false,
102
105
  }
103
106
  }
104
107
  }
@@ -115,13 +118,14 @@ pub struct Config {
115
118
  pub _root: Option<String>,
116
119
  pub minify: bool,
117
120
  pub elements_as_is: bool,
118
- pub validate: bool,
119
121
  pub source_maps: bool,
120
122
  pub reserved_elements: Vec<String>,
121
123
  pub node_modules_as_is: bool,
122
124
  pub components_as_is: bool,
123
125
  pub runtime_as_is: bool,
124
126
  pub iterations_as_is: bool,
127
+ pub no_clean: bool,
128
+ pub fouc_as_is: bool,
125
129
  pub working_dir: PathBuf,
126
130
  }
127
131
 
@@ -161,13 +165,14 @@ impl Config {
161
165
  _root: config.root,
162
166
  minify: config.minify,
163
167
  elements_as_is: config.elements_as_is,
164
- validate: config.validate,
165
168
  source_maps: config.source_maps,
166
169
  reserved_elements,
167
170
  node_modules_as_is: config.node_modules_as_is,
168
171
  components_as_is: config.components_as_is,
169
172
  runtime_as_is: config.runtime_as_is,
170
173
  iterations_as_is: config.iterations_as_is,
174
+ no_clean: config.no_clean,
175
+ fouc_as_is: config.fouc_as_is,
171
176
  working_dir,
172
177
  }
173
178
  }
@@ -14,7 +14,6 @@ use compiler::Compiler;
14
14
  struct ConfigOverrides {
15
15
  minify: bool,
16
16
  elements_as_is: bool,
17
- validate: bool,
18
17
  source_maps: bool,
19
18
  node_modules_as_is: bool,
20
19
  components_as_is: bool,
@@ -53,10 +52,6 @@ struct Args {
53
52
  #[arg(long, name = "source-maps")]
54
53
  source_maps: bool,
55
54
 
56
- /// Validate HTML syntax
57
- #[arg(long)]
58
- validate: bool,
59
-
60
55
  /// Initialize configuration in package.json
61
56
  #[arg(long)]
62
57
  init: bool,
@@ -84,6 +79,14 @@ struct Args {
84
79
  /// Skip iteration optimization - use runtime DOM cloning instead of compiled batch functions
85
80
  #[arg(long, name = "iterations-as-is")]
86
81
  iterations_as_is: bool,
82
+
83
+ /// Skip cleaning output directory before compilation
84
+ #[arg(long, name = "no-clean", hide = true)]
85
+ no_clean: bool,
86
+
87
+ /// Keep FOUC prevention class/attribute in compiled output
88
+ #[arg(long, name = "fouc-as-is")]
89
+ fouc_as_is: bool,
87
90
  }
88
91
 
89
92
  fn main() {
@@ -119,7 +122,6 @@ fn main() {
119
122
  // Store original config values before applying flag overrides
120
123
  let original_minify = config.minify;
121
124
  let original_elements_as_is = config.elements_as_is;
122
- let original_validate = config.validate;
123
125
  let original_source_maps = config.source_maps;
124
126
  let original_node_modules_as_is = config.node_modules_as_is;
125
127
  let original_components_as_is = config.components_as_is;
@@ -137,10 +139,6 @@ fn main() {
137
139
  overrides.elements_as_is = !original_elements_as_is;
138
140
  config.elements_as_is = true;
139
141
  }
140
- if args.validate {
141
- overrides.validate = !original_validate;
142
- config.validate = true;
143
- }
144
142
  if args.source_maps {
145
143
  overrides.source_maps = !original_source_maps;
146
144
  config.source_maps = true;
@@ -161,6 +159,12 @@ fn main() {
161
159
  overrides.iterations_as_is = !original_iterations_as_is;
162
160
  config.iterations_as_is = true;
163
161
  }
162
+ if args.no_clean {
163
+ config.no_clean = true;
164
+ }
165
+ if args.fouc_as_is {
166
+ config.fouc_as_is = true;
167
+ }
164
168
 
165
169
  if args.verbose {
166
170
  println!("{}", format!("Vibe Compiler v{}", env!("CARGO_PKG_VERSION")).cyan().bold());
@@ -191,21 +195,30 @@ fn main() {
191
195
  format!("{} (config: {})", value.to_string().green(), value.to_string().yellow())
192
196
  }
193
197
 
194
- // Alphabetically ordered with padding (longest key is "node-modules-as-is" = 18 chars)
195
- println!(" {}: {}", format!("{:<18}", "assets").cyan(), format_value_no_flag(&config._assets));
196
- println!(" {}: {}", format!("{:<18}", "components").cyan(), format_value_no_flag(&config.components));
197
- println!(" {}: {}", format!("{:<18}", "components-as-is").cyan(), format_bool_with_flag(config.components_as_is, overrides.components_as_is, original_components_as_is));
198
- println!(" {}: {}", format!("{:<18}", "elements-as-is").cyan(), format_bool_with_flag(config.elements_as_is, overrides.elements_as_is, original_elements_as_is));
199
- println!(" {}: {}", format!("{:<18}", "exclude-tags").cyan(), format_value_no_flag(format!("{:?}", config.reserved_elements)));
200
- println!(" {}: {}", format!("{:<18}", "minify").cyan(), format_bool_with_flag(config.minify, overrides.minify, original_minify));
201
- println!(" {}: {}", format!("{:<18}", "node-modules-as-is").cyan(), format_bool_with_flag(config.node_modules_as_is, overrides.node_modules_as_is, original_node_modules_as_is));
202
- println!(" {}: {}", format!("{:<18}", "output").cyan(), format_value_no_flag(&config._output_str));
203
- println!(" {}: {}", format!("{:<18}", "pages").cyan(), format_value_no_flag(&config.pages));
204
- println!(" {}: {}", format!("{:<18}", "root").cyan(), format_value_no_flag(config._root.as_ref().map(|s| s.as_str()).unwrap_or("null")));
205
- println!(" {}: {}", format!("{:<18}", "runtime-as-is").cyan(), format_bool_with_flag(config.runtime_as_is, overrides.runtime_as_is, original_runtime_as_is));
206
- println!(" {}: {}", format!("{:<18}", "source").cyan(), format_value_no_flag(&config._source_str));
207
- println!(" {}: {}", format!("{:<18}", "source-maps").cyan(), format_bool_with_flag(config.source_maps, overrides.source_maps, original_source_maps));
208
- println!(" {}: {}", format!("{:<18}", "validate").cyan(), format_bool_with_flag(config.validate, overrides.validate, original_validate));
198
+ fn format_reserved_elements(elements: &[String]) -> String {
199
+ if elements.len() <= 2 {
200
+ format!("{:?}", elements)
201
+ } else {
202
+ let remaining = elements.len() - 2;
203
+ format!("[\"component\", \"div\", ... + {} more]", remaining)
204
+ }
205
+ }
206
+
207
+ // Alphabetically ordered with padding (longest key is "reservedElements" = 16 chars)
208
+ println!(" {}: {}", format!("{:<16}", "assets").cyan(), format_value_no_flag(&config._assets));
209
+ println!(" {}: {}", format!("{:<16}", "components").cyan(), format_value_no_flag(&config.components));
210
+ println!(" {}: {}", format!("{:<16}", "componentsAsIs").cyan(), format_bool_with_flag(config.components_as_is, overrides.components_as_is, original_components_as_is));
211
+ println!(" {}: {}", format!("{:<16}", "elementsAsIs").cyan(), format_bool_with_flag(config.elements_as_is, overrides.elements_as_is, original_elements_as_is));
212
+ println!(" {}: {}", format!("{:<16}", "iterationsAsIs").cyan(), format_bool_with_flag(config.iterations_as_is, overrides.iterations_as_is, original_iterations_as_is));
213
+ println!(" {}: {}", format!("{:<16}", "minify").cyan(), format_bool_with_flag(config.minify, overrides.minify, original_minify));
214
+ println!(" {}: {}", format!("{:<16}", "nodeModulesAsIs").cyan(), format_bool_with_flag(config.node_modules_as_is, overrides.node_modules_as_is, original_node_modules_as_is));
215
+ println!(" {}: {}", format!("{:<16}", "output").cyan(), format_value_no_flag(&config._output_str));
216
+ println!(" {}: {}", format!("{:<16}", "pages").cyan(), format_value_no_flag(&config.pages));
217
+ println!(" {}: {}", format!("{:<16}", "reservedElements").cyan(), format_value_no_flag(format_reserved_elements(&config.reserved_elements)));
218
+ println!(" {}: {}", format!("{:<16}", "root").cyan(), format_value_no_flag(config._root.as_ref().map(|s| s.as_str()).unwrap_or("null")));
219
+ println!(" {}: {}", format!("{:<16}", "runtimeAsIs").cyan(), format_bool_with_flag(config.runtime_as_is, overrides.runtime_as_is, original_runtime_as_is));
220
+ println!(" {}: {}", format!("{:<16}", "source").cyan(), format_value_no_flag(&config._source_str));
221
+ println!(" {}: {}", format!("{:<16}", "sourceMaps").cyan(), format_bool_with_flag(config.source_maps, overrides.source_maps, original_source_maps));
209
222
  println!();
210
223
  } else {
211
224
  // Show version in non-verbose mode