use crate::{
ast::{make_ident, make_str},
dictionary_imports::InjectedImports,
packages::NESTED_DICTIONARIES_SUBDIR,
paths::relative_import_path,
};
use std::{collections::HashSet, path::Path};
use swc_core::{common::DUMMY_SP, ecma::ast::*};
pub struct DictionaryDirs<'a> {
pub dictionaries_dir: &'a str,
pub dynamic_dictionaries_dir: &'a str,
pub fetch_dictionaries_dir: &'a str,
}
fn json_import_attributes() -> Box<ObjectLit> {
Box::new(ObjectLit {
span: DUMMY_SP,
props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(make_ident("type").into()),
value: Box::new(Expr::Lit(Lit::Str(make_str("json")))),
})))],
})
}
fn default_import(local: Ident, specifier: String, with_json_attribute: bool) -> ModuleItem {
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
span: DUMMY_SP,
specifiers: vec![ImportSpecifier::Default(ImportDefaultSpecifier {
span: DUMMY_SP,
local,
})],
src: Box::new(Str::from(specifier)),
type_only: false,
with: with_json_attribute.then(json_import_attributes),
phase: ImportPhase::Evaluation,
}))
}
fn import_insert_position(body: &[ModuleItem]) -> usize {
let mut insert_position = 0;
for item in body {
let ModuleItem::Stmt(Stmt::Expr(ExprStmt { expr, .. })) = item else {
break;
};
let Expr::Lit(Lit::Str(Str { value, .. })) = &**expr else {
break;
};
let Some(directive) = value.as_str() else {
break;
};
if directive.starts_with("import") || directive.starts_with("require") {
break;
}
insert_position += 1;
}
insert_position
}
pub fn inject_dictionary_imports(
program: &mut Program,
injected_imports: &InjectedImports,
dirs: &DictionaryDirs<'_>,
nesting_dictionary_keys: &HashSet<&str>,
source_file_path: &str,
) {
let Program::Module(Module { body, .. }) = program else {
return;
};
let file_dir_abs = Path::new(source_file_path)
.parent()
.unwrap_or_else(|| Path::new("/"));
let mut insert_position = import_insert_position(body);
for (key, ident) in injected_imports.static_imports.iter().rev() {
let has_nested_dictionaries = nesting_dictionary_keys.contains(key.as_str());
let dictionary_file_abs = if has_nested_dictionaries {
Path::new(dirs.dictionaries_dir)
.join(NESTED_DICTIONARIES_SUBDIR)
.join(format!("{}.mjs", key))
} else {
Path::new(dirs.dictionaries_dir).join(format!("{}.json", key))
};
body.insert(
insert_position,
default_import(
ident.clone(),
relative_import_path(&dictionary_file_abs, file_dir_abs),
!has_nested_dictionaries,
),
);
insert_position += 1;
}
for (key, ident) in injected_imports.dynamic_imports.iter().rev() {
let is_fetch_loader = ident.sym.as_ref().ends_with("_fetch");
let target_dir = if is_fetch_loader {
dirs.fetch_dictionaries_dir
} else {
dirs.dynamic_dictionaries_dir
};
let dictionary_file_abs = Path::new(target_dir).join(format!("{}.mjs", key));
body.insert(
insert_position,
default_import(
ident.clone(),
relative_import_path(&dictionary_file_abs, file_dir_abs),
false,
),
);
insert_position += 1;
}
}