@tanstack/cta-engine 0.10.0-alpha.10

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/dist/add-ons.js +109 -0
  3. package/dist/add.js +125 -0
  4. package/dist/cli.js +119 -0
  5. package/dist/config-file.js +23 -0
  6. package/dist/constants.js +5 -0
  7. package/dist/create-app.js +491 -0
  8. package/dist/custom-add-on.js +253 -0
  9. package/dist/environment.js +119 -0
  10. package/dist/index.js +1 -0
  11. package/dist/mcp.js +213 -0
  12. package/dist/options.js +328 -0
  13. package/dist/package-manager.js +30 -0
  14. package/dist/templates.js +6 -0
  15. package/dist/toolchain.js +6 -0
  16. package/dist/types/add-ons.d.ts +5 -0
  17. package/dist/types/add.d.ts +3 -0
  18. package/dist/types/cli.d.ts +5 -0
  19. package/dist/types/config-file.d.ts +7 -0
  20. package/dist/types/constants.d.ts +6 -0
  21. package/dist/types/create-app.d.ts +7 -0
  22. package/dist/types/custom-add-on.d.ts +3 -0
  23. package/dist/types/environment.d.ts +12 -0
  24. package/dist/types/index.d.ts +1 -0
  25. package/dist/types/mcp.d.ts +1 -0
  26. package/dist/types/options.d.ts +6 -0
  27. package/dist/types/package-manager.d.ts +6 -0
  28. package/dist/types/templates.d.ts +1 -0
  29. package/dist/types/toolchain.d.ts +3 -0
  30. package/dist/types/types.d.ts +107 -0
  31. package/dist/types/utils.d.ts +1 -0
  32. package/dist/types.js +1 -0
  33. package/dist/utils.js +8 -0
  34. package/package.json +49 -0
  35. package/src/add-ons.ts +145 -0
  36. package/src/add.ts +186 -0
  37. package/src/cli.ts +181 -0
  38. package/src/config-file.ts +45 -0
  39. package/src/constants.ts +9 -0
  40. package/src/create-app.ts +793 -0
  41. package/src/custom-add-on.ts +326 -0
  42. package/src/environment.ts +144 -0
  43. package/src/index.ts +1 -0
  44. package/src/mcp.ts +254 -0
  45. package/src/options.ts +392 -0
  46. package/src/package-manager.ts +46 -0
  47. package/src/templates.ts +7 -0
  48. package/src/toolchain.ts +7 -0
  49. package/src/types.ts +119 -0
  50. package/src/utils.ts +10 -0
  51. package/templates/react/add-on/clerk/README.md +3 -0
  52. package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
  53. package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
  54. package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
  55. package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
  56. package/templates/react/add-on/clerk/info.json +13 -0
  57. package/templates/react/add-on/clerk/package.json +5 -0
  58. package/templates/react/add-on/convex/README.md +4 -0
  59. package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
  60. package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
  61. package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
  62. package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
  63. package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
  64. package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
  65. package/templates/react/add-on/convex/info.json +13 -0
  66. package/templates/react/add-on/convex/package.json +6 -0
  67. package/templates/react/add-on/form/assets/src/components/demo.FormComponents.tsx.ejs +300 -0
  68. package/templates/react/add-on/form/assets/src/hooks/demo.form-context.ts +4 -0
  69. package/templates/react/add-on/form/assets/src/hooks/demo.form.ts +22 -0
  70. package/templates/react/add-on/form/assets/src/routes/demo.form.address.tsx.ejs +213 -0
  71. package/templates/react/add-on/form/assets/src/routes/demo.form.simple.tsx.ejs +77 -0
  72. package/templates/react/add-on/form/info.json +26 -0
  73. package/templates/react/add-on/form/package.json +6 -0
  74. package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
  75. package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  76. package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
  77. package/templates/react/add-on/module-federation/info.json +7 -0
  78. package/templates/react/add-on/module-federation/package.json +5 -0
  79. package/templates/react/add-on/netlify/README.md +11 -0
  80. package/templates/react/add-on/netlify/info.json +7 -0
  81. package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  82. package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
  83. package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
  84. package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
  85. package/templates/react/add-on/sentry/info.json +14 -0
  86. package/templates/react/add-on/sentry/package.json +7 -0
  87. package/templates/react/add-on/shadcn/README.md +7 -0
  88. package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
  89. package/templates/react/add-on/shadcn/assets/components.json +21 -0
  90. package/templates/react/add-on/shadcn/assets/src/lib/utils.ts +6 -0
  91. package/templates/react/add-on/shadcn/assets/src/styles.css +138 -0
  92. package/templates/react/add-on/shadcn/info.json +7 -0
  93. package/templates/react/add-on/shadcn/package.json +9 -0
  94. package/templates/react/add-on/start/assets/_dot_gitignore.append +2 -0
  95. package/templates/react/add-on/start/assets/app.config.ts.ejs +19 -0
  96. package/templates/react/add-on/start/assets/src/api.ts +6 -0
  97. package/templates/react/add-on/start/assets/src/client.tsx +8 -0
  98. package/templates/react/add-on/start/assets/src/router.tsx.ejs +77 -0
  99. package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
  100. package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
  101. package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +50 -0
  102. package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
  103. package/templates/react/add-on/start/info.json +18 -0
  104. package/templates/react/add-on/start/package.json +13 -0
  105. package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
  106. package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
  107. package/templates/react/add-on/store/info.json +13 -0
  108. package/templates/react/add-on/store/package.json +6 -0
  109. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/init.ts +9 -0
  110. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/react.ts +4 -0
  111. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/router.ts +18 -0
  112. package/templates/react/add-on/tRPC/assets/src/routes/api.trpc.$.tsx +16 -0
  113. package/templates/react/add-on/tRPC/info.json +9 -0
  114. package/templates/react/add-on/tRPC/package.json +9 -0
  115. package/templates/react/add-on/table/assets/src/data/demo-table-data.ts +50 -0
  116. package/templates/react/add-on/table/assets/src/routes/demo.table.tsx.ejs +373 -0
  117. package/templates/react/add-on/table/info.json +13 -0
  118. package/templates/react/add-on/table/package.json +7 -0
  119. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
  120. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/root-provider.tsx.ejs +70 -0
  121. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +53 -0
  122. package/templates/react/add-on/tanstack-query/info.json +13 -0
  123. package/templates/react/add-on/tanstack-query/package.json +6 -0
  124. package/templates/react/base/README.md.ejs +558 -0
  125. package/templates/react/base/_dot_gitignore +5 -0
  126. package/templates/react/base/_dot_vscode/settings.biome.json +38 -0
  127. package/templates/react/base/_dot_vscode/settings.json +11 -0
  128. package/templates/react/base/index.html.ejs +20 -0
  129. package/templates/react/base/package.biome.json +10 -0
  130. package/templates/react/base/package.eslintprettier.json +11 -0
  131. package/templates/react/base/package.json +29 -0
  132. package/templates/react/base/package.ts.json +7 -0
  133. package/templates/react/base/package.tw.json +6 -0
  134. package/templates/react/base/public/favicon.ico +0 -0
  135. package/templates/react/base/public/logo192.png +0 -0
  136. package/templates/react/base/public/logo512.png +0 -0
  137. package/templates/react/base/public/manifest.json +25 -0
  138. package/templates/react/base/public/robots.txt +3 -0
  139. package/templates/react/base/src/App.css +38 -0
  140. package/templates/react/base/src/App.test.tsx.ejs +10 -0
  141. package/templates/react/base/src/App.tsx.ejs +74 -0
  142. package/templates/react/base/src/components/Header.tsx.ejs +27 -0
  143. package/templates/react/base/src/logo.svg +44 -0
  144. package/templates/react/base/src/reportWebVitals.ts.ejs +28 -0
  145. package/templates/react/base/src/styles.css.ejs +15 -0
  146. package/templates/react/base/toolchain/.prettierignore +3 -0
  147. package/templates/react/base/toolchain/biome.json +31 -0
  148. package/templates/react/base/toolchain/eslint.config.js +5 -0
  149. package/templates/react/base/toolchain/prettier.config.js +10 -0
  150. package/templates/react/base/tsconfig.json.ejs +29 -0
  151. package/templates/react/base/vite.config.js.ejs +23 -0
  152. package/templates/react/code-router/src/main.tsx.ejs +92 -0
  153. package/templates/react/example/tanchat/README.md +37 -0
  154. package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
  155. package/templates/react/example/tanchat/assets/public/example-guitar-flowers.jpg +0 -0
  156. package/templates/react/example/tanchat/assets/public/example-guitar-motherboard.jpg +0 -0
  157. package/templates/react/example/tanchat/assets/public/example-guitar-racing.jpg +0 -0
  158. package/templates/react/example/tanchat/assets/public/example-guitar-steamer-trunk.jpg +0 -0
  159. package/templates/react/example/tanchat/assets/public/example-guitar-superhero.jpg +0 -0
  160. package/templates/react/example/tanchat/assets/public/example-guitar-traveling.jpg +0 -0
  161. package/templates/react/example/tanchat/assets/public/example-guitar-video-games.jpg +0 -0
  162. package/templates/react/example/tanchat/assets/src/components/example-AIAssistant.tsx +173 -0
  163. package/templates/react/example/tanchat/assets/src/components/example-GuitarRecommendation.tsx +47 -0
  164. package/templates/react/example/tanchat/assets/src/data/example-guitars.ts +83 -0
  165. package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
  166. package/templates/react/example/tanchat/assets/src/integrations/tanchat/header-user.tsx +5 -0
  167. package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx +159 -0
  168. package/templates/react/example/tanchat/assets/src/routes/example.guitars/$guitarId.tsx +50 -0
  169. package/templates/react/example/tanchat/assets/src/routes/example.guitars/index.tsx +54 -0
  170. package/templates/react/example/tanchat/assets/src/store/example-assistant.ts +3 -0
  171. package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +62 -0
  172. package/templates/react/example/tanchat/assets/src/utils/demo.tools.ts +47 -0
  173. package/templates/react/example/tanchat/info.json +19 -0
  174. package/templates/react/example/tanchat/package.json +15 -0
  175. package/templates/react/file-router/package.fr.json +5 -0
  176. package/templates/react/file-router/src/main.tsx.ejs +55 -0
  177. package/templates/react/file-router/src/routes/__root.tsx.ejs +82 -0
  178. package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +352 -0
  179. package/templates/solid/add-on/form/info.json +13 -0
  180. package/templates/solid/add-on/form/package.json +5 -0
  181. package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
  182. package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  183. package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
  184. package/templates/solid/add-on/module-federation/info.json +7 -0
  185. package/templates/solid/add-on/module-federation/package.json +5 -0
  186. package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  187. package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
  188. package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
  189. package/templates/solid/add-on/sentry/info.json +13 -0
  190. package/templates/solid/add-on/sentry/package.json +5 -0
  191. package/templates/solid/add-on/solid-ui/README.md +9 -0
  192. package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
  193. package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
  194. package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
  195. package/templates/solid/add-on/solid-ui/info.json +11 -0
  196. package/templates/solid/add-on/solid-ui/package.json +9 -0
  197. package/templates/solid/add-on/start/assets/app.config.ts +16 -0
  198. package/templates/solid/add-on/start/assets/src/api.ts +6 -0
  199. package/templates/solid/add-on/start/assets/src/client.tsx +7 -0
  200. package/templates/solid/add-on/start/assets/src/router.tsx.ejs +24 -0
  201. package/templates/solid/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
  202. package/templates/solid/add-on/start/assets/src/ssr.tsx +12 -0
  203. package/templates/solid/add-on/start/info.json +14 -0
  204. package/templates/solid/add-on/start/package.json +12 -0
  205. package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
  206. package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
  207. package/templates/solid/add-on/store/info.json +13 -0
  208. package/templates/solid/add-on/store/package.json +6 -0
  209. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
  210. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
  211. package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
  212. package/templates/solid/add-on/tanstack-query/info.json +13 -0
  213. package/templates/solid/add-on/tanstack-query/package.json +6 -0
  214. package/templates/solid/base/README.md.ejs +215 -0
  215. package/templates/solid/base/_dot_cursorrules.append +35 -0
  216. package/templates/solid/base/_dot_gitignore +5 -0
  217. package/templates/solid/base/_dot_vscode/settings.biome.json +38 -0
  218. package/templates/solid/base/_dot_vscode/settings.json +11 -0
  219. package/templates/solid/base/index.html.ejs +20 -0
  220. package/templates/solid/base/package.biome.json +10 -0
  221. package/templates/solid/base/package.eslintprettier.json +11 -0
  222. package/templates/solid/base/package.json +22 -0
  223. package/templates/solid/base/package.ts.json +5 -0
  224. package/templates/solid/base/package.tw.json +6 -0
  225. package/templates/solid/base/public/favicon.ico +0 -0
  226. package/templates/solid/base/public/logo192.png +0 -0
  227. package/templates/solid/base/public/logo512.png +0 -0
  228. package/templates/solid/base/public/manifest.json +25 -0
  229. package/templates/solid/base/public/robots.txt +3 -0
  230. package/templates/solid/base/src/App.css +0 -0
  231. package/templates/solid/base/src/App.tsx.ejs +47 -0
  232. package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
  233. package/templates/solid/base/src/logo.svg +120 -0
  234. package/templates/solid/base/src/styles.css.ejs +15 -0
  235. package/templates/solid/base/toolchain/.prettierignore +3 -0
  236. package/templates/solid/base/toolchain/biome.json +31 -0
  237. package/templates/solid/base/toolchain/eslint.config.js +5 -0
  238. package/templates/solid/base/toolchain/prettier.config.js +10 -0
  239. package/templates/solid/base/tsconfig.json.ejs +31 -0
  240. package/templates/solid/base/vite.config.js.ejs +22 -0
  241. package/templates/solid/code-router/src/main.tsx.ejs +71 -0
  242. package/templates/solid/example/tanchat/README.md +52 -0
  243. package/templates/solid/example/tanchat/assets/ai-streaming-server/README.md +110 -0
  244. package/templates/solid/example/tanchat/assets/ai-streaming-server/_dot_env.example +1 -0
  245. package/templates/solid/example/tanchat/assets/ai-streaming-server/package.json +26 -0
  246. package/templates/solid/example/tanchat/assets/ai-streaming-server/src/index.ts +102 -0
  247. package/templates/solid/example/tanchat/assets/ai-streaming-server/tsconfig.json +15 -0
  248. package/templates/solid/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +149 -0
  249. package/templates/solid/example/tanchat/assets/src/demo.index.css +227 -0
  250. package/templates/solid/example/tanchat/assets/src/lib/demo-store.ts +13 -0
  251. package/templates/solid/example/tanchat/assets/src/routes/example.chat.tsx +435 -0
  252. package/templates/solid/example/tanchat/assets/src/store/demo.hooks.ts +17 -0
  253. package/templates/solid/example/tanchat/assets/src/store/demo.store.ts +133 -0
  254. package/templates/solid/example/tanchat/info.json +14 -0
  255. package/templates/solid/example/tanchat/package.json +7 -0
  256. package/templates/solid/file-router/package.fr.json +5 -0
  257. package/templates/solid/file-router/src/main.tsx.ejs +47 -0
  258. package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
  259. package/templates/solid/file-router/src/routes/index.tsx +43 -0
  260. package/tests/cra.test.ts +293 -0
  261. package/tests/snapshots/cra/cr-js-npm.json +33 -0
  262. package/tests/snapshots/cra/cr-ts-npm.json +34 -0
  263. package/tests/snapshots/cra/cr-ts-start-npm.json +38 -0
  264. package/tests/snapshots/cra/fr-ts-npm.json +34 -0
  265. package/tests/snapshots/cra/fr-ts-tw-npm.json +33 -0
  266. package/tests/snapshots/cra/solid-cr-js-npm.json +31 -0
  267. package/tests/snapshots/cra/solid-cr-ts-npm.json +32 -0
  268. package/tests/snapshots/cra/solid-cr-ts-start-npm.json +36 -0
  269. package/tests/snapshots/cra/solid-fr-ts-npm.json +33 -0
  270. package/tests/snapshots/cra/solid-fr-ts-tw-npm.json +32 -0
  271. package/tests/test-utilities.ts +87 -0
  272. package/tsconfig.json +17 -0
