1
+ import { Command } from "commander" ;
2
+ import path from "path" ;
3
+ import { promises as fs } from "fs" ;
4
+ import spawn from "cross-spawn" ;
5
+ import https from "https" ;
6
+ import { input , select } from "@inquirer/prompts" ;
7
+
8
+ const templates = [
9
+ { value : "react-app" , description : "React app using Dojo" } ,
10
+ { value : "react-phaser-example" , description : "React/Phaser app using Dojo" } ,
11
+ { value : "react-pwa-app" , description : "React Progressive Web Apps using Dojo" } ,
12
+ { value : "react-threejs" , description : "React Threejs using Dojo" } ,
13
+ ] ;
14
+
15
+ async function init ( projectName : string , cwd : string , template : string ) {
16
+ const projectPath = path . join ( cwd , projectName ) ;
17
+ const clientPath = path . join ( projectPath , 'client' ) ;
18
+ const dojoStarterPath = path . join ( projectPath , 'dojo-starter' ) ;
19
+
20
+ // Create project directories
21
+ await fs . mkdir ( projectPath , { recursive : true } ) ;
22
+ await fs . mkdir ( clientPath , { recursive : true } ) ;
23
+ await fs . mkdir ( dojoStarterPath , { recursive : true } ) ;
24
+
25
+ // Clone template into client directory
26
+ console . log ( `Downloading ${ template } into client directory...` ) ;
27
+ const cloneResult = spawn . sync ( "npx" , [
28
+ "degit" ,
29
+ `dojoengine/dojo.js/clients/react/${ template } ` ,
30
+ clientPath ,
31
+ ] , { stdio : "inherit" } ) ;
32
+
33
+ if ( cloneResult . status !== 0 ) {
34
+ throw new Error ( `Failed to clone template: ${ template } ` ) ;
35
+ }
36
+
37
+ // Rewrite package.json in client directory
38
+ await rewritePackageJson ( projectName , clientPath ) ;
39
+
40
+ // Clone dojo-starter
41
+ console . log ( `Downloading dojo-starter...` ) ;
42
+ spawn . sync ( "npx" , [ "degit" , `dojoengine/dojo-starter` , dojoStarterPath ] , { stdio : "inherit" } ) ;
43
+
44
+ console . log ( `Project initialized at ${ projectPath } ` ) ;
45
+ console . log ( "Congrats! Your new project has been set up successfully.\n" ) ;
46
+ console . log ( `Navigate into your project directory with:\n cd ${ projectName } \n` ) ;
47
+ console . log ( "You can then build the starter and run the client.\n" ) ;
48
+ console . log ( "For detailed instructions, follow the README here:\n" ) ;
49
+ console . log ( 'https://book.dojoengine.org/' ) ;
50
+ }
51
+
52
+ async function rewritePackageJson ( projectName : string , clientPath : string ) {
53
+ const packageJsonPath = path . join ( clientPath , "package.json" ) ;
54
+ const packageJson = JSON . parse ( await fs . readFile ( packageJsonPath , "utf-8" ) ) ;
55
+ const latestVersion = await getLatestVersion ( ) ;
56
+
57
+ packageJson . name = projectName ;
58
+
59
+ for ( let dep of Object . keys ( packageJson . dependencies ) ) {
60
+ if ( dep . startsWith ( "@dojoengine" ) && packageJson . dependencies [ dep ] . startsWith ( "workspace:" ) ) {
61
+ packageJson . dependencies [ dep ] = latestVersion ;
62
+ }
63
+ }
64
+
65
+ await fs . writeFile ( packageJsonPath , JSON . stringify ( packageJson , null , 2 ) ) ;
66
+ }
67
+
68
+ async function getLatestVersion ( ) : Promise < string > {
69
+ return new Promise ( ( resolve , reject ) => {
70
+ https . get (
71
+ "https://registry.npmjs.org/-/package/@dojoengine/core/dist-tags" ,
72
+ ( res ) => {
73
+ if ( res . statusCode === 200 ) {
74
+ let body = "" ;
75
+ res . on ( "data" , ( data ) => ( body += data ) ) ;
76
+ res . on ( "end" , ( ) => {
77
+ resolve ( JSON . parse ( body ) . latest ) ;
78
+ } ) ;
79
+ } else {
80
+ reject ( new Error ( `Failed to fetch latest version: ${ res . statusCode } ` ) ) ;
81
+ }
82
+ }
83
+ ) . on ( "error" , ( error ) => {
84
+ reject ( error ) ;
85
+ } ) ;
86
+ } ) ;
87
+ }
88
+
89
+ export const start = new Command ( )
90
+ . name ( "start" )
91
+ . description ( "initialize a new project with a selected template" )
92
+ . option ( "-c, --cwd <cwd>" , "the working directory" , process . cwd ( ) )
93
+ . action ( async ( options ) => {
94
+ try {
95
+ const cwd = path . resolve ( options . cwd ) ;
96
+
97
+ const template = await select ( {
98
+ message : "Select a template" ,
99
+ choices : templates ,
100
+ } ) ;
101
+
102
+ const projectName = await input ( {
103
+ message : "Project name " ,
104
+ validate : ( input : string ) => {
105
+ if ( / ^ ( [ A - Z a - z \- \_ \d ] ) + $ / . test ( input ) ) return true ;
106
+ else return "Project name may only include letters, numbers, underscores and hashes." ;
107
+ } ,
108
+ default : template ,
109
+ } ) ;
110
+
111
+ await init ( projectName , cwd , template ) ;
112
+ } catch ( error ) {
113
+ console . error ( "An error occurred:" , error ) ;
114
+ process . exit ( 1 ) ;
115
+ }
116
+ } ) ;
0 commit comments