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

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 (302) hide show
  1. package/dist/add-ons.js +73 -8
  2. package/dist/{add-to-app.js → add.js} +19 -19
  3. package/dist/cli.js +125 -0
  4. package/dist/config-file.js +3 -6
  5. package/dist/constants.js +2 -0
  6. package/dist/create-app.js +460 -76
  7. package/dist/custom-add-on.js +254 -0
  8. package/dist/environment.js +26 -11
  9. package/dist/file-helper.js +18 -0
  10. package/dist/index.js +1 -10
  11. package/dist/mcp.js +229 -0
  12. package/dist/options.js +330 -0
  13. package/dist/package-manager.js +6 -51
  14. package/dist/templates.js +6 -0
  15. package/dist/toolchain.js +6 -0
  16. package/dist/types/add-ons.d.ts +6 -2
  17. package/dist/types/add.d.ts +3 -0
  18. package/dist/types/cli.d.ts +7 -0
  19. package/dist/types/config-file.d.ts +2 -3
  20. package/dist/types/constants.d.ts +3 -0
  21. package/dist/types/custom-add-on.d.ts +3 -0
  22. package/dist/types/file-helper.d.ts +2 -0
  23. package/dist/types/index.d.ts +1 -13
  24. package/dist/types/mcp.d.ts +7 -0
  25. package/dist/types/options.d.ts +6 -0
  26. package/dist/types/package-manager.d.ts +1 -14
  27. package/dist/types/templates.d.ts +1 -0
  28. package/dist/types/toolchain.d.ts +3 -0
  29. package/dist/types/types.d.ts +63 -84
  30. package/dist/types/utils.d.ts +0 -5
  31. package/dist/utils.js +0 -9
  32. package/package.json +13 -3
  33. package/src/add-ons.ts +104 -11
  34. package/src/{add-to-app.ts → add.ts} +35 -29
  35. package/src/cli.ts +196 -0
  36. package/src/config-file.ts +6 -12
  37. package/src/constants.ts +5 -0
  38. package/src/create-app.ts +727 -126
  39. package/src/custom-add-on.ts +325 -0
  40. package/src/environment.ts +27 -12
  41. package/src/file-helper.ts +20 -0
  42. package/src/index.ts +1 -42
  43. package/src/mcp.ts +302 -0
  44. package/src/options.ts +410 -0
  45. package/src/package-manager.ts +9 -80
  46. package/src/templates.ts +7 -0
  47. package/src/toolchain.ts +7 -0
  48. package/src/types.ts +69 -98
  49. package/src/utils.ts +0 -17
  50. package/templates/react/add-on/clerk/README.md +3 -0
  51. package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
  52. package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
  53. package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
  54. package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
  55. package/templates/react/add-on/clerk/info.json +13 -0
  56. package/templates/react/add-on/clerk/package.json +5 -0
  57. package/templates/react/add-on/convex/README.md +4 -0
  58. package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
  59. package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
  60. package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
  61. package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
  62. package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
  63. package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
  64. package/templates/react/add-on/convex/info.json +13 -0
  65. package/templates/react/add-on/convex/package.json +6 -0
  66. package/templates/react/add-on/form/assets/src/components/demo.FormComponents.tsx.ejs +300 -0
  67. package/templates/react/add-on/form/assets/src/hooks/demo.form-context.ts +4 -0
  68. package/templates/react/add-on/form/assets/src/hooks/demo.form.ts +22 -0
  69. package/templates/react/add-on/form/assets/src/routes/demo.form.address.tsx.ejs +213 -0
  70. package/templates/react/add-on/form/assets/src/routes/demo.form.simple.tsx.ejs +77 -0
  71. package/templates/react/add-on/form/info.json +26 -0
  72. package/templates/react/add-on/form/package.json +6 -0
  73. package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
  74. package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  75. package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
  76. package/templates/react/add-on/module-federation/info.json +7 -0
  77. package/templates/react/add-on/module-federation/package.json +5 -0
  78. package/templates/react/add-on/netlify/README.md +11 -0
  79. package/templates/react/add-on/netlify/info.json +7 -0
  80. package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  81. package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
  82. package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
  83. package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
  84. package/templates/react/add-on/sentry/info.json +14 -0
  85. package/templates/react/add-on/sentry/package.json +7 -0
  86. package/templates/react/add-on/shadcn/README.md +7 -0
  87. package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
  88. package/templates/react/add-on/shadcn/assets/components.json +21 -0
  89. package/templates/react/add-on/shadcn/assets/src/lib/utils.ts +6 -0
  90. package/templates/react/add-on/shadcn/assets/src/styles.css +138 -0
  91. package/templates/react/add-on/shadcn/info.json +7 -0
  92. package/templates/react/add-on/shadcn/package.json +9 -0
  93. package/templates/react/add-on/start/assets/_dot_gitignore.append +2 -0
  94. package/templates/react/add-on/start/assets/app.config.ts.ejs +19 -0
  95. package/templates/react/add-on/start/assets/src/api.ts +6 -0
  96. package/templates/react/add-on/start/assets/src/client.tsx +8 -0
  97. package/templates/react/add-on/start/assets/src/router.tsx.ejs +77 -0
  98. package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
  99. package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
  100. package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +50 -0
  101. package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
  102. package/templates/react/add-on/start/info.json +18 -0
  103. package/templates/react/add-on/start/package.json +13 -0
  104. package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
  105. package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
  106. package/templates/react/add-on/store/info.json +13 -0
  107. package/templates/react/add-on/store/package.json +6 -0
  108. package/templates/react/add-on/t3env/README.md +16 -0
  109. package/templates/react/add-on/t3env/assets/src/env.ts +39 -0
  110. package/templates/react/add-on/t3env/info.json +10 -0
  111. package/templates/react/add-on/t3env/package.json +6 -0
  112. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/init.ts +9 -0
  113. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/react.ts +4 -0
  114. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/router.ts +18 -0
  115. package/templates/react/add-on/tRPC/assets/src/routes/api.trpc.$.tsx +16 -0
  116. package/templates/react/add-on/tRPC/info.json +9 -0
  117. package/templates/react/add-on/tRPC/package.json +9 -0
  118. package/templates/react/add-on/table/assets/src/data/demo-table-data.ts +50 -0
  119. package/templates/react/add-on/table/assets/src/routes/demo.table.tsx.ejs +373 -0
  120. package/templates/react/add-on/table/info.json +13 -0
  121. package/templates/react/add-on/table/package.json +7 -0
  122. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
  123. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/root-provider.tsx.ejs +70 -0
  124. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +53 -0
  125. package/templates/react/add-on/tanstack-query/info.json +13 -0
  126. package/templates/react/add-on/tanstack-query/package.json +6 -0
  127. package/templates/react/base/README.md.ejs +558 -0
  128. package/templates/react/base/_dot_gitignore +5 -0
  129. package/templates/react/base/_dot_vscode/settings.biome.json +38 -0
  130. package/templates/react/base/_dot_vscode/settings.json +11 -0
  131. package/templates/react/base/index.html.ejs +20 -0
  132. package/templates/react/base/package.biome.json +10 -0
  133. package/templates/react/base/package.eslintprettier.json +11 -0
  134. package/templates/react/base/package.json +30 -0
  135. package/templates/react/base/package.ts.json +7 -0
  136. package/templates/react/base/package.tw.json +6 -0
  137. package/templates/react/base/public/favicon.ico +0 -0
  138. package/templates/react/base/public/logo192.png +0 -0
  139. package/templates/react/base/public/logo512.png +0 -0
  140. package/templates/react/base/public/manifest.json +25 -0
  141. package/templates/react/base/public/robots.txt +3 -0
  142. package/templates/react/base/src/App.css +38 -0
  143. package/templates/react/base/src/App.test.tsx.ejs +10 -0
  144. package/templates/react/base/src/App.tsx.ejs +74 -0
  145. package/templates/react/base/src/components/Header.tsx.ejs +27 -0
  146. package/templates/react/base/src/logo.svg +44 -0
  147. package/templates/react/base/src/reportWebVitals.ts.ejs +28 -0
  148. package/templates/react/base/src/styles.css.ejs +15 -0
  149. package/templates/react/base/toolchain/.prettierignore +3 -0
  150. package/templates/react/base/toolchain/biome.json +31 -0
  151. package/templates/react/base/toolchain/eslint.config.js +5 -0
  152. package/templates/react/base/toolchain/prettier.config.js +10 -0
  153. package/templates/react/base/tsconfig.json.ejs +29 -0
  154. package/templates/react/base/vite.config.js.ejs +23 -0
  155. package/templates/react/code-router/src/main.tsx.ejs +92 -0
  156. package/templates/react/example/tanchat/README.md +37 -0
  157. package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
  158. package/templates/react/example/tanchat/assets/public/example-guitar-flowers.jpg +0 -0
  159. package/templates/react/example/tanchat/assets/public/example-guitar-motherboard.jpg +0 -0
  160. package/templates/react/example/tanchat/assets/public/example-guitar-racing.jpg +0 -0
  161. package/templates/react/example/tanchat/assets/public/example-guitar-steamer-trunk.jpg +0 -0
  162. package/templates/react/example/tanchat/assets/public/example-guitar-superhero.jpg +0 -0
  163. package/templates/react/example/tanchat/assets/public/example-guitar-traveling.jpg +0 -0
  164. package/templates/react/example/tanchat/assets/public/example-guitar-video-games.jpg +0 -0
  165. package/templates/react/example/tanchat/assets/src/components/example-AIAssistant.tsx +173 -0
  166. package/templates/react/example/tanchat/assets/src/components/example-GuitarRecommendation.tsx +47 -0
  167. package/templates/react/example/tanchat/assets/src/data/example-guitars.ts +83 -0
  168. package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
  169. package/templates/react/example/tanchat/assets/src/integrations/tanchat/header-user.tsx +5 -0
  170. package/templates/react/example/tanchat/assets/src/routes/api.messages.ts +24 -0
  171. package/templates/react/example/tanchat/assets/src/routes/api.sse.ts +23 -0
  172. package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx +159 -0
  173. package/templates/react/example/tanchat/assets/src/routes/example.guitars/$guitarId.tsx +50 -0
  174. package/templates/react/example/tanchat/assets/src/routes/example.guitars/index.tsx +54 -0
  175. package/templates/react/example/tanchat/assets/src/store/example-assistant.ts +3 -0
  176. package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +62 -0
  177. package/templates/react/example/tanchat/assets/src/utils/demo.sse.ts +31 -0
  178. package/templates/react/example/tanchat/assets/src/utils/demo.tools.ts +47 -0
  179. package/templates/react/example/tanchat/info.json +19 -0
  180. package/templates/react/example/tanchat/package.json +16 -0
  181. package/templates/react/file-router/package.fr.json +5 -0
  182. package/templates/react/file-router/src/main.tsx.ejs +55 -0
  183. package/templates/react/file-router/src/routes/__root.tsx.ejs +82 -0
  184. package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +352 -0
  185. package/templates/solid/add-on/form/info.json +13 -0
  186. package/templates/solid/add-on/form/package.json +5 -0
  187. package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
  188. package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  189. package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
  190. package/templates/solid/add-on/module-federation/info.json +7 -0
  191. package/templates/solid/add-on/module-federation/package.json +5 -0
  192. package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  193. package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
  194. package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
  195. package/templates/solid/add-on/sentry/info.json +13 -0
  196. package/templates/solid/add-on/sentry/package.json +5 -0
  197. package/templates/solid/add-on/solid-ui/README.md +9 -0
  198. package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
  199. package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
  200. package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
  201. package/templates/solid/add-on/solid-ui/info.json +11 -0
  202. package/templates/solid/add-on/solid-ui/package.json +9 -0
  203. package/templates/solid/add-on/start/assets/app.config.ts +16 -0
  204. package/templates/solid/add-on/start/assets/src/api.ts +6 -0
  205. package/templates/solid/add-on/start/assets/src/client.tsx +7 -0
  206. package/templates/solid/add-on/start/assets/src/router.tsx.ejs +24 -0
  207. package/templates/solid/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
  208. package/templates/solid/add-on/start/assets/src/ssr.tsx +12 -0
  209. package/templates/solid/add-on/start/info.json +14 -0
  210. package/templates/solid/add-on/start/package.json +12 -0
  211. package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
  212. package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
  213. package/templates/solid/add-on/store/info.json +13 -0
  214. package/templates/solid/add-on/store/package.json +6 -0
  215. package/templates/solid/add-on/t3env/README.md +16 -0
  216. package/templates/solid/add-on/t3env/assets/src/env.ts +39 -0
  217. package/templates/solid/add-on/t3env/info.json +10 -0
  218. package/templates/solid/add-on/t3env/package.json +6 -0
  219. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
  220. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
  221. package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
  222. package/templates/solid/add-on/tanstack-query/info.json +13 -0
  223. package/templates/solid/add-on/tanstack-query/package.json +6 -0
  224. package/templates/solid/base/README.md.ejs +215 -0
  225. package/templates/solid/base/_dot_cursorrules.append +35 -0
  226. package/templates/solid/base/_dot_gitignore +5 -0
  227. package/templates/solid/base/_dot_vscode/settings.biome.json +38 -0
  228. package/templates/solid/base/_dot_vscode/settings.json +11 -0
  229. package/templates/solid/base/index.html.ejs +20 -0
  230. package/templates/solid/base/package.biome.json +10 -0
  231. package/templates/solid/base/package.eslintprettier.json +11 -0
  232. package/templates/solid/base/package.json +23 -0
  233. package/templates/solid/base/package.ts.json +5 -0
  234. package/templates/solid/base/package.tw.json +6 -0
  235. package/templates/solid/base/public/favicon.ico +0 -0
  236. package/templates/solid/base/public/logo192.png +0 -0
  237. package/templates/solid/base/public/logo512.png +0 -0
  238. package/templates/solid/base/public/manifest.json +25 -0
  239. package/templates/solid/base/public/robots.txt +3 -0
  240. package/templates/solid/base/src/App.css +0 -0
  241. package/templates/solid/base/src/App.tsx.ejs +47 -0
  242. package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
  243. package/templates/solid/base/src/logo.svg +120 -0
  244. package/templates/solid/base/src/styles.css.ejs +15 -0
  245. package/templates/solid/base/toolchain/.prettierignore +3 -0
  246. package/templates/solid/base/toolchain/biome.json +31 -0
  247. package/templates/solid/base/toolchain/eslint.config.js +5 -0
  248. package/templates/solid/base/toolchain/prettier.config.js +10 -0
  249. package/templates/solid/base/tsconfig.json.ejs +31 -0
  250. package/templates/solid/base/vite.config.js.ejs +22 -0
  251. package/templates/solid/code-router/src/main.tsx.ejs +71 -0
  252. package/templates/solid/example/tanchat/README.md +52 -0
  253. package/templates/solid/example/tanchat/assets/ai-streaming-server/README.md +110 -0
  254. package/templates/solid/example/tanchat/assets/ai-streaming-server/_dot_env.example +1 -0
  255. package/templates/solid/example/tanchat/assets/ai-streaming-server/package.json +26 -0
  256. package/templates/solid/example/tanchat/assets/ai-streaming-server/src/index.ts +102 -0
  257. package/templates/solid/example/tanchat/assets/ai-streaming-server/tsconfig.json +15 -0
  258. package/templates/solid/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +149 -0
  259. package/templates/solid/example/tanchat/assets/src/demo.index.css +227 -0
  260. package/templates/solid/example/tanchat/assets/src/lib/demo-store.ts +13 -0
  261. package/templates/solid/example/tanchat/assets/src/routes/example.chat.tsx +435 -0
  262. package/templates/solid/example/tanchat/assets/src/store/demo.hooks.ts +17 -0
  263. package/templates/solid/example/tanchat/assets/src/store/demo.store.ts +133 -0
  264. package/templates/solid/example/tanchat/info.json +14 -0
  265. package/templates/solid/example/tanchat/package.json +7 -0
  266. package/templates/solid/file-router/package.fr.json +5 -0
  267. package/templates/solid/file-router/src/main.tsx.ejs +47 -0
  268. package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
  269. package/templates/solid/file-router/src/routes/index.tsx +43 -0
  270. package/tests/cra.test.ts +293 -0
  271. package/tests/snapshots/cra/cr-js-npm.json +33 -0
  272. package/tests/snapshots/cra/cr-ts-npm.json +34 -0
  273. package/tests/snapshots/cra/cr-ts-start-npm.json +38 -0
  274. package/tests/snapshots/cra/fr-ts-npm.json +34 -0
  275. package/tests/snapshots/cra/fr-ts-tw-npm.json +33 -0
  276. package/tests/snapshots/cra/solid-cr-js-npm.json +31 -0
  277. package/tests/snapshots/cra/solid-cr-ts-npm.json +32 -0
  278. package/tests/snapshots/cra/solid-cr-ts-start-npm.json +36 -0
  279. package/tests/snapshots/cra/solid-fr-ts-npm.json +33 -0
  280. package/tests/snapshots/cra/solid-fr-ts-tw-npm.json +32 -0
  281. package/tests/test-utilities.ts +87 -0
  282. package/dist/file-helpers.js +0 -59
  283. package/dist/frameworks.js +0 -78
  284. package/dist/integrations/git.js +0 -4
  285. package/dist/integrations/shadcn.js +0 -27
  286. package/dist/package-json.js +0 -46
  287. package/dist/template-file.js +0 -108
  288. package/dist/types/add-to-app.d.ts +0 -4
  289. package/dist/types/file-helpers.d.ts +0 -5
  290. package/dist/types/frameworks.d.ts +0 -5
  291. package/dist/types/integrations/git.d.ts +0 -2
  292. package/dist/types/integrations/shadcn.d.ts +0 -2
  293. package/dist/types/package-json.d.ts +0 -2
  294. package/dist/types/template-file.d.ts +0 -2
  295. package/src/file-helpers.ts +0 -73
  296. package/src/frameworks.ts +0 -114
  297. package/src/integrations/git.ts +0 -7
  298. package/src/integrations/shadcn.ts +0 -44
  299. package/src/package-json.ts +0 -62
  300. package/src/template-file.ts +0 -146
  301. package/tests/file-helper.test.ts +0 -37
  302. package/tests/package-manager.test.ts +0 -154
