create-vitnode-app 1.2.0-canary.4 → 1.2.0-canary.41
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.md +1 -1
- package/README.md +14 -5
- package/copy-of-vitnode-app/.vscode/settings.json +6 -0
- package/copy-of-vitnode-app/api/.gitignore_template +29 -0
- package/copy-of-vitnode-app/api/drizzle.config.ts +12 -0
- package/copy-of-vitnode-app/api/package.json +43 -0
- package/copy-of-vitnode-app/api/src/index.ts +27 -0
- package/copy-of-vitnode-app/api/src/vitnode.api.config.ts +23 -0
- package/copy-of-vitnode-app/api/tsconfig.json +16 -0
- package/copy-of-vitnode-app/api-bun/src/index.ts +13 -0
- package/copy-of-vitnode-app/api-single-app/.gitignore_template +37 -0
- package/copy-of-vitnode-app/{root → api-single-app}/src/app/api/[...route]/route.ts +0 -2
- package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +18 -0
- package/copy-of-vitnode-app/docker/docker-compose.yml +20 -0
- package/copy-of-vitnode-app/eslint/.prettierrc.mjs +1 -1
- package/copy-of-vitnode-app/eslint/eslint.config.mjs +1 -1
- package/copy-of-vitnode-app/monorepo/.gitignore_template +41 -0
- package/copy-of-vitnode-app/monorepo/turbo.json +47 -0
- package/copy-of-vitnode-app/root/.env.example +9 -0
- package/copy-of-vitnode-app/root/.gitignore_template +37 -0
- package/copy-of-vitnode-app/root/README.md +48 -0
- package/copy-of-vitnode-app/root/global.d.ts +9 -0
- package/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx +6 -1
- package/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx +9 -17
- package/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx +8 -5
- package/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx +11 -7
- package/copy-of-vitnode-app/root/src/app/global.css +127 -0
- package/copy-of-vitnode-app/root/src/middleware.ts +24 -0
- package/copy-of-vitnode-app/root/src/vitnode.config.ts +32 -0
- package/copy-of-vitnode-app/root/tsconfig.json +1 -1
- package/dist/src/create/create-package-json.js +255 -0
- package/dist/src/create/create-vitnode.js +119 -3
- package/dist/src/helpers/init-vitnode.js +15 -0
- package/dist/src/helpers/install-dependencies.js +67 -0
- package/dist/src/helpers/is-online.js +38 -0
- package/dist/src/helpers/is-writeable.js +1 -1
- package/dist/src/index.js +4 -1
- package/dist/src/prepare/prepare.js +1 -1
- package/dist/src/questions.js +38 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/copy-of-vitnode-app/root/.prettierrc.mjs +0 -11
- package/copy-of-vitnode-app/root/src/app/[locale]/admin/not-found.tsx +0 -5
- package/copy-of-vitnode-app/root/src/app/globals.css +0 -119
- package/copy-of-vitnode-app/root/src/app/not-found.tsx +0 -13
- package/copy-of-vitnode-app/root/src/vitnode.api.config.ts +0 -5
- /package/copy-of-vitnode-app/{root → api-single-app}/drizzle.config.ts +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
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
|
+
:root:not(.dark) {
|
|
9
|
+
--background: oklch(0.96 0.01 250);
|
|
10
|
+
--foreground: oklch(0.18 0.01 250);
|
|
11
|
+
--card: oklch(1 0 0);
|
|
12
|
+
--card-foreground: oklch(0.22 0.01 250);
|
|
13
|
+
--popover: oklch(1 0 0);
|
|
14
|
+
--popover-foreground: oklch(0.22 0.01 250);
|
|
15
|
+
--primary: oklch(0.51 0.16 262.61);
|
|
16
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
17
|
+
--secondary: oklch(0.93 0.01 250);
|
|
18
|
+
--secondary-foreground: oklch(0.25 0.01 250);
|
|
19
|
+
--muted: oklch(0.95 0.01 250);
|
|
20
|
+
--muted-foreground: oklch(0.35 0.01 250);
|
|
21
|
+
--accent: oklch(0.92 0.01 250);
|
|
22
|
+
--accent-foreground: oklch(0.25 0.01 250);
|
|
23
|
+
--destructive: oklch(0.6 0.2 24.45);
|
|
24
|
+
--warn: oklch(0.54 0.12 82.58);
|
|
25
|
+
--border: oklch(0.9 0.01 250);
|
|
26
|
+
--input: oklch(0.9 0.01 250);
|
|
27
|
+
--ring: oklch(0.7 0.13 250);
|
|
28
|
+
--chart-1: oklch(0.65 0.13 250);
|
|
29
|
+
--chart-2: oklch(0.6 0.11 260);
|
|
30
|
+
--chart-3: oklch(0.45 0.09 250);
|
|
31
|
+
--chart-4: oklch(0.8 0.13 250);
|
|
32
|
+
--chart-5: oklch(0.75 0.13 250);
|
|
33
|
+
--sidebar: var(--card);
|
|
34
|
+
--sidebar-foreground: oklch(0.22 0.01 250);
|
|
35
|
+
--sidebar-primary: var(--primary);
|
|
36
|
+
--sidebar-primary-foreground: var(--primary-foreground);
|
|
37
|
+
--sidebar-accent: oklch(0.97 0.01 250);
|
|
38
|
+
--sidebar-accent-foreground: oklch(0.25 0.01 250);
|
|
39
|
+
--sidebar-border: oklch(0.91 0.01 250);
|
|
40
|
+
--sidebar-ring: oklch(0.7 0.13 250);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.dark {
|
|
44
|
+
--background: oklch(0.16 0.01 250);
|
|
45
|
+
--foreground: oklch(0.96 0.01 250);
|
|
46
|
+
--card: oklch(0.18 0.01 250);
|
|
47
|
+
--card-foreground: oklch(0.96 0.01 250);
|
|
48
|
+
--popover: oklch(0.18 0.01 250);
|
|
49
|
+
--popover-foreground: oklch(0.96 0.01 250);
|
|
50
|
+
--primary: oklch(0.51 0.16 262.61);
|
|
51
|
+
--primary-foreground: oklch(0.98 0 0);
|
|
52
|
+
--secondary: oklch(0.22 0.01 250);
|
|
53
|
+
--secondary-foreground: oklch(0.96 0.01 250);
|
|
54
|
+
--muted: oklch(0.22 0.01 250);
|
|
55
|
+
--muted-foreground: oklch(0.7 0.01 250);
|
|
56
|
+
--accent: oklch(0.28 0.01 250);
|
|
57
|
+
--destructive: oklch(0.62 0.2 25.35);
|
|
58
|
+
--warn: oklch(0.76 0.18 81.84);
|
|
59
|
+
--border: oklch(0.26 0.01 250);
|
|
60
|
+
--input: oklch(0.26 0.01 250);
|
|
61
|
+
--ring: oklch(0.54 0.13 250);
|
|
62
|
+
--chart-1: oklch(0.45 0.09 250);
|
|
63
|
+
--chart-2: oklch(0.6 0.11 260);
|
|
64
|
+
--chart-3: oklch(0.75 0.13 250);
|
|
65
|
+
--chart-4: oklch(0.8 0.13 250);
|
|
66
|
+
--chart-5: oklch(0.65 0.13 250);
|
|
67
|
+
--sidebar: var(--card);
|
|
68
|
+
--sidebar-foreground: oklch(0.96 0.01 250);
|
|
69
|
+
--sidebar-primary: var(--primary);
|
|
70
|
+
--sidebar-primary-foreground: var(--primary-foreground);
|
|
71
|
+
--sidebar-accent: oklch(0.22 0.01 250);
|
|
72
|
+
--sidebar-accent-foreground: oklch(0.96 0.01 250);
|
|
73
|
+
--sidebar-border: oklch(0.21 0.01 250);
|
|
74
|
+
--sidebar-ring: oklch(0.54 0.13 250);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:root {
|
|
78
|
+
--radius: 0.625rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@theme inline {
|
|
82
|
+
--color-background: var(--background);
|
|
83
|
+
--color-foreground: var(--foreground);
|
|
84
|
+
--color-card: var(--card);
|
|
85
|
+
--color-card-foreground: var(--card-foreground);
|
|
86
|
+
--color-popover: var(--popover);
|
|
87
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
88
|
+
--color-primary: var(--primary);
|
|
89
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
90
|
+
--color-secondary: var(--secondary);
|
|
91
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
92
|
+
--color-muted: var(--muted);
|
|
93
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
94
|
+
--color-accent: var(--accent);
|
|
95
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
96
|
+
--color-destructive: var(--destructive);
|
|
97
|
+
--color-warn: var(--warn);
|
|
98
|
+
--color-border: var(--border);
|
|
99
|
+
--color-input: var(--input);
|
|
100
|
+
--color-ring: var(--ring);
|
|
101
|
+
--color-chart-1: var(--chart-1);
|
|
102
|
+
--color-chart-2: var(--chart-2);
|
|
103
|
+
--color-chart-3: var(--chart-3);
|
|
104
|
+
--color-chart-4: var(--chart-4);
|
|
105
|
+
--color-chart-5: var(--chart-5);
|
|
106
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
107
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
108
|
+
--radius-lg: var(--radius);
|
|
109
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
110
|
+
--color-sidebar: var(--sidebar);
|
|
111
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
112
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
113
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
114
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
115
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
116
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
117
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@layer base {
|
|
121
|
+
* {
|
|
122
|
+
@apply border-border outline-ring/50;
|
|
123
|
+
}
|
|
124
|
+
body {
|
|
125
|
+
@apply bg-background text-foreground;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import createMiddleware from 'next-intl/middleware';
|
|
2
|
+
|
|
3
|
+
import { vitNodeConfig } from './vitnode.config';
|
|
4
|
+
|
|
5
|
+
export default createMiddleware({
|
|
6
|
+
locales: vitNodeConfig.i18n.locales.map(locale => locale.code),
|
|
7
|
+
defaultLocale: vitNodeConfig.i18n.defaultLocale,
|
|
8
|
+
localePrefix: vitNodeConfig.i18n.localePrefix,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const config = {
|
|
12
|
+
matcher: [
|
|
13
|
+
// Enable a redirect to a matching locale at the root
|
|
14
|
+
'/',
|
|
15
|
+
|
|
16
|
+
// Set a cookie to remember the previous locale for
|
|
17
|
+
// all requests that have a locale prefix
|
|
18
|
+
'/(en)/:path*',
|
|
19
|
+
|
|
20
|
+
// Enable redirects that add missing locales
|
|
21
|
+
// (e.g. `/pathnames` -> `/en/pathnames`)
|
|
22
|
+
'/((?!_next|_vercel|api|.*\\..*).*)',
|
|
23
|
+
],
|
|
24
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { buildConfig, handleRequestConfig } from '@vitnode/core/vitnode.config';
|
|
2
|
+
import { getRequestConfig } from 'next-intl/server';
|
|
3
|
+
|
|
4
|
+
export const vitNodeConfig = buildConfig({
|
|
5
|
+
metadata: {
|
|
6
|
+
title: 'VitNode',
|
|
7
|
+
shortTitle: 'VitNode',
|
|
8
|
+
},
|
|
9
|
+
plugins: [],
|
|
10
|
+
i18n: {
|
|
11
|
+
locales: [
|
|
12
|
+
{
|
|
13
|
+
code: 'en',
|
|
14
|
+
name: 'English',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
defaultLocale: 'en',
|
|
18
|
+
},
|
|
19
|
+
theme: {
|
|
20
|
+
defaultTheme: 'light',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// This is the request config for the app. It will be used in the app router.
|
|
25
|
+
export default getRequestConfig(
|
|
26
|
+
async ({ requestLocale }) =>
|
|
27
|
+
await handleRequestConfig({
|
|
28
|
+
requestLocale,
|
|
29
|
+
vitNodeConfig,
|
|
30
|
+
pathToMessages: async path => await import(`./locales/${path}`),
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { getAvailablePackageManagers } from '../helpers/get-available-package-managers.js';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
export const createPackageJSON = async ({ appName, packageManager, root, eslint, docker, mode, monorepo, }) => {
|
|
8
|
+
const availablePackageManagers = await getAvailablePackageManagers();
|
|
9
|
+
const pkg = JSON.parse(await readFile(join(__dirname, '..', '..', '..', 'package.json'), 'utf-8'));
|
|
10
|
+
const pkgVitNodeVersion = `^${pkg.version}`;
|
|
11
|
+
const monorepoStructure = {
|
|
12
|
+
api: join(root, 'apps', 'api'),
|
|
13
|
+
web: join(root, 'apps', 'web'),
|
|
14
|
+
};
|
|
15
|
+
if (mode === 'apiMonorepo' || monorepo) {
|
|
16
|
+
const rootPackageJson = {
|
|
17
|
+
name: appName,
|
|
18
|
+
private: true,
|
|
19
|
+
scripts: {
|
|
20
|
+
'db:migrate': 'turbo db:migrate',
|
|
21
|
+
'db:push': 'turbo db:push',
|
|
22
|
+
init: 'turbo init',
|
|
23
|
+
dev: 'turbo dev',
|
|
24
|
+
build: 'turbo build',
|
|
25
|
+
start: 'turbo start',
|
|
26
|
+
...(eslint
|
|
27
|
+
? {
|
|
28
|
+
lint: 'eslint .',
|
|
29
|
+
'lint:fix': 'eslint . --fix',
|
|
30
|
+
}
|
|
31
|
+
: {}),
|
|
32
|
+
...(docker
|
|
33
|
+
? {
|
|
34
|
+
'docker:dev': `docker compose -f ./docker-compose.yml -p ${appName}-vitnode-dev up -d`,
|
|
35
|
+
}
|
|
36
|
+
: {}),
|
|
37
|
+
},
|
|
38
|
+
devDependencies: {
|
|
39
|
+
'@types/node': '^24',
|
|
40
|
+
'@vitnode/eslint-config': pkgVitNodeVersion,
|
|
41
|
+
...(eslint
|
|
42
|
+
? {
|
|
43
|
+
'prettier-plugin-tailwindcss': '^0.6.14',
|
|
44
|
+
prettier: '^3.6.2',
|
|
45
|
+
}
|
|
46
|
+
: {}),
|
|
47
|
+
turbo: '^2.5.4',
|
|
48
|
+
typescript: '^5.8.3',
|
|
49
|
+
zod: '^3.25.74',
|
|
50
|
+
},
|
|
51
|
+
packageManager: `${packageManager}@${availablePackageManagers[packageManager]}`,
|
|
52
|
+
workspaces: ['apps/*', 'plugins/*'],
|
|
53
|
+
};
|
|
54
|
+
await writeFile(join(root, 'package.json'), JSON.stringify(rootPackageJson, null, 2));
|
|
55
|
+
}
|
|
56
|
+
const apiPackageJson = {
|
|
57
|
+
name: mode === 'apiMonorepo' || monorepo ? 'api' : appName,
|
|
58
|
+
version: '0.1.0',
|
|
59
|
+
private: true,
|
|
60
|
+
type: 'module',
|
|
61
|
+
scripts: {
|
|
62
|
+
'db:push': 'vitnode push',
|
|
63
|
+
'db:migrate': 'vitnode migrate',
|
|
64
|
+
init: 'vitnode init --api',
|
|
65
|
+
...(packageManager === 'bun'
|
|
66
|
+
? {
|
|
67
|
+
dev: 'vitnode init --api && bun run --hot src/index.ts',
|
|
68
|
+
start: 'NODE_ENV=production bun run src/index.ts',
|
|
69
|
+
}
|
|
70
|
+
: {
|
|
71
|
+
dev: 'vitnode init --api && tsx watch src/index.ts',
|
|
72
|
+
build: 'tsc && tsc-alias -p tsconfig.json',
|
|
73
|
+
start: 'node dist/index.js',
|
|
74
|
+
}),
|
|
75
|
+
...(eslint
|
|
76
|
+
? {
|
|
77
|
+
lint: 'eslint .',
|
|
78
|
+
'lint:fix': 'eslint . --fix',
|
|
79
|
+
}
|
|
80
|
+
: {}),
|
|
81
|
+
...(docker && mode === 'onlyApi'
|
|
82
|
+
? {
|
|
83
|
+
'docker:dev': `docker compose -f ./docker-compose.yml -p ${appName}-vitnode-dev up -d`,
|
|
84
|
+
}
|
|
85
|
+
: {}),
|
|
86
|
+
'drizzle-kit': 'drizzle-kit',
|
|
87
|
+
},
|
|
88
|
+
dependencies: {
|
|
89
|
+
'@hono/zod-openapi': '^0.19.8',
|
|
90
|
+
'@hono/zod-validator': '^0.7.0',
|
|
91
|
+
'@react-email/components': '^0.2.0',
|
|
92
|
+
'@vitnode/core': pkgVitNodeVersion,
|
|
93
|
+
'drizzle-kit': '^0.31.3',
|
|
94
|
+
'drizzle-orm': '^0.44.2',
|
|
95
|
+
hono: '^4.8.3',
|
|
96
|
+
'next-intl': '^4.3.1',
|
|
97
|
+
react: '^19.1',
|
|
98
|
+
'react-dom': '^19.1',
|
|
99
|
+
zod: '^3.25.67',
|
|
100
|
+
},
|
|
101
|
+
devDependencies: {
|
|
102
|
+
'@hono/node-server': '^1.15.0',
|
|
103
|
+
...(packageManager === 'bun'
|
|
104
|
+
? {
|
|
105
|
+
'@types/bun': 'latest',
|
|
106
|
+
}
|
|
107
|
+
: {}),
|
|
108
|
+
'@types/node': '^24',
|
|
109
|
+
'@types/react': '^19.1',
|
|
110
|
+
'@types/react-dom': '^19.1',
|
|
111
|
+
'@vitnode/eslint-config': pkgVitNodeVersion,
|
|
112
|
+
dotenv: '^17.2.0',
|
|
113
|
+
...(eslint
|
|
114
|
+
? {
|
|
115
|
+
eslint: '^9.30.1',
|
|
116
|
+
...(mode === 'onlyApi'
|
|
117
|
+
? {
|
|
118
|
+
'prettier-plugin-tailwindcss': '^0.6.14',
|
|
119
|
+
prettier: '^3.6.2',
|
|
120
|
+
}
|
|
121
|
+
: {}),
|
|
122
|
+
}
|
|
123
|
+
: {}),
|
|
124
|
+
'react-email': '^4.1.1',
|
|
125
|
+
'tsc-alias': '^1.8.16',
|
|
126
|
+
tsx: '^4.20.3',
|
|
127
|
+
typescript: '^5.8.3',
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
if (mode === 'singleApp') {
|
|
131
|
+
const packageJson = {
|
|
132
|
+
name: monorepo ? 'web' : appName,
|
|
133
|
+
version: '0.1.0',
|
|
134
|
+
private: true,
|
|
135
|
+
type: 'module',
|
|
136
|
+
scripts: {
|
|
137
|
+
'db:push': 'vitnode push',
|
|
138
|
+
'db:migrate': 'vitnode migrate',
|
|
139
|
+
init: 'vitnode init',
|
|
140
|
+
dev: 'vitnode init && next dev --turbopack',
|
|
141
|
+
build: 'next build --turbopack',
|
|
142
|
+
start: 'next start',
|
|
143
|
+
...(eslint
|
|
144
|
+
? {
|
|
145
|
+
lint: 'eslint .',
|
|
146
|
+
'lint:fix': 'eslint . --fix',
|
|
147
|
+
}
|
|
148
|
+
: {}),
|
|
149
|
+
...(docker
|
|
150
|
+
? {
|
|
151
|
+
'docker:dev': `docker compose -f ./docker-compose.yml -p ${appName}-vitnode-dev up -d`,
|
|
152
|
+
}
|
|
153
|
+
: {}),
|
|
154
|
+
'drizzle-kit': 'drizzle-kit',
|
|
155
|
+
},
|
|
156
|
+
dependencies: {
|
|
157
|
+
'@hono/zod-openapi': '^0.19.9',
|
|
158
|
+
'@hono/zod-validator': '^0.7.0',
|
|
159
|
+
'@hookform/resolvers': '^5.1.1',
|
|
160
|
+
'@react-email/components': '^0.2.0',
|
|
161
|
+
'@vitnode/core': pkgVitNodeVersion,
|
|
162
|
+
'babel-plugin-react-compiler': '19.1.0-rc.2',
|
|
163
|
+
'drizzle-kit': '^0.31.4',
|
|
164
|
+
'drizzle-orm': '^0.44.2',
|
|
165
|
+
hono: '^4.8.4',
|
|
166
|
+
'lucide-react': '^0.525.0',
|
|
167
|
+
next: '^15.3.5',
|
|
168
|
+
'next-intl': '^4.3.4',
|
|
169
|
+
react: '^19.1',
|
|
170
|
+
'react-dom': '^19.1',
|
|
171
|
+
'react-hook-form': '^7.60.0',
|
|
172
|
+
sonner: '^2.0.6',
|
|
173
|
+
zod: '^3.25.74',
|
|
174
|
+
},
|
|
175
|
+
devDependencies: {
|
|
176
|
+
'@tailwindcss/postcss': '^4.1.11',
|
|
177
|
+
'@types/node': '^24',
|
|
178
|
+
'@types/react': '^19.1',
|
|
179
|
+
'@types/react-dom': '^19.1',
|
|
180
|
+
'@vitnode/eslint-config': pkgVitNodeVersion,
|
|
181
|
+
...(eslint
|
|
182
|
+
? {
|
|
183
|
+
eslint: '^9.30.1',
|
|
184
|
+
'prettier-plugin-tailwindcss': '^0.6.14',
|
|
185
|
+
prettier: '^3.6.2',
|
|
186
|
+
}
|
|
187
|
+
: {}),
|
|
188
|
+
'react-email': '^4.1.1',
|
|
189
|
+
turbo: '^2.5.4',
|
|
190
|
+
tailwindcss: '^4.1.11',
|
|
191
|
+
'tw-animate-css': '^1.3.5',
|
|
192
|
+
typescript: '^5.8.3',
|
|
193
|
+
},
|
|
194
|
+
packageManager: `${packageManager}@${availablePackageManagers[packageManager]}`,
|
|
195
|
+
};
|
|
196
|
+
await writeFile(join(monorepo ? monorepoStructure.web : root, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
197
|
+
}
|
|
198
|
+
else if (mode === 'apiMonorepo') {
|
|
199
|
+
await writeFile(join(root, 'apps', 'api', 'package.json'), JSON.stringify(apiPackageJson, null, 2));
|
|
200
|
+
const webPackageJson = {
|
|
201
|
+
name: 'web',
|
|
202
|
+
version: '0.1.0',
|
|
203
|
+
private: true,
|
|
204
|
+
type: 'module',
|
|
205
|
+
scripts: {
|
|
206
|
+
init: 'vitnode init --web',
|
|
207
|
+
dev: 'vitnode init --web && next dev --turbopack',
|
|
208
|
+
build: 'next build --turbopack',
|
|
209
|
+
start: 'next start',
|
|
210
|
+
...(eslint
|
|
211
|
+
? {
|
|
212
|
+
lint: 'eslint .',
|
|
213
|
+
'lint:fix': 'eslint . --fix',
|
|
214
|
+
}
|
|
215
|
+
: {}),
|
|
216
|
+
},
|
|
217
|
+
dependencies: {
|
|
218
|
+
'@vitnode/core': pkgVitNodeVersion,
|
|
219
|
+
'babel-plugin-react-compiler': '19.1.0-rc.2',
|
|
220
|
+
'lucide-react': '^0.525.0',
|
|
221
|
+
next: '^15.3.5',
|
|
222
|
+
'next-intl': '^4.3.4',
|
|
223
|
+
react: '^19.1',
|
|
224
|
+
'react-dom': '^19.1',
|
|
225
|
+
'react-hook-form': '^7.60.0',
|
|
226
|
+
sonner: '^2.0.6',
|
|
227
|
+
},
|
|
228
|
+
devDependencies: {
|
|
229
|
+
'@playwright/test': '^1.53.2',
|
|
230
|
+
'@tailwindcss/postcss': '^4.1.11',
|
|
231
|
+
'@types/mdx': '^2.0.13',
|
|
232
|
+
'@types/node': '^24',
|
|
233
|
+
'@types/react': '^19.1',
|
|
234
|
+
'@types/react-dom': '^19.1',
|
|
235
|
+
'@vitnode/eslint-config': pkgVitNodeVersion,
|
|
236
|
+
'class-variance-authority': '^0.7.1',
|
|
237
|
+
...(eslint
|
|
238
|
+
? {
|
|
239
|
+
eslint: '^9.30.1',
|
|
240
|
+
}
|
|
241
|
+
: {}),
|
|
242
|
+
postcss: '^8.5.6',
|
|
243
|
+
'react-email': '^4.1.1',
|
|
244
|
+
tailwindcss: '^4.1.11',
|
|
245
|
+
'tw-animate-css': '^1.3.5',
|
|
246
|
+
typescript: '^5.8.3',
|
|
247
|
+
zod: '^3.25.74',
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
await writeFile(join(root, 'apps', 'web', 'package.json'), JSON.stringify(webPackageJson, null, 2));
|
|
251
|
+
}
|
|
252
|
+
else if (mode === 'onlyApi') {
|
|
253
|
+
await writeFile(join(monorepo ? monorepoStructure.api : root, 'package.json'), JSON.stringify(apiPackageJson, null, 2));
|
|
254
|
+
}
|
|
255
|
+
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
|
-
import { cp, mkdir } from 'fs/promises';
|
|
2
|
+
import { copyFile, cp, mkdir, readFile, rename, writeFile } from 'fs/promises';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
import color from 'picocolors';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import { generateMigrationsVitnode, initFilesVitnode, } from '../helpers/init-vitnode.js';
|
|
8
|
+
import { installDependencies } from '../helpers/install-dependencies.js';
|
|
7
9
|
import { isFolderEmpty } from '../helpers/is-folder-empty.js';
|
|
8
|
-
|
|
10
|
+
import { createPackageJSON } from './create-package-json.js';
|
|
11
|
+
export const createVitNode = async ({ root, appName, packageManager, eslint, install, docker, mode, monorepo, }) => {
|
|
9
12
|
const spinner = ora(`Creating a new VitNode app in ${color.green(root)}. Using ${color.green(packageManager)}...`).start();
|
|
10
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
14
|
const __dirname = dirname(__filename);
|
|
@@ -18,9 +21,122 @@ export const createVitNode = async ({ root, appName, packageManager, }) => {
|
|
|
18
21
|
if (!isFolderEmpty(root, appName)) {
|
|
19
22
|
process.exit(1);
|
|
20
23
|
}
|
|
24
|
+
const monorepoStructure = {
|
|
25
|
+
api: join(root, 'apps', 'api'),
|
|
26
|
+
web: join(root, 'apps', 'web'),
|
|
27
|
+
};
|
|
28
|
+
spinner.text = 'Preparing the project structure...';
|
|
29
|
+
if (monorepo || mode === 'apiMonorepo') {
|
|
30
|
+
const dirsToCreate = [];
|
|
31
|
+
if (mode === 'apiMonorepo' || (monorepo && mode === 'onlyApi')) {
|
|
32
|
+
dirsToCreate.push(monorepoStructure.api);
|
|
33
|
+
}
|
|
34
|
+
if (mode === 'apiMonorepo' || (monorepo && mode === 'singleApp')) {
|
|
35
|
+
dirsToCreate.push(monorepoStructure.web);
|
|
36
|
+
}
|
|
37
|
+
await Promise.all(dirsToCreate.map(async (dir) => mkdir(dir, { recursive: true })));
|
|
38
|
+
}
|
|
21
39
|
spinner.text = 'Copying files...';
|
|
22
|
-
await cp(join(templatePath, '
|
|
40
|
+
await cp(join(templatePath, '.vscode'), join(root, '.vscode'), {
|
|
23
41
|
recursive: true,
|
|
24
42
|
});
|
|
43
|
+
if (mode === 'singleApp') {
|
|
44
|
+
await Promise.all([
|
|
45
|
+
cp(join(templatePath, 'root'), monorepo ? monorepoStructure.web : root, {
|
|
46
|
+
recursive: true,
|
|
47
|
+
}),
|
|
48
|
+
cp(join(templatePath, 'api-single-app'), monorepo ? monorepoStructure.web : root, {
|
|
49
|
+
recursive: true,
|
|
50
|
+
}),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
else if (mode === 'apiMonorepo') {
|
|
54
|
+
await Promise.all([
|
|
55
|
+
cp(join(templatePath, 'root'), monorepoStructure.web, {
|
|
56
|
+
recursive: true,
|
|
57
|
+
}),
|
|
58
|
+
cp(join(templatePath, 'api'), monorepoStructure.api, {
|
|
59
|
+
recursive: true,
|
|
60
|
+
}),
|
|
61
|
+
]);
|
|
62
|
+
if (packageManager === 'bun') {
|
|
63
|
+
await cp(join(templatePath, 'api-bun'), monorepoStructure.api, {
|
|
64
|
+
recursive: true,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (mode === 'onlyApi') {
|
|
69
|
+
await cp(join(templatePath, 'api'), monorepo ? monorepoStructure.api : root, {
|
|
70
|
+
recursive: true,
|
|
71
|
+
});
|
|
72
|
+
if (packageManager === 'bun') {
|
|
73
|
+
await cp(join(templatePath, 'api-bun'), monorepo ? monorepoStructure.api : root, {
|
|
74
|
+
recursive: true,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (mode === 'apiMonorepo' || monorepo) {
|
|
79
|
+
await cp(join(templatePath, 'monorepo'), root, {
|
|
80
|
+
recursive: true,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (eslint) {
|
|
84
|
+
spinner.text = 'Copying eslint files...';
|
|
85
|
+
await cp(join(templatePath, 'eslint'), root, {
|
|
86
|
+
recursive: true,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
spinner.text = 'Renaming special files...';
|
|
90
|
+
await rename(join(root, '.gitignore_template'), join(root, '.gitignore'));
|
|
91
|
+
if (mode === 'apiMonorepo' || monorepo) {
|
|
92
|
+
if (existsSync(join(monorepoStructure.api, '.gitignore_template'))) {
|
|
93
|
+
await rename(join(monorepoStructure.api, '.gitignore_template'), join(monorepoStructure.api, '.gitignore'));
|
|
94
|
+
}
|
|
95
|
+
if (existsSync(join(monorepoStructure.web, '.gitignore_template'))) {
|
|
96
|
+
await rename(join(monorepoStructure.web, '.gitignore_template'), join(monorepoStructure.web, '.gitignore'));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
spinner.text = 'Creating package.json...';
|
|
100
|
+
await createPackageJSON({
|
|
101
|
+
root,
|
|
102
|
+
appName,
|
|
103
|
+
packageManager,
|
|
104
|
+
eslint,
|
|
105
|
+
docker,
|
|
106
|
+
mode,
|
|
107
|
+
monorepo,
|
|
108
|
+
});
|
|
109
|
+
if (mode === 'apiMonorepo' && packageManager === 'pnpm') {
|
|
110
|
+
spinner.text = 'Creating pnpm-workspace.yaml...';
|
|
111
|
+
const pnpmWorkspaceContent = `packages:\n - 'apps/*'\n - 'plugins/*'\n`;
|
|
112
|
+
await writeFile(join(root, 'pnpm-workspace.yaml'), pnpmWorkspaceContent);
|
|
113
|
+
}
|
|
114
|
+
if (docker) {
|
|
115
|
+
spinner.text = 'Copying docker files...';
|
|
116
|
+
await copyFile(join(templatePath, 'docker', 'docker-compose.yml'), join(root, 'docker-compose.yml'));
|
|
117
|
+
const dockerComposePath = join(root, 'docker-compose.yml');
|
|
118
|
+
const dockerComposeContent = await readFile(dockerComposePath, 'utf-8');
|
|
119
|
+
const updatedContent = dockerComposeContent.replace(/vitnode_postgres_dev/g, `${appName}_vitnode_postgres_dev`);
|
|
120
|
+
await writeFile(dockerComposePath, updatedContent);
|
|
121
|
+
}
|
|
122
|
+
if (install) {
|
|
123
|
+
spinner.text = 'Installing dependencies...';
|
|
124
|
+
await installDependencies({
|
|
125
|
+
packageManager,
|
|
126
|
+
cwd: root,
|
|
127
|
+
});
|
|
128
|
+
if (mode !== 'onlyApi') {
|
|
129
|
+
spinner.text = 'Initializing VitNode files...';
|
|
130
|
+
initFilesVitnode({
|
|
131
|
+
packageManager,
|
|
132
|
+
cwd: mode === 'apiMonorepo' || monorepo ? monorepoStructure.web : root,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
spinner.text = 'Generating migrations...';
|
|
136
|
+
generateMigrationsVitnode({
|
|
137
|
+
packageManager,
|
|
138
|
+
cwd: mode === 'apiMonorepo' || monorepo ? monorepoStructure.api : root,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
25
141
|
spinner.succeed(`${color.green('Success!')} Created ${color.cyan(appName)} at ${color.cyan(root)}`);
|
|
26
142
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
export const initFilesVitnode = ({ packageManager: pm, cwd, }) => {
|
|
3
|
+
const packageManager = pm.split('@')[0];
|
|
4
|
+
const args = ['vitnode', 'prepare-plugins'];
|
|
5
|
+
spawn(packageManager, args, {
|
|
6
|
+
cwd,
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
|
+
export const generateMigrationsVitnode = ({ packageManager: pm, cwd, }) => {
|
|
10
|
+
const packageManager = pm.split('@')[0];
|
|
11
|
+
const args = ['vitnode', 'migrate', '--generate'];
|
|
12
|
+
spawn(packageManager, args, {
|
|
13
|
+
cwd,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import color from 'picocolors';
|
|
3
|
+
import { getOnline } from './is-online.js';
|
|
4
|
+
export const installDependencies = async ({ packageManager: pm, cwd, }) => {
|
|
5
|
+
const packageManager = pm.split('@')[0];
|
|
6
|
+
const isOnline = await getOnline();
|
|
7
|
+
const args = ['install'];
|
|
8
|
+
if (!isOnline) {
|
|
9
|
+
console.log(color.yellow('You appear to be offline.\nFalling back to the local cache.'));
|
|
10
|
+
args.push('--offline');
|
|
11
|
+
}
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
let stdout = '';
|
|
14
|
+
let stderr = '';
|
|
15
|
+
const child = spawn(packageManager, args, {
|
|
16
|
+
stdio: 'pipe',
|
|
17
|
+
cwd,
|
|
18
|
+
env: {
|
|
19
|
+
...process.env,
|
|
20
|
+
ADBLOCK: '1',
|
|
21
|
+
NODE_ENV: 'development',
|
|
22
|
+
DISABLE_OPENCOLLECTIVE: '1',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
child.stdout?.on('data', (data) => {
|
|
26
|
+
const output = data.toString();
|
|
27
|
+
stdout += output;
|
|
28
|
+
});
|
|
29
|
+
child.stderr?.on('data', (data) => {
|
|
30
|
+
const output = data.toString();
|
|
31
|
+
stderr += output;
|
|
32
|
+
});
|
|
33
|
+
child.on('close', code => {
|
|
34
|
+
if (code !== 0) {
|
|
35
|
+
console.error(color.red(`\n❌ Installation failed with exit code: ${code}`));
|
|
36
|
+
if (stderr) {
|
|
37
|
+
console.error(color.red('Error output:'));
|
|
38
|
+
console.error(stderr);
|
|
39
|
+
}
|
|
40
|
+
if (stdout) {
|
|
41
|
+
console.log(color.yellow('Standard output:'));
|
|
42
|
+
console.log(stdout);
|
|
43
|
+
}
|
|
44
|
+
if (stderr.includes('ENOTFOUND') || stderr.includes('network')) {
|
|
45
|
+
console.error(color.yellow('💡 Network error detected. Please check your internet connection.'));
|
|
46
|
+
}
|
|
47
|
+
else if (stderr.includes('EACCES') ||
|
|
48
|
+
stderr.includes('permission denied')) {
|
|
49
|
+
console.error(color.yellow('💡 Permission error detected. Try running with elevated privileges or check file permissions.'));
|
|
50
|
+
}
|
|
51
|
+
else if (stderr.includes('ENOSPC')) {
|
|
52
|
+
console.error(color.yellow('💡 Disk space error detected. Please free up some disk space.'));
|
|
53
|
+
}
|
|
54
|
+
else if (stderr.includes('ERR_PNPM_PEER_DEP_ISSUES')) {
|
|
55
|
+
console.error(color.yellow('💡 Peer dependency issues detected. Consider using --force flag or resolve conflicts manually.'));
|
|
56
|
+
}
|
|
57
|
+
reject(new Error(`Failed to install dependencies using ${packageManager}. Exit code: ${code}\n${stderr || stdout}`));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
child.on('error', error => {
|
|
63
|
+
console.error(color.red(`❌ Failed to start ${packageManager}:`), error.message);
|
|
64
|
+
reject(new Error(`Failed to start ${packageManager}: ${error.message}`));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
};
|