virastack 0.0.1 → 1.0.0

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 (433) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +94 -18
  3. package/bin/virastack.js +7 -0
  4. package/package.json +43 -9
  5. package/src/commands/add.js +64 -0
  6. package/src/commands/init.js +286 -0
  7. package/src/i18n.js +231 -0
  8. package/src/index.js +64 -0
  9. package/src/utils/args.js +60 -0
  10. package/src/utils/copy-template.js +80 -0
  11. package/src/utils/exec.js +29 -0
  12. package/src/utils/git.js +38 -0
  13. package/src/utils/is-online.js +14 -0
  14. package/src/utils/package-info.js +12 -0
  15. package/src/utils/package-manager.js +33 -0
  16. package/src/utils/setup-env.js +25 -0
  17. package/src/utils/telemetry.js +86 -0
  18. package/src/utils/tools.js +48 -0
  19. package/src/utils/update-package-json.js +44 -0
  20. package/src/utils/update-readme.js +25 -0
  21. package/src/utils/validate-project-name.js +26 -0
  22. package/templates/nextjs/.changeset/README.md +8 -0
  23. package/templates/nextjs/.changeset/config.json +11 -0
  24. package/templates/nextjs/.env.example +6 -0
  25. package/templates/nextjs/.github/workflows/ci.yml +41 -0
  26. package/templates/nextjs/.husky/commit-msg +1 -0
  27. package/templates/nextjs/.husky/pre-commit +1 -0
  28. package/templates/nextjs/.husky/pre-push +1 -0
  29. package/templates/nextjs/.prettierignore +6 -0
  30. package/templates/nextjs/.prettierrc +42 -0
  31. package/templates/nextjs/.vscode/extensions.json +7 -0
  32. package/templates/nextjs/.vscode/settings.json +20 -0
  33. package/templates/nextjs/LICENSE +21 -0
  34. package/templates/nextjs/README.md +202 -0
  35. package/templates/nextjs/commitlint.config.js +6 -0
  36. package/templates/nextjs/eslint.config.mjs +27 -0
  37. package/templates/nextjs/knip.config.js +25 -0
  38. package/templates/nextjs/next.config.ts +39 -0
  39. package/templates/nextjs/package.json +115 -0
  40. package/templates/nextjs/pnpm-lock.yaml +6467 -0
  41. package/templates/nextjs/pnpm-workspace.yaml +3 -0
  42. package/templates/nextjs/postcss.config.mjs +7 -0
  43. package/templates/nextjs/public/favicon.ico +0 -0
  44. package/templates/nextjs/public/llms.txt +96 -0
  45. package/templates/nextjs/public/logo.png +0 -0
  46. package/templates/nextjs/public/logo.webp +0 -0
  47. package/templates/nextjs/public/og.png +0 -0
  48. package/templates/nextjs/src/app/error.tsx +34 -0
  49. package/templates/nextjs/src/app/favicon.ico +0 -0
  50. package/templates/nextjs/src/app/global-error.tsx +32 -0
  51. package/templates/nextjs/src/app/layout.tsx +37 -0
  52. package/templates/nextjs/src/app/loading.tsx +12 -0
  53. package/templates/nextjs/src/app/manifest.ts +22 -0
  54. package/templates/nextjs/src/app/not-found.tsx +18 -0
  55. package/templates/nextjs/src/app/page.tsx +5 -0
  56. package/templates/nextjs/src/app/robots.ts +13 -0
  57. package/templates/nextjs/src/app/sitemap.ts +14 -0
  58. package/templates/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
  59. package/templates/nextjs/src/components/ui/avatar.tsx +54 -0
  60. package/templates/nextjs/src/components/ui/button.tsx +46 -0
  61. package/templates/nextjs/src/components/ui/dialog.tsx +107 -0
  62. package/templates/nextjs/src/components/ui/field.tsx +24 -0
  63. package/templates/nextjs/src/components/ui/index.ts +27 -0
  64. package/templates/nextjs/src/components/ui/input.tsx +23 -0
  65. package/templates/nextjs/src/components/ui/label.tsx +17 -0
  66. package/templates/nextjs/src/components/ui/skeleton.tsx +13 -0
  67. package/templates/nextjs/src/components/ui/table.tsx +94 -0
  68. package/templates/nextjs/src/components/ui/tabs.tsx +46 -0
  69. package/templates/nextjs/src/config/seo.config.ts +37 -0
  70. package/templates/nextjs/src/config/site.config.ts +15 -0
  71. package/templates/nextjs/src/constants/index.ts +5 -0
  72. package/templates/nextjs/src/env.ts +41 -0
  73. package/templates/nextjs/src/features/landing/api/get-users.api.ts +7 -0
  74. package/templates/nextjs/src/features/landing/api/index.ts +2 -0
  75. package/templates/nextjs/src/features/landing/api/query-keys.ts +7 -0
  76. package/templates/nextjs/src/features/landing/components/CartDemo.tsx +66 -0
  77. package/templates/nextjs/src/features/landing/components/Features.tsx +39 -0
  78. package/templates/nextjs/src/features/landing/components/Hero.tsx +65 -0
  79. package/templates/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
  80. package/templates/nextjs/src/features/landing/components/ProjectFormDemo.tsx +62 -0
  81. package/templates/nextjs/src/features/landing/components/Reveal.tsx +83 -0
  82. package/templates/nextjs/src/features/landing/components/Showcase.tsx +66 -0
  83. package/templates/nextjs/src/features/landing/components/UsersDemo.tsx +188 -0
  84. package/templates/nextjs/src/features/landing/data/features.data.tsx +47 -0
  85. package/templates/nextjs/src/features/landing/data/index.ts +1 -0
  86. package/templates/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
  87. package/templates/nextjs/src/features/landing/helpers/index.ts +7 -0
  88. package/templates/nextjs/src/features/landing/helpers/motion.ts +21 -0
  89. package/templates/nextjs/src/features/landing/hooks/index.ts +1 -0
  90. package/templates/nextjs/src/features/landing/hooks/use-users.ts +16 -0
  91. package/templates/nextjs/src/features/landing/icons/index.ts +6 -0
  92. package/templates/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
  93. package/templates/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
  94. package/templates/nextjs/src/features/landing/icons/react.tsx +20 -0
  95. package/templates/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
  96. package/templates/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
  97. package/templates/nextjs/src/features/landing/icons/typescript.tsx +16 -0
  98. package/templates/nextjs/src/features/landing/index.ts +1 -0
  99. package/templates/nextjs/src/features/landing/schemas/index.ts +1 -0
  100. package/templates/nextjs/src/features/landing/schemas/project.schema.ts +7 -0
  101. package/templates/nextjs/src/features/landing/stores/counter.store.ts +18 -0
  102. package/templates/nextjs/src/features/landing/stores/index.ts +1 -0
  103. package/templates/nextjs/src/features/landing/types/index.ts +1 -0
  104. package/templates/nextjs/src/features/landing/types/user.types.ts +9 -0
  105. package/templates/nextjs/src/helpers/format-date.ts +13 -0
  106. package/templates/nextjs/src/helpers/index.ts +1 -0
  107. package/templates/nextjs/src/hooks/index.ts +5 -0
  108. package/templates/nextjs/src/lib/api.ts +97 -0
  109. package/templates/nextjs/src/lib/query-client.ts +18 -0
  110. package/templates/nextjs/src/lib/utils.ts +12 -0
  111. package/templates/nextjs/src/providers/Providers.tsx +15 -0
  112. package/templates/nextjs/src/providers/QueryProvider.tsx +19 -0
  113. package/templates/nextjs/src/providers/ThemeProvider.tsx +9 -0
  114. package/templates/nextjs/src/providers/index.ts +1 -0
  115. package/templates/nextjs/src/proxy.ts +6 -0
  116. package/templates/nextjs/src/schemas/index.ts +5 -0
  117. package/templates/nextjs/src/stores/index.ts +5 -0
  118. package/templates/nextjs/src/styles/tailwind.css +122 -0
  119. package/templates/nextjs/src/types/site-config.types.ts +10 -0
  120. package/templates/nextjs/tsconfig.json +45 -0
  121. package/templates/tanstack/.changeset/README.md +8 -0
  122. package/templates/tanstack/.changeset/config.json +11 -0
  123. package/templates/tanstack/.env.example +6 -0
  124. package/templates/tanstack/.github/workflows/ci.yml +41 -0
  125. package/templates/tanstack/.husky/commit-msg +1 -0
  126. package/templates/tanstack/.husky/pre-commit +1 -0
  127. package/templates/tanstack/.husky/pre-push +1 -0
  128. package/templates/tanstack/.prettierignore +7 -0
  129. package/templates/tanstack/.prettierrc +41 -0
  130. package/templates/tanstack/.vscode/extensions.json +7 -0
  131. package/templates/tanstack/.vscode/settings.json +22 -0
  132. package/templates/tanstack/LICENSE +21 -0
  133. package/templates/tanstack/README.md +204 -0
  134. package/templates/tanstack/commitlint.config.js +6 -0
  135. package/templates/tanstack/eslint.config.js +28 -0
  136. package/templates/tanstack/knip.config.js +22 -0
  137. package/templates/tanstack/package.json +130 -0
  138. package/templates/tanstack/pnpm-lock.yaml +5835 -0
  139. package/templates/tanstack/pnpm-workspace.yaml +3 -0
  140. package/templates/tanstack/public/favicon.ico +0 -0
  141. package/templates/tanstack/public/llms.txt +86 -0
  142. package/templates/tanstack/public/logo.png +0 -0
  143. package/templates/tanstack/public/logo.webp +0 -0
  144. package/templates/tanstack/public/og.png +0 -0
  145. package/templates/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
  146. package/templates/tanstack/src/components/shared/NotFound.tsx +18 -0
  147. package/templates/tanstack/src/components/shared/Pending.tsx +13 -0
  148. package/templates/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
  149. package/templates/tanstack/src/components/ui/avatar.tsx +52 -0
  150. package/templates/tanstack/src/components/ui/button.tsx +47 -0
  151. package/templates/tanstack/src/components/ui/dialog.tsx +107 -0
  152. package/templates/tanstack/src/components/ui/field.tsx +24 -0
  153. package/templates/tanstack/src/components/ui/index.ts +27 -0
  154. package/templates/tanstack/src/components/ui/input.tsx +23 -0
  155. package/templates/tanstack/src/components/ui/label.tsx +17 -0
  156. package/templates/tanstack/src/components/ui/skeleton.tsx +13 -0
  157. package/templates/tanstack/src/components/ui/table.tsx +94 -0
  158. package/templates/tanstack/src/components/ui/tabs.tsx +46 -0
  159. package/templates/tanstack/src/config/seo.config.ts +52 -0
  160. package/templates/tanstack/src/config/site.config.ts +15 -0
  161. package/templates/tanstack/src/constants/index.ts +5 -0
  162. package/templates/tanstack/src/env.ts +47 -0
  163. package/templates/tanstack/src/features/landing/api/get-users.api.ts +7 -0
  164. package/templates/tanstack/src/features/landing/api/index.ts +2 -0
  165. package/templates/tanstack/src/features/landing/api/query-keys.ts +7 -0
  166. package/templates/tanstack/src/features/landing/components/CartDemo.tsx +66 -0
  167. package/templates/tanstack/src/features/landing/components/Features.tsx +39 -0
  168. package/templates/tanstack/src/features/landing/components/Hero.tsx +63 -0
  169. package/templates/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
  170. package/templates/tanstack/src/features/landing/components/ProjectFormDemo.tsx +63 -0
  171. package/templates/tanstack/src/features/landing/components/Reveal.tsx +83 -0
  172. package/templates/tanstack/src/features/landing/components/Showcase.tsx +66 -0
  173. package/templates/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
  174. package/templates/tanstack/src/features/landing/data/features.data.tsx +45 -0
  175. package/templates/tanstack/src/features/landing/data/index.ts +1 -0
  176. package/templates/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
  177. package/templates/tanstack/src/features/landing/helpers/index.ts +7 -0
  178. package/templates/tanstack/src/features/landing/helpers/motion.ts +21 -0
  179. package/templates/tanstack/src/features/landing/hooks/index.ts +1 -0
  180. package/templates/tanstack/src/features/landing/hooks/use-users.ts +19 -0
  181. package/templates/tanstack/src/features/landing/icons/index.ts +5 -0
  182. package/templates/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
  183. package/templates/tanstack/src/features/landing/icons/react.tsx +20 -0
  184. package/templates/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
  185. package/templates/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
  186. package/templates/tanstack/src/features/landing/icons/typescript.tsx +16 -0
  187. package/templates/tanstack/src/features/landing/index.ts +1 -0
  188. package/templates/tanstack/src/features/landing/schemas/index.ts +1 -0
  189. package/templates/tanstack/src/features/landing/schemas/project.schema.ts +7 -0
  190. package/templates/tanstack/src/features/landing/stores/counter.store.ts +18 -0
  191. package/templates/tanstack/src/features/landing/stores/index.ts +1 -0
  192. package/templates/tanstack/src/features/landing/types/index.ts +1 -0
  193. package/templates/tanstack/src/features/landing/types/user.types.ts +9 -0
  194. package/templates/tanstack/src/helpers/format-date.ts +13 -0
  195. package/templates/tanstack/src/helpers/index.ts +1 -0
  196. package/templates/tanstack/src/hooks/index.ts +5 -0
  197. package/templates/tanstack/src/lib/api.ts +97 -0
  198. package/templates/tanstack/src/lib/query-client.ts +18 -0
  199. package/templates/tanstack/src/lib/utils.ts +13 -0
  200. package/templates/tanstack/src/providers/Providers.tsx +12 -0
  201. package/templates/tanstack/src/providers/ThemeProvider.tsx +98 -0
  202. package/templates/tanstack/src/providers/devtools.tsx +6 -0
  203. package/templates/tanstack/src/providers/index.ts +1 -0
  204. package/templates/tanstack/src/providers/root-provider.ts +9 -0
  205. package/templates/tanstack/src/routeTree.gen.ts +123 -0
  206. package/templates/tanstack/src/router.tsx +27 -0
  207. package/templates/tanstack/src/routes/__root.tsx +78 -0
  208. package/templates/tanstack/src/routes/index.tsx +13 -0
  209. package/templates/tanstack/src/routes/robots[.]txt.ts +21 -0
  210. package/templates/tanstack/src/routes/site[.]webmanifest.ts +32 -0
  211. package/templates/tanstack/src/routes/sitemap[.]xml.ts +27 -0
  212. package/templates/tanstack/src/schemas/index.ts +5 -0
  213. package/templates/tanstack/src/start.ts +20 -0
  214. package/templates/tanstack/src/stores/index.ts +5 -0
  215. package/templates/tanstack/src/styles/tailwind.css +123 -0
  216. package/templates/tanstack/src/types/site-config.types.ts +10 -0
  217. package/templates/tanstack/tsconfig.json +25 -0
  218. package/templates/tanstack/tsr.config.json +3 -0
  219. package/templates/tanstack/vite.config.ts +12 -0
  220. package/templates-i18n/nextjs/.changeset/README.md +8 -0
  221. package/templates-i18n/nextjs/.changeset/config.json +11 -0
  222. package/templates-i18n/nextjs/.env.example +6 -0
  223. package/templates-i18n/nextjs/.github/workflows/ci.yml +41 -0
  224. package/templates-i18n/nextjs/.husky/commit-msg +1 -0
  225. package/templates-i18n/nextjs/.husky/pre-commit +1 -0
  226. package/templates-i18n/nextjs/.husky/pre-push +1 -0
  227. package/templates-i18n/nextjs/.prettierignore +6 -0
  228. package/templates-i18n/nextjs/.prettierrc +42 -0
  229. package/templates-i18n/nextjs/.vscode/extensions.json +7 -0
  230. package/templates-i18n/nextjs/.vscode/settings.json +20 -0
  231. package/templates-i18n/nextjs/LICENSE +21 -0
  232. package/templates-i18n/nextjs/README.md +202 -0
  233. package/templates-i18n/nextjs/commitlint.config.js +6 -0
  234. package/templates-i18n/nextjs/eslint.config.mjs +27 -0
  235. package/templates-i18n/nextjs/knip.config.js +29 -0
  236. package/templates-i18n/nextjs/next.config.ts +42 -0
  237. package/templates-i18n/nextjs/package.json +117 -0
  238. package/templates-i18n/nextjs/pnpm-lock.yaml +6854 -0
  239. package/templates-i18n/nextjs/pnpm-workspace.yaml +3 -0
  240. package/templates-i18n/nextjs/postcss.config.mjs +7 -0
  241. package/templates-i18n/nextjs/public/favicon.ico +0 -0
  242. package/templates-i18n/nextjs/public/llms.txt +96 -0
  243. package/templates-i18n/nextjs/public/logo.png +0 -0
  244. package/templates-i18n/nextjs/public/logo.webp +0 -0
  245. package/templates-i18n/nextjs/public/og.png +0 -0
  246. package/templates-i18n/nextjs/src/app/[locale]/error.tsx +34 -0
  247. package/templates-i18n/nextjs/src/app/[locale]/layout.tsx +60 -0
  248. package/templates-i18n/nextjs/src/app/[locale]/loading.tsx +12 -0
  249. package/templates-i18n/nextjs/src/app/[locale]/not-found.tsx +18 -0
  250. package/templates-i18n/nextjs/src/app/[locale]/page.tsx +10 -0
  251. package/templates-i18n/nextjs/src/app/favicon.ico +0 -0
  252. package/templates-i18n/nextjs/src/app/global-error.tsx +32 -0
  253. package/templates-i18n/nextjs/src/app/manifest.ts +22 -0
  254. package/templates-i18n/nextjs/src/app/robots.ts +13 -0
  255. package/templates-i18n/nextjs/src/app/sitemap.ts +14 -0
  256. package/templates-i18n/nextjs/src/components/shared/LanguageSwitcher.tsx +48 -0
  257. package/templates-i18n/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
  258. package/templates-i18n/nextjs/src/components/ui/avatar.tsx +54 -0
  259. package/templates-i18n/nextjs/src/components/ui/button.tsx +46 -0
  260. package/templates-i18n/nextjs/src/components/ui/dialog.tsx +107 -0
  261. package/templates-i18n/nextjs/src/components/ui/field.tsx +24 -0
  262. package/templates-i18n/nextjs/src/components/ui/index.ts +27 -0
  263. package/templates-i18n/nextjs/src/components/ui/input.tsx +23 -0
  264. package/templates-i18n/nextjs/src/components/ui/label.tsx +17 -0
  265. package/templates-i18n/nextjs/src/components/ui/skeleton.tsx +13 -0
  266. package/templates-i18n/nextjs/src/components/ui/table.tsx +94 -0
  267. package/templates-i18n/nextjs/src/components/ui/tabs.tsx +46 -0
  268. package/templates-i18n/nextjs/src/config/seo.config.ts +37 -0
  269. package/templates-i18n/nextjs/src/config/site.config.ts +15 -0
  270. package/templates-i18n/nextjs/src/constants/index.ts +5 -0
  271. package/templates-i18n/nextjs/src/env.ts +41 -0
  272. package/templates-i18n/nextjs/src/features/landing/api/get-users.api.ts +7 -0
  273. package/templates-i18n/nextjs/src/features/landing/api/index.ts +2 -0
  274. package/templates-i18n/nextjs/src/features/landing/api/query-keys.ts +7 -0
  275. package/templates-i18n/nextjs/src/features/landing/components/CartDemo.tsx +68 -0
  276. package/templates-i18n/nextjs/src/features/landing/components/Features.tsx +84 -0
  277. package/templates-i18n/nextjs/src/features/landing/components/Hero.tsx +76 -0
  278. package/templates-i18n/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
  279. package/templates-i18n/nextjs/src/features/landing/components/ProjectFormDemo.tsx +64 -0
  280. package/templates-i18n/nextjs/src/features/landing/components/Reveal.tsx +83 -0
  281. package/templates-i18n/nextjs/src/features/landing/components/Showcase.tsx +83 -0
  282. package/templates-i18n/nextjs/src/features/landing/components/UsersDemo.tsx +191 -0
  283. package/templates-i18n/nextjs/src/features/landing/data/features.data.tsx +47 -0
  284. package/templates-i18n/nextjs/src/features/landing/data/index.ts +1 -0
  285. package/templates-i18n/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
  286. package/templates-i18n/nextjs/src/features/landing/helpers/index.ts +7 -0
  287. package/templates-i18n/nextjs/src/features/landing/helpers/motion.ts +21 -0
  288. package/templates-i18n/nextjs/src/features/landing/hooks/index.ts +1 -0
  289. package/templates-i18n/nextjs/src/features/landing/hooks/use-users.ts +16 -0
  290. package/templates-i18n/nextjs/src/features/landing/icons/index.ts +6 -0
  291. package/templates-i18n/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
  292. package/templates-i18n/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
  293. package/templates-i18n/nextjs/src/features/landing/icons/react.tsx +20 -0
  294. package/templates-i18n/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
  295. package/templates-i18n/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
  296. package/templates-i18n/nextjs/src/features/landing/icons/typescript.tsx +16 -0
  297. package/templates-i18n/nextjs/src/features/landing/index.ts +1 -0
  298. package/templates-i18n/nextjs/src/features/landing/schemas/index.ts +1 -0
  299. package/templates-i18n/nextjs/src/features/landing/schemas/project.schema.ts +9 -0
  300. package/templates-i18n/nextjs/src/features/landing/stores/counter.store.ts +18 -0
  301. package/templates-i18n/nextjs/src/features/landing/stores/index.ts +1 -0
  302. package/templates-i18n/nextjs/src/features/landing/types/index.ts +1 -0
  303. package/templates-i18n/nextjs/src/features/landing/types/user.types.ts +9 -0
  304. package/templates-i18n/nextjs/src/helpers/format-date.ts +13 -0
  305. package/templates-i18n/nextjs/src/helpers/index.ts +1 -0
  306. package/templates-i18n/nextjs/src/hooks/index.ts +5 -0
  307. package/templates-i18n/nextjs/src/i18n/request.ts +16 -0
  308. package/templates-i18n/nextjs/src/i18n/routing.ts +11 -0
  309. package/templates-i18n/nextjs/src/lib/api.ts +97 -0
  310. package/templates-i18n/nextjs/src/lib/query-client.ts +18 -0
  311. package/templates-i18n/nextjs/src/lib/utils.ts +12 -0
  312. package/templates-i18n/nextjs/src/messages/en.json +55 -0
  313. package/templates-i18n/nextjs/src/messages/tr.json +55 -0
  314. package/templates-i18n/nextjs/src/providers/Providers.tsx +15 -0
  315. package/templates-i18n/nextjs/src/providers/QueryProvider.tsx +19 -0
  316. package/templates-i18n/nextjs/src/providers/ThemeProvider.tsx +9 -0
  317. package/templates-i18n/nextjs/src/providers/index.ts +1 -0
  318. package/templates-i18n/nextjs/src/proxy.ts +16 -0
  319. package/templates-i18n/nextjs/src/schemas/index.ts +5 -0
  320. package/templates-i18n/nextjs/src/stores/index.ts +5 -0
  321. package/templates-i18n/nextjs/src/styles/tailwind.css +122 -0
  322. package/templates-i18n/nextjs/src/types/site-config.types.ts +10 -0
  323. package/templates-i18n/nextjs/tsconfig.json +45 -0
  324. package/templates-i18n/tanstack/.changeset/README.md +8 -0
  325. package/templates-i18n/tanstack/.changeset/config.json +11 -0
  326. package/templates-i18n/tanstack/.env.example +6 -0
  327. package/templates-i18n/tanstack/.github/workflows/ci.yml +41 -0
  328. package/templates-i18n/tanstack/.husky/commit-msg +1 -0
  329. package/templates-i18n/tanstack/.husky/pre-commit +1 -0
  330. package/templates-i18n/tanstack/.husky/pre-push +1 -0
  331. package/templates-i18n/tanstack/.prettierignore +7 -0
  332. package/templates-i18n/tanstack/.prettierrc +41 -0
  333. package/templates-i18n/tanstack/.vscode/extensions.json +7 -0
  334. package/templates-i18n/tanstack/.vscode/settings.json +22 -0
  335. package/templates-i18n/tanstack/LICENSE +21 -0
  336. package/templates-i18n/tanstack/README.md +204 -0
  337. package/templates-i18n/tanstack/commitlint.config.js +6 -0
  338. package/templates-i18n/tanstack/eslint.config.js +29 -0
  339. package/templates-i18n/tanstack/knip.config.js +26 -0
  340. package/templates-i18n/tanstack/messages/en.json +65 -0
  341. package/templates-i18n/tanstack/messages/tr.json +65 -0
  342. package/templates-i18n/tanstack/package.json +132 -0
  343. package/templates-i18n/tanstack/pnpm-lock.yaml +5998 -0
  344. package/templates-i18n/tanstack/pnpm-workspace.yaml +3 -0
  345. package/templates-i18n/tanstack/project.inlang/README.md +157 -0
  346. package/templates-i18n/tanstack/project.inlang/settings.json +9 -0
  347. package/templates-i18n/tanstack/public/favicon.ico +0 -0
  348. package/templates-i18n/tanstack/public/llms.txt +86 -0
  349. package/templates-i18n/tanstack/public/logo.png +0 -0
  350. package/templates-i18n/tanstack/public/logo.webp +0 -0
  351. package/templates-i18n/tanstack/public/og.png +0 -0
  352. package/templates-i18n/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
  353. package/templates-i18n/tanstack/src/components/shared/LanguageSwitcher.tsx +57 -0
  354. package/templates-i18n/tanstack/src/components/shared/NotFound.tsx +18 -0
  355. package/templates-i18n/tanstack/src/components/shared/Pending.tsx +13 -0
  356. package/templates-i18n/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
  357. package/templates-i18n/tanstack/src/components/ui/avatar.tsx +52 -0
  358. package/templates-i18n/tanstack/src/components/ui/button.tsx +47 -0
  359. package/templates-i18n/tanstack/src/components/ui/dialog.tsx +107 -0
  360. package/templates-i18n/tanstack/src/components/ui/field.tsx +24 -0
  361. package/templates-i18n/tanstack/src/components/ui/index.ts +27 -0
  362. package/templates-i18n/tanstack/src/components/ui/input.tsx +23 -0
  363. package/templates-i18n/tanstack/src/components/ui/label.tsx +17 -0
  364. package/templates-i18n/tanstack/src/components/ui/skeleton.tsx +13 -0
  365. package/templates-i18n/tanstack/src/components/ui/table.tsx +94 -0
  366. package/templates-i18n/tanstack/src/components/ui/tabs.tsx +46 -0
  367. package/templates-i18n/tanstack/src/config/seo.config.ts +52 -0
  368. package/templates-i18n/tanstack/src/config/site.config.ts +15 -0
  369. package/templates-i18n/tanstack/src/constants/index.ts +5 -0
  370. package/templates-i18n/tanstack/src/env.ts +47 -0
  371. package/templates-i18n/tanstack/src/features/landing/api/get-users.api.ts +7 -0
  372. package/templates-i18n/tanstack/src/features/landing/api/index.ts +2 -0
  373. package/templates-i18n/tanstack/src/features/landing/api/query-keys.ts +7 -0
  374. package/templates-i18n/tanstack/src/features/landing/components/CartDemo.tsx +67 -0
  375. package/templates-i18n/tanstack/src/features/landing/components/Features.tsx +79 -0
  376. package/templates-i18n/tanstack/src/features/landing/components/Hero.tsx +68 -0
  377. package/templates-i18n/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
  378. package/templates-i18n/tanstack/src/features/landing/components/ProjectFormDemo.tsx +64 -0
  379. package/templates-i18n/tanstack/src/features/landing/components/Reveal.tsx +83 -0
  380. package/templates-i18n/tanstack/src/features/landing/components/Showcase.tsx +81 -0
  381. package/templates-i18n/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
  382. package/templates-i18n/tanstack/src/features/landing/data/features.data.tsx +45 -0
  383. package/templates-i18n/tanstack/src/features/landing/data/index.ts +1 -0
  384. package/templates-i18n/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
  385. package/templates-i18n/tanstack/src/features/landing/helpers/index.ts +7 -0
  386. package/templates-i18n/tanstack/src/features/landing/helpers/motion.ts +21 -0
  387. package/templates-i18n/tanstack/src/features/landing/hooks/index.ts +1 -0
  388. package/templates-i18n/tanstack/src/features/landing/hooks/use-users.ts +19 -0
  389. package/templates-i18n/tanstack/src/features/landing/icons/index.ts +5 -0
  390. package/templates-i18n/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
  391. package/templates-i18n/tanstack/src/features/landing/icons/react.tsx +20 -0
  392. package/templates-i18n/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
  393. package/templates-i18n/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
  394. package/templates-i18n/tanstack/src/features/landing/icons/typescript.tsx +16 -0
  395. package/templates-i18n/tanstack/src/features/landing/index.ts +1 -0
  396. package/templates-i18n/tanstack/src/features/landing/schemas/index.ts +1 -0
  397. package/templates-i18n/tanstack/src/features/landing/schemas/project.schema.ts +10 -0
  398. package/templates-i18n/tanstack/src/features/landing/stores/counter.store.ts +18 -0
  399. package/templates-i18n/tanstack/src/features/landing/stores/index.ts +1 -0
  400. package/templates-i18n/tanstack/src/features/landing/types/index.ts +1 -0
  401. package/templates-i18n/tanstack/src/features/landing/types/user.types.ts +9 -0
  402. package/templates-i18n/tanstack/src/helpers/format-date.ts +13 -0
  403. package/templates-i18n/tanstack/src/helpers/index.ts +1 -0
  404. package/templates-i18n/tanstack/src/hooks/index.ts +5 -0
  405. package/templates-i18n/tanstack/src/lib/api.ts +97 -0
  406. package/templates-i18n/tanstack/src/lib/query-client.ts +18 -0
  407. package/templates-i18n/tanstack/src/lib/utils.ts +13 -0
  408. package/templates-i18n/tanstack/src/paraglide/README.md +162 -0
  409. package/templates-i18n/tanstack/src/providers/Providers.tsx +12 -0
  410. package/templates-i18n/tanstack/src/providers/ThemeProvider.tsx +98 -0
  411. package/templates-i18n/tanstack/src/providers/devtools.tsx +6 -0
  412. package/templates-i18n/tanstack/src/providers/index.ts +1 -0
  413. package/templates-i18n/tanstack/src/providers/root-provider.ts +9 -0
  414. package/templates-i18n/tanstack/src/routeTree.gen.ts +179 -0
  415. package/templates-i18n/tanstack/src/router.tsx +27 -0
  416. package/templates-i18n/tanstack/src/routes/$lang/index.tsx +13 -0
  417. package/templates-i18n/tanstack/src/routes/$lang.tsx +22 -0
  418. package/templates-i18n/tanstack/src/routes/__root.tsx +80 -0
  419. package/templates-i18n/tanstack/src/routes/index.tsx +13 -0
  420. package/templates-i18n/tanstack/src/routes/robots[.]txt.ts +21 -0
  421. package/templates-i18n/tanstack/src/routes/site[.]webmanifest.ts +32 -0
  422. package/templates-i18n/tanstack/src/routes/sitemap[.]xml.ts +27 -0
  423. package/templates-i18n/tanstack/src/schemas/index.ts +5 -0
  424. package/templates-i18n/tanstack/src/server.ts +10 -0
  425. package/templates-i18n/tanstack/src/start.ts +20 -0
  426. package/templates-i18n/tanstack/src/stores/index.ts +5 -0
  427. package/templates-i18n/tanstack/src/styles/tailwind.css +123 -0
  428. package/templates-i18n/tanstack/src/types/site-config.types.ts +10 -0
  429. package/templates-i18n/tanstack/tsconfig.json +26 -0
  430. package/templates-i18n/tanstack/tsr.config.json +3 -0
  431. package/templates-i18n/tanstack/vite.config.ts +31 -0
  432. package/index.js +0 -3
  433. package/public/start.jpg +0 -0
