Skip to content

Commit 5f419c5

Browse files
authored
feat: JavaScript target IDL type object exports and factory function deprecations (#665)
**Overview** The JavaScript target would be more useful (especially to Azle developers) if it exported the IDL type objects that it generates. We also no longer need the factory functions as of `3.0.0`, thus we will mark them deprecated and provide direct exports for that previous functionality. **Requirements** The JavaScript target should export all of the IDL type objects, including the generated `IDL.Service` and `init args`, without the need to use a factory function. **Considerations** There are no breaking changes besides some formatting of the generated JavaScript files. The `idflFactory` and `init` functions have been marked as deprecated but not removed. Developers can choose to import the `IDL` type objects now, but they are not forced to. This is mainly an ergonomic change with deprecations to prepare for a future with the best developer experience by default.
1 parent 3721557 commit 5f419c5

33 files changed

Lines changed: 801 additions & 38 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
* Non-breaking changes:
1717
+ Added `pp_named_args`, `pp_named_init_args` in `pretty::candid` module.
18+
+ The `JavaScript` `didc` target now exports its generated IDL type objects.
19+
+ The `JavaScript` and `TypeScript` `didc` targets now export `idlService` and `idlInitArgs` (non-factory-function altneratives to `idlFactory` and `init`).
1820

1921
### candid_parser
2022

rust/candid_parser/src/bindings/javascript.rs

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -193,25 +193,36 @@ fn pp_defs<'a>(
193193
env: &'a TypeEnv,
194194
def_list: &'a [&'a str],
195195
recs: &'a BTreeSet<&'a str>,
196+
export: bool,
196197
) -> RcDoc<'a> {
197-
let recs_doc = lines(
198-
recs.iter()
199-
.map(|id| kwd("const").append(ident(id)).append(" = IDL.Rec();")),
200-
);
201-
let defs = lines(def_list.iter().map(|&id| {
198+
let export_prefix = if export { str("export ") } else { RcDoc::nil() };
199+
200+
let recs_doc = lines(recs.iter().map(|id| {
201+
export_prefix
202+
.clone()
203+
.append(kwd("const"))
204+
.append(ident(id))
205+
.append(" = IDL.Rec();")
206+
}));
207+
let mut defs = lines(def_list.iter().map(|&id| {
202208
let ty = env.find_type(&id.into()).unwrap();
203209
if recs.contains(id) {
204210
ident(id)
205211
.append(".fill")
206212
.append(enclose("(", pp_ty(ty), ");"))
207213
} else {
208-
kwd("const")
214+
export_prefix
215+
.clone()
216+
.append(kwd("const"))
209217
.append(ident(id))
210218
.append(" = ")
211219
.append(pp_ty(ty))
212220
.append(";")
213221
}
214222
}));
223+
if !def_list.is_empty() {
224+
defs = defs.append(RcDoc::hardline())
225+
}
215226
recs_doc.append(defs)
216227
}
217228

@@ -230,39 +241,71 @@ fn pp_actor<'a>(ty: &'a Type, recs: &'a BTreeSet<&'a str>) -> RcDoc<'a> {
230241
}
231242
}
232243

