create-vitnode-app 1.2.0-canary.7 → 1.2.0-canary.71

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 (79) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +25 -5
  3. package/copy-of-vitnode-app/.vscode/settings.json +11 -0
  4. package/copy-of-vitnode-app/README.md +40 -0
  5. package/copy-of-vitnode-app/api/.gitignore_template +29 -0
  6. package/copy-of-vitnode-app/api/drizzle.config.ts +12 -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 +16 -0
  11. package/copy-of-vitnode-app/api-single-app/.env.example +8 -0
  12. package/copy-of-vitnode-app/api-single-app/.gitignore_template +37 -0
  13. package/copy-of-vitnode-app/api-single-app/drizzle.config.ts +12 -0
  14. package/copy-of-vitnode-app/api-single-app/src/app/api/[...route]/route.ts +16 -0
  15. package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +18 -0
  16. package/copy-of-vitnode-app/docker/docker-compose.yml +20 -0
  17. package/copy-of-vitnode-app/eslint/.prettierrc.mjs +1 -1
  18. package/copy-of-vitnode-app/eslint/eslint.config.mjs +18 -2
  19. package/copy-of-vitnode-app/eslint-react/eslint.config.mjs +19 -0
  20. package/copy-of-vitnode-app/monorepo/.gitignore_template +41 -0
  21. package/copy-of-vitnode-app/monorepo/apps/api/.env.example +8 -0
  22. package/copy-of-vitnode-app/monorepo/apps/web/.env.example +1 -0
  23. package/copy-of-vitnode-app/monorepo/turbo.json +40 -0
  24. package/copy-of-vitnode-app/root/.env.example +2 -4
  25. package/copy-of-vitnode-app/root/.gitignore_template +37 -0
  26. package/copy-of-vitnode-app/root/global.d.ts +9 -0
  27. package/copy-of-vitnode-app/root/next.config.ts +3 -6
  28. package/copy-of-vitnode-app/root/postcss.config.mjs +1 -1
  29. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/[...rest]/page.tsx +1 -1
  30. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx +8 -3
  31. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/not-found.tsx +1 -1
  32. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx +20 -27
  33. package/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx +2 -2
  34. package/copy-of-vitnode-app/root/src/app/[locale]/admin/page.tsx +1 -1
  35. package/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx +20 -17
  36. package/copy-of-vitnode-app/root/src/app/global-error.tsx +8 -11
  37. package/copy-of-vitnode-app/root/src/app/global.css +63 -64
  38. package/copy-of-vitnode-app/root/src/app/layout.tsx +1 -1
  39. package/copy-of-vitnode-app/root/src/proxy.ts +24 -0
  40. package/copy-of-vitnode-app/root/src/vitnode.config.ts +32 -0
  41. package/copy-of-vitnode-app/root/tsconfig.json +1 -1
  42. package/copy-of-vitnode-plugin/root/.swcrc +25 -0
  43. package/copy-of-vitnode-plugin/root/global.d.ts +10 -0
  44. package/copy-of-vitnode-plugin/root/npmignore.template +19 -0
  45. package/copy-of-vitnode-plugin/root/tsconfig.json +25 -0
  46. package/dist/src/create/create-package-json.js +251 -56
  47. package/dist/src/create/create-vitnode.js +197 -28
  48. package/dist/src/create/package-versions.js +40 -0
  49. package/dist/src/helpers/get-available-package-managers.js +6 -6
  50. package/dist/src/helpers/get-package-json.js +2 -2
  51. package/dist/src/helpers/get-package-manager-from-root.js +15 -0
  52. package/dist/src/helpers/get-vitnode-package-version.js +9 -0
  53. package/dist/src/helpers/init-vitnode.js +21 -0
  54. package/dist/src/helpers/install-dependencies.js +55 -14
  55. package/dist/src/helpers/is-folder-empty.js +25 -26
  56. package/dist/src/helpers/is-online.js +16 -16
  57. package/dist/src/helpers/is-writeable.js +3 -3
  58. package/dist/src/helpers/validate-pkg.js +1 -1
  59. package/dist/src/helpers/with-If.js +1 -0
  60. package/dist/src/index.js +29 -26
  61. package/dist/src/plugin/create/add-plugin-to-workspace.js +74 -0
  62. package/dist/src/plugin/create/create-package-json.js +62 -0
  63. package/dist/src/plugin/create/create-plugin-vitnode.js +72 -0
  64. package/dist/src/plugin/index.js +17 -6
  65. package/dist/src/plugin/questions.js +14 -0
  66. package/dist/src/plugin/validation.js +76 -0
  67. package/dist/src/prepare/prepare.js +11 -8
  68. package/dist/src/questions.js +52 -15
  69. package/dist/src/validation.js +15 -15
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +16 -9
  72. package/copy-of-vitnode-app/root/.prettierrc.mjs +0 -11
  73. package/copy-of-vitnode-app/root/README.md +0 -1
  74. package/copy-of-vitnode-app/root/drizzle.config.ts +0 -12
  75. package/copy-of-vitnode-app/root/gitignore_template +0 -43
  76. package/copy-of-vitnode-app/root/src/app/[locale]/admin/not-found.tsx +0 -5
  77. package/copy-of-vitnode-app/root/src/app/api/[...route]/route.ts +0 -18
  78. package/copy-of-vitnode-app/root/src/app/not-found.tsx +0 -13
  79. package/copy-of-vitnode-app/root/src/vitnode.api.config.ts +0 -5
