create-vitnode-app 1.2.0-canary.8 → 2.0.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 (110) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +52 -18
  3. package/copy-of-vitnode-app/.vscode/settings.json +10 -0
  4. package/copy-of-vitnode-app/README.md +40 -0
  5. package/copy-of-vitnode-app/api/.gitignore_template +32 -0
  6. package/copy-of-vitnode-app/api/drizzle.config.ts +12 -0
  7. package/copy-of-vitnode-app/api/src/i18n.ts +38 -0
  8. package/copy-of-vitnode-app/api/src/index.ts +27 -0
  9. package/copy-of-vitnode-app/api/src/vitnode.api.config.ts +40 -0
  10. package/copy-of-vitnode-app/api/tsconfig.json +17 -0
  11. package/copy-of-vitnode-app/api-bun/src/index.ts +16 -0
  12. package/copy-of-vitnode-app/api-single-app/drizzle.config.ts +12 -0
  13. package/copy-of-vitnode-app/api-single-app/src/routes/api/$.ts +32 -0
  14. package/copy-of-vitnode-app/api-single-app/src/server/api-bridge.ts +22 -0
  15. package/copy-of-vitnode-app/api-single-app/src/server/vitnode-api.server.ts +29 -0
  16. package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +37 -0
  17. package/copy-of-vitnode-app/docker/docker-compose.yml +30 -0
  18. package/copy-of-vitnode-app/eslint/.prettierrc.mjs +1 -1
  19. package/copy-of-vitnode-app/eslint/eslint.config.mjs +18 -2
  20. package/copy-of-vitnode-app/{root → eslint-react}/.prettierrc.mjs +1 -1
  21. package/copy-of-vitnode-app/eslint-react/eslint.config.mjs +19 -0
  22. package/copy-of-vitnode-app/monorepo/.gitignore_template +41 -0
  23. package/copy-of-vitnode-app/monorepo/apps/api/.env.example +12 -0
  24. package/copy-of-vitnode-app/monorepo/apps/web/.env.example +12 -0
  25. package/copy-of-vitnode-app/monorepo/turbo.json +53 -0
  26. package/copy-of-vitnode-app/root/.env.example +16 -5
  27. package/copy-of-vitnode-app/root/.gitignore_template +41 -0
  28. package/copy-of-vitnode-app/root/global.d.ts +28 -0
  29. package/copy-of-vitnode-app/root/src/components/admin-shell.tsx +72 -0
  30. package/copy-of-vitnode-app/root/src/components/main-header.tsx +33 -0
  31. package/copy-of-vitnode-app/root/src/i18n.ts +36 -0
  32. package/copy-of-vitnode-app/root/src/lib/admin-auth.ts +11 -0
  33. package/copy-of-vitnode-app/root/src/lib/admin-nav.ts +43 -0
  34. package/copy-of-vitnode-app/root/src/lib/admin-search.ts +37 -0
  35. package/copy-of-vitnode-app/root/src/lib/auth.ts +66 -0
  36. package/copy-of-vitnode-app/root/src/lib/content-registry.ts +51 -0
  37. package/copy-of-vitnode-app/root/src/lib/document-headers.ts +128 -0
  38. package/copy-of-vitnode-app/root/src/lib/i18n/runtime.ts +75 -0
  39. package/copy-of-vitnode-app/root/src/lib/i18n/shared.ts +20 -0
  40. package/copy-of-vitnode-app/root/src/lib/navigation.ts +24 -0
  41. package/copy-of-vitnode-app/root/src/lib/page-head.ts +19 -0
  42. package/copy-of-vitnode-app/root/src/locales/app.ts +28 -0
  43. package/copy-of-vitnode-app/root/src/locales/packages.ts +43 -0
  44. package/copy-of-vitnode-app/root/src/router.tsx +227 -0
  45. package/copy-of-vitnode-app/root/src/routes/__root.tsx +212 -0
  46. package/copy-of-vitnode-app/root/src/routes/_admin/admin.core.index.tsx +70 -0
  47. package/copy-of-vitnode-app/root/src/routes/_admin.tsx +258 -0
  48. package/copy-of-vitnode-app/root/src/routes/_main/index.tsx +60 -0
  49. package/copy-of-vitnode-app/root/src/routes/_main.tsx +64 -0
  50. package/copy-of-vitnode-app/root/src/server/messages.server.ts +29 -0
  51. package/copy-of-vitnode-app/root/src/start.ts +88 -0
  52. package/copy-of-vitnode-app/root/src/styles.css +171 -0
  53. package/copy-of-vitnode-app/root/src/vitnode.config.ts +58 -0
  54. package/copy-of-vitnode-app/root/src/vitnode.shell.config.ts +35 -0
  55. package/copy-of-vitnode-app/root/tsconfig.json +24 -16
  56. package/copy-of-vitnode-app/root/tsr.config.json +3 -0
  57. package/copy-of-vitnode-app/root/vite.config.ts +119 -0
  58. package/copy-of-vitnode-plugin/root/.swcrc +26 -0
  59. package/copy-of-vitnode-plugin/root/global.d.ts +21 -0
  60. package/copy-of-vitnode-plugin/root/npmignore.template +17 -0
  61. package/copy-of-vitnode-plugin/root/tsconfig.build.json +5 -0
  62. package/copy-of-vitnode-plugin/root/tsconfig.json +25 -0
  63. package/dist/src/create/create-package-json.js +266 -56
  64. package/dist/src/create/create-vitnode.js +177 -28
  65. package/dist/src/create/package-versions.js +49 -0
  66. package/dist/src/helpers/get-available-package-managers.js +6 -6
  67. package/dist/src/helpers/get-package-json.js +2 -2
  68. package/dist/src/helpers/get-package-manager-from-root.js +15 -0
  69. package/dist/src/helpers/get-vitnode-package-version.js +9 -0
  70. package/dist/src/helpers/init-vitnode.js +34 -0
  71. package/dist/src/helpers/install-dependencies.js +55 -14
  72. package/dist/src/helpers/is-folder-empty.js +25 -26
  73. package/dist/src/helpers/is-online.js +16 -16
  74. package/dist/src/helpers/is-writeable.js +3 -3
  75. package/dist/src/helpers/validate-pkg.js +1 -1
  76. package/dist/src/helpers/with-If.js +1 -0
  77. package/dist/src/index.js +29 -26
  78. package/dist/src/plugin/create/add-plugin-to-workspace.js +74 -0
  79. package/dist/src/plugin/create/create-package-json.js +54 -0
  80. package/dist/src/plugin/create/create-plugin-vitnode.js +80 -0
  81. package/dist/src/plugin/create/route-templates.js +144 -0
  82. package/dist/src/plugin/index.js +17 -6
  83. package/dist/src/plugin/questions.js +14 -0
  84. package/dist/src/plugin/validation.js +76 -0
  85. package/dist/src/prepare/prepare.js +11 -8
  86. package/dist/src/questions.js +52 -15
  87. package/dist/src/validation.js +15 -15
  88. package/dist/tsconfig.build.tsbuildinfo +1 -0
  89. package/package.json +22 -12
  90. package/copy-of-vitnode-app/root/README.md +0 -1
  91. package/copy-of-vitnode-app/root/drizzle.config.ts +0 -12
  92. package/copy-of-vitnode-app/root/gitignore_template +0 -43
  93. package/copy-of-vitnode-app/root/next.config.ts +0 -11
  94. package/copy-of-vitnode-app/root/postcss.config.mjs +0 -8
  95. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/[...rest]/page.tsx +0 -5
  96. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx +0 -10
  97. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/not-found.tsx +0 -5
  98. package/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx +0 -113
  99. package/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx +0 -10
  100. package/copy-of-vitnode-app/root/src/app/[locale]/admin/not-found.tsx +0 -5
  101. package/copy-of-vitnode-app/root/src/app/[locale]/admin/page.tsx +0 -5
  102. package/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx +0 -36
  103. package/copy-of-vitnode-app/root/src/app/api/[...route]/route.ts +0 -18
  104. package/copy-of-vitnode-app/root/src/app/favicon.ico +0 -0
  105. package/copy-of-vitnode-app/root/src/app/global-error.tsx +0 -27
  106. package/copy-of-vitnode-app/root/src/app/global.css +0 -128
  107. package/copy-of-vitnode-app/root/src/app/layout.tsx +0 -9
  108. package/copy-of-vitnode-app/root/src/app/not-found.tsx +0 -13
  109. package/copy-of-vitnode-app/root/src/vitnode.api.config.ts +0 -5
  110. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,66 +1,276 @@
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 i18nScripts = {
18
+ "i18n:create": "vitnode i18n:create",
19
+ "i18n:check": "vitnode i18n:check",
20
+ "i18n:delete": "vitnode i18n:delete",
21
+ "i18n:update": "vitnode i18n:update",
22
+ "i18n:update:ai": "vitnode i18n:update:ai",
23
+ };
24
+ const dockerDevScript = (appName) => `docker compose -f ./docker-compose.yml -p ${appName}-vitnode-dev up -d`;
25
+ export const rootScripts = (enableEslint, enableDocker, appName) => ({
26
+ "db:migrate": "turbo db:migrate",
27
+ "db:prepare": "turbo db:prepare",
28
+ dev: "turbo db:prepare && turbo dev",
29
+ build: "turbo build",
30
+ start: "turbo start",
31
+ "i18n:create": "turbo i18n:create",
32
+ "i18n:check": "turbo i18n:check",
33
+ "i18n:delete": "turbo i18n:delete",
34
+ "i18n:update": "turbo i18n:update",
35
+ "i18n:update:ai": "turbo i18n:update:ai",
36
+ ...withIf(enableEslint, {
37
+ lint: "turbo lint",
38
+ "lint:fix": "turbo lint:fix",
39
+ }),
40
+ ...withIf(enableDocker, { "docker:dev": dockerDevScript(appName) }),
41
+ });
42
+ export const apiScripts = (pm, eslint, docker, onlyApi, appName) => {
43
+ return {
44
+ "db:migrate": "vitnode migrate",
45
+ "db:prepare": "vitnode db:prepare",
46
+ ...(pm === "bun"
47
+ ? {
48
+ dev: "vitnode db:prepare && bun run --hot src/index.ts",
49
+ start: "NODE_ENV=production bun run src/index.ts",
50
+ }
51
+ : {
52
+ dev: "vitnode db:prepare && tsx watch src/index.ts",
53
+ build: "tsc && tsc-alias -p tsconfig.json",
54
+ start: "node dist/index.js",
55
+ }),
56
+ "dev:email": "email dev --dir src/emails",
57
+ ...i18nScripts,
58
+ ...withIf(eslint, eslintScripts),
59
+ ...withIf(docker && onlyApi, { "docker:dev": dockerDevScript(appName) }),
60
+ "drizzle-kit": "drizzle-kit",
61
+ };
62
+ };
63
+ export const singleAppScripts = (eslint, docker, appName) => ({
64
+ "db:migrate": "vitnode migrate",
65
+ "db:prepare": "vitnode db:prepare",
66
+ dev: "vitnode db:prepare && vite dev --port 3000",
67
+ "dev:email": "email dev --dir src/emails",
68
+ build: "vite build",
69
+ start: "node .output/server/index.mjs",
70
+ ...i18nScripts,
71
+ ...withIf(eslint, eslintScripts),
72
+ ...withIf(docker, { "docker:dev": dockerDevScript(appName) }),
73
+ "drizzle-kit": "drizzle-kit",
74
+ });
75
+ export const webScripts = (eslint) => ({
76
+ dev: "vite dev --port 3000",
77
+ build: "vite build",
78
+ start: "node .output/server/index.mjs",
79
+ ...i18nScripts,
80
+ ...withIf(eslint, eslintScripts),
81
+ });
82
+ const baseDevDeps = (eslint, includePrettier) => ({
83
+ "@types/node": versionsPackageJson.typesNode,
84
+ "@vitnode/config": "",
85
+ ...withIf(eslint, {
86
+ eslint: versionsPackageJson.eslint,
87
+ ...withIf(includePrettier, {
88
+ prettier: versionsPackageJson.prettier,
89
+ "prettier-plugin-tailwindcss": versionsPackageJson.prettierTailwind,
90
+ }),
91
+ }),
92
+ });
93
+ const rootDevDeps = (eslint) => ({
94
+ ...baseDevDeps(eslint, true),
95
+ turbo: versionsPackageJson.turbo,
96
+ typescript: versionsPackageJson.typescript,
97
+ zod: versionsPackageJson.zod,
98
+ });
99
+ const apiDeps = {
100
+ "@hono/zod-openapi": versionsPackageJson.honoZodOpenapi,
101
+ "@hono/zod-validator": versionsPackageJson.honoZodValidator,
102
+ "@vitnode/core": "",
103
+ "drizzle-kit": versionsPackageJson.drizzleKit,
104
+ "drizzle-orm": versionsPackageJson.drizzleOrm,
105
+ hono: versionsPackageJson.hono,
106
+ react: versionsPackageJson.react,
107
+ "react-dom": versionsPackageJson.reactDom,
108
+ "react-email": versionsPackageJson.reactEmail,
109
+ ws: versionsPackageJson.ws,
110
+ zod: versionsPackageJson.zod,
111
+ };
112
+ const apiDevDeps = (pm, eslint) => ({
113
+ "@hono/node-server": "^2.0",
114
+ "@react-email/ui": versionsPackageJson.reactEmailUi,
115
+ ...(pm === "bun" ? { "@types/bun": versionsPackageJson.typesBun } : {}),
116
+ "@types/node": versionsPackageJson.typesNode,
117
+ "@types/react": versionsPackageJson.typesReact,
118
+ "@types/react-dom": versionsPackageJson.typesReactDom,
119
+ "@vitnode/config": "",
120
+ dotenv: versionsPackageJson.dotenv,
121
+ ...withIf(eslint, {
122
+ eslint: versionsPackageJson.eslint,
123
+ }),
124
+ "tsc-alias": versionsPackageJson.tscAlias,
125
+ tsx: versionsPackageJson.tsx,
126
+ typescript: versionsPackageJson.typescript,
127
+ });
128
+ const tanstackWebDeps = {
129
+ "@tailwindcss/vite": versionsPackageJson.tailwindVite,
130
+ "@tanstack/react-query": versionsPackageJson.tanstackReactQuery,
131
+ "@tanstack/react-router": versionsPackageJson.tanstackReactRouter,
132
+ "@tanstack/react-router-ssr-query": versionsPackageJson.tanstackRouterSsrQuery,
133
+ "@tanstack/react-start": versionsPackageJson.tanstackReactStart,
134
+ "@vitnode/core": "",
135
+ "lucide-react": versionsPackageJson.lucide,
136
+ nitro: versionsPackageJson.nitro,
137
+ react: versionsPackageJson.react,
138
+ "react-dom": versionsPackageJson.reactDom,
139
+ "react-hook-form": versionsPackageJson.rhf,
140
+ sonner: versionsPackageJson.sonner,
141
+ tailwindcss: versionsPackageJson.tailwind,
142
+ tslib: versionsPackageJson.tslib,
143
+ "use-intl": versionsPackageJson.useIntl,
144
+ zod: versionsPackageJson.zod,
145
+ };
146
+ const singleAppDeps = {
147
+ ...tanstackWebDeps,
148
+ "@hono/zod-openapi": versionsPackageJson.honoZodOpenapi,
149
+ "@hono/zod-validator": versionsPackageJson.honoZodValidator,
150
+ "@hookform/resolvers": versionsPackageJson.rhfResolvers,
151
+ "drizzle-kit": versionsPackageJson.drizzleKit,
152
+ "drizzle-orm": versionsPackageJson.drizzleOrm,
153
+ hono: versionsPackageJson.hono,
154
+ "react-email": versionsPackageJson.reactEmail,
155
+ shadcn: versionsPackageJson.shadcn,
156
+ };
157
+ const tanstackWebDevDeps = {
158
+ "@tanstack/devtools-vite": versionsPackageJson.tanstackDevtoolsVite,
159
+ "@tanstack/react-devtools": versionsPackageJson.tanstackReactDevtools,
160
+ "@tanstack/react-query-devtools": versionsPackageJson.tanstackQueryDevtools,
161
+ "@tanstack/react-router-devtools": versionsPackageJson.tanstackRouterDevtools,
162
+ "@types/node": versionsPackageJson.typesNode,
163
+ "@types/react": versionsPackageJson.typesReact,
164
+ "@types/react-dom": versionsPackageJson.typesReactDom,
165
+ "@vitejs/plugin-react": versionsPackageJson.viteReact,
166
+ "@vitnode/config": "",
167
+ "tw-animate-css": versionsPackageJson.twAnimateCss,
168
+ typescript: versionsPackageJson.typescript,
169
+ vite: versionsPackageJson.vite,
170
+ };
171
+ const singleAppDevDeps = (eslint) => ({
172
+ ...tanstackWebDevDeps,
173
+ "@react-email/ui": versionsPackageJson.reactEmailUi,
174
+ ...withIf(eslint, {
175
+ eslint: versionsPackageJson.eslint,
176
+ prettier: versionsPackageJson.prettier,
177
+ "prettier-plugin-tailwindcss": versionsPackageJson.prettierTailwind,
178
+ }),
179
+ turbo: versionsPackageJson.turbo,
180
+ });
181
+ const webDeps = {
182
+ ...tanstackWebDeps,
183
+ shadcn: versionsPackageJson.shadcn,
184
+ };
185
+ const webDevDeps = (eslint) => ({
186
+ ...tanstackWebDevDeps,
187
+ "@hookform/resolvers": versionsPackageJson.rhfResolvers,
188
+ "class-variance-authority": versionsPackageJson.cva,
189
+ ...withIf(eslint, { eslint: versionsPackageJson.eslint }),
190
+ });
191
+ export const createPackageJSON = async ({ appName, packageManager, root, eslint, docker, mode, monorepo, }) => {
192
+ const vitnodeVersionRange = await getVitnodePackageVersion();
193
+ const pmVersions = await getAvailablePackageManagers();
194
+ const pmSpec = `${packageManager}@${pmVersions[packageManager]}`;
195
+ const p = paths(root);
196
+ const isApiMonorepo = mode === "apiMonorepo" || !!monorepo;
197
+ const isOnlyApi = mode === "onlyApi";
198
+ const isSingleApp = mode === "singleApp";
199
+ if (isApiMonorepo) {
200
+ const rootPkg = {
201
+ name: appName,
202
+ private: true,
203
+ scripts: rootScripts(eslint, !!docker, appName),
204
+ devDependencies: {
205
+ ...rootDevDeps(eslint),
206
+ "@vitnode/config": vitnodeVersionRange,
207
+ },
208
+ packageManager: pmSpec,
209
+ workspaces: ["apps/*", "plugins/*"],
210
+ };
211
+ await writeJson(join(p.root, "package.json"), rootPkg);
212
+ }
213
+ const apiPkg = {
214
+ name: isApiMonorepo ? "api" : appName,
215
+ version: "0.1.0",
11
216
  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
- },
217
+ type: "module",
218
+ scripts: apiScripts(packageManager, eslint, !!docker, mode === "onlyApi", appName),
27
219
  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',
220
+ ...apiDeps,
221
+ "@vitnode/core": vitnodeVersionRange,
45
222
  },
