create-vitnode-app 0.1.6 → 1.2.0-canary.1

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 (94) hide show
  1. package/{templates → copy-of-vitnode-app}/eslint/.prettierrc.mjs +1 -1
  2. package/{templates/eslint/apps/frontend → copy-of-vitnode-app/eslint}/eslint.config.mjs +1 -2
  3. package/copy-of-vitnode-app/root/next.config.ts +11 -0
  4. package/{templates/basic/apps/frontend → copy-of-vitnode-app/root}/postcss.config.mjs +1 -2
  5. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/[...rest]/page.tsx +5 -0
  6. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx +10 -0
  7. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/not-found.tsx +5 -0
  8. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx +113 -0
  9. package/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx +7 -0
  10. package/copy-of-vitnode-app/root/src/app/[locale]/admin/not-found.tsx +5 -0
  11. package/copy-of-vitnode-app/root/src/app/[locale]/admin/page.tsx +5 -0
  12. package/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx +35 -0
  13. package/copy-of-vitnode-app/root/src/app/api/[...route]/route.ts +17 -0
  14. package/copy-of-vitnode-app/root/src/app/global-error.tsx +27 -0
  15. package/copy-of-vitnode-app/root/src/app/globals.css +119 -0
  16. package/{templates/basic/apps/frontend → copy-of-vitnode-app/root}/src/app/layout.tsx +1 -1
  17. package/copy-of-vitnode-app/root/src/app/not-found.tsx +13 -0
  18. package/copy-of-vitnode-app/root/src/vitnode.api.config.ts +5 -0
  19. package/copy-of-vitnode-app/root/tsconfig.json +21 -0
  20. package/dist/src/create/create-vitnode.js +26 -0
  21. package/dist/src/helpers/get-available-package-managers.js +24 -0
  22. package/dist/src/helpers/get-package-json.js +2 -0
  23. package/dist/src/helpers/is-folder-empty.js +49 -0
  24. package/dist/src/helpers/is-writeable.js +11 -0
  25. package/dist/src/helpers/packages-json.js +1 -0
  26. package/dist/src/helpers/validate-pkg.js +14 -0
  27. package/dist/src/index.js +65 -0
  28. package/dist/src/plugin/index.js +18 -0
  29. package/dist/src/prepare/prepare.js +16 -0
  30. package/dist/src/questions.js +46 -0
  31. package/dist/src/validation.js +40 -0
  32. package/dist/tsconfig.tsbuildinfo +1 -0
  33. package/package.json +15 -23
  34. package/README.md +0 -40
  35. package/dist/index.cjs +0 -49
  36. package/templates/basic/.env.template +0 -16
  37. package/templates/basic/.gitignore_template +0 -58
  38. package/templates/basic/README.md +0 -44
  39. package/templates/basic/apps/backend/drizzle.config.ts +0 -9
  40. package/templates/basic/apps/backend/nest-cli.json +0 -15
  41. package/templates/basic/apps/backend/src/app.module.ts +0 -24
  42. package/templates/basic/apps/backend/src/database/config.ts +0 -16
  43. package/templates/basic/apps/backend/src/database/database.module.ts +0 -17
  44. package/templates/basic/apps/backend/src/database/database.service.ts +0 -9
  45. package/templates/basic/apps/backend/src/main.ts +0 -13
  46. package/templates/basic/apps/backend/src/plugins/plugins.module.ts +0 -8
  47. package/templates/basic/apps/backend/src/plugins/welcome/admin/admin.module.ts +0 -4
  48. package/templates/basic/apps/backend/src/plugins/welcome/admin/database/index.ts +0 -1
  49. package/templates/basic/apps/backend/src/plugins/welcome/config.json +0 -12
  50. package/templates/basic/apps/backend/src/plugins/welcome/welcome.module.ts +0 -8
  51. package/templates/basic/apps/backend/tsconfig.build.json +0 -4
  52. package/templates/basic/apps/backend/tsconfig.json +0 -15
  53. package/templates/basic/apps/backend/uploads/index.html +0 -0
  54. package/templates/basic/apps/backend/uploads/private/index.html +0 -0
  55. package/templates/basic/apps/backend/uploads/public/index.html +0 -0
  56. package/templates/basic/apps/backend/uploads/temp/index.html +0 -0
  57. package/templates/basic/apps/frontend/global.d.ts +0 -11
  58. package/templates/basic/apps/frontend/next.config.ts +0 -6
  59. package/templates/basic/apps/frontend/public/sw.js +0 -3
  60. package/templates/basic/apps/frontend/src/app/[locale]/(main)/[...slug]/page.tsx +0 -10
  61. package/templates/basic/apps/frontend/src/app/[locale]/(main)/footer.tsx +0 -18
  62. package/templates/basic/apps/frontend/src/app/[locale]/(main)/layout.tsx +0 -12
  63. package/templates/basic/apps/frontend/src/app/[locale]/(main)/not-found.tsx +0 -5
  64. package/templates/basic/apps/frontend/src/app/[locale]/(main)/page.tsx +0 -9
  65. package/templates/basic/apps/frontend/src/app/[locale]/admin/(auth)/[...slug]/page.tsx +0 -12
  66. package/templates/basic/apps/frontend/src/app/[locale]/admin/(auth)/error.tsx +0 -9
  67. package/templates/basic/apps/frontend/src/app/[locale]/admin/(auth)/layout.tsx +0 -13
  68. package/templates/basic/apps/frontend/src/app/[locale]/admin/(auth)/not-found.tsx +0 -5
  69. package/templates/basic/apps/frontend/src/app/[locale]/admin/page.tsx +0 -5
  70. package/templates/basic/apps/frontend/src/app/[locale]/layout.tsx +0 -13
  71. package/templates/basic/apps/frontend/src/app/global.css +0 -94
  72. package/templates/basic/apps/frontend/src/app/not-found.tsx +0 -8
  73. package/templates/basic/apps/frontend/src/app/robots.ts +0 -9
  74. package/templates/basic/apps/frontend/src/app/sitemap.ts +0 -9
  75. package/templates/basic/apps/frontend/src/i18n.ts +0 -13
  76. package/templates/basic/apps/frontend/src/middleware.ts +0 -8
  77. package/templates/basic/apps/frontend/src/plugins/admin/langs/en.json +0 -710
  78. package/templates/basic/apps/frontend/src/plugins/core/langs/en.json +0 -408
  79. package/templates/basic/apps/frontend/src/plugins/welcome/langs/en.json +0 -15
  80. package/templates/basic/apps/frontend/src/plugins/welcome/templates/default-page.tsx +0 -40
  81. package/templates/basic/apps/frontend/tailwind.config.ts +0 -16
  82. package/templates/basic/apps/frontend/tsconfig.json +0 -33
  83. package/templates/basic/apps/shared/plugins/welcome/example.dto.ts +0 -0
  84. package/templates/basic/apps/shared/tsconfig.json +0 -15
  85. package/templates/basic/turbo.json +0 -34
  86. package/templates/docker/apps/backend/DockerFile +0 -31
  87. package/templates/docker/apps/frontend/DockerFile +0 -38
  88. package/templates/docker/deploy.sh +0 -213
  89. package/templates/docker/docker-compose-dev.yml +0 -35
  90. package/templates/docker/docker-compose.yml +0 -56
  91. package/templates/eslint/apps/backend/eslint.config.mjs +0 -4
  92. package/templates/eslint/apps/shared/eslint.config.mjs +0 -4
  93. package/templates/pnpm/pnpm-workspace.yaml +0 -2
  94. /package/{templates/basic/apps/frontend/public → copy-of-vitnode-app/root/src/app}/favicon.ico +0 -0