package/src/mcp.ts ADDED
@@ -0,0 +1,254 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
+ import express from 'express'
5
+ import { z } from 'zod'
6
+
7
+ import { createApp } from './create-app.js'
8
+ import { finalizeAddOns } from './add-ons.js'
9
+ import { createDefaultEnvironment } from './environment.js'
10
+
11
+ const server = new McpServer({
12
+ name: 'Demo',
13
+ version: '1.0.0',
14
+ })
15
+
16
+ const tanStackReactAddOns = [
17
+ {
18
+ id: 'clerk',
19
+ description: 'Enable authentication with Clerk',
20
+ },
21
+ {
22
+ id: 'convex',
23
+ description: 'Enable a database using Convex',
24
+ },
25
+ {
26
+ id: 'form',
27
+ description: 'Form handling library',
28
+ },
29
+ {
30
+ id: 'netlify',
31
+ description: 'Enable deployments to Netlify',
32
+ },
33
+ {
34
+ id: 'sentry',
35
+ description: 'Enable Sentry error tracking',
36
+ },
37
+ {
38
+ id: 'shadcn',
39
+ description: 'Enable integration of the Shadcn UI component library',
40
+ },
41
+ {
42
+ id: 'start',
43
+ description:
44
+ 'Set this if you want a TanStack Start application that supports server functions or APIs',
45
+ },
46
+ {
47
+ id: 'tanstack-query',
48
+ description: 'Enable TanStack Query for data fetching',
49
+ },
50
+ {
51
+ id: 'store',
52
+ description: 'Enable the TanStack Store state management library',
53
+ },
54
+ {
55
+ id: 'tanchat',
56
+ description: 'Add an AI chatbot example to the application',
57
+ },
58
+ ]
59
+
60
+ server.tool('listTanStackReactAddOns', {}, () => {
61
+ return {
62
+ content: [{ type: 'text', text: JSON.stringify(tanStackReactAddOns) }],
63
+ }
64
+ })
65
+
66
+ server.tool(
67
+ 'createTanStackReactApplication',
68
+ {
69
+ projectName: z
70
+ .string()
71
+ .describe(
72
+ 'The package.json module name of the application (will also be the directory name)',
73
+ ),
74
+ cwd: z.string().describe('The directory to create the application in'),
75
+ addOns: z
76
+ .array(
77
+ z.enum([
78
+ 'clerk',
79
+ 'convex',
80
+ 'form',
81
+ 'netlify',
82
+ 'sentry',
83
+ 'shadcn',
84
+ 'start',
85
+ 'store',
86
+ 'tanstack-query',
87
+ 'tanchat',
88
+ ]),
89
+ )
90
+ .describe('The IDs of the add-ons to install'),
91
+ },
92
+ async ({ projectName, addOns, cwd }) => {
93
+ try {
94
+ process.chdir(cwd)
95
+ const chosenAddOns = await finalizeAddOns(
96
+ 'react',
97
+ 'file-router',
98
+ addOns as unknown as Array<string>,
99
+ )
100
+ await createApp(
101
+ {
102
+ projectName: projectName.replace(/^\//, './'),
103
+ framework: 'react',
104
+ typescript: true,
105
+ tailwind: true,
106
+ packageManager: 'pnpm',
107
+ toolchain: 'none',
108
+ mode: 'file-router',
109
+ addOns: true,
110
+ chosenAddOns,
111
+ git: true,
112
+ variableValues: {},
113
+ },
114
+ {
115
+ silent: true,
116
+ environment: createDefaultEnvironment(),
117
+ name: 'create-tsrouter-app',
118
+ },
119
+ )
120
+ return {
121
+ content: [{ type: 'text', text: 'Application created successfully' }],
122
+ }
123
+ } catch (error) {
124
+ return {
125
+ content: [
126
+ { type: 'text', text: `Error creating application: ${error}` },
127
+ ],
128
+ }
129
+ }
130
+ },
131
+ )
132
+
133
+ const tanStackSolidAddOns = [
134
+ {
135
+ id: 'solid-ui',
136
+ description: 'Enable integration of the Solid UI component library',
137
+ },
138
+ {
139
+ id: 'form',
140
+ description: 'Form handling library',
141
+ },
142
+ {
143
+ id: 'sentry',
144
+ description: 'Enable Sentry error tracking',
145
+ },
146
+ {
147
+ id: 'store',
148
+ description: 'Enable the TanStack Store state management library',
149
+ },
150
+ {
151
+ id: 'tanstack-query',
152
+ description: 'Enable TanStack Query for data fetching',
153
+ },
154
+ {
155
+ id: 'tanchat',
156
+ description: 'Add an AI chatbot example to the application',
157
+ },
158
+ ]
159
+
160
+ server.tool('listTanStackSolidAddOns', {}, () => {
161
+ return {
162
+ content: [{ type: 'text', text: JSON.stringify(tanStackSolidAddOns) }],
163
+ }
164
+ })
165
+
166
+ server.tool(
167
+ 'createTanStackSolidApplication',
168
+ {
169
+ projectName: z
170
+ .string()
171
+ .describe(
172
+ 'The package.json module name of the application (will also be the directory name)',
173
+ ),
174
+ cwd: z.string().describe('The directory to create the application in'),
175
+ addOns: z
176
+ .array(
177
+ z.enum([
178
+ 'solid-ui',
179
+ 'form',
180
+ 'sentry',
181
+ 'store',
182
+ 'tanstack-query',
183
+ 'tanchat',
184
+ ]),
185
+ )
186
+ .describe('The IDs of the add-ons to install'),
187
+ },
188
+ async ({ projectName, addOns, cwd }) => {
189
+ try {
190
+ process.chdir(cwd)
191
+ const chosenAddOns = await finalizeAddOns(
192
+ 'solid',
193
+ 'file-router',
194
+ addOns as unknown as Array<string>,
195
+ )
196
+ await createApp(
197
+ {
198
+ projectName: projectName.replace(/^\//, './'),
199
+ framework: 'solid',
200
+ typescript: true,
201
+ tailwind: true,
202
+ packageManager: 'pnpm',
203
+ toolchain: 'none',
204
+ mode: 'file-router',
205
+ addOns: true,
206
+ chosenAddOns,
207
+ git: true,
208
+ variableValues: {},
209
+ },
210
+ {
211
+ silent: true,
212
+ environment: createDefaultEnvironment(),
213
+ name: 'create-tsrouter-app',
214
+ },
215
+ )
216
+ return {
217
+ content: [{ type: 'text', text: 'Application created successfully' }],
218
+ }
219
+ } catch (error) {
220
+ return {
221
+ content: [
222
+ { type: 'text', text: `Error creating application: ${error}` },
223
+ ],
224
+ }
225
+ }
226
+ },
227
+ )
228
+
229
+ export default async function runServer(sse: boolean) {
230
+ if (sse) {
231
+ const app = express()
232
+
233
+ let transport: SSEServerTransport | null = null
234
+
235
+ app.get('/sse', (req, res) => {
236
+ transport = new SSEServerTransport('/messages', res)
237
+ server.connect(transport)
238
+ })
239
+
240
+ app.post('/messages', (req, res) => {
241
+ if (transport) {
242
+ transport.handlePostMessage(req, res)
243
+ }
244
+ })
245
+
246
+ const port = process.env.PORT || 8080
247
+ app.listen(port, () => {
248
+ console.log(`Server is running on port http://localhost:${port}/sse`)
249
+ })
250
+ } else {
251
+ const transport = new StdioServerTransport()
252
+ await server.connect(transport)
253
+ }
254
+ }
package/src/options.ts ADDED
@@ -0,0 +1,392 @@
1
+ import {
2
+ cancel,
3
+ confirm,
4
+ isCancel,
5
+ multiselect,
6
+ select,
7
+ text,
8
+ } from '@clack/prompts'
9
+
10
+ import {
11
+ DEFAULT_PACKAGE_MANAGER,
12
+ SUPPORTED_PACKAGE_MANAGERS,
13
+ getPackageManager,
14
+ } from './package-manager.js'
15
+ import { DEFAULT_TOOLCHAIN, SUPPORTED_TOOLCHAINS } from './toolchain.js'
16
+ import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js'
17
+ import { finalizeAddOns, getAllAddOns, loadRemoteAddOn } from './add-ons.js'
18
+
19
+ import type { AddOn, CliOptions, Options, Overlay, Variable } from './types.js'
20
+
21
+ // If all CLI options are provided, use them directly
22
+ export async function normalizeOptions(
23
+ cliOptions: CliOptions,
24
+ forcedAddOns?: Array<string>,
25
+ ): Promise<Options | undefined> {
26
+ // in some cases, if you use windows/powershell, the argument for addons
27
+ // if sepparated by comma is not really passed as an array, but as a string
28
+ // with spaces, We need to normalize this edge case.
29
+ if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
30
+ const parseSeparatedArgs = cliOptions.addOns[0].split(' ')
31
+ if (parseSeparatedArgs.length > 1) {
32
+ cliOptions.addOns = parseSeparatedArgs
33
+ }
34
+ if (forcedAddOns) {
35
+ cliOptions.addOns = [...cliOptions.addOns, ...forcedAddOns]
36
+ }
37
+ } else if (forcedAddOns) {
38
+ cliOptions.addOns = [...forcedAddOns]
39
+ }
40
+
41
+ if (cliOptions.projectName) {
42
+ let typescript =
43
+ cliOptions.template === 'typescript' ||
44
+ cliOptions.template === 'file-router' ||
45
+ cliOptions.framework === 'solid'
46
+
47
+ let tailwind = !!cliOptions.tailwind
48
+ if (cliOptions.framework === 'solid') {
49
+ tailwind = true
50
+ }
51
+
52
+ let mode: typeof FILE_ROUTER | typeof CODE_ROUTER =
53
+ cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
54
+
55
+ const overlay = cliOptions.overlay
56
+ ? ((await loadRemoteAddOn(cliOptions.overlay)) as Overlay)
57
+ : undefined
58
+
59
+ if (overlay) {
60
+ tailwind = overlay.tailwind
61
+ typescript = overlay.typescript
62
+ cliOptions.framework = overlay.framework
63
+ mode = overlay.mode
64
+ }
65
+
66
+ let addOns = false
67
+ let chosenAddOns: Array<AddOn> = []
68
+ if (Array.isArray(cliOptions.addOns) || overlay?.dependsOn) {
69
+ addOns = true
70
+ let finalAddOns = [...(overlay?.dependsOn || [])]
71
+ if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
72
+ finalAddOns = [...finalAddOns, ...cliOptions.addOns]
73
+ }
74
+ chosenAddOns = await finalizeAddOns(
75
+ cliOptions.framework || DEFAULT_FRAMEWORK,
76
+ cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
77
+ finalAddOns,
78
+ )
79
+ tailwind = true
80
+ typescript = true
81
+ }
82
+
83
+ return {
84
+ framework: cliOptions.framework || 'react',
85
+ projectName: cliOptions.projectName,
86
+ typescript,
87
+ tailwind,
88
+ packageManager:
89
+ cliOptions.packageManager ||
90
+ getPackageManager() ||
91
+ DEFAULT_PACKAGE_MANAGER,
92
+ toolchain: cliOptions.toolchain || DEFAULT_TOOLCHAIN,
93
+ mode,
94
+ git: !!cliOptions.git,
95
+ addOns,
96
+ chosenAddOns,
97
+ variableValues: {},
98
+ overlay,
99
+ }
100
+ }
101
+ }
102
+
103
+ async function collectVariables(
104
+ variables: Array<Variable>,
105
+ ): Promise<Record<string, string | number | boolean>> {
106
+ const responses: Record<string, string | number | boolean> = {}
107
+ for (const variable of variables) {
108
+ if (variable.type === 'string') {
109
+ const response = await text({
110
+ message: variable.description,
111
+ initialValue: variable.default,
112
+ })
113
+ if (isCancel(response)) {
114
+ cancel('Operation cancelled.')
115
+ process.exit(0)
116
+ }
117
+ responses[variable.name] = response
118
+ } else if (variable.type === 'number') {
119
+ const response = await text({
120
+ message: variable.description,
121
+ initialValue: variable.default.toString(),
122
+ })
123
+ if (isCancel(response)) {
124
+ cancel('Operation cancelled.')
125
+ process.exit(0)
126
+ }
127
+ responses[variable.name] = Number(response)
128
+ } else {
129
+ const response = await confirm({
130
+ message: variable.description,
131
+ initialValue: variable.default === true,
132
+ })
133
+ if (isCancel(response)) {
134
+ cancel('Operation cancelled.')
135
+ process.exit(0)
136
+ }
137
+ responses[variable.name] = response
138
+ }
139
+ }
140
+ return responses
141
+ }
142
+
143
+ export async function promptForOptions(
144
+ cliOptions: CliOptions,
145
+ {
146
+ forcedAddOns = [],
147
+ forcedMode,
148
+ }: {
149
+ forcedAddOns?: Array<string>
150
+ forcedMode?: 'typescript' | 'javascript' | 'file-router'
151
+ },
152
+ ): Promise<Required<Options>> {
153
+ const options = {} as Required<Options>
154
+
155
+ options.framework = cliOptions.framework || DEFAULT_FRAMEWORK
156
+ if (options.framework === 'solid') {
157
+ options.typescript = true
158
+ options.tailwind = true
159
+ }
160
+
161
+ if (cliOptions.addOns) {
162
+ options.typescript = true
163
+ }
164
+
165
+ if (!cliOptions.projectName) {
166
+ const value = await text({
167
+ message: 'What would you like to name your project?',
168
+ defaultValue: 'my-app',
169
+ validate(value) {
170
+ if (!value) {
171
+ return 'Please enter a name'
172
+ }
173
+ },
174
+ })
175
+ if (isCancel(value)) {
176
+ cancel('Operation cancelled.')
177
+ process.exit(0)
178
+ }
179
+ options.projectName = value
180
+ }
181
+
182
+ // Router type selection
183
+ if (!cliOptions.template && !forcedMode) {
184
+ const routerType = await select({
185
+ message: 'Select the router type:',
186
+ options: [
187
+ {
188
+ value: FILE_ROUTER,
189
+ label: 'File Router - File-based routing structure',
190
+ },
191
+ {
192
+ value: CODE_ROUTER,
193
+ label: 'Code Router - Traditional code-based routing',
194
+ },
195
+ ],
196
+ initialValue: FILE_ROUTER,
197
+ })
198
+ if (isCancel(routerType)) {
199
+ cancel('Operation cancelled.')
200
+ process.exit(0)
201
+ }
202
+ options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
203
+ } else if (forcedMode) {
204
+ options.mode = forcedMode === 'file-router' ? FILE_ROUTER : CODE_ROUTER
205
+ options.typescript = options.mode === FILE_ROUTER
206
+ } else {
207
+ options.mode =
208
+ cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
209
+ if (options.mode === FILE_ROUTER) {
210
+ options.typescript = true
211
+ }
212
+ }
213
+
214
+ // TypeScript selection (if using Code Router)
215
+ if (!options.typescript) {
216
+ if (options.mode === CODE_ROUTER) {
217
+ const typescriptEnable = await confirm({
218
+ message: 'Would you like to use TypeScript?',
219
+ initialValue: true,
220
+ })
221
+ if (isCancel(typescriptEnable)) {
222
+ cancel('Operation cancelled.')
223
+ process.exit(0)
224
+ }
225
+ options.typescript = typescriptEnable
226
+ } else {
227
+ options.typescript = true
228
+ }
229
+ }
230
+
231
+ // Tailwind selection
232
+ if (!cliOptions.tailwind && options.framework === 'react') {
233
+ const tailwind = await confirm({
234
+ message: 'Would you like to use Tailwind CSS?',
235
+ initialValue: true,
236
+ })
237
+ if (isCancel(tailwind)) {
238
+ cancel('Operation cancelled.')
239
+ process.exit(0)
240
+ }
241
+ options.tailwind = tailwind
242
+ } else {
243
+ options.tailwind = options.framework === 'solid' || !!cliOptions.tailwind
244
+ }
245
+
246
+ // Package manager selection
247
+ if (cliOptions.packageManager === undefined) {
248
+ const detectedPackageManager = getPackageManager()
249
+ if (!detectedPackageManager) {
250
+ const pm = await select({
251
+ message: 'Select package manager:',
252
+ options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
253
+ value: pm,
254
+ label: pm,
255
+ })),
256
+ initialValue: DEFAULT_PACKAGE_MANAGER,
257
+ })
258
+ if (isCancel(pm)) {
259
+ cancel('Operation cancelled.')
260
+ process.exit(0)
261
+ }
262
+ options.packageManager = pm
263
+ } else {
264
+ options.packageManager = detectedPackageManager
265
+ }
266
+ } else {
267
+ options.packageManager = cliOptions.packageManager
268
+ }
269
+
270
+ // Toolchain selection
271
+ if (cliOptions.toolchain === undefined) {
272
+ const tc = await select({
273
+ message: 'Select toolchain',
274
+ options: SUPPORTED_TOOLCHAINS.map((tc) => ({
275
+ value: tc,
276
+ label: tc,
277
+ })),
278
+ initialValue: DEFAULT_TOOLCHAIN,
279
+ })
280
+ if (isCancel(tc)) {
281
+ cancel('Operation cancelled.')
282
+ process.exit(0)
283
+ }
284
+ options.toolchain = tc
285
+ } else {
286
+ options.toolchain = cliOptions.toolchain
287
+ }
288
+
289
+ options.chosenAddOns = []
290
+ if (Array.isArray(cliOptions.addOns)) {
291
+ options.chosenAddOns = await finalizeAddOns(
292
+ options.framework,
293
+ options.mode,
294
+ Array.from(new Set([...cliOptions.addOns, ...forcedAddOns])),
295
+ )
296
+ options.tailwind = true
297
+ } else if (cliOptions.addOns) {
298
+ // Select any add-ons
299
+ const allAddOns = await getAllAddOns(options.framework, options.mode)
300
+ const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on')
301
+ let selectedAddOns: Array<string> = []
302
+ if (options.typescript && addOns.length > 0) {
303
+ const value = await multiselect({
304
+ message: 'What add-ons would you like for your project:',
305
+ options: addOns
306
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
307
+ .map((addOn) => ({
308
+ value: addOn.id,
309
+ label: addOn.name,
310
+ hint: addOn.description,
311
+ })),
312
+ required: false,
313
+ })
314
+
315
+ if (isCancel(value)) {
316
+ cancel('Operation cancelled.')
317
+ process.exit(0)
318
+ }
319
+ selectedAddOns = value
320
+ }
321
+
322
+ // Select any examples
323
+ let selectedExamples: Array<string> = []
324
+ const examples = allAddOns.filter((addOn) => addOn.type === 'example')
325
+ if (options.typescript && examples.length > 0) {
326
+ const value = await multiselect({
327
+ message: 'Would you like any examples?',
328
+ options: examples
329
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
330
+ .map((addOn) => ({
331
+ value: addOn.id,
332
+ label: addOn.name,
333
+ hint: addOn.description,
334
+ })),
335
+ required: false,
336
+ })
337
+
338
+ if (isCancel(value)) {
339
+ cancel('Operation cancelled.')
340
+ process.exit(0)
341
+ }
342
+ selectedExamples = value
343
+ }
344
+
345
+ if (
346
+ selectedAddOns.length > 0 ||
347
+ selectedExamples.length > 0 ||
348
+ forcedAddOns.length > 0
349
+ ) {
350
+ options.chosenAddOns = await finalizeAddOns(
351
+ options.framework,
352
+ options.mode,
353
+ Array.from(
354
+ new Set([...selectedAddOns, ...selectedExamples, ...forcedAddOns]),
355
+ ),
356
+ )
357
+ options.tailwind = true
358
+ }
359
+ } else if (forcedAddOns.length > 0) {
360
+ options.chosenAddOns = await finalizeAddOns(
361
+ options.framework,
362
+ options.mode,
363
+ forcedAddOns,
364
+ )
365
+ }
366
+
367
+ // Collect variables
368
+ const variables: Array<Variable> = []
369
+ for (const addOn of options.chosenAddOns) {
370
+ for (const variable of addOn.variables ?? []) {
371
+ variables.push(variable)
372
+ }
373
+ }
374
+ options.variableValues = await collectVariables(variables)
375
+
376
+ // Git selection
377
+ if (cliOptions.git === undefined) {
378
+ const git = await confirm({
379
+ message: 'Would you like to initialize a new git repository?',
380
+ initialValue: true,
381
+ })
382
+ if (isCancel(git)) {
383
+ cancel('Operation cancelled.')
384
+ process.exit(0)
385
+ }
386
+ options.git = git
387
+ } else {
388
+ options.git = !!cliOptions.git
389
+ }
390
+
391
+ return options
392
+ }
@@ -0,0 +1,46 @@
1
+ import type { Environment } from './types.js'
2
+
3
+ export const SUPPORTED_PACKAGE_MANAGERS = [
4
+ 'npm',
5
+ 'yarn',
6
+ 'pnpm',
7
+ 'bun',
8
+ 'deno',
9
+ ] as const
10
+ export type PackageManager = (typeof SUPPORTED_PACKAGE_MANAGERS)[number]
11
+ export const DEFAULT_PACKAGE_MANAGER: PackageManager = 'npm'
12
+
13
+ export function getPackageManager(): PackageManager | undefined {
14
+ const userAgent = process.env.npm_config_user_agent
15
+
16
+ if (userAgent === undefined) {
17
+ return DEFAULT_PACKAGE_MANAGER
18
+ }
19
+
20
+ const packageManager = SUPPORTED_PACKAGE_MANAGERS.find((manager) =>
21
+ userAgent.startsWith(manager),
22
+ )
23
+
24
+ return packageManager
25
+ }
26
+
27
+ export function packageManagerExecute(
28
+ environment: Environment,
29
+ packagerManager: PackageManager,
30
+ pkg: string,
31
+ args: Array<string>,
32
+ cwd: string,
33
+ ) {
34
+ switch (packagerManager) {
35
+ case 'yarn':
36
+ return environment.execute('yarn', ['dlx', pkg, ...args], cwd)
37
+ case 'pnpm':
38
+ return environment.execute('pnpx', [pkg, ...args], cwd)
39
+ case 'bun':
40
+ return environment.execute('bunx', ['--bun', pkg, ...args], cwd)
41
+ case 'deno':
42
+ return environment.execute('deno', ['run', `npm:${pkg}`, ...args], cwd)
43
+ default:
44
+ return environment.execute('npx', [pkg, ...args], cwd)
45
+ }
46
+ }
@@ -0,0 +1,7 @@
1
+ import { dirname, join } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ export function getTemplatesRoot() {
5
+ // Get the parent directory because the compiled code is in the dist folder
6
+ return join(dirname(dirname(fileURLToPath(import.meta.url))), 'templates')
7
+ }
@@ -0,0 +1,7 @@
1
+ export const SUPPORTED_TOOLCHAINS = [
2
+ 'none',
3
+ 'biome',
4
+ 'eslint+prettier',
5
+ ] as const
6
+ export type ToolChain = (typeof SUPPORTED_TOOLCHAINS)[number]
7
+ export const DEFAULT_TOOLCHAIN: ToolChain = 'none'