@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
@@ -6,7 +6,7 @@ import {
6
6
  unlink,
7
7
  writeFile,
8
8
  } from 'node:fs/promises'
9
- import { existsSync, readdirSync, statSync } from 'node:fs'
9
+ import { existsSync } from 'node:fs'
10
10
  import { dirname } from 'node:path'
11
11
  import { execa } from 'execa'
12
12
  import { memfs } from 'memfs'
@@ -49,14 +49,18 @@ export function createDefaultEnvironment(): Environment {
49
49
  await unlink(path)
50
50
  },
51
51
 
52
- readFile: (path: string, encoding?: BufferEncoding) =>
53
- readFile(path, { encoding: encoding || 'utf8' }),
54
52
  exists: (path: string) => existsSync(path),
55
- readdir: (path) => readdirSync(path),
56
- isDirectory: (path) => {
57
- const stat = statSync(path)
58
- return stat.isDirectory()
59
- },
53
+
54
+ intro: () => {},
55
+ outro: () => {},
56
+ info: () => {},
57
+ error: () => {},
58
+ warn: () => {},
59
+ confirm: () => Promise.resolve(true),
60
+ spinner: () => ({
61
+ start: () => {},
62
+ stop: () => {},
63
+ }),
60
64
  }
61
65
  }
62
66
 
@@ -101,12 +105,6 @@ export function createMemoryEnvironment() {
101
105
  })
102
106
  return Promise.resolve()
103
107
  }
104
- environment.readFile = async (path: string, encoding?: BufferEncoding) => {
105
- if (isTemplatePath(path)) {
106
- return (await readFile(path, encoding)).toString()
107
- }
108
- return fs.readFileSync(path, 'utf8').toString()
109
- }
110
108
  environment.writeFile = async (path: string, contents: string) => {
111
109
  fs.mkdirSync(dirname(path), { recursive: true })
112
110
  await fs.writeFileSync(path, contents)
@@ -120,19 +118,6 @@ export function createMemoryEnvironment() {
120
118
  }
121
119
  return fs.existsSync(path)
122
120
  }
123
- environment.readdir = (path: string) => {
124
- if (isTemplatePath(path)) {
125
- return readdirSync(path)
126
- }
127
- return fs.readdirSync(path).map((file) => file.toString())
128
- }
129
- environment.isDirectory = (path) => {
130
- if (isTemplatePath(path)) {
131
- const stat = statSync(path)
132
- return stat.isDirectory()
133
- }
134
- return fs.statSync(path).isDirectory()
135
- }
136
121
  environment.finishRun = () => {
137
122
  output.files = vol.toJSON() as Record<string, string>
138
123
  }
