@@ -7,8 +7,13 @@ use crate::lib::program;
7
7
use crate :: util:: assets;
8
8
use crate :: util:: clap:: parsers:: project_name_parser;
9
9
use anyhow:: { anyhow, bail, ensure, Context } ;
10
+ use clap:: builder:: PossibleValuesParser ;
10
11
use clap:: { Parser , ValueEnum } ;
11
12
use console:: { style, Style } ;
13
+ use dfx_core:: config:: project_templates:: {
14
+ get_project_template, get_sorted_templates, project_template_cli_names, Category ,
15
+ ProjectTemplateName , ResourceLocation ,
16
+ } ;
12
17
use dfx_core:: json:: { load_json_file, save_json_file} ;
13
18
use dialoguer:: theme:: ColorfulTheme ;
14
19
use dialoguer:: { FuzzySelect , MultiSelect } ;
@@ -38,6 +43,8 @@ const AGENT_JS_DEFAULT_INSTALL_DIST_TAG: &str = "latest";
38
43
// check.
39
44
const CHECK_VERSION_TIMEOUT : Duration = Duration :: from_secs ( 2 ) ;
40
45
46
+ const BACKEND_MOTOKO : & str = "motoko" ;
47
+
41
48
/// Creates a new project.
42
49
#[ derive( Parser ) ]
43
50
pub struct NewOpts {
@@ -46,8 +53,8 @@ pub struct NewOpts {
46
53
project_name : String ,
47
54
48
55
/// Choose the type of canister in the starter project.
49
- #[ arg( long, value_enum ) ]
50
- r#type : Option < BackendType > ,
56
+ #[ arg( long, value_parser=backend_project_template_name_parser ( ) ) ]
57
+ r#type : Option < String > ,
51
58
52
59
/// Provides a preview the directories and files to be created without adding them to the file system.
53
60
#[ arg( long) ]
@@ -70,24 +77,8 @@ pub struct NewOpts {
70
77
extras : Vec < Extra > ,
71
78
}
72
79
73
- #[ derive( ValueEnum , Debug , Copy , Clone , PartialEq , Eq ) ]
74
- enum BackendType {
75
- Motoko ,
76
- Rust ,
77
- Azle ,
78
- Kybra ,
79
- }
80
-
81
- impl Display for BackendType {
82
- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
83
- match self {
84
- Self :: Motoko => "Motoko" ,
85
- Self :: Rust => "Rust" ,
86
- Self :: Azle => "TypeScript (Azle)" ,
87
- Self :: Kybra => "Python (Kybra)" ,
88
- }
89
- . fmt ( f)
90
- }
80
+ fn backend_project_template_name_parser ( ) -> PossibleValuesParser {
81
+ PossibleValuesParser :: new ( project_template_cli_names ( Category :: Backend ) )
91
82
}
92
83
93
84
#[ derive( ValueEnum , Debug , Copy , Clone , PartialEq , Eq ) ]
@@ -466,19 +457,17 @@ fn get_agent_js_version_from_npm(dist_tag: &str) -> DfxResult<String> {
466
457
}
467
458
468
459
pub fn exec ( env : & dyn Environment , mut opts : NewOpts ) -> DfxResult {
469
- use BackendType :: * ;
470
460
let log = env. get_logger ( ) ;
471
461
let dry_run = opts. dry_run ;
472
462
473
- let r#type = if let Some ( r#type) = opts. r#type {
474
- r#type
463
+ let backend_template_name = if let Some ( r#type) = opts. r#type {
464
+ ProjectTemplateName ( r#type)
475
465
} else if opts. frontend . is_none ( ) && opts. extras . is_empty ( ) && io:: stdout ( ) . is_terminal ( ) {
476
466
opts = get_opts_interactively ( opts) ?;
477
- opts. r#type . unwrap ( )
467
+ ProjectTemplateName ( opts. r#type . unwrap ( ) )
478
468
} else {
479
- Motoko
469
+ ProjectTemplateName ( BACKEND_MOTOKO . to_string ( ) )
480
470
} ;
481
-
482
471
let project_name = Path :: new ( opts. project_name . as_str ( ) ) ;
483
472
if project_name. exists ( ) {
484
473
bail ! ( "Cannot create a new project because the directory already exists." ) ;
@@ -560,7 +549,9 @@ pub fn exec(env: &dyn Environment, mut opts: NewOpts) -> DfxResult {
560
549
opts. frontend . unwrap_or ( FrontendType :: Vanilla )
561
550
} ;
562
551
563
- if r#type == Azle || frontend. has_js ( ) {
552
+ let backend = get_project_template ( & backend_template_name) ;
553
+
554
+ if backend. has_js || frontend. has_js ( ) {
564
555
write_files_from_entries (
565
556
log,
566
557
& mut assets:: new_project_js_files ( ) . context ( "Failed to get JS config archive." ) ?,
@@ -570,20 +561,19 @@ pub fn exec(env: &dyn Environment, mut opts: NewOpts) -> DfxResult {
570
561
) ?;
571
562
}
572
563
573
- // Default to start with motoko
574
- let mut new_project_files = match r#type {
575
- Rust => assets:: new_project_rust_files ( ) . context ( "Failed to get rust archive." ) ?,
576
- Motoko => assets:: new_project_motoko_files ( ) . context ( "Failed to get motoko archive." ) ?,
577
- Azle => assets:: new_project_azle_files ( ) . context ( "Failed to get azle archive." ) ?,
578
- Kybra => assets:: new_project_kybra_files ( ) . context ( "Failed to get kybra archive." ) ?,
564
+ match backend. resource_location {
565
+ ResourceLocation :: Bundled { get_archive_fn } => {
566
+ let mut new_project_files = get_archive_fn ( )
567
+ . with_context ( || format ! ( "Failed to get {} archive." , backend. name. 0 ) ) ?;
568
+ write_files_from_entries (
569
+ log,
570
+ & mut new_project_files,
571
+ project_name,
572
+ dry_run,
573
+ & variables,
574
+ ) ?;
575
+ }
579
576
} ;
580
- write_files_from_entries (
581
- log,
582
- & mut new_project_files,
583
- project_name,
584
- dry_run,
585
- & variables,
586
- ) ?;
587
577
588
578
if opts. extras . contains ( & Extra :: InternetIdentity ) {
589
579
write_files_from_entries (
@@ -645,7 +635,7 @@ pub fn exec(env: &dyn Environment, mut opts: NewOpts) -> DfxResult {
645
635
init_git ( log, project_name) ?;
646
636
}
647
637
648
- if r#type == Rust {
638
+ if backend . update_cargo_lockfile {
649
639
// dfx build will use --locked, so update the lockfile beforehand
650
640
const MSG : & str = "You will need to run it yourself (or a similar command like `cargo vendor`), because `dfx build` will use the --locked flag with Cargo." ;
651
641
if let Ok ( code) = Command :: new ( "cargo" )
@@ -683,17 +673,21 @@ pub fn exec(env: &dyn Environment, mut opts: NewOpts) -> DfxResult {
683
673
}
684
674
685
675
fn get_opts_interactively ( opts : NewOpts ) -> DfxResult < NewOpts > {
686
- use BackendType :: * ;
687
676
use Extra :: * ;
688
677
use FrontendType :: * ;
689
678
let theme = ColorfulTheme :: default ( ) ;
690
- let backends_list = [ Motoko , Rust , Azle , Kybra ] ;
679
+ let backend_templates = get_sorted_templates ( Category :: Backend ) ;
680
+ let backends_list = backend_templates
681
+ . iter ( )
682
+ . map ( |t| t. display . clone ( ) )
683
+ . collect :: < Vec < _ > > ( ) ;
684
+
691
685
let backend = FuzzySelect :: with_theme ( & theme)
692
686
. items ( & backends_list)
693
687
. default ( 0 )
694
688
. with_prompt ( "Select a backend language:" )
695
689
. interact ( ) ?;
696
- let backend = backends_list [ backend] ;
690
+ let backend = & backend_templates [ backend] ;
697
691
let frontends_list = [ SvelteKit , React , Vue , Vanilla , SimpleAssets , None ] ;
698
692
let frontend = FuzzySelect :: with_theme ( & theme)
699
693
. items ( & frontends_list)
@@ -715,7 +709,7 @@ fn get_opts_interactively(opts: NewOpts) -> DfxResult<NewOpts> {
715
709
let opts = NewOpts {
716
710
extras,
717
711
frontend : Some ( frontend) ,
718
- r#type : Some ( backend) ,
712
+ r#type : Some ( backend. name . 0 . clone ( ) ) ,
719
713
..opts
720
714
} ;
721
715
Ok ( opts)
0 commit comments