mod modules_worker;
mod plain_text;
mod project_assets;
mod service_worker;
mod text_blob;
mod wasm_module;
use anyhow::Result;
use reqwest::blocking::multipart::Form;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use crate::settings::binding;
use crate::settings::toml::{Target, TargetType, UploadFormat, UsageModel};
use crate::sites::AssetManifest;
use crate::wranglerjs;
use plain_text::PlainText;
pub use project_assets::{ModuleConfig, ModuleType};
use project_assets::{ModulesAssets, ServiceWorkerAssets};
use text_blob::TextBlob;
use wasm_module::WasmModule;
use super::{krate, Package};
pub fn build(
target: &Target,
asset_manifest: Option<AssetManifest>,
session_config: Option<serde_json::Value>,
) -> Result<Form> {
let target_type = &target.target_type;
let compatibility_date = target.compatibility_date.clone();
let compatibility_flags = target.compatibility_flags.clone();
let kv_namespaces = &target.kv_namespaces;
let r2_buckets = &target.r2_buckets;
let durable_object_classes = target
.durable_objects
.as_ref()
.and_then(|d| d.classes.clone())
.unwrap_or_default();
let mut text_blobs: Vec<TextBlob> = Vec::new();
let mut plain_texts: Vec<PlainText> = Vec::new();
let mut wasm_modules: Vec<WasmModule> = Vec::new();
let usage_model = target.usage_model;
if let Some(blobs) = &target.text_blobs {
for (key, blob_path) in blobs.iter() {
let blob = fs::read_to_string(blob_path)?;
text_blobs.push(TextBlob::new(blob, key.clone())?);
}
}
if let Some(modules) = &target.wasm_modules {
for (key, module_path) in modules.iter() {
wasm_modules.push(WasmModule::new(module_path.clone(), key.clone())?);
}
}
if let Some(vars) = &target.vars {
for (key, value) in vars.iter() {
plain_texts.push(PlainText::new(key.clone(), value.clone())?)
}
}
if let Some(asset_manifest) = asset_manifest {
log::info!("adding __STATIC_CONTENT_MANIFEST");
let binding = "__STATIC_CONTENT_MANIFEST".to_string();
let asset_manifest_blob = get_asset_manifest_blob(asset_manifest)?;
let text_blob = TextBlob::new(asset_manifest_blob, binding)?;
text_blobs.push(text_blob);
}
match target_type {
TargetType::Rust => {
log::info!("Rust project detected. Publishing...");
let name = krate::Krate::new("./")?.name.replace("-", "_");
build_generated_dir()?;
concat_js(&name)?;
let path = PathBuf::from(format!("./pkg/{}_bg.wasm", name));
let binding = "wasm".to_string();
let wasm_module = WasmModule::new(path, binding)?;
wasm_modules.push(wasm_module);
let script_path = PathBuf::from("./worker/generated/script.js");
let assets = ServiceWorkerAssets {
script_path,
compatibility_date,
compatibility_flags,
wasm_modules,
kv_namespaces: kv_namespaces.to_vec(),
r2_buckets: r2_buckets.to_vec(),
durable_object_classes,
text_blobs,
plain_texts,
usage_model,
};
service_worker::build_form(&assets, session_config)
}
TargetType::JavaScript => match &target.build {
Some(config) => match &config.upload {
UploadFormat::ServiceWorker {} => {
log::info!("Plain JavaScript project detected. Publishing...");
let package_dir = target.package_dir()?;
let package = Package::new(&package_dir)?;
let script_path = package_dir.join(package.main(&package_dir)?);
let assets = ServiceWorkerAssets {
script_path,
compatibility_date,
compatibility_flags,
wasm_modules,
kv_namespaces: kv_namespaces.to_vec(),
r2_buckets: r2_buckets.to_vec(),
durable_object_classes,
text_blobs,
plain_texts,
usage_model,
};
service_worker::build_form(&assets, session_config)
}
UploadFormat::Modules { main, dir, rules } => {
let migration = match &target.migrations {
Some(migrations) => migrations.api_migration()?,
None => None,
};
let module_config = ModuleConfig::new(main, dir, rules);
let assets = ModulesAssets::new(
compatibility_date,
compatibility_flags,
module_config.get_modules()?,
kv_namespaces.to_vec(),
r2_buckets.to_vec(),
durable_object_classes,
migration,
text_blobs,
plain_texts,
usage_model,
)?;
modules_worker::build_form(&assets, session_config)
}
},
None => {
log::info!("Plain JavaScript project detected. Publishing...");
let package_dir = target.package_dir()?;
let package = Package::new(&package_dir)?;
let script_path = package.main(&package_dir)?;
let assets = ServiceWorkerAssets {
script_path,
compatibility_date,
compatibility_flags,
wasm_modules,
kv_namespaces: kv_namespaces.to_vec(),
r2_buckets: r2_buckets.to_vec(),
durable_object_classes,
text_blobs,
plain_texts,
usage_model,
};
service_worker::build_form(&assets, session_config)
}
},
TargetType::Webpack => {
log::info!("webpack project detected. Publishing...");
let package_dir = target.package_dir()?;
let bundle = wranglerjs::Bundle::new(&package_dir);
let script_path = bundle.script_path();
if bundle.has_wasm() {
let path = bundle.wasm_path();
let binding = bundle.get_wasm_binding();
let wasm_module = WasmModule::new(path, binding)?;
wasm_modules.push(wasm_module);
}
let assets = ServiceWorkerAssets {
script_path,
compatibility_date,
compatibility_flags,
wasm_modules,
kv_namespaces: kv_namespaces.to_vec(),
r2_buckets: r2_buckets.to_vec(),
durable_object_classes,
text_blobs,
plain_texts,
usage_model,
};
service_worker::build_form(&assets, session_config)
}
}
}
fn get_asset_manifest_blob(asset_manifest: AssetManifest) -> Result<String> {
let asset_manifest = serde_json::to_string(&asset_manifest)?;
Ok(asset_manifest)
}
fn filestem_from_path(path: &Path) -> Option<String> {
path.file_stem()?.to_str().map(|s| s.to_string())
}
fn build_generated_dir() -> Result<()> {
let dir = "./worker/generated";
if !Path::new(dir).is_dir() {
fs::create_dir("./worker/generated")?;
}
Ok(())
}
fn concat_js(name: &str) -> Result<()> {
let bindgen_js_path = format!("./pkg/{}.js", name);
let bindgen_js: String = fs::read_to_string(bindgen_js_path)?.parse()?;
let worker_js: String = fs::read_to_string("./worker/worker.js")?.parse()?;
let js = format!("{} {}", bindgen_js, worker_js);
fs::write("./worker/generated/script.js", js.as_bytes())?;
Ok(())
}