@tanstack/cta-engine 0.10.0-alpha.17 → 0.10.0-alpha.19

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 (314) hide show
  1. package/dist/add-ons.js +8 -73
  2. package/dist/{add.js → add-to-app.js} +19 -19
  3. package/dist/config-file.js +6 -3
  4. package/dist/constants.js +0 -2
  5. package/dist/create-app.js +67 -460
  6. package/dist/environment.js +11 -26
  7. package/dist/file-helpers.js +59 -0
  8. package/dist/frameworks.js +78 -0
  9. package/dist/index.js +10 -1
  10. package/dist/integrations/git.js +4 -0
  11. package/dist/integrations/shadcn.js +27 -0
  12. package/dist/package-json.js +45 -0
  13. package/dist/package-manager.js +51 -6
  14. package/dist/template-file.js +122 -0
  15. package/dist/types/add-ons.d.ts +2 -6
  16. package/dist/types/add-to-app.d.ts +4 -0
  17. package/dist/types/config-file.d.ts +3 -2
  18. package/dist/types/constants.d.ts +0 -3
  19. package/dist/types/file-helpers.d.ts +5 -0
  20. package/dist/types/frameworks.d.ts +5 -0
  21. package/dist/types/index.d.ts +13 -1
  22. package/dist/types/integrations/git.d.ts +2 -0
  23. package/dist/types/integrations/shadcn.d.ts +2 -0
  24. package/dist/types/package-json.d.ts +2 -0
  25. package/dist/types/package-manager.d.ts +14 -1
  26. package/dist/types/template-file.d.ts +2 -0
  27. package/dist/types/types.d.ts +85 -63
  28. package/dist/types/utils.d.ts +5 -0
  29. package/dist/utils.js +9 -0
  30. package/package.json +4 -13
  31. package/src/add-ons.ts +11 -104
  32. package/src/{add.ts → add-to-app.ts} +29 -35
  33. package/src/config-file.ts +12 -6
  34. package/src/constants.ts +0 -5
  35. package/src/create-app.ts +125 -736
  36. package/src/environment.ts +12 -27
  37. package/src/file-helpers.ts +73 -0
  38. package/src/frameworks.ts +114 -0
  39. package/src/index.ts +42 -1
  40. package/src/integrations/git.ts +7 -0
  41. package/src/integrations/shadcn.ts +44 -0
  42. package/src/package-json.ts +62 -0
  43. package/src/package-manager.ts +80 -9
  44. package/src/template-file.ts +165 -0
  45. package/src/types.ts +100 -69
  46. package/src/utils.ts +17 -0
  47. package/tests/add-ons.test.ts +67 -0
  48. package/tests/config-file.test.ts +64 -0
  49. package/tests/create-app.test.ts +144 -0
  50. package/tests/environment.test.ts +96 -0
  51. package/tests/file-helper.test.ts +90 -0
  52. package/tests/frameworks.test.ts +95 -0
  53. package/tests/index.test.ts +9 -0
  54. package/tests/integrations/git.test.ts +20 -0
  55. package/tests/integrations/shadcn.test.ts +106 -0
  56. package/tests/package-json.test.ts +63 -0
  57. package/tests/package-manager.test.ts +154 -0
  58. package/tests/template-file.test.ts +215 -0
  59. package/tests/utils.test.ts +23 -0
  60. package/vitest.config.ts +19 -0
  61. package/dist/cli.js +0 -125
  62. package/dist/custom-add-on.js +0 -254
  63. package/dist/file-helper.js +0 -18
  64. package/dist/mcp.js +0 -229
  65. package/dist/options.js +0 -330
  66. package/dist/templates.js +0 -6
  67. package/dist/toolchain.js +0 -6
  68. package/dist/types/add.d.ts +0 -3
  69. package/dist/types/cli.d.ts +0 -7
  70. package/dist/types/custom-add-on.d.ts +0 -3
  71. package/dist/types/file-helper.d.ts +0 -2
  72. package/dist/types/mcp.d.ts +0 -7
  73. package/dist/types/options.d.ts +0 -6
  74. package/dist/types/templates.d.ts +0 -1
  75. package/dist/types/toolchain.d.ts +0 -3
  76. package/src/cli.ts +0 -196
  77. package/src/custom-add-on.ts +0 -325
  78. package/src/file-helper.ts +0 -20
  79. package/src/mcp.ts +0 -302
  80. package/src/options.ts +0 -410
  81. package/src/templates.ts +0 -7
  82. package/src/toolchain.ts +0 -7
  83. package/templates/react/add-on/clerk/README.md +0 -3
  84. package/templates/react/add-on/clerk/assets/_dot_env.local.append +0 -2
  85. package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +0 -19
  86. package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +0 -18
  87. package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +0 -20
  88. package/templates/react/add-on/clerk/info.json +0 -13
  89. package/templates/react/add-on/clerk/package.json +0 -5
  90. package/templates/react/add-on/convex/README.md +0 -4
  91. package/templates/react/add-on/convex/assets/_dot_cursorrules.append +0 -93
  92. package/templates/react/add-on/convex/assets/_dot_env.local.append +0 -3
  93. package/templates/react/add-on/convex/assets/convex/products.ts +0 -8
  94. package/templates/react/add-on/convex/assets/convex/schema.ts +0 -10
  95. package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +0 -20
  96. package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +0 -33
  97. package/templates/react/add-on/convex/info.json +0 -13
  98. package/templates/react/add-on/convex/package.json +0 -6
  99. package/templates/react/add-on/form/assets/src/components/demo.FormComponents.tsx.ejs +0 -300
  100. package/templates/react/add-on/form/assets/src/hooks/demo.form-context.ts +0 -4
  101. package/templates/react/add-on/form/assets/src/hooks/demo.form.ts +0 -22
  102. package/templates/react/add-on/form/assets/src/routes/demo.form.address.tsx.ejs +0 -213
  103. package/templates/react/add-on/form/assets/src/routes/demo.form.simple.tsx.ejs +0 -77
  104. package/templates/react/add-on/form/info.json +0 -26
  105. package/templates/react/add-on/form/package.json +0 -6
  106. package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +0 -31
  107. package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +0 -3
  108. package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +0 -11
  109. package/templates/react/add-on/module-federation/info.json +0 -7
  110. package/templates/react/add-on/module-federation/package.json +0 -5
  111. package/templates/react/add-on/netlify/README.md +0 -11
  112. package/templates/react/add-on/netlify/info.json +0 -7
  113. package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +0 -22
  114. package/templates/react/add-on/sentry/assets/_dot_env.local.append +0 -2
  115. package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +0 -25
  116. package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +0 -480
  117. package/templates/react/add-on/sentry/info.json +0 -14
  118. package/templates/react/add-on/sentry/package.json +0 -7
  119. package/templates/react/add-on/shadcn/README.md +0 -7
  120. package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +0 -7
  121. package/templates/react/add-on/shadcn/assets/components.json +0 -21
  122. package/templates/react/add-on/shadcn/assets/src/lib/utils.ts +0 -6
  123. package/templates/react/add-on/shadcn/assets/src/styles.css +0 -138
  124. package/templates/react/add-on/shadcn/info.json +0 -7
  125. package/templates/react/add-on/shadcn/package.json +0 -9
  126. package/templates/react/add-on/start/assets/_dot_gitignore.append +0 -2
  127. package/templates/react/add-on/start/assets/app.config.ts.ejs +0 -19
  128. package/templates/react/add-on/start/assets/src/api.ts +0 -6
  129. package/templates/react/add-on/start/assets/src/client.tsx +0 -8
  130. package/templates/react/add-on/start/assets/src/router.tsx.ejs +0 -77
  131. package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +0 -11
  132. package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +0 -33
  133. package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +0 -50
  134. package/templates/react/add-on/start/assets/src/ssr.tsx +0 -12
  135. package/templates/react/add-on/start/info.json +0 -18
  136. package/templates/react/add-on/start/package.json +0 -13
  137. package/templates/react/add-on/store/assets/src/lib/demo-store.ts +0 -13
  138. package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +0 -75
  139. package/templates/react/add-on/store/info.json +0 -13
  140. package/templates/react/add-on/store/package.json +0 -6
  141. package/templates/react/add-on/t3env/README.md +0 -16
  142. package/templates/react/add-on/t3env/assets/src/env.ts +0 -39
  143. package/templates/react/add-on/t3env/info.json +0 -10
  144. package/templates/react/add-on/t3env/package.json +0 -6
  145. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/init.ts +0 -9
  146. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/react.ts +0 -4
  147. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/router.ts +0 -18
  148. package/templates/react/add-on/tRPC/assets/src/routes/api.trpc.$.tsx +0 -16
  149. package/templates/react/add-on/tRPC/info.json +0 -9
  150. package/templates/react/add-on/tRPC/package.json +0 -9
  151. package/templates/react/add-on/table/assets/src/data/demo-table-data.ts +0 -50
  152. package/templates/react/add-on/table/assets/src/routes/demo.table.tsx.ejs +0 -373
  153. package/templates/react/add-on/table/info.json +0 -13
  154. package/templates/react/add-on/table/package.json +0 -7
  155. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +0 -5
  156. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/root-provider.tsx.ejs +0 -70
  157. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +0 -53
  158. package/templates/react/add-on/tanstack-query/info.json +0 -13
  159. package/templates/react/add-on/tanstack-query/package.json +0 -6
  160. package/templates/react/base/README.md.ejs +0 -558
  161. package/templates/react/base/_dot_gitignore +0 -5
  162. package/templates/react/base/_dot_vscode/settings.biome.json +0 -38
  163. package/templates/react/base/_dot_vscode/settings.json +0 -11
  164. package/templates/react/base/index.html.ejs +0 -20
  165. package/templates/react/base/package.biome.json +0 -10
  166. package/templates/react/base/package.eslintprettier.json +0 -11
  167. package/templates/react/base/package.json +0 -30
  168. package/templates/react/base/package.ts.json +0 -7
  169. package/templates/react/base/package.tw.json +0 -6
  170. package/templates/react/base/public/favicon.ico +0 -0
  171. package/templates/react/base/public/logo192.png +0 -0
  172. package/templates/react/base/public/logo512.png +0 -0
  173. package/templates/react/base/public/manifest.json +0 -25
  174. package/templates/react/base/public/robots.txt +0 -3
  175. package/templates/react/base/src/App.css +0 -38
  176. package/templates/react/base/src/App.test.tsx.ejs +0 -10
  177. package/templates/react/base/src/App.tsx.ejs +0 -74
  178. package/templates/react/base/src/components/Header.tsx.ejs +0 -27
  179. package/templates/react/base/src/logo.svg +0 -44
  180. package/templates/react/base/src/reportWebVitals.ts.ejs +0 -28
  181. package/templates/react/base/src/styles.css.ejs +0 -15
  182. package/templates/react/base/toolchain/.prettierignore +0 -3
  183. package/templates/react/base/toolchain/biome.json +0 -31
  184. package/templates/react/base/toolchain/eslint.config.js +0 -5
  185. package/templates/react/base/toolchain/prettier.config.js +0 -10
  186. package/templates/react/base/tsconfig.json.ejs +0 -29
  187. package/templates/react/base/vite.config.js.ejs +0 -23
  188. package/templates/react/code-router/src/main.tsx.ejs +0 -92
  189. package/templates/react/example/tanchat/README.md +0 -37
  190. package/templates/react/example/tanchat/assets/_dot_env.local.append +0 -2
  191. package/templates/react/example/tanchat/assets/public/example-guitar-flowers.jpg +0 -0
  192. package/templates/react/example/tanchat/assets/public/example-guitar-motherboard.jpg +0 -0
  193. package/templates/react/example/tanchat/assets/public/example-guitar-racing.jpg +0 -0
  194. package/templates/react/example/tanchat/assets/public/example-guitar-steamer-trunk.jpg +0 -0
  195. package/templates/react/example/tanchat/assets/public/example-guitar-superhero.jpg +0 -0
  196. package/templates/react/example/tanchat/assets/public/example-guitar-traveling.jpg +0 -0
  197. package/templates/react/example/tanchat/assets/public/example-guitar-video-games.jpg +0 -0
  198. package/templates/react/example/tanchat/assets/src/components/example-AIAssistant.tsx +0 -173
  199. package/templates/react/example/tanchat/assets/src/components/example-GuitarRecommendation.tsx +0 -47
  200. package/templates/react/example/tanchat/assets/src/data/example-guitars.ts +0 -83
  201. package/templates/react/example/tanchat/assets/src/demo.index.css +0 -220
  202. package/templates/react/example/tanchat/assets/src/integrations/tanchat/header-user.tsx +0 -5
  203. package/templates/react/example/tanchat/assets/src/routes/api.messages.ts +0 -24
  204. package/templates/react/example/tanchat/assets/src/routes/api.sse.ts +0 -23
  205. package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx +0 -159
  206. package/templates/react/example/tanchat/assets/src/routes/example.guitars/$guitarId.tsx +0 -50
  207. package/templates/react/example/tanchat/assets/src/routes/example.guitars/index.tsx +0 -54
  208. package/templates/react/example/tanchat/assets/src/store/example-assistant.ts +0 -3
  209. package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +0 -62
  210. package/templates/react/example/tanchat/assets/src/utils/demo.sse.ts +0 -31
  211. package/templates/react/example/tanchat/assets/src/utils/demo.tools.ts +0 -47
  212. package/templates/react/example/tanchat/info.json +0 -19
  213. package/templates/react/example/tanchat/package.json +0 -16
  214. package/templates/react/file-router/package.fr.json +0 -5
  215. package/templates/react/file-router/src/main.tsx.ejs +0 -55
  216. package/templates/react/file-router/src/routes/__root.tsx.ejs +0 -82
  217. package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +0 -352
  218. package/templates/solid/add-on/form/info.json +0 -13
  219. package/templates/solid/add-on/form/package.json +0 -5
  220. package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +0 -27
  221. package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +0 -3
  222. package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +0 -9
  223. package/templates/solid/add-on/module-federation/info.json +0 -7
  224. package/templates/solid/add-on/module-federation/package.json +0 -5
  225. package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +0 -22
  226. package/templates/solid/add-on/sentry/assets/_dot_env.local.append +0 -2
  227. package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +0 -20
  228. package/templates/solid/add-on/sentry/info.json +0 -13
  229. package/templates/solid/add-on/sentry/package.json +0 -5
  230. package/templates/solid/add-on/solid-ui/README.md +0 -9
  231. package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +0 -6
  232. package/templates/solid/add-on/solid-ui/assets/src/styles.css +0 -138
  233. package/templates/solid/add-on/solid-ui/assets/ui.config.json +0 -13
  234. package/templates/solid/add-on/solid-ui/info.json +0 -11
  235. package/templates/solid/add-on/solid-ui/package.json +0 -9
  236. package/templates/solid/add-on/start/assets/app.config.ts +0 -16
  237. package/templates/solid/add-on/start/assets/src/api.ts +0 -6
  238. package/templates/solid/add-on/start/assets/src/client.tsx +0 -7
  239. package/templates/solid/add-on/start/assets/src/router.tsx.ejs +0 -24
  240. package/templates/solid/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +0 -49
  241. package/templates/solid/add-on/start/assets/src/ssr.tsx +0 -12
  242. package/templates/solid/add-on/start/info.json +0 -14
  243. package/templates/solid/add-on/start/package.json +0 -12
  244. package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +0 -13
  245. package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +0 -77
  246. package/templates/solid/add-on/store/info.json +0 -13
  247. package/templates/solid/add-on/store/package.json +0 -6
  248. package/templates/solid/add-on/t3env/README.md +0 -16
  249. package/templates/solid/add-on/t3env/assets/src/env.ts +0 -39
  250. package/templates/solid/add-on/t3env/info.json +0 -10
  251. package/templates/solid/add-on/t3env/package.json +0 -6
  252. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +0 -5
  253. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +0 -15
  254. package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +0 -30
  255. package/templates/solid/add-on/tanstack-query/info.json +0 -13
  256. package/templates/solid/add-on/tanstack-query/package.json +0 -6
  257. package/templates/solid/base/README.md.ejs +0 -215
  258. package/templates/solid/base/_dot_cursorrules.append +0 -35
  259. package/templates/solid/base/_dot_gitignore +0 -5
  260. package/templates/solid/base/_dot_vscode/settings.biome.json +0 -38
  261. package/templates/solid/base/_dot_vscode/settings.json +0 -11
  262. package/templates/solid/base/index.html.ejs +0 -20
  263. package/templates/solid/base/package.biome.json +0 -10
  264. package/templates/solid/base/package.eslintprettier.json +0 -11
  265. package/templates/solid/base/package.json +0 -23
  266. package/templates/solid/base/package.ts.json +0 -5
  267. package/templates/solid/base/package.tw.json +0 -6
  268. package/templates/solid/base/public/favicon.ico +0 -0
  269. package/templates/solid/base/public/logo192.png +0 -0
  270. package/templates/solid/base/public/logo512.png +0 -0
  271. package/templates/solid/base/public/manifest.json +0 -25
  272. package/templates/solid/base/public/robots.txt +0 -3
  273. package/templates/solid/base/src/App.css +0 -0
  274. package/templates/solid/base/src/App.tsx.ejs +0 -47
  275. package/templates/solid/base/src/components/Header.tsx.ejs +0 -26
  276. package/templates/solid/base/src/logo.svg +0 -120
  277. package/templates/solid/base/src/styles.css.ejs +0 -15
  278. package/templates/solid/base/toolchain/.prettierignore +0 -3
  279. package/templates/solid/base/toolchain/biome.json +0 -31
  280. package/templates/solid/base/toolchain/eslint.config.js +0 -5
  281. package/templates/solid/base/toolchain/prettier.config.js +0 -10
  282. package/templates/solid/base/tsconfig.json.ejs +0 -31
  283. package/templates/solid/base/vite.config.js.ejs +0 -22
  284. package/templates/solid/code-router/src/main.tsx.ejs +0 -71
  285. package/templates/solid/example/tanchat/README.md +0 -52
  286. package/templates/solid/example/tanchat/assets/ai-streaming-server/README.md +0 -110
  287. package/templates/solid/example/tanchat/assets/ai-streaming-server/_dot_env.example +0 -1
  288. package/templates/solid/example/tanchat/assets/ai-streaming-server/package.json +0 -26
  289. package/templates/solid/example/tanchat/assets/ai-streaming-server/src/index.ts +0 -102
  290. package/templates/solid/example/tanchat/assets/ai-streaming-server/tsconfig.json +0 -15
  291. package/templates/solid/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +0 -149
  292. package/templates/solid/example/tanchat/assets/src/demo.index.css +0 -227
  293. package/templates/solid/example/tanchat/assets/src/lib/demo-store.ts +0 -13
  294. package/templates/solid/example/tanchat/assets/src/routes/example.chat.tsx +0 -435
  295. package/templates/solid/example/tanchat/assets/src/store/demo.hooks.ts +0 -17
  296. package/templates/solid/example/tanchat/assets/src/store/demo.store.ts +0 -133
  297. package/templates/solid/example/tanchat/info.json +0 -14
  298. package/templates/solid/example/tanchat/package.json +0 -7
  299. package/templates/solid/file-router/package.fr.json +0 -5
  300. package/templates/solid/file-router/src/main.tsx.ejs +0 -47
  301. package/templates/solid/file-router/src/routes/__root.tsx.ejs +0 -41
  302. package/templates/solid/file-router/src/routes/index.tsx +0 -43
  303. package/tests/cra.test.ts +0 -293
  304. package/tests/snapshots/cra/cr-js-npm.json +0 -33
  305. package/tests/snapshots/cra/cr-ts-npm.json +0 -34
  306. package/tests/snapshots/cra/cr-ts-start-npm.json +0 -38
  307. package/tests/snapshots/cra/fr-ts-npm.json +0 -34
  308. package/tests/snapshots/cra/fr-ts-tw-npm.json +0 -33
  309. package/tests/snapshots/cra/solid-cr-js-npm.json +0 -31
  310. package/tests/snapshots/cra/solid-cr-ts-npm.json +0 -32
  311. package/tests/snapshots/cra/solid-cr-ts-start-npm.json +0 -36
  312. package/tests/snapshots/cra/solid-fr-ts-npm.json +0 -33
  313. package/tests/snapshots/cra/solid-fr-ts-tw-npm.json +0 -32
  314. package/tests/test-utilities.ts +0 -87