@@ -1,4 +1,4 @@
1
- import vitnodePrettier from "eslint-config-typescript-vitnode/prettierrc.mjs";
1
+ import vitnodePrettier from "eslint-config-typescript-vitnode/prettierrc";
2
2
 
3
3
  /**
4
4
  * @see https://prettier.io/docs/en/configuration.html
@@ -1,4 +1,3 @@
1
- // @ts-check
2
- import eslintVitNode from 'eslint-config-typescript-vitnode/eslint.react.mjs';
1
+ import eslintVitNode from 'eslint-config-typescript-vitnode/eslint';
3
2
 
4
3
  export default [...eslintVitNode];
@@ -0,0 +1,11 @@
1
+ import type { NextConfig } from 'next';
2
+ import { vitNodeNextConfig } from '@vitnode/core/config/next.config';
3
+
4
+ const nextConfig: NextConfig = {
5
+ experimental: {
6
+ inlineCss: true,
7
+ reactCompiler: true,
8
+ },
9
+ };
10
+
11
+ export default vitNodeNextConfig(nextConfig);
@@ -1,8 +1,7 @@
1
1
  /** @type {import('postcss-load-config').Config} */
2
2
  const config = {
3
3
  plugins: {
4
- tailwindcss: {},
5
- autoprefixer: {},
4
+ '@tailwindcss/postcss': {},
6
5
  },
7
6
  };
