create-vitnode-app 0.0.1-canary.0
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 +21 -0
- package/README.md +78 -0
- package/copy-of-vitnode-app/.vscode/settings.json +10 -0
- package/copy-of-vitnode-app/README.md +40 -0
- package/copy-of-vitnode-app/api/.gitignore_template +32 -0
- package/copy-of-vitnode-app/api/drizzle.config.ts +12 -0
- package/copy-of-vitnode-app/api/src/i18n.ts +38 -0
- package/copy-of-vitnode-app/api/src/index.ts +27 -0
- package/copy-of-vitnode-app/api/src/vitnode.api.config.ts +40 -0
- package/copy-of-vitnode-app/api/tsconfig.json +17 -0
- package/copy-of-vitnode-app/api-bun/src/index.ts +16 -0
- package/copy-of-vitnode-app/api-single-app/drizzle.config.ts +12 -0
- package/copy-of-vitnode-app/api-single-app/src/routes/api/$.ts +32 -0
- package/copy-of-vitnode-app/api-single-app/src/server/api-bridge.ts +22 -0
- package/copy-of-vitnode-app/api-single-app/src/server/vitnode-api.server.ts +29 -0
- package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +37 -0
- package/copy-of-vitnode-app/docker/docker-compose.yml +30 -0
- package/copy-of-vitnode-app/eslint/.prettierrc.mjs +11 -0
- package/copy-of-vitnode-app/eslint/eslint.config.mjs +19 -0
- package/copy-of-vitnode-app/eslint-react/.prettierrc.mjs +11 -0
- package/copy-of-vitnode-app/eslint-react/eslint.config.mjs +19 -0
- package/copy-of-vitnode-app/monorepo/.gitignore_template +41 -0
- package/copy-of-vitnode-app/monorepo/apps/api/.env.example +12 -0
- package/copy-of-vitnode-app/monorepo/apps/web/.env.example +12 -0
- package/copy-of-vitnode-app/monorepo/turbo.json +53 -0
- package/copy-of-vitnode-app/root/.env.example +22 -0
- package/copy-of-vitnode-app/root/.gitignore_template +41 -0
- package/copy-of-vitnode-app/root/global.d.ts +28 -0
- package/copy-of-vitnode-app/root/src/components/admin-shell.tsx +72 -0
- package/copy-of-vitnode-app/root/src/components/main-header.tsx +33 -0
- package/copy-of-vitnode-app/root/src/i18n.ts +36 -0
- package/copy-of-vitnode-app/root/src/lib/admin-auth.ts +11 -0
- package/copy-of-vitnode-app/root/src/lib/admin-nav.ts +43 -0
- package/copy-of-vitnode-app/root/src/lib/admin-search.ts +37 -0
- package/copy-of-vitnode-app/root/src/lib/auth.ts +66 -0
- package/copy-of-vitnode-app/root/src/lib/content-registry.ts +51 -0
- package/copy-of-vitnode-app/root/src/lib/document-headers.ts +128 -0
- package/copy-of-vitnode-app/root/src/lib/i18n/runtime.ts +75 -0
- package/copy-of-vitnode-app/root/src/lib/i18n/shared.ts +20 -0
- package/copy-of-vitnode-app/root/src/lib/navigation.ts +24 -0
- package/copy-of-vitnode-app/root/src/lib/page-head.ts +19 -0
- package/copy-of-vitnode-app/root/src/locales/app.ts +28 -0
- package/copy-of-vitnode-app/root/src/locales/packages.ts +43 -0
- package/copy-of-vitnode-app/root/src/router.tsx +227 -0
- package/copy-of-vitnode-app/root/src/routes/__root.tsx +212 -0
- package/copy-of-vitnode-app/root/src/routes/_admin/admin.core.index.tsx +70 -0
- package/copy-of-vitnode-app/root/src/routes/_admin.tsx +258 -0
- package/copy-of-vitnode-app/root/src/routes/_main/index.tsx +60 -0
- package/copy-of-vitnode-app/root/src/routes/_main.tsx +64 -0
- package/copy-of-vitnode-app/root/src/server/messages.server.ts +29 -0
- package/copy-of-vitnode-app/root/src/start.ts +88 -0
- package/copy-of-vitnode-app/root/src/styles.css +171 -0
- package/copy-of-vitnode-app/root/src/vitnode.config.ts +58 -0
- package/copy-of-vitnode-app/root/src/vitnode.shell.config.ts +35 -0
- package/copy-of-vitnode-app/root/tsconfig.json +29 -0
- package/copy-of-vitnode-app/root/tsr.config.json +3 -0
- package/copy-of-vitnode-app/root/vite.config.ts +119 -0
- package/copy-of-vitnode-plugin/root/.swcrc +26 -0
- package/copy-of-vitnode-plugin/root/global.d.ts +21 -0
- package/copy-of-vitnode-plugin/root/npmignore.template +17 -0
- package/copy-of-vitnode-plugin/root/tsconfig.build.json +5 -0
- package/copy-of-vitnode-plugin/root/tsconfig.json +25 -0
- package/dist/src/create/create-package-json.js +276 -0
- package/dist/src/create/create-vitnode.js +204 -0
- package/dist/src/create/package-versions.js +49 -0
- package/dist/src/helpers/get-available-package-managers.js +24 -0
- package/dist/src/helpers/get-package-json.js +2 -0
- package/dist/src/helpers/get-package-manager-from-root.js +15 -0
- package/dist/src/helpers/get-vitnode-package-version.js +9 -0
- package/dist/src/helpers/init-vitnode.js +34 -0
- package/dist/src/helpers/install-dependencies.js +71 -0
- package/dist/src/helpers/is-folder-empty.js +48 -0
- package/dist/src/helpers/is-online.js +38 -0
- package/dist/src/helpers/is-writeable.js +11 -0
- package/dist/src/helpers/packages-json.js +1 -0
- package/dist/src/helpers/validate-pkg.js +14 -0
- package/dist/src/helpers/with-If.js +1 -0
- package/dist/src/index.js +68 -0
- package/dist/src/plugin/create/add-plugin-to-workspace.js +74 -0
- package/dist/src/plugin/create/create-package-json.js +54 -0
- package/dist/src/plugin/create/create-plugin-vitnode.js +80 -0
- package/dist/src/plugin/create/route-templates.js +144 -0
- package/dist/src/plugin/index.js +29 -0
- package/dist/src/plugin/questions.js +14 -0
- package/dist/src/plugin/validation.js +76 -0
- package/dist/src/prepare/prepare.js +19 -0
- package/dist/src/questions.js +83 -0
- package/dist/src/validation.js +40 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,276 @@
|
|
|
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",
|
|
216
|
+
private: true,
|
|
217
|
+
type: "module",
|
|
218
|
+
scripts: apiScripts(packageManager, eslint, !!docker, mode === "onlyApi", appName),
|
|
219
|
+
dependencies: {
|
|
220
|
+
...apiDeps,
|
|
221
|
+
"@vitnode/core": vitnodeVersionRange,
|
|
222
|
+
},
|
|
223
|
+
devDependencies: {
|
|
224
|
+
...apiDevDeps(packageManager, eslint),
|
|
225
|
+
"@vitnode/config": vitnodeVersionRange,
|
|
226
|
+
...(eslint && mode === "onlyApi"
|
|
227
|
+
? {
|
|
228
|
+
prettier: versionsPackageJson.prettier,
|
|
229
|
+
"prettier-plugin-tailwindcss": versionsPackageJson.prettierTailwind,
|
|
230
|
+
}
|
|
231
|
+
: {}),
|
|
232
|
+
...(packageManager === "bun" ? {} : {}),
|
|
233
|
+
},
|
|
234
|
+
};
|
|
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
|
+
}
|
|
276
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
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
|
+
const spinner = ora(`Creating a new VitNode app in ${color.green(root)}. Using ${color.green(packageManager)}...`).start();
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
const templatePath = join(__dirname, "..", "..", "..", "copy-of-vitnode-app");
|
|
16
|
+
if (!existsSync(templatePath)) {
|
|
17
|
+
spinner.fail(`\n${color.red("Error!")} Template path ${color.cyan(templatePath)} does not exist.`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
await mkdir(root, { recursive: true });
|
|
21
|
+
if (!isFolderEmpty(root, appName)) {
|
|
22
|
+
process.exit(1);
|
|
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
|
+
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"), {
|
|
42
|
+
recursive: true,
|
|
43
|
+
});
|
|
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, {
|
|
78
|
+
recursive: true,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
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...";
|
|
123
|
+
await createPackageJSON({
|
|
124
|
+
root,
|
|
125
|
+
appName,
|
|
126
|
+
packageManager,
|
|
127
|
+
eslint,
|
|
128
|
+
docker,
|
|
129
|
+
mode,
|
|
130
|
+
monorepo,
|
|
131
|
+
});
|
|
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
|
+
}
|
|
160
|
+
if (install) {
|
|
161
|
+
spinner.text = "Installing dependencies...";
|
|
162
|
+
await installDependencies({
|
|
163
|
+
packageManager,
|
|
164
|
+
cwd: root,
|
|
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
|
+
}
|
|
202
|
+
}
|
|
203
|
+
spinner.succeed(`${color.green("Success!")} Created ${color.cyan(appName)} at ${color.cyan(root)}`);
|
|
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
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
export const execShellCommand = async (cmd) => {
|
|
3
|
+
return await new Promise(resolve => {
|
|
4
|
+
exec(cmd, (error, stdout, stderr) => {
|
|
5
|
+
if (error) {
|
|
6
|
+
resolve(undefined);
|
|
7
|
+
}
|
|
8
|
+
const result = stdout ? stdout : stderr;
|
|
9
|
+
resolve(result.replace(/\s+/g, ""));
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export const getAvailablePackageManagers = async () => {
|
|
14
|
+
const [npm, pnpm, bun] = await Promise.all([
|
|
15
|
+
execShellCommand("npm --version"),
|
|
16
|
+
execShellCommand("pnpm --version"),
|
|
17
|
+
execShellCommand("bun --version"),
|
|
18
|
+
]);
|
|
19
|
+
return {
|
|
20
|
+
pnpm,
|
|
21
|
+
npm,
|
|
22
|
+
bun,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
@@ -0,0 +1,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
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { readFile } 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 getVitnodePackageVersion = async () => {
|
|
7
|
+
const cliPkg = JSON.parse(await readFile(join(__dirname, "..", "..", "..", "package.json"), "utf-8"));
|
|
8
|
+
return `^${cliPkg.version}`;
|
|
9
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import color from "picocolors";
|
|
3
|
+
export const generateMigrationsVitnode = async ({ packageManager: pm, cwd, }) => {
|
|
4
|
+
const packageManager = pm.split("@")[0];
|
|
5
|
+
const args = ["vitnode", "migrate", "--generate"];
|
|
6
|
+
await new Promise((resolve, reject) => {
|
|
7
|
+
let output = "";
|
|
8
|
+
const child = spawn(packageManager, args, {
|
|
9
|
+
cwd,
|
|
10
|
+
env: process.env,
|
|
11
|
+
shell: true,
|
|
12
|
+
stdio: "pipe",
|
|
13
|
+
});
|
|
14
|
+
child.stdout?.on("data", (data) => {
|
|
15
|
+
output += data.toString();
|
|
16
|
+
});
|
|
17
|
+
child.stderr?.on("data", (data) => {
|
|
18
|
+
output += data.toString();
|
|
19
|
+
});
|
|
20
|
+
child.on("error", error => {
|
|
21
|
+
reject(new Error(`Failed to start ${packageManager}: ${error.message}\nRun "${packageManager} vitnode db:prepare" in the new project once your database is running.`));
|
|
22
|
+
});
|
|
23
|
+
child.on("close", code => {
|
|
24
|
+
if (code === 0) {
|
|
25
|
+
resolve();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (output.trim().length > 0) {
|
|
29
|
+
console.error(color.red(output.trim()));
|
|
30
|
+
}
|
|
31
|
+
reject(new Error(`"${packageManager} vitnode migrate --generate" exited with code ${String(code)}. Your project was created - run "${packageManager} dev" once your database is running and it will migrate itself.`));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|