Skip to content

Commit

Permalink
implemented exporting of IDL objects along with ts-js target for comb…
Browse files Browse the repository at this point in the history
…ined TypeScript file with the JavaScript runtime values
  • Loading branch information
lastmjs committed Sep 19, 2024
1 parent 5234523 commit de34652
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 32 deletions.
12 changes: 10 additions & 2 deletions rust/candid_parser/src/bindings/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,16 @@ pub fn chase_def_use<'a>(
Ok(res)
}

pub fn chase_types<'a>(env: &'a TypeEnv, tys: &'a [Type]) -> Result<Vec<&'a str>> {
let mut seen = BTreeSet::new();
pub fn chase_types<'a>(
env: &'a TypeEnv,
tys: &'a [Type],
seen: Option<&BTreeSet<&'a str>>,
) -> Result<Vec<&'a str>> {
let mut seen = if let Some(seen) = seen {
BTreeSet::from_iter(seen.iter().cloned())
} else {
BTreeSet::new()
};
let mut res = Vec::new();
for t in tys.iter() {
chase_type(&mut seen, &mut res, env, t)?;
Expand Down
51 changes: 37 additions & 14 deletions rust/candid_parser/src/bindings/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,13 @@ fn pp_defs<'a>(
env: &'a TypeEnv,
def_list: &'a [&'a str],
recs: &'a BTreeSet<&'a str>,
export: bool,
) -> RcDoc<'a> {
let var_decl = if export { "export const" } else { "const" };

let recs_doc = lines(
recs.iter()
.map(|id| kwd("const").append(ident(id)).append(" = IDL.Rec();")),
.map(|id| kwd(var_decl).append(ident(id)).append(" = IDL.Rec();")),
);
let defs = lines(def_list.iter().map(|id| {
let ty = env.find_type(id).unwrap();
Expand All @@ -206,7 +209,7 @@ fn pp_defs<'a>(
.append(".fill")
.append(enclose("(", pp_ty(ty), ");"))
} else {
kwd("const")
kwd(var_decl)
.append(ident(id))
.append(" = ")
.append(pp_ty(ty))
Expand All @@ -231,35 +234,55 @@ fn pp_actor<'a>(ty: &'a Type, recs: &'a BTreeSet<&'a str>) -> RcDoc<'a> {
}
}

pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
pub fn compile(env: &TypeEnv, actor: &Option<Type>, ts_js: bool) -> String {
match actor {
None => {
let def_list: Vec<_> = env.0.iter().map(|pair| pair.0.as_ref()).collect();
let recs = infer_rec(env, &def_list).unwrap();
let doc = pp_defs(env, &def_list, &recs);
let doc = pp_defs(env, &def_list, &recs, true);
doc.pretty(LINE_WIDTH).to_string()
}
Some(actor) => {
let def_list = chase_actor(env, actor).unwrap();
let recs = infer_rec(env, &def_list).unwrap();
let defs = pp_defs(env, &def_list, &recs);
let defs_exported = pp_defs(env, &def_list, &recs, true);
let defs_not_exported = pp_defs(env, &def_list, &recs, false);
let init = if let TypeInner::Class(ref args, _) = actor.as_ref() {
args.as_slice()
} else {
&[][..]
};
let actor = kwd("return").append(pp_actor(actor, &recs)).append(";");
let body = defs.append(actor);
let doc = str("export const idlFactory = ({ IDL }) => ")
.append(enclose_space("{", body, "};"));
let body = defs_not_exported.append(actor);
let doc = if ts_js {
str("")
} else {
str("import { IDL } from '@dfinity/candid';").append(RcDoc::line())
}
.append(defs_exported)
.append(format!(
"export const idlFactory{} = ({{ IDL }}) => ",
if ts_js { ": idlFactory" } else { "" }
))
.append(enclose_space("{", body, "};"));
// export init args
let init_defs = chase_types(env, init).unwrap();
let init_defs = chase_types(env, init, None).unwrap();
let init_recs = infer_rec(env, &init_defs).unwrap();
let init_defs_doc = pp_defs(env, &init_defs, &init_recs);
let init_defs_deduplicated =
chase_types(env, init, Some(&BTreeSet::from_iter(def_list.clone()))).unwrap();
let init_recs_deduplicated = infer_rec(env, &init_defs_deduplicated).unwrap();
let init_defs_exported_doc =
pp_defs(env, &init_defs_deduplicated, &init_recs_deduplicated, true);
let init_defs_not_exported_doc = pp_defs(env, &init_defs, &init_recs, false);
let init_doc = kwd("return").append(pp_args(init)).append(";");
let init_doc = init_defs_doc.append(init_doc);
let init_doc =
str("export const init = ({ IDL }) => ").append(enclose_space("{", init_doc, "};"));
let init_doc = init_defs_not_exported_doc.append(init_doc);
let init_doc = init_defs_exported_doc
.append(format!(
"export const init{} = ({}) => ",
if ts_js { ": init" } else { "" },
if init_defs.len() != 0 { "{ IDL }" } else { "" }
))
.append(enclose_space("{", init_doc, "};"));
let init_doc = init_doc.pretty(LINE_WIDTH).to_string();
let doc = doc.append(RcDoc::hardline()).append(init_doc);
doc.pretty(LINE_WIDTH).to_string()
Expand Down Expand Up @@ -405,7 +428,7 @@ import { Principal } from './principal';
},
)
.unwrap();
res += &super::compile(&env, &None);
res += &super::compile(&env, &None, false);
for (i, assert) in test.asserts.iter().enumerate() {
let mut types = Vec::new();
for ty in assert.typ.iter() {
Expand Down
1 change: 1 addition & 0 deletions rust/candid_parser/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub mod javascript;
pub mod motoko;
pub mod rust;
pub mod typescript;
pub mod typescript_and_javascript;
51 changes: 40 additions & 11 deletions rust/candid_parser/src/bindings/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,51 @@ fn pp_actor<'a>(env: &'a TypeEnv, ty: &'a Type) -> RcDoc<'a> {
}
}

pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
let header = r#"import type { Principal } from '@dfinity/principal';
import type { ActorMethod } from '@dfinity/agent';
import type { IDL } from '@dfinity/candid';
"#;
pub fn compile(env: &TypeEnv, actor: &Option<Type>, ts_js: bool) -> String {
let actor_method_import = format!(
"import{}{{ ActorMethod }} from '@dfinity/agent';",
if ts_js { " " } else { " type " }
);

let idl_import = format!(
"import{}{{ IDL }} from '@dfinity/candid';",
if ts_js { " " } else { " type " }
);

let principal_import = format!(
"import{}{{ Principal }} from '@dfinity/principal';",
if ts_js { " " } else { " type " }
);

let def_list: Vec<_> = env.0.iter().map(|pair| pair.0.as_ref()).collect();
let defs = pp_defs(env, &def_list);
let actor = match actor {
None => RcDoc::nil(),
Some(actor) => pp_actor(env, actor)
.append(RcDoc::line())
.append("export declare const idlFactory: IDL.InterfaceFactory;")
.append(RcDoc::line())
.append("export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];"),
Some(actor) => {
let idl_factory_export = if ts_js {
"export type idlFactory = IDL.InterfaceFactory;"
} else {
"export declare const idlFactory: IDL.InterfaceFactory;"
};

let init_export = if ts_js {
"export type init = (args: { IDL: typeof IDL }) => IDL.Type[];"
} else {
"export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];"
};

pp_actor(env, actor)
.append(RcDoc::line())
.append(idl_factory_export)
.append(RcDoc::line())
.append(init_export)
}
};
let doc = RcDoc::text(header)
let doc = RcDoc::text(actor_method_import)
.append(RcDoc::line())
.append(idl_import)
.append(RcDoc::line())
.append(principal_import)
.append(RcDoc::line())
.append(defs)
.append(actor);
Expand Down
11 changes: 11 additions & 0 deletions rust/candid_parser/src/bindings/typescript_and_javascript.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use super::javascript::compile as javascript_compile;
use super::typescript::compile as typescript_compile;
use candid::pretty::utils::*;
use candid::types::{Type, TypeEnv};

pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
let ts = typescript_compile(env, actor, true);
let js = javascript_compile(env, actor, true);

str(&ts.clone()).append(js).pretty(LINE_WIDTH).to_string()
}
4 changes: 2 additions & 2 deletions rust/candid_parser/tests/parse_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ fn compiler_test(resource: &str) {
}
{
let mut output = mint.new_goldenfile(filename.with_extension("js")).unwrap();
let content = javascript::compile(&env, &actor);
let content = javascript::compile(&env, &actor, false);
writeln!(output, "{content}").unwrap();
}
{
let mut output = mint
.new_goldenfile(filename.with_extension("d.ts"))
.unwrap();
let content = typescript::compile(&env, &actor);
let content = typescript::compile(&env, &actor, false);
writeln!(output, "{content}").unwrap();
}
}
Expand Down
9 changes: 6 additions & 3 deletions tools/didc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ enum Command {
Bind {
/// Specifies did file for code generation
input: PathBuf,
#[clap(short, long, value_parser = ["js", "ts", "did", "mo", "rs", "rs-agent", "rs-stub"])]
#[clap(short, long, value_parser = ["js", "ts", "ts-js", "did", "mo", "rs", "rs-agent", "rs-stub"])]
/// Specifies target language
target: String,
#[clap(short, long)]
Expand Down Expand Up @@ -230,8 +230,11 @@ fn main() -> Result<()> {
)?);
}
let content = match target.as_str() {
"js" => candid_parser::bindings::javascript::compile(&env, &actor),
"ts" => candid_parser::bindings::typescript::compile(&env, &actor),
"js" => candid_parser::bindings::javascript::compile(&env, &actor, false),
"ts" => candid_parser::bindings::typescript::compile(&env, &actor, false),
"ts-js" => {
candid_parser::bindings::typescript_and_javascript::compile(&env, &actor)
}
"did" => candid_parser::pretty::candid::compile(&env, &actor),
"mo" => candid_parser::bindings::motoko::compile(&env, &actor),
"rs" => {
Expand Down

0 comments on commit de34652

Please sign in to comment.