@@ -1,66 +1,261 @@
1
- import { readFile, writeFile } from 'fs/promises';
2
- import { dirname, join } from 'path';
3
- import { fileURLToPath } from 'url';
4
- const __filename = fileURLToPath(import.meta.url);
5
- const __dirname = dirname(__filename);
6
- export const createPackageJSON = async ({ appName, packageManager, root, eslint, }) => {
7
- const pkg = JSON.parse(await readFile(join(__dirname, '..', '..', '..', 'package.json'), 'utf-8'));
8
- const packageJson = {
9
- name: appName,
10
- version: '0.1.0',
1
+ import { writeFile } from "fs/promises";
2
+ import { join } from "path";
3
+ import { getAvailablePackageManagers } from "../helpers/get-available-package-managers.js";
4
+ import { getVitnodePackageVersion } from "../helpers/get-vitnode-package-version.js";
5
+ import { withIf } from "../helpers/with-If.js";
6
+ import { versionsPackageJson } from "./package-versions.js";
7
+ const writeJson = async (path, data) => writeFile(path, JSON.stringify(data, null, 2));
8
+ const paths = (root) => ({
9
+ root,
10
+ api: join(root, "apps", "api"),
11
+ web: join(root, "apps", "web"),
12
+ });
13
+ const eslintScripts = {
14
+ lint: "eslint .",
15
+ "lint:fix": "eslint . --fix",
16
+ };
17
+ const dockerDevScript = (appName) => `docker compose -f ./docker-compose.yml -p ${appName}-vitnode-dev up -d`;
18
+ const rootScripts = (enableEslint, enableDocker, appName) => ({
19
+ "db:migrate": "turbo db:migrate",
20
+ init: "turbo init",
21
+ dev: "turbo dev",
22
+ build: "turbo build",
23
+ start: "turbo start",
24
+ ...withIf(enableEslint, {
25
+ lint: "turbo lint",
26
+ "lint:fix": "turbo lint:fix",
27
+ }),
28
+ ...withIf(enableDocker, { "docker:dev": dockerDevScript(appName) }),
29
+ });
30
+ const apiScripts = (pm, eslint, docker, onlyApi, appName) => ({
31
+ "db:migrate": "vitnode migrate",
32
+ init: "vitnode init --api",
33
+ ...(pm === "bun"
34
+ ? {
35
+ dev: "vitnode init --api && bun run --hot src/index.ts",
36
+ start: "NODE_ENV=production bun run src/index.ts",
37
+ }
38
+ : {
39
+ dev: "vitnode init --api && tsx watch src/index.ts",
40
+ build: "tsc && tsc-alias -p tsconfig.json",
41
+ start: "node dist/index.js",
42
+ }),
43
+ "dev:email": "email dev --dir src/emails",
44
+ ...withIf(eslint, eslintScripts),
45
+ ...withIf(docker && onlyApi, { "docker:dev": dockerDevScript(appName) }),
46
+ "drizzle-kit": "drizzle-kit",
47
+ });
48
+ const singleAppScripts = (eslint, docker, appName) => ({
49
+ "db:migrate": "vitnode migrate",
50
+ init: "vitnode init",
51
+ dev: "vitnode init && next dev",
52
+ "dev:email": "email dev --dir src/emails",
53
+ build: "next build",
54
+ start: "next start",
55
+ ...withIf(eslint, eslintScripts),
56
+ ...withIf(docker, { "docker:dev": dockerDevScript(appName) }),
57
+ "drizzle-kit": "drizzle-kit",
58
+ });
59
+ const webScripts = (eslint) => ({
60
+ init: "vitnode init --web",
61
+ dev: "vitnode init --web && next dev",
62
+ build: "next build",
63
+ start: "next start",
64
+ ...withIf(eslint, eslintScripts),
65
+ });
66
+ const baseDevDeps = (eslint, includePrettier) => ({
67
+ "@types/node": versionsPackageJson.typesNode,
68
+ "@vitnode/config": "",
69
+ ...withIf(eslint, {
70
+ eslint: versionsPackageJson.eslint,
71
+ ...withIf(includePrettier, {
72
+ prettier: versionsPackageJson.prettier,
73
+ "prettier-plugin-tailwindcss": versionsPackageJson.prettierTailwind,
74
+ }),
75
+ }),
76
+ });
77
+ const rootDevDeps = (eslint) => ({
78
+ ...baseDevDeps(eslint, true),
79
+ turbo: versionsPackageJson.turbo,
80
+ typescript: versionsPackageJson.typescript,
81
+ zod: versionsPackageJson.zod,
82
+ });
83
+ const apiDeps = {
84
+ "@hono/zod-openapi": versionsPackageJson.honoZodOpenapi,
85
+ "@hono/zod-validator": versionsPackageJson.honoZodValidator,
86
+ "@react-email/components": versionsPackageJson.reactEmailComponents,
87
+ "@vitnode/core": "",
88
+ "drizzle-kit": versionsPackageJson.drizzleKit,
89
+ "drizzle-orm": versionsPackageJson.drizzleOrm,
90
+ hono: versionsPackageJson.hono,
91
+ "next-intl": versionsPackageJson.nextIntl,
92
+ react: versionsPackageJson.react,
93
+ "react-dom": versionsPackageJson.reactDom,
94
+ "use-intl": versionsPackageJson.useIntl,
95
+ zod: versionsPackageJson.zod,
96
+ };
97
+ const apiDevDeps = (pm, eslint) => ({
98
+ "@hono/node-server": "^1.19",
99
+ ...(pm === "bun" ? { "@types/bun": versionsPackageJson.typesBun } : {}),
100
+ "@types/node": versionsPackageJson.typesNode,
101
+ "@types/react": versionsPackageJson.typesReact,
102
+ "@types/react-dom": versionsPackageJson.typesReactDom,
103
+ "@vitnode/config": "",
104
+ dotenv: versionsPackageJson.dotenv,
105
+ ...withIf(eslint, {
106
+ eslint: versionsPackageJson.eslint,
107
+ }),
108
+ "react-email": versionsPackageJson.reactEmail,
109
+ "tsc-alias": versionsPackageJson.tscAlias,
110
+ tsx: versionsPackageJson.tsx,
111
+ typescript: versionsPackageJson.typescript,
112
+ });
113
+ const singleAppDeps = {
114
+ "@hono/zod-openapi": versionsPackageJson.honoZodOpenapi,
115
+ "@hono/zod-validator": versionsPackageJson.honoZodValidator,
116
+ "@hookform/resolvers": versionsPackageJson.rhfResolvers,
117
+ "@react-email/components": versionsPackageJson.reactEmailComponents,
118
+ "@vitnode/core": "",
119
+ "drizzle-kit": versionsPackageJson.drizzleKit,
120
+ "drizzle-orm": versionsPackageJson.drizzleOrm,
121
+ hono: versionsPackageJson.hono,
122
+ "lucide-react": versionsPackageJson.lucide,
123
+ next: versionsPackageJson.nextSingle,
124
+ "next-intl": versionsPackageJson.nextIntl,
125
+ react: versionsPackageJson.react,
126
+ "react-dom": versionsPackageJson.reactDom,
127
+ "react-hook-form": versionsPackageJson.rhf,
128
+ sonner: versionsPackageJson.sonner,
129
+ "use-intl": versionsPackageJson.useIntl,
130
+ zod: versionsPackageJson.zod,
131
+ };
132
+ const singleAppDevDeps = (eslint) => ({
133
+ "@tailwindcss/postcss": versionsPackageJson.tailwindPostcss,
134
+ "@types/node": versionsPackageJson.typesNode,
135
+ "@types/react": versionsPackageJson.typesReact,
136
+ "@types/react-dom": versionsPackageJson.typesReactDom,
137
+ "@vitnode/config": "",
138
+ "babel-plugin-react-compiler": versionsPackageJson.babelPluginReactCompiler,
139
+ ...withIf(eslint, {
140
+ eslint: versionsPackageJson.eslint,
141
+ prettier: versionsPackageJson.prettier,
142
+ "prettier-plugin-tailwindcss": versionsPackageJson.prettierTailwind,
143
+ }),
144
+ "react-email": versionsPackageJson.reactEmail,
145
+ turbo: versionsPackageJson.turbo,
146
+ tailwindcss: versionsPackageJson.tailwind,
147
+ "tw-animate-css": versionsPackageJson.twAnimateCss,
148
+ typescript: versionsPackageJson.typescript,
149
+ });
150
+ const webDeps = {
151
+ "@vitnode/core": "",
152
+ "lucide-react": versionsPackageJson.lucide,
153
+ next: versionsPackageJson.nextSingle,
154
+ "next-intl": versionsPackageJson.nextIntl,
155
+ react: versionsPackageJson.react,
156
+ "react-dom": versionsPackageJson.reactDom,
157
+ "react-hook-form": versionsPackageJson.rhf,
158
+ sonner: versionsPackageJson.sonner,
159
+ };
160
+ const webDevDeps = (eslint) => ({
161
+ "@hookform/resolvers": versionsPackageJson.rhfResolvers,
162
+ "@tailwindcss/postcss": versionsPackageJson.tailwindPostcss,
163
+ "@types/node": versionsPackageJson.typesNode,
164
+ "@types/react": versionsPackageJson.typesReact,
165
+ "@types/react-dom": versionsPackageJson.typesReactDom,
166
+ "@vitnode/config": "",
167
+ "babel-plugin-react-compiler": versionsPackageJson.babelPluginReactCompiler,
168
+ "class-variance-authority": versionsPackageJson.cva,
169
+ ...withIf(eslint, { eslint: versionsPackageJson.eslint }),
170
+ postcss: versionsPackageJson.postcss,
171
+ tailwindcss: versionsPackageJson.tailwind,
172
+ "tw-animate-css": versionsPackageJson.twAnimateCss,
173
+ typescript: versionsPackageJson.typescript,
174
+ zod: versionsPackageJson.zod,
175
+ });
176
+ export const createPackageJSON = async ({ appName, packageManager, root, eslint, docker, mode, monorepo, }) => {
177
+ const vitnodeVersionRange = await getVitnodePackageVersion();
178
+ const pmVersions = await getAvailablePackageManagers();
179
+ const pmSpec = `${packageManager}@${pmVersions[packageManager]}`;
180
+ const p = paths(root);
181
+ const isApiMonorepo = mode === "apiMonorepo" || !!monorepo;
182
+ const isOnlyApi = mode === "onlyApi";
183
+ const isSingleApp = mode === "singleApp";
184
+ if (isApiMonorepo) {
185
+ const rootPkg = {
186
+ name: appName,
187
+ private: true,
188
+ scripts: rootScripts(eslint, !!docker, appName),
189
+ devDependencies: {
190
+ ...rootDevDeps(eslint),
191
+ "@vitnode/config": vitnodeVersionRange,
192
+ },
193
+ packageManager: pmSpec,
194
+ workspaces: ["apps/*", "plugins/*"],
195
+ };
196
+ await writeJson(join(p.root, "package.json"), rootPkg);
197
+ }
198
+ const apiPkg = {
199
+ name: isApiMonorepo ? "api" : appName,
200
+ version: "0.1.0",
11
201
  private: true,
12
- type: 'module',
13
- scripts: {
14
- 'drizzle-kit': 'drizzle-kit',
15
- 'db:push': 'drizzle-kit push',
16
- 'db:migrate': 'drizzle-kit up && drizzle-kit generate && drizzle-kit migrate',
17
- dev: 'vitnode init && next dev --turbopack',
18
- build: 'next build --turbopack',
19
- start: 'next start',
20
- ...(eslint
21
- ? {
22
- lint: 'eslint .',
23
- 'lint:fix': 'eslint . --fix',
24
- }
25
- : {}),
26
- },
202
+ type: "module",
203
+ scripts: apiScripts(packageManager, eslint, !!docker, mode === "onlyApi", appName),
27
204
  dependencies: {
28
- '@hono/zod-openapi': '^0.19.8',
29
- '@hono/zod-validator': '^0.7.0',
30
- '@hookform/resolvers': '^5.0.1',
31
- '@react-email/components': '^0.0.41',
32
- '@vitnode/core': `^${pkg.version}`,
33
- 'babel-plugin-react-compiler': '19.1.0-rc.2',
34
- 'drizzle-kit': '^0.31.1',
35
- 'drizzle-orm': '^0.44.1',
36
- hono: '^4.7.10',
37
- 'lucide-react': '^0.511.0',
38
- next: '^15.3.3',
39
- 'next-intl': '^4.1.0',
40
- react: '^19.1.0',
41
- 'react-dom': '^19.1.0',
42
- 'react-hook-form': '^7.56.4',
43
- sonner: '^2.0.3',
44
- zod: '^3.25.42',
205
+ ...apiDeps,
206
+ "@vitnode/core": vitnodeVersionRange,
45
207
  },
46
208
  devDependencies: {
47
- '@tailwindcss/postcss': '^4.1.8',
48
- '@types/node': '^22.15.29',
49
- '@types/react': '^19.1.6',
50
- '@types/react-dom': '^19.1.5',
51
- ...(eslint
209
+ ...apiDevDeps(packageManager, eslint),
210
+ "@vitnode/config": vitnodeVersionRange,
211
+ ...(eslint && mode === "onlyApi"
52
212
  ? {
53
- eslint: '^9.27.0',
54
- 'eslint-config-typescript-vitnode': `^${pkg.version}`,
55
- prettier: '^3.5.3',
213
+ prettier: versionsPackageJson.prettier,
214
+ "prettier-plugin-tailwindcss": versionsPackageJson.prettierTailwind,
56
215
  }
57
216
  : {}),
58
- 'react-email': '^4.0.15',
59
- tailwindcss: '^4.1.8',
60
- 'tw-animate-css': '^1.3.2',
61
- typescript: '^5.8.3',
217
+ ...(packageManager === "bun" ? {} : {}),
62
218
  },
63
- packageManager,
64
219
  };
65
- await writeFile(join(root, 'apps', 'frontend', 'package.json'), JSON.stringify(packageJson, null, 2));
220
+ if (isSingleApp) {
221
+ const singlePkg = {
222
+ name: monorepo ? "web" : appName,
223
+ version: "0.1.0",
224
+ private: true,
225
+ type: "module",
226
+ scripts: singleAppScripts(eslint, !!docker, appName),
227
+ dependencies: {
228
+ ...singleAppDeps,
229
+ "@vitnode/core": vitnodeVersionRange,
230
+ },
231
+ devDependencies: {
232
+ ...singleAppDevDeps(eslint),
233
+ "@vitnode/config": vitnodeVersionRange,
234
+ },
235
+ packageManager: pmSpec,
236
+ };
237
+ await writeJson(join(monorepo ? p.web : p.root, "package.json"), singlePkg);
238
+ }
239
+ if (mode === "apiMonorepo") {
240
+ await writeJson(join(p.api, "package.json"), apiPkg);
241
+ const webPkg = {
242
+ name: "web",
243
+ version: "0.1.0",
244
+ private: true,
245
+ type: "module",
246
+ scripts: webScripts(eslint),
247
+ dependencies: {
248
+ ...webDeps,
249
+ "@vitnode/core": vitnodeVersionRange,
250
+ },
251
+ devDependencies: {
252
+ ...webDevDeps(eslint),
253
+ "@vitnode/config": vitnodeVersionRange,
254
+ },
255
+ };
256
+ await writeJson(join(p.web, "package.json"), webPkg);
257
+ }
258
+ if (isOnlyApi) {
259
+ await writeJson(join(monorepo ? p.api : p.root, "package.json"), apiPkg);
260
+ }
66
261
  };
@@ -1,55 +1,224 @@
1
- import { randomBytes } from 'crypto';
2
- import { existsSync } from 'fs';
3
- import { copyFile, cp, mkdir, readFile, rename, writeFile } from 'fs/promises';
4
- import ora from 'ora';
5
- import { dirname, join } from 'path';
6
- import color from 'picocolors';
7
- import { fileURLToPath } from 'url';
8
- import { installDependencies } from '../helpers/install-dependencies.js';
9
- import { isFolderEmpty } from '../helpers/is-folder-empty.js';
10
- import { createPackageJSON } from './create-package-json.js';
11
- export const createVitNode = async ({ root, appName, packageManager, eslint, install, }) => {
1
+ import { existsSync } from "node:fs";
2
+ import { copyFile, cp, mkdir, readFile, rename, writeFile, } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import ora from "ora";
6
+ import color from "picocolors";
7
+ import { generateMigrationsVitnode, initFilesVitnode, } from "../helpers/init-vitnode.js";
8
+ import { installDependencies } from "../helpers/install-dependencies.js";
9
+ import { isFolderEmpty } from "../helpers/is-folder-empty.js";
10
+ import { createPackageJSON } from "./create-package-json.js";
11
+ export const createVitNode = async ({ root, appName, packageManager, eslint, install, docker, mode, monorepo, }) => {
12
12
  const spinner = ora(`Creating a new VitNode app in ${color.green(root)}. Using ${color.green(packageManager)}...`).start();
13
13
  const __filename = fileURLToPath(import.meta.url);
14
14
  const __dirname = dirname(__filename);
15
- const templatePath = join(__dirname, '..', '..', '..', 'copy-of-vitnode-app');
15
+ const templatePath = join(__dirname, "..", "..", "..", "copy-of-vitnode-app");
16
16
  if (!existsSync(templatePath)) {
17
- spinner.fail(`\n${color.red('Error!')} Template path ${color.cyan(templatePath)} does not exist.`);
17
+ spinner.fail(`\n${color.red("Error!")} Template path ${color.cyan(templatePath)} does not exist.`);
18
18
  process.exit(1);
19
19
  }
20
20
  await mkdir(root, { recursive: true });
21
21
  if (!isFolderEmpty(root, appName)) {
22
22
  process.exit(1);
23
23
  }
24
- spinner.text = 'Copying files...';
25
- await cp(join(templatePath, 'root'), root, {
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
+ monorepo = true;
31
+ const dirsToCreate = [];
32
+ if (mode === "apiMonorepo" || (monorepo && mode === "onlyApi")) {
33
+ dirsToCreate.push(monorepoStructure.api);
34
+ }
35
+ if (mode === "apiMonorepo" || (monorepo && mode === "singleApp")) {
36
+ dirsToCreate.push(monorepoStructure.web);
37
+ }
38
+ await Promise.all(dirsToCreate.map(async (dir) => mkdir(dir, { recursive: true })));
39
+ }
40
+ spinner.text = "Copying files...";
41
+ await cp(join(templatePath, ".vscode"), join(root, ".vscode"), {
26
42
  recursive: true,
27
43
  });
28
- if (eslint) {
29
- spinner.text = 'Copying eslint files...';
30
- await cp(join(templatePath, 'eslint'), root, {
44
+ if (mode === "singleApp") {
45
+ await Promise.all([
46
+ cp(join(templatePath, "root"), monorepo ? monorepoStructure.web : root, {
47
+ recursive: true,
48
+ }),
49
+ cp(join(templatePath, "api-single-app"), monorepo ? monorepoStructure.web : root, {
50
+ recursive: true,
51
+ }),
52
+ ]);
53
+ }
54
+ else if (mode === "apiMonorepo") {
55
+ await Promise.all([
56
+ cp(join(templatePath, "root"), monorepoStructure.web, {
57
+ recursive: true,
58
+ }),
59
+ cp(join(templatePath, "api"), monorepoStructure.api, {
60
+ recursive: true,
61
+ }),
62
+ ]);
63
+ if (packageManager === "bun") {
64
+ await cp(join(templatePath, "api-bun"), monorepoStructure.api, {
65
+ recursive: true,
66
+ });
67
+ }
68
+ }
69
+ else if (mode === "onlyApi") {
70
+ await cp(join(templatePath, "api"), monorepo ? monorepoStructure.api : root, {
31
71
  recursive: true,
32
72
  });
73
+ if (packageManager === "bun") {
74
+ await cp(join(templatePath, "api-bun"), monorepo ? monorepoStructure.api : root, {
75
+ recursive: true,
76
+ });
77
+ }
33
78
  }
34
- spinner.text = 'Renaming special files...';
35
- await rename(join(root, 'gitignore_template'), join(root, '.gitignore'));
36
- spinner.text = 'Creating package.json...';
79
+ if (mode === "apiMonorepo" || (monorepo && mode !== "singleApp")) {
80
+ await cp(join(templatePath, "monorepo"), root, {
81
+ recursive: true,
82
+ });
83
+ }
84
+ else if (monorepo && mode === "singleApp") {
85
+ await copyFile(join(templatePath, "monorepo", "turbo.json"), join(root, "turbo.json"));
86
+ await copyFile(join(templatePath, "monorepo", ".gitignore_template"), join(root, ".gitignore_template"));
87
+ }
88
+ if (eslint) {
89
+ spinner.text = "Copying ESLint & Prettier files...";
90
+ if (monorepo) {
91
+ if (existsSync(monorepoStructure.api)) {
92
+ await cp(join(templatePath, "eslint"), monorepoStructure.api, {
93
+ recursive: true,
94
+ });
95
+ }
96
+ if (existsSync(monorepoStructure.web)) {
97
+ await cp(join(templatePath, "eslint-react"), monorepoStructure.web, {
98
+ recursive: true,
99
+ });
100
+ }
101
+ }
102
+ else {
103
+ if (mode === "onlyApi") {
104
+ await cp(join(templatePath, "eslint"), root, {
105
+ recursive: true,
106
+ });
107
+ }
108
+ else if (mode === "singleApp") {
109
+ await cp(join(templatePath, "eslint-react"), root, {
110
+ recursive: true,
111
+ });
112
+ }
113
+ }
114
+ }
115
+ spinner.text = "Renaming special files...";
116
+ await rename(join(root, ".gitignore_template"), join(root, ".gitignore"));
117
+ if (mode === "apiMonorepo" || monorepo) {
118
+ if (existsSync(join(monorepoStructure.api, ".gitignore_template"))) {
119
+ await rename(join(monorepoStructure.api, ".gitignore_template"), join(monorepoStructure.api, ".gitignore"));
120
+ }
121
+ if (existsSync(join(monorepoStructure.web, ".gitignore_template"))) {
122
+ await rename(join(monorepoStructure.web, ".gitignore_template"), join(monorepoStructure.web, ".gitignore"));
123
+ }
124
+ }
125
+ spinner.text = "Creating package.json...";
37
126
  await createPackageJSON({
38
127
  root,
39
128
  appName,
40
129
  packageManager,
41
130
  eslint,
131
+ docker,
132
+ mode,
133
+ monorepo,
42
134
  });
43
- spinner.text = 'Changing .env file...';
44
- await copyFile(join(root, '.env.template'), join(root, '.env'));
45
- const envPath = join(root, '.env');
46
- const newEnv = (await readFile(envPath, 'utf-8')).replace('LOGIN_TOKEN_SECRET=vitnode_secret', `LOGIN_TOKEN_SECRET=${randomBytes(32).toString('hex')}`);
47
- await writeFile(envPath, newEnv);
135
+ if ((mode === "apiMonorepo" || monorepo) && packageManager === "pnpm") {
136
+ spinner.text = "Creating pnpm-workspace.yaml...";
137
+ const pnpmWorkspaceContent = `packages:\n - 'apps/*'\n - 'plugins/*'\n`;
138
+ await writeFile(join(root, "pnpm-workspace.yaml"), pnpmWorkspaceContent);
139
+ }
140
+ if (docker) {
141
+ spinner.text = "Copying docker files...";
142
+ await copyFile(join(templatePath, "docker", "docker-compose.yml"), join(root, "docker-compose.yml"));
143
+ const dockerComposePath = join(root, "docker-compose.yml");
144
+ const dockerComposeContent = await readFile(dockerComposePath, "utf-8");
145
+ const updatedContent = dockerComposeContent.replace(/vitnode_postgres_dev/g, `${appName}_vitnode_postgres_dev`);
146
+ await writeFile(dockerComposePath, updatedContent);
147
+ }
148
+ spinner.text = "Updating VitNode paths...";
149
+ if ((mode === "apiMonorepo" || monorepo) &&
150
+ packageManager !== "pnpm" &&
151
+ mode !== "onlyApi") {
152
+ const globalCssPath = join(monorepoStructure.web, "src", "app", "global.css");
153
+ const globalCssContent = await readFile(globalCssPath, "utf-8");
154
+ const updatedGlobalCssContent = globalCssContent.replaceAll('@source "../../node_modules/@vitnode/', '@source "../../../../node_modules/@vitnode/');
155
+ await writeFile(globalCssPath, updatedGlobalCssContent);
156
+ }
157
+ if (mode === "apiMonorepo") {
158
+ spinner.text = "Setting up environment variables...";
159
+ const envExamplePath = join(monorepoStructure.web, ".env.example");
160
+ if (existsSync(envExamplePath)) {
161
+ await rename(envExamplePath, join(monorepoStructure.web, ".env"));
162
+ }
163
+ }
48
164
  if (install) {
49
- spinner.text = 'Installing dependencies...';
165
+ spinner.text = "Installing dependencies...";
50
166
  await installDependencies({
51
167
  packageManager,
168
+ cwd: root,
169
+ });
170
+ spinner.text = "Initializing VitNode files...";
171
+ if (mode === "apiMonorepo") {
172
+ initFilesVitnode({
173
+ packageManager,
174
+ cwd: monorepoStructure.web,
175
+ flag: "web",
176
+ });
177
+ initFilesVitnode({
178
+ packageManager,
179
+ cwd: monorepoStructure.api,
180
+ flag: "api",
181
+ });
182
+ initFilesVitnode({
183
+ packageManager,
184
+ cwd: root,
185
+ });
186
+ }
187
+ else {
188
+ initFilesVitnode({
189
+ packageManager,
190
+ cwd: root,
191
+ });
192
+ }
193
+ spinner.text = "Preparing README...";
194
+ await copyFile(join(templatePath, "README.md"), join(root, "README.md"));
195
+ let readmeContent = await readFile(join(root, "README.md"), "utf-8");
196
+ readmeContent = readmeContent.replaceAll("pnpm", packageManager);
197
+ let startUrlsText = "[http://localhost:3000](http://localhost:3000)";
198
+ if (mode === "onlyApi") {
199
+ startUrlsText = "[http://localhost:8080](http://localhost:8080)";
200
+ }
201
+ else if (mode === "apiMonorepo") {
202
+ startUrlsText =
203
+ "[http://localhost:3000](http://localhost:3000) for the Web app and [http://localhost:8080](http://localhost:8080) for the API";
204
+ }
205
+ readmeContent = readmeContent.replace("{{START_URLS}}", startUrlsText);
206
+ await writeFile(join(root, "README.md"), readmeContent);
207
+ spinner.text = "Generating migrations...";
208
+ let migrationsCwd;
209
+ if (mode === "apiMonorepo" || (monorepo && mode !== "singleApp")) {
210
+ migrationsCwd = monorepoStructure.api;
211
+ }
212
+ else if (mode === "singleApp" && monorepo) {
213
+ migrationsCwd = monorepoStructure.web;
214
+ }
215
+ else {
216
+ migrationsCwd = root;
217
+ }
218
+ generateMigrationsVitnode({
219
+ packageManager,
220
+ cwd: migrationsCwd,
52
221
  });
53
222
  }
54
- spinner.succeed(`${color.green('Success!')} Created ${color.cyan(appName)} at ${color.cyan(root)}`);
223
+ spinner.succeed(`${color.green("Success!")} Created ${color.cyan(appName)} at ${color.cyan(root)}`);
55
224
  };
@@ -0,0 +1,40 @@
1
+ export const versionsPackageJson = {
2
+ typesNode: "^24",
3
+ typesReact: "^19.2",
4
+ typesReactDom: "^19.2",
5
+ typesBun: "latest",
6
+ turbo: "^2.6",
7
+ typescript: "^5.9",
8
+ tsx: "^4.20.6",
9
+ tscAlias: "^1.8.16",
10
+ eslint: "^9.39.1",
11
+ prettier: "^3.6.2",
12
+ prettierTailwind: "^0.6.14",
13
+ tailwind: "^4.1.17",
14
+ tailwindPostcss: "^4.1.17",
15
+ postcss: "^8.5.6",
16
+ twAnimateCss: "^1.4.0",
17
+ react: "^19.2",
18
+ reactDom: "^19.2",
19
+ nextSingle: "^16.0",
20
+ nextIntl: "^4.5",
21
+ useIntl: "^4.5",
22
+ rhf: "^7.66.0",
23
+ rhfResolvers: "^5.1.1",
24
+ lucide: "^0.553.0",
25
+ sonner: "^2.0.7",
26
+ dotenv: "^17.2.2",
27
+ drizzleKit: "^0.31.7",
28
+ drizzleOrm: "^0.44.7",
29
+ hono: "^4.10",
30
+ honoZodOpenapi: "^1.1.4",
31
+ honoZodValidator: "^0.7.4",
32
+ reactEmail: "^5.0",
33
+ reactEmailComponents: "^1.0",
34
+ zod: "^4.1.12",
35
+ cva: "^0.7.1",
36
+ babelPluginReactCompiler: "^1.0",
37
+ swcCli: "^0.7.9",
38
+ swcCore: "^1.15.1",
39
+ concurrently: "^9.2.1",
40
+ };
@@ -1,20 +1,20 @@
1
- import { exec } from 'child_process';
1
+ import { exec } from "node:child_process";
2
2
  export const execShellCommand = async (cmd) => {
3
- return new Promise(resolve => {
3
+ return await new Promise(resolve => {
4
4
  exec(cmd, (error, stdout, stderr) => {
5
5
  if (error) {
6
6
  resolve(undefined);
7
7
  }
8
8
  const result = stdout ? stdout : stderr;
9
- resolve(result.replace(/\s+/g, ''));
9
+ resolve(result.replace(/\s+/g, ""));
10
10
  });
11
11
  });
12
12
  };
13
13
  export const getAvailablePackageManagers = async () => {
14
14
  const [npm, pnpm, bun] = await Promise.all([
15
- execShellCommand('npm --version'),
16
- execShellCommand('pnpm --version'),
17
- execShellCommand('bun --version'),
15
+ execShellCommand("npm --version"),
16
+ execShellCommand("pnpm --version"),
17
+ execShellCommand("bun --version"),
18
18
  ]);
19
19
  return {
20
20
  pnpm,
@@ -1,2 +1,2 @@
1
- import { readFileSync } from 'fs';
2
- export const packageJson = JSON.parse(readFileSync(new URL('../../../package.json', import.meta.url), 'utf-8'));
1
+ import { readFileSync } from "node:fs";
2
+ export const packageJson = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf-8"));
@@ -0,0 +1,15 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export const getPackageManagerFromRoot = (rootPath) => {
4
+ try {
5
+ const packageJsonPath = join(rootPath, "package.json");
6
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
7
+ if (packageJson.packageManager) {
8
+ return packageJson.packageManager.split("@")[0];
9
+ }
10
+ return "npm";
11
+ }
12
+ catch {
13
+ return "npm";
14
+ }
15
+ };