@@ -0,0 +1,73 @@
1
+ import { readFileSync, readdirSync, statSync } from 'node:fs'
2
+ import { basename, extname, resolve } from 'node:path'
3
+
4
+ const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico']
5
+
6
+ export function readFileHelper(path: string): string {
7
+ if (BINARY_EXTENSIONS.includes(extname(path))) {
8
+ return `base64::${readFileSync(path).toString('base64')}`
9
+ } else {
10
+ return readFileSync(path, 'utf-8').toString()
11
+ }
12
+ }
13
+
14
+ export function getBinaryFile(content: string): string | null {
15
+ if (content.startsWith('base64::')) {
16
+ const binaryContent = Buffer.from(content.replace('base64::', ''), 'base64')
17
+ return binaryContent as unknown as string
18
+ }
19
+ return null
20
+ }
21
+
22
+ export function relativePath(from: string, to: string) {
23
+ const cleanedFrom = from.startsWith('./') ? from.slice(2) : from
24
+ const cleanedTo = to.startsWith('./') ? to.slice(2) : to
25
+
26
+ const fromSegments = cleanedFrom.split('/')
27
+ const toSegments = cleanedTo.split('/')
28
+
29
+ fromSegments.pop()
30
+ toSegments.pop()
31
+
32
+ let commonIndex = 0
33
+ while (
34
+ commonIndex < fromSegments.length &&
35
+ commonIndex < toSegments.length &&
36
+ fromSegments[commonIndex] === toSegments[commonIndex]
37
+ ) {
38
+ commonIndex++
39
+ }
40
+
41
+ const upLevels = fromSegments.length - commonIndex
42
+ const downLevels = toSegments.slice(commonIndex)
43
+
44
+ if (upLevels === 0 && downLevels.length === 0) {
45
+ return `./${basename(to)}`
46
+ } else if (upLevels === 0 && downLevels.length > 0) {
47
+ return `./${downLevels.join('/')}/${basename(to)}`
48
+ } else {
49
+ const relativePath = [...Array(upLevels).fill('..'), ...downLevels].join(
50
+ '/',
51
+ )
52
+ return `${relativePath}/${basename(to)}`
53
+ }
54
+ }
55
+
56
+ export function isDirectory(path: string): boolean {
57
+ return statSync(path).isDirectory()
58
+ }
59
+
60
+ export function findFilesRecursively(
61
+ path: string,
62
+ files: Record<string, string>,
63
+ ) {
64
+ const dirFiles = readdirSync(path)
65
+ for (const file of dirFiles) {
66
+ const filePath = resolve(path, file)
67
+ if (isDirectory(filePath)) {
68
+ findFilesRecursively(filePath, files)
69
+ } else {
70
+ files[filePath] = readFileHelper(filePath)
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,114 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+
4
+ import {
5
+ findFilesRecursively,
6
+ isDirectory,
7
+ readFileHelper,
8
+ } from './file-helpers.js'
9
+
10
+ import type { AddOn, Framework, FrameworkDefinition } from './types.js'
11
+
12
+ const frameworks: Array<Framework> = []
13
+
14
+ function getAddOns(framework: FrameworkDefinition) {
15
+ const addOns: Array<AddOn> = []
16
+
17
+ for (const addOnsBase of framework.addOnsDirectories) {
18
+ for (const dir of readdirSync(addOnsBase).filter((file) =>
19
+ isDirectory(resolve(addOnsBase, file)),
20
+ )) {
21
+ const filePath = resolve(addOnsBase, dir, 'info.json')
22
+ const fileContent = readFileSync(filePath, 'utf-8')
23
+ const info = JSON.parse(fileContent)
24
+
25
+ let packageAdditions: Record<string, string> = {}
26
+ if (existsSync(resolve(addOnsBase, dir, 'package.json'))) {
27
+ packageAdditions = JSON.parse(
28
+ readFileSync(resolve(addOnsBase, dir, 'package.json'), 'utf-8'),
29
+ )
30
+ }
31
+
32
+ let readme: string | undefined
33
+ if (existsSync(resolve(addOnsBase, dir, 'README.md'))) {
34
+ readme = readFileSync(resolve(addOnsBase, dir, 'README.md'), 'utf-8')
35
+ }
36
+
37
+ const absoluteFiles: Record<string, string> = {}
38
+ const assetsDir = resolve(addOnsBase, dir, 'assets')
39
+ if (existsSync(assetsDir)) {
40
+ findFilesRecursively(assetsDir, absoluteFiles)
41
+ }
42
+ const files: Record<string, string> = {}
43
+ for (const file of Object.keys(absoluteFiles)) {
44
+ files[file.replace(assetsDir, '.')] = readFileHelper(file)
45
+ }
46
+
47
+ const getFiles = () => {
48
+ return Promise.resolve(Object.keys(files))
49
+ }
50
+ const getFileContents = (path: string) => {
51
+ return Promise.resolve(files[path])
52
+ }
53
+
54
+ addOns.push({
55
+ ...info,
56
+ id: dir,
57
+ packageAdditions,
58
+ readme,
59
+ files,
60
+ deletedFiles: [],
61
+ getFiles,
62
+ getFileContents,
63
+ })
64
+ }
65
+ }
66
+
67
+ return addOns
68
+ }
69
+
70
+ export function registerFramework(framework: FrameworkDefinition) {
71
+ const baseAssetsDirectory = resolve(framework.baseDirectory, 'base')
72
+
73
+ const basePackageJSON = JSON.parse(
74
+ readFileSync(resolve(baseAssetsDirectory, 'package.json'), 'utf8'),
75
+ )
76
+ const optionalPackages = JSON.parse(
77
+ readFileSync(resolve(framework.baseDirectory, 'packages.json'), 'utf8'),
78
+ )
79
+
80
+ const addOns = getAddOns(framework)
81
+
82
+ const frameworkWithBundler: Framework = {
83
+ ...framework,
84
+ getFiles: () => {
85
+ const files: Record<string, string> = {}
86
+ findFilesRecursively(baseAssetsDirectory, files)
87
+ return Promise.resolve(
88
+ Object.keys(files).map((path) =>
89
+ path.replace(baseAssetsDirectory, '.'),
90
+ ),
91
+ )
92
+ },
93
+ getFileContents: (path: string) => {
94
+ return Promise.resolve(readFileHelper(resolve(baseAssetsDirectory, path)))
95
+ },
96
+ basePackageJSON,
97
+ optionalPackages,
98
+ getAddOns: () => addOns,
99
+ }
100
+
101
+ frameworks.push(frameworkWithBundler)
102
+ }
103
+
104
+ export function getFrameworkById(id: string) {
105
+ return frameworks.find((framework) => framework.id === id)
106
+ }
107
+
108
+ export function getFrameworkByName(name: string) {
109
+ return frameworks.find((framework) => framework.name === name)
110
+ }
111
+
112
+ export function getFrameworks() {
113
+ return frameworks
114
+ }
package/src/index.ts CHANGED
@@ -1 +1,42 @@
1
- export { cli } from './cli.js'
1
+ export { createApp } from './create-app.js'
2
+ export { addToApp } from './add-to-app.js'
3
+
4
+ export { finalizeAddOns, getAllAddOns, loadRemoteAddOn } from './add-ons.js'
5
+ export {
6
+ createMemoryEnvironment,
7
+ createDefaultEnvironment,
8
+ } from './environment.js'
9
+ export { CODE_ROUTER, CONFIG_FILE, FILE_ROUTER } from './constants.js'
10
+ export {
11
+ DEFAULT_PACKAGE_MANAGER,
12
+ SUPPORTED_PACKAGE_MANAGERS,
13
+ getPackageManager,
14
+ getPackageManagerInstallCommand,
15
+ getPackageManagerExecuteCommand,
16
+ getPackageManagerScriptCommand,
17
+ packageManagerInstall,
18
+ packageManagerExecute,
19
+ } from './package-manager.js'
20
+ export {
21
+ registerFramework,
22
+ getFrameworkById,
23
+ getFrameworkByName,
24
+ getFrameworks,
25
+ } from './frameworks.js'
26
+ export { formatCommand, jsSafeName, sortObject } from './utils.js'
27
+ export { writeConfigFile, readConfigFile } from './config-file.js'
28
+ export { readFileHelper, getBinaryFile, relativePath } from './file-helpers.js'
29
+
30
+ export type {
31
+ AddOn,
32
+ Environment,
33
+ FileBundleHandler,
34
+ FrameworkDefinition,
35
+ Mode,
36
+ Options,
37
+ Starter,
38
+ TemplateOptions,
39
+ Variable,
40
+ } from './types.js'
41
+ export type { PersistedOptions } from './config-file.js'
42
+ export type { PackageManager } from './package-manager.js'
@@ -0,0 +1,7 @@
1
+ import { resolve } from 'node:path'
2
+
3
+ import type { Environment } from '../types.js'
4
+
5
+ export async function setupGit(environment: Environment, targetDir: string) {
6
+ await environment.execute('git', ['init'], resolve(targetDir))
7
+ }
@@ -0,0 +1,44 @@
1
+ import { resolve } from 'node:path'
2
+ import { packageManagerExecute } from '../package-manager.js'
3
+ import type { Environment, Options } from '../types.js'
4
+
5
+ export async function installShadcnComponents(
6
+ environment: Environment,
7
+ targetDir: string,
8
+ options: Options,
9
+ silent: boolean,
10
+ ) {
11
+ const s = silent ? null : environment.spinner()
12
+
13
+ if (options.chosenAddOns.find((a) => a.id === 'shadcn')) {
14
+ const shadcnComponents = new Set<string>()
15
+ for (const addOn of options.chosenAddOns) {
16
+ if (addOn.shadcnComponents) {
17
+ for (const component of addOn.shadcnComponents) {
18
+ shadcnComponents.add(component)
19
+ }
20
+ }
21
+ }
22
+ if (options.starter) {
23
+ if (options.starter.shadcnComponents) {
24
+ for (const component of options.starter.shadcnComponents) {
25
+ shadcnComponents.add(component)
26
+ }
27
+ }
28
+ }
29
+
30
+ if (shadcnComponents.size > 0) {
31
+ s?.start(
32
+ `Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`,
33
+ )
34
+ await packageManagerExecute(
35
+ environment,
36
+ resolve(targetDir),
37
+ options.packageManager,
38
+ 'shadcn@latest',
39
+ ['add', '--silent', '--yes', ...Array.from(shadcnComponents)],
40
+ )
41
+ s?.stop(`Installed additional shadcn components`)
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,62 @@
1
+ import { FILE_ROUTER } from './constants.js'
2
+ import { sortObject } from './utils.js'
3
+
4
+ import type { Options } from './types.js'
5
+
6
+ function mergePackageJSON(
7
+ packageJSON: Record<string, any>,
8
+ overlayPackageJSON: Record<string, any>,
9
+ ) {
10
+ return {
11
+ ...packageJSON,
12
+ dependencies: {
13
+ ...packageJSON.dependencies,
14
+ ...overlayPackageJSON.dependencies,
15
+ },
16
+ devDependencies: {
17
+ ...packageJSON.devDependencies,
18
+ ...overlayPackageJSON.devDependencies,
19
+ },
20
+ scripts: {
21
+ ...packageJSON.scripts,
22
+ ...overlayPackageJSON.scripts,
23
+ },
24
+ }
25
+ }
26
+
27
+ export function createPackageJSON(options: Options) {
28
+ let packageJSON = {
29
+ ...JSON.parse(JSON.stringify(options.framework.basePackageJSON)),
30
+ name: options.projectName,
31
+ }
32
+
33
+ const additions: Array<Record<string, any> | undefined> = [
34
+ options.typescript
35
+ ? options.framework.optionalPackages.typescript
36
+ : undefined,
37
+ options.tailwind
38
+ ? options.framework.optionalPackages.tailwindcss
39
+ : undefined,
40
+ options.mode === FILE_ROUTER
41
+ ? options.framework.optionalPackages['file-router']
42
+ : undefined,
43
+ ]
44
+ for (const addition of additions.filter(Boolean)) {
45
+ packageJSON = mergePackageJSON(packageJSON, addition!)
46
+ }
47
+
48
+ for (const addOn of options.chosenAddOns.map(
49
+ (addOn) => addOn.packageAdditions,
50
+ )) {
51
+ packageJSON = mergePackageJSON(packageJSON, addOn)
52
+ }
53
+
54
+ packageJSON.dependencies = sortObject(
55
+ (packageJSON.dependencies ?? {}) as Record<string, string>,
56
+ )
57
+ packageJSON.devDependencies = sortObject(
58
+ (packageJSON.devDependencies ?? {}) as Record<string, string>,
59
+ )
60
+
61
+ return packageJSON
62
+ }
@@ -24,23 +24,94 @@ export function getPackageManager(): PackageManager | undefined {
24
24
  return packageManager
25
25
  }
26
26
 
27
- export function packageManagerExecute(
28
- environment: Environment,
27
+ export function getPackageManagerScriptCommand(
28
+ packagerManager: PackageManager,
29
+ args: Array<string> = [],
30
+ ) {
31
+ switch (packagerManager) {
32
+ case 'yarn':
33
+ return { command: 'yarn', args: ['run', ...args] }
34
+ case 'pnpm':
35
+ return { command: 'pnpm', args: [...args] }
36
+ case 'bun':
37
+ return { command: 'bunx', args: ['--bun', 'run', ...args] }
38
+ case 'deno':
39
+ return { command: 'deno', args: ['task', ...args] }
40
+ default:
41
+ return { command: 'npm', args: ['run', ...args] }
42
+ }
43
+ }
44
+
45
+ export function getPackageManagerExecuteCommand(
29
46
  packagerManager: PackageManager,
30
47
  pkg: string,
31
- args: Array<string>,
32
- cwd: string,
48
+ args: Array<string> = [],
33
49
  ) {
34
50
  switch (packagerManager) {
35
51
  case 'yarn':
36
- return environment.execute('yarn', ['dlx', pkg, ...args], cwd)
52
+ return { command: 'yarn', args: ['dlx', pkg, ...args] }
37
53
  case 'pnpm':
38
- return environment.execute('pnpx', [pkg, ...args], cwd)
54
+ return { command: 'pnpx', args: [pkg, ...args] }
39
55
  case 'bun':
40
- return environment.execute('bunx', ['--bun', pkg, ...args], cwd)
56
+ return { command: 'bunx', args: ['--bun', pkg, ...args] }
41
57
  case 'deno':
42
- return environment.execute('deno', ['run', `npm:${pkg}`, ...args], cwd)
58
+ return { command: 'deno', args: ['run', `npm:${pkg}`, ...args] }
43
59
  default:
44
- return environment.execute('npx', [pkg, ...args], cwd)
60
+ return { command: 'npx', args: [pkg, ...args] }
45
61
  }
46
62
  }
63
+
64
+ export function getPackageManagerInstallCommand(
65
+ packagerManager: PackageManager,
66
+ pkg?: string,
67
+ isDev: boolean = false,
68
+ ) {
69
+ if (pkg) {
70
+ switch (packagerManager) {
71
+ case 'yarn':
72
+ case 'pnpm':
73
+ return {
74
+ command: packagerManager,
75
+ args: ['add', pkg, isDev ? '--dev' : ''],
76
+ }
77
+ default:
78
+ return {
79
+ command: packagerManager,
80
+ args: ['install', pkg, isDev ? '-D' : ''],
81
+ }
82
+ }
83
+ } else {
84
+ return {
85
+ command: packagerManager,
86
+ args: ['install'],
87
+ }
88
+ }
89
+ }
90
+
91
+ export function packageManagerInstall(
92
+ environment: Environment,
93
+ cwd: string,
94
+ packagerManager: PackageManager,
95
+ pkg?: string,
96
+ ) {
97
+ const { command, args: commandArgs } = getPackageManagerInstallCommand(
98
+ packagerManager,
99
+ pkg,
100
+ )
101
+ return environment.execute(command, commandArgs, cwd)
102
+ }
103
+
104
+ export function packageManagerExecute(
105
+ environment: Environment,
106
+ cwd: string,
107
+ packagerManager: PackageManager,
108
+ pkg: string,
109
+ args: Array<string>,
110
+ ) {
111
+ const { command, args: commandArgs } = getPackageManagerExecuteCommand(
112
+ packagerManager,
113
+ pkg,
114
+ args,
115
+ )
116
+ return environment.execute(command, commandArgs, cwd)
117
+ }
@@ -0,0 +1,165 @@
1
+ import { resolve } from 'node:path'
2
+ import { render } from 'ejs'
3
+ import { format } from 'prettier'
4
+
5
+ import { CODE_ROUTER, FILE_ROUTER } from './constants.js'
6
+ import { formatCommand } from './utils.js'
7
+ import {
8
+ getPackageManagerInstallCommand,
9
+ getPackageManagerScriptCommand,
10
+ } from './package-manager.js'
11
+ import { relativePath } from './file-helpers.js'
12
+
13
+ import type { AddOn, Environment, Options } from './types.js'
14
+
15
+ function convertDotFilesAndPaths(path: string) {
16
+ return path
17
+ .split('/')
18
+ .map((segment) => segment.replace(/^_dot_/, '.'))
19
+ .join('/')
20
+ }
21
+
22
+ export function createTemplateFile(
23
+ environment: Environment,
24
+ options: Options,
25
+ targetDir: string,
26
+ ) {
27
+ function getPackageManagerAddScript(
28
+ packageName: string,
29
+ isDev: boolean = false,
30
+ ) {
31
+ return formatCommand(
32
+ getPackageManagerInstallCommand(
33
+ options.packageManager,
34
+ packageName,
35
+ isDev,
36
+ ),
37
+ )
38
+ }
39
+ function getPackageManagerRunScript(
40
+ scriptName: string,
41
+ args: Array<string> = [],
42
+ ) {
43
+ return formatCommand(
44
+ getPackageManagerScriptCommand(options.packageManager, [
45
+ scriptName,
46
+ ...args,
47
+ ]),
48
+ )
49
+ }
50
+
51
+ class IgnoreFileError extends Error {
52
+ constructor() {
53
+ super('ignoreFile')
54
+ this.name = 'IgnoreFileError'
55
+ }
56
+ }
57
+
58
+ const integrations: Array<Required<AddOn>['integrations'][number]> = []
59
+ for (const addOn of options.chosenAddOns) {
60
+ if (addOn.integrations) {
61
+ for (const integration of addOn.integrations) {
62
+ integrations.push(integration)
63
+ }
64
+ }
65
+ }
66
+
67
+ const routes: Array<Required<AddOn>['routes'][number]> = []
68
+ for (const addOn of options.chosenAddOns) {
69
+ if (addOn.routes) {
70
+ routes.push(...addOn.routes)
71
+ }
72
+ }
73
+
74
+ const variables = {
75
+ ...options.variableValues,
76
+ ...options.chosenAddOns.reduce<Record<string, any>>((acc, addOn) => {
77
+ return {
78
+ ...acc,
79
+ ...addOn.variables,
80
+ }
81
+ }, {}),
82
+ }
83
+
84
+ const addOnEnabled = options.chosenAddOns.reduce<Record<string, boolean>>(
85
+ (acc, addOn) => {
86
+ acc[addOn.id] = true
87
+ return acc
88
+ },
89
+ {},
90
+ )
91
+
92
+ return async function templateFile(file: string, content: string) {
93
+ const templateValues = {
94
+ packageManager: options.packageManager,
95
+ projectName: options.projectName,
96
+ typescript: options.typescript,
97
+ tailwind: options.tailwind,
98
+ js: options.typescript ? 'ts' : 'js',
99
+ jsx: options.typescript ? 'tsx' : 'jsx',
100
+ fileRouter: options.mode === FILE_ROUTER,
101
+ codeRouter: options.mode === CODE_ROUTER,
102
+ addOnEnabled,
103
+ addOns: options.chosenAddOns,
104
+ integrations,
105
+ routes,
106
+ variables,
107
+
108
+ getPackageManagerAddScript,
109
+ getPackageManagerRunScript,
110
+
111
+ relativePath: (path: string) => relativePath(file, path),
112
+
113
+ ignoreFile: () => {
114
+ throw new IgnoreFileError()
115
+ },
116
+ }
117
+
118
+ let ignoreFile = false
119
+
120
+ if (file.endsWith('.ejs')) {
121
+ try {
122
+ content = render(content, templateValues)
123
+ } catch (error) {
124
+ if (error instanceof IgnoreFileError) {
125
+ ignoreFile = true
126
+ } else {
127
+ console.error(file, error)
128
+ environment.error(`EJS error in file ${file}`, error?.toString())
129
+ process.exit(1)
130
+ }
131
+ }
132
+ }
133
+
134
+ if (ignoreFile) {
135
+ return
136
+ }
137
+
138
+ let target = convertDotFilesAndPaths(file.replace('.ejs', ''))
139
+
140
+ let append = false
141
+ if (target.endsWith('.append')) {
142
+ append = true
143
+ target = target.replace('.append', '')
144
+ }
145
+
146
+ if (target.endsWith('.ts') || target.endsWith('.tsx')) {
147
+ content = await format(content, {
148
+ semi: false,
149
+ singleQuote: true,
150
+ trailingComma: 'all',
151
+ parser: 'typescript',
152
+ })
153
+ }
154
+
155
+ if (!options.typescript) {
156
+ target = target.replace(/\.tsx$/, '.jsx').replace(/\.ts$/, '.js')
157
+ }
158
+
159
+ if (append) {
160
+ await environment.appendFile(resolve(targetDir, target), content)
161
+ } else {
162
+ await environment.writeFile(resolve(targetDir, target), content)
163
+ }
164
+ }
165
+ }