46
223
  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
224
+ ...apiDevDeps(packageManager, eslint),
225
+ "@vitnode/config": vitnodeVersionRange,
226
+ ...(eslint && mode === "onlyApi"
52
227
  ? {
53
- eslint: '^9.27.0',
54
- 'eslint-config-typescript-vitnode': `^${pkg.version}`,
55
- prettier: '^3.5.3',
228
+ prettier: versionsPackageJson.prettier,
229
+ "prettier-plugin-tailwindcss": versionsPackageJson.prettierTailwind,
56
230
  }
57
231
  : {}),
58
- 'react-email': '^4.0.15',
59
- tailwindcss: '^4.1.8',
60
- 'tw-animate-css': '^1.3.2',
61
- typescript: '^5.8.3',
232
+ ...(packageManager === "bun" ? {} : {}),
62
233
  },
63
- packageManager,
64
234
  };
65
- await writeFile(join(root, 'package.json'), JSON.stringify(packageJson, null, 2));
235
+ if (isSingleApp) {
236
+ const singlePkg = {
237
+ name: monorepo ? "web" : appName,
238
+ version: "0.1.0",
239
+ private: true,
240
+ type: "module",
241
+ scripts: singleAppScripts(eslint, !!docker, appName),
242
+ dependencies: {
243
+ ...singleAppDeps,
244
+ "@vitnode/core": vitnodeVersionRange,
245
+ },
246
+ devDependencies: {
247
+ ...singleAppDevDeps(eslint),
248
+ "@vitnode/config": vitnodeVersionRange,
249
+ },
250
+ packageManager: pmSpec,
251
+ };
252
+ await writeJson(join(monorepo ? p.web : p.root, "package.json"), singlePkg);
253
+ }
254
+ if (mode === "apiMonorepo") {
255
+ await writeJson(join(p.api, "package.json"), apiPkg);
256
+ const webPkg = {
257
+ name: "web",
258
+ version: "0.1.0",
259
+ private: true,
260
+ type: "module",
261
+ scripts: webScripts(eslint),
262
+ dependencies: {
263
+ ...webDeps,
264
+ "@vitnode/core": vitnodeVersionRange,
265
+ },
266
+ devDependencies: {
267
+ ...webDevDeps(eslint),
268
+ "@vitnode/config": vitnodeVersionRange,
269
+ },
270
+ };
271
+ await writeJson(join(p.web, "package.json"), webPkg);
272
+ }
273
+ if (isOnlyApi) {
274
+ await writeJson(join(monorepo ? p.api : p.root, "package.json"), apiPkg);
275
+ }
66
276
  };
