@tanstack/cta-engine 0.10.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/LICENSE +21 -0
  2. package/dist/add-ons.js +109 -0
  3. package/dist/add.js +127 -0
  4. package/dist/cli.js +112 -0
  5. package/dist/config-file.js +23 -0
  6. package/dist/constants.js +5 -0
  7. package/dist/create-app.js +491 -0
  8. package/dist/custom-add-on.js +254 -0
  9. package/dist/environment.js +119 -0
  10. package/dist/index.js +1 -0
  11. package/dist/mcp.js +211 -0
  12. package/dist/options.js +309 -0
  13. package/dist/package-manager.js +30 -0
  14. package/dist/templates.js +6 -0
  15. package/dist/toolchain.js +6 -0
  16. package/dist/types/add-ons.d.ts +5 -0
  17. package/dist/types/add.d.ts +3 -0
  18. package/dist/types/cli.d.ts +1 -0
  19. package/dist/types/config-file.d.ts +7 -0
  20. package/dist/types/constants.d.ts +6 -0
  21. package/dist/types/create-app.d.ts +6 -0
  22. package/dist/types/custom-add-on.d.ts +3 -0
  23. package/dist/types/environment.d.ts +12 -0
  24. package/dist/types/index.d.ts +1 -0
  25. package/dist/types/mcp.d.ts +1 -0
  26. package/dist/types/options.d.ts +3 -0
  27. package/dist/types/package-manager.d.ts +6 -0
  28. package/dist/types/templates.d.ts +1 -0
  29. package/dist/types/toolchain.d.ts +3 -0
  30. package/dist/types/types.d.ts +107 -0
  31. package/dist/types/utils.d.ts +1 -0
  32. package/dist/types.js +1 -0
  33. package/dist/utils.js +8 -0
  34. package/package.json +49 -0
  35. package/src/add-ons.ts +145 -0
  36. package/src/add.ts +184 -0
  37. package/src/cli.ts +163 -0
  38. package/src/config-file.ts +45 -0
  39. package/src/constants.ts +9 -0
  40. package/src/create-app.ts +791 -0
  41. package/src/custom-add-on.ts +323 -0
  42. package/src/environment.ts +144 -0
  43. package/src/index.ts +1 -0
  44. package/src/mcp.ts +252 -0
  45. package/src/options.ts +359 -0
  46. package/src/package-manager.ts +46 -0
  47. package/src/templates.ts +7 -0
  48. package/src/toolchain.ts +7 -0
  49. package/src/types.ts +119 -0
  50. package/src/utils.ts +10 -0
  51. package/templates/react/add-on/clerk/README.md +3 -0
  52. package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
  53. package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
  54. package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
  55. package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
  56. package/templates/react/add-on/clerk/info.json +13 -0
  57. package/templates/react/add-on/clerk/package.json +5 -0
  58. package/templates/react/add-on/convex/README.md +4 -0
  59. package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
  60. package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
  61. package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
  62. package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
  63. package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
  64. package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
  65. package/templates/react/add-on/convex/info.json +13 -0
  66. package/templates/react/add-on/convex/package.json +6 -0
  67. package/templates/react/add-on/form/assets/src/components/demo.FormComponents.tsx.ejs +300 -0
  68. package/templates/react/add-on/form/assets/src/hooks/demo.form-context.ts +4 -0
  69. package/templates/react/add-on/form/assets/src/hooks/demo.form.ts +22 -0
  70. package/templates/react/add-on/form/assets/src/routes/demo.form.address.tsx.ejs +213 -0
  71. package/templates/react/add-on/form/assets/src/routes/demo.form.simple.tsx.ejs +77 -0
  72. package/templates/react/add-on/form/info.json +26 -0
  73. package/templates/react/add-on/form/package.json +6 -0
  74. package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
  75. package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  76. package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
  77. package/templates/react/add-on/module-federation/info.json +7 -0
  78. package/templates/react/add-on/module-federation/package.json +5 -0
  79. package/templates/react/add-on/netlify/README.md +11 -0
  80. package/templates/react/add-on/netlify/info.json +7 -0
  81. package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  82. package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
  83. package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
  84. package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
  85. package/templates/react/add-on/sentry/info.json +14 -0
  86. package/templates/react/add-on/sentry/package.json +7 -0
  87. package/templates/react/add-on/shadcn/README.md +7 -0
  88. package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
  89. package/templates/react/add-on/shadcn/assets/components.json +21 -0
  90. package/templates/react/add-on/shadcn/assets/src/lib/utils.ts +6 -0
  91. package/templates/react/add-on/shadcn/assets/src/styles.css +138 -0
  92. package/templates/react/add-on/shadcn/info.json +7 -0
  93. package/templates/react/add-on/shadcn/package.json +9 -0
  94. package/templates/react/add-on/start/assets/_dot_gitignore.append +2 -0
  95. package/templates/react/add-on/start/assets/app.config.ts.ejs +19 -0
  96. package/templates/react/add-on/start/assets/src/api.ts +6 -0
  97. package/templates/react/add-on/start/assets/src/client.tsx +8 -0
  98. package/templates/react/add-on/start/assets/src/router.tsx.ejs +77 -0
  99. package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
  100. package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
  101. package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +50 -0
  102. package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
  103. package/templates/react/add-on/start/info.json +18 -0
  104. package/templates/react/add-on/start/package.json +13 -0
  105. package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
  106. package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
  107. package/templates/react/add-on/store/info.json +13 -0
  108. package/templates/react/add-on/store/package.json +6 -0
  109. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/init.ts +9 -0
  110. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/react.ts +4 -0
  111. package/templates/react/add-on/tRPC/assets/src/integrations/trpc/router.ts +18 -0
  112. package/templates/react/add-on/tRPC/assets/src/routes/api.trpc.$.tsx +16 -0
  113. package/templates/react/add-on/tRPC/info.json +9 -0
  114. package/templates/react/add-on/tRPC/package.json +9 -0
  115. package/templates/react/add-on/table/assets/src/data/demo-table-data.ts +50 -0
  116. package/templates/react/add-on/table/assets/src/routes/demo.table.tsx.ejs +373 -0
  117. package/templates/react/add-on/table/info.json +13 -0
  118. package/templates/react/add-on/table/package.json +7 -0
  119. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
  120. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/root-provider.tsx.ejs +70 -0
  121. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +53 -0
  122. package/templates/react/add-on/tanstack-query/info.json +13 -0
  123. package/templates/react/add-on/tanstack-query/package.json +6 -0
  124. package/templates/react/base/README.md.ejs +558 -0
  125. package/templates/react/base/_dot_gitignore +5 -0
  126. package/templates/react/base/_dot_vscode/settings.biome.json +38 -0
  127. package/templates/react/base/_dot_vscode/settings.json +11 -0
  128. package/templates/react/base/index.html.ejs +20 -0
  129. package/templates/react/base/package.biome.json +10 -0
  130. package/templates/react/base/package.eslintprettier.json +11 -0
  131. package/templates/react/base/package.json +29 -0
  132. package/templates/react/base/package.ts.json +7 -0
  133. package/templates/react/base/package.tw.json +6 -0
  134. package/templates/react/base/public/favicon.ico +0 -0
  135. package/templates/react/base/public/logo192.png +0 -0
  136. package/templates/react/base/public/logo512.png +0 -0
  137. package/templates/react/base/public/manifest.json +25 -0
  138. package/templates/react/base/public/robots.txt +3 -0
  139. package/templates/react/base/src/App.css +38 -0
  140. package/templates/react/base/src/App.test.tsx.ejs +10 -0
  141. package/templates/react/base/src/App.tsx.ejs +74 -0
  142. package/templates/react/base/src/components/Header.tsx.ejs +27 -0
  143. package/templates/react/base/src/logo.svg +44 -0
  144. package/templates/react/base/src/reportWebVitals.ts.ejs +28 -0
  145. package/templates/react/base/src/styles.css.ejs +15 -0
  146. package/templates/react/base/toolchain/.prettierignore +3 -0
  147. package/templates/react/base/toolchain/biome.json +31 -0
  148. package/templates/react/base/toolchain/eslint.config.js +5 -0
  149. package/templates/react/base/toolchain/prettier.config.js +10 -0
  150. package/templates/react/base/tsconfig.json.ejs +29 -0
  151. package/templates/react/base/vite.config.js.ejs +23 -0
  152. package/templates/react/code-router/src/main.tsx.ejs +92 -0
  153. package/templates/react/example/tanchat/README.md +37 -0
  154. package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
  155. package/templates/react/example/tanchat/assets/public/example-guitar-flowers.jpg +0 -0
  156. package/templates/react/example/tanchat/assets/public/example-guitar-motherboard.jpg +0 -0
  157. package/templates/react/example/tanchat/assets/public/example-guitar-racing.jpg +0 -0
  158. package/templates/react/example/tanchat/assets/public/example-guitar-steamer-trunk.jpg +0 -0
  159. package/templates/react/example/tanchat/assets/public/example-guitar-superhero.jpg +0 -0
  160. package/templates/react/example/tanchat/assets/public/example-guitar-traveling.jpg +0 -0
  161. package/templates/react/example/tanchat/assets/public/example-guitar-video-games.jpg +0 -0
  162. package/templates/react/example/tanchat/assets/src/components/example-AIAssistant.tsx +173 -0
  163. package/templates/react/example/tanchat/assets/src/components/example-GuitarRecommendation.tsx +47 -0
  164. package/templates/react/example/tanchat/assets/src/data/example-guitars.ts +83 -0
  165. package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
  166. package/templates/react/example/tanchat/assets/src/integrations/tanchat/header-user.tsx +5 -0
  167. package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx +159 -0
  168. package/templates/react/example/tanchat/assets/src/routes/example.guitars/$guitarId.tsx +50 -0
  169. package/templates/react/example/tanchat/assets/src/routes/example.guitars/index.tsx +54 -0
  170. package/templates/react/example/tanchat/assets/src/store/example-assistant.ts +3 -0
  171. package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +62 -0
  172. package/templates/react/example/tanchat/assets/src/utils/demo.tools.ts +47 -0
  173. package/templates/react/example/tanchat/info.json +19 -0
  174. package/templates/react/example/tanchat/package.json +15 -0
  175. package/templates/react/file-router/package.fr.json +5 -0
  176. package/templates/react/file-router/src/main.tsx.ejs +55 -0
  177. package/templates/react/file-router/src/routes/__root.tsx.ejs +82 -0
  178. package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +352 -0
  179. package/templates/solid/add-on/form/info.json +13 -0
  180. package/templates/solid/add-on/form/package.json +5 -0
  181. package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
  182. package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  183. package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
  184. package/templates/solid/add-on/module-federation/info.json +7 -0
  185. package/templates/solid/add-on/module-federation/package.json +5 -0
  186. package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  187. package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
  188. package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
  189. package/templates/solid/add-on/sentry/info.json +13 -0
  190. package/templates/solid/add-on/sentry/package.json +5 -0
  191. package/templates/solid/add-on/solid-ui/README.md +9 -0
  192. package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
  193. package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
  194. package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
  195. package/templates/solid/add-on/solid-ui/info.json +11 -0
  196. package/templates/solid/add-on/solid-ui/package.json +9 -0
  197. package/templates/solid/add-on/start/assets/app.config.ts +16 -0
  198. package/templates/solid/add-on/start/assets/src/api.ts +6 -0
  199. package/templates/solid/add-on/start/assets/src/client.tsx +7 -0
  200. package/templates/solid/add-on/start/assets/src/router.tsx.ejs +24 -0
  201. package/templates/solid/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
  202. package/templates/solid/add-on/start/assets/src/ssr.tsx +12 -0
  203. package/templates/solid/add-on/start/info.json +14 -0
  204. package/templates/solid/add-on/start/package.json +12 -0
  205. package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
  206. package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
  207. package/templates/solid/add-on/store/info.json +13 -0
  208. package/templates/solid/add-on/store/package.json +6 -0
  209. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
  210. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
  211. package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
  212. package/templates/solid/add-on/tanstack-query/info.json +13 -0
  213. package/templates/solid/add-on/tanstack-query/package.json +6 -0
  214. package/templates/solid/base/README.md.ejs +215 -0
  215. package/templates/solid/base/_dot_cursorrules.append +35 -0
  216. package/templates/solid/base/_dot_gitignore +5 -0
  217. package/templates/solid/base/_dot_vscode/settings.biome.json +38 -0
  218. package/templates/solid/base/_dot_vscode/settings.json +11 -0
  219. package/templates/solid/base/index.html.ejs +20 -0
  220. package/templates/solid/base/package.biome.json +10 -0
  221. package/templates/solid/base/package.eslintprettier.json +11 -0
  222. package/templates/solid/base/package.json +22 -0
  223. package/templates/solid/base/package.ts.json +5 -0
  224. package/templates/solid/base/package.tw.json +6 -0
  225. package/templates/solid/base/public/favicon.ico +0 -0
  226. package/templates/solid/base/public/logo192.png +0 -0
  227. package/templates/solid/base/public/logo512.png +0 -0
  228. package/templates/solid/base/public/manifest.json +25 -0
  229. package/templates/solid/base/public/robots.txt +3 -0
  230. package/templates/solid/base/src/App.css +0 -0
  231. package/templates/solid/base/src/App.tsx.ejs +47 -0
  232. package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
  233. package/templates/solid/base/src/logo.svg +120 -0
  234. package/templates/solid/base/src/styles.css.ejs +15 -0
  235. package/templates/solid/base/toolchain/.prettierignore +3 -0
  236. package/templates/solid/base/toolchain/biome.json +31 -0
  237. package/templates/solid/base/toolchain/eslint.config.js +5 -0
  238. package/templates/solid/base/toolchain/prettier.config.js +10 -0
  239. package/templates/solid/base/tsconfig.json.ejs +31 -0
  240. package/templates/solid/base/vite.config.js.ejs +22 -0
  241. package/templates/solid/code-router/src/main.tsx.ejs +71 -0
  242. package/templates/solid/example/tanchat/README.md +52 -0
  243. package/templates/solid/example/tanchat/assets/ai-streaming-server/README.md +110 -0
  244. package/templates/solid/example/tanchat/assets/ai-streaming-server/_dot_env.example +1 -0
  245. package/templates/solid/example/tanchat/assets/ai-streaming-server/package.json +26 -0
  246. package/templates/solid/example/tanchat/assets/ai-streaming-server/src/index.ts +102 -0
  247. package/templates/solid/example/tanchat/assets/ai-streaming-server/tsconfig.json +15 -0
  248. package/templates/solid/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +149 -0
  249. package/templates/solid/example/tanchat/assets/src/demo.index.css +227 -0
  250. package/templates/solid/example/tanchat/assets/src/lib/demo-store.ts +13 -0
  251. package/templates/solid/example/tanchat/assets/src/routes/example.chat.tsx +435 -0
  252. package/templates/solid/example/tanchat/assets/src/store/demo.hooks.ts +17 -0
  253. package/templates/solid/example/tanchat/assets/src/store/demo.store.ts +133 -0
  254. package/templates/solid/example/tanchat/info.json +14 -0
  255. package/templates/solid/example/tanchat/package.json +7 -0
  256. package/templates/solid/file-router/package.fr.json +5 -0
  257. package/templates/solid/file-router/src/main.tsx.ejs +47 -0
  258. package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
  259. package/templates/solid/file-router/src/routes/index.tsx +43 -0
  260. package/tests/cra.test.ts +293 -0
  261. package/tests/snapshots/cra/cr-js-npm.json +33 -0
  262. package/tests/snapshots/cra/cr-ts-npm.json +34 -0
  263. package/tests/snapshots/cra/cr-ts-start-npm.json +38 -0
  264. package/tests/snapshots/cra/fr-ts-npm.json +34 -0
  265. package/tests/snapshots/cra/fr-ts-tw-npm.json +33 -0
  266. package/tests/snapshots/cra/solid-cr-js-npm.json +31 -0
  267. package/tests/snapshots/cra/solid-cr-ts-npm.json +32 -0
  268. package/tests/snapshots/cra/solid-cr-ts-start-npm.json +36 -0
  269. package/tests/snapshots/cra/solid-fr-ts-npm.json +33 -0
  270. package/tests/snapshots/cra/solid-fr-ts-tw-npm.json +32 -0
  271. package/tests/test-utilities.ts +87 -0
  272. package/tsconfig.json +11 -0
