@coherent.js/cli 1.0.0-beta.3 → 1.0.0-beta.5
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 +4 -12
- package/dist/index.js +481 -201
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Command-line interface for Coherent.js projects. Scaffold new applications, gene
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Install globally
|
|
9
|
-
|
|
9
|
+
pnpm add -g @coherent.js/cli
|
|
10
10
|
|
|
11
11
|
# Create a new project
|
|
12
12
|
coherent create my-app
|
|
@@ -23,24 +23,16 @@ coherent dev
|
|
|
23
23
|
### Global Installation (Recommended)
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
npm install -g @coherent.js/cli
|
|
27
|
-
# or
|
|
28
|
-
yarn global add @coherent.js/cli
|
|
29
|
-
# or
|
|
30
26
|
pnpm add -g @coherent.js/cli
|
|
31
27
|
```
|
|
32
28
|
|
|
33
29
|
### Local Installation
|
|
34
30
|
|
|
35
31
|
```bash
|
|
36
|
-
npm install --save-dev @coherent.js/cli
|
|
37
|
-
# or
|
|
38
|
-
yarn add --dev @coherent.js/cli
|
|
39
|
-
# or
|
|
40
32
|
pnpm add -D @coherent.js/cli
|
|
41
33
|
|
|
42
|
-
# Use with
|
|
43
|
-
|
|
34
|
+
# Use with pnpm
|
|
35
|
+
pnpm exec coherent create my-app
|
|
44
36
|
```
|
|
45
37
|
|
|
46
38
|
## 🛠️ Commands
|
|
@@ -381,7 +373,7 @@ test('Button renders correctly', () => {
|
|
|
381
373
|
|
|
382
374
|
Run tests with:
|
|
383
375
|
```bash
|
|
384
|
-
|
|
376
|
+
pnpm test
|
|
385
377
|
# or if using the CLI
|
|
386
378
|
coherent test
|
|
387
379
|
```
|
package/dist/index.js
CHANGED
|
@@ -43,13 +43,14 @@ function getCLIVersion() {
|
|
|
43
43
|
// src/generators/runtime-scaffold.js
|
|
44
44
|
var cliVersion = getCLIVersion();
|
|
45
45
|
function generateBuiltInServer(options = {}) {
|
|
46
|
-
const { port = 3e3, hasApi = false, hasDatabase = false } = options;
|
|
46
|
+
const { port = 3e3, hasApi = false, hasDatabase = false, hasAuth = false } = options;
|
|
47
47
|
const imports = [
|
|
48
48
|
`import http from 'http';`,
|
|
49
49
|
`import { render } from '@coherent.js/core';`
|
|
50
50
|
];
|
|
51
51
|
if (hasApi) imports.push(`import { setupRoutes } from './api/routes.js';`);
|
|
52
52
|
if (hasDatabase) imports.push(`import { initDatabase } from './db/index.js';`);
|
|
53
|
+
if (hasAuth) imports.push(`import { setupAuthRoutes } from './api/auth.js';`);
|
|
53
54
|
const server = `
|
|
54
55
|
${imports.join("\n")}
|
|
55
56
|
import { HomePage } from './components/HomePage.js';
|
|
@@ -60,15 +61,21 @@ ${hasDatabase ? `// Initialize database
|
|
|
60
61
|
await initDatabase();
|
|
61
62
|
` : ""}${hasApi ? `// Setup API routes
|
|
62
63
|
const apiRoutes = setupRoutes();
|
|
64
|
+
` : ""}${hasAuth ? `// Setup auth routes
|
|
65
|
+
const authRoutes = setupAuthRoutes();
|
|
63
66
|
` : ""}
|
|
64
67
|
const server = http.createServer(async (req, res) => {
|
|
65
68
|
const url = new URL(req.url, \`http://\${req.headers.host}\`);
|
|
66
69
|
|
|
67
|
-
${hasApi ? ` // Handle API routes
|
|
70
|
+
${hasApi || hasAuth ? ` // Handle API routes
|
|
68
71
|
if (url.pathname.startsWith('/api')) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
const allRoutes = [...${hasApi ? "apiRoutes" : "[]"}, ...${hasAuth ? "authRoutes" : "[]"}];
|
|
73
|
+
for (const route of allRoutes) {
|
|
74
|
+
const match = matchRoute(route.path, url.pathname, req.method, route.method);
|
|
75
|
+
if (match) {
|
|
76
|
+
req.params = match.params;
|
|
77
|
+
return route.handler(req, res);
|
|
78
|
+
}
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
|
|
@@ -101,6 +108,42 @@ ${hasApi ? ` // Handle API routes
|
|
|
101
108
|
}
|
|
102
109
|
});
|
|
103
110
|
|
|
111
|
+
// Route matching helper
|
|
112
|
+
function matchRoute(routePattern, urlPath, requestMethod, routeMethod) {
|
|
113
|
+
// Check HTTP method
|
|
114
|
+
if (requestMethod !== routeMethod) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Split paths into segments
|
|
119
|
+
const routeSegments = routePattern.split('/').filter(Boolean);
|
|
120
|
+
const urlSegments = urlPath.split('/').filter(Boolean);
|
|
121
|
+
|
|
122
|
+
// Check if lengths match
|
|
123
|
+
if (routeSegments.length !== urlSegments.length) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const params = {};
|
|
128
|
+
|
|
129
|
+
// Match each segment
|
|
130
|
+
for (let i = 0; i < routeSegments.length; i++) {
|
|
131
|
+
const routeSegment = routeSegments[i];
|
|
132
|
+
const urlSegment = urlSegments[i];
|
|
133
|
+
|
|
134
|
+
// Check for parameter (e.g., :id)
|
|
135
|
+
if (routeSegment.startsWith(':')) {
|
|
136
|
+
const paramName = routeSegment.substring(1);
|
|
137
|
+
params[paramName] = urlSegment;
|
|
138
|
+
} else if (routeSegment !== urlSegment) {
|
|
139
|
+
// Literal segment doesn't match
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { params };
|
|
145
|
+
}
|
|
146
|
+
|
|
104
147
|
server.listen(PORT, () => {
|
|
105
148
|
console.log(\`Server running at http://localhost:\${PORT}\`);
|
|
106
149
|
});
|
|
@@ -517,7 +560,7 @@ process.on('SIGINT', async () => {
|
|
|
517
560
|
function generateExampleModel(dbType) {
|
|
518
561
|
const models = {
|
|
519
562
|
postgres: `
|
|
520
|
-
import { getDatabase } from '
|
|
563
|
+
import { getDatabase } from '../index.js';
|
|
521
564
|
|
|
522
565
|
export class UserModel {
|
|
523
566
|
static async createTable() {
|
|
@@ -570,7 +613,7 @@ export class UserModel {
|
|
|
570
613
|
}
|
|
571
614
|
`,
|
|
572
615
|
mysql: `
|
|
573
|
-
import { getDatabase } from '
|
|
616
|
+
import { getDatabase } from '../index.js';
|
|
574
617
|
|
|
575
618
|
export class UserModel {
|
|
576
619
|
static async createTable() {
|
|
@@ -623,7 +666,7 @@ export class UserModel {
|
|
|
623
666
|
}
|
|
624
667
|
`,
|
|
625
668
|
sqlite: `
|
|
626
|
-
import { getDatabase } from '
|
|
669
|
+
import { getDatabase } from '../index.js';
|
|
627
670
|
|
|
628
671
|
export class UserModel {
|
|
629
672
|
static createTable() {
|
|
@@ -673,7 +716,7 @@ export class UserModel {
|
|
|
673
716
|
}
|
|
674
717
|
`,
|
|
675
718
|
mongodb: `
|
|
676
|
-
import { getDatabase } from '
|
|
719
|
+
import { getDatabase } from '../index.js';
|
|
677
720
|
|
|
678
721
|
export class UserModel {
|
|
679
722
|
static collectionName = 'users';
|
|
@@ -1156,6 +1199,158 @@ router.get('/me', authMiddleware, async (req, res) => {
|
|
|
1156
1199
|
});
|
|
1157
1200
|
|
|
1158
1201
|
export default router;
|
|
1202
|
+
`,
|
|
1203
|
+
"built-in": `
|
|
1204
|
+
// Auth routes for built-in HTTP server
|
|
1205
|
+
import { generateToken, verifyToken } from '../middleware/auth.js';
|
|
1206
|
+
import { UserModel } from '../db/models/User.js';
|
|
1207
|
+
|
|
1208
|
+
// Register handler
|
|
1209
|
+
export async function registerHandler(req, res) {
|
|
1210
|
+
try {
|
|
1211
|
+
// Parse JSON body
|
|
1212
|
+
let body = '';
|
|
1213
|
+
req.on('data', chunk => {
|
|
1214
|
+
body += chunk.toString();
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
req.on('end', async () => {
|
|
1218
|
+
try {
|
|
1219
|
+
const { email, name, password } = JSON.parse(body);
|
|
1220
|
+
|
|
1221
|
+
// Validate input
|
|
1222
|
+
if (!email || !name || !password) {
|
|
1223
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1224
|
+
return res.end(JSON.stringify({ error: 'Missing required fields' }));
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// Check if user exists
|
|
1228
|
+
const existingUser = await UserModel.findByEmail(email);
|
|
1229
|
+
if (existingUser) {
|
|
1230
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1231
|
+
return res.end(JSON.stringify({ error: 'User already exists' }));
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// Create user (you should hash the password!)
|
|
1235
|
+
const user = await UserModel.create({ email, name });
|
|
1236
|
+
|
|
1237
|
+
// Generate token
|
|
1238
|
+
const token = generateToken({ id: user.id, email: user.email });
|
|
1239
|
+
|
|
1240
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1241
|
+
res.end(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name }, token }));
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
console.error('Register error:', error);
|
|
1244
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1245
|
+
res.end(JSON.stringify({ error: 'Registration failed' }));
|
|
1246
|
+
}
|
|
1247
|
+
});
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
console.error('Register error:', error);
|
|
1250
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1251
|
+
res.end(JSON.stringify({ error: 'Registration failed' }));
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
// Login handler
|
|
1256
|
+
export async function loginHandler(req, res) {
|
|
1257
|
+
try {
|
|
1258
|
+
// Parse JSON body
|
|
1259
|
+
let body = '';
|
|
1260
|
+
req.on('data', chunk => {
|
|
1261
|
+
body += chunk.toString();
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
req.on('end', async () => {
|
|
1265
|
+
try {
|
|
1266
|
+
const { email, password } = JSON.parse(body);
|
|
1267
|
+
|
|
1268
|
+
// Validate input
|
|
1269
|
+
if (!email || !password) {
|
|
1270
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1271
|
+
return res.end(JSON.stringify({ error: 'Missing required fields' }));
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// Find user
|
|
1275
|
+
const user = await UserModel.findByEmail(email);
|
|
1276
|
+
if (!user) {
|
|
1277
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
1278
|
+
return res.end(JSON.stringify({ error: 'Invalid credentials' }));
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// Verify password (you should implement proper password checking!)
|
|
1282
|
+
|
|
1283
|
+
// Generate token
|
|
1284
|
+
const token = generateToken({ id: user.id, email: user.email });
|
|
1285
|
+
|
|
1286
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1287
|
+
res.end(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name }, token }));
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
console.error('Login error:', error);
|
|
1290
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1291
|
+
res.end(JSON.stringify({ error: 'Login failed' }));
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
} catch (error) {
|
|
1295
|
+
console.error('Login error:', error);
|
|
1296
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1297
|
+
res.end(JSON.stringify({ error: 'Login failed' }));
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// Get current user handler
|
|
1302
|
+
export async function meHandler(req, res) {
|
|
1303
|
+
try {
|
|
1304
|
+
// Verify token from Authorization header
|
|
1305
|
+
const authHeader = req.headers.authorization;
|
|
1306
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
1307
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
1308
|
+
return res.end(JSON.stringify({ error: 'No token provided' }));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
const token = authHeader.substring(7);
|
|
1312
|
+
const decoded = verifyToken(token);
|
|
1313
|
+
|
|
1314
|
+
if (!decoded) {
|
|
1315
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
1316
|
+
return res.end(JSON.stringify({ error: 'Invalid or expired token' }));
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
const user = await UserModel.findById(decoded.id);
|
|
1320
|
+
if (!user) {
|
|
1321
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1322
|
+
return res.end(JSON.stringify({ error: 'User not found' }));
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1326
|
+
res.end(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name } }));
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
console.error('Get user error:', error);
|
|
1329
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1330
|
+
res.end(JSON.stringify({ error: 'Failed to get user' }));
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// For built-in HTTP server compatibility
|
|
1335
|
+
export function setupAuthRoutes() {
|
|
1336
|
+
return [
|
|
1337
|
+
{
|
|
1338
|
+
path: '/api/auth/register',
|
|
1339
|
+
method: 'POST',
|
|
1340
|
+
handler: registerHandler
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
path: '/api/auth/login',
|
|
1344
|
+
method: 'POST',
|
|
1345
|
+
handler: loginHandler
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
path: '/api/auth/me',
|
|
1349
|
+
method: 'GET',
|
|
1350
|
+
handler: meHandler
|
|
1351
|
+
}
|
|
1352
|
+
];
|
|
1353
|
+
}
|
|
1159
1354
|
`,
|
|
1160
1355
|
fastify: `
|
|
1161
1356
|
import { generateToken } from '../plugins/auth.js';
|
|
@@ -1504,6 +1699,73 @@ router.post('/users', {
|
|
|
1504
1699
|
}
|
|
1505
1700
|
});
|
|
1506
1701
|
|
|
1702
|
+
// Handler for GET /api/users/:id
|
|
1703
|
+
export async function getUsersByIdHandler(req, res) {
|
|
1704
|
+
try {
|
|
1705
|
+
// Extract ID from URL parameters
|
|
1706
|
+
const { id } = req.params;
|
|
1707
|
+
|
|
1708
|
+
// Call the original handler
|
|
1709
|
+
const result = await router.handle('GET', '/users/:id', { params: { id } }, {});
|
|
1710
|
+
|
|
1711
|
+
// Send JSON response
|
|
1712
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1713
|
+
res.end(JSON.stringify(result));
|
|
1714
|
+
} catch (error) {
|
|
1715
|
+
console.error('API Error:', error);
|
|
1716
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1717
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// Handler for POST /api/users
|
|
1722
|
+
export async function postUsersHandler(req, res) {
|
|
1723
|
+
try {
|
|
1724
|
+
// Parse JSON body
|
|
1725
|
+
let body = '';
|
|
1726
|
+
req.on('data', chunk => {
|
|
1727
|
+
body += chunk.toString();
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
req.on('end', async () => {
|
|
1731
|
+
try {
|
|
1732
|
+
const parsedBody = JSON.parse(body);
|
|
1733
|
+
|
|
1734
|
+
// Call the original handler
|
|
1735
|
+
const result = await router.handle('POST', '/users', { body: parsedBody }, {});
|
|
1736
|
+
|
|
1737
|
+
// Send JSON response
|
|
1738
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1739
|
+
res.end(JSON.stringify(result));
|
|
1740
|
+
} catch (error) {
|
|
1741
|
+
console.error('API Error:', error);
|
|
1742
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1743
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
} catch (error) {
|
|
1747
|
+
console.error('API Error:', error);
|
|
1748
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1749
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
// For built-in HTTP server compatibility
|
|
1754
|
+
export function setupRoutes() {
|
|
1755
|
+
return [
|
|
1756
|
+
{
|
|
1757
|
+
path: '/api/users/:id',
|
|
1758
|
+
method: 'GET',
|
|
1759
|
+
handler: getUsersByIdHandler
|
|
1760
|
+
},
|
|
1761
|
+
{
|
|
1762
|
+
path: '/api/users',
|
|
1763
|
+
method: 'POST',
|
|
1764
|
+
handler: postUsersHandler
|
|
1765
|
+
}
|
|
1766
|
+
];
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1507
1769
|
export default router;
|
|
1508
1770
|
`;
|
|
1509
1771
|
return {
|
|
@@ -2295,16 +2557,16 @@ A Coherent.js application built with pure JavaScript objects.
|
|
|
2295
2557
|
|
|
2296
2558
|
\`\`\`bash
|
|
2297
2559
|
# Install dependencies
|
|
2298
|
-
|
|
2560
|
+
pnpm install
|
|
2299
2561
|
|
|
2300
2562
|
# Start development server
|
|
2301
|
-
|
|
2563
|
+
pnpm run dev
|
|
2302
2564
|
|
|
2303
2565
|
# Build for production
|
|
2304
|
-
|
|
2566
|
+
pnpm run build
|
|
2305
2567
|
|
|
2306
2568
|
# Run tests
|
|
2307
|
-
|
|
2569
|
+
pnpm test
|
|
2308
2570
|
\`\`\`
|
|
2309
2571
|
|
|
2310
2572
|
## Project Structure
|
|
@@ -2323,7 +2585,7 @@ tests/ # Test files
|
|
|
2323
2585
|
## Learn More
|
|
2324
2586
|
|
|
2325
2587
|
- [Coherent.js Documentation](https://github.com/Tomdrouv1/coherent.js)
|
|
2326
|
-
- [API Reference](https://github.com/Tomdrouv1/coherent.js/docs/api-reference.md)
|
|
2588
|
+
- [API Reference](https://github.com/Tomdrouv1/coherent.js/tree/main/docs/api-reference.md)
|
|
2327
2589
|
|
|
2328
2590
|
## License
|
|
2329
2591
|
|
|
@@ -2387,21 +2649,21 @@ logs
|
|
|
2387
2649
|
.node_repl_history
|
|
2388
2650
|
`;
|
|
2389
2651
|
writeFileSync(join2(projectPath, ".gitignore"), gitignore);
|
|
2390
|
-
const testFile = `import {
|
|
2391
|
-
import assert from 'node:assert';
|
|
2652
|
+
const testFile = `import { describe, it, expect } from 'vitest';
|
|
2392
2653
|
import { render } from '@coherent.js/core';
|
|
2393
2654
|
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2655
|
+
describe('Basic Component Rendering', () => {
|
|
2656
|
+
it('renders basic component', () => {
|
|
2657
|
+
const component = {
|
|
2658
|
+
div: {
|
|
2659
|
+
text: 'Hello, World!'
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2400
2662
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
});
|
|
2404
|
-
|
|
2663
|
+
const html = render(component);
|
|
2664
|
+
expect(html).toContain('Hello, World!');
|
|
2665
|
+
});
|
|
2666
|
+
});`;
|
|
2405
2667
|
writeFileSync(join2(projectPath, "tests/basic.test.js"), testFile);
|
|
2406
2668
|
}
|
|
2407
2669
|
|
|
@@ -2776,7 +3038,7 @@ function generateBasicComponent(name) {
|
|
|
2776
3038
|
|
|
2777
3039
|
/**
|
|
2778
3040
|
* ${name} component
|
|
2779
|
-
*
|
|
3041
|
+
*
|
|
2780
3042
|
* @param {Object} props - Component properties
|
|
2781
3043
|
* @param {string} props.className - CSS class name
|
|
2782
3044
|
* @param {Array|Object} props.children - Child elements
|
|
@@ -2803,7 +3065,7 @@ function generateFunctionalComponent(name) {
|
|
|
2803
3065
|
|
|
2804
3066
|
/**
|
|
2805
3067
|
* ${name} - Functional component with business logic
|
|
2806
|
-
*
|
|
3068
|
+
*
|
|
2807
3069
|
* @param {Object} props - Component properties
|
|
2808
3070
|
* @param {Array} props.items - Items to display
|
|
2809
3071
|
* @param {Function} props.onItemClick - Callback for item clicks
|
|
@@ -2821,11 +3083,11 @@ export const ${name} = createComponent(({ items = [], onItemClick, className = '
|
|
|
2821
3083
|
div: {
|
|
2822
3084
|
className: \`${name.toLowerCase()} \${className}\`.trim(),
|
|
2823
3085
|
children: [
|
|
2824
|
-
{
|
|
2825
|
-
h3: {
|
|
3086
|
+
{
|
|
3087
|
+
h3: {
|
|
2826
3088
|
className: '${name.toLowerCase()}__title',
|
|
2827
|
-
text: '${name}'
|
|
2828
|
-
}
|
|
3089
|
+
text: '${name}'
|
|
3090
|
+
}
|
|
2829
3091
|
},
|
|
2830
3092
|
{
|
|
2831
3093
|
ul: {
|
|
@@ -2868,17 +3130,17 @@ function generateInteractiveComponent(name) {
|
|
|
2868
3130
|
|
|
2869
3131
|
/**
|
|
2870
3132
|
* ${name} - Interactive component with state management
|
|
2871
|
-
*
|
|
3133
|
+
*
|
|
2872
3134
|
* @param {Object} props - Component properties
|
|
2873
3135
|
* @param {*} props.initialValue - Initial value
|
|
2874
3136
|
* @param {Function} props.onChange - Change callback
|
|
2875
3137
|
* @param {string} props.className - CSS class name
|
|
2876
3138
|
*/
|
|
2877
|
-
export const ${name} = createComponent(({
|
|
2878
|
-
initialValue = '',
|
|
3139
|
+
export const ${name} = createComponent(({
|
|
3140
|
+
initialValue = '',
|
|
2879
3141
|
onChange,
|
|
2880
3142
|
className = '',
|
|
2881
|
-
...props
|
|
3143
|
+
...props
|
|
2882
3144
|
}) => {
|
|
2883
3145
|
// Component state (handled by Coherent.js hydration)
|
|
2884
3146
|
const state = {
|
|
@@ -2955,7 +3217,7 @@ function generateLayoutComponent(name) {
|
|
|
2955
3217
|
|
|
2956
3218
|
/**
|
|
2957
3219
|
* ${name} - Layout component for page structure
|
|
2958
|
-
*
|
|
3220
|
+
*
|
|
2959
3221
|
* @param {Object} props - Component properties
|
|
2960
3222
|
* @param {string} props.title - Page title
|
|
2961
3223
|
* @param {Array|Object} props.children - Child content
|
|
@@ -2963,7 +3225,7 @@ function generateLayoutComponent(name) {
|
|
|
2963
3225
|
* @param {Object} props.footer - Footer content
|
|
2964
3226
|
* @param {string} props.className - CSS class name
|
|
2965
3227
|
*/
|
|
2966
|
-
export const ${name} = createComponent(({
|
|
3228
|
+
export const ${name} = createComponent(({
|
|
2967
3229
|
title = 'Page Title',
|
|
2968
3230
|
children = [],
|
|
2969
3231
|
header = null,
|
|
@@ -3037,38 +3299,39 @@ export const ${name} = createComponent(({
|
|
|
3037
3299
|
`;
|
|
3038
3300
|
}
|
|
3039
3301
|
function generateTestContent(name) {
|
|
3040
|
-
return `import {
|
|
3041
|
-
import assert from 'node:assert';
|
|
3302
|
+
return `import { describe, it, expect } from 'vitest';
|
|
3042
3303
|
import { render } from '@coherent.js/core';
|
|
3043
3304
|
import { ${name} } from './${name}.js';
|
|
3044
3305
|
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
assert(typeof html === 'string');
|
|
3050
|
-
assert(html.length > 0);
|
|
3051
|
-
assert(html.includes('${name.toLowerCase()}'));
|
|
3052
|
-
});
|
|
3306
|
+
describe('${name}', () => {
|
|
3307
|
+
it('renders correctly', () => {
|
|
3308
|
+
const component = ${name}({});
|
|
3309
|
+
const html = render(component);
|
|
3053
3310
|
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
assert(html.includes('test-class'));
|
|
3059
|
-
});
|
|
3311
|
+
expect(typeof html).toBe('string');
|
|
3312
|
+
expect(html.length).toBeGreaterThan(0);
|
|
3313
|
+
expect(html).toContain('${name.toLowerCase()}');
|
|
3314
|
+
});
|
|
3060
3315
|
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
const html = render(component);
|
|
3068
|
-
|
|
3069
|
-
assert(html.includes('Test child content'));
|
|
3316
|
+
it('accepts className prop', () => {
|
|
3317
|
+
const component = ${name}({ className: 'test-class' });
|
|
3318
|
+
const html = render(component);
|
|
3319
|
+
|
|
3320
|
+
expect(html).toContain('test-class');
|
|
3321
|
+
});
|
|
3070
3322
|
});
|
|
3071
|
-
|
|
3323
|
+
|
|
3324
|
+
it('renders children correctly', () => {
|
|
3325
|
+
const children = [
|
|
3326
|
+
{ p: { text: 'Test child content' } }
|
|
3327
|
+
];
|
|
3328
|
+
|
|
3329
|
+
const component = ${name}({ children });
|
|
3330
|
+
const html = render(component);
|
|
3331
|
+
|
|
3332
|
+
expect(html).toContain('Test child content');
|
|
3333
|
+
});
|
|
3334
|
+
});`;
|
|
3072
3335
|
}
|
|
3073
3336
|
function generateStoryContent(name) {
|
|
3074
3337
|
return `import { ${name} } from './${name}.js';
|
|
@@ -3161,7 +3424,7 @@ function generateBasicPage(name) {
|
|
|
3161
3424
|
/**
|
|
3162
3425
|
* ${name} Page Component
|
|
3163
3426
|
* Route: /${routeName}
|
|
3164
|
-
*
|
|
3427
|
+
*
|
|
3165
3428
|
* @param {Object} props - Page properties
|
|
3166
3429
|
* @param {Object} props.params - Route parameters
|
|
3167
3430
|
* @param {Object} props.query - Query parameters
|
|
@@ -3179,17 +3442,17 @@ export const ${name} = createComponent(({ params = {}, query = {}, request, ...p
|
|
|
3179
3442
|
head: {
|
|
3180
3443
|
children: [
|
|
3181
3444
|
{ title: { text: pageTitle } },
|
|
3182
|
-
{
|
|
3183
|
-
meta: {
|
|
3445
|
+
{
|
|
3446
|
+
meta: {
|
|
3184
3447
|
name: 'description',
|
|
3185
3448
|
content: pageDescription
|
|
3186
|
-
}
|
|
3449
|
+
}
|
|
3187
3450
|
},
|
|
3188
|
-
{
|
|
3189
|
-
meta: {
|
|
3190
|
-
name: 'viewport',
|
|
3191
|
-
content: 'width=device-width, initial-scale=1.0'
|
|
3192
|
-
}
|
|
3451
|
+
{
|
|
3452
|
+
meta: {
|
|
3453
|
+
name: 'viewport',
|
|
3454
|
+
content: 'width=device-width, initial-scale=1.0'
|
|
3455
|
+
}
|
|
3193
3456
|
}
|
|
3194
3457
|
]
|
|
3195
3458
|
}
|
|
@@ -3300,10 +3563,10 @@ ${name}.description = '${name} page description';
|
|
|
3300
3563
|
|
|
3301
3564
|
// Usage in router:
|
|
3302
3565
|
// app.get('/${routeName}', (req, res) => {
|
|
3303
|
-
// const html = render(${name}({
|
|
3304
|
-
// params: req.params,
|
|
3566
|
+
// const html = render(${name}({
|
|
3567
|
+
// params: req.params,
|
|
3305
3568
|
// query: req.query,
|
|
3306
|
-
// request: req
|
|
3569
|
+
// request: req
|
|
3307
3570
|
// }));
|
|
3308
3571
|
// res.send(html);
|
|
3309
3572
|
// });
|
|
@@ -3331,11 +3594,11 @@ export const ${name} = createComponent(({ stats = {}, user = null }) => {
|
|
|
3331
3594
|
head: {
|
|
3332
3595
|
children: [
|
|
3333
3596
|
{ title: { text: '${name} Dashboard' } },
|
|
3334
|
-
{
|
|
3335
|
-
meta: {
|
|
3336
|
-
name: 'viewport',
|
|
3337
|
-
content: 'width=device-width, initial-scale=1.0'
|
|
3338
|
-
}
|
|
3597
|
+
{
|
|
3598
|
+
meta: {
|
|
3599
|
+
name: 'viewport',
|
|
3600
|
+
content: 'width=device-width, initial-scale=1.0'
|
|
3601
|
+
}
|
|
3339
3602
|
}
|
|
3340
3603
|
]
|
|
3341
3604
|
}
|
|
@@ -3486,11 +3749,11 @@ export const ${name} = createComponent(({ initialData = {}, errors = {} }) => {
|
|
|
3486
3749
|
head: {
|
|
3487
3750
|
children: [
|
|
3488
3751
|
{ title: { text: '${name} Form' } },
|
|
3489
|
-
{
|
|
3490
|
-
meta: {
|
|
3491
|
-
name: 'viewport',
|
|
3492
|
-
content: 'width=device-width, initial-scale=1.0'
|
|
3493
|
-
}
|
|
3752
|
+
{
|
|
3753
|
+
meta: {
|
|
3754
|
+
name: 'viewport',
|
|
3755
|
+
content: 'width=device-width, initial-scale=1.0'
|
|
3756
|
+
}
|
|
3494
3757
|
}
|
|
3495
3758
|
]
|
|
3496
3759
|
}
|
|
@@ -3630,42 +3893,42 @@ export const ${name} = createComponent(({ initialData = {}, errors = {} }) => {
|
|
|
3630
3893
|
`;
|
|
3631
3894
|
}
|
|
3632
3895
|
function generateTestContent2(name) {
|
|
3633
|
-
return `import {
|
|
3634
|
-
import assert from 'node:assert';
|
|
3896
|
+
return `import { describe, it, expect } from 'vitest';
|
|
3635
3897
|
import { render } from '@coherent.js/core';
|
|
3636
3898
|
import { ${name} } from './${name}.js';
|
|
3637
3899
|
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
assert(typeof html === 'string');
|
|
3643
|
-
assert(html.length > 0);
|
|
3644
|
-
assert(html.includes('<html>'));
|
|
3645
|
-
assert(html.includes('${name}'));
|
|
3646
|
-
});
|
|
3900
|
+
describe('${name} Page', () => {
|
|
3901
|
+
it('renders correctly', () => {
|
|
3902
|
+
const page = ${name}({});
|
|
3903
|
+
const html = render(page);
|
|
3647
3904
|
|
|
3648
|
-
|
|
3905
|
+
expect(typeof html).toBe('string');
|
|
3906
|
+
expect(html.length).toBeGreaterThan(0);
|
|
3907
|
+
expect(html).toContain('<html>');
|
|
3908
|
+
expect(html).toContain('${name}');
|
|
3909
|
+
});
|
|
3910
|
+
|
|
3911
|
+
it('includes proper head elements', () => {
|
|
3649
3912
|
const page = ${name}({});
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
});
|
|
3913
|
+
const html = render(page);
|
|
3914
|
+
|
|
3915
|
+
expect(html).toContain('<title>');
|
|
3916
|
+
expect(html).toContain('<meta');
|
|
3917
|
+
expect(html).toContain('viewport');
|
|
3918
|
+
});
|
|
3919
|
+
|
|
3920
|
+
it('renders with custom props', () => {
|
|
3921
|
+
const props = {
|
|
3922
|
+
params: { id: '123' },
|
|
3923
|
+
query: { search: 'test' }
|
|
3924
|
+
};
|
|
3656
3925
|
|
|
3657
|
-
test('${name} page renders with custom props', () => {
|
|
3658
|
-
const props = {
|
|
3659
|
-
params: { id: '123' },
|
|
3660
|
-
query: { search: 'test' }
|
|
3661
|
-
};
|
|
3662
|
-
|
|
3663
3926
|
const page = ${name}(props);
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
});
|
|
3668
|
-
|
|
3927
|
+
const html = render(page);
|
|
3928
|
+
|
|
3929
|
+
expect(html).toContain('${name}');
|
|
3930
|
+
});
|
|
3931
|
+
});`;
|
|
3669
3932
|
}
|
|
3670
3933
|
function toPascalCase2(str) {
|
|
3671
3934
|
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (_, c) => c.toUpperCase());
|
|
@@ -3722,7 +3985,7 @@ function generateRESTAPI(apiName, originalName) {
|
|
|
3722
3985
|
/**
|
|
3723
3986
|
* ${className} API Routes
|
|
3724
3987
|
* REST API for ${apiName} resources
|
|
3725
|
-
*
|
|
3988
|
+
*
|
|
3726
3989
|
* Base URL: /api/${apiName}
|
|
3727
3990
|
*/
|
|
3728
3991
|
|
|
@@ -3742,14 +4005,14 @@ const sampleData = [
|
|
|
3742
4005
|
const ${apiName}Schema = {
|
|
3743
4006
|
type: 'object',
|
|
3744
4007
|
properties: {
|
|
3745
|
-
name: {
|
|
3746
|
-
type: 'string',
|
|
4008
|
+
name: {
|
|
4009
|
+
type: 'string',
|
|
3747
4010
|
minLength: 1,
|
|
3748
|
-
maxLength: 100
|
|
4011
|
+
maxLength: 100
|
|
3749
4012
|
},
|
|
3750
|
-
description: {
|
|
4013
|
+
description: {
|
|
3751
4014
|
type: 'string',
|
|
3752
|
-
maxLength: 500
|
|
4015
|
+
maxLength: 500
|
|
3753
4016
|
}
|
|
3754
4017
|
},
|
|
3755
4018
|
required: ['name'],
|
|
@@ -3759,14 +4022,14 @@ const ${apiName}Schema = {
|
|
|
3759
4022
|
const ${apiName}UpdateSchema = {
|
|
3760
4023
|
type: 'object',
|
|
3761
4024
|
properties: {
|
|
3762
|
-
name: {
|
|
3763
|
-
type: 'string',
|
|
4025
|
+
name: {
|
|
4026
|
+
type: 'string',
|
|
3764
4027
|
minLength: 1,
|
|
3765
|
-
maxLength: 100
|
|
4028
|
+
maxLength: 100
|
|
3766
4029
|
},
|
|
3767
|
-
description: {
|
|
4030
|
+
description: {
|
|
3768
4031
|
type: 'string',
|
|
3769
|
-
maxLength: 500
|
|
4032
|
+
maxLength: 500
|
|
3770
4033
|
}
|
|
3771
4034
|
},
|
|
3772
4035
|
additionalProperties: false,
|
|
@@ -3781,21 +4044,21 @@ const ${apiName}UpdateSchema = {
|
|
|
3781
4044
|
*/
|
|
3782
4045
|
${camelCaseApiName}API.get('/', (req, res) => {
|
|
3783
4046
|
const { page = 1, limit = 10, search } = req.query;
|
|
3784
|
-
|
|
4047
|
+
|
|
3785
4048
|
let data = [...sampleData];
|
|
3786
|
-
|
|
4049
|
+
|
|
3787
4050
|
// Apply search filter
|
|
3788
4051
|
if (search) {
|
|
3789
|
-
data = data.filter(item =>
|
|
4052
|
+
data = data.filter(item =>
|
|
3790
4053
|
item.name.toLowerCase().includes(search.toLowerCase())
|
|
3791
4054
|
);
|
|
3792
4055
|
}
|
|
3793
|
-
|
|
4056
|
+
|
|
3794
4057
|
// Apply pagination
|
|
3795
4058
|
const startIndex = (page - 1) * limit;
|
|
3796
4059
|
const endIndex = startIndex + parseInt(limit);
|
|
3797
4060
|
const paginatedData = data.slice(startIndex, endIndex);
|
|
3798
|
-
|
|
4061
|
+
|
|
3799
4062
|
return {
|
|
3800
4063
|
data: paginatedData,
|
|
3801
4064
|
pagination: {
|
|
@@ -3814,14 +4077,14 @@ ${camelCaseApiName}API.get('/', (req, res) => {
|
|
|
3814
4077
|
${camelCaseApiName}API.get('/:id', (req, res) => {
|
|
3815
4078
|
const { id } = req.params;
|
|
3816
4079
|
const item = sampleData.find(item => item.id === id);
|
|
3817
|
-
|
|
4080
|
+
|
|
3818
4081
|
if (!item) {
|
|
3819
4082
|
return res.status(404).json({
|
|
3820
4083
|
_error: '${className} not found',
|
|
3821
4084
|
code: 'NOT_FOUND'
|
|
3822
4085
|
});
|
|
3823
4086
|
}
|
|
3824
|
-
|
|
4087
|
+
|
|
3825
4088
|
return { data: item };
|
|
3826
4089
|
});
|
|
3827
4090
|
|
|
@@ -3829,11 +4092,11 @@ ${camelCaseApiName}API.get('/:id', (req, res) => {
|
|
|
3829
4092
|
* POST /${apiName}
|
|
3830
4093
|
* Create a new ${apiName} item
|
|
3831
4094
|
*/
|
|
3832
|
-
${camelCaseApiName}API.post('/',
|
|
4095
|
+
${camelCaseApiName}API.post('/',
|
|
3833
4096
|
withValidation(${apiName}Schema),
|
|
3834
4097
|
(req, res) => {
|
|
3835
4098
|
const { name, description } = req.body;
|
|
3836
|
-
|
|
4099
|
+
|
|
3837
4100
|
const newItem = {
|
|
3838
4101
|
id: String(Date.now()),
|
|
3839
4102
|
name,
|
|
@@ -3841,9 +4104,9 @@ ${camelCaseApiName}API.post('/',
|
|
|
3841
4104
|
createdAt: new Date().toISOString(),
|
|
3842
4105
|
updatedAt: new Date().toISOString()
|
|
3843
4106
|
};
|
|
3844
|
-
|
|
4107
|
+
|
|
3845
4108
|
sampleData.push(newItem);
|
|
3846
|
-
|
|
4109
|
+
|
|
3847
4110
|
return res.status(201).json({
|
|
3848
4111
|
data: newItem,
|
|
3849
4112
|
message: '${className} created successfully'
|
|
@@ -3860,22 +4123,22 @@ ${camelCaseApiName}API.put('/:id',
|
|
|
3860
4123
|
(req, res) => {
|
|
3861
4124
|
const { id } = req.params;
|
|
3862
4125
|
const itemIndex = sampleData.findIndex(item => item.id === id);
|
|
3863
|
-
|
|
4126
|
+
|
|
3864
4127
|
if (itemIndex === -1) {
|
|
3865
4128
|
return res.status(404).json({
|
|
3866
4129
|
_error: '${className} not found',
|
|
3867
4130
|
code: 'NOT_FOUND'
|
|
3868
4131
|
});
|
|
3869
4132
|
}
|
|
3870
|
-
|
|
4133
|
+
|
|
3871
4134
|
const updatedItem = {
|
|
3872
4135
|
...sampleData[itemIndex],
|
|
3873
4136
|
...req.body,
|
|
3874
4137
|
updatedAt: new Date().toISOString()
|
|
3875
4138
|
};
|
|
3876
|
-
|
|
4139
|
+
|
|
3877
4140
|
sampleData[itemIndex] = updatedItem;
|
|
3878
|
-
|
|
4141
|
+
|
|
3879
4142
|
return {
|
|
3880
4143
|
data: updatedItem,
|
|
3881
4144
|
message: '${className} updated successfully'
|
|
@@ -3890,16 +4153,16 @@ ${camelCaseApiName}API.put('/:id',
|
|
|
3890
4153
|
${camelCaseApiName}API.delete('/:id', (req, res) => {
|
|
3891
4154
|
const { id } = req.params;
|
|
3892
4155
|
const itemIndex = sampleData.findIndex(item => item.id === id);
|
|
3893
|
-
|
|
4156
|
+
|
|
3894
4157
|
if (itemIndex === -1) {
|
|
3895
4158
|
return res.status(404).json({
|
|
3896
4159
|
_error: '${className} not found',
|
|
3897
4160
|
code: 'NOT_FOUND'
|
|
3898
4161
|
});
|
|
3899
4162
|
}
|
|
3900
|
-
|
|
4163
|
+
|
|
3901
4164
|
const deletedItem = sampleData.splice(itemIndex, 1)[0];
|
|
3902
|
-
|
|
4165
|
+
|
|
3903
4166
|
return {
|
|
3904
4167
|
data: deletedItem,
|
|
3905
4168
|
message: '${className} deleted successfully'
|
|
@@ -3920,7 +4183,7 @@ export default ${camelCaseApiName}API;
|
|
|
3920
4183
|
// Usage example:
|
|
3921
4184
|
// import express from 'express';
|
|
3922
4185
|
// import ${camelCaseApiName}API from './api/${apiName}.js';
|
|
3923
|
-
//
|
|
4186
|
+
//
|
|
3924
4187
|
// const app = express();
|
|
3925
4188
|
// app.use(express.json());
|
|
3926
4189
|
// app.use('/api', ${camelCaseApiName}API.toExpress());
|
|
@@ -3941,7 +4204,7 @@ function generateRPCAPI(apiName, originalName) {
|
|
|
3941
4204
|
/**
|
|
3942
4205
|
* ${className} RPC API
|
|
3943
4206
|
* Remote Procedure Call API for ${apiName}
|
|
3944
|
-
*
|
|
4207
|
+
*
|
|
3945
4208
|
* Base URL: /rpc/${apiName}
|
|
3946
4209
|
*/
|
|
3947
4210
|
|
|
@@ -3964,10 +4227,10 @@ sampleData.set('2', { id: '2', name: 'Sample ${className} 2', createdAt: new Dat
|
|
|
3964
4227
|
${camelCaseApiName}RPC.post('/list', (req, res) => {
|
|
3965
4228
|
const { params = {} } = req.body;
|
|
3966
4229
|
const { limit = 10, offset = 0 } = params;
|
|
3967
|
-
|
|
4230
|
+
|
|
3968
4231
|
const items = Array.from(sampleData.values())
|
|
3969
4232
|
.slice(offset, offset + limit);
|
|
3970
|
-
|
|
4233
|
+
|
|
3971
4234
|
return {
|
|
3972
4235
|
jsonrpc: '2.0',
|
|
3973
4236
|
result: {
|
|
@@ -3982,7 +4245,7 @@ ${camelCaseApiName}RPC.post('/list', (req, res) => {
|
|
|
3982
4245
|
* RPC Method: ${apiName}.get
|
|
3983
4246
|
* Get a specific ${apiName} item
|
|
3984
4247
|
*/
|
|
3985
|
-
${camelCaseApiName}RPC.post('/get',
|
|
4248
|
+
${camelCaseApiName}RPC.post('/get',
|
|
3986
4249
|
withValidation({
|
|
3987
4250
|
type: 'object',
|
|
3988
4251
|
properties: {
|
|
@@ -3999,7 +4262,7 @@ ${camelCaseApiName}RPC.post('/get',
|
|
|
3999
4262
|
(req, res) => {
|
|
4000
4263
|
const { params } = req.body;
|
|
4001
4264
|
const item = sampleData.get(params.id);
|
|
4002
|
-
|
|
4265
|
+
|
|
4003
4266
|
if (!item) {
|
|
4004
4267
|
return {
|
|
4005
4268
|
jsonrpc: '2.0',
|
|
@@ -4010,7 +4273,7 @@ ${camelCaseApiName}RPC.post('/get',
|
|
|
4010
4273
|
id: req.body.id
|
|
4011
4274
|
};
|
|
4012
4275
|
}
|
|
4013
|
-
|
|
4276
|
+
|
|
4014
4277
|
return {
|
|
4015
4278
|
jsonrpc: '2.0',
|
|
4016
4279
|
result: item,
|
|
@@ -4041,16 +4304,16 @@ ${camelCaseApiName}RPC.post('/create',
|
|
|
4041
4304
|
(req, res) => {
|
|
4042
4305
|
const { params } = req.body;
|
|
4043
4306
|
const id = String(Date.now());
|
|
4044
|
-
|
|
4307
|
+
|
|
4045
4308
|
const newItem = {
|
|
4046
4309
|
id,
|
|
4047
4310
|
...params,
|
|
4048
4311
|
createdAt: new Date(),
|
|
4049
4312
|
updatedAt: new Date()
|
|
4050
4313
|
};
|
|
4051
|
-
|
|
4314
|
+
|
|
4052
4315
|
sampleData.set(id, newItem);
|
|
4053
|
-
|
|
4316
|
+
|
|
4054
4317
|
return {
|
|
4055
4318
|
jsonrpc: '2.0',
|
|
4056
4319
|
result: newItem,
|
|
@@ -4082,7 +4345,7 @@ ${camelCaseApiName}RPC.post('/update',
|
|
|
4082
4345
|
(req, res) => {
|
|
4083
4346
|
const { params } = req.body;
|
|
4084
4347
|
const existing = sampleData.get(params.id);
|
|
4085
|
-
|
|
4348
|
+
|
|
4086
4349
|
if (!existing) {
|
|
4087
4350
|
return {
|
|
4088
4351
|
jsonrpc: '2.0',
|
|
@@ -4093,15 +4356,15 @@ ${camelCaseApiName}RPC.post('/update',
|
|
|
4093
4356
|
id: req.body.id
|
|
4094
4357
|
};
|
|
4095
4358
|
}
|
|
4096
|
-
|
|
4359
|
+
|
|
4097
4360
|
const updated = {
|
|
4098
4361
|
...existing,
|
|
4099
4362
|
...params,
|
|
4100
4363
|
updatedAt: new Date()
|
|
4101
4364
|
};
|
|
4102
|
-
|
|
4365
|
+
|
|
4103
4366
|
sampleData.set(params.id, updated);
|
|
4104
|
-
|
|
4367
|
+
|
|
4105
4368
|
return {
|
|
4106
4369
|
jsonrpc: '2.0',
|
|
4107
4370
|
result: updated,
|
|
@@ -4131,7 +4394,7 @@ ${camelCaseApiName}RPC.post('/delete',
|
|
|
4131
4394
|
(req, res) => {
|
|
4132
4395
|
const { params } = req.body;
|
|
4133
4396
|
const item = sampleData.get(params.id);
|
|
4134
|
-
|
|
4397
|
+
|
|
4135
4398
|
if (!item) {
|
|
4136
4399
|
return {
|
|
4137
4400
|
jsonrpc: '2.0',
|
|
@@ -4142,9 +4405,9 @@ ${camelCaseApiName}RPC.post('/delete',
|
|
|
4142
4405
|
id: req.body.id
|
|
4143
4406
|
};
|
|
4144
4407
|
}
|
|
4145
|
-
|
|
4408
|
+
|
|
4146
4409
|
sampleData.delete(params.id);
|
|
4147
|
-
|
|
4410
|
+
|
|
4148
4411
|
return {
|
|
4149
4412
|
jsonrpc: '2.0',
|
|
4150
4413
|
result: { success: true, deleted: item },
|
|
@@ -4158,17 +4421,17 @@ export default ${camelCaseApiName}RPC;
|
|
|
4158
4421
|
}
|
|
4159
4422
|
function generateTestContent3(apiName, originalName) {
|
|
4160
4423
|
const className = toPascalCase3(originalName);
|
|
4161
|
-
return `import {
|
|
4162
|
-
import assert from 'node:assert';
|
|
4424
|
+
return `import { describe, it, expect } from 'vitest';
|
|
4163
4425
|
import ${apiName}API from './${apiName}.js';
|
|
4164
4426
|
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4427
|
+
describe('${className} API', () => {
|
|
4428
|
+
it('should be defined', () => {
|
|
4429
|
+
expect(typeof ${apiName}API).toBe('object');
|
|
4430
|
+
expect(typeof ${apiName}API.get).toBe('function');
|
|
4431
|
+
expect(typeof ${apiName}API.post).toBe('function');
|
|
4169
4432
|
});
|
|
4170
4433
|
|
|
4171
|
-
|
|
4434
|
+
it('should handle GET requests', async () => {
|
|
4172
4435
|
const mockReq = {
|
|
4173
4436
|
query: {}
|
|
4174
4437
|
};
|
|
@@ -4176,15 +4439,15 @@ test('${className} API should handle GET requests', async () => {
|
|
|
4176
4439
|
status: (code) => mockRes,
|
|
4177
4440
|
json: (data) => data
|
|
4178
4441
|
};
|
|
4179
|
-
|
|
4442
|
+
|
|
4180
4443
|
// This is a basic test structure
|
|
4181
4444
|
// In a real test, you'd use a testing framework like supertest
|
|
4182
|
-
|
|
4445
|
+
expect(true).toBe(true); // Placeholder
|
|
4183
4446
|
});
|
|
4184
4447
|
|
|
4185
4448
|
// Add more specific tests for your API endpoints
|
|
4186
4449
|
// Example:
|
|
4187
|
-
//
|
|
4450
|
+
// it('POST /${apiName} should create new item', async () => {
|
|
4188
4451
|
// // Test implementation
|
|
4189
4452
|
// });
|
|
4190
4453
|
//
|
|
@@ -4753,7 +5016,7 @@ function expand_(str, isTop) {
|
|
|
4753
5016
|
return expansions;
|
|
4754
5017
|
}
|
|
4755
5018
|
|
|
4756
|
-
// ../../node_modules/.pnpm/minimatch@10.
|
|
5019
|
+
// ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/assert-valid-pattern.js
|
|
4757
5020
|
var MAX_PATTERN_LENGTH = 1024 * 64;
|
|
4758
5021
|
var assertValidPattern = (pattern) => {
|
|
4759
5022
|
if (typeof pattern !== "string") {
|
|
@@ -4764,7 +5027,7 @@ var assertValidPattern = (pattern) => {
|
|
|
4764
5027
|
}
|
|
4765
5028
|
};
|
|
4766
5029
|
|
|
4767
|
-
// ../../node_modules/.pnpm/minimatch@10.
|
|
5030
|
+
// ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/brace-expressions.js
|
|
4768
5031
|
var posixClasses = {
|
|
4769
5032
|
"[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true],
|
|
4770
5033
|
"[:alpha:]": ["\\p{L}\\p{Nl}", true],
|
|
@@ -4873,12 +5136,15 @@ var parseClass = (glob2, position) => {
|
|
|
4873
5136
|
return [comb, uflag, endPos - pos, true];
|
|
4874
5137
|
};
|
|
4875
5138
|
|
|
4876
|
-
// ../../node_modules/.pnpm/minimatch@10.
|
|
4877
|
-
var unescape = (s, { windowsPathsNoEscape = false } = {}) => {
|
|
4878
|
-
|
|
5139
|
+
// ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/unescape.js
|
|
5140
|
+
var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
|
|
5141
|
+
if (magicalBraces) {
|
|
5142
|
+
return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
|
|
5143
|
+
}
|
|
5144
|
+
return windowsPathsNoEscape ? s.replace(/\[([^\/\\{}])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\{}])\]/g, "$1$2").replace(/\\([^\/{}])/g, "$1");
|
|
4879
5145
|
};
|
|
4880
5146
|
|
|
4881
|
-
// ../../node_modules/.pnpm/minimatch@10.
|
|
5147
|
+
// ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/ast.js
|
|
4882
5148
|
var types = /* @__PURE__ */ new Set(["!", "?", "+", "*", "@"]);
|
|
4883
5149
|
var isExtglobType = (c) => types.has(c);
|
|
4884
5150
|
var startNoTraversal = "(?!(?:^|/)\\.\\.?(?:$|/))";
|
|
@@ -5229,7 +5495,7 @@ var AST = class _AST {
|
|
|
5229
5495
|
if (this.#root === this)
|
|
5230
5496
|
this.#fillNegs();
|
|
5231
5497
|
if (!this.type) {
|
|
5232
|
-
const noEmpty = this.isStart() && this.isEnd();
|
|
5498
|
+
const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
|
|
5233
5499
|
const src = this.#parts.map((p) => {
|
|
5234
5500
|
const [re, _, hasMagic2, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
|
|
5235
5501
|
this.#hasMagic = this.#hasMagic || hasMagic2;
|
|
@@ -5339,10 +5605,7 @@ var AST = class _AST {
|
|
|
5339
5605
|
}
|
|
5340
5606
|
}
|
|
5341
5607
|
if (c === "*") {
|
|
5342
|
-
|
|
5343
|
-
re += starNoEmpty;
|
|
5344
|
-
else
|
|
5345
|
-
re += star;
|
|
5608
|
+
re += noEmpty && glob2 === "*" ? starNoEmpty : star;
|
|
5346
5609
|
hasMagic2 = true;
|
|
5347
5610
|
continue;
|
|
5348
5611
|
}
|
|
@@ -5357,12 +5620,15 @@ var AST = class _AST {
|
|
|
5357
5620
|
}
|
|
5358
5621
|
};
|
|
5359
5622
|
|
|
5360
|
-
// ../../node_modules/.pnpm/minimatch@10.
|
|
5361
|
-
var escape = (s, { windowsPathsNoEscape = false } = {}) => {
|
|
5623
|
+
// ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/escape.js
|
|
5624
|
+
var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
|
|
5625
|
+
if (magicalBraces) {
|
|
5626
|
+
return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
|
|
5627
|
+
}
|
|
5362
5628
|
return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&");
|
|
5363
5629
|
};
|
|
5364
5630
|
|
|
5365
|
-
// ../../node_modules/.pnpm/minimatch@10.
|
|
5631
|
+
// ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/index.js
|
|
5366
5632
|
var minimatch = (p, pattern, options = {}) => {
|
|
5367
5633
|
assertValidPattern(pattern);
|
|
5368
5634
|
if (!options.nocomment && pattern.charAt(0) === "#") {
|
|
@@ -5999,16 +6265,27 @@ var Minimatch = class {
|
|
|
5999
6265
|
pp[i] = twoStar;
|
|
6000
6266
|
}
|
|
6001
6267
|
} else if (next === void 0) {
|
|
6002
|
-
pp[i - 1] = prev + "(
|
|
6268
|
+
pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + ")?";
|
|
6003
6269
|
} else if (next !== GLOBSTAR) {
|
|
6004
6270
|
pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + "\\/)" + next;
|
|
6005
6271
|
pp[i + 1] = GLOBSTAR;
|
|
6006
6272
|
}
|
|
6007
6273
|
});
|
|
6008
|
-
|
|
6274
|
+
const filtered = pp.filter((p) => p !== GLOBSTAR);
|
|
6275
|
+
if (this.partial && filtered.length >= 1) {
|
|
6276
|
+
const prefixes = [];
|
|
6277
|
+
for (let i = 1; i <= filtered.length; i++) {
|
|
6278
|
+
prefixes.push(filtered.slice(0, i).join("/"));
|
|
6279
|
+
}
|
|
6280
|
+
return "(?:" + prefixes.join("|") + ")";
|
|
6281
|
+
}
|
|
6282
|
+
return filtered.join("/");
|
|
6009
6283
|
}).join("|");
|
|
6010
6284
|
const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
|
|
6011
6285
|
re = "^" + open + re + close + "$";
|
|
6286
|
+
if (this.partial) {
|
|
6287
|
+
re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
|
|
6288
|
+
}
|
|
6012
6289
|
if (this.negate)
|
|
6013
6290
|
re = "^(?!" + re + ").+$";
|
|
6014
6291
|
try {
|
|
@@ -6080,10 +6357,10 @@ minimatch.Minimatch = Minimatch;
|
|
|
6080
6357
|
minimatch.escape = escape;
|
|
6081
6358
|
minimatch.unescape = unescape;
|
|
6082
6359
|
|
|
6083
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
6360
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/glob.js
|
|
6084
6361
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6085
6362
|
|
|
6086
|
-
// ../../node_modules/.pnpm/lru-cache@11.2.
|
|
6363
|
+
// ../../node_modules/.pnpm/lru-cache@11.2.2/node_modules/lru-cache/dist/esm/index.js
|
|
6087
6364
|
var defaultPerf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
|
|
6088
6365
|
var warned = /* @__PURE__ */ new Set();
|
|
6089
6366
|
var PROCESS = typeof process === "object" && !!process ? process : {};
|
|
@@ -7129,7 +7406,8 @@ var LRUCache = class _LRUCache {
|
|
|
7129
7406
|
return fetchFail(ac.signal.reason);
|
|
7130
7407
|
}
|
|
7131
7408
|
const bf2 = p;
|
|
7132
|
-
|
|
7409
|
+
const vl = this.#valList[index];
|
|
7410
|
+
if (vl === p || ignoreAbort && updateCache && vl === void 0) {
|
|
7133
7411
|
if (v2 === void 0) {
|
|
7134
7412
|
if (bf2.__staleWhileFetching !== void 0) {
|
|
7135
7413
|
this.#valList[index] = bf2.__staleWhileFetching;
|
|
@@ -7483,7 +7761,7 @@ var LRUCache = class _LRUCache {
|
|
|
7483
7761
|
}
|
|
7484
7762
|
};
|
|
7485
7763
|
|
|
7486
|
-
// ../../node_modules/.pnpm/path-scurry@2.0.
|
|
7764
|
+
// ../../node_modules/.pnpm/path-scurry@2.0.1/node_modules/path-scurry/dist/esm/index.js
|
|
7487
7765
|
import { posix, win32 } from "node:path";
|
|
7488
7766
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7489
7767
|
import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps } from "fs";
|
|
@@ -8368,7 +8646,7 @@ var Minipass = class extends EventEmitter {
|
|
|
8368
8646
|
}
|
|
8369
8647
|
};
|
|
8370
8648
|
|
|
8371
|
-
// ../../node_modules/.pnpm/path-scurry@2.0.
|
|
8649
|
+
// ../../node_modules/.pnpm/path-scurry@2.0.1/node_modules/path-scurry/dist/esm/index.js
|
|
8372
8650
|
var realpathSync = rps.native;
|
|
8373
8651
|
var defaultFS = {
|
|
8374
8652
|
lstatSync,
|
|
@@ -8413,7 +8691,7 @@ var ENOREALPATH = 512;
|
|
|
8413
8691
|
var ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
|
|
8414
8692
|
var TYPEMASK = 1023;
|
|
8415
8693
|
var entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
|
|
8416
|
-
var normalizeCache =
|
|
8694
|
+
var normalizeCache = new LRUCache({ max: 2 ** 12 });
|
|
8417
8695
|
var normalize = (s) => {
|
|
8418
8696
|
const c = normalizeCache.get(s);
|
|
8419
8697
|
if (c)
|
|
@@ -8422,7 +8700,7 @@ var normalize = (s) => {
|
|
|
8422
8700
|
normalizeCache.set(s, n);
|
|
8423
8701
|
return n;
|
|
8424
8702
|
};
|
|
8425
|
-
var normalizeNocaseCache =
|
|
8703
|
+
var normalizeNocaseCache = new LRUCache({ max: 2 ** 12 });
|
|
8426
8704
|
var normalizeNocase = (s) => {
|
|
8427
8705
|
const c = normalizeNocaseCache.get(s);
|
|
8428
8706
|
if (c)
|
|
@@ -8579,6 +8857,7 @@ var PathBase = class {
|
|
|
8579
8857
|
get parentPath() {
|
|
8580
8858
|
return (this.parent || this).fullpath();
|
|
8581
8859
|
}
|
|
8860
|
+
/* c8 ignore start */
|
|
8582
8861
|
/**
|
|
8583
8862
|
* Deprecated alias for Dirent['parentPath'] Somewhat counterintuitively,
|
|
8584
8863
|
* this property refers to the *parent* path, not the path object itself.
|
|
@@ -8588,6 +8867,7 @@ var PathBase = class {
|
|
|
8588
8867
|
get path() {
|
|
8589
8868
|
return this.parentPath;
|
|
8590
8869
|
}
|
|
8870
|
+
/* c8 ignore stop */
|
|
8591
8871
|
/**
|
|
8592
8872
|
* Do not create new Path objects directly. They should always be accessed
|
|
8593
8873
|
* via the PathScurry class or other methods on the Path class.
|
|
@@ -10094,7 +10374,7 @@ var PathScurryDarwin = class extends PathScurryPosix {
|
|
|
10094
10374
|
var Path = process.platform === "win32" ? PathWin32 : PathPosix;
|
|
10095
10375
|
var PathScurry = process.platform === "win32" ? PathScurryWin32 : process.platform === "darwin" ? PathScurryDarwin : PathScurryPosix;
|
|
10096
10376
|
|
|
10097
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
10377
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/pattern.js
|
|
10098
10378
|
var isPatternList = (pl) => pl.length >= 1;
|
|
10099
10379
|
var isGlobList = (gl) => gl.length >= 1;
|
|
10100
10380
|
var Pattern = class _Pattern {
|
|
@@ -10259,7 +10539,7 @@ var Pattern = class _Pattern {
|
|
|
10259
10539
|
}
|
|
10260
10540
|
};
|
|
10261
10541
|
|
|
10262
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
10542
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/ignore.js
|
|
10263
10543
|
var defaultPlatform2 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
|
|
10264
10544
|
var Ignore = class {
|
|
10265
10545
|
relative;
|
|
@@ -10346,7 +10626,7 @@ var Ignore = class {
|
|
|
10346
10626
|
}
|
|
10347
10627
|
};
|
|
10348
10628
|
|
|
10349
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
10629
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/processor.js
|
|
10350
10630
|
var HasWalkedCache = class _HasWalkedCache {
|
|
10351
10631
|
store;
|
|
10352
10632
|
constructor(store = /* @__PURE__ */ new Map()) {
|
|
@@ -10567,7 +10847,7 @@ var Processor = class _Processor {
|
|
|
10567
10847
|
}
|
|
10568
10848
|
};
|
|
10569
10849
|
|
|
10570
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
10850
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/walker.js
|
|
10571
10851
|
var makeIgnore = (ignore, opts) => typeof ignore === "string" ? new Ignore([ignore], opts) : Array.isArray(ignore) ? new Ignore(ignore, opts) : ignore;
|
|
10572
10852
|
var GlobUtil = class {
|
|
10573
10853
|
path;
|
|
@@ -10894,7 +11174,7 @@ var GlobStream = class extends GlobUtil {
|
|
|
10894
11174
|
}
|
|
10895
11175
|
};
|
|
10896
11176
|
|
|
10897
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
11177
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/glob.js
|
|
10898
11178
|
var defaultPlatform3 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
|
|
10899
11179
|
var Glob = class {
|
|
10900
11180
|
absolute;
|
|
@@ -11094,7 +11374,7 @@ var Glob = class {
|
|
|
11094
11374
|
}
|
|
11095
11375
|
};
|
|
11096
11376
|
|
|
11097
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
11377
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/has-magic.js
|
|
11098
11378
|
var hasMagic = (pattern, options = {}) => {
|
|
11099
11379
|
if (!Array.isArray(pattern)) {
|
|
11100
11380
|
pattern = [pattern];
|
|
@@ -11106,7 +11386,7 @@ var hasMagic = (pattern, options = {}) => {
|
|
|
11106
11386
|
return false;
|
|
11107
11387
|
};
|
|
11108
11388
|
|
|
11109
|
-
// ../../node_modules/.pnpm/glob@11.0
|
|
11389
|
+
// ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/index.js
|
|
11110
11390
|
function globStreamSync(pattern, options = {}) {
|
|
11111
11391
|
return new Glob(pattern, options).streamSync();
|
|
11112
11392
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coherent.js/cli",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.5",
|
|
4
4
|
"description": "Command-line interface for Coherent.js projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"commander": "^12.1.0",
|
|
32
|
-
"glob": "11.0
|
|
32
|
+
"glob": "11.1.0",
|
|
33
33
|
"ora": "^8.1.0",
|
|
34
34
|
"picocolors": "^1.1.1",
|
|
35
35
|
"prompts": "^2.4.2"
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"access": "public",
|
|
44
44
|
"registry": "https://registry.npmjs.org/"
|
|
45
45
|
},
|
|
46
|
+
"sideEffects": false,
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "node build.mjs",
|
|
48
49
|
"clean": "rm -rf dist/",
|