clovie 0.1.4 → 0.1.6
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 +241 -107
- package/bin/cli.js +100 -11
- package/config/clovie.config.js +18 -1
- package/dist/index.cjs +4600 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +229 -0
- package/dist/index.js +4614 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -11
- package/lib/core/bundler.js +0 -31
- package/lib/core/cache.js +0 -69
- package/lib/core/discover.js +0 -86
- package/lib/core/getAssets.js +0 -38
- package/lib/core/getData.js +0 -19
- package/lib/core/getPartials.js +0 -59
- package/lib/core/getStyles.js +0 -16
- package/lib/core/getViews.js +0 -195
- package/lib/core/render.js +0 -67
- package/lib/core/server.js +0 -104
- package/lib/core/watcher.js +0 -283
- package/lib/core/write.js +0 -24
- package/lib/main.js +0 -284
- package/lib/utils/clean.js +0 -21
- package/lib/utils/create.js +0 -31
- package/lib/utils/readFilesToMap.js +0 -24
package/README.md
CHANGED
|
@@ -1,40 +1,105 @@
|
|
|
1
1
|
# Clovie - Vintage Web Dev Tooling
|
|
2
2
|
|
|
3
|
-
A Node.js-based static site generator designed to be simple, fast, and highly modular.
|
|
3
|
+
A Node.js-based static site generator and web framework designed to be simple, fast, and highly modular. Built on the **brickworks/engine** pattern for maximum flexibility and maintainability.
|
|
4
|
+
|
|
5
|
+
## Architecture Overview
|
|
6
|
+
|
|
7
|
+
Clovie uses a **service-oriented architecture** where all functionality is provided by services that extend `ServiceProvider` from the brickworks/engine framework. The engine orchestrates these services through dependency injection and provides stable state management.
|
|
8
|
+
|
|
9
|
+
### Core Services
|
|
10
|
+
|
|
11
|
+
- **File** - File system operations (reading, writing, watching)
|
|
12
|
+
- **Compiler** - Template compilation with live reload injection
|
|
13
|
+
- **Views** - View discovery and processing
|
|
14
|
+
- **Routes** - Route generation and processing (static & dynamic)
|
|
15
|
+
- **Build** - Static site generation orchestration
|
|
16
|
+
- **Server** - Express server management (conditionally loaded)
|
|
17
|
+
- **Cache** - Build caching and incremental builds
|
|
18
|
+
|
|
19
|
+
### Two Operating Modes
|
|
20
|
+
|
|
21
|
+
**Static Mode (`type: 'static'`)**:
|
|
22
|
+
- Generates static HTML files to output directory
|
|
23
|
+
- Uses Express server only for development (live reload, file serving)
|
|
24
|
+
- Traditional static site generator behavior
|
|
25
|
+
|
|
26
|
+
**Server Mode (`type: 'server'`)**:
|
|
27
|
+
- Builds a real Express application
|
|
28
|
+
- Serves static files + handles dynamic routes/API endpoints
|
|
29
|
+
- Full web application server with server-side rendering
|
|
4
30
|
|
|
5
31
|
## Project Structure
|
|
6
32
|
|
|
7
33
|
```
|
|
8
|
-
|
|
9
|
-
├── __tests__/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
├──
|
|
16
|
-
│ ├──
|
|
17
|
-
│
|
|
18
|
-
│
|
|
19
|
-
│
|
|
20
|
-
│
|
|
21
|
-
│
|
|
22
|
-
│
|
|
23
|
-
│
|
|
24
|
-
│
|
|
25
|
-
│
|
|
26
|
-
│
|
|
27
|
-
|
|
28
|
-
└──
|
|
34
|
+
clovie/
|
|
35
|
+
├── __tests__/ # Test files
|
|
36
|
+
├── bin/ # CLI executable
|
|
37
|
+
│ └── cli.js # Command line interface
|
|
38
|
+
├── config/ # Configuration files
|
|
39
|
+
│ └── clovie.config.js # Default configuration
|
|
40
|
+
├── lib/ # Source code - Service-based architecture
|
|
41
|
+
│ ├── createClovie.js # Engine factory function
|
|
42
|
+
│ ├── Build.js # Build service
|
|
43
|
+
│ ├── Cache.js # Caching service
|
|
44
|
+
│ ├── Compiler.js # Template compilation service
|
|
45
|
+
│ ├── File.js # File system service
|
|
46
|
+
│ ├── Routes.js # Routing service
|
|
47
|
+
│ ├── Server.js # Express server service
|
|
48
|
+
│ ├── Views.js # View processing service
|
|
49
|
+
│ └── utils/ # Utility functions
|
|
50
|
+
│ ├── clean.js # Directory cleaning
|
|
51
|
+
│ ├── discover.js # Auto-discovery
|
|
52
|
+
│ └── liveReloadScript.js # Live reload
|
|
53
|
+
├── templates/ # Project templates
|
|
54
|
+
└── examples/ # Configuration examples
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Service Architecture
|
|
58
|
+
|
|
59
|
+
Each service in Clovie extends `ServiceProvider` and defines:
|
|
60
|
+
|
|
61
|
+
- **Static manifest**: Name, namespace, version, and dependencies
|
|
62
|
+
- **initialize()**: Setup phase with access to config and context
|
|
63
|
+
- **actions()**: Methods exposed through the engine context
|
|
64
|
+
|
|
65
|
+
### Service Dependencies
|
|
66
|
+
|
|
67
|
+
Services declare their dependencies in their manifest:
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
static manifest = {
|
|
71
|
+
name: 'Clovie Build',
|
|
72
|
+
namespace: 'build',
|
|
73
|
+
version: '1.0.0',
|
|
74
|
+
dependencies: [Cache, Routes] // Initialized first
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### State Management
|
|
79
|
+
|
|
80
|
+
The engine provides two state stores:
|
|
81
|
+
|
|
82
|
+
- **`state`**: Reactive store for build-time data (from config.data)
|
|
83
|
+
- **`stable`**: Persistent storage (cache, build stats, etc.)
|
|
84
|
+
|
|
85
|
+
Services access these via `useContext()`:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
actions(useContext) {
|
|
89
|
+
const { state, stable, file, compiler } = useContext();
|
|
90
|
+
// Service methods can access other services and state
|
|
91
|
+
}
|
|
29
92
|
```
|
|
30
93
|
|
|
31
94
|
## Core Features
|
|
32
95
|
|
|
33
96
|
- **Template Engine Agnostic**: Support for Handlebars, Nunjucks, Pug, Mustache, or custom engines
|
|
34
|
-
- **Asset Processing**: JavaScript bundling with esbuild, SCSS compilation, static asset copying
|
|
35
|
-
- **Development Server**: Live reload with
|
|
36
|
-
- **
|
|
37
|
-
- **
|
|
97
|
+
- **Asset Processing**: JavaScript bundling with esbuild, SCSS compilation, static asset copying
|
|
98
|
+
- **Development Server**: Live reload with Express and file watching
|
|
99
|
+
- **Dynamic Routing**: Powerful route system for both static and server-side page generation
|
|
100
|
+
- **Server-Side Rendering**: Full Express applications with dynamic routes and API endpoints
|
|
101
|
+
- **Incremental Builds**: Smart caching for faster rebuilds
|
|
102
|
+
- **Auto-Discovery**: Intelligent project structure detection
|
|
38
103
|
|
|
39
104
|
## Usage
|
|
40
105
|
|
|
@@ -71,18 +136,27 @@ clovie create my-site
|
|
|
71
136
|
|
|
72
137
|
### Building and Development
|
|
73
138
|
|
|
139
|
+
#### Static Mode (Default)
|
|
74
140
|
```bash
|
|
75
|
-
# Build
|
|
141
|
+
# Build static files
|
|
76
142
|
clovie build
|
|
77
143
|
# or
|
|
78
144
|
npm run build
|
|
79
145
|
|
|
80
|
-
#
|
|
146
|
+
# Development server with live reload
|
|
81
147
|
clovie watch
|
|
82
148
|
# or
|
|
83
149
|
npm run dev
|
|
84
150
|
```
|
|
85
151
|
|
|
152
|
+
#### Server Mode
|
|
153
|
+
```bash
|
|
154
|
+
# Run as Express server application
|
|
155
|
+
clovie server
|
|
156
|
+
# or add to package.json:
|
|
157
|
+
# "scripts": { "serve": "clovie server" }
|
|
158
|
+
```
|
|
159
|
+
|
|
86
160
|
## Configuration
|
|
87
161
|
|
|
88
162
|
### Minimal Configuration (Recommended)
|
|
@@ -103,28 +177,87 @@ Clovie will automatically detect:
|
|
|
103
177
|
- `styles/main.scss` for SCSS entry point
|
|
104
178
|
- `assets/` directory for static files
|
|
105
179
|
|
|
106
|
-
###
|
|
107
|
-
|
|
108
|
-
If you need custom paths or behavior:
|
|
180
|
+
### Static Site Configuration
|
|
109
181
|
|
|
110
182
|
```javascript
|
|
111
183
|
export default {
|
|
112
|
-
|
|
184
|
+
type: 'static', // Default - generates static files
|
|
185
|
+
|
|
186
|
+
// Auto-detected paths (override if needed)
|
|
187
|
+
views: './src/views',
|
|
113
188
|
scripts: './src/js/app.js',
|
|
114
|
-
styles: './src/
|
|
115
|
-
views: './templates',
|
|
189
|
+
styles: './src/scss/main.scss',
|
|
116
190
|
assets: './public',
|
|
117
|
-
outputDir: './
|
|
191
|
+
outputDir: './dist',
|
|
118
192
|
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
193
|
+
// Template compilation
|
|
194
|
+
templateCompiler: (template, data) => {
|
|
195
|
+
return yourTemplateEngine(template, data);
|
|
122
196
|
},
|
|
123
197
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
198
|
+
// Routes for dynamic pages from data
|
|
199
|
+
routes: [{
|
|
200
|
+
name: 'Blog Posts',
|
|
201
|
+
path: '/posts/:slug',
|
|
202
|
+
template: 'post.html',
|
|
203
|
+
repeat: (state) => state.get(['posts']),
|
|
204
|
+
data: (state, post) => ({
|
|
205
|
+
...post,
|
|
206
|
+
title: post.title,
|
|
207
|
+
slug: post.slug
|
|
208
|
+
})
|
|
209
|
+
}]
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Server Application Configuration
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
export default {
|
|
217
|
+
type: 'server', // Express application mode
|
|
218
|
+
|
|
219
|
+
port: 3000,
|
|
220
|
+
outputDir: './dist', // Serve static files from here
|
|
221
|
+
|
|
222
|
+
// Same view/asset processing as static mode
|
|
223
|
+
views: './src/views',
|
|
224
|
+
scripts: './src/js',
|
|
225
|
+
styles: './src/scss',
|
|
226
|
+
|
|
227
|
+
// Server-specific routes
|
|
228
|
+
routes: [{
|
|
229
|
+
name: 'Posts API',
|
|
230
|
+
path: '/api/posts',
|
|
231
|
+
method: 'GET',
|
|
232
|
+
handler: async (req, res) => {
|
|
233
|
+
const posts = await getPosts();
|
|
234
|
+
res.json(posts);
|
|
235
|
+
}
|
|
236
|
+
}, {
|
|
237
|
+
name: 'Post Pages',
|
|
238
|
+
path: '/posts/:slug',
|
|
239
|
+
template: 'post.html',
|
|
240
|
+
data: async (state, params) => {
|
|
241
|
+
const post = await getPost(params.slug);
|
|
242
|
+
return { post };
|
|
243
|
+
}
|
|
244
|
+
}],
|
|
245
|
+
|
|
246
|
+
// API routes
|
|
247
|
+
api: [{
|
|
248
|
+
path: '/api/users',
|
|
249
|
+
method: 'POST',
|
|
250
|
+
handler: async (req, res) => {
|
|
251
|
+
const user = await createUser(req.body);
|
|
252
|
+
res.json(user);
|
|
253
|
+
}
|
|
254
|
+
}],
|
|
255
|
+
|
|
256
|
+
// Express middleware
|
|
257
|
+
middleware: [
|
|
258
|
+
express.json(),
|
|
259
|
+
cors()
|
|
260
|
+
]
|
|
128
261
|
};
|
|
129
262
|
```
|
|
130
263
|
|
|
@@ -152,9 +285,9 @@ export default {
|
|
|
152
285
|
};
|
|
153
286
|
```
|
|
154
287
|
|
|
155
|
-
###
|
|
288
|
+
### Dynamic Routes & Data-Driven Pages
|
|
156
289
|
|
|
157
|
-
Create multiple pages from data arrays using the
|
|
290
|
+
Create multiple pages from data arrays using the routes system:
|
|
158
291
|
|
|
159
292
|
```javascript
|
|
160
293
|
// clovie.config.js
|
|
@@ -163,51 +296,46 @@ export default {
|
|
|
163
296
|
data: {
|
|
164
297
|
title: 'My Blog',
|
|
165
298
|
posts: [
|
|
166
|
-
{ id: 1, title: 'First Post', content: 'Hello World' },
|
|
167
|
-
{ id: 2, title: 'Second Post', content: 'Another post' },
|
|
168
|
-
{ id: 3, title: 'Third Post', content: 'Yet another' }
|
|
299
|
+
{ id: 1, title: 'First Post', content: 'Hello World', slug: 'first-post' },
|
|
300
|
+
{ id: 2, title: 'Second Post', content: 'Another post', slug: 'second-post' },
|
|
301
|
+
{ id: 3, title: 'Third Post', content: 'Yet another', slug: 'third-post' }
|
|
169
302
|
]
|
|
170
303
|
},
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
date: new Date().toISOString()
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
304
|
+
routes: [{
|
|
305
|
+
name: 'Blog Posts',
|
|
306
|
+
path: '/posts/:slug',
|
|
307
|
+
template: 'post.html',
|
|
308
|
+
repeat: (state) => state.get(['posts']),
|
|
309
|
+
data: (state, post) => ({
|
|
310
|
+
...post,
|
|
311
|
+
excerpt: post.content.substring(0, 100) + '...',
|
|
312
|
+
date: new Date().toISOString()
|
|
313
|
+
})
|
|
314
|
+
}]
|
|
187
315
|
};
|
|
188
316
|
```
|
|
189
317
|
|
|
190
|
-
**Template (`
|
|
318
|
+
**Template (`post.html`):**
|
|
191
319
|
```html
|
|
192
320
|
<!DOCTYPE html>
|
|
193
321
|
<html>
|
|
194
322
|
<head>
|
|
195
|
-
<title>{{
|
|
323
|
+
<title>{{title}} - {{../title}}</title>
|
|
196
324
|
</head>
|
|
197
325
|
<body>
|
|
198
326
|
<article>
|
|
199
|
-
<h1>{{
|
|
200
|
-
<p>{{
|
|
201
|
-
<div>{{
|
|
327
|
+
<h1>{{title}}</h1>
|
|
328
|
+
<p>{{excerpt}}</p>
|
|
329
|
+
<div>{{content}}</div>
|
|
202
330
|
</article>
|
|
203
331
|
</body>
|
|
204
332
|
</html>
|
|
205
333
|
```
|
|
206
334
|
|
|
207
335
|
**Output:**
|
|
208
|
-
- `post
|
|
209
|
-
- `post
|
|
210
|
-
- `post
|
|
336
|
+
- `posts/first-post.html` - First post page
|
|
337
|
+
- `posts/second-post.html` - Second post page
|
|
338
|
+
- `posts/third-post.html` - Third post page
|
|
211
339
|
|
|
212
340
|
### Custom Template Engines
|
|
213
341
|
|
|
@@ -263,50 +391,55 @@ export default {
|
|
|
263
391
|
};
|
|
264
392
|
```
|
|
265
393
|
|
|
266
|
-
### Pagination
|
|
394
|
+
### Route Pagination
|
|
267
395
|
|
|
268
|
-
|
|
396
|
+
Routes support built-in pagination for large datasets:
|
|
269
397
|
|
|
270
398
|
```javascript
|
|
271
399
|
export default {
|
|
272
400
|
// ... other config
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
401
|
+
routes: [{
|
|
402
|
+
name: 'Blog Pagination',
|
|
403
|
+
path: '/blog/:page?',
|
|
404
|
+
template: 'blog.html',
|
|
405
|
+
paginate: 5, // 5 posts per page
|
|
406
|
+
repeat: (state) => state.get(['posts']),
|
|
407
|
+
data: (state, posts, pageInfo) => ({
|
|
408
|
+
posts,
|
|
409
|
+
pagination: pageInfo,
|
|
410
|
+
title: `Blog - Page ${pageInfo.current}`
|
|
411
|
+
})
|
|
412
|
+
}]
|
|
282
413
|
};
|
|
283
414
|
```
|
|
284
415
|
|
|
285
416
|
**Output:**
|
|
286
|
-
- `blog.html` - First 5 posts
|
|
287
|
-
- `blog
|
|
288
|
-
- `blog
|
|
417
|
+
- `blog.html` - First 5 posts (page 1)
|
|
418
|
+
- `blog/2.html` - Next 5 posts (page 2)
|
|
419
|
+
- `blog/3.html` - Remaining posts (page 3)
|
|
289
420
|
|
|
290
|
-
### Data Transformation
|
|
421
|
+
### Data Transformation in Routes
|
|
291
422
|
|
|
292
|
-
Transform data before rendering
|
|
423
|
+
Transform data before rendering using the `data` function:
|
|
293
424
|
|
|
294
425
|
```javascript
|
|
295
426
|
export default {
|
|
296
427
|
// ... other config
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
428
|
+
routes: [{
|
|
429
|
+
name: 'Products',
|
|
430
|
+
path: '/products/:slug',
|
|
431
|
+
template: 'product.html',
|
|
432
|
+
repeat: (state) => state.get(['products']),
|
|
433
|
+
data: (state, product) => ({
|
|
434
|
+
...product,
|
|
435
|
+
price: `$${product.price.toFixed(2)}`,
|
|
436
|
+
slug: product.name.toLowerCase().replace(/\s+/g, '-'),
|
|
437
|
+
inStock: product.quantity > 0,
|
|
438
|
+
relatedProducts: state.get(['products']).filter(p =>
|
|
439
|
+
p.category === product.category && p.id !== product.id
|
|
440
|
+
)
|
|
441
|
+
})
|
|
442
|
+
}]
|
|
310
443
|
};
|
|
311
444
|
```
|
|
312
445
|
|
|
@@ -358,10 +491,11 @@ Clovie automatically detects common project structures:
|
|
|
358
491
|
### Best Practices
|
|
359
492
|
|
|
360
493
|
1. **Use partial templates** (files starting with `_`) for reusable components
|
|
361
|
-
2. **Validate data structures** before passing to
|
|
362
|
-
3. **Handle async data** with proper error catching
|
|
363
|
-
4. **Use meaningful
|
|
364
|
-
5. **Transform data** in
|
|
494
|
+
2. **Validate data structures** before passing to routes
|
|
495
|
+
3. **Handle async data** with proper error catching in route data functions
|
|
496
|
+
4. **Use meaningful route paths** for SEO and organization
|
|
497
|
+
5. **Transform data** in route data functions, not in templates
|
|
498
|
+
6. **Separate static and dynamic routes** for better performance
|
|
365
499
|
|
|
366
500
|
### Project Structure
|
|
367
501
|
|
|
@@ -423,9 +557,9 @@ npm run test:watch
|
|
|
423
557
|
- Ensure the `views` path in your config is correct
|
|
424
558
|
- Create the views directory if it doesn't exist
|
|
425
559
|
|
|
426
|
-
**"
|
|
427
|
-
- Check that your
|
|
428
|
-
- Ensure the
|
|
560
|
+
**"Route repeat function must return an array"**
|
|
561
|
+
- Check that your route's repeat function returns an array
|
|
562
|
+
- Ensure the data structure matches the route configuration
|
|
429
563
|
|
|
430
564
|
**"Maximum directory depth exceeded"**
|
|
431
565
|
- Check for circular symlinks or extremely deep directory structures
|
package/bin/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
8
8
|
const __dirname = dirname(__filename);
|
|
9
9
|
|
|
10
10
|
// Local
|
|
11
|
-
import
|
|
11
|
+
import { createClovie } from "../lib/createClovie.js";
|
|
12
12
|
|
|
13
13
|
// Check for create command first (before any argument parsing)
|
|
14
14
|
if (process.argv.includes('create') && process.argv.length > 2) {
|
|
@@ -91,6 +91,11 @@ if (mainOptions.command === 'watch') {
|
|
|
91
91
|
options.watch = true;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// Handle server command
|
|
95
|
+
if (mainOptions.command === 'server') {
|
|
96
|
+
options.server = true;
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
// Config path
|
|
95
100
|
const configPath = path.resolve(process.cwd(), options.config);
|
|
96
101
|
|
|
@@ -113,19 +118,103 @@ async function main() {
|
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
//
|
|
117
|
-
|
|
121
|
+
// Override config type if server command is used
|
|
122
|
+
if (options.server) {
|
|
123
|
+
config.type = 'server';
|
|
124
|
+
}
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
process.exit(1);
|
|
122
|
-
});
|
|
126
|
+
// New Clovie instance
|
|
127
|
+
const clovie = await createClovie(config);
|
|
123
128
|
|
|
124
|
-
if (options.
|
|
125
|
-
|
|
129
|
+
if (options.server) {
|
|
130
|
+
// Server mode - run as Express server
|
|
131
|
+
console.log('🌐 Starting server mode...');
|
|
132
|
+
|
|
133
|
+
// Load data into state first
|
|
134
|
+
if (config.data) {
|
|
135
|
+
console.log('📊 Loading data into state...');
|
|
136
|
+
let loadedData = {};
|
|
137
|
+
if (typeof config.data === 'function') {
|
|
138
|
+
loadedData = await config.data();
|
|
139
|
+
} else if (typeof config.data === 'object') {
|
|
140
|
+
loadedData = config.data;
|
|
141
|
+
}
|
|
142
|
+
clovie.state.load(loadedData);
|
|
143
|
+
console.log(` Loaded ${Object.keys(loadedData).length} data sources into state`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Start server
|
|
147
|
+
clovie.server.start();
|
|
148
|
+
|
|
149
|
+
// Keep the process running
|
|
150
|
+
process.on('SIGINT', () => {
|
|
151
|
+
console.log('\n🛑 Stopping server...');
|
|
152
|
+
clovie.server.stop();
|
|
153
|
+
process.exit(0);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
} else if (options.watch) {
|
|
157
|
+
// Development mode with file watching
|
|
158
|
+
console.log('🏗️ Initial build...');
|
|
159
|
+
await clovie.build.static();
|
|
160
|
+
console.log('✅ Initial build completed\n');
|
|
161
|
+
|
|
162
|
+
// Start development server
|
|
163
|
+
clovie.server.start();
|
|
164
|
+
|
|
165
|
+
// Set up file watching
|
|
166
|
+
console.log('👀 Setting up file watching...');
|
|
167
|
+
const watchPaths = [
|
|
168
|
+
config.views,
|
|
169
|
+
config.partials,
|
|
170
|
+
config.styles,
|
|
171
|
+
config.scripts,
|
|
172
|
+
].filter(Boolean); // Remove undefined paths
|
|
173
|
+
|
|
174
|
+
const watchers = clovie.file.watch(watchPaths);
|
|
175
|
+
|
|
176
|
+
// Set up event handlers for each watcher
|
|
177
|
+
watchers.forEach(watcher => {
|
|
178
|
+
watcher.on('change', async (filePath) => {
|
|
179
|
+
console.log(`🔄 File changed: ${filePath}`);
|
|
180
|
+
console.log('🔄 Triggering rebuild...');
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const result = await clovie.build.static();
|
|
184
|
+
console.log(`✅ Rebuild completed in ${result.buildTime}ms`);
|
|
185
|
+
|
|
186
|
+
// Notify live reload
|
|
187
|
+
if (clovie.server && clovie.server.notifyReload) {
|
|
188
|
+
clovie.server.notifyReload();
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('❌ Rebuild failed:', error.message);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
watcher.on('error', (error) => {
|
|
196
|
+
console.error('❌ File watcher error:', error);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
console.log(`🌐 Development server running at http://localhost:${config.port || 3000}`);
|
|
201
|
+
console.log('👀 Watching for file changes...');
|
|
202
|
+
console.log('Press Ctrl+C to stop the server\n');
|
|
203
|
+
|
|
204
|
+
// Keep the process running
|
|
205
|
+
process.on('SIGINT', () => {
|
|
206
|
+
console.log('\n🛑 Stopping development server...');
|
|
207
|
+
if (clovie.file.isWatching()) {
|
|
208
|
+
clovie.file.stopWatching();
|
|
209
|
+
}
|
|
210
|
+
process.exit(0);
|
|
211
|
+
});
|
|
212
|
+
|
|
126
213
|
} else {
|
|
127
|
-
|
|
128
|
-
|
|
214
|
+
// Build mode
|
|
215
|
+
const result = await clovie.build.static();
|
|
216
|
+
console.log(`✅ Build completed in ${result.buildTime}ms`);
|
|
217
|
+
console.log(`📁 Generated ${result.filesGenerated} files`);
|
|
129
218
|
process.exit(0);
|
|
130
219
|
}
|
|
131
220
|
} catch (err) {
|
package/config/clovie.config.js
CHANGED
|
@@ -31,5 +31,22 @@ export default {
|
|
|
31
31
|
// Development options
|
|
32
32
|
watch: false,
|
|
33
33
|
port: 3000,
|
|
34
|
-
open: false
|
|
34
|
+
open: false,
|
|
35
|
+
routes: [
|
|
36
|
+
{
|
|
37
|
+
name: 'Products',
|
|
38
|
+
path: '/products/:slug',
|
|
39
|
+
template: 'index.html',
|
|
40
|
+
repeat: (state) => {
|
|
41
|
+
return state.get(['products'])
|
|
42
|
+
},
|
|
43
|
+
data: (state, item) => {
|
|
44
|
+
return {
|
|
45
|
+
...item,
|
|
46
|
+
slug: item.name,
|
|
47
|
+
title: item.name
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
]
|
|
35
52
|
}
|