244+
fn pp_imports<'a>() -> RcDoc<'a> {
245+
str("import { IDL } from '@dfinity/candid';")
246+
.append(RcDoc::hardline())
247+
.append(RcDoc::hardline())
248+
}
249+
233250
pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
234251
match actor {
235252
None => {
236253
let def_list: Vec<_> = env.to_sorted_iter().map(|pair| pair.0.as_str()).collect();
237254
let recs = infer_rec(env, &def_list).unwrap();
238-
let doc = pp_defs(env, &def_list, &recs);
239-
doc.pretty(LINE_WIDTH).to_string()
255+
let doc = pp_defs(env, &def_list, &recs, true);
256+
let result = pp_imports().append(doc).pretty(LINE_WIDTH).to_string();
257+
258+
result
240259
}
241260
Some(actor) => {
242261
let def_list = chase_actor(env, actor).unwrap();
243262
let recs = infer_rec(env, &def_list).unwrap();
244-
let defs = pp_defs(env, &def_list, &recs);
245263
let types = if let TypeInner::Class(ref args, _) = actor.as_ref() {
246264
args.iter().map(|arg| arg.typ.clone()).collect::<Vec<_>>()
247265
} else {
248266
Vec::new()
249267
};
250-
let init = types.as_slice();
251-
let actor = kwd("return").append(pp_actor(actor, &recs)).append(";");
252-
let body = defs.append(actor);
253-
let doc = str("export const idlFactory = ({ IDL }) => ")
254-
.append(enclose_space("{", body, "};"));
255-
// export init args
256-
let init_defs = chase_types(env, init).unwrap();
268+
let init_types = types.as_slice();
269+
270+
let defs = pp_defs(env, &def_list, &recs, true);
271+
let actor = pp_actor(actor, &recs);
272+
273+
let idl_service = str("export const idlService = ")
274+
.append(actor.clone())
275+
.append(";");
276+
277+
let idl_init_args = str("export const idlInitArgs = ")
278+
.append(pp_rets(init_types))
279+
.append(";");
280+
281+
let idl_factory_return = kwd("return").append(actor).append(";");
282+
let idl_factory_body = pp_defs(env, &def_list, &recs, false).append(idl_factory_return);
283+
let idl_factory_doc = str("export const idlFactory = ({ IDL }) => ")
284+
.append(enclose_space("{", idl_factory_body, "};"));
285+
286+
let init_defs = chase_types(env, init_types).unwrap();
257287
let init_recs = infer_rec(env, &init_defs).unwrap();
258-
let init_defs_doc = pp_defs(env, &init_defs, &init_recs);
259-
let init_doc = kwd("return").append(pp_rets(init)).append(";");
288+
let init_defs_doc = pp_defs(env, &init_defs, &init_recs, false);
289+
let init_doc = kwd("return").append(pp_rets(init_types)).append(";");
260290
let init_doc = init_defs_doc.append(init_doc);
261291
let init_doc =
262292
str("export const init = ({ IDL }) => ").append(enclose_space("{", init_doc, "};"));
263293
let init_doc = init_doc.pretty(LINE_WIDTH).to_string();
264-
let doc = doc.append(RcDoc::hardline()).append(init_doc);
265-
doc.pretty(LINE_WIDTH).to_string()
294+
295+
let result = pp_imports()
296+
.append(defs)
297+
.append(idl_service)
298+
.append(RcDoc::hardline())
299+
.append(RcDoc::hardline())
300+
.append(idl_init_args)
301+
.append(RcDoc::hardline())
302+
.append(RcDoc::hardline())
303+
.append(idl_factory_doc)
304+
.append(RcDoc::hardline())
305+
.append(RcDoc::hardline())
306+
.append(init_doc);
307+
308+
result.pretty(LINE_WIDTH).to_string()
266309
}
267310
}
268311
}

rust/candid_parser/src/bindings/typescript.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,9 @@ fn pp_actor<'a>(env: &'a TypeEnv, ty: &'a Type, syntax: Option<&'a IDLType>) ->
317317
}
318318

319319
pub fn compile(env: &TypeEnv, actor: &Option<Type>, prog: &IDLMergedProg) -> String {
320-
let header = r#"import type { Principal } from '@dfinity/principal';
321-
import type { ActorMethod } from '@dfinity/agent';
320+
let header = r#"import type { ActorMethod } from '@dfinity/agent';
322321
import type { IDL } from '@dfinity/candid';
322+
import type { Principal } from '@dfinity/principal';
323323
"#;
324324
let syntax_actor = prog.resolve_actor().ok().flatten();
325325
let def_list: Vec<_> = env.to_sorted_iter().map(|pair| pair.0.as_str()).collect();
@@ -332,6 +332,10 @@ import type { IDL } from '@dfinity/candid';
332332
.map(|s| pp_docs(s.docs.as_ref()))
333333
.unwrap_or(RcDoc::nil());
334334
docs.append(pp_actor(env, actor, syntax_actor.as_ref().map(|s| &s.typ)))
335+
.append(RcDoc::line())
336+
.append("export declare const idlService: IDL.ServiceClass;")
337+
.append(RcDoc::line())
338+
.append("export declare const idlInitArgs: IDL.Type[];")
335339
.append(RcDoc::line())
336340
.append("export declare const idlFactory: IDL.InterfaceFactory;")
337341
.append(RcDoc::line())

rust/candid_parser/tests/assets/ok/actor.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Principal } from '@dfinity/principal';
21
import type { ActorMethod } from '@dfinity/agent';
32
import type { IDL } from '@dfinity/candid';
3+
import type { Principal } from '@dfinity/principal';
44

55
export type f = ActorMethod<[number], number>;
66
export type g = f;
@@ -13,5 +13,7 @@ export interface _SERVICE {
1313
'h2' : h,
1414
'o' : ActorMethod<[o], o>,
1515
}
16+
export declare const idlService: IDL.ServiceClass;
17+
export declare const idlInitArgs: IDL.Type[];
1618
export declare const idlFactory: IDL.InterfaceFactory;
1719
export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];