package/src/i18n.js ADDED
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Minimal i18n dictionary for the ViraStack Start.
3
+ * Default locale is English; pass --tr to switch to Turkish.
4
+ */
5
+
6
+ const dictionaries = {
7
+ en: {
8
+ cancel: "Operation cancelled.",
9
+ "telemetry.notice":
10
+ "ViraStack collects anonymous usage data (template, i18n choice, selected tools) to improve the CLI. No personal data is ever collected. Disable anytime with --telemetry-disable.",
11
+
12
+ "projectName.message": "What is your project named?",
13
+ "projectName.placeholder": "virastack-app",
14
+ "projectName.errorEmpty":
15
+ 'Please enter a project name, or "." to use the current directory.',
16
+ "projectName.errorInvalid":
17
+ "Please enter a valid npm package name (lowercase letters, numbers, dashes).",
18
+ "projectName.errorNonInteractive":
19
+ "Project name is required in non-interactive mode. Pass a name, use --name, or \".\".",
20
+ "projectName.confirmNonEmptyDir":
21
+ 'Directory "{dir}" is not empty. Continue anyway?',
22
+
23
+ "template.message": "Which template would you like to use?",
24
+ "template.nextjs.label": "Next.js App Router",
25
+ "template.nextjs.hint": "Recommended",
26
+ "template.tanstack.label": "TanStack Start",
27
+ "template.tanstack.hint": "File-based routing + SSR",
28
+ "template.tanstack.notReady":
29
+ "The TanStack Start template isn't available in this install. Using Next.js instead.",
30
+ "template.errorInvalid":
31
+ 'Unknown template "{template}". Available templates: nextjs, tanstack.',
32
+
33
+ "i18n.message": "Would you like multi-language (i18n) support?",
34
+ "i18n.active": "Yes",
35
+ "i18n.inactive": "No",
36
+ "i18n.notReady":
37
+ "The multi-language (i18n) template isn't ready for this stack yet. Using the standard template instead.",
38
+
39
+ "tools.message": "Which ViraStack tools would you like to include?",
40
+ "tools.mask.label": "@virastack/mask",
41
+ "tools.mask.hint": "Headless input formatter",
42
+ "tools.password.label": "@virastack/password",
43
+ "tools.password.hint": "Password visibility toggle",
44
+ "tools.ai.label": "@virastack/ai",
45
+ "tools.ai.hint": "AI rules for Cursor & Claude",
46
+
47
+ "scaffold.copying": "Creating project files...",
48
+ "scaffold.copyDone": "Project files created.",
49
+ "scaffold.gitInit": "Initializing git repository...",
50
+ "scaffold.gitInitDone": "Git repository initialized.",
51
+ "scaffold.gitInitSkipped": "Skipped git initialization.",
52
+ "scaffold.installing": "Installing dependencies with {pm}...",
53
+ "scaffold.installDone": "Dependencies installed.",
54
+ "scaffold.installFailed":
55
+ "Failed to install dependencies.",
56
+ "scaffold.skipInstall":
57
+ "Skipping dependency install (--skip-install).",
58
+ "scaffold.offline":
59
+ "You appear to be offline. Skipping dependency install and AI setup — run them manually when you're back online.",
60
+ "scaffold.aiInit": "Configuring ViraStack AI rules...",
61
+ "scaffold.aiInitDone": "🤖 ViraStack AI agent rules successfully integrated.",
62
+ "scaffold.aiInitFailed":
63
+ "Failed to configure ViraStack AI rules.",
64
+ "scaffold.aiInitSkipped":
65
+ "Skipping AI setup because dependency install failed.",
66
+ "scaffold.skillsInitFailed":
67
+ "Design skills could not be installed. Run manually: npx skills@latest add emilkowalski/skills && npx skills@latest add jakubkrehel/make-interfaces-feel-better",
68
+
69
+ "done.title": "You're all set!",
70
+ "done.partialTitle": "Project files are ready — finish setup manually:",
71
+ "done.installFailedNote":
72
+ "Dependency install failed. The project was created, but you need to install packages before running it.",
73
+ "done.cd": " cd {dir}",
74
+ "done.manualInstall": " {cmd}",
75
+ "done.manualAi": " {cmd}",
76
+ "done.dev": " {cmd}",
77
+
78
+ "add.toolMissing": "Please specify a tool to add. Available tools: {list}.",
79
+ "add.toolInvalid": 'Unknown tool "{tool}". Available tools: {list}.',
80
+ "add.notAProject": "No package.json found in the current directory.",
81
+ "add.installing": "Adding {pkg}...",
82
+ "add.done": "{pkg} added.",
83
+ "add.installFailed": "Failed to add {pkg}.",
84
+
85
+ help: `Usage:
86
+ pnpm dlx virastack [init] [name] Scaffold a new project
87
+ pnpm dlx virastack add <tool> Add a ViraStack tool to an existing project
88
+
89
+ Tools:
90
+ mask, password, ai
91
+
92
+ Options:
93
+ --name <name> Project name (or pass as positional arg / ".")
94
+ --template <name> Template: nextjs | tanstack
95
+ --tools <list> Comma-separated tools (e.g. mask,password)
96
+ --i18n / --no-i18n Enable or disable the i18n template
97
+ --yes, -y Non-interactive mode (use flags/defaults)
98
+ --skip-install Scaffold files only; skip install + AI setup
99
+ --tr Ask questions in Turkish
100
+ --telemetry-disable Disable anonymous usage tracking
101
+ -v, --version Print the version
102
+ -h, --help Show this help message
103
+
104
+ Examples:
105
+ pnpm dlx virastack
106
+ pnpm dlx virastack my-app --yes
107
+ pnpm dlx virastack init my-app --template nextjs --tools mask --yes`,
108
+ },
109
+
110
+ tr: {
111
+ cancel: "İşlem iptal edildi.",
112
+ "telemetry.notice":
113
+ "ViraStack, CLI'ı geliştirmek için anonim kullanım verisi toplar (şablon, i18n tercihi, seçilen araçlar). Hiçbir kişisel veri toplanmaz. --telemetry-disable ile her zaman kapatabilirsiniz.",
114
+
115
+ "projectName.message": "Projenizin adı ne olsun?",
116
+ "projectName.placeholder": "virastack-app",
117
+ "projectName.errorEmpty":
118
+ 'Lütfen bir proje adı girin, ya da mevcut dizini kullanmak için "." yazın.',
119
+ "projectName.errorInvalid":
120
+ "Lütfen geçerli bir npm paket adı girin (küçük harf, rakam, tire).",
121
+ "projectName.errorNonInteractive":
122
+ 'İnteraktif olmayan modda proje adı zorunlu. Bir ad, --name veya "." verin.',
123
+ "projectName.confirmNonEmptyDir":
124
+ '"{dir}" dizini boş değil. Devam edilsin mi?',
125
+
126
+ "template.message": "Hangi şablonla başlamak istiyorsunuz?",
127
+ "template.nextjs.label": "Next.js App Router",
128
+ "template.nextjs.hint": "Önerilen",
129
+ "template.tanstack.label": "TanStack Start",
130
+ "template.tanstack.hint": "Dosya tabanlı routing + SSR",
131
+ "template.tanstack.notReady":
132
+ "Bu kurulumda TanStack Start şablonu yok. Next.js kullanılacak.",
133
+ "template.errorInvalid":
134
+ 'Bilinmeyen şablon "{template}". Kullanılabilir şablonlar: nextjs, tanstack.',
135
+
136
+ "i18n.message": "Çoklu dil (i18n) desteği ister misiniz?",
137
+ "i18n.active": "Evet",
138
+ "i18n.inactive": "Hayır",
139
+ "i18n.notReady":
140
+ "Bu şablon için çoklu dil (i18n) sürümü henüz hazır değil. Standart şablon kullanılacak.",
141
+
142
+ "tools.message": "Projenize hangi ViraStack araçları eklensin?",
143
+ "tools.mask.label": "@virastack/mask",
144
+ "tools.mask.hint": "Headless input formatlayıcı",
145
+ "tools.password.label": "@virastack/password",
146
+ "tools.password.hint": "Şifre görünürlük anahtarı",
147
+ "tools.ai.label": "@virastack/ai",
148
+ "tools.ai.hint": "Cursor & Claude için AI kuralları",
149
+
150
+ "scaffold.copying": "Proje dosyaları oluşturuluyor...",
151
+ "scaffold.copyDone": "Proje dosyaları oluşturuldu.",
152
+ "scaffold.gitInit": "Git deposu başlatılıyor...",
153
+ "scaffold.gitInitDone": "Git deposu başlatıldı.",
154
+ "scaffold.gitInitSkipped": "Git başlatma adımı atlandı.",
155
+ "scaffold.installing": "Bağımlılıklar {pm} ile kuruluyor...",
156
+ "scaffold.installDone": "Bağımlılıklar kuruldu.",
157
+ "scaffold.installFailed":
158
+ "Bağımlılıklar kurulamadı.",
159
+ "scaffold.skipInstall":
160
+ "Bağımlılık kurulumu atlanıyor (--skip-install).",
161
+ "scaffold.offline":
162
+ "İnternet bağlantısı yok gibi görünüyor. Bağımlılık kurulumu ve AI yapılandırması atlandı — çevrimiçi olduğunuzda manuel çalıştırın.",
163
+ "scaffold.aiInit": "ViraStack AI kuralları yapılandırılıyor...",
164
+ "scaffold.aiInitDone": "🤖 ViraStack AI ajan kuralları projeye başarıyla entegre edildi.",
165
+ "scaffold.aiInitFailed":
166
+ "ViraStack AI kuralları yapılandırılamadı.",
167
+ "scaffold.aiInitSkipped":
168
+ "Bağımlılık kurulumu başarısız olduğu için AI yapılandırması atlandı.",
169
+ "scaffold.skillsInitFailed":
170
+ "Tasarım skill'leri kurulamadı. Manuel çalıştırın: npx skills@latest add emilkowalski/skills && npx skills@latest add jakubkrehel/make-interfaces-feel-better",
171
+
172
+ "done.title": "Her şey hazır!",
173
+ "done.partialTitle": "Proje dosyaları hazır — kurulumu manuel tamamlayın:",
174
+ "done.installFailedNote":
175
+ "Bağımlılık kurulumu başarısız. Proje oluşturuldu, ancak çalıştırmadan önce paketleri kurmanız gerekiyor.",
176
+ "done.cd": " cd {dir}",
177
+ "done.manualInstall": " {cmd}",
178
+ "done.manualAi": " {cmd}",
179
+ "done.dev": " {cmd}",
180
+
181
+ "add.toolMissing":
182
+ "Lütfen eklemek istediğiniz aracı belirtin. Kullanılabilir araçlar: {list}.",
183
+ "add.toolInvalid": '"{tool}" bilinmeyen bir araç. Kullanılabilir araçlar: {list}.',
184
+ "add.notAProject": "Bu dizinde package.json bulunamadı.",
185
+ "add.installing": "{pkg} ekleniyor...",
186
+ "add.done": "{pkg} eklendi.",
187
+ "add.installFailed": "{pkg} eklenemedi.",
188
+
189
+ help: `Kullanım:
190
+ pnpm dlx virastack [init] [ad] Yeni bir proje oluştur
191
+ pnpm dlx virastack add <araç> Mevcut projeye bir ViraStack aracı ekle
192
+
193
+ Araçlar:
194
+ mask, password, ai
195
+
196
+ Seçenekler:
197
+ --name <ad> Proje adı (veya positional arg / ".")
198
+ --template <ad> Şablon: nextjs | tanstack
199
+ --tools <liste> Virgülle ayrılmış araçlar (örn. mask,password)
200
+ --i18n / --no-i18n i18n şablonunu aç / kapat
201
+ --yes, -y İnteraktif olmayan mod (flag/varsayılanlar)
202
+ --skip-install Sadece dosyaları oluştur; kurulum + AI atla
203
+ --tr Soruları Türkçe sor
204
+ --telemetry-disable Anonim kullanım verisi toplamayı kapat
205
+ -v, --version Sürümü göster
206
+ -h, --help Bu yardım mesajını göster
207
+
208
+ Örnekler:
209
+ pnpm dlx virastack
210
+ pnpm dlx virastack my-app --yes
211
+ pnpm dlx virastack init my-app --template nextjs --tools mask --yes`,
212
+ },
213
+ };
214
+
215
+ let currentLocale = "en";
216
+
217
+ export function setLocale(locale) {
218
+ currentLocale = dictionaries[locale] ? locale : "en";
219
+ }
220
+
221
+ export function getLocale() {
222
+ return currentLocale;
223
+ }
224
+
225
+ export function t(key, vars = {}) {
226
+ const dictionary = dictionaries[currentLocale] ?? dictionaries.en;
227
+ const template = dictionary[key] ?? dictionaries.en[key] ?? key;
228
+ return template.replace(/\{(\w+)\}/g, (_, name) =>
229
+ name in vars ? String(vars[name]) : `{${name}}`,
230
+ );
231
+ }
package/src/index.js ADDED
@@ -0,0 +1,64 @@
1
+ import { getFlag, hasFlag, parseArgs } from "./utils/args.js";
2
+ import { getCliVersion } from "./utils/package-info.js";
3
+ import { disableTelemetry } from "./utils/telemetry.js";
4
+ import { setLocale, t } from "./i18n.js";
5
+ import { runInit } from "./commands/init.js";
6
+ import { runAdd } from "./commands/add.js";
7
+ import { parseToolsFlag } from "./utils/tools.js";
8
+
9
+ function resolveI18nFlag(flags) {
10
+ if (hasFlag(flags, "no-i18n")) return false;
11
+ if (hasFlag(flags, "i18n")) return true;
12
+ return undefined;
13
+ }
14
+
15
+ function buildInitOptions(flags, projectName) {
16
+ const nameFlag = getFlag(flags, "name");
17
+ const template = getFlag(flags, "template");
18
+ const tools = getFlag(flags, "tools");
19
+
20
+ return {
21
+ name: projectName ?? (typeof nameFlag === "string" ? nameFlag : undefined),
22
+ template: typeof template === "string" ? template : undefined,
23
+ i18n: resolveI18nFlag(flags),
24
+ tools: tools === undefined ? undefined : parseToolsFlag(tools),
25
+ yes: hasFlag(flags, "yes", "y"),
26
+ skipInstall: hasFlag(flags, "skip-install"),
27
+ };
28
+ }
29
+
30
+ export async function main() {
31
+ const { positionals, flags } = parseArgs(process.argv);
32
+ setLocale(hasFlag(flags, "tr") ? "tr" : "en");
33
+
34
+ if (hasFlag(flags, "telemetry-disable")) {
35
+ await disableTelemetry();
36
+ }
37
+
38
+ if (hasFlag(flags, "v", "version")) {
39
+ console.log(getCliVersion());
40
+ return;
41
+ }
42
+
43
+ if (hasFlag(flags, "h", "help")) {
44
+ console.log(t("help"));
45
+ return;
46
+ }
47
+
48
+ const [command, ...rest] = positionals;
49
+
50
+ switch (command) {
51
+ case undefined:
52
+ await runInit(buildInitOptions(flags));
53
+ break;
54
+ case "init":
55
+ await runInit(buildInitOptions(flags, rest[0]));
56
+ break;
57
+ case "add":
58
+ await runAdd(rest[0]);
59
+ break;
60
+ default:
61
+ // `pnpm dlx virastack my-app --yes` — first token is the project name.
62
+ await runInit(buildInitOptions(flags, command));
63
+ }
64
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Tiny argv parser: splits flags ("--foo", "-f", "--key=value") from
3
+ * positional args. Kept dependency-free since the CLI surface is small.
4
+ *
5
+ * Value-taking long flags consume the next token when it is not itself a flag.
6
+ */
7
+
8
+ const VALUE_FLAGS = new Set(["name", "template", "tools"]);
9
+
10
+ /**
11
+ * @param {string[]} argv
12
+ * @returns {{ positionals: string[], flags: Map<string, string | true> }}
13
+ */
14
+ export function parseArgs(argv) {
15
+ const args = argv.slice(2);
16
+ const flags = new Map();
17
+ const positionals = [];
18
+
19
+ for (let i = 0; i < args.length; i += 1) {
20
+ const arg = args[i];
21
+
22
+ if (arg.startsWith("--") && arg.length > 2) {
23
+ const body = arg.slice(2);
24
+ const eq = body.indexOf("=");
25
+
26
+ if (eq !== -1) {
27
+ flags.set(body.slice(0, eq), body.slice(eq + 1));
28
+ continue;
29
+ }
30
+
31
+ if (VALUE_FLAGS.has(body) && i + 1 < args.length && !args[i + 1].startsWith("-")) {
32
+ flags.set(body, args[i + 1]);
33
+ i += 1;
34
+ continue;
35
+ }
36
+
37
+ flags.set(body, true);
38
+ continue;
39
+ }
40
+
41
+ if (arg.startsWith("-") && arg.length > 1) {
42
+ for (const ch of arg.slice(1)) {
43
+ flags.set(ch, true);
44
+ }
45
+ continue;
46
+ }
47
+
48
+ positionals.push(arg);
49
+ }
50
+
51
+ return { positionals, flags };
52
+ }
53
+
54
+ export function hasFlag(flags, ...names) {
55
+ return names.some((name) => flags.has(name));
56
+ }
57
+
58
+ export function getFlag(flags, name) {
59
+ return flags.get(name);
60
+ }
@@ -0,0 +1,80 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ // Lockfiles are excluded on purpose: we run a fresh install with whichever
5
+ // package manager the user invoked the CLI with, and a stale lockfile from
6
+ // the template (always pnpm) would otherwise conflict with npm/yarn/bun.
7
+ const IGNORED_ENTRIES = new Set([
8
+ ".git",
9
+ "node_modules",
10
+ ".next",
11
+ ".tanstack",
12
+ ".output",
13
+ ".nitro",
14
+ ".wrangler",
15
+ ".vinxi",
16
+ "out",
17
+ "dist",
18
+ "build",
19
+ "coverage",
20
+ ".vercel",
21
+ ".turbo",
22
+ ".eslintcache",
23
+ ".knip",
24
+ ".DS_Store",
25
+ "Thumbs.db",
26
+ "pnpm-lock.yaml",
27
+ "package-lock.json",
28
+ "yarn.lock",
29
+ "bun.lock",
30
+ "bun.lockb",
31
+ "skills-lock.json",
32
+ ".gitkeep",
33
+ "assets",
34
+ ]);
35
+
36
+ export function shouldIgnore(name) {
37
+ if (IGNORED_ENTRIES.has(name)) return true;
38
+ // Keep .env.example; skip local/secret env files and editor dumps.
39
+ if (name.startsWith(".env") && name !== ".env.example") return true;
40
+ if (name.endsWith(".tsbuildinfo")) return true;
41
+ if (name.endsWith(".log")) return true;
42
+ return false;
43
+ }
44
+
45
+ export async function copyTemplate(srcDir, destDir) {
46
+ await fs.mkdir(destDir, { recursive: true });
47
+ const entries = await fs.readdir(srcDir, { withFileTypes: true });
48
+
49
+ for (const entry of entries) {
50
+ if (shouldIgnore(entry.name)) continue;
51
+
52
+ const srcPath = path.join(srcDir, entry.name);
53
+ const destPath = path.join(destDir, entry.name);
54
+
55
+ if (entry.isDirectory()) {
56
+ await copyTemplate(srcPath, destPath);
57
+ } else {
58
+ await fs.copyFile(srcPath, destPath);
59
+ }
60
+ }
61
+ }
62
+
63
+ export async function dirHasFiles(dir) {
64
+ try {
65
+ const entries = await fs.readdir(dir);
66
+ return entries.some((entry) => entry !== ".gitkeep");
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+
72
+ export async function isDirEmpty(dir) {
73
+ try {
74
+ const entries = await fs.readdir(dir);
75
+ return entries.length === 0;
76
+ } catch {
77
+ // Directory doesn't exist yet — treat as empty, we'll create it.
78
+ return true;
79
+ }
80
+ }
@@ -0,0 +1,29 @@
1
+ import spawn from "cross-spawn";
2
+
3
+ /**
4
+ * Runs a command and resolves once it exits successfully, rejecting otherwise.
5
+ * Uses cross-spawn so package manager shims (npm.cmd, pnpm.cmd, ...) resolve on Windows too.
6
+ */
7
+ export function runCommand(command, args = [], options = {}) {
8
+ return new Promise((resolve, reject) => {
9
+ const child = spawn(command, args, { stdio: "ignore", ...options });
10
+
11
+ child.on("error", reject);
12
+ child.on("close", (code) => {
13
+ if (code === 0) {
14
+ resolve();
15
+ } else {
16
+ reject(new Error(`"${command} ${args.join(" ")}" exited with code ${code}`));
17
+ }
18
+ });
19
+ });
20
+ }
21
+
22
+ export async function commandExists(command) {
23
+ try {
24
+ await runCommand(command, ["--version"]);
25
+ return true;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
@@ -0,0 +1,38 @@
1
+ import { runCommand, commandExists } from "./exec.js";
2
+
3
+ export async function isInsideGitRepo(dir) {
4
+ try {
5
+ await runCommand("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dir });
6
+ return true;
7
+ } catch {
8
+ return false;
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Best-effort git init + initial commit. Never throws: a missing git binary
14
+ * or an already-initialized repo (e.g. scaffolding into a monorepo) should
15
+ * not fail the whole scaffold. Returns whether a new repo was created.
16
+ */
17
+ export async function initGitRepo(dir) {
18
+ if (!(await commandExists("git"))) return false;
19
+ if (await isInsideGitRepo(dir)) return false;
20
+
21
+ try {
22
+ await runCommand("git", ["init"], { cwd: dir });
23
+ await runCommand("git", ["add", "-A"], { cwd: dir });
24
+ await runCommand("git", ["commit", "-m", "chore: initial commit from virastack"], {
25
+ cwd: dir,
26
+ env: {
27
+ ...process.env,
28
+ GIT_AUTHOR_NAME: "ViraStack Start",
29
+ GIT_AUTHOR_EMAIL: "noreply@virastack.com",
30
+ GIT_COMMITTER_NAME: "ViraStack Start",
31
+ GIT_COMMITTER_EMAIL: "noreply@virastack.com",
32
+ },
33
+ });
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
@@ -0,0 +1,14 @@
1
+ import dns from "node:dns/promises";
2
+
3
+ /**
4
+ * Best-effort connectivity check against the npm registry host.
5
+ * Used to skip install / network-dependent steps when offline.
6
+ */
7
+ export async function isOnline() {
8
+ try {
9
+ await dns.lookup("registry.npmjs.org");
10
+ return true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
@@ -0,0 +1,12 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const pkg = JSON.parse(
7
+ readFileSync(path.join(__dirname, "../../package.json"), "utf8"),
8
+ );
9
+
10
+ export function getCliVersion() {
11
+ return pkg.version;
12
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Detects the package manager the CLI was invoked with, mirroring the
3
+ * technique used by create-next-app and create-vite (npm_config_user_agent).
4
+ * We never ask the user for this — it keeps the interactive flow short.
5
+ */
6
+ export function detectPackageManager() {
7
+ const userAgent = process.env.npm_config_user_agent ?? "";
8
+
9
+ if (userAgent.startsWith("pnpm")) return "pnpm";
10
+ if (userAgent.startsWith("yarn")) return "yarn";
11
+ if (userAgent.startsWith("bun")) return "bun";
12
+ return "npm";
13
+ }
14
+
15
+ export function getInstallArgs(pm) {
16
+ return pm === "npm" ? ["install", "--legacy-peer-deps"] : ["install"];
17
+ }
18
+
19
+ export function getAddArgs(pm, packages) {
20
+ return pm === "npm" ? ["install", "--legacy-peer-deps", ...packages] : ["add", ...packages];
21
+ }
22
+
23
+ export function getInstallCommand(pm) {
24
+ return pm === "npm" ? "npm install" : `${pm} install`;
25
+ }
26
+
27
+ export function getRunDevCommand(pm) {
28
+ return pm === "npm" ? "npm run dev" : `${pm} dev`;
29
+ }
30
+
31
+ export function getAiInitCommand() {
32
+ return "npx --yes @virastack/ai init --force";
33
+ }
@@ -0,0 +1,25 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Seeds a working .env.local from .env.example so `npm run dev` works
6
+ * immediately after scaffold. Skips if .env.local already exists.
7
+ */
8
+ export async function setupEnvFile(targetDir) {
9
+ const examplePath = path.join(targetDir, ".env.example");
10
+ const localPath = path.join(targetDir, ".env.local");
11
+
12
+ try {
13
+ await fs.access(examplePath);
14
+ } catch {
15
+ return false;
16
+ }
17
+
18
+ try {
19
+ await fs.access(localPath);
20
+ return false;
21
+ } catch {
22
+ await fs.copyFile(examplePath, localPath);
23
+ return true;
24
+ }
25
+ }
@@ -0,0 +1,86 @@
1
+ import { promises as fs } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { getCliVersion } from "./package-info.js";
5
+
6
+ const CONFIG_DIR = path.join(os.homedir(), ".virastack");
7
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
8
+ const DEFAULT_ENDPOINT = "https://virastack.com/api/telemetry";
9
+ const REQUEST_TIMEOUT_MS = 2000;
10
+
11
+ async function readConfig() {
12
+ try {
13
+ const raw = await fs.readFile(CONFIG_FILE, "utf8");
14
+ return JSON.parse(raw);
15
+ } catch {
16
+ return {};
17
+ }
18
+ }
19
+
20
+ async function writeConfig(patch) {
21
+ try {
22
+ const current = await readConfig();
23
+ await fs.mkdir(CONFIG_DIR, { recursive: true });
24
+ await fs.writeFile(
25
+ CONFIG_FILE,
26
+ `${JSON.stringify({ ...current, ...patch }, null, 2)}\n`,
27
+ );
28
+ } catch {
29
+ // Non-fatal: telemetry preferences are a nice-to-have, never block the CLI.
30
+ }
31
+ }
32
+
33
+ export async function isTelemetryDisabled() {
34
+ if (process.env.VIRASTACK_TELEMETRY_DISABLED === "1") return true;
35
+ if (process.env.DO_NOT_TRACK === "1") return true;
36
+
37
+ const config = await readConfig();
38
+ return config.telemetryDisabled === true;
39
+ }
40
+
41
+ export async function disableTelemetry() {
42
+ await writeConfig({ telemetryDisabled: true });
43
+ }
44
+
45
+ export async function hasSeenTelemetryNotice() {
46
+ const config = await readConfig();
47
+ return config.telemetryNoticeShown === true;
48
+ }
49
+
50
+ export async function markTelemetryNoticeShown() {
51
+ await writeConfig({ telemetryNoticeShown: true });
52
+ }
53
+
54
+ /**
55
+ * Fire-and-forget anonymous usage event. Never collects personal data
56
+ * (no project name, no file paths, no IP is read or stored by the CLI) and
57
+ * never throws — a slow or unreachable endpoint must not affect the CLI flow.
58
+ */
59
+ export async function trackEvent(event, properties = {}) {
60
+ if (await isTelemetryDisabled()) return;
61
+
62
+ const endpoint = process.env.VIRASTACK_TELEMETRY_URL ?? DEFAULT_ENDPOINT;
63
+ const controller = new AbortController();
64
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
65
+
66
+ try {
67
+ await fetch(endpoint, {
68
+ method: "POST",
69
+ headers: { "content-type": "application/json" },
70
+ body: JSON.stringify({
71
+ event,
72
+ properties: {
73
+ ...properties,
74
+ cliVersion: getCliVersion(),
75
+ nodeVersion: process.version,
76
+ platform: process.platform,
77
+ },
78
+ }),
79
+ signal: controller.signal,
80
+ });
81
+ } catch {
82
+ // Swallow silently — telemetry must never break or slow down the CLI.
83
+ } finally {
84
+ clearTimeout(timeoutId);
85
+ }
86
+ }