@constela/start 1.2.1 → 1.2.3
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 +477 -0
- package/dist/{chunk-6AN7W7KZ.js → chunk-U6CTMEFX.js} +62 -15
- package/dist/cli/index.js +4 -45
- package/dist/index.d.ts +32 -1
- package/dist/index.js +5 -1
- package/package.json +4 -4
package/README.md
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
# @constela/start
|
|
2
|
+
|
|
3
|
+
Meta-framework for Constela applications with dev server, build tools, and SSG.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @constela/start
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
This package provides a full-stack framework for building Constela applications:
|
|
14
|
+
|
|
15
|
+
- **Dev Server** - Vite-powered development server with HMR
|
|
16
|
+
- **Build** - Production builds with SSG support
|
|
17
|
+
- **File Router** - File-based routing conventions
|
|
18
|
+
- **Data Loading** - Glob, file, and API data sources
|
|
19
|
+
- **MDX** - Markdown with JSX component support
|
|
20
|
+
- **Layouts** - Nested layout composition
|
|
21
|
+
- **API Routes** - Server-side API endpoints
|
|
22
|
+
- **Middleware** - Request middleware chain
|
|
23
|
+
- **Edge Adapters** - Deploy to Cloudflare, Vercel, Deno
|
|
24
|
+
|
|
25
|
+
## CLI Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Development server
|
|
29
|
+
npx constela-start dev --port 3000
|
|
30
|
+
|
|
31
|
+
# Production build
|
|
32
|
+
npx constela-start build --outDir dist
|
|
33
|
+
|
|
34
|
+
# Start production server
|
|
35
|
+
npx constela-start start --port 3000
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API Reference
|
|
39
|
+
|
|
40
|
+
### createDevServer
|
|
41
|
+
|
|
42
|
+
Creates a development server with hot reload.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { createDevServer } from '@constela/start';
|
|
46
|
+
|
|
47
|
+
const server = await createDevServer({
|
|
48
|
+
port: 3000,
|
|
49
|
+
host: 'localhost',
|
|
50
|
+
routesDir: 'src/routes',
|
|
51
|
+
publicDir: 'public',
|
|
52
|
+
layoutsDir: 'src/layouts',
|
|
53
|
+
css: {
|
|
54
|
+
links: ['/styles/main.css'],
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**DevServerOptions:**
|
|
60
|
+
- `port?: number` - Server port (default: 3000)
|
|
61
|
+
- `host?: string` - Server host (default: localhost)
|
|
62
|
+
- `routesDir?: string` - Routes directory (default: src/routes)
|
|
63
|
+
- `publicDir?: string` - Static files directory (default: public)
|
|
64
|
+
- `layoutsDir?: string` - Layouts directory
|
|
65
|
+
- `css?: { links?: string[] }` - CSS configuration
|
|
66
|
+
|
|
67
|
+
### build
|
|
68
|
+
|
|
69
|
+
Builds the application for production.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { build } from '@constela/start';
|
|
73
|
+
|
|
74
|
+
const result = await build({
|
|
75
|
+
outDir: 'dist',
|
|
76
|
+
routesDir: 'src/routes',
|
|
77
|
+
publicDir: 'public',
|
|
78
|
+
layoutsDir: 'src/layouts',
|
|
79
|
+
css: { links: ['/styles/main.css'] },
|
|
80
|
+
target: 'node', // or 'edge'
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
console.log('Routes:', result.routes);
|
|
84
|
+
console.log('Files:', result.files);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**BuildOptions:**
|
|
88
|
+
- `outDir?: string` - Output directory (default: dist)
|
|
89
|
+
- `routesDir?: string` - Pages directory
|
|
90
|
+
- `publicDir?: string` - Static files directory
|
|
91
|
+
- `layoutsDir?: string` - Layouts directory
|
|
92
|
+
- `css?: { links?: string[] }` - CSS configuration
|
|
93
|
+
- `target?: 'node' | 'edge'` - Build target
|
|
94
|
+
|
|
95
|
+
### generateStaticPages
|
|
96
|
+
|
|
97
|
+
Generates static HTML pages for SSG.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { generateStaticPages } from '@constela/start';
|
|
101
|
+
|
|
102
|
+
const files = await generateStaticPages({
|
|
103
|
+
routesDir: 'src/routes',
|
|
104
|
+
outDir: 'dist',
|
|
105
|
+
layoutsDir: 'src/layouts',
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## File-Based Routing
|
|
110
|
+
|
|
111
|
+
### scanRoutes
|
|
112
|
+
|
|
113
|
+
Discovers routes from the file system.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { scanRoutes } from '@constela/start';
|
|
117
|
+
|
|
118
|
+
const routes = await scanRoutes('src/routes');
|
|
119
|
+
// [{ path: '/', file: 'index.json', type: 'page' }, ...]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Routing Conventions:**
|
|
123
|
+
|
|
124
|
+
| File | Route |
|
|
125
|
+
|------|-------|
|
|
126
|
+
| `index.json` | `/` |
|
|
127
|
+
| `about.json` | `/about` |
|
|
128
|
+
| `users/[id].json` | `/users/:id` |
|
|
129
|
+
| `docs/[...slug].json` | `/docs/*` |
|
|
130
|
+
| `api/users.ts` | `/api/users` (API route) |
|
|
131
|
+
| `_middleware.ts` | Middleware |
|
|
132
|
+
|
|
133
|
+
### filePathToPattern
|
|
134
|
+
|
|
135
|
+
Converts file paths to route patterns.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { filePathToPattern } from '@constela/start';
|
|
139
|
+
|
|
140
|
+
filePathToPattern('users/[id].json'); // '/users/:id'
|
|
141
|
+
filePathToPattern('docs/[...slug].json'); // '/docs/*'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Data Loading
|
|
145
|
+
|
|
146
|
+
### DataLoader
|
|
147
|
+
|
|
148
|
+
Loads data from various sources.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { DataLoader } from '@constela/start';
|
|
152
|
+
|
|
153
|
+
const loader = new DataLoader('src/routes');
|
|
154
|
+
|
|
155
|
+
// Glob pattern
|
|
156
|
+
const posts = await loader.loadGlob('content/blog/*.mdx', 'mdx');
|
|
157
|
+
|
|
158
|
+
// Single file
|
|
159
|
+
const config = await loader.loadFile('data/config.json');
|
|
160
|
+
|
|
161
|
+
// API endpoint
|
|
162
|
+
const users = await loader.loadApi('https://api.example.com/users');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Data Source Types
|
|
166
|
+
|
|
167
|
+
**Glob Pattern:**
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"data": {
|
|
171
|
+
"posts": {
|
|
172
|
+
"type": "glob",
|
|
173
|
+
"pattern": "content/blog/*.mdx",
|
|
174
|
+
"transform": "mdx"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**File:**
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"data": {
|
|
184
|
+
"config": {
|
|
185
|
+
"type": "file",
|
|
186
|
+
"path": "data/config.json"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**API:**
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"data": {
|
|
196
|
+
"users": {
|
|
197
|
+
"type": "api",
|
|
198
|
+
"url": "https://api.example.com/users"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Transforms
|
|
205
|
+
|
|
206
|
+
- `mdx` - Markdown with JSX to Constela nodes
|
|
207
|
+
- `yaml` - YAML to JSON
|
|
208
|
+
- `csv` - CSV to array of objects
|
|
209
|
+
|
|
210
|
+
## MDX Support
|
|
211
|
+
|
|
212
|
+
### mdxToConstela
|
|
213
|
+
|
|
214
|
+
Transforms MDX content to Constela view nodes.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { mdxToConstela } from '@constela/start';
|
|
218
|
+
|
|
219
|
+
const result = await mdxToConstela(mdxContent, {
|
|
220
|
+
components: {
|
|
221
|
+
Callout: {
|
|
222
|
+
params: { type: { type: 'string' } },
|
|
223
|
+
view: { kind: 'element', tag: 'div', ... },
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Supported Markdown:**
|
|
230
|
+
- Headings, paragraphs, emphasis, strong
|
|
231
|
+
- Links, images, lists (ordered/unordered)
|
|
232
|
+
- Blockquotes, tables (GFM)
|
|
233
|
+
- Code blocks with language
|
|
234
|
+
- Horizontal rules, line breaks
|
|
235
|
+
|
|
236
|
+
**MDX Extensions:**
|
|
237
|
+
- JSX elements
|
|
238
|
+
- Custom components (PascalCase)
|
|
239
|
+
- Expression interpolation
|
|
240
|
+
|
|
241
|
+
### Static Paths with MDX
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"data": {
|
|
246
|
+
"docs": {
|
|
247
|
+
"type": "glob",
|
|
248
|
+
"pattern": "content/docs/*.mdx",
|
|
249
|
+
"transform": "mdx"
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"route": {
|
|
253
|
+
"path": "/docs/:slug",
|
|
254
|
+
"getStaticPaths": {
|
|
255
|
+
"source": "docs",
|
|
256
|
+
"params": {
|
|
257
|
+
"slug": { "expr": "get", "base": { "expr": "var", "name": "item" }, "path": "slug" }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Layout System
|
|
265
|
+
|
|
266
|
+
### LayoutResolver
|
|
267
|
+
|
|
268
|
+
Manages layout discovery and composition.
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { LayoutResolver } from '@constela/start';
|
|
272
|
+
|
|
273
|
+
const resolver = new LayoutResolver('src/layouts');
|
|
274
|
+
await resolver.scan();
|
|
275
|
+
|
|
276
|
+
if (resolver.has('docs')) {
|
|
277
|
+
const layout = await resolver.load('docs');
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Layout Definition
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"version": "1.0",
|
|
286
|
+
"type": "layout",
|
|
287
|
+
"view": {
|
|
288
|
+
"kind": "element",
|
|
289
|
+
"tag": "div",
|
|
290
|
+
"children": [
|
|
291
|
+
{ "kind": "component", "name": "Header" },
|
|
292
|
+
{ "kind": "slot" },
|
|
293
|
+
{ "kind": "component", "name": "Footer" }
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Named Slots:**
|
|
300
|
+
```json
|
|
301
|
+
{ "kind": "slot", "name": "sidebar" }
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Page with Layout
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"route": {
|
|
309
|
+
"path": "/docs/:slug",
|
|
310
|
+
"layout": "docs",
|
|
311
|
+
"layoutParams": {
|
|
312
|
+
"title": { "expr": "data", "name": "doc", "path": "title" }
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## API Routes
|
|
319
|
+
|
|
320
|
+
### createAPIHandler
|
|
321
|
+
|
|
322
|
+
Creates an API route handler.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { createAPIHandler } from '@constela/start';
|
|
326
|
+
|
|
327
|
+
const handler = createAPIHandler({
|
|
328
|
+
GET: async (ctx) => {
|
|
329
|
+
return new Response(JSON.stringify({ users: [] }), {
|
|
330
|
+
headers: { 'Content-Type': 'application/json' },
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
POST: async (ctx) => {
|
|
334
|
+
const body = await ctx.request.json();
|
|
335
|
+
// ...
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**APIContext:**
|
|
341
|
+
```typescript
|
|
342
|
+
interface APIContext {
|
|
343
|
+
params: Record<string, string>;
|
|
344
|
+
query: URLSearchParams;
|
|
345
|
+
request: Request;
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Supported Methods:** GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
|
|
350
|
+
|
|
351
|
+
## Middleware
|
|
352
|
+
|
|
353
|
+
### createMiddlewareChain
|
|
354
|
+
|
|
355
|
+
Creates a middleware chain for request processing.
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import { createMiddlewareChain } from '@constela/start';
|
|
359
|
+
|
|
360
|
+
const chain = createMiddlewareChain([
|
|
361
|
+
async (ctx, next) => {
|
|
362
|
+
console.log('Request:', ctx.url);
|
|
363
|
+
const response = await next();
|
|
364
|
+
console.log('Response:', response.status);
|
|
365
|
+
return response;
|
|
366
|
+
},
|
|
367
|
+
async (ctx, next) => {
|
|
368
|
+
ctx.locals.user = await getUser(ctx.request);
|
|
369
|
+
return next();
|
|
370
|
+
},
|
|
371
|
+
]);
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**MiddlewareContext:**
|
|
375
|
+
```typescript
|
|
376
|
+
interface MiddlewareContext {
|
|
377
|
+
request: Request;
|
|
378
|
+
params: Record<string, string>;
|
|
379
|
+
url: URL;
|
|
380
|
+
locals: Record<string, unknown>;
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Edge Adapters
|
|
385
|
+
|
|
386
|
+
### createAdapter
|
|
387
|
+
|
|
388
|
+
Creates an adapter for edge deployment.
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import { createAdapter } from '@constela/start';
|
|
392
|
+
|
|
393
|
+
const adapter = createAdapter({
|
|
394
|
+
platform: 'cloudflare', // or 'vercel', 'deno', 'node'
|
|
395
|
+
routes: scannedRoutes,
|
|
396
|
+
fallback: notFoundProgram,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Cloudflare Workers
|
|
400
|
+
export default {
|
|
401
|
+
fetch: adapter.fetch,
|
|
402
|
+
};
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Platforms:**
|
|
406
|
+
- `cloudflare` - Cloudflare Workers
|
|
407
|
+
- `vercel` - Vercel Edge Functions
|
|
408
|
+
- `deno` - Deno Deploy
|
|
409
|
+
- `node` - Node.js
|
|
410
|
+
|
|
411
|
+
## Page Types
|
|
412
|
+
|
|
413
|
+
### Static Page
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// pages/about.ts
|
|
417
|
+
import type { CompiledProgram } from '@constela/compiler';
|
|
418
|
+
|
|
419
|
+
const page: CompiledProgram = {
|
|
420
|
+
version: '1.0',
|
|
421
|
+
state: {},
|
|
422
|
+
actions: {},
|
|
423
|
+
view: { kind: 'element', tag: 'div', ... },
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
export default page;
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Dynamic Page
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// pages/users/[id].ts
|
|
433
|
+
import type { PageExportFunction, StaticPathsResult } from '@constela/start';
|
|
434
|
+
|
|
435
|
+
export const getStaticPaths = async (): Promise<StaticPathsResult> => ({
|
|
436
|
+
paths: [
|
|
437
|
+
{ params: { id: '1' } },
|
|
438
|
+
{ params: { id: '2' } },
|
|
439
|
+
],
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
const page: PageExportFunction = async (params) => {
|
|
443
|
+
const user = await fetchUser(params.id);
|
|
444
|
+
return compileUserPage(user);
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
export default page;
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Configuration
|
|
451
|
+
|
|
452
|
+
### ConstelaConfig
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
interface ConstelaConfig {
|
|
456
|
+
adapter?: 'cloudflare' | 'vercel' | 'deno' | 'node';
|
|
457
|
+
ssg?: {
|
|
458
|
+
routes?: string[];
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Exports
|
|
464
|
+
|
|
465
|
+
### Runtime Entry Points
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
// Client-side hydration
|
|
469
|
+
import '@constela/start/client';
|
|
470
|
+
|
|
471
|
+
// Server-side rendering
|
|
472
|
+
import { render } from '@constela/start/server';
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
## License
|
|
476
|
+
|
|
477
|
+
MIT
|
|
@@ -1880,7 +1880,7 @@ import { analyzeLayoutPass, transformLayoutPass, composeLayoutWithPage } from "@
|
|
|
1880
1880
|
var DEFAULT_PORT = 3e3;
|
|
1881
1881
|
var DEFAULT_HOST = "localhost";
|
|
1882
1882
|
var DEFAULT_PUBLIC_DIR = "public";
|
|
1883
|
-
var DEFAULT_ROUTES_DIR = "src/
|
|
1883
|
+
var DEFAULT_ROUTES_DIR = "src/routes";
|
|
1884
1884
|
function matchRoute(url, routes) {
|
|
1885
1885
|
const normalizedUrl = url === "/" ? "/" : url.replace(/\/$/, "");
|
|
1886
1886
|
const urlSegments = normalizedUrl.split("/").filter(Boolean);
|
|
@@ -2001,7 +2001,7 @@ async function createDevServer(options = {}) {
|
|
|
2001
2001
|
return actualPort;
|
|
2002
2002
|
},
|
|
2003
2003
|
async listen() {
|
|
2004
|
-
return new Promise((
|
|
2004
|
+
return new Promise((resolve5, reject) => {
|
|
2005
2005
|
httpServer = createServer(async (req, res) => {
|
|
2006
2006
|
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
2007
2007
|
const pathname = url.pathname;
|
|
@@ -2138,7 +2138,7 @@ h1 { color: #666; }
|
|
|
2138
2138
|
if (address) {
|
|
2139
2139
|
actualPort = address.port;
|
|
2140
2140
|
}
|
|
2141
|
-
|
|
2141
|
+
resolve5();
|
|
2142
2142
|
});
|
|
2143
2143
|
});
|
|
2144
2144
|
},
|
|
@@ -2147,9 +2147,9 @@ h1 { color: #666; }
|
|
|
2147
2147
|
await viteServer.close();
|
|
2148
2148
|
viteServer = null;
|
|
2149
2149
|
}
|
|
2150
|
-
return new Promise((
|
|
2150
|
+
return new Promise((resolve5, reject) => {
|
|
2151
2151
|
if (!httpServer) {
|
|
2152
|
-
|
|
2152
|
+
resolve5();
|
|
2153
2153
|
return;
|
|
2154
2154
|
}
|
|
2155
2155
|
httpServer.closeAllConnections();
|
|
@@ -2158,7 +2158,7 @@ h1 { color: #666; }
|
|
|
2158
2158
|
reject(err);
|
|
2159
2159
|
} else {
|
|
2160
2160
|
httpServer = null;
|
|
2161
|
-
|
|
2161
|
+
resolve5();
|
|
2162
2162
|
}
|
|
2163
2163
|
});
|
|
2164
2164
|
});
|
|
@@ -2170,7 +2170,7 @@ h1 { color: #666; }
|
|
|
2170
2170
|
// src/build/index.ts
|
|
2171
2171
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
2172
2172
|
import { mkdir as mkdir2, writeFile, cp, readdir } from "fs/promises";
|
|
2173
|
-
import { join as join9, dirname as dirname5, relative as relative2, basename as basename4 } from "path";
|
|
2173
|
+
import { join as join9, dirname as dirname5, relative as relative2, basename as basename4, isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
|
|
2174
2174
|
|
|
2175
2175
|
// src/build/bundler.ts
|
|
2176
2176
|
import * as esbuild from "esbuild";
|
|
@@ -2207,6 +2207,7 @@ async function bundleRuntime(options) {
|
|
|
2207
2207
|
}
|
|
2208
2208
|
|
|
2209
2209
|
// src/build/index.ts
|
|
2210
|
+
var DEFAULT_PUBLIC_DIR2 = "public";
|
|
2210
2211
|
function isDynamicRoute(pattern) {
|
|
2211
2212
|
return pattern.includes(":") || pattern.includes("*");
|
|
2212
2213
|
}
|
|
@@ -2440,7 +2441,7 @@ function validateJsonPage(content, filePath) {
|
|
|
2440
2441
|
async function build2(options) {
|
|
2441
2442
|
const outDir = options?.outDir ?? "dist";
|
|
2442
2443
|
const routesDir = options?.routesDir ?? "src/routes";
|
|
2443
|
-
const publicDir = options?.publicDir;
|
|
2444
|
+
const publicDir = options?.publicDir ?? DEFAULT_PUBLIC_DIR2;
|
|
2444
2445
|
const layoutsDir = options?.layoutsDir;
|
|
2445
2446
|
const generatedFiles = [];
|
|
2446
2447
|
await mkdir2(outDir, { recursive: true });
|
|
@@ -2470,8 +2471,11 @@ async function build2(options) {
|
|
|
2470
2471
|
};
|
|
2471
2472
|
}
|
|
2472
2473
|
const runtimePath = await bundleRuntime({ outDir });
|
|
2474
|
+
const absoluteRoutesDir = isAbsolute2(routesDir) ? routesDir : resolve4(routesDir);
|
|
2475
|
+
const projectRoot = dirname5(dirname5(absoluteRoutesDir));
|
|
2473
2476
|
for (const route of jsonPages) {
|
|
2474
|
-
const
|
|
2477
|
+
const relPathFromRoutesDir = relative2(absoluteRoutesDir, route.file);
|
|
2478
|
+
const relPathFromProjectRoot = relative2(projectRoot, route.file);
|
|
2475
2479
|
const content = readFileSync5(route.file, "utf-8");
|
|
2476
2480
|
const page = validateJsonPage(content, route.file);
|
|
2477
2481
|
if (isDynamicRoute(route.pattern)) {
|
|
@@ -2485,8 +2489,8 @@ async function build2(options) {
|
|
|
2485
2489
|
for (const pathEntry of staticPaths.paths) {
|
|
2486
2490
|
const params = pathEntry.params;
|
|
2487
2491
|
const outputPath = paramsToOutputPath(route.pattern, params, outDir);
|
|
2488
|
-
const loader = new JsonPageLoader(
|
|
2489
|
-
let pageInfo = await loader.loadPage(
|
|
2492
|
+
const loader = new JsonPageLoader(projectRoot);
|
|
2493
|
+
let pageInfo = await loader.loadPage(relPathFromProjectRoot);
|
|
2490
2494
|
if (layoutsDir) {
|
|
2491
2495
|
pageInfo = await processLayouts(pageInfo, layoutsDir);
|
|
2492
2496
|
}
|
|
@@ -2503,9 +2507,9 @@ async function build2(options) {
|
|
|
2503
2507
|
routes.push(routePath);
|
|
2504
2508
|
}
|
|
2505
2509
|
} else {
|
|
2506
|
-
const outputPath = getOutputPath(
|
|
2507
|
-
const loader = new JsonPageLoader(
|
|
2508
|
-
let pageInfo = await loader.loadPage(
|
|
2510
|
+
const outputPath = getOutputPath(relPathFromRoutesDir, outDir);
|
|
2511
|
+
const loader = new JsonPageLoader(projectRoot);
|
|
2512
|
+
let pageInfo = await loader.loadPage(relPathFromProjectRoot);
|
|
2509
2513
|
if (layoutsDir) {
|
|
2510
2514
|
pageInfo = await processLayouts(pageInfo, layoutsDir);
|
|
2511
2515
|
}
|
|
@@ -2527,6 +2531,47 @@ async function build2(options) {
|
|
|
2527
2531
|
};
|
|
2528
2532
|
}
|
|
2529
2533
|
|
|
2534
|
+
// src/config/config-loader.ts
|
|
2535
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
2536
|
+
import { join as join10 } from "path";
|
|
2537
|
+
var CONFIG_FILENAME = "constela.config.json";
|
|
2538
|
+
async function loadConfig(projectRoot) {
|
|
2539
|
+
const configPath = join10(projectRoot, CONFIG_FILENAME);
|
|
2540
|
+
if (!existsSync8(configPath)) {
|
|
2541
|
+
return {};
|
|
2542
|
+
}
|
|
2543
|
+
let content;
|
|
2544
|
+
try {
|
|
2545
|
+
content = readFileSync6(configPath, "utf-8");
|
|
2546
|
+
} catch (error) {
|
|
2547
|
+
throw new Error(`Failed to read config file: ${configPath}`);
|
|
2548
|
+
}
|
|
2549
|
+
try {
|
|
2550
|
+
return JSON.parse(content);
|
|
2551
|
+
} catch {
|
|
2552
|
+
throw new Error(`Invalid JSON in config file: ${configPath}`);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
async function resolveConfig(fileConfig, cliOptions) {
|
|
2556
|
+
if (!cliOptions) {
|
|
2557
|
+
return { ...fileConfig };
|
|
2558
|
+
}
|
|
2559
|
+
const result = { ...fileConfig };
|
|
2560
|
+
if (cliOptions.css !== void 0) result.css = cliOptions.css;
|
|
2561
|
+
if (cliOptions.layoutsDir !== void 0) result.layoutsDir = cliOptions.layoutsDir;
|
|
2562
|
+
if (cliOptions.routesDir !== void 0) result.routesDir = cliOptions.routesDir;
|
|
2563
|
+
if (cliOptions.publicDir !== void 0) result.publicDir = cliOptions.publicDir;
|
|
2564
|
+
if (cliOptions.outDir !== void 0) {
|
|
2565
|
+
result.build = { ...result.build, outDir: cliOptions.outDir };
|
|
2566
|
+
}
|
|
2567
|
+
if (cliOptions.port !== void 0 || cliOptions.host !== void 0) {
|
|
2568
|
+
result.dev = { ...result.dev };
|
|
2569
|
+
if (cliOptions.port !== void 0) result.dev.port = cliOptions.port;
|
|
2570
|
+
if (cliOptions.host !== void 0) result.dev.host = cliOptions.host;
|
|
2571
|
+
}
|
|
2572
|
+
return result;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2530
2575
|
export {
|
|
2531
2576
|
filePathToPattern,
|
|
2532
2577
|
scanRoutes,
|
|
@@ -2549,5 +2594,7 @@ export {
|
|
|
2549
2594
|
loadLayout,
|
|
2550
2595
|
LayoutResolver,
|
|
2551
2596
|
createDevServer,
|
|
2552
|
-
build2 as build
|
|
2597
|
+
build2 as build,
|
|
2598
|
+
loadConfig,
|
|
2599
|
+
resolveConfig
|
|
2553
2600
|
};
|
package/dist/cli/index.js
CHANGED
|
@@ -1,54 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
build,
|
|
3
|
-
createDevServer
|
|
4
|
-
|
|
3
|
+
createDevServer,
|
|
4
|
+
loadConfig,
|
|
5
|
+
resolveConfig
|
|
6
|
+
} from "../chunk-U6CTMEFX.js";
|
|
5
7
|
import "../chunk-PUTC5BCP.js";
|
|
6
8
|
|
|
7
9
|
// src/cli/index.ts
|
|
8
10
|
import { Command } from "commander";
|
|
9
|
-
|
|
10
|
-
// src/config/config-loader.ts
|
|
11
|
-
import { existsSync, readFileSync } from "fs";
|
|
12
|
-
import { join } from "path";
|
|
13
|
-
var CONFIG_FILENAME = "constela.config.json";
|
|
14
|
-
async function loadConfig(projectRoot) {
|
|
15
|
-
const configPath = join(projectRoot, CONFIG_FILENAME);
|
|
16
|
-
if (!existsSync(configPath)) {
|
|
17
|
-
return {};
|
|
18
|
-
}
|
|
19
|
-
let content;
|
|
20
|
-
try {
|
|
21
|
-
content = readFileSync(configPath, "utf-8");
|
|
22
|
-
} catch (error) {
|
|
23
|
-
throw new Error(`Failed to read config file: ${configPath}`);
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(content);
|
|
27
|
-
} catch {
|
|
28
|
-
throw new Error(`Invalid JSON in config file: ${configPath}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
async function resolveConfig(fileConfig, cliOptions) {
|
|
32
|
-
if (!cliOptions) {
|
|
33
|
-
return { ...fileConfig };
|
|
34
|
-
}
|
|
35
|
-
const result = { ...fileConfig };
|
|
36
|
-
if (cliOptions.css !== void 0) result.css = cliOptions.css;
|
|
37
|
-
if (cliOptions.layoutsDir !== void 0) result.layoutsDir = cliOptions.layoutsDir;
|
|
38
|
-
if (cliOptions.routesDir !== void 0) result.routesDir = cliOptions.routesDir;
|
|
39
|
-
if (cliOptions.publicDir !== void 0) result.publicDir = cliOptions.publicDir;
|
|
40
|
-
if (cliOptions.outDir !== void 0) {
|
|
41
|
-
result.build = { ...result.build, outDir: cliOptions.outDir };
|
|
42
|
-
}
|
|
43
|
-
if (cliOptions.port !== void 0 || cliOptions.host !== void 0) {
|
|
44
|
-
result.dev = { ...result.dev };
|
|
45
|
-
if (cliOptions.port !== void 0) result.dev.port = cliOptions.port;
|
|
46
|
-
if (cliOptions.host !== void 0) result.dev.host = cliOptions.host;
|
|
47
|
-
}
|
|
48
|
-
return result;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// src/cli/index.ts
|
|
52
11
|
var devHandler = async (options) => {
|
|
53
12
|
const port = parseInt(options.port, 10);
|
|
54
13
|
const host = options.host ?? "localhost";
|
package/dist/index.d.ts
CHANGED
|
@@ -522,4 +522,35 @@ declare class DataLoader {
|
|
|
522
522
|
getCacheSize(): number;
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
|
|
525
|
+
interface ConstelaConfigFile {
|
|
526
|
+
css?: string | string[];
|
|
527
|
+
layoutsDir?: string;
|
|
528
|
+
routesDir?: string;
|
|
529
|
+
publicDir?: string;
|
|
530
|
+
build?: {
|
|
531
|
+
outDir?: string;
|
|
532
|
+
};
|
|
533
|
+
dev?: {
|
|
534
|
+
port?: number;
|
|
535
|
+
host?: string;
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
interface CLIOptions {
|
|
539
|
+
css?: string | undefined;
|
|
540
|
+
layoutsDir?: string | undefined;
|
|
541
|
+
routesDir?: string | undefined;
|
|
542
|
+
publicDir?: string | undefined;
|
|
543
|
+
outDir?: string | undefined;
|
|
544
|
+
port?: number | undefined;
|
|
545
|
+
host?: string | undefined;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Load config from constela.config.json in project root
|
|
549
|
+
*/
|
|
550
|
+
declare function loadConfig(projectRoot: string): Promise<ConstelaConfigFile>;
|
|
551
|
+
/**
|
|
552
|
+
* Merge file config with CLI options (CLI takes precedence)
|
|
553
|
+
*/
|
|
554
|
+
declare function resolveConfig(fileConfig: ConstelaConfigFile, cliOptions?: CLIOptions): Promise<ConstelaConfigFile>;
|
|
555
|
+
|
|
556
|
+
export { type APIContext, type APIModule, type BuildOptions, type CLIOptions, type ComponentDef$1 as ComponentDef, type ConstelaConfig, type ConstelaConfigFile, DataLoader, type DevServerOptions, type GenerateStaticPagesOptions, type GlobResult, type LayoutInfo, LayoutResolver, type MDXToConstelaOptions, type MdxGlobResult, type Middleware, type MiddlewareContext, type MiddlewareNext, type PageExportFunction, type PageModule, type ScannedLayout, type ScannedRoute, type StaticFileResult, type StaticPath, type StaticPathEntry, type StaticPathsProvider, type StaticPathsResult, build, createAPIHandler, createAdapter, createDevServer, createMiddlewareChain, filePathToPattern, generateStaticPages, generateStaticPaths, getMimeType, isPageExportFunction, isPathSafe, loadApi, loadComponentDefinitions, loadConfig, loadFile, loadGlob, loadLayout, mdxContentToNode, mdxToConstela, resolveConfig, resolveLayout, resolvePageExport, resolveStaticFile, scanLayouts, scanRoutes, transformCsv, transformMdx, transformYaml };
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,13 @@ import {
|
|
|
9
9
|
isPathSafe,
|
|
10
10
|
loadApi,
|
|
11
11
|
loadComponentDefinitions,
|
|
12
|
+
loadConfig,
|
|
12
13
|
loadFile,
|
|
13
14
|
loadGlob,
|
|
14
15
|
loadLayout,
|
|
15
16
|
mdxContentToNode,
|
|
16
17
|
mdxToConstela,
|
|
18
|
+
resolveConfig,
|
|
17
19
|
resolveLayout,
|
|
18
20
|
resolveStaticFile,
|
|
19
21
|
scanLayouts,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
transformCsv,
|
|
22
24
|
transformMdx,
|
|
23
25
|
transformYaml
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-U6CTMEFX.js";
|
|
25
27
|
import {
|
|
26
28
|
generateHydrationScript,
|
|
27
29
|
renderPage,
|
|
@@ -332,11 +334,13 @@ export {
|
|
|
332
334
|
isPathSafe,
|
|
333
335
|
loadApi,
|
|
334
336
|
loadComponentDefinitions,
|
|
337
|
+
loadConfig,
|
|
335
338
|
loadFile,
|
|
336
339
|
loadGlob,
|
|
337
340
|
loadLayout,
|
|
338
341
|
mdxContentToNode,
|
|
339
342
|
mdxToConstela,
|
|
343
|
+
resolveConfig,
|
|
340
344
|
resolveLayout,
|
|
341
345
|
resolvePageExport,
|
|
342
346
|
resolveStaticFile,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constela/start",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Meta-framework for Constela applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"unified": "^11.0.0",
|
|
42
42
|
"vite": "^6.0.0",
|
|
43
43
|
"@constela/compiler": "0.7.0",
|
|
44
|
-
"@constela/router": "8.0.0",
|
|
45
|
-
"@constela/runtime": "0.10.1",
|
|
46
44
|
"@constela/core": "0.7.0",
|
|
47
|
-
"@constela/server": "3.0.0"
|
|
45
|
+
"@constela/server": "3.0.0",
|
|
46
|
+
"@constela/router": "8.0.0",
|
|
47
|
+
"@constela/runtime": "0.10.1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/mdast": "^4.0.4",
|