package/src/types.ts CHANGED
@@ -1,61 +1,135 @@
1
1
  import type { CODE_ROUTER, FILE_ROUTER } from './constants.js'
2
2
  import type { PackageManager } from './package-manager.js'
3
- import type { ToolChain } from './toolchain.js'
4
-
5
- export type Framework = 'solid' | 'react'
6
3
 
7
4
  export type TemplateOptions = 'typescript' | 'javascript' | 'file-router'
8
5
 
9
6
  export type Mode = typeof CODE_ROUTER | typeof FILE_ROUTER
10
7
 
8
+ export type FileBundleHandler = {
9
+ getFiles: () => Promise<Array<string>>
10
+ getFileContents: (path: string) => Promise<string>
11
+ }
12
+
13
+ export type Integration = {
14
+ type: 'provider' | 'root-provider' | 'layout' | 'header-user'
15
+ path: string
16
+ jsName: string
17
+ }
18
+
19
+ export type AddOnDefinition = {
20
+ id: string
21
+ name: string
22
+ description: string
23
+ type: 'add-on' | 'example' | 'starter' | 'toolchain'
24
+ link: string
25
+ templates: Array<string>
26
+ routes?: Array<{
27
+ url: string
28
+ name: string
29
+ path: string
30
+ jsName: string
31
+ }>
32
+ packageAdditions: {
33
+ dependencies?: Record<string, string>
34
+ devDependencies?: Record<string, string>
35
+ scripts?: Record<string, string>
36
+ }
37
+ command?: {
38
+ command: string
39
+ args?: Array<string>
40
+ }
41
+ readme?: string
42
+ phase: 'setup' | 'add-on'
43
+ shadcnComponents?: Array<string>
44
+ warning?: string
45
+ dependsOn?: Array<string>
46
+ integrations?: Array<Integration>
47
+ variables?: Array<Variable>
48
+
49
+ files?: Record<string, string>
50
+ deletedFiles?: Array<string>
51
+ }
52
+
53
+ export type StarterDefinition = AddOnDefinition & {
54
+ type: 'starter'
55
+ version: string
56
+ author: string
57
+ link: string
58
+ license: string
59
+ mode: Mode
60
+ framework: string
61
+ typescript: boolean
62
+ tailwind: boolean
63
+ }
64
+
65
+ export type AddOn = AddOnDefinition & FileBundleHandler
66
+ export type Starter = StarterDefinition & FileBundleHandler
67
+
68
+ export type FrameworkDefinition = {
69
+ id: string
70
+ name: string
71
+ description: string
72
+ version: string
73
+
74
+ baseDirectory: string
75
+ addOnsDirectories: Array<string>
76
+ examplesDirectory: string
77
+ }
78
+
79
+ export type Framework = FrameworkDefinition &
80
+ FileBundleHandler & {
81
+ basePackageJSON: Record<string, any>
82
+ optionalPackages: Record<string, any>
83
+
84
+ getAddOns: () => Array<AddOn>
85
+ }
86
+
11
87
  export interface Options {
12
88
  framework: Framework
13
89
  projectName: string
14
90
  typescript: boolean
15
91
  tailwind: boolean
16
92
  packageManager: PackageManager
17
- toolchain: ToolChain
18
93
  mode: Mode
19
94
  addOns: boolean
20
95
  chosenAddOns: Array<AddOn>
21
96
  git: boolean
22
97
  variableValues: Record<string, string | number | boolean>
23
- overlay?: AddOn | undefined
24
- }
25
-
26
- export interface CliOptions {
27
- template?: TemplateOptions
28
- framework?: Framework
29
- tailwind?: boolean
30
- packageManager?: PackageManager
31
- toolchain?: ToolChain
32
- projectName?: string
33
- git?: boolean
34
- addOns?: Array<string> | boolean
35
- listAddOns?: boolean
36
- mcp?: boolean
37
- mcpSse?: boolean
38
- overlay?: string
39
- targetDir?: string
98
+ starter?: AddOn | undefined
40
99
  }
