pub mod code_splitter;
pub mod differential_loading;
mod expression_generation;
mod generator;
pub mod minifier;
pub mod module_federation;
pub mod polyfills;
pub mod source_maps;
mod statement_generation;
pub mod tree_shaker;
mod type_conversion;
mod type_definitions;
pub mod v8_optimizer;
pub mod web_workers;
use crate::codegen::backend::{CodegenBackend, CodegenConfig, CodegenOutput, Target};
use crate::parser::Program;
use anyhow::Result;
use generator::JavaScriptGenerator;
pub struct JavaScriptBackend {
}
impl JavaScriptBackend {
pub fn new() -> Self {
Self {}
}
}
impl Default for JavaScriptBackend {
fn default() -> Self {
Self::new()
}
}
impl CodegenBackend for JavaScriptBackend {
fn name(&self) -> &str {
"JavaScript"
}
fn target(&self) -> Target {
Target::JavaScript
}
fn generate(&self, program: &Program, config: &CodegenConfig) -> Result<CodegenOutput> {
let program = if config.tree_shake {
tree_shaker::shake_tree(program)
} else {
program.clone()
};
let mut generator = JavaScriptGenerator::new();
let mut code = generator.generate(&program);
if config.polyfills {
let polyfill_config = polyfills::PolyfillConfig {
target: polyfills::PolyfillTarget::ES2015,
include_promise: true,
include_array_methods: true,
include_object_methods: true,
include_symbol: false,
};
let polyfill_code = polyfills::generate_polyfills(&polyfill_config);
code = format!("{}\n{}", polyfill_code, code);
}
if config.v8_optimize {
let optimizer = v8_optimizer::V8Optimizer::new();
code = optimizer.optimize(&code);
}
if config.minify {
let mut minifier = minifier::Minifier::new();
code = minifier.minify(&code);
}
let source_map_json = if config.source_maps {
let source_map = source_maps::generate_source_map("output.js", "input.wj", "", &code);
Some(serde_json::to_string(&source_map).unwrap_or_default())
} else {
None
};
let mut output = CodegenOutput::new(code, "js".to_string());
if let Some(source_map) = source_map_json {
output = output.with_source_map(source_map);
}
if config.type_definitions {
if let Some(type_defs) = type_definitions::generate_type_definitions(&program) {
output = output.with_type_definitions(type_defs);
}
}
Ok(output)
}
fn make_idiomatic(&self, code: String, _config: &CodegenConfig) -> Result<String> {
Ok(code)
}
fn generate_type_definitions(&self, program: &Program) -> Option<String> {
type_definitions::generate_type_definitions(program)
}
fn generate_additional_files(
&self,
_program: &Program,
_config: &CodegenConfig,
) -> Vec<(String, String)> {
vec![
("package.json".to_string(), self.generate_package_json()),
(".gitignore".to_string(), self.generate_gitignore()),
]
}
fn target_specific_optimizations(&self) -> Vec<String> {
vec![
"async_await_optimization".to_string(),
"closure_optimization".to_string(),
"tree_shaking".to_string(),
]
}
}
impl JavaScriptBackend {
fn generate_package_json(&self) -> String {
r#"{
"name": "windjammer-generated",
"version": "0.1.0",
"description": "Generated by Windjammer JavaScript transpiler",
"type": "module",
"main": "index.js",
"exports": {
".": "./index.js"
},
"engines": {
"node": ">=18.0.0"
},
"keywords": ["windjammer"],
"license": "MIT"
}
"#
.to_string()
}
fn generate_gitignore(&self) -> String {
r#"node_modules/
dist/
*.log
.DS_Store
"#
.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_javascript_backend_creation() {
let backend = JavaScriptBackend::new();
assert_eq!(backend.name(), "JavaScript");
assert_eq!(backend.target(), Target::JavaScript);
}
#[test]
fn test_generates_package_json() {
let backend = JavaScriptBackend::new();
let files = backend
.generate_additional_files(&Program { items: vec![] }, &CodegenConfig::default());
assert!(!files.is_empty());
assert!(files.iter().any(|(name, _)| name == "package.json"));
}
#[test]
fn test_generates_code() {
let backend = JavaScriptBackend::new();
let program = Program { items: vec![] };
let config = CodegenConfig::default();
let result = backend.generate(&program, &config);
assert!(result.is_ok());
}
}