create-tsrouter-app 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +41 -9
  2. package/dist/add-ons.js +69 -0
  3. package/dist/cli.js +78 -0
  4. package/dist/constants.js +4 -0
  5. package/dist/create-app.js +371 -0
  6. package/dist/index.js +2 -347
  7. package/dist/mcp.js +169 -0
  8. package/dist/options.js +260 -0
  9. package/dist/{utils/getPackageManager.js → package-manager.js} +1 -0
  10. package/dist/types.js +1 -0
  11. package/package.json +6 -3
  12. package/src/add-ons.ts +156 -0
  13. package/src/cli.ts +114 -0
  14. package/src/constants.ts +7 -0
  15. package/src/create-app.ts +582 -0
  16. package/src/index.ts +2 -507
  17. package/src/mcp.ts +205 -0
  18. package/src/options.ts +308 -0
  19. package/src/{utils/getPackageManager.ts → package-manager.ts} +1 -0
  20. package/src/types.ts +30 -0
  21. package/templates/react/add-on/clerk/README.md +3 -0
  22. package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
  23. package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
  24. package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
  25. package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
  26. package/templates/react/add-on/clerk/info.json +13 -0
  27. package/templates/react/add-on/clerk/package.json +5 -0
  28. package/templates/react/add-on/convex/README.md +4 -0
  29. package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
  30. package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
  31. package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
  32. package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
  33. package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
  34. package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
  35. package/templates/react/add-on/convex/info.json +13 -0
  36. package/templates/react/add-on/convex/package.json +6 -0
  37. package/templates/react/add-on/form/assets/src/routes/demo.form.tsx.ejs +62 -0
  38. package/templates/react/add-on/form/info.json +13 -0
  39. package/templates/react/add-on/form/package.json +5 -0
  40. package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
  41. package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  42. package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
  43. package/templates/react/add-on/module-federation/info.json +7 -0
  44. package/templates/react/add-on/module-federation/package.json +5 -0
  45. package/templates/react/add-on/netlify/README.md +11 -0
  46. package/templates/react/add-on/netlify/info.json +7 -0
  47. package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  48. package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
  49. package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
  50. package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
  51. package/templates/react/add-on/sentry/info.json +14 -0
  52. package/templates/react/add-on/sentry/package.json +7 -0
  53. package/templates/react/add-on/shadcn/README.md +7 -0
  54. package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
  55. package/templates/react/add-on/shadcn/info.json +11 -0
  56. package/templates/react/add-on/start/assets/app.config.ts +16 -0
  57. package/templates/react/add-on/start/assets/postcss.config.ts +5 -0
  58. package/templates/react/add-on/start/assets/src/api.ts +6 -0
  59. package/templates/react/add-on/start/assets/src/client.tsx +10 -0
  60. package/templates/react/add-on/start/assets/src/router.tsx.ejs +51 -0
  61. package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
  62. package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
  63. package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
  64. package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
  65. package/templates/react/add-on/start/info.json +19 -0
  66. package/templates/react/add-on/start/package.json +14 -0
  67. package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
  68. package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
  69. package/templates/react/add-on/store/info.json +13 -0
  70. package/templates/react/add-on/store/package.json +6 -0
  71. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
  72. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +9 -0
  73. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +38 -0
  74. package/templates/react/add-on/tanstack-query/info.json +13 -0
  75. package/templates/react/add-on/tanstack-query/package.json +6 -0
  76. package/templates/{base → react/base}/README.md.ejs +9 -0
  77. package/templates/react/base/_dot_vscode/settings.json +11 -0
  78. package/templates/react/base/src/components/Header.tsx.ejs +25 -0
  79. package/templates/{base/tsconfig.json → react/base/tsconfig.json.ejs} +5 -1
  80. package/templates/react/base/vite.config.js.ejs +24 -0
  81. package/templates/{code-router → react/code-router}/src/main.tsx.ejs +17 -1
  82. package/templates/react/example/tanchat/README.md +37 -0
  83. package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
  84. package/templates/react/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +148 -0
  85. package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
  86. package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx.ejs +375 -0
  87. package/templates/react/example/tanchat/assets/src/store/demo.hooks.ts +21 -0
  88. package/templates/react/example/tanchat/assets/src/store/demo.store.ts +133 -0
  89. package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +108 -0
  90. package/templates/react/example/tanchat/info.json +15 -0
  91. package/templates/react/example/tanchat/package.json +10 -0
  92. package/templates/react/file-router/src/routes/__root.tsx.ejs +71 -0
  93. package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +148 -0
  94. package/templates/solid/add-on/form/info.json +13 -0
  95. package/templates/solid/add-on/form/package.json +5 -0
  96. package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
  97. package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  98. package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
  99. package/templates/solid/add-on/module-federation/info.json +7 -0
  100. package/templates/solid/add-on/module-federation/package.json +5 -0
  101. package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  102. package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
  103. package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
  104. package/templates/solid/add-on/sentry/info.json +13 -0
  105. package/templates/solid/add-on/sentry/package.json +5 -0
  106. package/templates/solid/add-on/solid-ui/README.md +9 -0
  107. package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
  108. package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
  109. package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
  110. package/templates/solid/add-on/solid-ui/info.json +11 -0
  111. package/templates/solid/add-on/solid-ui/package.json +9 -0
  112. package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
  113. package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
  114. package/templates/solid/add-on/store/info.json +13 -0
  115. package/templates/solid/add-on/store/package.json +6 -0
  116. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
  117. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
  118. package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
  119. package/templates/solid/add-on/tanstack-query/info.json +13 -0
  120. package/templates/solid/add-on/tanstack-query/package.json +6 -0
  121. package/templates/solid/base/README.md.ejs +200 -0
  122. package/templates/solid/base/_dot_cursorrules.append +35 -0
  123. package/templates/solid/base/_dot_gitignore +5 -0
  124. package/templates/solid/base/_dot_vscode/settings.json +11 -0
  125. package/templates/solid/base/index.html.ejs +20 -0
  126. package/templates/solid/base/package.json +22 -0
  127. package/templates/solid/base/package.ts.json +5 -0
  128. package/templates/solid/base/package.tw.json +6 -0
  129. package/templates/solid/base/public/favicon.ico +0 -0
  130. package/templates/solid/base/public/logo192.png +0 -0
  131. package/templates/solid/base/public/logo512.png +0 -0
  132. package/templates/solid/base/public/manifest.json +25 -0
  133. package/templates/solid/base/public/robots.txt +3 -0
  134. package/templates/solid/base/src/App.css +0 -0
  135. package/templates/solid/base/src/App.tsx.ejs +47 -0
  136. package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
  137. package/templates/solid/base/src/logo.svg +120 -0
  138. package/templates/solid/base/src/styles.css.ejs +15 -0
  139. package/templates/solid/base/tsconfig.json.ejs +30 -0
  140. package/templates/solid/base/vite.config.js.ejs +22 -0
  141. package/templates/solid/code-router/src/main.tsx.ejs +69 -0
  142. package/templates/solid/file-router/package.fr.json +5 -0
  143. package/templates/solid/file-router/src/main.tsx.ejs +44 -0
  144. package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
  145. package/templates/solid/file-router/src/routes/index.tsx +43 -0
  146. package/templates/base/vite.config.js.ejs +0 -15
  147. package/templates/file-router/src/routes/__root.tsx +0 -11
  148. /package/templates/{base/gitignore → react/base/_dot_gitignore} +0 -0
  149. /package/templates/{base → react/base}/index.html.ejs +0 -0
  150. /package/templates/{base → react/base}/package.json +0 -0
  151. /package/templates/{base → react/base}/package.ts.json +0 -0
  152. /package/templates/{base → react/base}/package.tw.json +0 -0
  153. /package/templates/{base → react/base}/public/favicon.ico +0 -0
  154. /package/templates/{base → react/base}/public/logo192.png +0 -0
  155. /package/templates/{base → react/base}/public/logo512.png +0 -0
  156. /package/templates/{base → react/base}/public/manifest.json +0 -0
  157. /package/templates/{base → react/base}/public/robots.txt +0 -0
  158. /package/templates/{base → react/base}/src/App.css +0 -0
  159. /package/templates/{base → react/base}/src/App.test.tsx.ejs +0 -0
  160. /package/templates/{base → react/base}/src/App.tsx.ejs +0 -0
  161. /package/templates/{base → react/base}/src/logo.svg +0 -0
  162. /package/templates/{base → react/base}/src/reportWebVitals.ts.ejs +0 -0
  163. /package/templates/{base → react/base}/src/styles.css.ejs +0 -0
  164. /package/templates/{file-router → react/file-router}/package.fr.json +0 -0
  165. /package/templates/{file-router → react/file-router}/src/main.tsx.ejs +0 -0