@@ -1,55 +1,204 @@
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 } 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
+ const destination = monorepo ? monorepoStructure.web : root;
46
+ await cp(join(templatePath, "root"), destination, { recursive: true });
47
+ await cp(join(templatePath, "api-single-app"), destination, {
48
+ recursive: true,
49
+ });
50
+ }
51
+ else if (mode === "apiMonorepo") {
52
+ await Promise.all([
53
+ cp(join(templatePath, "root"), monorepoStructure.web, {
54
+ recursive: true,
55
+ }),
56
+ cp(join(templatePath, "api"), monorepoStructure.api, {
57
+ recursive: true,
58
+ }),
59
+ ]);
60
+ if (packageManager === "bun") {
61
+ await cp(join(templatePath, "api-bun"), monorepoStructure.api, {
62
+ recursive: true,
63
+ });
64
+ }
65
+ }
66
+ else if (mode === "onlyApi") {
67
+ await cp(join(templatePath, "api"), monorepo ? monorepoStructure.api : root, {
68
+ recursive: true,
69
+ });
70
+ if (packageManager === "bun") {
71
+ await cp(join(templatePath, "api-bun"), monorepo ? monorepoStructure.api : root, {
72
+ recursive: true,
73
+ });
74
+ }
75
+ }
76
+ if (mode === "apiMonorepo" || (monorepo && mode !== "singleApp")) {
77
+ await cp(join(templatePath, "monorepo"), root, {
31
78
  recursive: true,
32
79
  });
33
80
  }
