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.
- package/LICENSE +1 -1
- package/README.md +94 -18
- package/bin/virastack.js +7 -0
- package/package.json +43 -9
- package/src/commands/add.js +64 -0
- package/src/commands/init.js +286 -0
- package/src/i18n.js +231 -0
- package/src/index.js +64 -0
- package/src/utils/args.js +60 -0
- package/src/utils/copy-template.js +80 -0
- package/src/utils/exec.js +29 -0
- package/src/utils/git.js +38 -0
- package/src/utils/is-online.js +14 -0
- package/src/utils/package-info.js +12 -0
- package/src/utils/package-manager.js +33 -0
- package/src/utils/setup-env.js +25 -0
- package/src/utils/telemetry.js +86 -0
- package/src/utils/tools.js +48 -0
- package/src/utils/update-package-json.js +44 -0
- package/src/utils/update-readme.js +25 -0
- package/src/utils/validate-project-name.js +26 -0
- package/templates/nextjs/.changeset/README.md +8 -0
- package/templates/nextjs/.changeset/config.json +11 -0
- package/templates/nextjs/.env.example +6 -0
- package/templates/nextjs/.github/workflows/ci.yml +41 -0
- package/templates/nextjs/.husky/commit-msg +1 -0
- package/templates/nextjs/.husky/pre-commit +1 -0
- package/templates/nextjs/.husky/pre-push +1 -0
- package/templates/nextjs/.prettierignore +6 -0
- package/templates/nextjs/.prettierrc +42 -0
- package/templates/nextjs/.vscode/extensions.json +7 -0
- package/templates/nextjs/.vscode/settings.json +20 -0
- package/templates/nextjs/LICENSE +21 -0
- package/templates/nextjs/README.md +202 -0
- package/templates/nextjs/commitlint.config.js +6 -0
- package/templates/nextjs/eslint.config.mjs +27 -0
- package/templates/nextjs/knip.config.js +25 -0
- package/templates/nextjs/next.config.ts +39 -0
- package/templates/nextjs/package.json +115 -0
- package/templates/nextjs/pnpm-lock.yaml +6467 -0
- package/templates/nextjs/pnpm-workspace.yaml +3 -0
- package/templates/nextjs/postcss.config.mjs +7 -0
- package/templates/nextjs/public/favicon.ico +0 -0
- package/templates/nextjs/public/llms.txt +96 -0
- package/templates/nextjs/public/logo.png +0 -0
- package/templates/nextjs/public/logo.webp +0 -0
- package/templates/nextjs/public/og.png +0 -0
- package/templates/nextjs/src/app/error.tsx +34 -0
- package/templates/nextjs/src/app/favicon.ico +0 -0
- package/templates/nextjs/src/app/global-error.tsx +32 -0
- package/templates/nextjs/src/app/layout.tsx +37 -0
- package/templates/nextjs/src/app/loading.tsx +12 -0
- package/templates/nextjs/src/app/manifest.ts +22 -0
- package/templates/nextjs/src/app/not-found.tsx +18 -0
- package/templates/nextjs/src/app/page.tsx +5 -0
- package/templates/nextjs/src/app/robots.ts +13 -0
- package/templates/nextjs/src/app/sitemap.ts +14 -0
- package/templates/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
- package/templates/nextjs/src/components/ui/avatar.tsx +54 -0
- package/templates/nextjs/src/components/ui/button.tsx +46 -0
- package/templates/nextjs/src/components/ui/dialog.tsx +107 -0
- package/templates/nextjs/src/components/ui/field.tsx +24 -0
- package/templates/nextjs/src/components/ui/index.ts +27 -0
- package/templates/nextjs/src/components/ui/input.tsx +23 -0
- package/templates/nextjs/src/components/ui/label.tsx +17 -0
- package/templates/nextjs/src/components/ui/skeleton.tsx +13 -0
- package/templates/nextjs/src/components/ui/table.tsx +94 -0
- package/templates/nextjs/src/components/ui/tabs.tsx +46 -0
- package/templates/nextjs/src/config/seo.config.ts +37 -0
- package/templates/nextjs/src/config/site.config.ts +15 -0
- package/templates/nextjs/src/constants/index.ts +5 -0
- package/templates/nextjs/src/env.ts +41 -0
- package/templates/nextjs/src/features/landing/api/get-users.api.ts +7 -0
- package/templates/nextjs/src/features/landing/api/index.ts +2 -0
- package/templates/nextjs/src/features/landing/api/query-keys.ts +7 -0
- package/templates/nextjs/src/features/landing/components/CartDemo.tsx +66 -0
- package/templates/nextjs/src/features/landing/components/Features.tsx +39 -0
- package/templates/nextjs/src/features/landing/components/Hero.tsx +65 -0
- package/templates/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates/nextjs/src/features/landing/components/ProjectFormDemo.tsx +62 -0
- package/templates/nextjs/src/features/landing/components/Reveal.tsx +83 -0
- package/templates/nextjs/src/features/landing/components/Showcase.tsx +66 -0
- package/templates/nextjs/src/features/landing/components/UsersDemo.tsx +188 -0
- package/templates/nextjs/src/features/landing/data/features.data.tsx +47 -0
- package/templates/nextjs/src/features/landing/data/index.ts +1 -0
- package/templates/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates/nextjs/src/features/landing/helpers/index.ts +7 -0
- package/templates/nextjs/src/features/landing/helpers/motion.ts +21 -0
- package/templates/nextjs/src/features/landing/hooks/index.ts +1 -0
- package/templates/nextjs/src/features/landing/hooks/use-users.ts +16 -0
- package/templates/nextjs/src/features/landing/icons/index.ts +6 -0
- package/templates/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
- package/templates/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates/nextjs/src/features/landing/icons/react.tsx +20 -0
- package/templates/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates/nextjs/src/features/landing/icons/typescript.tsx +16 -0
- package/templates/nextjs/src/features/landing/index.ts +1 -0
- package/templates/nextjs/src/features/landing/schemas/index.ts +1 -0
- package/templates/nextjs/src/features/landing/schemas/project.schema.ts +7 -0
- package/templates/nextjs/src/features/landing/stores/counter.store.ts +18 -0
- package/templates/nextjs/src/features/landing/stores/index.ts +1 -0
- package/templates/nextjs/src/features/landing/types/index.ts +1 -0
- package/templates/nextjs/src/features/landing/types/user.types.ts +9 -0
- package/templates/nextjs/src/helpers/format-date.ts +13 -0
- package/templates/nextjs/src/helpers/index.ts +1 -0
- package/templates/nextjs/src/hooks/index.ts +5 -0
- package/templates/nextjs/src/lib/api.ts +97 -0
- package/templates/nextjs/src/lib/query-client.ts +18 -0
- package/templates/nextjs/src/lib/utils.ts +12 -0
- package/templates/nextjs/src/providers/Providers.tsx +15 -0
- package/templates/nextjs/src/providers/QueryProvider.tsx +19 -0
- package/templates/nextjs/src/providers/ThemeProvider.tsx +9 -0
- package/templates/nextjs/src/providers/index.ts +1 -0
- package/templates/nextjs/src/proxy.ts +6 -0
- package/templates/nextjs/src/schemas/index.ts +5 -0
- package/templates/nextjs/src/stores/index.ts +5 -0
- package/templates/nextjs/src/styles/tailwind.css +122 -0
- package/templates/nextjs/src/types/site-config.types.ts +10 -0
- package/templates/nextjs/tsconfig.json +45 -0
- package/templates/tanstack/.changeset/README.md +8 -0
- package/templates/tanstack/.changeset/config.json +11 -0
- package/templates/tanstack/.env.example +6 -0
- package/templates/tanstack/.github/workflows/ci.yml +41 -0
- package/templates/tanstack/.husky/commit-msg +1 -0
- package/templates/tanstack/.husky/pre-commit +1 -0
- package/templates/tanstack/.husky/pre-push +1 -0
- package/templates/tanstack/.prettierignore +7 -0
- package/templates/tanstack/.prettierrc +41 -0
- package/templates/tanstack/.vscode/extensions.json +7 -0
- package/templates/tanstack/.vscode/settings.json +22 -0
- package/templates/tanstack/LICENSE +21 -0
- package/templates/tanstack/README.md +204 -0
- package/templates/tanstack/commitlint.config.js +6 -0
- package/templates/tanstack/eslint.config.js +28 -0
- package/templates/tanstack/knip.config.js +22 -0
- package/templates/tanstack/package.json +130 -0
- package/templates/tanstack/pnpm-lock.yaml +5835 -0
- package/templates/tanstack/pnpm-workspace.yaml +3 -0
- package/templates/tanstack/public/favicon.ico +0 -0
- package/templates/tanstack/public/llms.txt +86 -0
- package/templates/tanstack/public/logo.png +0 -0
- package/templates/tanstack/public/logo.webp +0 -0
- package/templates/tanstack/public/og.png +0 -0
- package/templates/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
- package/templates/tanstack/src/components/shared/NotFound.tsx +18 -0
- package/templates/tanstack/src/components/shared/Pending.tsx +13 -0
- package/templates/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
- package/templates/tanstack/src/components/ui/avatar.tsx +52 -0
- package/templates/tanstack/src/components/ui/button.tsx +47 -0
- package/templates/tanstack/src/components/ui/dialog.tsx +107 -0
- package/templates/tanstack/src/components/ui/field.tsx +24 -0
- package/templates/tanstack/src/components/ui/index.ts +27 -0
- package/templates/tanstack/src/components/ui/input.tsx +23 -0
- package/templates/tanstack/src/components/ui/label.tsx +17 -0
- package/templates/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates/tanstack/src/components/ui/table.tsx +94 -0
- package/templates/tanstack/src/components/ui/tabs.tsx +46 -0
- package/templates/tanstack/src/config/seo.config.ts +52 -0
- package/templates/tanstack/src/config/site.config.ts +15 -0
- package/templates/tanstack/src/constants/index.ts +5 -0
- package/templates/tanstack/src/env.ts +47 -0
- package/templates/tanstack/src/features/landing/api/get-users.api.ts +7 -0
- package/templates/tanstack/src/features/landing/api/index.ts +2 -0
- package/templates/tanstack/src/features/landing/api/query-keys.ts +7 -0
- package/templates/tanstack/src/features/landing/components/CartDemo.tsx +66 -0
- package/templates/tanstack/src/features/landing/components/Features.tsx +39 -0
- package/templates/tanstack/src/features/landing/components/Hero.tsx +63 -0
- package/templates/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates/tanstack/src/features/landing/components/ProjectFormDemo.tsx +63 -0
- package/templates/tanstack/src/features/landing/components/Reveal.tsx +83 -0
- package/templates/tanstack/src/features/landing/components/Showcase.tsx +66 -0
- package/templates/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates/tanstack/src/features/landing/data/features.data.tsx +45 -0
- package/templates/tanstack/src/features/landing/data/index.ts +1 -0
- package/templates/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates/tanstack/src/features/landing/helpers/index.ts +7 -0
- package/templates/tanstack/src/features/landing/helpers/motion.ts +21 -0
- package/templates/tanstack/src/features/landing/hooks/index.ts +1 -0
- package/templates/tanstack/src/features/landing/hooks/use-users.ts +19 -0
- package/templates/tanstack/src/features/landing/icons/index.ts +5 -0
- package/templates/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates/tanstack/src/features/landing/icons/react.tsx +20 -0
- package/templates/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates/tanstack/src/features/landing/icons/typescript.tsx +16 -0
- package/templates/tanstack/src/features/landing/index.ts +1 -0
- package/templates/tanstack/src/features/landing/schemas/index.ts +1 -0
- package/templates/tanstack/src/features/landing/schemas/project.schema.ts +7 -0
- package/templates/tanstack/src/features/landing/stores/counter.store.ts +18 -0
- package/templates/tanstack/src/features/landing/stores/index.ts +1 -0
- package/templates/tanstack/src/features/landing/types/index.ts +1 -0
- package/templates/tanstack/src/features/landing/types/user.types.ts +9 -0
- package/templates/tanstack/src/helpers/format-date.ts +13 -0
- package/templates/tanstack/src/helpers/index.ts +1 -0
- package/templates/tanstack/src/hooks/index.ts +5 -0
- package/templates/tanstack/src/lib/api.ts +97 -0
- package/templates/tanstack/src/lib/query-client.ts +18 -0
- package/templates/tanstack/src/lib/utils.ts +13 -0
- package/templates/tanstack/src/providers/Providers.tsx +12 -0
- package/templates/tanstack/src/providers/ThemeProvider.tsx +98 -0
- package/templates/tanstack/src/providers/devtools.tsx +6 -0
- package/templates/tanstack/src/providers/index.ts +1 -0
- package/templates/tanstack/src/providers/root-provider.ts +9 -0
- package/templates/tanstack/src/routeTree.gen.ts +123 -0
- package/templates/tanstack/src/router.tsx +27 -0
- package/templates/tanstack/src/routes/__root.tsx +78 -0
- package/templates/tanstack/src/routes/index.tsx +13 -0
- package/templates/tanstack/src/routes/robots[.]txt.ts +21 -0
- package/templates/tanstack/src/routes/site[.]webmanifest.ts +32 -0
- package/templates/tanstack/src/routes/sitemap[.]xml.ts +27 -0
- package/templates/tanstack/src/schemas/index.ts +5 -0
- package/templates/tanstack/src/start.ts +20 -0
- package/templates/tanstack/src/stores/index.ts +5 -0
- package/templates/tanstack/src/styles/tailwind.css +123 -0
- package/templates/tanstack/src/types/site-config.types.ts +10 -0
- package/templates/tanstack/tsconfig.json +25 -0
- package/templates/tanstack/tsr.config.json +3 -0
- package/templates/tanstack/vite.config.ts +12 -0
- package/templates-i18n/nextjs/.changeset/README.md +8 -0
- package/templates-i18n/nextjs/.changeset/config.json +11 -0
- package/templates-i18n/nextjs/.env.example +6 -0
- package/templates-i18n/nextjs/.github/workflows/ci.yml +41 -0
- package/templates-i18n/nextjs/.husky/commit-msg +1 -0
- package/templates-i18n/nextjs/.husky/pre-commit +1 -0
- package/templates-i18n/nextjs/.husky/pre-push +1 -0
- package/templates-i18n/nextjs/.prettierignore +6 -0
- package/templates-i18n/nextjs/.prettierrc +42 -0
- package/templates-i18n/nextjs/.vscode/extensions.json +7 -0
- package/templates-i18n/nextjs/.vscode/settings.json +20 -0
- package/templates-i18n/nextjs/LICENSE +21 -0
- package/templates-i18n/nextjs/README.md +202 -0
- package/templates-i18n/nextjs/commitlint.config.js +6 -0
- package/templates-i18n/nextjs/eslint.config.mjs +27 -0
- package/templates-i18n/nextjs/knip.config.js +29 -0
- package/templates-i18n/nextjs/next.config.ts +42 -0
- package/templates-i18n/nextjs/package.json +117 -0
- package/templates-i18n/nextjs/pnpm-lock.yaml +6854 -0
- package/templates-i18n/nextjs/pnpm-workspace.yaml +3 -0
- package/templates-i18n/nextjs/postcss.config.mjs +7 -0
- package/templates-i18n/nextjs/public/favicon.ico +0 -0
- package/templates-i18n/nextjs/public/llms.txt +96 -0
- package/templates-i18n/nextjs/public/logo.png +0 -0
- package/templates-i18n/nextjs/public/logo.webp +0 -0
- package/templates-i18n/nextjs/public/og.png +0 -0
- package/templates-i18n/nextjs/src/app/[locale]/error.tsx +34 -0
- package/templates-i18n/nextjs/src/app/[locale]/layout.tsx +60 -0
- package/templates-i18n/nextjs/src/app/[locale]/loading.tsx +12 -0
- package/templates-i18n/nextjs/src/app/[locale]/not-found.tsx +18 -0
- package/templates-i18n/nextjs/src/app/[locale]/page.tsx +10 -0
- package/templates-i18n/nextjs/src/app/favicon.ico +0 -0
- package/templates-i18n/nextjs/src/app/global-error.tsx +32 -0
- package/templates-i18n/nextjs/src/app/manifest.ts +22 -0
- package/templates-i18n/nextjs/src/app/robots.ts +13 -0
- package/templates-i18n/nextjs/src/app/sitemap.ts +14 -0
- package/templates-i18n/nextjs/src/components/shared/LanguageSwitcher.tsx +48 -0
- package/templates-i18n/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
- package/templates-i18n/nextjs/src/components/ui/avatar.tsx +54 -0
- package/templates-i18n/nextjs/src/components/ui/button.tsx +46 -0
- package/templates-i18n/nextjs/src/components/ui/dialog.tsx +107 -0
- package/templates-i18n/nextjs/src/components/ui/field.tsx +24 -0
- package/templates-i18n/nextjs/src/components/ui/index.ts +27 -0
- package/templates-i18n/nextjs/src/components/ui/input.tsx +23 -0
- package/templates-i18n/nextjs/src/components/ui/label.tsx +17 -0
- package/templates-i18n/nextjs/src/components/ui/skeleton.tsx +13 -0
- package/templates-i18n/nextjs/src/components/ui/table.tsx +94 -0
- package/templates-i18n/nextjs/src/components/ui/tabs.tsx +46 -0
- package/templates-i18n/nextjs/src/config/seo.config.ts +37 -0
- package/templates-i18n/nextjs/src/config/site.config.ts +15 -0
- package/templates-i18n/nextjs/src/constants/index.ts +5 -0
- package/templates-i18n/nextjs/src/env.ts +41 -0
- package/templates-i18n/nextjs/src/features/landing/api/get-users.api.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/api/index.ts +2 -0
- package/templates-i18n/nextjs/src/features/landing/api/query-keys.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/components/CartDemo.tsx +68 -0
- package/templates-i18n/nextjs/src/features/landing/components/Features.tsx +84 -0
- package/templates-i18n/nextjs/src/features/landing/components/Hero.tsx +76 -0
- package/templates-i18n/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates-i18n/nextjs/src/features/landing/components/ProjectFormDemo.tsx +64 -0
- package/templates-i18n/nextjs/src/features/landing/components/Reveal.tsx +83 -0
- package/templates-i18n/nextjs/src/features/landing/components/Showcase.tsx +83 -0
- package/templates-i18n/nextjs/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates-i18n/nextjs/src/features/landing/data/features.data.tsx +47 -0
- package/templates-i18n/nextjs/src/features/landing/data/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/index.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/motion.ts +21 -0
- package/templates-i18n/nextjs/src/features/landing/hooks/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/hooks/use-users.ts +16 -0
- package/templates-i18n/nextjs/src/features/landing/icons/index.ts +6 -0
- package/templates-i18n/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
- package/templates-i18n/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates-i18n/nextjs/src/features/landing/icons/react.tsx +20 -0
- package/templates-i18n/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates-i18n/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates-i18n/nextjs/src/features/landing/icons/typescript.tsx +16 -0
- package/templates-i18n/nextjs/src/features/landing/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/schemas/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/schemas/project.schema.ts +9 -0
- package/templates-i18n/nextjs/src/features/landing/stores/counter.store.ts +18 -0
- package/templates-i18n/nextjs/src/features/landing/stores/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/types/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/types/user.types.ts +9 -0
- package/templates-i18n/nextjs/src/helpers/format-date.ts +13 -0
- package/templates-i18n/nextjs/src/helpers/index.ts +1 -0
- package/templates-i18n/nextjs/src/hooks/index.ts +5 -0
- package/templates-i18n/nextjs/src/i18n/request.ts +16 -0
- package/templates-i18n/nextjs/src/i18n/routing.ts +11 -0
- package/templates-i18n/nextjs/src/lib/api.ts +97 -0
- package/templates-i18n/nextjs/src/lib/query-client.ts +18 -0
- package/templates-i18n/nextjs/src/lib/utils.ts +12 -0
- package/templates-i18n/nextjs/src/messages/en.json +55 -0
- package/templates-i18n/nextjs/src/messages/tr.json +55 -0
- package/templates-i18n/nextjs/src/providers/Providers.tsx +15 -0
- package/templates-i18n/nextjs/src/providers/QueryProvider.tsx +19 -0
- package/templates-i18n/nextjs/src/providers/ThemeProvider.tsx +9 -0
- package/templates-i18n/nextjs/src/providers/index.ts +1 -0
- package/templates-i18n/nextjs/src/proxy.ts +16 -0
- package/templates-i18n/nextjs/src/schemas/index.ts +5 -0
- package/templates-i18n/nextjs/src/stores/index.ts +5 -0
- package/templates-i18n/nextjs/src/styles/tailwind.css +122 -0
- package/templates-i18n/nextjs/src/types/site-config.types.ts +10 -0
- package/templates-i18n/nextjs/tsconfig.json +45 -0
- package/templates-i18n/tanstack/.changeset/README.md +8 -0
- package/templates-i18n/tanstack/.changeset/config.json +11 -0
- package/templates-i18n/tanstack/.env.example +6 -0
- package/templates-i18n/tanstack/.github/workflows/ci.yml +41 -0
- package/templates-i18n/tanstack/.husky/commit-msg +1 -0
- package/templates-i18n/tanstack/.husky/pre-commit +1 -0
- package/templates-i18n/tanstack/.husky/pre-push +1 -0
- package/templates-i18n/tanstack/.prettierignore +7 -0
- package/templates-i18n/tanstack/.prettierrc +41 -0
- package/templates-i18n/tanstack/.vscode/extensions.json +7 -0
- package/templates-i18n/tanstack/.vscode/settings.json +22 -0
- package/templates-i18n/tanstack/LICENSE +21 -0
- package/templates-i18n/tanstack/README.md +204 -0
- package/templates-i18n/tanstack/commitlint.config.js +6 -0
- package/templates-i18n/tanstack/eslint.config.js +29 -0
- package/templates-i18n/tanstack/knip.config.js +26 -0
- package/templates-i18n/tanstack/messages/en.json +65 -0
- package/templates-i18n/tanstack/messages/tr.json +65 -0
- package/templates-i18n/tanstack/package.json +132 -0
- package/templates-i18n/tanstack/pnpm-lock.yaml +5998 -0
- package/templates-i18n/tanstack/pnpm-workspace.yaml +3 -0
- package/templates-i18n/tanstack/project.inlang/README.md +157 -0
- package/templates-i18n/tanstack/project.inlang/settings.json +9 -0
- package/templates-i18n/tanstack/public/favicon.ico +0 -0
- package/templates-i18n/tanstack/public/llms.txt +86 -0
- package/templates-i18n/tanstack/public/logo.png +0 -0
- package/templates-i18n/tanstack/public/logo.webp +0 -0
- package/templates-i18n/tanstack/public/og.png +0 -0
- package/templates-i18n/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
- package/templates-i18n/tanstack/src/components/shared/LanguageSwitcher.tsx +57 -0
- package/templates-i18n/tanstack/src/components/shared/NotFound.tsx +18 -0
- package/templates-i18n/tanstack/src/components/shared/Pending.tsx +13 -0
- package/templates-i18n/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
- package/templates-i18n/tanstack/src/components/ui/avatar.tsx +52 -0
- package/templates-i18n/tanstack/src/components/ui/button.tsx +47 -0
- package/templates-i18n/tanstack/src/components/ui/dialog.tsx +107 -0
- package/templates-i18n/tanstack/src/components/ui/field.tsx +24 -0
- package/templates-i18n/tanstack/src/components/ui/index.ts +27 -0
- package/templates-i18n/tanstack/src/components/ui/input.tsx +23 -0
- package/templates-i18n/tanstack/src/components/ui/label.tsx +17 -0
- package/templates-i18n/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates-i18n/tanstack/src/components/ui/table.tsx +94 -0
- package/templates-i18n/tanstack/src/components/ui/tabs.tsx +46 -0
- package/templates-i18n/tanstack/src/config/seo.config.ts +52 -0
- package/templates-i18n/tanstack/src/config/site.config.ts +15 -0
- package/templates-i18n/tanstack/src/constants/index.ts +5 -0
- package/templates-i18n/tanstack/src/env.ts +47 -0
- package/templates-i18n/tanstack/src/features/landing/api/get-users.api.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/api/index.ts +2 -0
- package/templates-i18n/tanstack/src/features/landing/api/query-keys.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/components/CartDemo.tsx +67 -0
- package/templates-i18n/tanstack/src/features/landing/components/Features.tsx +79 -0
- package/templates-i18n/tanstack/src/features/landing/components/Hero.tsx +68 -0
- package/templates-i18n/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates-i18n/tanstack/src/features/landing/components/ProjectFormDemo.tsx +64 -0
- package/templates-i18n/tanstack/src/features/landing/components/Reveal.tsx +83 -0
- package/templates-i18n/tanstack/src/features/landing/components/Showcase.tsx +81 -0
- package/templates-i18n/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates-i18n/tanstack/src/features/landing/data/features.data.tsx +45 -0
- package/templates-i18n/tanstack/src/features/landing/data/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/index.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/motion.ts +21 -0
- package/templates-i18n/tanstack/src/features/landing/hooks/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/hooks/use-users.ts +19 -0
- package/templates-i18n/tanstack/src/features/landing/icons/index.ts +5 -0
- package/templates-i18n/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates-i18n/tanstack/src/features/landing/icons/react.tsx +20 -0
- package/templates-i18n/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates-i18n/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates-i18n/tanstack/src/features/landing/icons/typescript.tsx +16 -0
- package/templates-i18n/tanstack/src/features/landing/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/schemas/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/schemas/project.schema.ts +10 -0
- package/templates-i18n/tanstack/src/features/landing/stores/counter.store.ts +18 -0
- package/templates-i18n/tanstack/src/features/landing/stores/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/types/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/types/user.types.ts +9 -0
- package/templates-i18n/tanstack/src/helpers/format-date.ts +13 -0
- package/templates-i18n/tanstack/src/helpers/index.ts +1 -0
- package/templates-i18n/tanstack/src/hooks/index.ts +5 -0
- package/templates-i18n/tanstack/src/lib/api.ts +97 -0
- package/templates-i18n/tanstack/src/lib/query-client.ts +18 -0
- package/templates-i18n/tanstack/src/lib/utils.ts +13 -0
- package/templates-i18n/tanstack/src/paraglide/README.md +162 -0
- package/templates-i18n/tanstack/src/providers/Providers.tsx +12 -0
- package/templates-i18n/tanstack/src/providers/ThemeProvider.tsx +98 -0
- package/templates-i18n/tanstack/src/providers/devtools.tsx +6 -0
- package/templates-i18n/tanstack/src/providers/index.ts +1 -0
- package/templates-i18n/tanstack/src/providers/root-provider.ts +9 -0
- package/templates-i18n/tanstack/src/routeTree.gen.ts +179 -0
- package/templates-i18n/tanstack/src/router.tsx +27 -0
- package/templates-i18n/tanstack/src/routes/$lang/index.tsx +13 -0
- package/templates-i18n/tanstack/src/routes/$lang.tsx +22 -0
- package/templates-i18n/tanstack/src/routes/__root.tsx +80 -0
- package/templates-i18n/tanstack/src/routes/index.tsx +13 -0
- package/templates-i18n/tanstack/src/routes/robots[.]txt.ts +21 -0
- package/templates-i18n/tanstack/src/routes/site[.]webmanifest.ts +32 -0
- package/templates-i18n/tanstack/src/routes/sitemap[.]xml.ts +27 -0
- package/templates-i18n/tanstack/src/schemas/index.ts +5 -0
- package/templates-i18n/tanstack/src/server.ts +10 -0
- package/templates-i18n/tanstack/src/start.ts +20 -0
- package/templates-i18n/tanstack/src/stores/index.ts +5 -0
- package/templates-i18n/tanstack/src/styles/tailwind.css +123 -0
- package/templates-i18n/tanstack/src/types/site-config.types.ts +10 -0
- package/templates-i18n/tanstack/tsconfig.json +26 -0
- package/templates-i18n/tanstack/tsr.config.json +3 -0
- package/templates-i18n/tanstack/vite.config.ts +31 -0
- package/index.js +0 -3
- 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
|
+
}
|
package/src/utils/git.js
ADDED
|
@@ -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
|
+
}
|