@ape-egg/vibe 2.1.22 → 3.0.0
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 +112 -5
- package/boot.js +4 -4
- package/component.js +27 -29
- package/hot-module-refresh.js +4 -4
- package/index.js +26 -17
- package/llms.txt +36 -5
- package/package.json +20 -14
- package/runtime/affected.js +159 -36
- package/runtime/cleanup.js +45 -1
- package/runtime/component.js +360 -98
- package/runtime/conditionals.js +111 -14
- package/runtime/debug.js +24 -0
- package/runtime/dispatch.js +172 -0
- package/runtime/hydrate.js +277 -110
- package/runtime/index.js +189 -65
- package/runtime/iterate.js +125 -50
- package/runtime/iteration-utils.js +59 -8
- package/runtime/manifest.js +77 -2
- package/runtime/parse.js +81 -11
- package/runtime/pre-compiled-iterations.js +19 -6
- package/runtime/pre-compiled-manifest.js +13 -4
- package/runtime/staging.js +153 -0
- package/runtime/state.js +31 -0
- package/runtime/tracking.js +173 -0
- package/runtime/utils.js +155 -78
- package/spa.js +206 -0
- package/vibe.css +8 -4
- package/CHANGELOG.md +0 -1159
- package/ROADMAP.md +0 -397
- package/compiler/bin/vibe-compile.js +0 -121
- package/compiler/native/.gitkeep +0 -0
- package/compiler/native/vibe-compiler-darwin-arm64 +0 -0
- package/compiler/native/vibe-compiler-linux-x64 +0 -0
- package/compiler/src/Cargo.lock +0 -2023
- package/compiler/src/Cargo.toml +0 -38
- package/compiler/src/compiler/PRE-RENDERING-IMPLEMENTATION.md +0 -241
- package/compiler/src/compiler/binding_case.rs +0 -88
- package/compiler/src/compiler/compile.rs +0 -2522
- package/compiler/src/compiler/component_tagger.rs +0 -469
- package/compiler/src/compiler/iteration_optimizer.rs +0 -455
- package/compiler/src/compiler/js_analyzer.rs +0 -715
- package/compiler/src/compiler/manifest_builder.rs +0 -693
- package/compiler/src/compiler/mod.rs +0 -15
- package/compiler/src/compiler/name_binding_protect.rs +0 -207
- package/compiler/src/compiler/reassignment_analyzer.rs +0 -456
- package/compiler/src/compiler/state_extractor.rs +0 -263
- package/compiler/src/compiler/value_stamper.rs +0 -921
- package/compiler/src/compiler/watcher.rs +0 -1147
- package/compiler/src/config.rs +0 -239
- package/compiler/src/main.rs +0 -347
- package/compiler/src/parser/element.rs +0 -96
- package/compiler/src/parser/html.rs +0 -1004
- package/compiler/src/parser/mod.rs +0 -8
- package/runtime/pre-compiled-manifest.test.mjs +0 -58
- package/runtime/scope.js +0 -50
- package/test-results/.last-run.json +0 -4
package/compiler/src/config.rs
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
use serde::{Deserialize, Serialize};
|
|
2
|
-
use std::fs;
|
|
3
|
-
use std::path::PathBuf;
|
|
4
|
-
use thiserror::Error;
|
|
5
|
-
|
|
6
|
-
#[derive(Error, Debug)]
|
|
7
|
-
pub enum ConfigError {
|
|
8
|
-
#[error("Failed to read package.json: {0}")]
|
|
9
|
-
ReadError(#[from] std::io::Error),
|
|
10
|
-
#[error("Failed to parse package.json: {0}")]
|
|
11
|
-
ParseError(#[from] serde_json::Error),
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
15
|
-
#[serde(rename_all = "camelCase")]
|
|
16
|
-
pub struct VibeCompilerConfig {
|
|
17
|
-
#[serde(default = "default_source")]
|
|
18
|
-
pub source: String,
|
|
19
|
-
#[serde(default = "default_output")]
|
|
20
|
-
pub output: String,
|
|
21
|
-
#[serde(default = "default_components")]
|
|
22
|
-
pub components: String,
|
|
23
|
-
#[serde(default = "default_pages")]
|
|
24
|
-
pub pages: String,
|
|
25
|
-
#[serde(default = "default_assets")]
|
|
26
|
-
pub assets: String,
|
|
27
|
-
#[serde(default)]
|
|
28
|
-
pub root: Option<String>,
|
|
29
|
-
#[serde(default)]
|
|
30
|
-
pub minify: bool,
|
|
31
|
-
#[serde(default)]
|
|
32
|
-
pub elements_as_is: bool,
|
|
33
|
-
#[serde(default)]
|
|
34
|
-
pub source_maps: bool,
|
|
35
|
-
#[serde(default)]
|
|
36
|
-
pub reserved_elements: Vec<String>,
|
|
37
|
-
#[serde(default)]
|
|
38
|
-
pub skip_files: Vec<String>,
|
|
39
|
-
#[serde(default)]
|
|
40
|
-
pub node_modules_as_is: bool,
|
|
41
|
-
#[serde(default)]
|
|
42
|
-
pub components_as_is: bool,
|
|
43
|
-
#[serde(default)]
|
|
44
|
-
pub runtime_as_is: bool,
|
|
45
|
-
#[serde(default)]
|
|
46
|
-
pub iterations_as_is: bool,
|
|
47
|
-
#[serde(default)]
|
|
48
|
-
pub no_clean: bool,
|
|
49
|
-
#[serde(default)]
|
|
50
|
-
pub fouc_as_is: bool,
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
fn default_source() -> String { "./".to_string() }
|
|
54
|
-
fn default_output() -> String { "./compiled".to_string() }
|
|
55
|
-
fn default_components() -> String { "components".to_string() }
|
|
56
|
-
fn default_pages() -> String { "pages".to_string() }
|
|
57
|
-
fn default_assets() -> String { "assets".to_string() }
|
|
58
|
-
|
|
59
|
-
/// Get list of standard HTML5 elements + "component"
|
|
60
|
-
/// Component names matching these are reserved and not allowed
|
|
61
|
-
pub fn get_default_reserved_elements() -> Vec<String> {
|
|
62
|
-
vec![
|
|
63
|
-
"a", "abbr", "address", "area", "article", "aside", "audio",
|
|
64
|
-
"b", "base", "bdi", "bdo", "blockquote", "body", "br", "button",
|
|
65
|
-
"canvas", "caption", "cite", "code", "col", "colgroup",
|
|
66
|
-
"data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt",
|
|
67
|
-
"em", "embed",
|
|
68
|
-
"fieldset", "figcaption", "figure", "footer", "form",
|
|
69
|
-
"h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html",
|
|
70
|
-
"i", "iframe", "img", "input", "ins",
|
|
71
|
-
"kbd",
|
|
72
|
-
"label", "legend", "li", "link",
|
|
73
|
-
"main", "map", "mark", "menu", "meta", "meter",
|
|
74
|
-
"nav", "noscript",
|
|
75
|
-
"object", "ol", "optgroup", "option", "output",
|
|
76
|
-
"p", "param", "picture", "pre", "progress",
|
|
77
|
-
"q",
|
|
78
|
-
"rp", "rt", "ruby",
|
|
79
|
-
"s", "samp", "script", "search", "section", "select", "slot", "small", "source", "span", "strong", "style", "sub", "summary", "sup", "svg",
|
|
80
|
-
"table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track",
|
|
81
|
-
"u", "ul",
|
|
82
|
-
"var", "video",
|
|
83
|
-
"wbr",
|
|
84
|
-
"component", // Always reserved
|
|
85
|
-
].into_iter().map(|s| s.to_string()).collect()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
impl Default for VibeCompilerConfig {
|
|
89
|
-
fn default() -> Self {
|
|
90
|
-
Self {
|
|
91
|
-
source: default_source(),
|
|
92
|
-
output: default_output(),
|
|
93
|
-
components: default_components(),
|
|
94
|
-
pages: default_pages(),
|
|
95
|
-
assets: default_assets(),
|
|
96
|
-
root: None,
|
|
97
|
-
minify: false,
|
|
98
|
-
elements_as_is: false,
|
|
99
|
-
source_maps: false,
|
|
100
|
-
reserved_elements: vec![],
|
|
101
|
-
skip_files: vec![],
|
|
102
|
-
node_modules_as_is: false,
|
|
103
|
-
components_as_is: false,
|
|
104
|
-
runtime_as_is: false,
|
|
105
|
-
iterations_as_is: false,
|
|
106
|
-
no_clean: false,
|
|
107
|
-
fouc_as_is: false,
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
#[derive(Debug, Clone)]
|
|
113
|
-
pub struct Config {
|
|
114
|
-
pub source: PathBuf,
|
|
115
|
-
pub output: PathBuf,
|
|
116
|
-
pub _source_str: String, // Original config value for display
|
|
117
|
-
pub _output_str: String, // Original config value for display
|
|
118
|
-
pub components: String,
|
|
119
|
-
pub pages: String,
|
|
120
|
-
pub _assets: String,
|
|
121
|
-
pub root: Option<String>,
|
|
122
|
-
pub minify: bool,
|
|
123
|
-
pub elements_as_is: bool,
|
|
124
|
-
pub source_maps: bool,
|
|
125
|
-
pub reserved_elements: Vec<String>,
|
|
126
|
-
pub skip_files: Vec<String>,
|
|
127
|
-
pub node_modules_as_is: bool,
|
|
128
|
-
pub components_as_is: bool,
|
|
129
|
-
pub runtime_as_is: bool,
|
|
130
|
-
pub iterations_as_is: bool,
|
|
131
|
-
pub no_clean: bool,
|
|
132
|
-
pub fouc_as_is: bool,
|
|
133
|
-
pub working_dir: PathBuf,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
impl Config {
|
|
137
|
-
/// Load config from package.json, or use defaults if not found
|
|
138
|
-
pub fn load(working_dir: PathBuf) -> Self {
|
|
139
|
-
let package_path = working_dir.join("package.json");
|
|
140
|
-
|
|
141
|
-
let config = if package_path.exists() {
|
|
142
|
-
fs::read_to_string(&package_path)
|
|
143
|
-
.ok()
|
|
144
|
-
.and_then(|content| serde_json::from_str::<serde_json::Value>(&content).ok())
|
|
145
|
-
.and_then(|package| package.get("vibe-compiler").cloned())
|
|
146
|
-
.and_then(|vibe_config| serde_json::from_value::<VibeCompilerConfig>(vibe_config).ok())
|
|
147
|
-
.unwrap_or_default()
|
|
148
|
-
} else {
|
|
149
|
-
VibeCompilerConfig::default()
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
let source_str = config.source.clone();
|
|
153
|
-
let output_str = config.output.clone();
|
|
154
|
-
let source = working_dir.join(&config.source);
|
|
155
|
-
let output = working_dir.join(&config.output);
|
|
156
|
-
|
|
157
|
-
// Combine default reserved elements with user-provided ones
|
|
158
|
-
let mut reserved_elements = get_default_reserved_elements();
|
|
159
|
-
reserved_elements.extend(config.reserved_elements);
|
|
160
|
-
|
|
161
|
-
// Combine built-in skip patterns with user-provided ones (append, not replace)
|
|
162
|
-
let mut skip_files: Vec<String> = crate::compiler::compile::SKIP_FILES
|
|
163
|
-
.iter()
|
|
164
|
-
.map(|s| s.to_string())
|
|
165
|
-
.collect();
|
|
166
|
-
skip_files.extend(config.skip_files);
|
|
167
|
-
|
|
168
|
-
Self {
|
|
169
|
-
source,
|
|
170
|
-
output,
|
|
171
|
-
_source_str: source_str,
|
|
172
|
-
_output_str: output_str,
|
|
173
|
-
components: config.components,
|
|
174
|
-
pages: config.pages,
|
|
175
|
-
_assets: config.assets,
|
|
176
|
-
root: config.root,
|
|
177
|
-
minify: config.minify,
|
|
178
|
-
elements_as_is: config.elements_as_is,
|
|
179
|
-
source_maps: config.source_maps,
|
|
180
|
-
reserved_elements,
|
|
181
|
-
skip_files,
|
|
182
|
-
node_modules_as_is: config.node_modules_as_is,
|
|
183
|
-
components_as_is: config.components_as_is,
|
|
184
|
-
runtime_as_is: config.runtime_as_is,
|
|
185
|
-
iterations_as_is: config.iterations_as_is,
|
|
186
|
-
no_clean: config.no_clean,
|
|
187
|
-
fouc_as_is: config.fouc_as_is,
|
|
188
|
-
working_dir,
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
pub fn components_path(&self) -> PathBuf {
|
|
193
|
-
self.source.join(&self.components)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
pub fn _pages_path(&self) -> PathBuf {
|
|
197
|
-
self.source.join(&self.pages)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
pub fn _assets_path(&self) -> PathBuf {
|
|
201
|
-
self.source.join(&self._assets)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
pub fn init_config(working_dir: &PathBuf) -> Result<(), ConfigError> {
|
|
206
|
-
let package_path = working_dir.join("package.json");
|
|
207
|
-
|
|
208
|
-
if !package_path.exists() {
|
|
209
|
-
// Create minimal package.json with vibe-compiler config
|
|
210
|
-
let config = VibeCompilerConfig::default();
|
|
211
|
-
let package = serde_json::json!({
|
|
212
|
-
"vibe-compiler": config
|
|
213
|
-
});
|
|
214
|
-
let formatted = serde_json::to_string_pretty(&package)?;
|
|
215
|
-
fs::write(&package_path, formatted)?;
|
|
216
|
-
println!("Created package.json with vibe-compiler config");
|
|
217
|
-
return Ok(());
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
let content = fs::read_to_string(&package_path)?;
|
|
221
|
-
let mut package: serde_json::Value = serde_json::from_str(&content)?;
|
|
222
|
-
|
|
223
|
-
// Don't overwrite existing config
|
|
224
|
-
if package.get("vibe-compiler").is_some() {
|
|
225
|
-
println!("vibe-compiler config already exists in package.json");
|
|
226
|
-
return Ok(());
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
let default_config = VibeCompilerConfig::default();
|
|
230
|
-
let config_value = serde_json::to_value(&default_config)?;
|
|
231
|
-
|
|
232
|
-
package["vibe-compiler"] = config_value;
|
|
233
|
-
|
|
234
|
-
let formatted = serde_json::to_string_pretty(&package)?;
|
|
235
|
-
fs::write(&package_path, formatted)?;
|
|
236
|
-
|
|
237
|
-
println!("Added vibe-compiler config to package.json");
|
|
238
|
-
Ok(())
|
|
239
|
-
}
|
package/compiler/src/main.rs
DELETED
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
mod config;
|
|
2
|
-
mod parser;
|
|
3
|
-
mod compiler;
|
|
4
|
-
|
|
5
|
-
use clap::Parser as ClapParser;
|
|
6
|
-
use colored::Colorize;
|
|
7
|
-
use std::path::PathBuf;
|
|
8
|
-
|
|
9
|
-
use config::Config;
|
|
10
|
-
use compiler::Compiler;
|
|
11
|
-
|
|
12
|
-
/// Track which config values were overridden by flags
|
|
13
|
-
#[derive(Debug, Default)]
|
|
14
|
-
struct ConfigOverrides {
|
|
15
|
-
minify: bool,
|
|
16
|
-
elements_as_is: bool,
|
|
17
|
-
source_maps: bool,
|
|
18
|
-
node_modules_as_is: bool,
|
|
19
|
-
components_as_is: bool,
|
|
20
|
-
runtime_as_is: bool,
|
|
21
|
-
iterations_as_is: bool,
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/// Vibe Compiler - Compiles Vibe source files into optimized output
|
|
25
|
-
#[derive(ClapParser, Debug)]
|
|
26
|
-
#[command(name = "vibe-compile")]
|
|
27
|
-
#[command(author = "Kim Korte")]
|
|
28
|
-
#[command(version)]
|
|
29
|
-
#[command(about = "Compiles Vibe source files into optimized output")]
|
|
30
|
-
struct Args {
|
|
31
|
-
/// Working directory (defaults to current directory)
|
|
32
|
-
#[arg(long)]
|
|
33
|
-
cwd: Option<PathBuf>,
|
|
34
|
-
|
|
35
|
-
/// Watch for file changes
|
|
36
|
-
#[arg(long)]
|
|
37
|
-
watch: bool,
|
|
38
|
-
|
|
39
|
-
/// Minify output
|
|
40
|
-
#[arg(long)]
|
|
41
|
-
minify: bool,
|
|
42
|
-
|
|
43
|
-
/// Enable verbose logging with step-by-step output
|
|
44
|
-
#[arg(long)]
|
|
45
|
-
verbose: bool,
|
|
46
|
-
|
|
47
|
-
/// Keep custom HTML elements as-is (don't transform to divs for accessibility)
|
|
48
|
-
#[arg(long, name = "elements-as-is")]
|
|
49
|
-
elements_as_is: bool,
|
|
50
|
-
|
|
51
|
-
/// Generate source maps
|
|
52
|
-
#[arg(long, name = "source-maps")]
|
|
53
|
-
source_maps: bool,
|
|
54
|
-
|
|
55
|
-
/// Initialize configuration in package.json
|
|
56
|
-
#[arg(long)]
|
|
57
|
-
init: bool,
|
|
58
|
-
|
|
59
|
-
/// Custom source directory (overrides config)
|
|
60
|
-
#[arg(long)]
|
|
61
|
-
source: Option<PathBuf>,
|
|
62
|
-
|
|
63
|
-
/// Custom output directory (overrides config)
|
|
64
|
-
#[arg(long)]
|
|
65
|
-
output: Option<PathBuf>,
|
|
66
|
-
|
|
67
|
-
/// Copy node_modules as-is without production-only install (overrides config)
|
|
68
|
-
#[arg(long, name = "node-modules-as-is")]
|
|
69
|
-
node_modules_as_is: bool,
|
|
70
|
-
|
|
71
|
-
/// Keep components directory and <component> tags as-is (don't inline components)
|
|
72
|
-
#[arg(long, name = "components-as-is")]
|
|
73
|
-
components_as_is: bool,
|
|
74
|
-
|
|
75
|
-
/// Skip manifest generation - output will use runtime-only mode (no FOUC prevention)
|
|
76
|
-
#[arg(long, name = "runtime-as-is")]
|
|
77
|
-
runtime_as_is: bool,
|
|
78
|
-
|
|
79
|
-
/// Skip iteration optimization - use runtime DOM cloning instead of compiled batch functions
|
|
80
|
-
#[arg(long, name = "iterations-as-is")]
|
|
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,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
fn main() {
|
|
93
|
-
let args = Args::parse();
|
|
94
|
-
|
|
95
|
-
// Determine working directory
|
|
96
|
-
let working_dir = args.cwd
|
|
97
|
-
.unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));
|
|
98
|
-
|
|
99
|
-
if args.init {
|
|
100
|
-
match config::init_config(&working_dir) {
|
|
101
|
-
Ok(_) => {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
Err(e) => {
|
|
105
|
-
eprintln!("{}: {}", "Error".red(), e);
|
|
106
|
-
std::process::exit(1);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Load config from package.json (uses defaults if not found)
|
|
112
|
-
let mut config = Config::load(working_dir);
|
|
113
|
-
|
|
114
|
-
// Override config with CLI args
|
|
115
|
-
if let Some(source) = args.source {
|
|
116
|
-
config.source = config.working_dir.join(source);
|
|
117
|
-
}
|
|
118
|
-
if let Some(output) = args.output {
|
|
119
|
-
config.output = config.working_dir.join(output);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Store original config values before applying flag overrides
|
|
123
|
-
let original_minify = config.minify;
|
|
124
|
-
let original_elements_as_is = config.elements_as_is;
|
|
125
|
-
let original_source_maps = config.source_maps;
|
|
126
|
-
let original_node_modules_as_is = config.node_modules_as_is;
|
|
127
|
-
let original_components_as_is = config.components_as_is;
|
|
128
|
-
let original_runtime_as_is = config.runtime_as_is;
|
|
129
|
-
let original_iterations_as_is = config.iterations_as_is;
|
|
130
|
-
|
|
131
|
-
// Track overrides for verbose output
|
|
132
|
-
let mut overrides = ConfigOverrides::default();
|
|
133
|
-
|
|
134
|
-
if args.minify {
|
|
135
|
-
overrides.minify = !original_minify;
|
|
136
|
-
config.minify = true;
|
|
137
|
-
}
|
|
138
|
-
if args.elements_as_is {
|
|
139
|
-
overrides.elements_as_is = !original_elements_as_is;
|
|
140
|
-
config.elements_as_is = true;
|
|
141
|
-
}
|
|
142
|
-
if args.source_maps {
|
|
143
|
-
overrides.source_maps = !original_source_maps;
|
|
144
|
-
config.source_maps = true;
|
|
145
|
-
}
|
|
146
|
-
if args.node_modules_as_is {
|
|
147
|
-
overrides.node_modules_as_is = !original_node_modules_as_is;
|
|
148
|
-
config.node_modules_as_is = true;
|
|
149
|
-
}
|
|
150
|
-
if args.components_as_is {
|
|
151
|
-
overrides.components_as_is = !original_components_as_is;
|
|
152
|
-
config.components_as_is = true;
|
|
153
|
-
}
|
|
154
|
-
if args.runtime_as_is {
|
|
155
|
-
overrides.runtime_as_is = !original_runtime_as_is;
|
|
156
|
-
config.runtime_as_is = true;
|
|
157
|
-
}
|
|
158
|
-
if args.iterations_as_is {
|
|
159
|
-
overrides.iterations_as_is = !original_iterations_as_is;
|
|
160
|
-
config.iterations_as_is = true;
|
|
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
|
-
}
|
|
168
|
-
|
|
169
|
-
if args.verbose {
|
|
170
|
-
println!("{}", format!("Vibe Compiler v{}", env!("CARGO_PKG_VERSION")).cyan().bold());
|
|
171
|
-
println!();
|
|
172
|
-
println!(" {}: {}", "Working dir".cyan(), config.working_dir.display());
|
|
173
|
-
println!(" {}: {}", "Source".cyan(), config.source.display());
|
|
174
|
-
println!(" {}: {}", "Output".cyan(), config.output.display());
|
|
175
|
-
println!(" {}: {}", "Components".cyan(), config.components);
|
|
176
|
-
println!(" {}: {}", "Pages".cyan(), config.pages);
|
|
177
|
-
println!(" {}: {}", "Assets".cyan(), config._assets);
|
|
178
|
-
println!();
|
|
179
|
-
|
|
180
|
-
// Show config values (alphabetically ordered)
|
|
181
|
-
println!("{}", "Compiler config".cyan().bold());
|
|
182
|
-
println!();
|
|
183
|
-
println!(" CLI flags override package.json config");
|
|
184
|
-
println!();
|
|
185
|
-
|
|
186
|
-
fn format_bool_with_flag(final_value: bool, overridden: bool, original_value: bool) -> String {
|
|
187
|
-
if overridden {
|
|
188
|
-
format!("{} (config: {}, flag: {})", final_value.to_string().green(), original_value.to_string().yellow(), final_value.to_string().green())
|
|
189
|
-
} else {
|
|
190
|
-
format!("{} (config: {})", final_value.to_string().green(), original_value.to_string().yellow())
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
fn format_value_no_flag<T: std::fmt::Display>(value: T) -> String {
|
|
195
|
-
format!("{} (config: {})", value.to_string().green(), value.to_string().yellow())
|
|
196
|
-
}
|
|
197
|
-
|
|
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
|
-
fn format_list_preview(items: &[String]) -> String {
|
|
208
|
-
if items.len() <= 2 {
|
|
209
|
-
format!("{:?}", items)
|
|
210
|
-
} else {
|
|
211
|
-
format!("[{:?}, {:?}, ... + {} more]", items[0], items[1], items.len() - 2)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Alphabetically ordered with padding (longest key is "reservedElements" = 16 chars)
|
|
216
|
-
println!(" {}: {}", format!("{:<16}", "assets").cyan(), format_value_no_flag(&config._assets));
|
|
217
|
-
println!(" {}: {}", format!("{:<16}", "components").cyan(), format_value_no_flag(&config.components));
|
|
218
|
-
println!(" {}: {}", format!("{:<16}", "componentsAsIs").cyan(), format_bool_with_flag(config.components_as_is, overrides.components_as_is, original_components_as_is));
|
|
219
|
-
println!(" {}: {}", format!("{:<16}", "elementsAsIs").cyan(), format_bool_with_flag(config.elements_as_is, overrides.elements_as_is, original_elements_as_is));
|
|
220
|
-
println!(" {}: {}", format!("{:<16}", "iterationsAsIs").cyan(), format_bool_with_flag(config.iterations_as_is, overrides.iterations_as_is, original_iterations_as_is));
|
|
221
|
-
println!(" {}: {}", format!("{:<16}", "minify").cyan(), format_bool_with_flag(config.minify, overrides.minify, original_minify));
|
|
222
|
-
println!(" {}: {}", format!("{:<16}", "nodeModulesAsIs").cyan(), format_bool_with_flag(config.node_modules_as_is, overrides.node_modules_as_is, original_node_modules_as_is));
|
|
223
|
-
println!(" {}: {}", format!("{:<16}", "output").cyan(), format_value_no_flag(&config._output_str));
|
|
224
|
-
println!(" {}: {}", format!("{:<16}", "pages").cyan(), format_value_no_flag(&config.pages));
|
|
225
|
-
println!(" {}: {}", format!("{:<16}", "reservedElements").cyan(), format_value_no_flag(format_reserved_elements(&config.reserved_elements)));
|
|
226
|
-
println!(" {}: {}", format!("{:<16}", "root").cyan(), format_value_no_flag(config.root.as_ref().map(|s| s.as_str()).unwrap_or("null")));
|
|
227
|
-
println!(" {}: {}", format!("{:<16}", "runtimeAsIs").cyan(), format_bool_with_flag(config.runtime_as_is, overrides.runtime_as_is, original_runtime_as_is));
|
|
228
|
-
println!(" {}: {}", format!("{:<16}", "skipFiles").cyan(), format_value_no_flag(format_list_preview(&config.skip_files)));
|
|
229
|
-
println!(" {}: {}", format!("{:<16}", "source").cyan(), format_value_no_flag(&config._source_str));
|
|
230
|
-
println!(" {}: {}", format!("{:<16}", "sourceMaps").cyan(), format_bool_with_flag(config.source_maps, overrides.source_maps, original_source_maps));
|
|
231
|
-
println!();
|
|
232
|
-
} else {
|
|
233
|
-
// Show version in non-verbose mode
|
|
234
|
-
println!("{}", format!("Vibe Compiler v{}", env!("CARGO_PKG_VERSION")).cyan());
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Handle watch mode
|
|
238
|
-
if args.watch {
|
|
239
|
-
if let Err(e) = compiler::watcher::watch(config, args.verbose) {
|
|
240
|
-
eprintln!("{}: {}", "Error".red(), e);
|
|
241
|
-
std::process::exit(1);
|
|
242
|
-
}
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Save runtime_as_is before moving config
|
|
247
|
-
let runtime_as_is = config.runtime_as_is;
|
|
248
|
-
|
|
249
|
-
let mut compiler = Compiler::new(config, args.verbose);
|
|
250
|
-
|
|
251
|
-
match compiler.compile() {
|
|
252
|
-
Ok(stats) => {
|
|
253
|
-
// Generate manifests BEFORE showing success (unless runtime-as-is is enabled)
|
|
254
|
-
let mut manifest_time_ms = 0.0;
|
|
255
|
-
let mut manifest_stats_result = None;
|
|
256
|
-
if !runtime_as_is {
|
|
257
|
-
match compiler.generate_manifests() {
|
|
258
|
-
Ok(manifest_stats) => {
|
|
259
|
-
manifest_time_ms = manifest_stats.total_time_ms;
|
|
260
|
-
manifest_stats_result = Some(manifest_stats);
|
|
261
|
-
}
|
|
262
|
-
Err(e) => {
|
|
263
|
-
eprintln!("\n{}: Manifest generation failed: {}", "Warning".yellow(), e);
|
|
264
|
-
eprintln!("Compilation succeeded but manifests were not generated.");
|
|
265
|
-
eprintln!("Use --runtime-as-is to skip manifest generation.");
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Show success headline
|
|
271
|
-
println!("\n{}", format!("Compilation successful! (v{}) ✅", env!("CARGO_PKG_VERSION")).green().bold());
|
|
272
|
-
println!();
|
|
273
|
-
|
|
274
|
-
// Show individual phase timings (validation first, then components, HTML, manifests, copied)
|
|
275
|
-
|
|
276
|
-
// Show validation time if it happened
|
|
277
|
-
if let Some(validation_time) = stats.validation_time_ms {
|
|
278
|
-
println!("* Validated components in {:.0}ms", validation_time);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
let total_components_unique = stats.internal_components_unique + stats.external_components_unique;
|
|
282
|
-
|
|
283
|
-
// Always show components line
|
|
284
|
-
if stats.components_as_is {
|
|
285
|
-
println!("* Compiled components (0 internal, 0 external) - \"components-as-is\": true");
|
|
286
|
-
} else if total_components_unique > 0 {
|
|
287
|
-
println!("* Compiled components ({} internal, {} external) in {:.0}ms",
|
|
288
|
-
stats.internal_components_unique,
|
|
289
|
-
stats.external_components_unique,
|
|
290
|
-
stats.components_time_ms
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if stats.files_compiled > 0 {
|
|
295
|
-
println!("* Compiled HTML ({} file{}) in {:.0}ms",
|
|
296
|
-
stats.files_compiled,
|
|
297
|
-
if stats.files_compiled == 1 { "" } else { "s" },
|
|
298
|
-
stats.compile_time_ms
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Show manifest stats between HTML and Copied files
|
|
303
|
-
if let Some(manifest_stats) = manifest_stats_result {
|
|
304
|
-
println!("* Generated manifests ({} file{}, {} skipped) in {:.0}ms",
|
|
305
|
-
manifest_stats.pages_processed,
|
|
306
|
-
if manifest_stats.pages_processed == 1 { "" } else { "s" },
|
|
307
|
-
manifest_stats.pages_skipped,
|
|
308
|
-
manifest_stats.total_time_ms
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
if stats.files_copied > 0 {
|
|
313
|
-
println!("* Copied files ({} file{}) in {:.0}ms",
|
|
314
|
-
stats.files_copied,
|
|
315
|
-
if stats.files_copied == 1 { "" } else { "s" },
|
|
316
|
-
stats.copy_time_ms
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Show node_modules handling
|
|
321
|
-
if let Some(nm_time) = stats.node_modules_time_ms {
|
|
322
|
-
if stats.node_modules_copied_as_is {
|
|
323
|
-
println!("* copied node_modules in {:.0}ms", nm_time);
|
|
324
|
-
} else if let Some(ref pkg_manager) = stats.package_manager {
|
|
325
|
-
println!("* {} install in {:.0}ms", pkg_manager, nm_time);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Calculate total duration as sum of all individual operations
|
|
330
|
-
let total_duration_ms = stats.validation_time_ms.unwrap_or(0.0)
|
|
331
|
-
+ stats.components_time_ms
|
|
332
|
-
+ stats.compile_time_ms
|
|
333
|
-
+ stats.copy_time_ms
|
|
334
|
-
+ stats.node_modules_time_ms.unwrap_or(0.0)
|
|
335
|
-
+ manifest_time_ms;
|
|
336
|
-
|
|
337
|
-
println!("\n{} in {:.0}ms", "Compiled".green(), total_duration_ms);
|
|
338
|
-
|
|
339
|
-
// Exit explicitly to kill background server thread
|
|
340
|
-
std::process::exit(0);
|
|
341
|
-
}
|
|
342
|
-
Err(e) => {
|
|
343
|
-
eprintln!("{}: {}", "Error".red(), e);
|
|
344
|
-
std::process::exit(1);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
|
-
use std::path::PathBuf;
|
|
3
|
-
|
|
4
|
-
/// Represents a parsed Vibe element/component
|
|
5
|
-
#[derive(Debug, Clone)]
|
|
6
|
-
pub struct Element {
|
|
7
|
-
/// Tag name (derived from filename, e.g., "counter" from counter.html)
|
|
8
|
-
pub _tag_name: String,
|
|
9
|
-
/// Original file path
|
|
10
|
-
pub _path: PathBuf,
|
|
11
|
-
/// Raw HTML content (the inner content of the element)
|
|
12
|
-
pub content: String,
|
|
13
|
-
/// Bindings found in this element (@[property])
|
|
14
|
-
pub _bindings: Vec<String>,
|
|
15
|
-
/// Nested element references (custom tags used within this element)
|
|
16
|
-
pub _nested_elements: Vec<String>,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
impl Element {
|
|
20
|
-
pub fn new(tag_name: String, path: PathBuf, content: String) -> Self {
|
|
21
|
-
let bindings = extract_bindings(&content);
|
|
22
|
-
let nested_elements = extract_custom_tags(&content);
|
|
23
|
-
|
|
24
|
-
Self {
|
|
25
|
-
_tag_name: tag_name,
|
|
26
|
-
_path: path,
|
|
27
|
-
content,
|
|
28
|
-
_bindings: bindings,
|
|
29
|
-
_nested_elements: nested_elements,
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/// Extract all @[property] bindings from content
|
|
35
|
-
fn extract_bindings(content: &str) -> Vec<String> {
|
|
36
|
-
let mut bindings = Vec::new();
|
|
37
|
-
let re = regex::Regex::new(r"@\[([^\]]+)\]").unwrap();
|
|
38
|
-
|
|
39
|
-
for cap in re.captures_iter(content) {
|
|
40
|
-
if let Some(m) = cap.get(1) {
|
|
41
|
-
let binding = m.as_str().to_string();
|
|
42
|
-
if !bindings.contains(&binding) {
|
|
43
|
-
bindings.push(binding);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
bindings
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/// Extract custom HTML tags (potential element references)
|
|
52
|
-
/// Custom tags are lowercase with hyphens, or single-word lowercase non-standard tags
|
|
53
|
-
fn extract_custom_tags(content: &str) -> Vec<String> {
|
|
54
|
-
let mut tags = Vec::new();
|
|
55
|
-
// Match opening tags: <tag-name> or <tagname (self-closing handled too)
|
|
56
|
-
let re = regex::Regex::new(r"<([a-z][a-z0-9-]*)[>\s/]").unwrap();
|
|
57
|
-
|
|
58
|
-
// Standard HTML5 elements to exclude
|
|
59
|
-
let standard_tags: std::collections::HashSet<&str> = [
|
|
60
|
-
"a", "abbr", "address", "area", "article", "aside", "audio",
|
|
61
|
-
"b", "base", "bdi", "bdo", "blockquote", "body", "br", "button",
|
|
62
|
-
"canvas", "caption", "cite", "code", "col", "colgroup",
|
|
63
|
-
"data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt",
|
|
64
|
-
"em", "embed",
|
|
65
|
-
"fieldset", "figcaption", "figure", "footer", "form",
|
|
66
|
-
"h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html",
|
|
67
|
-
"i", "iframe", "img", "input", "ins",
|
|
68
|
-
"kbd",
|
|
69
|
-
"label", "legend", "li", "link",
|
|
70
|
-
"main", "map", "mark", "menu", "meta", "meter",
|
|
71
|
-
"nav", "noscript",
|
|
72
|
-
"object", "ol", "optgroup", "option", "output",
|
|
73
|
-
"p", "param", "picture", "pre", "progress",
|
|
74
|
-
"q",
|
|
75
|
-
"rp", "rt", "ruby",
|
|
76
|
-
"s", "samp", "script", "search", "section", "select", "slot", "small", "source", "span", "strong", "style", "sub", "summary", "sup", "svg",
|
|
77
|
-
"table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track",
|
|
78
|
-
"u", "ul",
|
|
79
|
-
"var", "video",
|
|
80
|
-
"wbr",
|
|
81
|
-
].into_iter().collect();
|
|
82
|
-
|
|
83
|
-
for cap in re.captures_iter(content) {
|
|
84
|
-
if let Some(m) = cap.get(1) {
|
|
85
|
-
let tag = m.as_str().to_string();
|
|
86
|
-
if !standard_tags.contains(tag.as_str()) && !tags.contains(&tag) {
|
|
87
|
-
tags.push(tag);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
tags
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/// Cache of loaded elements by tag name
|
|
96
|
-
pub type ElementCache = HashMap<String, Element>;
|