34
- spinner.text = 'Renaming special files...';
35
- await rename(join(root, 'gitignore_template'), join(root, '.gitignore'));
36
- spinner.text = 'Creating package.json...';
81
+ else if (monorepo && mode === "singleApp") {
82
+ await copyFile(join(templatePath, "monorepo", "turbo.json"), join(root, "turbo.json"));
83
+ await copyFile(join(templatePath, "monorepo", ".gitignore_template"), join(root, ".gitignore_template"));
84
+ }
85
+ if (eslint) {
86
+ spinner.text = "Copying ESLint & Prettier files...";
87
+ if (monorepo) {
88
+ if (existsSync(monorepoStructure.api)) {
89
+ await cp(join(templatePath, "eslint"), monorepoStructure.api, {
90
+ recursive: true,
91
+ });
92
+ }
93
+ if (existsSync(monorepoStructure.web)) {
94
+ await cp(join(templatePath, "eslint-react"), monorepoStructure.web, {
95
+ recursive: true,
96
+ });
97
+ }
98
+ }
99
+ else {
100
+ if (mode === "onlyApi") {
101
+ await cp(join(templatePath, "eslint"), root, {
102
+ recursive: true,
103
+ });
104
+ }
105
+ else if (mode === "singleApp") {
106
+ await cp(join(templatePath, "eslint-react"), root, {
107
+ recursive: true,
108
+ });
109
+ }
110
+ }
111
+ }
112
+ spinner.text = "Renaming special files...";
113
+ await rename(join(root, ".gitignore_template"), join(root, ".gitignore"));
114
+ if (mode === "apiMonorepo" || monorepo) {
115
+ if (existsSync(join(monorepoStructure.api, ".gitignore_template"))) {
116
+ await rename(join(monorepoStructure.api, ".gitignore_template"), join(monorepoStructure.api, ".gitignore"));
117
+ }
118
+ if (existsSync(join(monorepoStructure.web, ".gitignore_template"))) {
119
+ await rename(join(monorepoStructure.web, ".gitignore_template"), join(monorepoStructure.web, ".gitignore"));
120
+ }
121
+ }
122
+ spinner.text = "Creating package.json...";
37
123
  await createPackageJSON({
38
124
  root,
39
125
  appName,
40
126
  packageManager,
41
127
  eslint,
128
+ docker,
129
+ mode,
130
+ monorepo,
42
131
  });
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);
132
+ if ((mode === "apiMonorepo" || monorepo) && packageManager === "pnpm") {
133
+ spinner.text = "Creating pnpm-workspace.yaml...";
134
+ const pnpmWorkspaceContent = `packages:\n - 'apps/*'\n - 'plugins/*'\n`;
135
+ await writeFile(join(root, "pnpm-workspace.yaml"), pnpmWorkspaceContent);
136
+ }
137
+ if (docker) {
138
+ spinner.text = "Copying docker files...";
139
+ await copyFile(join(templatePath, "docker", "docker-compose.yml"), join(root, "docker-compose.yml"));
140
+ const dockerComposePath = join(root, "docker-compose.yml");
141
+ const dockerComposeContent = await readFile(dockerComposePath, "utf-8");
142
+ const updatedContent = dockerComposeContent.replace(/vitnode_postgres_dev/g, `${appName}_vitnode_postgres_dev`);
143
+ await writeFile(dockerComposePath, updatedContent);
144
+ }
145
+ spinner.text = "Updating VitNode paths...";
146
+ if ((mode === "apiMonorepo" || monorepo) &&
147
+ packageManager === "npm" &&
148
+ mode !== "onlyApi") {
149
+ const stylesPath = join(monorepoStructure.web, "src", "styles.css");
150
+ const stylesContent = await readFile(stylesPath, "utf-8");
151
+ await writeFile(stylesPath, stylesContent.replaceAll('@source "../node_modules/@vitnode/', '@source "../../../node_modules/@vitnode/'));
152
+ }
153
+ if (mode === "apiMonorepo") {
154
+ spinner.text = "Setting up environment variables...";
155
+ const envExamplePath = join(monorepoStructure.web, ".env.example");
156
+ if (existsSync(envExamplePath)) {
157
+ await rename(envExamplePath, join(monorepoStructure.web, ".env"));
158
+ }
159
+ }
48
160
  if (install) {
49
- spinner.text = 'Installing dependencies...';
161
+ spinner.text = "Installing dependencies...";
50
162
  await installDependencies({
51
163
  packageManager,
164
+ cwd: root,
52
165
  });
166
+ spinner.text = "Preparing README...";
167
+ await copyFile(join(templatePath, "README.md"), join(root, "README.md"));
168
+ let readmeContent = await readFile(join(root, "README.md"), "utf-8");
169
+ readmeContent = readmeContent.replaceAll("pnpm", packageManager);
170
+ let startUrlsText = "[http://localhost:3000](http://localhost:3000)";
171
+ if (mode === "onlyApi") {
172
+ startUrlsText = "[http://localhost:8000](http://localhost:8000)";
173
+ }
174
+ else if (mode === "apiMonorepo") {
175
+ startUrlsText =
176
+ "[http://localhost:3000](http://localhost:3000) for the Web app and [http://localhost:8000](http://localhost:8000) for the API";
177
+ }
178
+ readmeContent = readmeContent.replace("{{START_URLS}}", startUrlsText);
179
+ await writeFile(join(root, "README.md"), readmeContent);
180
+ spinner.text = "Generating migrations...";
181
+ let migrationsCwd;
182
+ if (mode === "apiMonorepo" || (monorepo && mode !== "singleApp")) {
183
+ migrationsCwd = monorepoStructure.api;
184
+ }
185
+ else if (mode === "singleApp" && monorepo) {
186
+ migrationsCwd = monorepoStructure.web;
187
+ }
188
+ else {
189
+ migrationsCwd = root;
190
+ }
191
+ try {
192
+ await generateMigrationsVitnode({
193
+ packageManager,
194
+ cwd: migrationsCwd,
195
+ });
196
+ }
197
+ catch (error) {
198
+ spinner.fail(`${color.red("Error!")} Created ${color.cyan(appName)}, but could not generate its migrations.`);
199
+ console.error(color.red(error instanceof Error ? error.message : String(error)));
200
+ process.exit(1);
201
+ }
53
202
  }