41
100
 
42
- export type Environment = {
101
+ type ProjectEnvironment = {
43
102
  startRun: () => void
44
103
  finishRun: () => void
45
104
  getErrors: () => Array<string>
105
+ }
46
106
 
107
+ type FileEnvironment = {
47
108
  appendFile: (path: string, contents: string) => Promise<void>
48
109
  copyFile: (from: string, to: string) => Promise<void>
49
110
  writeFile: (path: string, contents: string) => Promise<void>
50
111
  execute: (command: string, args: Array<string>, cwd: string) => Promise<void>
51
112
  deleteFile: (path: string) => Promise<void>
52
-
53
- readFile: (path: string, encoding?: BufferEncoding) => Promise<string>
54
113
  exists: (path: string) => boolean
55
- readdir: (path: string) => Array<string>
56
- isDirectory: (path: string) => boolean
57
114
  }
58
115
 
116
+ type UIEnvironment = {
117
+ intro: (message: string) => void
118
+ outro: (message: string) => void
119
+
120
+ info: (title?: string, message?: string) => void
121
+ error: (title?: string, message?: string) => void
122
+ warn: (title?: string, message?: string) => void
123
+
124
+ spinner: () => {
125
+ start: (message: string) => void
126
+ stop: (message: string) => void
127
+ }
128
+ confirm: (message: string) => Promise<boolean>
129
+ }
130
+
131
+ export type Environment = ProjectEnvironment & FileEnvironment & UIEnvironment
132
+
59
133
  type BooleanVariable = {
60
134
  name: string
61
135
  default: boolean
@@ -78,46 +152,3 @@ type StringVariable = {
78
152
  }
79
153
 
80
154
  export type Variable = BooleanVariable | NumberVariable | StringVariable
81
-
82
- export type AddOn = {
83
- id: string
84
- name: string
85
- description: string
86
- type: 'add-on' | 'example' | 'overlay'
87
- link: string
88
- templates: Array<string>
89
- routes: Array<{
90
- url: string
91
- name: string
92
- }>
93
- packageAdditions: {
94
- dependencies?: Record<string, string>
95
- devDependencies?: Record<string, string>
96
- scripts?: Record<string, string>
97
- }
98
- command?: {
99
- command: string
100
- args?: Array<string>
101
- }
102
- readme?: string
103
- phase: 'setup' | 'add-on'
104
- shadcnComponents?: Array<string>
105
- warning?: string
106
- dependsOn?: Array<string>
107
- variables?: Array<Variable>
108
-
109
- files?: Record<string, string>
110
- deletedFiles?: Array<string>
111
- }
112
-
113
- export type Overlay = AddOn & {
114
- type: 'overlay'
115
- version: string
116
- author: string
117
- link: string
118
- license: string
119
- mode: Mode
120
- framework: Framework
121
- typescript: boolean
122
- tailwind: boolean
123
- }
package/src/utils.ts CHANGED
@@ -8,3 +8,20 @@ export function sortObject(
8
8
  return acc
9
9
  }, {})
10
10
  }