@@ -1,113 +1,497 @@
1
- import { basename, resolve } from 'node:path';
2
- import { getBinaryFile } from './file-helpers.js';
3
- import { formatCommand } from './utils.js';
1
+ import { basename, dirname, resolve } from 'node:path';
2
+ import { log, outro, spinner } from '@clack/prompts';
3
+ import { render } from 'ejs';
4
+ import { format } from 'prettier';
5
+ import chalk from 'chalk';
6
+ import { getTemplatesRoot } from './templates.js';
7
+ import { CODE_ROUTER, FILE_ROUTER } from './constants.js';
8
+ import { sortObject } from './utils.js';
4
9
  import { writeConfigFile } from './config-file.js';
5
- import { getPackageManagerScriptCommand, packageManagerInstall, } from './package-manager.js';
6
- import { createPackageJSON } from './package-json.js';
7
- import { createTemplateFile } from './template-file.js';
8
- import { installShadcnComponents } from './integrations/shadcn.js';
9
- import { setupGit } from './integrations/git.js';
10
- async function writeFiles(environment, targetDir, options) {
11
- const templateFileFromContent = createTemplateFile(environment, options, targetDir);
12
- async function writeFileBundle(bundle) {
13
- const files = await bundle.getFiles();
10
+ import { packageManagerExecute } from './package-manager.js';
11
+ import { getBinaryFile } from './file-helper.js';
12
+ function createCopyFiles(environment, targetDir) {
13
+ return async function copyFiles(templateDir, files,
14
+ // optionally copy files from a folder to the root
15
+ toRoot) {
14
16
  for (const file of files) {
15
- const contents = await bundle.getFileContents(file);
16
- const binaryFile = getBinaryFile(contents);
17
- if (binaryFile) {
18
- await environment.writeFile(resolve(targetDir, file), binaryFile);
17
+ let targetFileName = file.replace('.tw', '');
18
+ if (toRoot) {
19
+ const fileNoPath = targetFileName.split('/').pop();
20
+ targetFileName = fileNoPath ? `./${fileNoPath}` : targetFileName;
19
21
  }
20
- else {
21
- await templateFileFromContent(file, contents);
22
+ await environment.copyFile(resolve(templateDir, file), resolve(targetDir, targetFileName));
23
+ }
24
+ };
25
+ }
26
+ function jsSafeName(name) {
27
+ return name
28
+ .split(/[^a-zA-Z0-9]/)
29
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
30
+ .join('');
31
+ }
32
+ function createTemplateFile(environment, projectName, options, targetDir) {
33
+ return async function templateFile(file, content, targetFileName, extraTemplateValues) {
34
+ function getPackageManagerAddScript(packageName, isDev = false) {
35
+ let command;
36
+ switch (options.packageManager) {
37
+ case 'yarn':
38
+ case 'pnpm':
39
+ command = isDev
40
+ ? `${options.packageManager} add ${packageName} --dev`
41
+ : `${options.packageManager} add ${packageName}`;
42
+ break;
43
+ default:
44
+ command = isDev
45
+ ? `${options.packageManager} install ${packageName} -D`
46
+ : `${options.packageManager} install ${packageName}`;
47
+ break;
22
48
  }
49
+ return command;
50
+ }
51
+ function getPackageManagerRunScript(scriptName) {
52
+ let command;
53
+ switch (options.packageManager) {
54
+ case 'yarn':
55
+ case 'pnpm':
56
+ command = `${options.packageManager} ${scriptName}`;
57
+ break;
58
+ case 'deno':
59
+ command = `${options.packageManager} task ${scriptName}`;
60
+ break;
61
+ default:
62
+ command = `${options.packageManager} run ${scriptName}`;
63
+ break;
64
+ }
65
+ return command;
66
+ }
67
+ const templateValues = {
68
+ packageManager: options.packageManager,
69
+ projectName: projectName,
70
+ typescript: options.typescript,
71
+ tailwind: options.tailwind,
72
+ toolchain: options.toolchain,
73
+ js: options.typescript ? 'ts' : 'js',
74
+ jsx: options.typescript ? 'tsx' : 'jsx',
75
+ fileRouter: options.mode === FILE_ROUTER,
76
+ codeRouter: options.mode === CODE_ROUTER,
77
+ addOnEnabled: options.chosenAddOns.reduce((acc, addOn) => {
78
+ acc[addOn.id] = true;
79
+ return acc;
80
+ }, {}),
81
+ addOns: options.chosenAddOns,
82
+ ...extraTemplateValues,
83
+ getPackageManagerAddScript,
84
+ getPackageManagerRunScript,
85
+ };
86
+ try {
87
+ content = render(content, templateValues);
88
+ }
89
+ catch (error) {
90
+ console.error(chalk.red(`EJS error in file ${file}`));
91
+ console.error(error);
92
+ process.exit(1);
93
+ }
94
+ const target = targetFileName ?? file.replace('.ejs', '');
95
+ if (target.endsWith('.ts') || target.endsWith('.tsx')) {
96
+ content = await format(content, {
97
+ semi: false,
98
+ singleQuote: true,
99
+ trailingComma: 'all',
100
+ parser: 'typescript',
101
+ });
102
+ }
103
+ await environment.writeFile(resolve(targetDir, target), content);
104
+ };
105
+ }
106
+ async function createPackageJSON(environment, projectName, options, templateDir, routerDir, targetDir, addOns) {
107
+ let packageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.json'), 'utf8'));
108
+ packageJSON.name = projectName;
109
+ if (options.typescript) {
110
+ const tsPackageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.ts.json'), 'utf8'));
111
+ packageJSON = {
112
+ ...packageJSON,
113
+ devDependencies: {
114
+ ...packageJSON.devDependencies,
115
+ ...tsPackageJSON.devDependencies,
116
+ },
117
+ };
118
+ }
119
+ if (options.tailwind) {
120
+ const twPackageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.tw.json'), 'utf8'));
121
+ packageJSON = {
122
+ ...packageJSON,
123
+ dependencies: {
124
+ ...packageJSON.dependencies,
125
+ ...twPackageJSON.dependencies,
126
+ },
127
+ };
128
+ }
129
+ if (options.toolchain === 'biome') {
130
+ const biomePackageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.biome.json'), 'utf8'));
131
+ packageJSON = {
132
+ ...packageJSON,
133
+ scripts: {
134
+ ...packageJSON.scripts,
135
+ ...biomePackageJSON.scripts,
136
+ },
137
+ devDependencies: {
138
+ ...packageJSON.devDependencies,
139
+ ...biomePackageJSON.devDependencies,
140
+ },
141
+ };
142
+ }
143
+ if (options.toolchain === 'eslint+prettier') {
144
+ const eslintPrettierPackageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.eslintprettier.json'), 'utf-8'));
145
+ packageJSON = {
146
+ ...packageJSON,
147
+ scripts: {
148
+ ...packageJSON.scripts,
149
+ ...eslintPrettierPackageJSON.scripts,
150
+ },
151
+ devDependencies: {
152
+ ...packageJSON.devDependencies,
153
+ ...eslintPrettierPackageJSON.devDependencies,
154
+ },
155
+ };
156
+ }
157
+ if (options.mode === FILE_ROUTER) {
158
+ const frPackageJSON = JSON.parse(await environment.readFile(resolve(routerDir, 'package.fr.json'), 'utf8'));
159
+ packageJSON = {
160
+ ...packageJSON,
161
+ dependencies: {
162
+ ...packageJSON.dependencies,
163
+ ...frPackageJSON.dependencies,
164
+ },
165
+ };
166
+ }
167
+ for (const addOn of addOns) {
168
+ packageJSON = {
169
+ ...packageJSON,
170
+ dependencies: {
171
+ ...packageJSON.dependencies,
172
+ ...addOn.dependencies,
173
+ },
174
+ devDependencies: {
175
+ ...packageJSON.devDependencies,
176
+ ...addOn.devDependencies,
177
+ },
178
+ scripts: {
179
+ ...packageJSON.scripts,
180
+ ...addOn.scripts,
181
+ },
182
+ };
183
+ }
184
+ packageJSON.dependencies = sortObject(packageJSON.dependencies);
185
+ packageJSON.devDependencies = sortObject(packageJSON.devDependencies);
186
+ await environment.writeFile(resolve(targetDir, 'package.json'), JSON.stringify(packageJSON, null, 2));
187
+ }
188
+ async function copyAddOnFile(environment, content, target, targetPath, templateFile) {
189
+ let targetFile = basename(target).replace(/^_dot_/, '.');
190
+ let isTemplate = false;
191
+ if (targetFile.endsWith('.ejs')) {
192
+ targetFile = targetFile.replace('.ejs', '');
193
+ isTemplate = true;
194
+ }
195
+ let isAppend = false;
196
+ if (targetFile.endsWith('.append')) {
197
+ targetFile = targetFile.replace('.append', '');
198
+ isAppend = true;
199
+ }
200
+ const finalTargetPath = resolve(dirname(targetPath), targetFile);
201
+ const binaryContent = getBinaryFile(content);
202
+ if (binaryContent) {
203
+ await environment.writeFile(finalTargetPath, binaryContent);
204
+ return;
205
+ }
206
+ if (isTemplate) {
207
+ await templateFile(content, finalTargetPath);
208
+ }
209
+ else {
210
+ if (isAppend) {
211
+ await environment.appendFile(finalTargetPath, content);
212
+ }
213
+ else {
214
+ await environment.writeFile(finalTargetPath, content);
23
215
  }
24
216
  }
25
- await writeFileBundle(options.framework);
26
- for (const type of ['add-on', 'example', 'toolchain']) {
217
+ }
218
+ export async function createApp(options, { silent = false, environment, cwd, appName = 'TanStack', }) {
219
+ environment.startRun();
220
+ const templateDirBase = resolve(getTemplatesRoot(), options.framework, 'base');
221
+ const templateDirRouter = resolve(getTemplatesRoot(), options.framework, options.mode);
222
+ let targetDir = cwd || '';
223
+ if (!targetDir.length) {
224
+ targetDir = resolve(process.cwd(), options.projectName);
225
+ if (environment.exists(targetDir)) {
226
+ if (!silent) {
227
+ log.error(`Directory "${options.projectName}" already exists`);
228
+ }
229
+ return;
230
+ }
231
+ }
232
+ const copyFiles = createCopyFiles(environment, targetDir);
233
+ const templateFileFromContent = createTemplateFile(environment, options.projectName, options, targetDir);
234
+ async function templateFile(templateBase, file, targetFileName, extraTemplateValues) {
235
+ const content = await environment.readFile(resolve(templateBase, file), 'utf-8');
236
+ return templateFileFromContent(file, content.toString(), targetFileName, extraTemplateValues);
237
+ }
238
+ const isAddOnEnabled = (id) => options.chosenAddOns.find((a) => a.id === id);
239
+ async function runAddOn(addOn) {
240
+ if (addOn.files) {
241
+ for (const file of Object.keys(addOn.files)) {
242
+ await copyAddOnFile(environment, addOn.files[file], file, resolve(targetDir, file), (content, targetFileName) => templateFileFromContent(targetFileName, content));
243
+ }
244
+ }
245
+ if (addOn.deletedFiles) {
246
+ for (const file of addOn.deletedFiles) {
247
+ await environment.deleteFile(resolve(targetDir, file));
248
+ }
249
+ }
250
+ if (addOn.command && addOn.command.command) {
251
+ await environment.execute(addOn.command.command, addOn.command.args || [], resolve(targetDir));
252
+ }
253
+ }
254
+ // Setup the .vscode directory
255
+ switch (options.toolchain) {
256
+ case 'biome':
257
+ await environment.copyFile(resolve(templateDirBase, '_dot_vscode/settings.biome.json'), resolve(targetDir, '.vscode/settings.json'));
258
+ break;
259
+ case 'none':
260
+ default:
261
+ await environment.copyFile(resolve(templateDirBase, '_dot_vscode/settings.json'), resolve(targetDir, '.vscode/settings.json'));
262
+ }
263
+ // Fill the public directory
264
+ copyFiles(templateDirBase, [
265
+ './public/robots.txt',
266
+ './public/favicon.ico',
267
+ './public/manifest.json',
268
+ './public/logo192.png',
269
+ './public/logo512.png',
270
+ ]);
271
+ // Check for a .cursorrules file
272
+ if (environment.exists(resolve(templateDirBase, '.cursorrules'))) {
273
+ await environment.copyFile(resolve(templateDirBase, '.cursorrules'), resolve(targetDir, '.cursorrules'));
274
+ }
275
+ // Copy in Vite and Tailwind config and CSS
276
+ if (!options.tailwind) {
277
+ await copyFiles(templateDirBase, ['./src/App.css']);
278
+ }
279
+ // Don't create a vite.config.js file if we are building a Start app
280
+ if (!isAddOnEnabled('start')) {
281
+ await templateFile(templateDirBase, './vite.config.js.ejs');
282
+ }
283
+ await templateFile(templateDirBase, './src/styles.css.ejs');
284
+ copyFiles(templateDirBase, ['./src/logo.svg']);
285
+ if (options.toolchain === 'biome') {
286
+ copyFiles(templateDirBase, ['./toolchain/biome.json'], true);
287
+ }
288
+ if (options.toolchain === 'eslint+prettier') {
289
+ copyFiles(templateDirBase, [
290
+ './toolchain/eslint.config.js',
291
+ './toolchain/prettier.config.js',
292
+ './toolchain/.prettierignore',
293
+ ], true);
294
+ }
295
+ // Setup reportWebVitals
296
+ if (!isAddOnEnabled('start') && options.framework === 'react') {
297
+ if (options.typescript) {
298
+ await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs');
299
+ }
300
+ else {
301
+ await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs', './src/reportWebVitals.js');
302
+ }
303
+ }
304
+ if (!isAddOnEnabled('start')) {
305
+ await templateFile(templateDirBase, './index.html.ejs');
306
+ }
307
+ // Add .gitignore
308
+ await environment.copyFile(resolve(templateDirBase, '_dot_gitignore'), resolve(targetDir, '.gitignore'));
309
+ // Setup tsconfig
310
+ if (options.typescript) {
311
+ await templateFile(templateDirBase, './tsconfig.json.ejs', './tsconfig.json');
312
+ }
313
+ // Setup the package.json file, optionally with typescript, tailwind and formatter/linter
314
+ await createPackageJSON(environment, options.projectName, options, templateDirBase, templateDirRouter, targetDir, options.chosenAddOns.map((addOn) => addOn.packageAdditions));
315
+ // Copy all the asset files from the addons
316
+ const s = silent ? null : spinner();
317
+ for (const type of ['add-on', 'example']) {
27
318
  for (const phase of ['setup', 'add-on', 'example']) {
28
319
  for (const addOn of options.chosenAddOns.filter((addOn) => addOn.phase === phase && addOn.type === type)) {
29
- await writeFileBundle(addOn);
320
+ s?.start(`Setting up ${addOn.name}...`);
321
+ await runAddOn(addOn);
322
+ s?.stop(`${addOn.name} setup complete`);
30
323
  }
31
324
  }
32
325
  }
33
- if (options.starter) {
34
- await writeFileBundle(options.starter);
326
+ if (isAddOnEnabled('shadcn')) {
327
+ const shadcnComponents = new Set();
328
+ for (const addOn of options.chosenAddOns) {
329
+ if (addOn.shadcnComponents) {
330
+ for (const component of addOn.shadcnComponents) {
331
+ shadcnComponents.add(component);
332
+ }
333
+ }
334
+ }
335
+ if (options.overlay) {
336
+ if (options.overlay.shadcnComponents) {
337
+ for (const component of options.overlay.shadcnComponents) {
338
+ shadcnComponents.add(component);
339
+ }
340
+ }
341
+ }
342
+ if (shadcnComponents.size > 0) {
343
+ s?.start(`Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`);
344
+ await packageManagerExecute(environment, options.packageManager, 'shadcn@latest', ['add', '--silent', '--yes', ...shadcnComponents], resolve(targetDir));
345
+ s?.stop(`Installed additional shadcn components`);
346
+ }
35
347
  }
36
- await environment.writeFile(resolve(targetDir, './package.json'), JSON.stringify(createPackageJSON(options), null, 2));
37
- await writeConfigFile(environment, targetDir, options);
38
- }
39
- async function runCommandsAndInstallDependencies(environment, targetDir, options, silent) {
40
- const s = silent ? null : environment.spinner();
41
- // Setup git
42
- if (options.git) {
43
- s?.start(`Initializing git repository...`);
44
- await setupGit(environment, targetDir);
45
- s?.stop(`Initialized git repository`);
348
+ const integrations = [];
349
+ if (environment.exists(resolve(targetDir, 'src/integrations'))) {
350
+ for (const integration of environment.readdir(resolve(targetDir, 'src/integrations'))) {
351
+ const integrationName = jsSafeName(integration);
352
+ if (environment.exists(resolve(targetDir, 'src/integrations', integration, 'layout.tsx'))) {
353
+ integrations.push({
354
+ type: 'layout',
355
+ name: `${integrationName}Layout`,
356
+ path: `integrations/${integration}/layout`,
357
+ });
358
+ }
359
+ if (environment.exists(resolve(targetDir, 'src/integrations', integration, 'provider.tsx'))) {
360
+ integrations.push({
361
+ type: 'provider',
362
+ name: `${integrationName}Provider`,
363
+ path: `integrations/${integration}/provider`,
364
+ });
365
+ }
366
+ if (environment.exists(resolve(targetDir, 'src/integrations', integration, 'root-provider.tsx'))) {
367
+ integrations.push({
368
+ type: 'root-provider',
369
+ name: integrationName,
370
+ path: `integrations/${integration}/root-provider`,
371
+ });
372
+ }
373
+ if (environment.exists(resolve(targetDir, 'src/integrations', integration, 'header-user.tsx'))) {
374
+ integrations.push({
375
+ type: 'header-user',
376
+ name: `${integrationName}Header`,
377
+ path: `integrations/${integration}/header-user`,
378
+ });
379
+ }
380
+ }
46
381
  }
47
- // Install dependencies
48
- s?.start(`Installing dependencies via ${options.packageManager}...`);
49
- await packageManagerInstall(environment, resolve(targetDir), options.packageManager);
50
- s?.stop(`Installed dependencies`);
51
- for (const phase of ['setup', 'add-on', 'example']) {
52
- for (const addOn of options.chosenAddOns.filter((addOn) => addOn.phase === phase && addOn.command && addOn.command.command)) {
53
- s?.start(`Setting up ${addOn.name}...`);
54
- await environment.execute(addOn.command.command, addOn.command.args || [], resolve(targetDir));
55
- s?.stop(`${addOn.name} setup complete`);
382
+ const routes = [];
383
+ if (environment.exists(resolve(targetDir, 'src/routes'))) {
384
+ for (const file of environment.readdir(resolve(targetDir, 'src/routes'))) {
385
+ const name = file.replace(/\.tsx?|\.jsx?/, '');
386
+ const safeRouteName = jsSafeName(name);
387
+ routes.push({
388
+ path: `./routes/${name}`,
389
+ name: safeRouteName,
390
+ });
56
391
  }
57
392
  }
58
- // Adding starter
59
- if (options.starter &&
60
- options.starter.command &&
61
- options.starter.command.command) {
62
- s?.start(`Setting up starter ${options.starter.name}...`);
63
- await environment.execute(options.starter.command.command, options.starter.command.args || [], resolve(targetDir));
64
- s?.stop(`Starter ${options.starter.name} setup complete`);
393
+ // Create the main entry point
394
+ if (!isAddOnEnabled('start')) {
395
+ if (options.typescript) {
396
+ await templateFile(templateDirRouter, './src/main.tsx.ejs', './src/main.tsx', {
397
+ routes,
398
+ integrations,
399
+ });
400
+ }
401
+ else {
402
+ await templateFile(templateDirRouter, './src/main.tsx.ejs', './src/main.jsx', {
403
+ routes,
404
+ integrations,
405
+ });
406
+ }
407
+ }
408
+ // Setup the app component. There are four variations, typescript/javascript and tailwind/non-tailwind.
409
+ if (options.mode === FILE_ROUTER) {
410
+ await templateFile(templateDirRouter, './src/routes/__root.tsx.ejs', './src/routes/__root.tsx', {
411
+ integrations,
412
+ });
413
+ await templateFile(templateDirBase, './src/App.tsx.ejs', './src/routes/index.tsx');
414
+ }
415
+ else {
416
+ await templateFile(templateDirBase, './src/App.tsx.ejs', options.typescript ? undefined : './src/App.jsx');
417
+ if (options.framework === 'react') {
418
+ await templateFile(templateDirBase, './src/App.test.tsx.ejs', options.typescript ? undefined : './src/App.test.jsx');
419
+ }
420
+ }
421
+ if (routes.length > 0 ||
422
+ options.chosenAddOns.length > 0 ||
423
+ integrations.length > 0) {
424
+ await templateFile(templateDirBase, './src/components/Header.tsx.ejs', './src/components/Header.tsx', {
425
+ integrations,
426
+ });
65
427
  }
66
- await installShadcnComponents(environment, targetDir, options, silent);
67
- }
68
- function report(environment, options, appName, targetDir) {
69
428
  const warnings = [];
70
429
  for (const addOn of options.chosenAddOns) {
71
430
  if (addOn.warning) {
72
431
  warnings.push(addOn.warning);
73
432
  }
74
433
  }
434
+ // Create the README.md
435
+ await templateFile(templateDirBase, 'README.md.ejs');
436
+ // Adding overlay
437
+ if (options.overlay) {
438
+ s?.start(`Setting up overlay ${options.overlay.name}...`);
439
+ await runAddOn(options.overlay);
440
+ s?.stop(`Overlay ${options.overlay.name} setup complete`);
441
+ }
442
+ // Install dependencies
443
+ s?.start(`Installing dependencies via ${options.packageManager}...`);
444
+ await environment.execute(options.packageManager, ['install'], resolve(targetDir));
445
+ s?.stop(`Installed dependencies`);
75
446
  if (warnings.length > 0) {
76
- environment.warn('Warnings', warnings.join('\n'));
447
+ if (!silent) {
448
+ log.warn(chalk.red(warnings.join('\n')));
449
+ }
450
+ }
451
+ if (options.toolchain === 'biome') {
452
+ s?.start(`Applying toolchain ${options.toolchain}...`);
453
+ switch (options.packageManager) {
454
+ case 'pnpm':
455
+ // pnpm automatically forwards extra arguments
456
+ await environment.execute(options.packageManager, ['run', 'check', '--fix'], resolve(targetDir));
457
+ break;
458
+ default:
459
+ await environment.execute(options.packageManager, ['run', 'check', '--', '--fix'], resolve(targetDir));
460
+ break;
461
+ }
462
+ s?.stop(`Applied toolchain ${options.toolchain}...`);
463
+ }
464
+ if (options.toolchain === 'eslint+prettier') {
465
+ s?.start(`Applying toolchain ${options.toolchain}...`);
466
+ await environment.execute(options.packageManager, ['run', 'check'], targetDir);
467
+ s?.stop(`Applied toolchain ${options.toolchain}...`);
468
+ }
469
+ if (options.git) {
470
+ s?.start(`Initializing git repository...`);
471
+ await environment.execute('git', ['init'], resolve(targetDir));
472
+ s?.stop(`Initialized git repository`);
77
473
  }
78
- // Format errors
474
+ await writeConfigFile(environment, targetDir, options);
475
+ environment.finishRun();
79
476
  let errorStatement = '';
80
477
  if (environment.getErrors().length) {
81
478
  errorStatement = `
82
479
 
83
- Errors were encountered during this process:
480
+ ${chalk.red('Errors were encountered during this process:')}
84
481
 
85
482
  ${environment.getErrors().join('\n')}`;
86
483
  }
87
- environment.outro(`Your ${appName} app is ready in '${basename(targetDir)}'.
484
+ if (!silent) {
485
+ let startCommand = `${options.packageManager} ${isAddOnEnabled('start') ? 'dev' : 'start'}`;
486
+ if (options.packageManager === 'deno') {
487
+ startCommand = `deno ${isAddOnEnabled('start') ? 'task dev' : 'start'}`;
488
+ }
489
+ outro(`Your ${appName} app is ready in '${basename(targetDir)}'.
88
490
 
89
491
  Use the following commands to start your app:
90
492
  % cd ${options.projectName}
91
- % ${formatCommand(getPackageManagerScriptCommand(options.packageManager, ['dev']))}
493
+ % ${startCommand}
92
494
 
93
- Please check the README.md for more information on testing, styling, adding routes, etc.${errorStatement}`);
94
- }
95
- export async function createApp(options, { silent = false, environment, cwd, appName = 'TanStack', }) {
96
- environment.startRun();
97
- let targetDir = cwd || '';
98
- if (!targetDir.length) {
99
- targetDir = resolve(process.cwd(), options.projectName);
100
- if (environment.exists(targetDir)) {
101
- if (!silent) {
102
- environment.error(`Directory "${options.projectName}" already exists`);
103
- }
104
- return;
105
- }
106
- }
107
- await writeFiles(environment, targetDir, options);
108
- await runCommandsAndInstallDependencies(environment, targetDir, options, silent);
109
- environment.finishRun();
110
- if (!silent) {
111
- report(environment, options, appName, targetDir);
495
+ Please read the README.md for more information on testing, styling, adding routes, react-query, etc.${errorStatement}`);
112
496
  }
113
497
  }