rust/candid_parser/tests/assets/ok/actor.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
1+
import { IDL } from '@dfinity/candid';
2+
3+
export const o = IDL.Rec();
4+
export const f = IDL.Func([IDL.Int8], [IDL.Int8], []);
5+
export const h = IDL.Func([f], [f], []);
6+
export const g = f;
7+
o.fill(IDL.Opt(o));
8+
9+
export const idlService = IDL.Service({
10+
'f' : IDL.Func([IDL.Nat], [h], []),
11+
'g' : f,
12+
'h' : g,
13+
'h2' : h,
14+
'o' : IDL.Func([o], [o], []),
15+
});
16+
17+
export const idlInitArgs = [];
18+
119
export const idlFactory = ({ IDL }) => {
220
const o = IDL.Rec();
321
const f = IDL.Func([IDL.Int8], [IDL.Int8], []);
422
const h = IDL.Func([f], [f], []);
523
const g = f;
624
o.fill(IDL.Opt(o));
25+
726
return IDL.Service({
827
'f' : IDL.Func([IDL.Nat], [h], []),
928
'g' : f,
@@ -12,4 +31,5 @@ export const idlFactory = ({ IDL }) => {
1231
'o' : IDL.Func([o], [o], []),
1332
});
1433
};
34+
1535
export const init = ({ IDL }) => { return []; };

rust/candid_parser/tests/assets/ok/class.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Principal } from '@dfinity/principal';
21
import type { ActorMethod } from '@dfinity/agent';
32
import type { IDL } from '@dfinity/candid';
3+
import type { Principal } from '@dfinity/principal';
44

55
export type List = [] | [[bigint, List]];
66
export interface Profile { 'age' : number, 'name' : string }
@@ -14,5 +14,7 @@ export interface _SERVICE {
1414
'get' : ActorMethod<[], List>,
1515
'set' : ActorMethod<[List], List>,
1616
}
17+
export declare const idlService: IDL.ServiceClass;
18+
export declare const idlInitArgs: IDL.Type[];
1719
export declare const idlFactory: IDL.InterfaceFactory;
1820
export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1+
import { IDL } from '@dfinity/candid';
2+
3+
export const List = IDL.Rec();
4+
List.fill(IDL.Opt(IDL.Tuple(IDL.Int, List)));
5+
export const Profile = IDL.Record({ 'age' : IDL.Nat8, 'name' : IDL.Text });
6+
7+
export const idlService = IDL.Service({
8+
'get' : IDL.Func([], [List], []),
9+
'set' : IDL.Func([List], [List], []),
10+
});
11+
12+
export const idlInitArgs = [IDL.Int, List, Profile];
13+
114
export const idlFactory = ({ IDL }) => {
215
const List = IDL.Rec();
316
List.fill(IDL.Opt(IDL.Tuple(IDL.Int, List)));
417
const Profile = IDL.Record({ 'age' : IDL.Nat8, 'name' : IDL.Text });
18+
519
return IDL.Service({
620
'get' : IDL.Func([], [List], []),
721
'set' : IDL.Func([List], [List], []),
822
});
923
};
24+
1025
export const init = ({ IDL }) => {
1126
const List = IDL.Rec();
1227
List.fill(IDL.Opt(IDL.Tuple(IDL.Int, List)));
1328
const Profile = IDL.Record({ 'age' : IDL.Nat8, 'name' : IDL.Text });
29+
1430
return [IDL.Int, List, Profile];
1531
};

rust/candid_parser/tests/assets/ok/comment.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Principal } from '@dfinity/principal';
21
import type { ActorMethod } from '@dfinity/agent';
32
import type { IDL } from '@dfinity/candid';
3+
import type { Principal } from '@dfinity/principal';
44

55
/**
66
* line comment
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
const id = IDL.Nat8;
1+
import { IDL } from '@dfinity/candid';
2+
3+
export const id = IDL.Nat8;
4+
25

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Principal } from '@dfinity/principal';
21
import type { ActorMethod } from '@dfinity/agent';
32
import type { IDL } from '@dfinity/candid';
3+
import type { Principal } from '@dfinity/principal';
44

55
export type A = [] | [B];
66
export type B = [] | [C];
@@ -9,5 +9,7 @@ export type X = Y;
99
export type Y = Z;
1010
export type Z = A;
1111
export interface _SERVICE { 'f' : ActorMethod<[A, B, C, X, Y, Z], undefined> }
12+
export declare const idlService: IDL.ServiceClass;
13+
export declare const idlInitArgs: IDL.Type[];
1214
export declare const idlFactory: IDL.InterfaceFactory;
1315
export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];

0 commit comments

Comments
 (0)