package/src/options.ts ADDED
@@ -0,0 +1,359 @@
1
+ import {
2
+ cancel,
3
+ confirm,
4
+ isCancel,
5
+ multiselect,
6
+ select,
7
+ text,
8
+ } from '@clack/prompts'
9
+
10
+ import {
11
+ DEFAULT_PACKAGE_MANAGER,
12
+ SUPPORTED_PACKAGE_MANAGERS,
13
+ getPackageManager,
14
+ } from './package-manager.js'
15
+ import { DEFAULT_TOOLCHAIN, SUPPORTED_TOOLCHAINS } from './toolchain.js'
16
+ import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js'
17
+ import { finalizeAddOns, getAllAddOns, loadRemoteAddOn } from './add-ons.js'
18
+
19
+ import type { AddOn, CliOptions, Options, Overlay, Variable } from './types.js'
20
+
21
+ // If all CLI options are provided, use them directly
22
+ export async function normalizeOptions(
23
+ cliOptions: CliOptions,
24
+ ): Promise<Options | undefined> {
25
+ // in some cases, if you use windows/powershell, the argument for addons
26
+ // if sepparated by comma is not really passed as an array, but as a string
27
+ // with spaces, We need to normalize this edge case.
28
+ if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
29
+ const parseSeparatedArgs = cliOptions.addOns[0].split(' ')
30
+ if (parseSeparatedArgs.length > 1) {
31
+ cliOptions.addOns = parseSeparatedArgs
32
+ }
33
+ }
34
+ if (cliOptions.projectName) {
35
+ let typescript =
36
+ cliOptions.template === 'typescript' ||
37
+ cliOptions.template === 'file-router' ||
38
+ cliOptions.framework === 'solid'
39
+
40
+ let tailwind = !!cliOptions.tailwind
41
+ if (cliOptions.framework === 'solid') {
42
+ tailwind = true
43
+ }
44
+
45
+ let mode: typeof FILE_ROUTER | typeof CODE_ROUTER =
46
+ cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
47
+
48
+ const overlay = cliOptions.overlay
49
+ ? ((await loadRemoteAddOn(cliOptions.overlay)) as Overlay)
50
+ : undefined
51
+
52
+ if (overlay) {
53
+ tailwind = overlay.tailwind
54
+ typescript = overlay.typescript
55
+ cliOptions.framework = overlay.framework
56
+ mode = overlay.mode
57
+ }
58
+
59
+ let addOns = false
60
+ let chosenAddOns: Array<AddOn> = []
61
+ if (Array.isArray(cliOptions.addOns) || overlay?.dependsOn) {
62
+ addOns = true
63
+ let finalAddOns = [...(overlay?.dependsOn || [])]
64
+ if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
65
+ finalAddOns = [...finalAddOns, ...cliOptions.addOns]
66
+ }
67
+ chosenAddOns = await finalizeAddOns(
68
+ cliOptions.framework || DEFAULT_FRAMEWORK,
69
+ cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
70
+ finalAddOns,
71
+ )
72
+ tailwind = true
73
+ typescript = true
74
+ }
75
+
76
+ return {
77
+ framework: cliOptions.framework || 'react',
78
+ projectName: cliOptions.projectName,
79
+ typescript,
80
+ tailwind,
81
+ packageManager:
82
+ cliOptions.packageManager ||
83
+ getPackageManager() ||
84
+ DEFAULT_PACKAGE_MANAGER,
85
+ toolchain: cliOptions.toolchain || DEFAULT_TOOLCHAIN,
86
+ mode,
87
+ git: !!cliOptions.git,
88
+ addOns,
89
+ chosenAddOns,
90
+ variableValues: {},
91
+ overlay,
92
+ }
93
+ }
94
+ }
95
+
96
+ async function collectVariables(
97
+ variables: Array<Variable>,
98
+ ): Promise<Record<string, string | number | boolean>> {
99
+ const responses: Record<string, string | number | boolean> = {}
100
+ for (const variable of variables) {
101
+ if (variable.type === 'string') {
102
+ const response = await text({
103
+ message: variable.description,
104
+ initialValue: variable.default,
105
+ })
106
+ if (isCancel(response)) {
107
+ cancel('Operation cancelled.')
108
+ process.exit(0)
109
+ }
110
+ responses[variable.name] = response
111
+ } else if (variable.type === 'number') {
112
+ const response = await text({
113
+ message: variable.description,
114
+ initialValue: variable.default.toString(),
115
+ })
116
+ if (isCancel(response)) {
117
+ cancel('Operation cancelled.')
118
+ process.exit(0)
119
+ }
120
+ responses[variable.name] = Number(response)
121
+ } else {
122
+ const response = await confirm({
123
+ message: variable.description,
124
+ initialValue: variable.default === true,
125
+ })
126
+ if (isCancel(response)) {
127
+ cancel('Operation cancelled.')
128
+ process.exit(0)
129
+ }
130
+ responses[variable.name] = response
131
+ }
132
+ }
133
+ return responses
134
+ }
135
+
136
+ export async function promptForOptions(
137
+ cliOptions: CliOptions,
138
+ ): Promise<Required<Options>> {
139
+ const options = {} as Required<Options>
140
+
141
+ options.framework = cliOptions.framework || DEFAULT_FRAMEWORK
142
+ if (options.framework === 'solid') {
143
+ options.typescript = true
144
+ options.tailwind = true
145
+ }
146
+
147
+ if (cliOptions.addOns) {
148
+ options.typescript = true
149
+ }
150
+
151
+ if (!cliOptions.projectName) {
152
+ const value = await text({
153
+ message: 'What would you like to name your project?',
154
+ defaultValue: 'my-app',
155
+ validate(value) {
156
+ if (!value) {
157
+ return 'Please enter a name'
158
+ }
159
+ },
160
+ })
161
+ if (isCancel(value)) {
162
+ cancel('Operation cancelled.')
163
+ process.exit(0)
164
+ }
165
+ options.projectName = value
166
+ }
167
+
168
+ // Router type selection
169
+ if (!cliOptions.template) {
170
+ const routerType = await select({
171
+ message: 'Select the router type:',
172
+ options: [
173
+ {
174
+ value: FILE_ROUTER,
175
+ label: 'File Router - File-based routing structure',
176
+ },
177
+ {
178
+ value: CODE_ROUTER,
179
+ label: 'Code Router - Traditional code-based routing',
180
+ },
181
+ ],
182
+ initialValue: FILE_ROUTER,
183
+ })
184
+ if (isCancel(routerType)) {
185
+ cancel('Operation cancelled.')
186
+ process.exit(0)
187
+ }
188
+ options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
189
+ } else {
190
+ options.mode =
191
+ cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
192
+ if (options.mode === FILE_ROUTER) {
193
+ options.typescript = true
194
+ }
195
+ }
196
+
197
+ // TypeScript selection (if using Code Router)
198
+ if (!options.typescript) {
199
+ if (options.mode === CODE_ROUTER) {
200
+ const typescriptEnable = await confirm({
201
+ message: 'Would you like to use TypeScript?',
202
+ initialValue: true,
203
+ })
204
+ if (isCancel(typescriptEnable)) {
205
+ cancel('Operation cancelled.')
206
+ process.exit(0)
207
+ }
208
+ options.typescript = typescriptEnable
209
+ } else {
210
+ options.typescript = true
211
+ }
212
+ }
213
+
214
+ // Tailwind selection
215
+ if (!cliOptions.tailwind && options.framework === 'react') {
216
+ const tailwind = await confirm({
217
+ message: 'Would you like to use Tailwind CSS?',
218
+ initialValue: true,
219
+ })
220
+ if (isCancel(tailwind)) {
221
+ cancel('Operation cancelled.')
222
+ process.exit(0)
223
+ }
224
+ options.tailwind = tailwind
225
+ } else {
226
+ options.tailwind = options.framework === 'solid' || !!cliOptions.tailwind
227
+ }
228
+
229
+ // Package manager selection
230
+ if (cliOptions.packageManager === undefined) {
231
+ const detectedPackageManager = getPackageManager()
232
+ if (!detectedPackageManager) {
233
+ const pm = await select({
234
+ message: 'Select package manager:',
235
+ options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
236
+ value: pm,
237
+ label: pm,
238
+ })),
239
+ initialValue: DEFAULT_PACKAGE_MANAGER,
240
+ })
241
+ if (isCancel(pm)) {
242
+ cancel('Operation cancelled.')
243
+ process.exit(0)
244
+ }
245
+ options.packageManager = pm
246
+ } else {
247
+ options.packageManager = detectedPackageManager
248
+ }
249
+ } else {
250
+ options.packageManager = cliOptions.packageManager
251
+ }
252
+
253
+ // Toolchain selection
254
+ if (cliOptions.toolchain === undefined) {
255
+ const tc = await select({
256
+ message: 'Select toolchain',
257
+ options: SUPPORTED_TOOLCHAINS.map((tc) => ({
258
+ value: tc,
259
+ label: tc,
260
+ })),
261
+ initialValue: DEFAULT_TOOLCHAIN,
262
+ })
263
+ if (isCancel(tc)) {
264
+ cancel('Operation cancelled.')
265
+ process.exit(0)
266
+ }
267
+ options.toolchain = tc
268
+ } else {
269
+ options.toolchain = cliOptions.toolchain
270
+ }
271
+
272
+ options.chosenAddOns = []
273
+ if (Array.isArray(cliOptions.addOns)) {
274
+ options.chosenAddOns = await finalizeAddOns(
275
+ options.framework,
276
+ options.mode,
277
+ cliOptions.addOns,
278
+ )
279
+ options.tailwind = true
280
+ } else if (cliOptions.addOns) {
281
+ // Select any add-ons
282
+ const allAddOns = await getAllAddOns(options.framework, options.mode)
283
+ const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on')
284
+ let selectedAddOns: Array<string> = []
285
+ if (options.typescript && addOns.length > 0) {
286
+ const value = await multiselect({
287
+ message: 'What add-ons would you like for your project:',
288
+ options: addOns.map((addOn) => ({
289
+ value: addOn.id,
290
+ label: addOn.name,
291
+ hint: addOn.description,
292
+ })),
293
+ required: false,
294
+ })
295
+
296
+ if (isCancel(value)) {
297
+ cancel('Operation cancelled.')
298
+ process.exit(0)
299
+ }
300
+ selectedAddOns = value
301
+ }
302
+
303
+ // Select any examples
304
+ let selectedExamples: Array<string> = []
305
+ const examples = allAddOns.filter((addOn) => addOn.type === 'example')
306
+ if (options.typescript && examples.length > 0) {
307
+ const value = await multiselect({
308
+ message: 'Would you like any examples?',
309
+ options: examples.map((addOn) => ({
310
+ value: addOn.id,
311
+ label: addOn.name,
312
+ hint: addOn.description,
313
+ })),
314
+ required: false,
315
+ })
316
+
317
+ if (isCancel(value)) {
318
+ cancel('Operation cancelled.')
319
+ process.exit(0)
320
+ }
321
+ selectedExamples = value
322
+ }
323
+
324
+ if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
325
+ options.chosenAddOns = await finalizeAddOns(
326
+ options.framework,
327
+ options.mode,
328
+ [...selectedAddOns, ...selectedExamples],
329
+ )
330
+ options.tailwind = true
331
+ }
332
+ }
333
+
334
+ // Collect variables
335
+ const variables: Array<Variable> = []
336
+ for (const addOn of options.chosenAddOns) {
337
+ for (const variable of addOn.variables ?? []) {
338
+ variables.push(variable)
339
+ }
340
+ }
341
+ options.variableValues = await collectVariables(variables)
342
+
343
+ // Git selection
344
+ if (cliOptions.git === undefined) {
345
+ const git = await confirm({
346
+ message: 'Would you like to initialize a new git repository?',
347
+ initialValue: true,
348
+ })
349
+ if (isCancel(git)) {
350
+ cancel('Operation cancelled.')
351
+ process.exit(0)
352
+ }
353
+ options.git = git
354
+ } else {
355
+ options.git = !!cliOptions.git
356
+ }
357
+
358
+ return options
359
+ }
@@ -0,0 +1,46 @@
1
+ import type { Environment } from './types.js'
2
+
3
+ export const SUPPORTED_PACKAGE_MANAGERS = [
4
+ 'npm',
5
+ 'yarn',
6
+ 'pnpm',
7
+ 'bun',
8
+ 'deno',
9
+ ] as const
10
+ export type PackageManager = (typeof SUPPORTED_PACKAGE_MANAGERS)[number]
11
+ export const DEFAULT_PACKAGE_MANAGER: PackageManager = 'npm'
12
+
13
+ export function getPackageManager(): PackageManager | undefined {
14
+ const userAgent = process.env.npm_config_user_agent
15
+
16
+ if (userAgent === undefined) {
17
+ return DEFAULT_PACKAGE_MANAGER
18
+ }
19
+
20
+ const packageManager = SUPPORTED_PACKAGE_MANAGERS.find((manager) =>
21
+ userAgent.startsWith(manager),
22
+ )
23
+
24
+ return packageManager
25
+ }
26
+
27
+ export function packageManagerExecute(
28
+ environment: Environment,
29
+ packagerManager: PackageManager,
30
+ pkg: string,
31
+ args: Array<string>,
32
+ cwd: string,
33
+ ) {
34
+ switch (packagerManager) {
35
+ case 'yarn':
36
+ return environment.execute('yarn', ['dlx', pkg, ...args], cwd)
37
+ case 'pnpm':
38
+ return environment.execute('pnpx', [pkg, ...args], cwd)
39
+ case 'bun':
40
+ return environment.execute('bunx', ['--bun', pkg, ...args], cwd)
41
+ case 'deno':
42
+ return environment.execute('deno', ['run', `npm:${pkg}`, ...args], cwd)
43
+ default:
44
+ return environment.execute('npx', [pkg, ...args], cwd)
45
+ }
46
+ }
@@ -0,0 +1,7 @@
1
+ import { dirname, join } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ export function getTemplatesRoot() {
5
+ // Get the parent directory because the compiled code is in the dist folder
6
+ return join(dirname(dirname(fileURLToPath(import.meta.url))), 'templates')
7
+ }
@@ -0,0 +1,7 @@
1
+ export const SUPPORTED_TOOLCHAINS = [
2
+ 'none',
3
+ 'biome',
4
+ 'eslint+prettier',
5
+ ] as const
6
+ export type ToolChain = (typeof SUPPORTED_TOOLCHAINS)[number]
7
+ export const DEFAULT_TOOLCHAIN: ToolChain = 'none'
package/src/types.ts ADDED
@@ -0,0 +1,119 @@
1
+ import type { CODE_ROUTER, FILE_ROUTER } from './constants.js'
2
+ import type { PackageManager } from './package-manager.js'
3
+ import type { ToolChain } from './toolchain.js'
4
+
5
+ export type Framework = 'solid' | 'react'
6
+
7
+ export interface Options {
8
+ framework: Framework
9
+ projectName: string
10
+ typescript: boolean
11
+ tailwind: boolean
12
+ packageManager: PackageManager
13
+ toolchain: ToolChain
14
+ mode: typeof CODE_ROUTER | typeof FILE_ROUTER
15
+ addOns: boolean
16
+ chosenAddOns: Array<AddOn>
17
+ git: boolean
18
+ variableValues: Record<string, string | number | boolean>
19
+ overlay?: AddOn | undefined
20
+ }
21
+
22
+ export interface CliOptions {
23
+ template?: 'typescript' | 'javascript' | 'file-router'
24
+ framework?: Framework
25
+ tailwind?: boolean
26
+ packageManager?: PackageManager
27
+ toolchain?: ToolChain
28
+ projectName?: string
29
+ git?: boolean
30
+ addOns?: Array<string> | boolean
31
+ listAddOns?: boolean
32
+ mcp?: boolean
33
+ mcpSse?: boolean
34
+ overlay?: string
35
+ targetDir?: string
36
+ }
37
+
38
+ export type Environment = {
39
+ startRun: () => void
40
+ finishRun: () => void
41
+ getErrors: () => Array<string>
42
+
43
+ appendFile: (path: string, contents: string) => Promise<void>
44
+ copyFile: (from: string, to: string) => Promise<void>
45
+ writeFile: (path: string, contents: string) => Promise<void>
46
+ execute: (command: string, args: Array<string>, cwd: string) => Promise<void>
47
+ deleteFile: (path: string) => Promise<void>
48
+
49
+ readFile: (path: string, encoding?: BufferEncoding) => Promise<string>
50
+ exists: (path: string) => boolean
51
+ readdir: (path: string) => Array<string>
52
+ isDirectory: (path: string) => boolean
53
+ }
54
+
55
+ type BooleanVariable = {
56
+ name: string
57
+ default: boolean
58
+ description: string
59
+ type: 'boolean'
60
+ }
61
+
62
+ type NumberVariable = {
63
+ name: string
64
+ default: number
65
+ description: string
66
+ type: 'number'
67
+ }
68
+
69
+ type StringVariable = {
70
+ name: string
71
+ default: string
72
+ description: string
73
+ type: 'string'
74
+ }
75
+
76
+ export type Variable = BooleanVariable | NumberVariable | StringVariable
77
+
78
+ export type AddOn = {
79
+ id: string
80
+ name: string
81
+ description: string
82
+ type: 'add-on' | 'example' | 'overlay'
83
+ link: string
84
+ templates: Array<string>
85
+ routes: Array<{
86
+ url: string
87
+ name: string
88
+ }>
89
+ packageAdditions: {
90
+ dependencies?: Record<string, string>
91
+ devDependencies?: Record<string, string>
92
+ scripts?: Record<string, string>
93
+ }
94
+ command?: {
95
+ command: string
96
+ args?: Array<string>
97
+ }
98
+ readme?: string
99
+ phase: 'setup' | 'add-on'
100
+ shadcnComponents?: Array<string>
101
+ warning?: string
102
+ dependsOn?: Array<string>
103
+ variables?: Array<Variable>
104
+
105
+ files?: Record<string, string>
106
+ deletedFiles?: Array<string>
107
+ }
108
+
109
+ export type Overlay = AddOn & {
110
+ type: 'overlay'
111
+ version: string
112
+ author: string
113
+ link: string
114
+ license: string
115
+ mode: typeof CODE_ROUTER | typeof FILE_ROUTER
116
+ framework: Framework
117
+ typescript: boolean
118
+ tailwind: boolean
119
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,10 @@
1
+ export function sortObject(
2
+ obj: Record<string, string>,
3
+ ): Record<string, string> {
4
+ return Object.keys(obj)
5
+ .sort()
6
+ .reduce<Record<string, string>>((acc, key) => {
7
+ acc[key] = obj[key]
8
+ return acc
9
+ }, {})
10
+ }
@@ -0,0 +1,3 @@
1
+ ## Setting up Clerk
2
+
3
+ - Set the `VITE_CLERK_PUBLISHABLE_KEY` in your `.env.local`.
@@ -0,0 +1,2 @@
1
+ # Clerk configuration, get this key from your [Dashboard](https://clerk.com/dashboard).
2
+ VITE_CLERK_PUBLISHABLE_KEY=
@@ -0,0 +1,19 @@
1
+ import {
2
+ SignedIn,
3
+ SignInButton,
4
+ SignedOut,
5
+ UserButton,
6
+ } from '@clerk/clerk-react'
7
+
8
+ export default function HeaderUser() {
9
+ return (
10
+ <>
11
+ <SignedIn>
12
+ <UserButton />
13
+ </SignedIn>
14
+ <SignedOut>
15
+ <SignInButton />
16
+ </SignedOut>
17
+ </>
18
+ )
19
+ }
@@ -0,0 +1,18 @@
1
+ import { ClerkProvider } from '@clerk/clerk-react'
2
+
3
+ const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
4
+ if (!PUBLISHABLE_KEY) {
5
+ throw new Error('Add your Clerk Publishable Key to the .env.local file')
6
+ }
7
+
8
+ export default function AppClerkProvider({
9
+ children,
10
+ }: {
11
+ children: React.ReactNode
12
+ }) {
13
+ return (
14
+ <ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
15
+ {children}
16
+ </ClerkProvider>
17
+ )
18
+ }
@@ -0,0 +1,20 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { useUser } from '@clerk/clerk-react'
3
+
4
+ export const Route = createFileRoute('/demo/clerk')({
5
+ component: App,
6
+ })
7
+
8
+ function App() {
9
+ const { isSignedIn, user, isLoaded } = useUser()
10
+
11
+ if (!isLoaded) {
12
+ return <div className="p-4">Loading...</div>
13
+ }
14
+
15
+ if (!isSignedIn) {
16
+ return <div className="p-4">Sign in to view this page</div>
17
+ }
18
+
19
+ return <div className="p-4">Hello {user.firstName}!</div>
20
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "Clerk",
3
+ "description": "Add Clerk authentication to your application.",
4
+ "phase": "add-on",
5
+ "templates": ["file-router"],
6
+ "link": "https://clerk.com",
7
+ "routes": [
8
+ {
9
+ "url": "/demo/clerk",
10
+ "name": "Clerk"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "@clerk/clerk-react": "^5.22.13"
4
+ }
5
+ }
@@ -0,0 +1,4 @@
1
+ ## Setting up Convex
2
+
3
+ - Set the `VITE_CONVEX_URL` and `CONVEX_DEPLOYMENT` environment variables in your `.env.local`. (Or run `npx convex init` to set them automatically.)
4
+ - Run `npx convex dev` to start the Convex server.