54
- spinner.succeed(`${color.green('Success!')} Created ${color.cyan(appName)} at ${color.cyan(root)}`);
203
+ spinner.succeed(`${color.green("Success!")} Created ${color.cyan(appName)} at ${color.cyan(root)}`);
55
204
  };
@@ -0,0 +1,49 @@
1
+ export const versionsPackageJson = {
2
+ typesNode: "^26",
3
+ typesReact: "^19.2",
4
+ typesReactDom: "^19.2",
5
+ typesBun: "latest",
6
+ turbo: "^2.10",
7
+ typescript: "^6.0",
8
+ tsx: "^4",
9
+ tscAlias: "^1.9",
10
+ eslint: "^10",
11
+ prettier: "^3.9",
12
+ prettierTailwind: "^0.8",
13
+ tailwind: "^4.2",
14
+ twAnimateCss: "^1.4.0",
15
+ react: "^19.2",
16
+ reactDom: "^19.2",
17
+ useIntl: "^4.13.7",
18
+ rhf: "^7",
19
+ rhfResolvers: "^5.1.1",
20
+ lucide: "^1.21",
21
+ sonner: "^2.0.7",
22
+ dotenv: "^17.4",
23
+ drizzleKit: "1.0.0-rc.4",
24
+ drizzleOrm: "1.0.0-rc.4",
25
+ hono: "^4.12",
26
+ honoZodOpenapi: "^1.5",
27
+ honoZodValidator: "^0.9",
28
+ reactEmail: "^6.9",
29
+ reactEmailUi: "^6.9",
30
+ zod: "^4.4",
31
+ ws: "^8",
32
+ cva: "^0.7.1",
33
+ tanstackReactRouter: "^1.170",
34
+ tanstackReactStart: "^1.168",
35
+ tanstackReactQuery: "^5.101",
36
+ tanstackRouterSsrQuery: "^1.167",
37
+ tanstackReactDevtools: "^0.10",
38
+ tanstackDevtoolsVite: "^0.8",
39
+ tanstackQueryDevtools: "^5.102",
40
+ tanstackRouterDevtools: "^1.167",
41
+ vite: "^8.0",
42
+ viteReact: "^6.0",
43
+ tailwindVite: "^4.2",
44
+ nitro: "3.0.260610-beta",
45
+ tslib: "^2.8.1",
46
+ swcCli: "^0.8.1",
47
+ swcCore: "^1.15",
48
+ shadcn: "^4",
49
+ };
@@ -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
+ };