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.
Files changed (47) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +14 -5
  3. package/copy-of-vitnode-app/.vscode/settings.json +6 -0
  4. package/copy-of-vitnode-app/api/.gitignore_template +29 -0
  5. package/copy-of-vitnode-app/api/drizzle.config.ts +12 -0
  6. package/copy-of-vitnode-app/api/package.json +43 -0
  7. package/copy-of-vitnode-app/api/src/index.ts +27 -0
  8. package/copy-of-vitnode-app/api/src/vitnode.api.config.ts +23 -0
  9. package/copy-of-vitnode-app/api/tsconfig.json +16 -0
  10. package/copy-of-vitnode-app/api-bun/src/index.ts +13 -0
  11. package/copy-of-vitnode-app/api-single-app/.gitignore_template +37 -0
  12. package/copy-of-vitnode-app/{root → api-single-app}/src/app/api/[...route]/route.ts +0 -2
  13. package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +18 -0
  14. package/copy-of-vitnode-app/docker/docker-compose.yml +20 -0
  15. package/copy-of-vitnode-app/eslint/.prettierrc.mjs +1 -1
  16. package/copy-of-vitnode-app/eslint/eslint.config.mjs +1 -1
  17. package/copy-of-vitnode-app/monorepo/.gitignore_template +41 -0
  18. package/copy-of-vitnode-app/monorepo/turbo.json +47 -0
  19. package/copy-of-vitnode-app/root/.env.example +9 -0
  20. package/copy-of-vitnode-app/root/.gitignore_template +37 -0
  21. package/copy-of-vitnode-app/root/README.md +48 -0
  22. package/copy-of-vitnode-app/root/global.d.ts +9 -0
  23. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx +6 -1
  24. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx +9 -17
  25. package/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx +8 -5
  26. package/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx +11 -7
  27. package/copy-of-vitnode-app/root/src/app/global.css +127 -0
  28. package/copy-of-vitnode-app/root/src/middleware.ts +24 -0
  29. package/copy-of-vitnode-app/root/src/vitnode.config.ts +32 -0
  30. package/copy-of-vitnode-app/root/tsconfig.json +1 -1
  31. package/dist/src/create/create-package-json.js +255 -0
  32. package/dist/src/create/create-vitnode.js +119 -3
  33. package/dist/src/helpers/init-vitnode.js +15 -0
  34. package/dist/src/helpers/install-dependencies.js +67 -0
  35. package/dist/src/helpers/is-online.js +38 -0
  36. package/dist/src/helpers/is-writeable.js +1 -1
  37. package/dist/src/index.js +4 -1
  38. package/dist/src/prepare/prepare.js +1 -1
  39. package/dist/src/questions.js +38 -0
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +8 -8
  42. package/copy-of-vitnode-app/root/.prettierrc.mjs +0 -11
  43. package/copy-of-vitnode-app/root/src/app/[locale]/admin/not-found.tsx +0 -5
  44. package/copy-of-vitnode-app/root/src/app/globals.css +0 -119
  45. package/copy-of-vitnode-app/root/src/app/not-found.tsx +0 -13
  46. package/copy-of-vitnode-app/root/src/vitnode.api.config.ts +0 -5
  47. /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
+ );
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/tsconfig",
3
- "extends": "eslint-config-typescript-vitnode/tsconfig",
3
+ "extends": "@vitnode/eslint-config/tsconfig",
4
4
  "compilerOptions": {
5
5
  "target": "ESNext",
6
6
  "module": "esnext",
@@ -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
- export const createVitNode = async ({ root, appName, packageManager, }) => {
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, 'root'), root, {
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
+ };