webspresso 0.0.9 → 0.0.11
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 +88 -1
- package/bin/webspresso.js +524 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,9 +73,70 @@ webspresso new my-app --no-tailwind
|
|
|
73
73
|
- Asks if you want to install in the current directory
|
|
74
74
|
- If current directory is not empty, shows a warning
|
|
75
75
|
- Prompts for project name (defaults to current folder name)
|
|
76
|
+
- Asks if you will use a database (SQLite, PostgreSQL, or MySQL)
|
|
77
|
+
- If yes, adds the appropriate driver to `package.json` and creates `webspresso.db.js` config
|
|
78
|
+
- After project creation, asks if you want to install dependencies
|
|
79
|
+
- If yes, runs `npm install` and `npm run build:css`
|
|
80
|
+
- Then asks if you want to start the development server
|
|
81
|
+
- If yes, starts `npm run dev` automatically
|
|
82
|
+
|
|
83
|
+
**Auto Installation:**
|
|
84
|
+
```bash
|
|
85
|
+
# With --install flag (semi-interactive)
|
|
86
|
+
webspresso new my-app --install
|
|
87
|
+
# → Automatically runs: npm install && npm run build:css
|
|
88
|
+
# → Then prompts: "Start development server?" [Y/n]
|
|
89
|
+
# → If yes: starts npm run dev (with watch:css if Tailwind enabled)
|
|
90
|
+
|
|
91
|
+
# Without --install flag (fully interactive)
|
|
92
|
+
webspresso new my-app
|
|
93
|
+
# → Prompts: "Install dependencies and build CSS now?" [Y/n]
|
|
94
|
+
# → If yes: runs npm install && npm run build:css
|
|
95
|
+
# → Then: "Start development server?" [Y/n]
|
|
96
|
+
# → If yes: starts npm run dev (with watch:css if Tailwind enabled)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Note:** When dev server starts with Tailwind CSS, it automatically runs `watch:css` in the background to watch for CSS changes.
|
|
100
|
+
|
|
101
|
+
**Database Selection:**
|
|
102
|
+
During project creation, you'll be asked if you want to use a database:
|
|
103
|
+
- **SQLite** (better-sqlite3) - Recommended for development and small projects
|
|
104
|
+
- **PostgreSQL** (pg) - For production applications
|
|
105
|
+
- **MySQL** (mysql2) - Alternative SQL database
|
|
106
|
+
|
|
107
|
+
If you select a database:
|
|
108
|
+
- The appropriate driver is added to `package.json` dependencies
|
|
109
|
+
- `webspresso.db.js` config file is created with proper settings
|
|
110
|
+
- `migrations/` directory is created
|
|
111
|
+
- `models/` directory is created
|
|
112
|
+
- `DATABASE_URL` is added to `.env.example` with a template
|
|
113
|
+
|
|
114
|
+
**Seed Data Generation:**
|
|
115
|
+
After selecting a database, you'll be asked if you want to generate seed data:
|
|
116
|
+
- If yes, `@faker-js/faker` is added to dependencies
|
|
117
|
+
- `seeds/` directory is created with `seeds/index.js`
|
|
118
|
+
- `npm run seed` script is added to `package.json`
|
|
119
|
+
- The seed script automatically detects models in `models/` directory and generates fake data based on their schemas
|
|
120
|
+
|
|
121
|
+
To run seeds after creating models:
|
|
122
|
+
```bash
|
|
123
|
+
npm run seed
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The seed script will:
|
|
127
|
+
- Load all models from `models/` directory
|
|
128
|
+
- Generate 10 fake records per model (by default)
|
|
129
|
+
- Use smart field detection based on column names (email, name, title, etc.)
|
|
130
|
+
|
|
131
|
+
You can always add database support later by:
|
|
132
|
+
1. Installing the driver: `npm install better-sqlite3` (or `pg`, `mysql2`)
|
|
133
|
+
2. Creating `webspresso.db.js` config file
|
|
134
|
+
3. Adding `DATABASE_URL` to your `.env` file
|
|
135
|
+
4. Creating `models/` directory and defining your models
|
|
136
|
+
5. Optionally adding seed support: `npm install @faker-js/faker` and creating `seeds/index.js`
|
|
76
137
|
|
|
77
138
|
Options:
|
|
78
|
-
- `-i, --install` - Auto run `npm install` and `npm run build:css`
|
|
139
|
+
- `-i, --install` - Auto run `npm install` and `npm run build:css` (non-interactive)
|
|
79
140
|
- `--no-tailwind` - Skip Tailwind CSS setup
|
|
80
141
|
|
|
81
142
|
The project includes:
|
|
@@ -1018,6 +1079,32 @@ npm install better-sqlite3
|
|
|
1018
1079
|
|
|
1019
1080
|
### Database Seeding
|
|
1020
1081
|
|
|
1082
|
+
**CLI Command:**
|
|
1083
|
+
|
|
1084
|
+
The easiest way to seed your database is using the CLI command:
|
|
1085
|
+
|
|
1086
|
+
```bash
|
|
1087
|
+
# Run seeds (requires seeds/index.js)
|
|
1088
|
+
webspresso seed
|
|
1089
|
+
|
|
1090
|
+
# Setup seed files if they don't exist
|
|
1091
|
+
webspresso seed --setup
|
|
1092
|
+
|
|
1093
|
+
# Use custom database config
|
|
1094
|
+
webspresso seed --config ./custom-db-config.js
|
|
1095
|
+
|
|
1096
|
+
# Use different environment
|
|
1097
|
+
webspresso seed --env production
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
The `webspresso seed` command:
|
|
1101
|
+
- Automatically loads all models from `models/` directory
|
|
1102
|
+
- Generates fake data based on model schemas
|
|
1103
|
+
- Creates 10 records per model by default
|
|
1104
|
+
- Uses smart field detection for appropriate fake data
|
|
1105
|
+
|
|
1106
|
+
**Manual Setup:**
|
|
1107
|
+
|
|
1021
1108
|
Generate fake data for testing and development using `@faker-js/faker`:
|
|
1022
1109
|
|
|
1023
1110
|
```bash
|
package/bin/webspresso.js
CHANGED
|
@@ -121,6 +121,43 @@ program
|
|
|
121
121
|
|
|
122
122
|
console.log(`\n🚀 Creating new Webspresso project: ${projectName}\n`);
|
|
123
123
|
|
|
124
|
+
// Ask about database
|
|
125
|
+
const { useDatabase, databaseType } = await inquirer.prompt([
|
|
126
|
+
{
|
|
127
|
+
type: 'confirm',
|
|
128
|
+
name: 'useDatabase',
|
|
129
|
+
message: 'Will you use a database?',
|
|
130
|
+
default: false
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'list',
|
|
134
|
+
name: 'databaseType',
|
|
135
|
+
message: 'Which database?',
|
|
136
|
+
choices: [
|
|
137
|
+
{ name: 'SQLite (better-sqlite3)', value: 'better-sqlite3' },
|
|
138
|
+
{ name: 'PostgreSQL (pg)', value: 'pg' },
|
|
139
|
+
{ name: 'MySQL (mysql2)', value: 'mysql2' },
|
|
140
|
+
{ name: 'None', value: null }
|
|
141
|
+
],
|
|
142
|
+
default: 'better-sqlite3',
|
|
143
|
+
when: (answers) => answers.useDatabase
|
|
144
|
+
}
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
// Ask about seed data if database is selected
|
|
148
|
+
let useSeed = false;
|
|
149
|
+
if (useDatabase && databaseType) {
|
|
150
|
+
const { generateSeed } = await inquirer.prompt([
|
|
151
|
+
{
|
|
152
|
+
type: 'confirm',
|
|
153
|
+
name: 'generateSeed',
|
|
154
|
+
message: 'Generate seed data based on existing models?',
|
|
155
|
+
default: false
|
|
156
|
+
}
|
|
157
|
+
]);
|
|
158
|
+
useSeed = generateSeed;
|
|
159
|
+
}
|
|
160
|
+
|
|
124
161
|
// Create directory structure (skip root if using current dir)
|
|
125
162
|
if (!useCurrentDir) {
|
|
126
163
|
fs.mkdirSync(projectPath, { recursive: true });
|
|
@@ -130,6 +167,11 @@ program
|
|
|
130
167
|
fs.mkdirSync(path.join(projectPath, 'views'), { recursive: true });
|
|
131
168
|
fs.mkdirSync(path.join(projectPath, 'public'), { recursive: true });
|
|
132
169
|
|
|
170
|
+
// Create models directory if database is selected
|
|
171
|
+
if (useDatabase && databaseType) {
|
|
172
|
+
fs.mkdirSync(path.join(projectPath, 'models'), { recursive: true });
|
|
173
|
+
}
|
|
174
|
+
|
|
133
175
|
// Create package.json
|
|
134
176
|
const packageJson = {
|
|
135
177
|
name: projectName,
|
|
@@ -146,6 +188,23 @@ program
|
|
|
146
188
|
}
|
|
147
189
|
};
|
|
148
190
|
|
|
191
|
+
// Add database driver if selected
|
|
192
|
+
if (useDatabase && databaseType) {
|
|
193
|
+
const dbDrivers = {
|
|
194
|
+
'better-sqlite3': '^9.0.0',
|
|
195
|
+
'pg': '^8.0.0',
|
|
196
|
+
'mysql2': '^3.0.0'
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
packageJson.dependencies[databaseType] = dbDrivers[databaseType];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Add faker if seed is selected
|
|
203
|
+
if (useSeed) {
|
|
204
|
+
packageJson.dependencies['@faker-js/faker'] = '^8.0.0';
|
|
205
|
+
packageJson.scripts.seed = 'node seeds/index.js';
|
|
206
|
+
}
|
|
207
|
+
|
|
149
208
|
fs.writeFileSync(
|
|
150
209
|
path.join(projectPath, 'package.json'),
|
|
151
210
|
JSON.stringify(packageJson, null, 2) + '\n'
|
|
@@ -172,15 +231,186 @@ app.listen(PORT, () => {
|
|
|
172
231
|
fs.writeFileSync(path.join(projectPath, 'server.js'), serverJs);
|
|
173
232
|
|
|
174
233
|
// Create .env.example
|
|
175
|
-
|
|
234
|
+
let envExample = `PORT=3000
|
|
176
235
|
NODE_ENV=development
|
|
177
236
|
DEFAULT_LOCALE=en
|
|
178
237
|
SUPPORTED_LOCALES=en,tr
|
|
179
238
|
BASE_URL=http://localhost:3000
|
|
180
239
|
`;
|
|
181
240
|
|
|
241
|
+
if (useDatabase && databaseType) {
|
|
242
|
+
if (databaseType === 'better-sqlite3') {
|
|
243
|
+
envExample += `DATABASE_URL=sqlite:./database.sqlite
|
|
244
|
+
`;
|
|
245
|
+
} else if (databaseType === 'pg') {
|
|
246
|
+
envExample += `DATABASE_URL=postgresql://user:password@localhost:5432/dbname
|
|
247
|
+
`;
|
|
248
|
+
} else if (databaseType === 'mysql2') {
|
|
249
|
+
envExample += `DATABASE_URL=mysql://user:password@localhost:3306/dbname
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
182
254
|
fs.writeFileSync(path.join(projectPath, '.env.example'), envExample);
|
|
183
255
|
|
|
256
|
+
// Create database config if database is selected
|
|
257
|
+
if (useDatabase && databaseType) {
|
|
258
|
+
let dbConfig = '';
|
|
259
|
+
|
|
260
|
+
if (databaseType === 'better-sqlite3') {
|
|
261
|
+
dbConfig = `module.exports = {
|
|
262
|
+
client: 'better-sqlite3',
|
|
263
|
+
connection: {
|
|
264
|
+
filename: process.env.DATABASE_URL?.replace('sqlite:', '') || './database.sqlite'
|
|
265
|
+
},
|
|
266
|
+
migrations: {
|
|
267
|
+
directory: './migrations',
|
|
268
|
+
tableName: 'knex_migrations',
|
|
269
|
+
},
|
|
270
|
+
useNullAsDefault: true
|
|
271
|
+
};
|
|
272
|
+
`;
|
|
273
|
+
} else if (databaseType === 'pg') {
|
|
274
|
+
dbConfig = `module.exports = {
|
|
275
|
+
client: 'pg',
|
|
276
|
+
connection: process.env.DATABASE_URL,
|
|
277
|
+
migrations: {
|
|
278
|
+
directory: './migrations',
|
|
279
|
+
tableName: 'knex_migrations',
|
|
280
|
+
},
|
|
281
|
+
pool: {
|
|
282
|
+
min: 2,
|
|
283
|
+
max: 10
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
`;
|
|
287
|
+
} else if (databaseType === 'mysql2') {
|
|
288
|
+
dbConfig = `module.exports = {
|
|
289
|
+
client: 'mysql2',
|
|
290
|
+
connection: process.env.DATABASE_URL,
|
|
291
|
+
migrations: {
|
|
292
|
+
directory: './migrations',
|
|
293
|
+
tableName: 'knex_migrations',
|
|
294
|
+
},
|
|
295
|
+
pool: {
|
|
296
|
+
min: 2,
|
|
297
|
+
max: 10
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
fs.writeFileSync(path.join(projectPath, 'webspresso.db.js'), dbConfig);
|
|
304
|
+
|
|
305
|
+
// Create migrations directory
|
|
306
|
+
fs.mkdirSync(path.join(projectPath, 'migrations'), { recursive: true });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Create seed files if seed is selected
|
|
310
|
+
if (useSeed) {
|
|
311
|
+
fs.mkdirSync(path.join(projectPath, 'seeds'), { recursive: true });
|
|
312
|
+
|
|
313
|
+
const seedIndex = `require('dotenv').config();
|
|
314
|
+
const { faker } = require('@faker-js/faker');
|
|
315
|
+
const path = require('path');
|
|
316
|
+
const fs = require('fs');
|
|
317
|
+
const { createDatabase, getAllModels } = require('webspresso/orm');
|
|
318
|
+
const dbConfig = require('./webspresso.db.js');
|
|
319
|
+
|
|
320
|
+
const db = createDatabase(dbConfig);
|
|
321
|
+
const seeder = db.seeder(faker);
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Load all models from models/ directory
|
|
325
|
+
*/
|
|
326
|
+
function loadModels() {
|
|
327
|
+
const modelsDir = path.join(__dirname, 'models');
|
|
328
|
+
|
|
329
|
+
if (!fs.existsSync(modelsDir)) {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const modelFiles = fs.readdirSync(modelsDir)
|
|
334
|
+
.filter(file => file.endsWith('.js') && !file.startsWith('_'));
|
|
335
|
+
|
|
336
|
+
const loadedModels = [];
|
|
337
|
+
for (const file of modelFiles) {
|
|
338
|
+
try {
|
|
339
|
+
require(path.join(modelsDir, file));
|
|
340
|
+
loadedModels.push(file);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
console.warn(\`⚠️ Failed to load model from \${file}:\`, error.message);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return loadedModels;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Seed database with fake data
|
|
351
|
+
* This script automatically detects models in the models/ directory
|
|
352
|
+
* and generates seed data based on their schemas.
|
|
353
|
+
*/
|
|
354
|
+
async function runSeeds() {
|
|
355
|
+
try {
|
|
356
|
+
console.log('🌱 Starting seed process...');
|
|
357
|
+
|
|
358
|
+
// Load all models
|
|
359
|
+
const loadedFiles = loadModels();
|
|
360
|
+
|
|
361
|
+
if (loadedFiles.length === 0) {
|
|
362
|
+
console.log('⚠️ No model files found in models/ directory.');
|
|
363
|
+
console.log(' Create models first, then run: npm run seed');
|
|
364
|
+
await db.knex.destroy();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Get all registered models
|
|
369
|
+
const models = getAllModels();
|
|
370
|
+
|
|
371
|
+
if (models.size === 0) {
|
|
372
|
+
console.log('⚠️ No models registered. Make sure your model files export models using defineModel().');
|
|
373
|
+
await db.knex.destroy();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
console.log(\`📦 Found \${models.size} model(s):\`);
|
|
378
|
+
for (const [name] of models) {
|
|
379
|
+
console.log(\` - \${name}\`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Seed each model
|
|
383
|
+
const results = {};
|
|
384
|
+
for (const [modelName, model] of models) {
|
|
385
|
+
console.log(\`\\n🌱 Seeding \${modelName}...\`);
|
|
386
|
+
|
|
387
|
+
// Default count: 10 records per model
|
|
388
|
+
const count = 10;
|
|
389
|
+
const records = await seeder.seed(modelName, count);
|
|
390
|
+
results[modelName] = records.length;
|
|
391
|
+
|
|
392
|
+
console.log(\`✅ Created \${records.length} \${modelName} record(s)\`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
console.log(\`\\n✨ Seed completed! Created:\`);
|
|
396
|
+
for (const [modelName, count] of Object.entries(results)) {
|
|
397
|
+
console.log(\` - \${count} \${modelName} record(s)\`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
await db.knex.destroy();
|
|
401
|
+
} catch (error) {
|
|
402
|
+
console.error('❌ Seed failed:', error);
|
|
403
|
+
await db.knex.destroy().catch(() => {});
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
runSeeds();
|
|
409
|
+
`;
|
|
410
|
+
|
|
411
|
+
fs.writeFileSync(path.join(projectPath, 'seeds', 'index.js'), seedIndex);
|
|
412
|
+
}
|
|
413
|
+
|
|
184
414
|
// Create .gitignore
|
|
185
415
|
const gitignore = `node_modules/
|
|
186
416
|
.env
|
|
@@ -429,8 +659,8 @@ module.exports = {
|
|
|
429
659
|
console.log('✅ Tailwind CSS setup complete!');
|
|
430
660
|
}
|
|
431
661
|
|
|
432
|
-
//
|
|
433
|
-
|
|
662
|
+
// Helper function to run installation
|
|
663
|
+
async function runInstallation(projectPath, useTailwind) {
|
|
434
664
|
console.log('\n📦 Installing dependencies...\n');
|
|
435
665
|
const { execSync } = require('child_process');
|
|
436
666
|
try {
|
|
@@ -447,27 +677,137 @@ module.exports = {
|
|
|
447
677
|
});
|
|
448
678
|
}
|
|
449
679
|
|
|
450
|
-
console.log('\n✅
|
|
680
|
+
console.log('\n✅ Installation complete!\n');
|
|
681
|
+
} catch (err) {
|
|
682
|
+
console.error('❌ Installation failed:', err.message);
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Helper function to start dev server
|
|
688
|
+
function startDevServer(projectPath, useTailwind) {
|
|
689
|
+
console.log('\n🚀 Starting development server...\n');
|
|
690
|
+
console.log('Press Ctrl+C to stop\n');
|
|
691
|
+
|
|
692
|
+
const { spawn } = require('child_process');
|
|
693
|
+
|
|
694
|
+
if (useTailwind) {
|
|
695
|
+
// Start CSS watch and server together
|
|
696
|
+
const cssWatch = spawn('npm', ['run', 'watch:css'], {
|
|
697
|
+
stdio: 'inherit',
|
|
698
|
+
shell: true,
|
|
699
|
+
cwd: projectPath
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
const server = spawn('node', ['--watch', 'server.js'], {
|
|
703
|
+
stdio: 'inherit',
|
|
704
|
+
shell: true,
|
|
705
|
+
cwd: projectPath,
|
|
706
|
+
env: { ...process.env, PORT: process.env.PORT || '3000', NODE_ENV: 'development' }
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// Handle exit
|
|
710
|
+
const cleanup = () => {
|
|
711
|
+
cssWatch.kill();
|
|
712
|
+
server.kill();
|
|
713
|
+
process.exit(0);
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
process.on('SIGINT', cleanup);
|
|
717
|
+
process.on('SIGTERM', cleanup);
|
|
718
|
+
|
|
719
|
+
cssWatch.on('exit', cleanup);
|
|
720
|
+
server.on('exit', cleanup);
|
|
721
|
+
} else {
|
|
722
|
+
// Just start server
|
|
723
|
+
const server = spawn('node', ['--watch', 'server.js'], {
|
|
724
|
+
stdio: 'inherit',
|
|
725
|
+
shell: true,
|
|
726
|
+
cwd: projectPath,
|
|
727
|
+
env: { ...process.env, PORT: process.env.PORT || '3000', NODE_ENV: 'development' }
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
server.on('exit', (code) => {
|
|
731
|
+
process.exit(code || 0);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
process.on('SIGINT', () => {
|
|
735
|
+
server.kill();
|
|
736
|
+
process.exit(0);
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Auto install if requested or ask interactively
|
|
742
|
+
if (autoInstall) {
|
|
743
|
+
await runInstallation(projectPath, useTailwind);
|
|
744
|
+
|
|
745
|
+
// Ask if user wants to start dev server
|
|
746
|
+
const { shouldStartDev } = await inquirer.prompt([
|
|
747
|
+
{
|
|
748
|
+
type: 'confirm',
|
|
749
|
+
name: 'shouldStartDev',
|
|
750
|
+
message: 'Start development server?',
|
|
751
|
+
default: true
|
|
752
|
+
}
|
|
753
|
+
]);
|
|
754
|
+
|
|
755
|
+
if (shouldStartDev) {
|
|
756
|
+
startDevServer(projectPath, useTailwind);
|
|
757
|
+
} else {
|
|
758
|
+
console.log('✅ Project ready!\n');
|
|
451
759
|
console.log('Start developing:');
|
|
452
760
|
if (!useCurrentDir) {
|
|
453
761
|
console.log(` cd ${projectName}`);
|
|
454
762
|
}
|
|
455
763
|
console.log(' npm run dev\n');
|
|
456
|
-
} catch (err) {
|
|
457
|
-
console.error('❌ Installation failed:', err.message);
|
|
458
|
-
process.exit(1);
|
|
459
764
|
}
|
|
460
765
|
} else {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
766
|
+
// Ask if user wants to install dependencies
|
|
767
|
+
const { shouldInstall } = await inquirer.prompt([
|
|
768
|
+
{
|
|
769
|
+
type: 'confirm',
|
|
770
|
+
name: 'shouldInstall',
|
|
771
|
+
message: 'Install dependencies and build CSS now?',
|
|
772
|
+
default: true
|
|
773
|
+
}
|
|
774
|
+
]);
|
|
775
|
+
|
|
776
|
+
if (shouldInstall) {
|
|
777
|
+
await runInstallation(projectPath, useTailwind);
|
|
778
|
+
|
|
779
|
+
// Ask if user wants to start dev server
|
|
780
|
+
const { shouldStartDev } = await inquirer.prompt([
|
|
781
|
+
{
|
|
782
|
+
type: 'confirm',
|
|
783
|
+
name: 'shouldStartDev',
|
|
784
|
+
message: 'Start development server?',
|
|
785
|
+
default: true
|
|
786
|
+
}
|
|
787
|
+
]);
|
|
788
|
+
|
|
789
|
+
if (shouldStartDev) {
|
|
790
|
+
startDevServer(projectPath, useTailwind);
|
|
791
|
+
} else {
|
|
792
|
+
console.log('\n✅ Project ready!\n');
|
|
793
|
+
console.log('Start developing:');
|
|
794
|
+
if (!useCurrentDir) {
|
|
795
|
+
console.log(` cd ${projectName}`);
|
|
796
|
+
}
|
|
797
|
+
console.log(' npm run dev\n');
|
|
798
|
+
}
|
|
799
|
+
} else {
|
|
800
|
+
console.log('\n✅ Project created successfully!\n');
|
|
801
|
+
console.log('Next steps:');
|
|
802
|
+
if (!useCurrentDir) {
|
|
803
|
+
console.log(` cd ${projectName}`);
|
|
804
|
+
}
|
|
805
|
+
console.log(' npm install');
|
|
806
|
+
if (useTailwind) {
|
|
807
|
+
console.log(' npm run build:css');
|
|
808
|
+
}
|
|
809
|
+
console.log(' npm run dev\n');
|
|
469
810
|
}
|
|
470
|
-
console.log(' npm run dev\n');
|
|
471
811
|
}
|
|
472
812
|
});
|
|
473
813
|
|
|
@@ -1132,6 +1472,174 @@ exports.down = function(knex) {
|
|
|
1132
1472
|
`;
|
|
1133
1473
|
}
|
|
1134
1474
|
|
|
1475
|
+
// seed command
|
|
1476
|
+
program
|
|
1477
|
+
.command('seed')
|
|
1478
|
+
.description('Run database seeders to populate database with fake data')
|
|
1479
|
+
.option('-c, --config <path>', 'Path to database config file')
|
|
1480
|
+
.option('-e, --env <environment>', 'Environment (development, production)', 'development')
|
|
1481
|
+
.option('--setup', 'Setup seed files if they don\'t exist')
|
|
1482
|
+
.action(async (options) => {
|
|
1483
|
+
// Check if it's a Webspresso project
|
|
1484
|
+
if (!fs.existsSync(path.join(process.cwd(), 'server.js')) &&
|
|
1485
|
+
!fs.existsSync(path.join(process.cwd(), 'pages'))) {
|
|
1486
|
+
console.error('❌ Not a Webspresso project! Run this command in your project directory.');
|
|
1487
|
+
process.exit(1);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// Check for faker
|
|
1491
|
+
let faker;
|
|
1492
|
+
try {
|
|
1493
|
+
faker = require('@faker-js/faker');
|
|
1494
|
+
} catch {
|
|
1495
|
+
console.error('❌ @faker-js/faker not installed.');
|
|
1496
|
+
console.log(' Install it with: npm install @faker-js/faker');
|
|
1497
|
+
process.exit(1);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// Load database config
|
|
1501
|
+
const { config, path: configPath } = loadDbConfig(options.config);
|
|
1502
|
+
console.log(`\n📦 Using config: ${configPath}`);
|
|
1503
|
+
console.log(` Environment: ${options.env}\n`);
|
|
1504
|
+
|
|
1505
|
+
// Check if models directory exists
|
|
1506
|
+
const modelsDir = path.join(process.cwd(), 'models');
|
|
1507
|
+
if (!fs.existsSync(modelsDir)) {
|
|
1508
|
+
console.error('❌ models/ directory not found.');
|
|
1509
|
+
console.log(' Create models first, then run: webspresso seed');
|
|
1510
|
+
process.exit(1);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// Check if seeds directory exists
|
|
1514
|
+
const seedsDir = path.join(process.cwd(), 'seeds');
|
|
1515
|
+
const seedIndexPath = path.join(seedsDir, 'index.js');
|
|
1516
|
+
|
|
1517
|
+
if (!fs.existsSync(seedsDir) || !fs.existsSync(seedIndexPath)) {
|
|
1518
|
+
if (options.setup) {
|
|
1519
|
+
console.log('📝 Setting up seed files...\n');
|
|
1520
|
+
|
|
1521
|
+
// Create seeds directory
|
|
1522
|
+
if (!fs.existsSync(seedsDir)) {
|
|
1523
|
+
fs.mkdirSync(seedsDir, { recursive: true });
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// Create seed index file
|
|
1527
|
+
const seedIndex = `require('dotenv').config();
|
|
1528
|
+
const { faker } = require('@faker-js/faker');
|
|
1529
|
+
const path = require('path');
|
|
1530
|
+
const fs = require('fs');
|
|
1531
|
+
const { createDatabase, getAllModels } = require('webspresso/orm');
|
|
1532
|
+
const dbConfig = require('../webspresso.db.js');
|
|
1533
|
+
|
|
1534
|
+
const db = createDatabase(dbConfig);
|
|
1535
|
+
const seeder = db.seeder(faker);
|
|
1536
|
+
|
|
1537
|
+
/**
|
|
1538
|
+
* Load all models from models/ directory
|
|
1539
|
+
*/
|
|
1540
|
+
function loadModels() {
|
|
1541
|
+
const modelsDir = path.join(__dirname, '..', 'models');
|
|
1542
|
+
|
|
1543
|
+
if (!fs.existsSync(modelsDir)) {
|
|
1544
|
+
return [];
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
const modelFiles = fs.readdirSync(modelsDir)
|
|
1548
|
+
.filter(file => file.endsWith('.js') && !file.startsWith('_'));
|
|
1549
|
+
|
|
1550
|
+
const loadedModels = [];
|
|
1551
|
+
for (const file of modelFiles) {
|
|
1552
|
+
try {
|
|
1553
|
+
require(path.join(modelsDir, file));
|
|
1554
|
+
loadedModels.push(file);
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
console.warn(\`⚠️ Failed to load model from \${file}:\`, error.message);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
return loadedModels;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* Seed database with fake data
|
|
1565
|
+
* This script automatically detects models in the models/ directory
|
|
1566
|
+
* and generates seed data based on their schemas.
|
|
1567
|
+
*/
|
|
1568
|
+
async function runSeeds() {
|
|
1569
|
+
try {
|
|
1570
|
+
console.log('🌱 Starting seed process...');
|
|
1571
|
+
|
|
1572
|
+
// Load all models
|
|
1573
|
+
const loadedFiles = loadModels();
|
|
1574
|
+
|
|
1575
|
+
if (loadedFiles.length === 0) {
|
|
1576
|
+
console.log('⚠️ No model files found in models/ directory.');
|
|
1577
|
+
console.log(' Create models first, then run: webspresso seed');
|
|
1578
|
+
await db.knex.destroy();
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
// Get all registered models
|
|
1583
|
+
const models = getAllModels();
|
|
1584
|
+
|
|
1585
|
+
if (models.size === 0) {
|
|
1586
|
+
console.log('⚠️ No models registered. Make sure your model files export models using defineModel().');
|
|
1587
|
+
await db.knex.destroy();
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
console.log(\`📦 Found \${models.size} model(s):\`);
|
|
1592
|
+
for (const [name] of models) {
|
|
1593
|
+
console.log(\` - \${name}\`);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// Seed each model
|
|
1597
|
+
const results = {};
|
|
1598
|
+
for (const [modelName, model] of models) {
|
|
1599
|
+
console.log(\`\\n🌱 Seeding \${modelName}...\`);
|
|
1600
|
+
|
|
1601
|
+
// Default count: 10 records per model
|
|
1602
|
+
const count = 10;
|
|
1603
|
+
const records = await seeder.seed(modelName, count);
|
|
1604
|
+
results[modelName] = records.length;
|
|
1605
|
+
|
|
1606
|
+
console.log(\`✅ Created \${records.length} \${modelName} record(s)\`);
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
console.log(\`\\n✨ Seed completed! Created:\`);
|
|
1610
|
+
for (const [modelName, count] of Object.entries(results)) {
|
|
1611
|
+
console.log(\` - \${count} \${modelName} record(s)\`);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
await db.knex.destroy();
|
|
1615
|
+
} catch (error) {
|
|
1616
|
+
console.error('❌ Seed failed:', error);
|
|
1617
|
+
await db.knex.destroy().catch(() => {});
|
|
1618
|
+
process.exit(1);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
runSeeds();
|
|
1623
|
+
`;
|
|
1624
|
+
|
|
1625
|
+
fs.writeFileSync(seedIndexPath, seedIndex);
|
|
1626
|
+
console.log('✅ Seed files created!\n');
|
|
1627
|
+
} else {
|
|
1628
|
+
console.error('❌ seeds/index.js not found.');
|
|
1629
|
+
console.log(' Run with --setup flag to create seed files: webspresso seed --setup');
|
|
1630
|
+
process.exit(1);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// Run the seed script
|
|
1635
|
+
try {
|
|
1636
|
+
require(seedIndexPath);
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
console.error('❌ Failed to run seed:', error.message);
|
|
1639
|
+
process.exit(1);
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1135
1643
|
// Parse arguments
|
|
1136
1644
|
program.parse();
|
|
1137
1645
|
|
package/package.json
CHANGED