@@ -17,14 +17,27 @@ import type { PackageManager } from './utils/getPackageManager.js'
1717
1818const program = new Command ( )
1919
20+ const CODE_ROUTER = 'code-router'
21+ const FILE_ROUTER = 'file-router'
22+
2023interface Options {
2124 typescript: boolean
2225 tailwind: boolean
2326 packageManager: PackageManager
27+ mode: typeof CODE_ROUTER | typeof FILE_ROUTER
28+ }
29+
30+ function sortObject ( obj : Record < string , string > ) : Record < string , string > {
31+ return Object . keys ( obj )
32+ . sort ( )
33+ . reduce < Record < string , string >> ( ( acc , key ) => {
34+ acc [ key ] = obj [ key ]
35+ return acc
36+ } , { } )
2437}
2538
26- const createCopyFile = ( templateDir : string , targetDir : string ) =>
27- async function copyFiles ( files : Array < string > ) {
39+ function createCopyFile ( targetDir : string ) {
40+ return async function copyFiles ( templateDir : string , files : Array < string > ) {
2841 for ( const file of files ) {
2942 const targetFileName = file . replace ( '.tw' , '' )
3043 await copyFile (
@@ -33,33 +46,41 @@ const createCopyFile = (templateDir: string, targetDir: string) =>
3346 )
3447 }
3548 }
49+ }
3650
37- const createTemplateFile = (
51+ function createTemplateFile (
3852 projectName : string ,
3953 options : Required < Options > ,
40- templateDir: string,
4154 targetDir : string ,
42- ) =>
43- async function templateFile ( file : string , targetFileName ?: string ) {
55+ ) {
56+ return async function templateFile (
57+ templateDir : string ,
58+ file : string ,
59+ targetFileName ?: string ,
60+ ) {
4461 const templateValues = {
4562 packageManager : options . packageManager ,
4663 projectName : projectName ,
4764 typescript : options . typescript ,
4865 tailwind : options . tailwind ,
4966 js : options . typescript ? 'ts' : 'js' ,
5067 jsx : options . typescript ? 'tsx' : 'jsx' ,
68+ fileRouter : options . mode === FILE_ROUTER ,
69+ codeRouter : options . mode === CODE_ROUTER ,
5170 }
5271
5372 const template = await readFile ( resolve ( templateDir , file ) , 'utf-8' )
5473 const content = render ( template , templateValues )
5574 const target = targetFileName ?? file . replace ( '.ejs' , '' )
5675 await writeFile ( resolve ( targetDir , target ) , content )
5776 }
77+ }
5878
5979async function createPackageJSON (
6080 projectName : string ,
6181 options : Required < Options > ,
6282 templateDir : string ,
83+ routerDir : string ,
6384 targetDir : string ,
6485) {
6586 let packageJSON = JSON . parse (
@@ -90,25 +111,41 @@ async function createPackageJSON(
90111 } ,
91112 }
92113 }
114+ if ( options . mode === FILE_ROUTER ) {
115+ const frPackageJSON = JSON . parse (
116+ await readFile ( resolve ( routerDir , 'package.fr.json' ) , 'utf8' ) ,
117+ )
118+ packageJSON = {
119+ ...packageJSON ,
120+ dependencies : {
121+ ...packageJSON . dependencies ,
122+ ...frPackageJSON . dependencies ,
123+ } ,
124+ }
125+ }
126+ packageJSON . dependencies = sortObject (
127+ packageJSON . dependencies as Record < string , string > ,
128+ )
129+ packageJSON . devDependencies = sortObject (
130+ packageJSON . devDependencies as Record < string , string > ,
131+ )
93132 await writeFile (
94133 resolve ( targetDir , 'package.json' ) ,
95134 JSON . stringify ( packageJSON , null , 2 ) ,
96135 )
97136}
98137
99138async function createApp ( projectName : string , options : Required < Options > ) {
100- const templateDir = fileURLToPath (
101- new URL ( '../project-template' , import . meta. url ) ,
139+ const templateDirBase = fileURLToPath (
140+ new URL ( '../templates/base' , import . meta. url ) ,
141+ )
142+ const templateDirRouter = fileURLToPath (
143+ new URL ( `../templates/${ options . mode } ` , import . meta. url ) ,
102144 )
103145 const targetDir = resolve ( process . cwd ( ) , projectName )
104146
105- const copyFiles = createCopyFile ( templateDir , targetDir )
106- const templateFile = createTemplateFile (
107- projectName ,
108- options ,
109- templateDir ,
110- targetDir ,
111- )
147+ const copyFiles = createCopyFile ( targetDir )
148+ const templateFile = createTemplateFile ( projectName , options , targetDir )
112149
113150 intro ( `Creating a new TanStack app in ${ targetDir } ...` )
114151
@@ -118,13 +155,13 @@ async function createApp(projectName: string, options: Required<Options>) {
118155 // Setup the .vscode directory
119156 await mkdir ( resolve ( targetDir , '.vscode' ) , { recursive : true } )
120157 await copyFile (
121- resolve ( templateDir , '.vscode/settings.json' ) ,
158+ resolve ( templateDirBase , '.vscode/settings.json' ) ,
122159 resolve ( targetDir , '.vscode/settings.json' ) ,
123160 )
124161
125162 // Fill the public directory
126163 await mkdir ( resolve ( targetDir , 'public' ) , { recursive : true } )
127- copyFiles ( [
164+ copyFiles ( templateDirBase , [
128165 './public/robots.txt' ,
129166 './public/favicon.ico' ,
130167 './public/manifest.json' ,
@@ -134,55 +171,85 @@ async function createApp(projectName: string, options: Required<Options>) {
134171
135172 // Make the src directory
136173 await mkdir ( resolve ( targetDir , 'src' ) , { recursive : true } )
174+ if ( options . mode === FILE_ROUTER ) {
175+ await mkdir ( resolve ( targetDir , 'src/routes' ) , { recursive : true } )
176+ }
137177
138178 // Copy in Vite and Tailwind config and CSS
139179 if ( ! options . tailwind ) {
140- await copyFiles ( [ './src/App.css' ] )
180+ await copyFiles ( templateDirBase , [ '. / src / App . css '] )
141181 }
142- await templateFile ( '. / vite . config . js . ejs ')
143- await templateFile ( '. / src / styles . css . ejs ')
182+ await templateFile ( templateDirBase , '. / vite . config . js . ejs ')
183+ await templateFile ( templateDirBase , '. / src / styles . css . ejs ')
144184
145- copyFiles ( [ '. / src / logo . svg '] )
185+ copyFiles ( templateDirBase , [ '. / src / logo . svg '] )
146186
147187 // Setup the app component. There are four variations, typescript/javascript and tailwind/non-tailwind.
148- await templateFile (
149- './src/App.tsx.ejs' ,
150- options . typescript ? undefined : './src/App.jsx' ,
151- )
152- await templateFile (
153- './src/App.test.tsx.ejs' ,
154- options . typescript ? undefined : './src/App.test.jsx' ,
155- )
188+ if ( options . mode === FILE_ROUTER ) {
189+ copyFiles ( templateDirRouter , [ '. / src / routes / __root . tsx '] )
190+ await templateFile (
191+ templateDirBase ,
192+ './src/App.tsx.ejs' ,
193+ './src/routes/index.tsx' ,
194+ )
195+ } else {
196+ await templateFile (
197+ templateDirBase ,
198+ './src/App.tsx.ejs' ,
199+ options . typescript ? undefined : './src/App.jsx' ,
200+ )
201+ await templateFile (
202+ templateDirBase ,
203+ './src/App.test.tsx.ejs' ,
204+ options . typescript ? undefined : './src/App.test.jsx' ,
205+ )
206+ }
207+
208+ // Create the main entry point
209+ if ( options . typescript ) {
210+ await templateFile ( templateDirRouter , './src/main.tsx.ejs' )
211+ } else {
212+ await templateFile (
213+ templateDirRouter ,
214+ './src/main.tsx.ejs' ,
215+ './src/main.jsx' ,
216+ )
217+ }
156218
157219 // Setup the main, reportWebVitals and index.html files
158220 if ( options . typescript ) {
159- await templateFile ( './src/main.tsx.ejs' )
160- await templateFile ( './src/reportWebVitals.ts.ejs' )
221+ await templateFile ( templateDirBase , './src/reportWebVitals.ts.ejs' )
161222 } else {
162- await templateFile ( './src/main.tsx.ejs' , './src/main.jsx' )
163223 await templateFile (
224+ templateDirBase ,
164225 './src/reportWebVitals.ts.ejs' ,
165226 './src/reportWebVitals.js' ,
166227 )
167228 }
168- await templateFile ( './index.html.ejs' )
229+ await templateFile ( templateDirBase , './index.html.ejs' )
169230
170231 // Setup tsconfig
171232 if ( options . typescript ) {
172- await copyFiles ( [ './tsconfig.json' , './tsconfig.dev.json' ] )
233+ await copyFiles ( templateDirBase , [ './tsconfig.json' , './tsconfig.dev.json' ] )
173234 }
174235
175236 // Setup the package.json file, optionally with typescript and tailwind
176- await createPackageJSON ( projectName , options , templateDir , targetDir )
237+ await createPackageJSON (
238+ projectName ,
239+ options ,
240+ templateDirBase ,
241+ templateDirRouter ,
242+ targetDir ,
243+ )
177244
178245 // Add .gitignore
179246 await copyFile (
180- resolve ( templateDir , 'gitignore' ) ,
247+ resolve ( templateDirBase , 'gitignore' ) ,
181248 resolve ( targetDir , '.gitignore' ) ,
182249 )
183250
184251 // Create the README.md
185- await templateFile ( 'README.md.ejs' )
252+ await templateFile ( templateDirBase , 'README.md.ejs' )
186253
187254 // Install dependencies
188255 const s = spinner ( )
@@ -197,13 +264,17 @@ program
197264 . name ( 'create-tsrouter-app' )
198265 . description ( 'CLI to create a new TanStack application' )
199266 . argument ( '<project-name>' , 'name of the project' )
200- . option < 'typescript' | 'javascript' > (
267+ . option < 'typescript' | 'javascript' | 'file-router' > (
201268 '--template <type>' ,
202- 'project template (typescript/ javascript)' ,
269+ 'project template (typescript, javascript, file-router )' ,
203270 ( value ) => {
204- if ( value !== 'typescript' && value !== 'javascript' ) {
271+ if (
272+ value !== 'typescript' &&
273+ value !== 'javascript' &&
274+ value !== 'file-router'
275+ ) {
205276 throw new InvalidArgumentError (
206- `Invalid template: ${ value } . Only the following are allowed: typescript, javascript` ,
277+ `Invalid template: ${ value } . Only the following are allowed: typescript, javascript, file-router ` ,
207278 )
208279 }
209280 return value
@@ -230,17 +301,19 @@ program
230301 (
231302 projectName : string ,
232303 options : {
233- template : string
304+ template : 'typescript' | 'javascript' | 'file-router'
234305 tailwind : boolean
235306 packageManager : PackageManager
236307 } ,
237308 ) => {
238- const typescript = options . template === 'typescript'
309+ const typescript =
310+ options . template === 'typescript' || options . template === 'file-router'
239311
240312 createApp ( projectName , {
241313 typescript,
242314 tailwind : options . tailwind ,
243315 packageManager : options . packageManager ,
316+ mode : options . template === 'file-router' ? FILE_ROUTER : CODE_ROUTER ,
244317 } )
245318 } ,
246319 )
0 commit comments