create-vexcms 0.0.3 → 0.0.4
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/README.md +161 -0
- package/dist/index.js +39 -7
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# create-vexcms
|
|
2
|
+
|
|
3
|
+
Scaffolding CLI for [VEX CMS](https://github.com/ianyimi/vex) projects. Creates a complete Next.js application with Convex backend, Better Auth authentication, and an admin panel — ready to run.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm create vexcms@latest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with a project name:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm create vexcms@latest my-project
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Supports relative paths:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm create vexcms@latest apps/website
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Flags
|
|
24
|
+
|
|
25
|
+
| Flag | Description |
|
|
26
|
+
|------|-------------|
|
|
27
|
+
| `--bare` | Skip the marketing site template. Scaffolds an empty VEX CMS project with no collections. |
|
|
28
|
+
| `--orgs` | Enable multi-tenant organizations (adds Better Auth organizations plugin). |
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Empty project with no pre-built collections
|
|
32
|
+
pnpm create vexcms@latest my-app --bare
|
|
33
|
+
|
|
34
|
+
# Project with organizations support
|
|
35
|
+
pnpm create vexcms@latest my-app --orgs
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Interactive Prompts
|
|
39
|
+
|
|
40
|
+
The CLI walks you through:
|
|
41
|
+
|
|
42
|
+
1. **Project name** — validates npm package name rules, supports `.` for current directory
|
|
43
|
+
2. **Framework** — Next.js (recommended) or TanStack Start (coming soon)
|
|
44
|
+
3. **Email/password auth** — enable or disable (default: yes)
|
|
45
|
+
4. **OAuth providers** — multi-select from 30+ Better Auth providers (Google, GitHub, Discord, etc.)
|
|
46
|
+
5. **Organizations** — enable multi-tenant support (default: no, skipped if `--orgs` passed)
|
|
47
|
+
6. **Git repository** — initialize git (default: yes)
|
|
48
|
+
7. **Install dependencies** — run package install (default: no)
|
|
49
|
+
|
|
50
|
+
## Templates
|
|
51
|
+
|
|
52
|
+
### Marketing Site (default)
|
|
53
|
+
|
|
54
|
+
Pre-built collections for a marketing website:
|
|
55
|
+
|
|
56
|
+
- **Pages** — with title, slug, richtext content, draft/publish versioning, and live preview
|
|
57
|
+
- **Headers** — name, logo upload, sticky option
|
|
58
|
+
- **Footers** — name, richtext content, copyright text
|
|
59
|
+
- **Themes** — name, colors, font family
|
|
60
|
+
- **Site Settings** — site name, description, favicon upload
|
|
61
|
+
|
|
62
|
+
Also includes:
|
|
63
|
+
|
|
64
|
+
- Landing page with sign-in/sign-up flow
|
|
65
|
+
- First-user auto-admin promotion
|
|
66
|
+
- Admin panel onboarding tour (driver.js)
|
|
67
|
+
- Preview routes at `/preview/[slug]` with live preview support
|
|
68
|
+
- Public page routes at `/[slug]` (published pages only)
|
|
69
|
+
- Draft-aware queries using `vexQuery`
|
|
70
|
+
|
|
71
|
+
### Bare (`--bare`)
|
|
72
|
+
|
|
73
|
+
Empty VEX CMS project with:
|
|
74
|
+
|
|
75
|
+
- Authentication setup (Better Auth + Convex adapter)
|
|
76
|
+
- Admin panel wired at `/admin`
|
|
77
|
+
- Empty `collections: []` in `vex.config.ts`
|
|
78
|
+
- No onboarding tour
|
|
79
|
+
|
|
80
|
+
## Getting Started
|
|
81
|
+
|
|
82
|
+
After scaffolding your project:
|
|
83
|
+
|
|
84
|
+
### 1. Install dependencies
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd my-project
|
|
88
|
+
pnpm install
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2. Generate a Better Auth secret
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pnpm secret:create
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This generates a random 32-character string and copies it to your clipboard.
|
|
98
|
+
|
|
99
|
+
### 3. Configure environment variables
|
|
100
|
+
|
|
101
|
+
The CLI creates a `.env.local` file with `NEXT_PUBLIC_SITE_URL` set to your chosen port. Add the auth secret:
|
|
102
|
+
|
|
103
|
+
```env
|
|
104
|
+
# .env.local (auto-generated)
|
|
105
|
+
NEXT_PUBLIC_SITE_URL=http://localhost:3010
|
|
106
|
+
|
|
107
|
+
# Add manually:
|
|
108
|
+
BETTER_AUTH_SECRET=your-generated-secret-here
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 4. Start VEX dev server
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pnpm vex:dev
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This starts the VEX CLI watcher (generates schema, types, and queries) and the Convex dev server. On first run, it will prompt you to create or select a Convex project.
|
|
118
|
+
|
|
119
|
+
### 5. Add environment variables to Convex
|
|
120
|
+
|
|
121
|
+
In the [Convex Dashboard](https://dashboard.convex.dev), navigate to your project's **Settings > Environment Variables** and add:
|
|
122
|
+
|
|
123
|
+
- `BETTER_AUTH_SECRET` — the same secret from step 2
|
|
124
|
+
- `SITE_URL` — `http://localhost:3010` (or your chosen port)
|
|
125
|
+
|
|
126
|
+
### 6. Start the Next.js dev server
|
|
127
|
+
|
|
128
|
+
In a separate terminal:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pnpm dev
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 7. Create your admin account
|
|
135
|
+
|
|
136
|
+
Open `http://localhost:3010` in your browser. Click **"Create Admin Account"** to sign up. The first user is automatically promoted to admin and redirected to the admin panel.
|
|
137
|
+
|
|
138
|
+
### Available Scripts
|
|
139
|
+
|
|
140
|
+
| Script | Description |
|
|
141
|
+
|--------|-------------|
|
|
142
|
+
| `pnpm dev` | Start Next.js development server |
|
|
143
|
+
| `pnpm vex:dev` | Start VEX CLI watcher + Convex dev server |
|
|
144
|
+
| `pnpm vex:generate` | One-shot schema/type/query generation |
|
|
145
|
+
| `pnpm vex:update` | Update all `@vexcms/*` packages to latest |
|
|
146
|
+
| `pnpm secret:create` | Generate a random 32-character secret and copy to clipboard |
|
|
147
|
+
| `pnpm build` | Production build |
|
|
148
|
+
|
|
149
|
+
## Versioning
|
|
150
|
+
|
|
151
|
+
`create-vexcms` is versioned alongside all `@vexcms/*` packages. Running `pnpm create vexcms@latest` always scaffolds with the latest package versions. You can also pin a specific version:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pnpm create vexcms@0.0.3
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The scaffolded project's `@vexcms/*` dependencies match the version of `create-vexcms` used.
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -157,6 +157,26 @@ var VexFrameworkInstaller = class {
|
|
|
157
157
|
}
|
|
158
158
|
await fs3.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Configure the dev server port.
|
|
162
|
+
* Updates package.json dev script and creates .env.local with NEXT_PUBLIC_SITE_URL.
|
|
163
|
+
*/
|
|
164
|
+
async configurePort(port) {
|
|
165
|
+
const pkgPath = path.join(this.targetPath, "package.json");
|
|
166
|
+
const pkg = await fs3.readJson(pkgPath);
|
|
167
|
+
if (pkg.scripts?.dev) {
|
|
168
|
+
if (pkg.scripts.dev.includes("--port")) {
|
|
169
|
+
pkg.scripts.dev = pkg.scripts.dev.replace(/--port[= ]\d+/, `--port=${port}`);
|
|
170
|
+
} else {
|
|
171
|
+
pkg.scripts.dev = `${pkg.scripts.dev} --port=${port}`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
await fs3.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
175
|
+
const envLocalPath = path.join(this.targetPath, ".env.local");
|
|
176
|
+
const envContent = `NEXT_PUBLIC_SITE_URL=http://localhost:${port}
|
|
177
|
+
`;
|
|
178
|
+
await fs3.writeFile(envLocalPath, envContent);
|
|
179
|
+
}
|
|
160
180
|
/**
|
|
161
181
|
* Detect the package manager used to invoke the CLI
|
|
162
182
|
*/
|
|
@@ -330,6 +350,7 @@ var VexFrameworkInstaller = class {
|
|
|
330
350
|
const nameSpinner = ora("Configuring project...").start();
|
|
331
351
|
try {
|
|
332
352
|
await this.updatePackageName(options.projectName);
|
|
353
|
+
await this.configurePort(options.port);
|
|
333
354
|
nameSpinner.succeed("Project configured");
|
|
334
355
|
} catch (error) {
|
|
335
356
|
nameSpinner.fail("Failed to configure project");
|
|
@@ -2308,7 +2329,7 @@ async function main() {
|
|
|
2308
2329
|
inputArg = args[0];
|
|
2309
2330
|
} else {
|
|
2310
2331
|
inputArg = await input({
|
|
2311
|
-
message: "What is your project named?",
|
|
2332
|
+
message: "(1/8) What is your project named?",
|
|
2312
2333
|
default: "my-vexcms-app",
|
|
2313
2334
|
validate: (value) => {
|
|
2314
2335
|
const name = value.includes("/") ? path2.basename(value) : value;
|
|
@@ -2336,7 +2357,7 @@ async function main() {
|
|
|
2336
2357
|
let framework;
|
|
2337
2358
|
while (true) {
|
|
2338
2359
|
framework = await select({
|
|
2339
|
-
message: "Select a framework:",
|
|
2360
|
+
message: "(2/8) Select a framework:",
|
|
2340
2361
|
choices: [
|
|
2341
2362
|
{ name: "Next.js (Recommended)", value: "nextjs" },
|
|
2342
2363
|
{ name: "TanStack Start (Coming Soon)", value: "tanstack" }
|
|
@@ -2348,8 +2369,18 @@ async function main() {
|
|
|
2348
2369
|
}
|
|
2349
2370
|
break;
|
|
2350
2371
|
}
|
|
2372
|
+
const portInput = await input({
|
|
2373
|
+
message: "(3/8) Dev server port:",
|
|
2374
|
+
default: "3010",
|
|
2375
|
+
validate: (value) => {
|
|
2376
|
+
const num = parseInt(value, 10);
|
|
2377
|
+
if (isNaN(num) || num < 1 || num > 65535) return "Must be a valid port number (1-65535)";
|
|
2378
|
+
return true;
|
|
2379
|
+
}
|
|
2380
|
+
});
|
|
2381
|
+
const port = parseInt(portInput, 10);
|
|
2351
2382
|
const emailPasswordAuth = await confirm({
|
|
2352
|
-
message: "Enable email/password authentication?",
|
|
2383
|
+
message: "(4/8) Enable email/password authentication?",
|
|
2353
2384
|
default: true
|
|
2354
2385
|
});
|
|
2355
2386
|
const popularProviders = getPopularProviders();
|
|
@@ -2366,25 +2397,26 @@ async function main() {
|
|
|
2366
2397
|
}))
|
|
2367
2398
|
];
|
|
2368
2399
|
const oauthProviders = await checkbox({
|
|
2369
|
-
message: "Select OAuth providers (space to toggle, enter to confirm):",
|
|
2400
|
+
message: "(5/8) Select OAuth providers (space to toggle, enter to confirm):",
|
|
2370
2401
|
choices: allProviderChoices
|
|
2371
2402
|
});
|
|
2372
2403
|
const orgs = opts.orgs ?? await confirm({
|
|
2373
|
-
message: "Enable multi-tenant (organizations)?",
|
|
2404
|
+
message: "(6/8) Enable multi-tenant (organizations)?",
|
|
2374
2405
|
default: false
|
|
2375
2406
|
});
|
|
2376
2407
|
const initGit = await confirm({
|
|
2377
|
-
message: "Initialize a Git repository?",
|
|
2408
|
+
message: "(7/8) Initialize a Git repository?",
|
|
2378
2409
|
default: true
|
|
2379
2410
|
});
|
|
2380
2411
|
const installDependencies = await confirm({
|
|
2381
|
-
message: "Install dependencies?",
|
|
2412
|
+
message: "(8/8) Install dependencies?",
|
|
2382
2413
|
default: false
|
|
2383
2414
|
});
|
|
2384
2415
|
const options = {
|
|
2385
2416
|
projectName,
|
|
2386
2417
|
projectDir: targetDir,
|
|
2387
2418
|
framework,
|
|
2419
|
+
port,
|
|
2388
2420
|
bare,
|
|
2389
2421
|
orgs,
|
|
2390
2422
|
emailPasswordAuth,
|