11
+
12
+ export function jsSafeName(name: string) {
13
+ return name
14
+ .split(/[^a-zA-Z0-9]/)
15
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
16
+ .join('')
17
+ }
18
+
19
+ export function formatCommand({
20
+ command,
21
+ args,
22
+ }: {
23
+ command: string
24
+ args: Array<string>
25
+ }) {
26
+ return `${command} ${args.join(' ')}`.trim()
27
+ }
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { finalizeAddOns, getAllAddOns } from '../src/add-ons.js'
4
+
5
+ import type { AddOn, Framework } from '../src/types.js'
6
+
7
+ describe('getAllAddOns', () => {
8
+ it('filter add-ons', () => {
9
+ const addOns = getAllAddOns(
10
+ {
11
+ id: 'react-cra',
12
+ getAddOns: () => [
13
+ {
14
+ id: 'add-on-1',
15
+ description: 'Add-on 1',
16
+ templates: ['file-router'],
17
+ } as AddOn,
18
+ {
19
+ id: 'add-on-2',
20
+ description: 'Add-on 2',
21
+ templates: ['code-router'],
22
+ } as AddOn,
23
+ ],
24
+ } as Framework,
25
+ 'file-router',
26
+ )
27
+
28
+ expect(addOns.length).toEqual(1)
29
+ expect(addOns[0].id).toEqual('add-on-1')
30
+ })
31
+ })
32
+
33
+ describe('finalizeAddOns', () => {
34
+ it('should finalize add-ons', async () => {
35
+ const addOns = await finalizeAddOns(
36
+ {
37
+ id: 'react-cra',
38
+ getAddOns: () => [
39
+ {
40
+ id: 'add-on-1',
41
+ description: 'Add-on 1',
42
+ templates: ['file-router'],
43
+ dependsOn: ['add-on-2'],
44
+ } as AddOn,
45
+ {
46
+ id: 'add-on-2',
47
+ description: 'Add-on 2',
48
+ templates: ['file-router'],
49
+ } as AddOn,
50
+ {
51
+ id: 'add-on-3',
52
+ description: 'Add-on 3',
53
+ templates: ['file-router'],
54
+ } as AddOn,
55
+ ],
56
+ } as Framework,
57
+ 'file-router',
58
+ ['add-on-1'],
59
+ )
60
+
61
+ expect(addOns.length).toEqual(2)
62
+ const addOnIds = addOns.map((a) => a.id)
63
+ expect(addOnIds.includes('add-on-1')).toEqual(true)
64
+ expect(addOnIds.includes('add-on-2')).toEqual(true)
65
+ expect(addOnIds.includes('add-on-3')).toEqual(false)
66
+ })
67
+ })
@@ -0,0 +1,64 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { fs, vol } from 'memfs'
3
+ import { resolve } from 'node:path'
4
+
5
+ import { readConfigFile, writeConfigFile } from '../src/config-file.js'
6
+ import { CONFIG_FILE } from '../src/constants.js'
7
+
8
+ import type { AddOn, Environment, Framework, Options } from '../src/types.js'
9
+
10
+ vi.mock('node:fs', () => fs)
11
+ vi.mock('node:fs/promises', () => fs.promises)
12
+
13
+ beforeEach(() => {
14
+ vol.reset()
15
+ })
16
+
17
+ describe('writeConfigFile', () => {
18
+ it('should write the config file', async () => {
19
+ const options = {
20
+ framework: {
21
+ id: 'react-cra',
22
+ getAddOns: () => [],
23
+ } as unknown as Framework,
24
+ chosenAddOns: [
25
+ {
26
+ id: 'add-on-1',
27
+ description: 'Add-on 1',
28
+ templates: ['file-router'],
29
+ } as AddOn,
30
+ ],
31
+ addOns: [],
32
+ } as unknown as Options
33
+ const targetDir = 'test-dir'
34
+ const persistedOptions = {
35
+ version: 1,
36
+ framework: options.framework.id,
37
+ existingAddOns: options.chosenAddOns.map((addOn) => addOn.id),
38
+ }
39
+ const env = {
40
+ writeFile: (path, optionsString) => {
41
+ expect(path).toEqual(resolve(targetDir, CONFIG_FILE))
42
+ expect(optionsString).toEqual(JSON.stringify(persistedOptions, null, 2))
43
+ },
44
+ } as Environment
45
+ await writeConfigFile(env, targetDir, options)
46
+ })
47
+ })
48
+
49
+ describe('readConfigFile', () => {
50
+ it('should read the config file', async () => {
51
+ const persistedOptions = {
52
+ version: 1,
53
+ framework: 'react-cra',
54
+ existingAddOns: ['add-on-1'],
55
+ }
56
+ vol.mkdirSync('/test')
57
+ vol.writeFileSync(
58
+ resolve('/test', CONFIG_FILE),
59
+ JSON.stringify(persistedOptions, null, 2),
60
+ )
61
+ const config = await readConfigFile('/test')
62
+ expect(config).toEqual(persistedOptions)
63
+ })
64
+ })
@@ -0,0 +1,144 @@
1
+ import { resolve } from 'node:path'
2
+
3
+ import { describe, expect, it } from 'vitest'
4
+
5
+ import { createApp } from '../src/create-app.js'
6
+
7
+ import { createMemoryEnvironment } from '../src/environment.js'
8
+ import { FILE_ROUTER } from '../src/constants.js'
9
+ import { AddOn, Options } from '../src/types.js'
10
+
11
+ const simpleOptions = {
12
+ projectName: 'test',
13
+ framework: {
14
+ id: 'test',
15
+ name: 'Test',
16
+ basePackageJSON: {
17
+ scripts: {
18
+ dev: 'react-scripts start',
19
+ },
20
+ },
21
+ optionalPackages: {
22
+ typescript: {
23
+ devDependencies: {
24
+ typescript: '^5.0.0',
25
+ },
26
+ },
27
+ tailwindcss: {
28
+ dependencies: {
29
+ tailwindcss: '^3.0.0',
30
+ },
31
+ },
32
+ 'file-router': {
33
+ dependencies: {
34
+ 'file-router': '^1.0.0',
35
+ },
36
+ },
37
+ },
38
+ getFiles: () => ['./src/test.txt'],
39
+ getFileContents: () => 'Hello',
40
+ },
41
+ chosenAddOns: [],
42
+ packageManager: 'pnpm',
43
+ typescript: true,
44
+ tailwind: true,
45
+ mode: FILE_ROUTER,
46
+ variableValues: {},
47
+ } as unknown as Options
48
+
49
+ describe('createApp', () => {
50
+ it('should create an app', async () => {
51
+ const { environment, output } = createMemoryEnvironment()
52
+ await createApp(simpleOptions, {
53
+ silent: true,
54
+ environment,
55
+ name: 'Test',
56
+ cwd: '/',
57
+ appName: 'TanStack App',
58
+ })
59
+
60
+ expect(output.files['/src/test.txt']).toEqual('Hello')
61
+ })
62
+
63
+ it('should create an app - not silent', async () => {
64
+ const { environment, output } = createMemoryEnvironment()
65
+ await createApp(simpleOptions, {
66
+ silent: false,
67
+ environment,
68
+ name: 'Test',
69
+ cwd: '/foo/bar/baz',
70
+ appName: 'TanStack App',
71
+ })
72
+
73
+ const cwd = process.cwd()
74
+
75
+ expect(output.files[resolve(cwd, '/foo/bar/baz/src/test.txt')]).toEqual(
76
+ 'Hello',
77
+ )
78
+ })
79
+
80
+ it('should create an app - with a starter', async () => {
81
+ const { environment, output } = createMemoryEnvironment()
82
+ await createApp(
83
+ {
84
+ ...simpleOptions,
85
+ starter: {
86
+ command: {
87
+ command: 'echo',
88
+ args: ['Hello'],
89
+ },
90
+ getFiles: () => ['./src/test2.txt'],
91
+ getFileContents: () => 'Hello-2',
92
+ } as unknown as AddOn,
93
+ },
94
+ {
95
+ silent: false,
96
+ environment,
97
+ name: 'Test',
98
+ cwd: '/',
99
+ appName: 'TanStack App',
100
+ },
101
+ )
102
+
103
+ expect(output.files['/src/test2.txt']).toEqual('Hello-2')
104
+ expect(output.commands.some(({ command }) => command === 'echo')).toBe(true)
105
+ })
106
+
107
+ it('should create an app - with a add-on', async () => {
108
+ const { environment, output } = createMemoryEnvironment()
109
+ await createApp(
110
+ {
111
+ ...simpleOptions,
112
+ git: true,
113
+ addOns: true,
114
+ chosenAddOns: [
115
+ {
116
+ type: 'add-on',
117
+ phase: 'add-on',
118
+ warning: 'This is a warning',
119
+ command: {
120
+ command: 'echo',
121
+ args: ['Hello'],
122
+ },
123
+ packageAdditions: {
124
+ dependencies: {},
125
+ devDependencies: {},
126
+ },
127
+ getFiles: () => ['./src/test2.txt', './public/foo.jpg'],
128
+ getFileContents: () => 'base64::aGVsbG8=',
129
+ } as unknown as AddOn,
130
+ ],
131
+ },
132
+ {
133
+ silent: false,
134
+ environment,
135
+ name: 'Test',
136
+ cwd: '/',
137
+ appName: 'TanStack App',
138
+ },
139
+ )
140
+
141
+ expect(output.files['/src/test2.txt']).toEqual('hello')
142
+ expect(output.commands.some(({ command }) => command === 'echo')).toBe(true)
143
+ })
144
+ })
@@ -0,0 +1,96 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { fs, vol } from 'memfs'
3
+
4
+ vi.mock('node:fs', () => fs)
5
+ vi.mock('node:fs/promises', () => fs.promises)
6
+
7
+ beforeEach(() => {
8
+ vol.reset()
9
+ })
10
+
11
+ import {
12
+ createMemoryEnvironment,
13
+ createDefaultEnvironment,
14
+ } from '../src/environment.js'
15
+
16
+ describe('createMemoryEnvironment', () => {
17
+ it('should handle basic file operations', async () => {
18
+ const { environment, output } = createMemoryEnvironment()
19
+
20
+ environment.startRun()
21
+
22
+ await environment.writeFile('/test.txt', 'test')
23
+
24
+ environment.deleteFile('/test.txt')
25
+ expect(environment.exists('/test.txt')).toBe(false)
26
+
27
+ await environment.writeFile('/test.txt', 'test')
28
+
29
+ environment.finishRun()
30
+
31
+ expect(output.files['/test.txt']).toEqual('test')
32
+ })
33
+
34
+ it('should track command execution', async () => {
35
+ const { environment, output } = createMemoryEnvironment()
36
+
37
+ environment.startRun()
38
+ await environment.execute('echo', ['test'], '')
39
+ environment.finishRun()
40
+
41
+ expect(output.commands.length).toEqual(1)
42
+ expect(output.commands[0].command).toEqual('echo')
43
+ expect(output.commands[0].args).toEqual(['test'])
44
+ })
45
+ })
46
+
47
+ describe('createDefaultEnvironment', () => {
48
+ it('should create a default environment', async () => {
49
+ const environment = createDefaultEnvironment()
50
+ expect(environment).toBeDefined()
51
+ })
52
+
53
+ it('should write to the file system', async () => {
54
+ const environment = createDefaultEnvironment()
55
+ environment.startRun()
56
+ await environment.writeFile('/test.txt', 'test')
57
+ await environment.appendFile('/test.txt', 'test2')
58
+ await environment.copyFile('/test.txt', '/test2.txt')
59
+ environment.finishRun()
60
+
61
+ expect(fs.readFileSync('/test.txt', 'utf8')).toEqual('testtest2')
62
+ })
63
+
64
+ it('should allow deletes', async () => {
65
+ const environment = createDefaultEnvironment()
66
+ await environment.writeFile('/test.txt', 'test')
67
+ expect(fs.readFileSync('/test.txt', 'utf8')).toEqual('test')
68
+ await environment.deleteFile('/test.txt')
69
+ expect(environment.exists('/test.txt')).toBe(false)
70
+ })
71
+
72
+ it('should record errors', async () => {
73
+ const environment = createDefaultEnvironment()
74
+ environment.startRun()
75
+ await environment.execute('command-that-does-not-exist', ['test'], '')
76
+ environment.finishRun()
77
+ expect(environment.getErrors()).toEqual([
78
+ 'Command "command-that-does-not-exist test" did not run successfully. Please run this manually in your project.',
79
+ ])
80
+ })
81
+
82
+ it('should have UI methods', async () => {
83
+ const environment = createDefaultEnvironment()
84
+ environment.startRun()
85
+ environment.intro('test')
86
+ environment.outro('test')
87
+ environment.info('test')
88
+ environment.error('test')
89
+ environment.warn('test')
90
+ const s = environment.spinner()
91
+ s.start('foo')
92
+ s.stop('bar')
93
+ environment.finishRun()
94
+ expect(await environment.confirm('test')).toEqual(true)
95
+ })
96
+ })
@@ -0,0 +1,90 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { fs, vol } from 'memfs'
3
+
4
+ import {
5
+ findFilesRecursively,
6
+ isDirectory,
7
+ readFileHelper,
8
+ relativePath,
9
+ } from '../src/file-helpers.js'
10
+
11
+ vi.mock('node:fs', () => fs)
12
+ vi.mock('node:fs/promises', () => fs.promises)
13
+
14
+ beforeEach(() => {
15
+ vol.reset()
16
+ })
17
+
18
+ describe('relativePath', () => {
19
+ it('relative path with the same directory', () => {
20
+ expect(relativePath('src/utils.ts', 'src/index.ts')).toBe('./index.ts')
21
+ })
22
+ it('relative from a subdirectory', () => {
23
+ expect(relativePath('src/something/utils.ts', 'src/index.ts')).toBe(
24
+ '../index.ts',
25
+ )
26
+ })
27
+ it('relative to a subdirectory', () => {
28
+ expect(relativePath('src/utils.ts', 'src/something/index.ts')).toBe(
29
+ './something/index.ts',
30
+ )
31
+ })
32
+ it('same deep directory', () => {
33
+ expect(
34
+ relativePath('src/foo/bar/baz/utils.ts', 'src/foo/bar/baz/index.ts'),
35
+ ).toBe('./index.ts')
36
+ })
37
+ it('up several levels and down several levels', () => {
38
+ expect(relativePath('src/bar/baz/utils.ts', 'src/foo/bar/index.ts')).toBe(
39
+ '../../foo/bar/index.ts',
40
+ )
41
+ })
42
+ it('relative path with a different directory', () => {
43
+ expect(
44
+ relativePath(
45
+ './src/routes/__root.tsx.ejs',
46
+ 'src/integrations/tanstack-query/layout.tsx',
47
+ ),
48
+ ).toBe('../integrations/tanstack-query/layout.tsx')
49
+ })
50
+ })
51
+
52
+ describe('readFileHelper', () => {
53
+ it('should read a file', async () => {
54
+ vol.mkdirSync('/src')
55
+ fs.writeFileSync('/src/test.txt', 'Hello')
56
+ expect(readFileHelper('/src/test.txt')).toBe('Hello')
57
+ })
58
+ it('should read a binary file', async () => {
59
+ vol.mkdirSync('/src')
60
+ fs.writeFileSync('/src/test.jpg', 'Hello')
61
+ expect(readFileHelper('/src/test.jpg')).toBe('base64::SGVsbG8=')
62
+ })
63
+ })
64
+
65
+ describe('isDirectory', () => {
66
+ it('should check on files and directories', () => {
67
+ vol.mkdirSync('/src')
68
+ fs.writeFileSync('/src/test.txt', 'Hello')
69
+ expect(isDirectory('/src/test.txt')).toBe(false)
70
+ expect(isDirectory('/src')).toBe(true)
71
+ })
72
+ })
73
+
74
+ describe('findFilesRecursively', () => {
75
+ it('find files recursively', () => {
76
+ vol.mkdirSync('/src/subdir/subdir2', { recursive: true })
77
+ fs.writeFileSync('/src/test.txt', 'Hello')
78
+ fs.writeFileSync('/src/subdir/test.txt', 'Hello')
79
+ fs.writeFileSync('/src/subdir/subdir2/test.txt', 'Hello')
80
+
81
+ const files = {}
82
+ findFilesRecursively('/src', files)
83
+
84
+ expect(Object.keys(files)).toEqual([
85
+ '/src/subdir/subdir2/test.txt',
86
+ '/src/subdir/test.txt',
87
+ '/src/test.txt',
88
+ ])
89
+ })
90
+ })