package/src/index.ts CHANGED
@@ -1,509 +1,4 @@
1
1
  #!/usr/bin/env node
2
+ import { cli } from './cli.js'
2
3
 
3
- import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'
4
- import { existsSync } from 'node:fs'
5
- import { resolve } from 'node:path'
6
- import { fileURLToPath } from 'node:url'
7
- import { Command, InvalidArgumentError } from 'commander'
8
- import {
9
- cancel,
10
- confirm,
11
- intro,
12
- isCancel,
13
- log,
14
- outro,
15
- select,
16
- spinner,
17
- text,
18
- } from '@clack/prompts'
19
- import { execa } from 'execa'
20
- import { render } from 'ejs'
21
-
22
- import {
23
- DEFAULT_PACKAGE_MANAGER,
24
- SUPPORTED_PACKAGE_MANAGERS,
25
- getPackageManager,
26
- } from './utils/getPackageManager.js'
27
-
28
- import type { PackageManager } from './utils/getPackageManager.js'
29
-
30
- const program = new Command()
31
-
32
- const CODE_ROUTER = 'code-router'
33
- const FILE_ROUTER = 'file-router'
34
-
35
- interface Options {
36
- projectName: string
37
- typescript: boolean
38
- tailwind: boolean
39
- packageManager: PackageManager
40
- mode: typeof CODE_ROUTER | typeof FILE_ROUTER
41
- git: boolean
42
- }
43
-
44
- interface CliOptions {
45
- template?: 'typescript' | 'javascript' | 'file-router'
46
- tailwind?: boolean
47
- packageManager?: PackageManager
48
- projectName?: string
49
- git?: boolean
50
- }
51
-
52
- function sortObject(obj: Record<string, string>): Record<string, string> {
53
- return Object.keys(obj)
54
- .sort()
55
- .reduce<Record<string, string>>((acc, key) => {
56
- acc[key] = obj[key]
57
- return acc
58
- }, {})
59
- }
60
-
61
- function createCopyFile(targetDir: string) {
62
- return async function copyFiles(templateDir: string, files: Array<string>) {
63
- for (const file of files) {
64
- const targetFileName = file.replace('.tw', '')
65
- await copyFile(
66
- resolve(templateDir, file),
67
- resolve(targetDir, targetFileName),
68
- )
69
- }
70
- }
71
- }
72
-
73
- function createTemplateFile(
74
- projectName: string,
75
- options: Required<Options>,
76
- targetDir: string,
77
- ) {
78
- return async function templateFile(
79
- templateDir: string,
80
- file: string,
81
- targetFileName?: string,
82
- ) {
83
- const templateValues = {
84
- packageManager: options.packageManager,
85
- projectName: projectName,
86
- typescript: options.typescript,
87
- tailwind: options.tailwind,
88
- js: options.typescript ? 'ts' : 'js',
89
- jsx: options.typescript ? 'tsx' : 'jsx',
90
- fileRouter: options.mode === FILE_ROUTER,
91
- codeRouter: options.mode === CODE_ROUTER,
92
- }
93
-
94
- const template = await readFile(resolve(templateDir, file), 'utf-8')
95
- const content = render(template, templateValues)
96
- const target = targetFileName ?? file.replace('.ejs', '')
97
- await writeFile(resolve(targetDir, target), content)
98
- }
99
- }
100
-
101
- async function createPackageJSON(
102
- projectName: string,
103
- options: Required<Options>,
104
- templateDir: string,
105
- routerDir: string,
106
- targetDir: string,
107
- ) {
108
- let packageJSON = JSON.parse(
109
- await readFile(resolve(templateDir, 'package.json'), 'utf8'),
110
- )
111
- packageJSON.name = projectName
112
- if (options.typescript) {
113
- const tsPackageJSON = JSON.parse(
114
- await readFile(resolve(templateDir, 'package.ts.json'), 'utf8'),
115
- )
116
- packageJSON = {
117
- ...packageJSON,
118
- devDependencies: {
119
- ...packageJSON.devDependencies,
120
- ...tsPackageJSON.devDependencies,
121
- },
122
- }
123
- }
124
- if (options.tailwind) {
125
- const twPackageJSON = JSON.parse(
126
- await readFile(resolve(templateDir, 'package.tw.json'), 'utf8'),
127
- )
128
- packageJSON = {
129
- ...packageJSON,
130
- dependencies: {
131
- ...packageJSON.dependencies,
132
- ...twPackageJSON.dependencies,
133
- },
134
- }
135
- }
136
- if (options.mode === FILE_ROUTER) {
137
- const frPackageJSON = JSON.parse(
138
- await readFile(resolve(routerDir, 'package.fr.json'), 'utf8'),
139
- )
140
- packageJSON = {
141
- ...packageJSON,
142
- dependencies: {
143
- ...packageJSON.dependencies,
144
- ...frPackageJSON.dependencies,
145
- },
146
- }
147
- }
148
- packageJSON.dependencies = sortObject(
149
- packageJSON.dependencies as Record<string, string>,
150
- )
151
- packageJSON.devDependencies = sortObject(
152
- packageJSON.devDependencies as Record<string, string>,
153
- )
154
- await writeFile(
155
- resolve(targetDir, 'package.json'),
156
- JSON.stringify(packageJSON, null, 2),
157
- )
158
- }
159
-
160
- async function createApp(options: Required<Options>) {
161
- const templateDirBase = fileURLToPath(
162
- new URL('../templates/base', import.meta.url),
163
- )
164
- const templateDirRouter = fileURLToPath(
165
- new URL(`../templates/${options.mode}`, import.meta.url),
166
- )
167
- const targetDir = resolve(process.cwd(), options.projectName)
168
-
169
- if (existsSync(targetDir)) {
170
- log.error(`Directory "${options.projectName}" already exists`)
171
- return
172
- }
173
-
174
- const copyFiles = createCopyFile(targetDir)
175
- const templateFile = createTemplateFile(
176
- options.projectName,
177
- options,
178
- targetDir,
179
- )
180
-
181
- intro(`Creating a new TanStack app in ${targetDir}...`)
182
-
183
- // Make the root directory
184
- await mkdir(targetDir, { recursive: true })
185
-
186
- // Setup the .vscode directory
187
- await mkdir(resolve(targetDir, '.vscode'), { recursive: true })
188
- await copyFile(
189
- resolve(templateDirBase, '.vscode/settings.json'),
190
- resolve(targetDir, '.vscode/settings.json'),
191
- )
192
-
193
- // Fill the public directory
194
- await mkdir(resolve(targetDir, 'public'), { recursive: true })
195
- copyFiles(templateDirBase, [
196
- './public/robots.txt',
197
- './public/favicon.ico',
198
- './public/manifest.json',
199
- './public/logo192.png',
200
- './public/logo512.png',
201
- ])
202
-
203
- // Make the src directory
204
- await mkdir(resolve(targetDir, 'src'), { recursive: true })
205
- if (options.mode === FILE_ROUTER) {
206
- await mkdir(resolve(targetDir, 'src/routes'), { recursive: true })
207
- }
208
-
209
- // Copy in Vite and Tailwind config and CSS
210
- if (!options.tailwind) {
211
- await copyFiles(templateDirBase, ['./src/App.css'])
212
- }
213
- await templateFile(templateDirBase, './vite.config.js.ejs')
214
- await templateFile(templateDirBase, './src/styles.css.ejs')
215
-
216
- copyFiles(templateDirBase, ['./src/logo.svg'])
217
-
218
- // Setup the app component. There are four variations, typescript/javascript and tailwind/non-tailwind.
219
- if (options.mode === FILE_ROUTER) {
220
- copyFiles(templateDirRouter, ['./src/routes/__root.tsx'])
221
- await templateFile(
222
- templateDirBase,
223
- './src/App.tsx.ejs',
224
- './src/routes/index.tsx',
225
- )
226
- } else {
227
- await templateFile(
228
- templateDirBase,
229
- './src/App.tsx.ejs',
230
- options.typescript ? undefined : './src/App.jsx',
231
- )
232
- await templateFile(
233
- templateDirBase,
234
- './src/App.test.tsx.ejs',
235
- options.typescript ? undefined : './src/App.test.jsx',
236
- )
237
- }
238
-
239
- // Create the main entry point
240
- if (options.typescript) {
241
- await templateFile(templateDirRouter, './src/main.tsx.ejs')
242
- } else {
243
- await templateFile(
244
- templateDirRouter,
245
- './src/main.tsx.ejs',
246
- './src/main.jsx',
247
- )
248
- }
249
-
250
- // Setup the main, reportWebVitals and index.html files
251
- if (options.typescript) {
252
- await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs')
253
- } else {
254
- await templateFile(
255
- templateDirBase,
256
- './src/reportWebVitals.ts.ejs',
257
- './src/reportWebVitals.js',
258
- )
259
- }
260
- await templateFile(templateDirBase, './index.html.ejs')
261
-
262
- // Setup tsconfig
263
- if (options.typescript) {
264
- await copyFiles(templateDirBase, ['./tsconfig.json'])
265
- }
266
-
267
- // Setup the package.json file, optionally with typescript and tailwind
268
- await createPackageJSON(
269
- options.projectName,
270
- options,
271
- templateDirBase,
272
- templateDirRouter,
273
- targetDir,
274
- )
275
-
276
- // Add .gitignore
277
- await copyFile(
278
- resolve(templateDirBase, 'gitignore'),
279
- resolve(targetDir, '.gitignore'),
280
- )
281
-
282
- // Create the README.md
283
- await templateFile(templateDirBase, 'README.md.ejs')
284
-
285
- // Install dependencies
286
- const s = spinner()
287
- s.start(`Installing dependencies via ${options.packageManager}...`)
288
- await execa(options.packageManager, ['install'], { cwd: targetDir })
289
- s.stop(`Installed dependencies`)
290
-
291
- if (options.git) {
292
- s.start(`Initializing git repository...`)
293
- await execa('git', ['init'], { cwd: targetDir })
294
- s.stop(`Initialized git repository`)
295
- }
296
-
297
- outro(`Created your new TanStack app in ${targetDir}.
298
-
299
- Use the following commands to start your app:
300
-
301
- % cd ${options.projectName}
302
- % ${options.packageManager} start
303
-
304
- Please read README.md for more information on testing, styling, adding routes, react-query, etc.
305
- `)
306
- }
307
-
308
- // If all CLI options are provided, use them directly
309
- function normalizeOptions(
310
- cliOptions: CliOptions,
311
- ): Required<Options> | undefined {
312
- if (cliOptions.projectName) {
313
- const typescript =
314
- cliOptions.template === 'typescript' ||
315
- cliOptions.template === 'file-router'
316
-
317
- return {
318
- projectName: cliOptions.projectName,
319
- typescript,
320
- tailwind: !!cliOptions.tailwind,
321
- packageManager: cliOptions.packageManager || DEFAULT_PACKAGE_MANAGER,
322
- mode: cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
323
- git: !!cliOptions.git,
324
- }
325
- }
326
- }
327
-
328
- async function promptForOptions(
329
- cliOptions: CliOptions,
330
- ): Promise<Required<Options>> {
331
- const options = {} as Required<Options>
332
-
333
- if (!cliOptions.projectName) {
334
- const value = await text({
335
- message: 'What would you like to name your project?',
336
- defaultValue: 'my-app',
337
- validate(value) {
338
- if (!value) {
339
- return 'Please enter a name'
340
- }
341
- },
342
- })
343
- if (isCancel(value)) {
344
- cancel('Operation cancelled.')
345
- process.exit(0)
346
- }
347
- options.projectName = value
348
- }
349
-
350
- // Router type selection
351
- if (!cliOptions.template) {
352
- const routerType = await select({
353
- message: 'Select the router type:',
354
- options: [
355
- {
356
- value: FILE_ROUTER,
357
- label: 'File Router - File-based routing structure',
358
- },
359
- {
360
- value: CODE_ROUTER,
361
- label: 'Code Router - Traditional code-based routing',
362
- },
363
- ],
364
- initialValue: FILE_ROUTER,
365
- })
366
- if (isCancel(routerType)) {
367
- cancel('Operation cancelled.')
368
- process.exit(0)
369
- }
370
- options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
371
- } else {
372
- options.mode = cliOptions.template as
373
- | typeof CODE_ROUTER
374
- | typeof FILE_ROUTER
375
- if (options.mode === FILE_ROUTER) {
376
- options.typescript = true
377
- }
378
- }
379
-
380
- // TypeScript selection (if using Code Router)
381
- if (!options.typescript) {
382
- if (options.mode === CODE_ROUTER) {
383
- const typescriptEnable = await confirm({
384
- message: 'Would you like to use TypeScript?',
385
- initialValue: true,
386
- })
387
- if (isCancel(typescriptEnable)) {
388
- cancel('Operation cancelled.')
389
- process.exit(0)
390
- }
391
- options.typescript = typescriptEnable
392
- } else {
393
- options.typescript = true
394
- }
395
- }
396
-
397
- // Tailwind selection
398
- if (cliOptions.tailwind === undefined) {
399
- const tailwind = await confirm({
400
- message: 'Would you like to use Tailwind CSS?',
401
- initialValue: true,
402
- })
403
- if (isCancel(tailwind)) {
404
- cancel('Operation cancelled.')
405
- process.exit(0)
406
- }
407
- options.tailwind = tailwind
408
- } else {
409
- options.tailwind = cliOptions.tailwind
410
- }
411
-
412
- // Package manager selection
413
- if (cliOptions.packageManager === undefined) {
414
- const detectedPackageManager = getPackageManager()
415
- if (!detectedPackageManager) {
416
- const pm = await select({
417
- message: 'Select package manager:',
418
- options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
419
- value: pm,
420
- label: pm,
421
- })),
422
- initialValue: DEFAULT_PACKAGE_MANAGER,
423
- })
424
- if (isCancel(pm)) {
425
- cancel('Operation cancelled.')
426
- process.exit(0)
427
- }
428
- options.packageManager = pm
429
- } else {
430
- options.packageManager = detectedPackageManager
431
- }
432
- } else {
433
- options.packageManager = cliOptions.packageManager
434
- }
435
-
436
- // Git selection
437
- if (cliOptions.git === undefined) {
438
- const git = await confirm({
439
- message: 'Would you like to initialize a new git repository?',
440
- initialValue: true,
441
- })
442
- if (isCancel(git)) {
443
- cancel('Operation cancelled.')
444
- process.exit(0)
445
- }
446
- options.git = git
447
- } else {
448
- options.git = !!cliOptions.git
449
- }
450
-
451
- return options
452
- }
453
-
454
- program
455
- .name('create-tsrouter-app')
456
- .description('CLI to create a new TanStack application')
457
- .argument('[project-name]', 'name of the project')
458
- .option('--no-git', 'do not create a git repository')
459
- .option<'typescript' | 'javascript' | 'file-router'>(
460
- '--template <type>',
461
- 'project template (typescript, javascript, file-router)',
462
- (value) => {
463
- if (
464
- value !== 'typescript' &&
465
- value !== 'javascript' &&
466
- value !== 'file-router'
467
- ) {
468
- throw new InvalidArgumentError(
469
- `Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`,
470
- )
471
- }
472
- return value
473
- },
474
- )
475
- .option<PackageManager>(
476
- `--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`,
477
- `Explicitly tell the CLI to use this package manager`,
478
- (value) => {
479
- if (!SUPPORTED_PACKAGE_MANAGERS.includes(value as PackageManager)) {
480
- throw new InvalidArgumentError(
481
- `Invalid package manager: ${value}. Only the following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(
482
- ', ',
483
- )}`,
484
- )
485
- }
486
- return value as PackageManager
487
- },
488
- )
489
- .option('--tailwind', 'add Tailwind CSS')
490
- .action(async (projectName: string, options: CliOptions) => {
491
- try {
492
- const cliOptions = {
493
- projectName,
494
- ...options,
495
- } as CliOptions
496
- let finalOptions = normalizeOptions(cliOptions)
497
- if (!finalOptions) {
498
- finalOptions = await promptForOptions(cliOptions)
499
- }
500
- await createApp(finalOptions)
501
- } catch (error) {
502
- log.error(
503
- error instanceof Error ? error.message : 'An unknown error occurred',
504
- )
505
- process.exit(1)
506
- }
507
- })
508
-
509
- program.parse()
4
+ cli()
package/src/mcp.ts ADDED
@@ -0,0 +1,205 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
3
+ import { z } from 'zod'
4
+
5
+ import { createApp } from './create-app.js'
6
+ import { finalizeAddOns } from './add-ons.js'
7
+
8
+ const server = new McpServer({
9
+ name: 'Demo',
10
+ version: '1.0.0',
11
+ })
12
+
13
+ const tanStackReactAddOns = [
14
+ {
15
+ id: 'clerk',
16
+ description: 'Enable authentication with Clerk',
17
+ },
18
+ {
19
+ id: 'convex',
20
+ description: 'Enable a database using Convex',
21
+ },
22
+ {
23
+ id: 'form',
24
+ description: 'Form handling library',
25
+ },
26
+ {
27
+ id: 'netlify',
28
+ description: 'Enable deployments to Netlify',
29
+ },
30
+ {
31
+ id: 'sentry',
32
+ description: 'Enable Sentry error tracking',
33
+ },
34
+ {
35
+ id: 'shadcn',
36
+ description: 'Enable integration of the Shadcn UI component library',
37
+ },
38
+ {
39
+ id: 'start',
40
+ description:
41
+ 'Set this if you want a TanStack Start application that supports server functions or APIs',
42
+ },
43
+ {
44
+ id: 'tanstack-query',
45
+ description: 'Enable TanStack Query for data fetching',
46
+ },
47
+ {
48
+ id: 'store',
49
+ description: 'Enable the TanStack Store state management library',
50
+ },
51
+ ]
52
+
53
+ server.tool('listTanStackReactAddOns', {}, () => {
54
+ return {
55
+ content: [{ type: 'text', text: JSON.stringify(tanStackReactAddOns) }],
56
+ }
57
+ })
58
+
59
+ server.tool(
60
+ 'createTanStackReactApplication',
61
+ {
62
+ projectName: z
63
+ .string()
64
+ .describe(
65
+ 'The package.json module name of the application (will also be the directory name)',
66
+ ),
67
+ cwd: z.string().describe('The directory to create the application in'),
68
+ addOns: z
69
+ .array(
70
+ z.enum([
71
+ 'clerk',
72
+ 'convex',
73
+ 'form',
74
+ 'netlify',
75
+ 'sentry',
76
+ 'shadcn',
77
+ 'start',
78
+ 'store',
79
+ 'tanstack-query',
80
+ ]),
81
+ )
82
+ .describe('The IDs of the add-ons to install'),
83
+ },
84
+ async ({ projectName, addOns, cwd }) => {
85
+ try {
86
+ process.chdir(cwd)
87
+ const chosenAddOns = await finalizeAddOns(
88
+ 'react',
89
+ 'file-router',
90
+ addOns as unknown as Array<string>,
91
+ )
92
+ await createApp(
93
+ {
94
+ projectName: projectName.replace(/^\//, './'),
95
+ framework: 'react',
96
+ typescript: true,
97
+ tailwind: true,
98
+ packageManager: 'pnpm',
99
+ mode: 'file-router',
100
+ addOns: true,
101
+ chosenAddOns,
102
+ git: true,
103
+ variableValues: {},
104
+ },
105
+ {
106
+ silent: true,
107
+ },
108
+ )
109
+ return {
110
+ content: [{ type: 'text', text: 'Application created successfully' }],
111
+ }
112
+ } catch (error) {
113
+ return {
114
+ content: [
115
+ { type: 'text', text: `Error creating application: ${error}` },
116
+ ],
117
+ }
118
+ }
119
+ },
120
+ )
121
+
122
+ const tanStackSolidAddOns = [
123
+ {
124
+ id: 'solid-ui',
125
+ description: 'Enable integration of the Solid UI component library',
126
+ },
127
+ {
128
+ id: 'form',
129
+ description: 'Form handling library',
130
+ },
131
+ {
132
+ id: 'sentry',
133
+ description: 'Enable Sentry error tracking',
134
+ },
135
+ {
136
+ id: 'store',
137
+ description: 'Enable the TanStack Store state management library',
138
+ },
139
+ {
140
+ id: 'tanstack-query',
141
+ description: 'Enable TanStack Query for data fetching',
142
+ },
143
+ ]
144
+
145
+ server.tool('listTanStackSolidAddOns', {}, () => {
146
+ return {
147
+ content: [{ type: 'text', text: JSON.stringify(tanStackSolidAddOns) }],
148
+ }
149
+ })
150
+
151
+ server.tool(
152
+ 'createTanStackSolidApplication',
153
+ {
154
+ projectName: z
155
+ .string()
156
+ .describe(
157
+ 'The package.json module name of the application (will also be the directory name)',
158
+ ),
159
+ cwd: z.string().describe('The directory to create the application in'),
160
+ addOns: z
161
+ .array(z.enum(['solid-ui', 'form', 'sentry', 'store', 'tanstack-query']))
162
+ .describe('The IDs of the add-ons to install'),
163
+ },
164
+ async ({ projectName, addOns, cwd }) => {
165
+ try {
166
+ process.chdir(cwd)
167
+ const chosenAddOns = await finalizeAddOns(
168
+ 'solid',
169
+ 'file-router',
170
+ addOns as unknown as Array<string>,
171
+ )
172
+ await createApp(
173
+ {
174
+ projectName: projectName.replace(/^\//, './'),
175
+ framework: 'solid',
176
+ typescript: true,
177
+ tailwind: true,
178
+ packageManager: 'pnpm',
179
+ mode: 'file-router',
180
+ addOns: true,
181
+ chosenAddOns,
182
+ git: true,
183
+ variableValues: {},
184
+ },
185
+ {
186
+ silent: true,
187
+ },
188
+ )
189
+ return {
190
+ content: [{ type: 'text', text: 'Application created successfully' }],
191
+ }
192
+ } catch (error) {
193
+ return {
194
+ content: [
195
+ { type: 'text', text: `Error creating application: ${error}` },
196
+ ],
197
+ }
198
+ }
199
+ },
200
+ )
201
+
202
+ export default async function runServer() {
203
+ const transport = new StdioServerTransport()
204
+ await server.connect(transport)
205
+ }