8
7
 
@@ -0,0 +1,5 @@
1
+ import { notFound } from 'next/navigation';
2
+
3
+ export default function RestPage() {
4
+ notFound();
5
+ }
@@ -0,0 +1,10 @@
1
+ import { LogoVitNode } from '@vitnode/core/components/logo-vitnode';
2
+ import { ThemeLayout } from '@vitnode/core/views/layouts/theme/layout';
3
+
4
+ export default function Layout({ children }: { children: React.ReactNode }) {
5
+ return (
6
+ <ThemeLayout logo={<LogoVitNode className="w-34" />}>
7
+ {children}
8
+ </ThemeLayout>
9
+ );
10
+ }
@@ -0,0 +1,5 @@
1
+ import { ErrorView } from '@vitnode/core/views/error/error-view';
2
+
3
+ export default function NotFoundPage() {
4
+ return <ErrorView code={404} />;
5
+ }
@@ -0,0 +1,113 @@
1
+ import { LogoVitNode } from '@vitnode/core/components/logo-vitnode';
2
+ import { buttonVariants } from '@vitnode/core/components/ui/button';
3
+ import { Link } from '@vitnode/core/lib/navigation';
4
+ import { cn } from '@vitnode/core/lib/utils';
5
+ import { ArrowRight, Book, Terminal } from 'lucide-react';
6
+ import { getTranslations } from 'next-intl/server';
7
+
8
+ export default async function Home({
9
+ params,
10
+ }: {
11
+ params: Promise<{ locale: string }>;
12
+ }) {
13
+ const { locale } = await params;
14
+ const t = await getTranslations({ locale, namespace: 'welcome.home' });
15
+
16
+ return (
17
+ <main className="container mx-auto flex min-h-[calc(100vh-4rem)] flex-col items-center justify-center gap-16 px-4 py-16">
18
+ {/* Hero Section */}
19
+ <div className="flex max-w-3xl flex-col items-center space-y-8 text-center">
20
+ <LogoVitNode className="w-44" />
21
+
22
+ <div className="space-y-4">
23
+ <h1 className="from-foreground to-foreground/70 text-balance bg-gradient-to-br bg-clip-text text-4xl font-bold tracking-tight text-transparent md:text-5xl lg:text-6xl">
24
+ {t('title')}
25
+ </h1>
26
+ <p className="text-muted-foreground mx-auto max-w-2xl text-pretty text-lg">
27
+ {t('desc')}
28
+ </p>
29
+ </div>
30
+
31
+ {/* Primary Actions */}
32
+ <div className="flex flex-wrap items-center justify-center gap-4">
33
+ <Link
34
+ className={cn(
35
+ buttonVariants({
36
+ size: 'lg',
37
+ }),
38
+ )}
39
+ href="/admin"
40
+ target="_blank"
41
+ >
42
+ <Terminal className="mr-2 h-4 w-4" />
43
+ {t('go_to_admin_cp')}
44
+ <ArrowRight className="ml-2 h-4 w-4" />
45
+ </Link>
46
+
47
+ <Link
48
+ className={cn(
49
+ buttonVariants({
50
+ size: 'lg',
51
+ variant: 'outline',
52
+ }),
53
+ )}
54
+ href="https://vitnode.com/docs"
55
+ rel="noopener noreferrer"
56
+ target="_blank"
57
+ >
58
+ <Book className="mr-2 h-4 w-4" />
59
+ {t('read_our_docs')}
60
+ </Link>
61
+
62
+ <Link
63
+ className={cn(
64
+ buttonVariants({
65
+ size: 'lg',
66
+ variant: 'ghost',
67
+ }),
68
+ )}
69
+ href="https://github.com/VitNode/vitnode"
70
+ rel="noopener noreferrer"
71
+ target="_blank"
72
+ >
73
+ <svg
74
+ className="mr-2 h-4 w-4"
75
+ role="img"
76
+ viewBox="0 0 24 24"
77
+ xmlns="http://www.w3.org/2000/svg"
78
+ >
79
+ <path
80
+ d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
81
+ fill="currentColor"
82
+ />
83
+ </svg>
84
+ View on GitHub
85
+ </Link>
86
+ </div>
87
+ </div>
88
+
89
+ {/* Features Grid */}
90
+ <div className="grid w-full max-w-5xl grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
91
+ <div className="bg-card rounded-lg border p-6">
92
+ <h3 className="mb-2 font-semibold">Plugin System</h3>
93
+ <p className="text-muted-foreground text-sm">
94
+ Create and install plugins to extend your application&apos;s
95
+ functionality with ease.
96
+ </p>
97
+ </div>
98
+ <div className="bg-card rounded-lg border p-6">
99
+ <h3 className="mb-2 font-semibold">SSO Integration</h3>
100
+ <p className="text-muted-foreground text-sm">
101
+ Built-in support for Google, Facebook, and custom SSO providers.
102
+ </p>
103
+ </div>
104
+ <div className="bg-card rounded-lg border p-6">
105
+ <h3 className="mb-2 font-semibold">Modern Stack</h3>
106
+ <p className="text-muted-foreground text-sm">
107
+ Built with Next.js, TypeScript, and modern web technologies.
108
+ </p>
109
+ </div>
110
+ </div>
111
+ </main>
112
+ );
113
+ }
@@ -0,0 +1,7 @@
1
+ import { AdminLayout } from '@vitnode/core/views/admin/layouts/admin-layout';
2
+
3
+ export default function Layout(
4
+ props: React.ComponentProps<typeof AdminLayout>,
5
+ ) {
6
+ return <AdminLayout {...props} />;
7
+ }
@@ -0,0 +1,5 @@
1
+ import { ErrorView } from '@vitnode/core/views/error/error-view';
2
+
3
+ export default function NotFoundPage() {
4
+ return <ErrorView code={404} />;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { SignInAdminView } from '@vitnode/core/views/admin/sign-in/sign-in-admin-view';
2
+
3
+ export default function Page() {
4
+ return <SignInAdminView />;
5
+ }
@@ -0,0 +1,35 @@
1
+ import type { RootLayoutProps } from '@vitnode/core/views/layouts/root-layout';
2
+ import type { Metadata } from 'next';
3
+
4
+ import { vitNodeConfig } from '@/vitnode.config';
5
+ import {
6
+ generateMetadataRootLayout,
7
+ RootLayout,
8
+ } from '@vitnode/core/views/layouts/root-layout';
9
+ import { Geist, Geist_Mono } from 'next/font/google';
10
+
11
+ const geistSans = Geist({
12
+ variable: '--font-geist-sans',
13
+ subsets: ['latin'],
14
+ });
15
+
16
+ const geistMono = Geist_Mono({
17
+ variable: '--font-geist-mono',
18
+ subsets: ['latin'],
19
+ });
20
+
21
+ export const generateMetadata = (): Metadata =>
22
+ generateMetadataRootLayout(vitNodeConfig);
23
+
24
+ export const generateStaticParams = () =>
25
+ vitNodeConfig.i18n.locales.map(locale => ({ locale }));
26
+
27
+ export default function LocaleLayout(props: RootLayoutProps) {
28
+ return (
29
+ <RootLayout
30
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
31
+ config={vitNodeConfig}
32
+ {...props}
33
+ />
34
+ );
35
+ }
@@ -0,0 +1,17 @@
1
+ import { vitNodeApiConfig } from '@/vitnode.api.config';
2
+ import { vitNodeConfig } from '@/vitnode.config';
3
+ import { OpenAPIHono } from '@hono/zod-openapi';
4
+ import { VitNodeAPI } from '@vitnode/core/api/config';
5
+ import { handle } from 'hono/vercel';
6
+
7
+ const app = new OpenAPIHono().basePath('/api');
8
+ VitNodeAPI({
9
+ app,
10
+ vitNodeApiConfig,
11
+ vitNodeConfig,
12
+ });
13
+
14
+ export const GET = handle(app);
15
+ export const POST = handle(app);
16
+ export const PUT = handle(app);
17
+ export const DELETE = handle(app);
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+
3
+ import { vitNodeConfig } from '@/vitnode.config';
4
+ import { GlobalErrorView } from '@vitnode/core/views/error/global-error-view';
5
+
6
+ import './globals.css';
7
+
8
+ import { Geist, Geist_Mono } from 'next/font/google';
9
+
10
+ const geistSans = Geist({
11
+ variable: '--font-geist-sans',
12
+ subsets: ['latin'],
13
+ });
14
+
15
+ const geistMono = Geist_Mono({
16
+ variable: '--font-geist-mono',
17
+ subsets: ['latin'],
18
+ });
19
+
20
+ export default function GlobalError() {
21
+ return (
22
+ <GlobalErrorView
23
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
24
+ config={vitNodeConfig}
25
+ />
26
+ );
27
+ }
@@ -0,0 +1,119 @@
1
+ @import 'tailwindcss' source('../../src');
2
+
3
+ @import 'tw-animate-css';
4
+
5
+ @source "../../node_modules/@vitnode/core/dist/src/components";
6
+ @source "../../node_modules/@vitnode/core/dist/src/views";
7
+
8
+ @custom-variant dark (&:is(.dark *));
9
+
10
+ :root:not(.dark) {
11
+ --background: oklch(1 0 0);
12
+ --foreground: oklch(0.141 0.005 285.823);
13
+ --card: oklch(1 0 0);
14
+ --card-foreground: oklch(0.141 0.005 285.823);
15
+ --popover: oklch(1 0 0);
16
+ --popover-foreground: oklch(0.141 0.005 285.823);
17
+ --primary: oklch(0.51 0.16 262.61);
18
+ --primary-foreground: oklch(0.985 0 0);
19
+ --secondary: oklch(0.967 0.001 286.375);
20
+ --secondary-foreground: oklch(0.21 0.006 285.885);
21
+ --muted: oklch(0.967 0.001 286.375);
22
+ --muted-foreground: oklch(0.552 0.016 285.938);
23
+ --accent: oklch(0.94 0.005 286); /* Adjusted for better contrast on muted */
24
+ --accent-foreground: oklch(0.21 0.006 285.885);
25
+ --destructive: oklch(0.577 0.245 27.325);
26
+ --border: oklch(
27
+ 0.9 0.02 262.61
28
+ ); /* Similar hue to dark primary, lighter/desaturated */
29
+ --ring: oklch(0.7 0.16 262.61); /* Lighter version of dark primary */
30
+ --chart-1: oklch(0.65 0.22 40);
31
+ --chart-2: oklch(0.6 0.15 185);
32
+ --chart-3: oklch(0.42 0.09 230);
33
+ --chart-4: oklch(0.83 0.19 85);
34
+ --chart-5: oklch(0.77 0.18 70);
35
+ }
36
+
37
+ .dark {
38
+ --background: oklch(0.12 0 0);
39
+ --foreground: oklch(0.98 0.005 240);
40
+ --card: oklch(0.18 0.005 240);
41
+ --card-foreground: oklch(0.98 0.005 240);
42
+ --popover: oklch(0.18 0.005 240);
43
+ --popover-foreground: oklch(0.98 0.005 240);
44
+ --primary: oklch(0.51 0.16 262.61);
45
+ --primary-foreground: oklch(0.99 0.005 240);
46
+ --secondary: oklch(0.24 0.01 240);
47
+ --secondary-foreground: oklch(0.98 0.005 240);
48
+ --muted: oklch(0.22 0.01 240); /* Made slightly darker */
49
+ --muted-foreground: oklch(0.75 0.005 240);
50
+ --accent: oklch(0.28 0.015 240);
51
+ --destructive: oklch(0.704 0.191 22.216);
52
+ --border: oklch(0.28 0.005 240);
53
+ --ring: oklch(0.51 0.16 262.61);
54
+ --chart-1: oklch(0.55 0.24 265);
55
+ --chart-2: oklch(0.7 0.17 160);
56
+ --chart-3: oklch(0.8 0.18 70);
57
+ --chart-4: oklch(0.7 0.26 300);
58
+ --chart-5: oklch(0.7 0.24 20);
59
+ }
60
+
61
+ :root {
62
+ --radius: 0.625rem;
63
+ --input: var(--border);
64
+ --sidebar: var(--muted);
65
+ --sidebar-foreground: var(--foreground);
66
+ --sidebar-primary: var(--primary);
67
+ --sidebar-primary-foreground: var(--primary-foreground);
68
+ --sidebar-accent: var(--accent);
69
+ --sidebar-accent-foreground: var(--accent-foreground);
70
+ --sidebar-border: var(--border);
71
+ --sidebar-ring: var(--ring);
72
+ }
73
+
74
+ @theme inline {
75
+ --radius-sm: calc(var(--radius) - 4px);
76
+ --radius-md: calc(var(--radius) - 2px);
77
+ --radius-lg: var(--radius);
78
+ --radius-xl: calc(var(--radius) + 4px);
79
+ --color-background: var(--background);
80
+ --color-foreground: var(--foreground);
81
+ --color-card: var(--card);
82
+ --color-card-foreground: var(--card-foreground);
83
+ --color-popover: var(--popover);
84
+ --color-popover-foreground: var(--popover-foreground);
85
+ --color-primary: var(--primary);
86
+ --color-primary-foreground: var(--primary-foreground);
87
+ --color-secondary: var(--secondary);
88
+ --color-secondary-foreground: var(--secondary-foreground);
89
+ --color-muted: var(--muted);
90
+ --color-muted-foreground: var(--muted-foreground);
91
+ --color-accent: var(--accent);
92
+ --color-accent-foreground: var(--accent-foreground);
93
+ --color-destructive: var(--destructive);
94
+ --color-border: var(--border);
95
+ --color-input: var(--input);
96
+ --color-ring: var(--ring);
97
+ --color-chart-1: var(--chart-1);
98
+ --color-chart-2: var(--chart-2);
99
+ --color-chart-3: var(--chart-3);
100
+ --color-chart-4: var(--chart-4);
101
+ --color-chart-5: var(--chart-5);
102
+ --color-sidebar: var(--sidebar);
103
+ --color-sidebar-foreground: var(--sidebar-foreground);
104
+ --color-sidebar-primary: var(--sidebar-primary);
105
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
106
+ --color-sidebar-accent: var(--sidebar-accent);
107
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
108
+ --color-sidebar-border: var(--sidebar-border);
109
+ --color-sidebar-ring: var(--sidebar-ring);
110
+ }
111
+
112
+ @layer base {
113
+ * {
114
+ @apply border-border outline-ring/50;
115
+ }
116
+ body {
117
+ @apply bg-background text-foreground;
118
+ }
119
+ }
@@ -1,4 +1,4 @@
1
- import './global.css';
1
+ import './globals.css';
2
2
 
3
3
  export default async function RootLayout({
4
4
  children,
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+
3
+ import Error from 'next/error';
4
+
5
+ export default function NotFound() {
6
+ return (
7
+ <html lang="en">
8
+ <body>
9
+ <Error statusCode={404} />
10
+ </body>
11
+ </html>
12
+ );
13
+ }
@@ -0,0 +1,5 @@
1
+ import { buildApiConfig } from '@vitnode/core/vitnode.config';
2
+
3
+ export const vitNodeApiConfig = buildApiConfig({
4
+ plugins: [],
5
+ });
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "eslint-config-typescript-vitnode/tsconfig",
4
+ "compilerOptions": {
5
+ "target": "ESNext",
6
+ "module": "esnext",
7
+ "moduleResolution": "bundler",
8
+ "noEmit": true,
9
+ "baseUrl": ".",
10
+ "plugins": [
11
+ {
12
+ "name": "next"
13
+ }
14
+ ],
15
+ "paths": {
16
+ "@/*": ["./src/*"]
17
+ }
18
+ },
19
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
20
+ "exclude": ["node_modules"]
21
+ }
@@ -0,0 +1,26 @@
1
+ import { existsSync } from 'fs';
2
+ import { cp, mkdir } from 'fs/promises';
3
+ import ora from 'ora';
4
+ import { dirname, join } from 'path';
5
+ import color from 'picocolors';
6
+ import { fileURLToPath } from 'url';
7
+ import { isFolderEmpty } from '../helpers/is-folder-empty.js';
8
+ export const createVitNode = async ({ root, appName, packageManager, }) => {
9
+ const spinner = ora(`Creating a new VitNode app in ${color.green(root)}. Using ${color.green(packageManager)}...`).start();
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ const templatePath = join(__dirname, '..', '..', '..', 'copy-of-vitnode-app');
13
+ if (!existsSync(templatePath)) {
14
+ spinner.fail(`\n${color.red('Error!')} Template path ${color.cyan(templatePath)} does not exist.`);
15
+ process.exit(1);
16
+ }
17
+ await mkdir(root, { recursive: true });
18
+ if (!isFolderEmpty(root, appName)) {
19
+ process.exit(1);
20
+ }
21
+ spinner.text = 'Copying files...';
22
+ await cp(join(templatePath, 'root'), root, {
23
+ recursive: true,
24
+ });
25
+ spinner.succeed(`${color.green('Success!')} Created ${color.cyan(appName)} at ${color.cyan(root)}`);
26
+ };
@@ -0,0 +1,24 @@
1
+ import { exec } from 'child_process';
2
+ export const execShellCommand = async (cmd) => {
3
+ return new Promise(resolve => {
4
+ exec(cmd, (error, stdout, stderr) => {
5
+ if (error) {
6
+ resolve(undefined);
7
+ }
8
+ const result = stdout ? stdout : stderr;
9
+ resolve(result.replace(/\s+/g, ''));
10
+ });
11
+ });
12
+ };
13
+ export const getAvailablePackageManagers = async () => {
14
+ const [npm, pnpm, bun] = await Promise.all([
15
+ execShellCommand('npm --version'),
16
+ execShellCommand('pnpm --version'),
17
+ execShellCommand('bun --version'),
18
+ ]);
19
+ return {
20
+ pnpm,
21
+ npm,
22
+ bun,
23
+ };
24
+ };
@@ -0,0 +1,2 @@
1
+ import { readFileSync } from 'fs';
2
+ export const packageJson = JSON.parse(readFileSync(new URL('../../../package.json', import.meta.url), 'utf-8'));
@@ -0,0 +1,49 @@
1
+ import { lstatSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import colors from 'picocolors';
4
+ export function isFolderEmpty(root, name) {
5
+ const validFiles = [
6
+ '.DS_Store',
7
+ '.git',
8
+ '.gitattributes',
9
+ '.gitignore',
10
+ '.gitlab-ci.yml',
11
+ '.hg',
12
+ '.hgcheck',
13
+ '.hgignore',
14
+ '.idea',
15
+ '.npmignore',
16
+ '.travis.yml',
17
+ 'LICENSE',
18
+ 'Thumbs.db',
19
+ 'docs',
20
+ 'mkdocs.yml',
21
+ 'npm-debug.log',
22
+ 'yarn-debug.log',
23
+ 'yarn-error.log',
24
+ 'yarnrc.yml',
25
+ '.yarn',
26
+ ];
27
+ const conflicts = readdirSync(root).filter(file => !validFiles.includes(file) &&
28
+ !file.endsWith('.iml'));
29
+ if (conflicts.length > 0) {
30
+ console.log(`The directory ${colors.green(name)} contains files that could conflict:\n`);
31
+ for (const file of conflicts) {
32
+ try {
33
+ const stats = lstatSync(join(root, file));
34
+ if (stats.isDirectory()) {
35
+ console.log(`${colors.blue(file)}/`);
36
+ }
37
+ else {
38
+ console.log(file);
39
+ }
40
+ }
41
+ catch {
42
+ console.log(file);
43
+ }
44
+ }
45
+ console.log('\nEither try using a new directory name, or remove the files listed above.\n');
46
+ return false;
47
+ }
48
+ return true;
49
+ }
@@ -0,0 +1,11 @@
1
+ import { W_OK } from 'constants';
2
+ import { access } from 'fs/promises';
3
+ export async function isWriteable(directory) {
4
+ try {
5
+ await access(directory, W_OK);
6
+ return true;
7
+ }
8
+ catch (_) {
9
+ return false;
10
+ }
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import validateProjectName from 'validate-npm-package-name';
2
+ export const validateNpmName = ({ name, }) => {
3
+ const nameValidation = validateProjectName(name);
4
+ if (nameValidation.validForNewPackages) {
5
+ return { valid: true };
6
+ }
7
+ return {
8
+ valid: false,
9
+ problems: [
10
+ ...(nameValidation.errors ?? []),
11
+ ...(nameValidation.warnings ?? []),
12
+ ],
13
+ };
14
+ };
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ import { input } from '@inquirer/prompts';
3
+ import { Command, Option } from 'commander';
4
+ import { basename, resolve } from 'path';
5
+ import color from 'picocolors';
6
+ import { createVitNode } from './create/create-vitnode.js';
7
+ import { packageJson } from './helpers/get-package-json.js';
8
+ import { validateNpmName } from './helpers/validate-pkg.js';
9
+ import { createPlugin } from './plugin/index.js';
10
+ import { createQuestionsCli } from './questions.js';
11
+ import { validationProject } from './validation.js';
12
+ const [major] = process.versions.node.split('.').map(Number);
13
+ if (major < 20) {
14
+ console.error(color.red(`\nError: VitNode requires Node.js version 20 or higher.\nYou are currently using Node.js ${process.versions.node}\n`));
15
+ process.exit(1);
16
+ }
17
+ process.on('uncaughtException', (error) => {
18
+ if (error.name === 'ExitPromptError') {
19
+ console.log(color.dim('👋 VitNode setup cancelled - see you next time!'));
20
+ process.exit(0);
21
+ }
22
+ console.error(color.red('An unexpected error occurred:'));
23
+ console.error(color.red(error.stack ?? error.message));
24
+ process.exit(1);
25
+ });
26
+ const init = async () => {
27
+ let projectPath = '';
28
+ const program = new Command()
29
+ .version(packageJson.version)
30
+ .argument('[project-directory]')
31
+ .usage(`${color.green('[project-directory]')} [options]`)
32
+ .action(name => {
33
+ projectPath = name;
34
+ });
35
+ program.addOption(new Option('--package-manager <package-manager>', 'Specify the package manager to use').choices(['npm', 'pnpm', 'bun']));
36
+ program.option('--eslint', 'Initialize with eslint config.');
37
+ program.option('--skip-install', 'Skip installing packages after initializing the project.');
38
+ program.option('--plugin', 'Enable plugin mode.');
39
+ program.parse(process.argv);
40
+ const opts = program.opts();
41
+ if (opts.plugin) {
42
+ await createPlugin(projectPath);
43
+ return;
44
+ }
45
+ if (!projectPath) {
46
+ projectPath = await input({
47
+ message: 'What is your project named?',
48
+ default: 'my-vitnode',
49
+ validate: (name) => {
50
+ const validation = validateNpmName({ name: basename(resolve(name)) });
51
+ if (validation.valid)
52
+ return true;
53
+ return `Invalid project name: ${validation.problems[0]}`;
54
+ },
55
+ });
56
+ }
57
+ const { appName, root } = await validationProject(projectPath);
58
+ const options = await createQuestionsCli(program);
59
+ await createVitNode({
60
+ appName,
61
+ root,
62
+ ...options,
63
+ });
64
+ };
65
+ await init();