@cfdez11/vex 0.10.5 → 0.10.9

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 CHANGED
@@ -6,26 +6,7 @@
6
6
 
7
7
  A vanilla JavaScript meta-framework built on Express.js with file-based routing, multiple rendering strategies (SSR, CSR, SSG, ISR), streaming Suspense, and a Vue-like reactive system — no TypeScript, no bundler.
8
8
 
9
- ## Table of Contents
10
-
11
- - [Installation](#installation)
12
- - [Quick Start](#-quick-start)
13
- - [Project Structure](#-project-structure)
14
- - [Configuration](#-configuration-vexconfigjson)
15
- - [Creating a Page](#-creating-a-page)
16
- - [Components](#-components)
17
- - [Rendering Strategies](#-rendering-strategies)
18
- - [Layouts](#-layouts)
19
- - [Suspense (Streaming)](#-suspense-streaming)
20
- - [Reactive System](#-reactive-system)
21
- - [Template Syntax](#-template-syntax)
22
- - [Routing](#️-routing)
23
- - [Prefetching](#-prefetching)
24
- - [Styling](#-styling)
25
- - [Framework API](#-framework-api)
26
- - [Available Scripts](#-available-scripts)
27
- - [Rendering Flow](#️-rendering-flow)
28
- - [Roadmap](#️-roadmap)
9
+ Requires **Node.js >= 18**.
29
10
 
30
11
  ## Installation
31
12
 
@@ -33,9 +14,7 @@ A vanilla JavaScript meta-framework built on Express.js with file-based routing,
33
14
  npm install @cfdez11/vex
34
15
  ```
35
16
 
36
- Requires **Node.js >= 18**.
37
-
38
- ## 🚀 Quick Start
17
+ ## Quick Start
39
18
 
40
19
  ```bash
41
20
  mkdir my-app && cd my-app
@@ -64,621 +43,21 @@ Create the minimum structure:
64
43
  ```bash
65
44
  mkdir -p pages src public
66
45
  echo '@import "tailwindcss";' > src/input.css
67
- ```
68
-
69
- ```bash
70
46
  npm run dev
71
47
  # → http://localhost:3001
72
48
  ```
73
49
 
74
- ## 📁 Project Structure
75
-
76
- ```
77
- my-app/
78
- ├── pages/ # File-based routes
79
- │ ├── layout.vex # Root layout (wraps all pages)
80
- │ ├── page.vex # Home page → /
81
- │ ├── about/page.vex # About page → /about
82
- │ ├── users/[id]/page.vex # Dynamic → /users/:id
83
- │ ├── not-found/page.vex # 404 handler
84
- │ └── error/page.vex # 500 handler
85
- ├── components/ # Reusable .vex components (any subfolder works)
86
- ├── utils/ # User utilities (e.g. delay.js)
87
- ├── public/ # Static assets served at /
88
- │ └── styles.css # Compiled Tailwind output
89
- ├── src/
90
- │ └── input.css # Tailwind entry point
91
- ├── root.html # HTML shell template (optional override)
92
- └── vex.config.json # Framework config (optional)
93
- ```
94
-
95
- > Generated files are written to `.vexjs/` — do not edit them manually.
96
-
97
- ### Custom source directory
98
-
99
- If you prefer to keep all app code in a subfolder, set `srcDir` in `vex.config.json`:
100
-
101
- ```
102
- my-app/
103
- ├── app/ ← srcDir: "app"
104
- │ ├── pages/
105
- │ └── components/
106
- ├── public/
107
- └── vex.config.json
108
- ```
109
-
110
- ## ⚙️ Configuration (`vex.config.json`)
111
-
112
- Optional file at the project root.
113
-
114
- ```json
115
- {
116
- "srcDir": "app",
117
- "watchIgnore": ["dist", "coverage"]
118
- }
119
- ```
120
-
121
- | Field | Type | Default | Description |
122
- |-------|------|---------|-------------|
123
- | `srcDir` | `string` | `"."` | Directory containing `pages/`, `components/` and all user `.vex` files. When set, the dev watcher only observes this folder instead of the whole project root. |
124
- | `watchIgnore` | `string[]` | `[]` | Additional directory names to exclude from the dev file watcher. Merged with the built-in list: `node_modules`, `dist`, `build`, `.git`, `.vexjs`, `coverage`, `.next`, `.nuxt`, `tmp`, and more. |
125
-
126
- ## 📄 Creating a Page
127
-
128
- ```html
129
- <!-- pages/example/page.vex -->
130
- <script server>
131
- import UserCard from "@/components/user-card.vex";
132
-
133
- const metadata = { title: "My Page", description: "Page description" };
134
-
135
- async function getData({ req }) {
136
- return { message: "Hello from the server" };
137
- }
138
- </script>
139
-
140
- <script client>
141
- import Counter from "@/components/counter.vex";
142
- </script>
143
-
144
- <template>
145
- <h1>{{message}}</h1>
146
- <Counter start="0" />
147
- <UserCard :userId="1" />
148
- </template>
149
- ```
150
-
151
- Routes are auto-generated from the `pages/` folder — no manual registration needed.
152
-
153
- ## 🧩 Components
154
-
155
- Components are `.vex` files. They can live in any folder; the default convention is `components/`.
156
-
157
- ### Component structure
158
-
159
- ```html
160
- <!-- components/counter.vex -->
161
- <script client>
162
- import { reactive, computed } from "vex/reactive";
163
-
164
- const props = xprops({ start: { default: 0 } });
165
- const count = reactive(props.start);
166
- const stars = computed(() => "⭐".repeat(count.value));
167
- </script>
168
-
169
- <template>
170
- <div>
171
- <button @click="count.value--">-</button>
172
- <span>{{count.value}}</span>
173
- <button @click="count.value++">+</button>
174
- <div>{{stars.value}}</div>
175
- </div>
176
- </template>
177
- ```
178
-
179
- ### Server components
180
-
181
- ```html
182
- <!-- components/user-card.vex -->
183
- <script server>
184
- const props = xprops({ userId: { default: null } });
185
-
186
- async function getData({ props }) {
187
- const user = await fetch(`https://api.example.com/users/${props.userId}`)
188
- .then(r => r.json());
189
- return { user };
190
- }
191
- </script>
192
-
193
- <template>
194
- <div>
195
- <h3>{{user.name}}</h3>
196
- <p>{{user.email}}</p>
197
- </div>
198
- </template>
199
- ```
200
-
201
- ### Using components
202
-
203
- Import them in any page or component:
204
-
205
- ```html
206
- <script server>
207
- import UserCard from "@/components/user-card.vex";
208
- </script>
209
-
210
- <script client>
211
- import Counter from "@/components/counter.vex";
212
- </script>
213
-
214
- <template>
215
- <Counter :start="5" />
216
- <UserCard :userId="1" />
217
- </template>
218
- ```
219
-
220
- ### Component props (`xprops`)
221
-
222
- ```js
223
- const props = xprops({
224
- userId: { default: null },
225
- label: { default: "Click me" },
226
- });
227
- ```
228
-
229
- Pass them from the parent template:
230
-
231
- ```html
232
- <UserCard :userId="user.id" label="Profile" />
233
- ```
234
-
235
- ## 🎭 Rendering Strategies
236
-
237
- Configured via `metadata` in `<script server>`.
238
-
239
- ### SSR — Server-Side Rendering (default)
240
-
241
- Rendered fresh on every request. Best for dynamic, personalised or SEO-critical pages.
242
-
243
- ```html
244
- <script server>
245
- const metadata = { title: "Live Data" };
246
-
247
- async function getData() {
248
- const data = await fetch("https://api.example.com/data").then(r => r.json());
249
- return { data };
250
- }
251
- </script>
252
-
253
- <template>
254
- <h1>{{data.title}}</h1>
255
- </template>
256
- ```
257
-
258
- ### CSR — Client-Side Rendering
259
-
260
- No server-rendered HTML. The page fetches its own data in the browser. Use for highly interactive or authenticated areas.
261
-
262
- ```html
263
- <script client>
264
- import { reactive } from "vex/reactive";
265
-
266
- const data = reactive(null);
267
-
268
- fetch("/api/data").then(r => r.json()).then(v => data.value = v);
269
- </script>
270
-
271
- <template>
272
- <div x-if="data.value">
273
- <h1>{{data.value.title}}</h1>
274
- </div>
275
- <div x-if="!data.value">Loading…</div>
276
- </template>
277
- ```
278
-
279
- ### SSG — Static Site Generation
280
-
281
- Rendered once and cached forever. Best for content that rarely changes.
282
-
283
- ```html
284
- <script server>
285
- const metadata = { title: "Docs", static: true };
286
-
287
- async function getData() {
288
- return { content: await fetchDocs() };
289
- }
290
- </script>
291
- ```
292
-
293
- ### ISR — Incremental Static Regeneration
294
-
295
- Cached but automatically regenerated after N seconds. Best of speed and freshness.
296
-
297
- ```html
298
- <script server>
299
- const metadata = {
300
- title: "Weather",
301
- revalidate: 60, // regenerate every 60 s
302
- };
303
-
304
- async function getData({ req }) {
305
- const { city } = req.params;
306
- const weather = await fetchWeather(city);
307
- return { city, weather };
308
- }
309
- </script>
310
- ```
311
-
312
- **`revalidate` values:**
313
-
314
- | Value | Behaviour |
315
- |-------|-----------|
316
- | `10` (number) | Regenerate after N seconds |
317
- | `true` | Regenerate after 60 s |
318
- | `0` | Stale-while-revalidate (serve cache, regenerate in background) |
319
- | `false` / `"never"` | Pure SSG — never regenerate |
320
- | _(omitted)_ | SSR — no caching |
321
-
322
- ## 📐 Layouts
323
-
324
- ### Root layout
325
-
326
- `pages/layout.vex` wraps every page:
327
-
328
- ```html
329
- <script server>
330
- const props = xprops({ children: { default: "" } });
331
- </script>
332
-
333
- <template>
334
- <header>
335
- <nav>
336
- <a href="/" data-prefetch>Home</a>
337
- <a href="/about" data-prefetch>About</a>
338
- </nav>
339
- </header>
340
- <main>{{props.children}}</main>
341
- <footer>© 2026</footer>
342
- </template>
343
- ```
344
-
345
- ### Nested layouts
346
-
347
- Add a `layout.vex` inside any subdirectory:
348
-
349
- ```
350
- pages/
351
- layout.vex ← wraps everything
352
- docs/
353
- layout.vex ← wraps /docs/* only
354
- page.vex
355
- getting-started/page.vex
356
- ```
357
-
358
- ## ⏳ Suspense (Streaming)
50
+ ## Documentation
359
51
 
360
- Streams a fallback immediately while a slow component loads:
361
-
362
- ```html
363
- <script server>
364
- import SlowCard from "@/components/slow-card.vex";
365
- import SkeletonCard from "@/components/skeleton-card.vex";
366
- </script>
367
-
368
- <template>
369
- <Suspense :fallback="<SkeletonCard />">
370
- <SlowCard :userId="1" />
371
- </Suspense>
372
- </template>
373
- ```
374
-
375
- The server sends the skeleton on the first flush, then replaces it with the real content via a streamed `<template>` tag when it resolves.
376
-
377
- ## 🔄 Reactive System
378
-
379
- Mirrors Vue 3's Composition API. Import from `vex/reactive` in `<script client>` blocks.
380
-
381
- ### `reactive(value)`
382
-
383
- ```js
384
- import { reactive } from "vex/reactive";
385
-
386
- // Primitives → access via .value
387
- const count = reactive(0);
388
- count.value++;
389
-
390
- // Objects → direct property access
391
- const state = reactive({ x: 1, name: "Alice" });
392
- state.x++;
393
- state.name = "Bob";
394
- ```
395
-
396
- ### `computed(getter)`
397
-
398
- ```js
399
- import { reactive, computed } from "vex/reactive";
400
-
401
- const price = reactive(100);
402
- const qty = reactive(2);
403
- const total = computed(() => price.value * qty.value);
404
-
405
- console.log(total.value); // 200
406
- price.value = 150;
407
- console.log(total.value); // 300
408
- ```
409
-
410
- ### `effect(fn)`
411
-
412
- Runs immediately and re-runs whenever its reactive dependencies change.
413
-
414
- ```js
415
- import { reactive, effect } from "vex/reactive";
416
-
417
- const count = reactive(0);
418
- const stop = effect(() => document.title = `Count: ${count.value}`);
419
-
420
- count.value++; // effect re-runs
421
- stop(); // cleanup
422
- ```
423
-
424
- ### `watch(source, callback)`
425
-
426
- Runs only when the source changes (not on creation).
427
-
428
- ```js
429
- import { reactive, watch } from "vex/reactive";
430
-
431
- const count = reactive(0);
432
- watch(() => count.value, (newVal, oldVal) => {
433
- console.log(`${oldVal} → ${newVal}`);
434
- });
435
- ```
436
-
437
- ### Reactivity summary
438
-
439
- | Function | Auto-runs | Returns |
440
- |----------|-----------|---------|
441
- | `reactive()` | No | Proxy |
442
- | `effect()` | Yes (immediately + on change) | Cleanup fn |
443
- | `computed()` | On dependency change | Reactive value |
444
- | `watch()` | Only on change | — |
445
-
446
- ## 📝 Template Syntax
447
-
448
- | Syntax | Description |
449
- |--------|-------------|
450
- | `{{expr}}` | Interpolation |
451
- | `x-if="expr"` | Conditional rendering |
452
- | `x-for="item in items"` | List rendering |
453
- | `x-show="expr"` | Toggle `display` |
454
- | `:prop="expr"` | Dynamic prop/attribute |
455
- | `@click="handler"` | Event (client only) |
456
-
457
- ```html
458
- <template>
459
- <h1>Hello, {{name}}</h1>
460
-
461
- <ul>
462
- <li x-for="item in items">{{item}}</li>
463
- </ul>
464
-
465
- <div x-if="isVisible">Visible</div>
466
-
467
- <button :disabled="count.value <= 0" @click="count.value--">-</button>
468
- </template>
469
- ```
470
-
471
- > Keep logic in `getData` rather than inline expressions. Ternaries and filters are not supported in templates.
472
-
473
- ## 🛣️ Routing
474
-
475
- ### File-based routes
476
-
477
- | File | Route |
478
- |------|-------|
479
- | `pages/page.vex` | `/` |
480
- | `pages/about/page.vex` | `/about` |
481
- | `pages/users/[id]/page.vex` | `/users/:id` |
482
- | `pages/not-found/page.vex` | 404 |
483
- | `pages/error/page.vex` | 500 |
484
-
485
- ### Dynamic routes
486
-
487
- ```html
488
- <!-- pages/users/[id]/page.vex -->
489
- <script server>
490
- async function getData({ req }) {
491
- const { id } = req.params;
492
- return { user: await fetchUser(id) };
493
- }
494
- </script>
495
-
496
- <template>
497
- <h1>{{user.name}}</h1>
498
- </template>
499
- ```
500
-
501
- ### Pre-generate dynamic pages (SSG)
502
-
503
- ```js
504
- // inside <script server>
505
- export async function getStaticPaths() {
506
- return [
507
- { params: { id: "1" } },
508
- { params: { id: "2" } },
509
- ];
510
- }
511
- ```
512
-
513
- ### Client-side navigation
514
-
515
- ```js
516
- window.app.navigate("/about");
517
- ```
518
-
519
- ### Route & query params (client)
520
-
521
- ```js
522
- import { useRouteParams } from "vex/navigation";
523
- import { useQueryParams } from "vex/navigation";
524
-
525
- const { id } = useRouteParams(); // reactive, updates on navigation
526
- const { search } = useQueryParams();
527
- ```
528
-
529
- ## ⚡ Prefetching
530
-
531
- Add `data-prefetch` to any `<a>` tag to prefetch the page when the link enters the viewport:
532
-
533
- ```html
534
- <a href="/about" data-prefetch>About</a>
535
- ```
536
-
537
- The page component is loaded in the background; navigation to it is instant.
538
-
539
- ## 🎨 Styling
540
-
541
- The framework uses **Tailwind CSS v4**. The dev script watches `src/input.css` and outputs to `public/styles.css`.
542
-
543
- ```css
544
- /* src/input.css */
545
- @import "tailwindcss";
546
- ```
547
-
548
- Reference the stylesheet in `root.html`:
549
-
550
- ```html
551
- <link rel="stylesheet" href="/styles.css">
552
- ```
553
-
554
- ## 🔧 Framework API
555
-
556
- ### Import conventions
557
-
558
- | Pattern | Example | Behaviour |
559
- |---------|---------|-----------|
560
- | `vex/*` | `import { reactive } from "vex/reactive"` | Framework singleton — shared instance across all components |
561
- | `@/*` | `import store from "@/utils/store.js"` | Project alias for your source root — also a singleton |
562
- | `./` / `../` | `import { fn } from "./helpers.js"` | Relative user file — also a singleton |
563
- | npm bare specifier | `import { format } from "date-fns"` | Bundled inline by esbuild |
564
-
565
- All user JS files (`@/` and relative) are pre-bundled at startup: npm packages are inlined, while `vex/*`, `@/*`, and relative imports stay external. The browser's ES module cache guarantees every import of the same file returns the same instance — enabling shared reactive state across components without a dedicated store library.
566
-
567
- ### Client script imports
568
-
569
- | Import | Description |
570
- |--------|-------------|
571
- | `vex/reactive` | Reactivity engine (`reactive`, `computed`, `effect`, `watch`) |
572
- | `vex/navigation` | Router utilities (`useRouteParams`, `useQueryParams`) |
573
-
574
- ### Server script hooks
575
-
576
- | Export | Description |
577
- |--------|-------------|
578
- | `async getData({ req, props })` | Fetches data; return value is merged into template scope |
579
- | `metadata` / `async getMetadata({ req, props })` | Page-level config (`title`, `description`, `static`, `revalidate`) |
580
- | `async getStaticPaths()` | Returns `[{ params }]` for pre-rendering dynamic routes |
581
-
582
- ## 📦 Available Scripts
583
-
584
- ```bash
585
- vex dev # Start dev server with HMR (--watch)
586
- vex build # Pre-render pages, generate routes, bundle client JS
587
- vex start # Production server (requires a prior build)
588
- ```
589
-
590
- > `vex start` requires `vex build` to have been run first.
591
-
592
- ## 🏗️ Rendering Flow
593
-
594
- ### SSR (Server-Side Rendering)
595
-
596
- ```mermaid
597
- sequenceDiagram
598
- participant Client
599
- participant Server
600
- participant Router
601
- participant ComponentProcessor
602
- participant Streaming
603
- participant Cache
604
-
605
- Client->>Server: GET /page
606
- Server->>Router: handlePageRequest(req, res, route)
607
- Router->>Router: Check ISR cache
608
-
609
- alt Cache valid
610
- Router->>Cache: getCachedHtml()
611
- Cache-->>Router: html
612
- Router->>Client: Send cached HTML
613
- else No cache / stale
614
- Router->>ComponentProcessor: renderPageWithLayout()
615
- ComponentProcessor->>ComponentProcessor: processHtmlFile → getData → compileTemplate
616
- ComponentProcessor->>Streaming: renderComponents (Suspense boundaries)
617
- Streaming-->>ComponentProcessor: { html, suspenseComponents }
618
- ComponentProcessor-->>Router: { html, suspenseComponents }
619
-
620
- alt No Suspense
621
- Router->>Client: Send full HTML
622
- Router->>Cache: Save (if ISR)
623
- else Has Suspense
624
- Router->>Client: Stream initial HTML (skeleton)
625
- loop Each suspense component
626
- Router->>Streaming: renderSuspenseComponent()
627
- Streaming-->>Router: Resolved HTML
628
- Router->>Client: Stream replacement chunk
629
- end
630
- Router->>Client: End stream
631
- Router->>Cache: Save complete HTML (if ISR)
632
- end
633
- end
634
- ```
635
-
636
- ### ISR (Incremental Static Regeneration)
637
-
638
- ```mermaid
639
- sequenceDiagram
640
- participant Client
641
- participant Router
642
- participant Cache
643
- participant FileSystem
644
-
645
- Client->>Router: GET /page
646
- Router->>Cache: getCachedHtml(url, revalidateSeconds)
647
- Cache->>FileSystem: Read HTML + meta
648
-
649
- alt Cache exists and valid
650
- Cache-->>Router: { html, isStale: false }
651
- Router->>Client: Instant cached response
652
- else Cache stale or missing
653
- Router->>Router: renderPageWithLayout()
654
- Router->>Client: Fresh response
655
- Router->>FileSystem: saveComponentHtmlDisk()
656
- end
657
- ```
658
-
659
- ### Server Startup
660
-
661
- ```mermaid
662
- sequenceDiagram
663
- participant CLI
664
- participant index.js
665
- participant ComponentProcessor
666
-
667
- CLI->>index.js: node .../server/index.js
668
-
669
- alt NODE_ENV=production
670
- index.js->>index.js: import .vexjs/_routes.js
671
- note right of index.js: Fails if pnpm build not run
672
- else development
673
- index.js->>ComponentProcessor: build()
674
- ComponentProcessor-->>index.js: serverRoutes
675
- end
676
-
677
- index.js->>index.js: registerSSRRoutes(app, serverRoutes)
678
- index.js->>CLI: Listening on :3001
679
- ```
52
+ - [Routing & Project Structure](docs/routing.md)
53
+ - [Components & Layouts](docs/components.md)
54
+ - [Rendering Strategies](docs/rendering.md)
55
+ - [Reactive System](docs/reactivity.md)
56
+ - [Template Syntax](docs/templates.md)
57
+ - [Configuration & API](docs/configuration.md)
58
+ - [Deploy to Vercel](DEPLOY.md)
680
59
 
681
- ## 🗺️ Roadmap
60
+ ## Roadmap
682
61
 
683
62
  - [x] File-based routing with dynamic segments
684
63
  - [x] SSR / CSR / SSG / ISR rendering strategies
@@ -697,17 +76,16 @@ sequenceDiagram
697
76
  - [x] `vex.config.json` — configurable `srcDir` and `watchIgnore`
698
77
  - [x] Published to npm as `@cfdez11/vex`
699
78
  - [x] VS Code extension with syntax highlighting and go-to-definition
700
- - [ ] Refactor client component prop pipeline: evaluate `:props` expressions directly in `streaming.js` with the page scope instead of going through `template.js` → `String()` → `JSON.stringify` → `JSON.parse`. Eliminates the unnecessary serialization round-trip for array/object props.
701
- - [ ] esbuild minification: enable `minify: true` in `generateClientBundle` and `buildUserFile` for production builds. Zero-cost win — esbuild is already in the pipeline.
702
- - [ ] esbuild source maps: enable `sourcemap: "inline"` in dev mode so browser devtools show original source instead of the bundle.
703
- - [ ] esbuild browser target: add `target: ["es2020"]` (or configurable) to transpile modern syntax for broader browser compatibility.
704
- - [ ] esbuild code splitting: shared npm packages (e.g. `date-fns`) are currently inlined into every component bundle separately. Code splitting would emit a shared chunk, reducing total download size when multiple components share the same dependency.
79
+ - [ ] Refactor client component prop pipeline
80
+ - [ ] esbuild minification for production builds
81
+ - [ ] esbuild source maps in dev mode
82
+ - [ ] esbuild browser target config
83
+ - [ ] esbuild code splitting for shared dependencies
705
84
  - [ ] Devtools
706
- - [ ] Typescript in framework
707
- - [ ] Allow typescript to devs
708
- - [ ] Improve extension (hightlight, redirects, etc)
709
- - [ ] Create theme syntax
710
- - [ ] Create docs page
85
+ - [ ] TypeScript support (framework + user code)
86
+ - [ ] Improved VS Code extension
87
+ - [ ] Theme syntax
88
+ - [ ] Docs page
711
89
  - [ ] Authentication middleware
712
90
  - [ ] CDN cache integration
713
91
  - [ ] Fix Suspense marker replacement with multi-root templates
@@ -1,4 +1,4 @@
1
- import fs from"fs/promises";import{watch,existsSync,statSync,readFileSync}from"fs";import path from"path";import crypto from"crypto";import{fileURLToPath,pathToFileURL}from"url";import esbuild from"esbuild";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),FRAMEWORK_DIR=path.resolve(__dirname,"..",".."),PROJECT_ROOT=process.cwd(),ROOT_DIR=PROJECT_ROOT;let _vexConfig={};try{_vexConfig=JSON.parse(readFileSync(path.join(PROJECT_ROOT,"vex.config.json"),"utf-8"))}catch{}const SRC_DIR=path.resolve(PROJECT_ROOT,_vexConfig.srcDir||"."),WATCH_IGNORE=new Set(["dist","build","out",".output",".vexjs","public","node_modules",".git",".svn","coverage",".nyc_output",".next",".nuxt",".svelte-kit",".astro","tmp","temp",".cache",".claude",...(_vexConfig.watchIgnore||[]).filter(p=>!/[\/\*\.]/.test(p))]),WATCH_IGNORE_FILES=["server.js",...(_vexConfig.watchIgnore||[]).filter(p=>/[\/\*\.]/.test(p))],PAGES_DIR=path.resolve(SRC_DIR,"pages"),SERVER_APP_DIR=path.join(FRAMEWORK_DIR,"server"),CLIENT_DIR=path.join(FRAMEWORK_DIR,"client"),CLIENT_SERVICES_DIR=path.join(CLIENT_DIR,"services"),GENERATED_DIR=path.join(PROJECT_ROOT,".vexjs"),CACHE_DIR=path.join(GENERATED_DIR,"_cache"),CLIENT_COMPONENTS_DIR=path.join(GENERATED_DIR,"_components"),USER_GENERATED_DIR=path.join(GENERATED_DIR,"user"),ROOT_HTML_USER=path.join(PROJECT_ROOT,"root.html"),ROOT_HTML_DEFAULT=path.join(FRAMEWORK_DIR,"server","root.html"),ROOT_HTML_DIR=ROOT_HTML_USER;async function minifyServicesDir(src,dest){const jsFiles=[],otherFiles=[],collect=async(srcDir,destDir)=>{const entries=await fs.readdir(srcDir,{withFileTypes:!0});await Promise.all(entries.map(async entry=>{const srcPath=path.join(srcDir,entry.name),destPath=path.join(destDir,entry.name);entry.isDirectory()?(await fs.mkdir(destPath,{recursive:!0}),await collect(srcPath,destPath)):entry.name.endsWith(".js")?jsFiles.push({in:srcPath,out:destPath}):otherFiles.push({src:srcPath,dest:destPath})}))};await collect(src,dest),await Promise.all([esbuild.build({entryPoints:jsFiles.map(f=>f.in),bundle:!1,format:"esm",platform:"browser",minify:!0,legalComments:"none",outdir:dest,outbase:src,logLevel:"silent"}),...otherFiles.map(f=>fs.copyFile(f.src,f.dest))])}async function initializeDirectories(){try{const servicesDir=path.join(GENERATED_DIR,"services");return await Promise.all([fs.mkdir(GENERATED_DIR,{recursive:!0}),fs.mkdir(CACHE_DIR,{recursive:!0}),fs.mkdir(CLIENT_COMPONENTS_DIR,{recursive:!0}),fs.mkdir(USER_GENERATED_DIR,{recursive:!0}),fs.mkdir(servicesDir,{recursive:!0})]),process.env.NODE_ENV==="production"?await minifyServicesDir(CLIENT_SERVICES_DIR,servicesDir):await fs.cp(CLIENT_SERVICES_DIR,servicesDir,{recursive:!0}),!0}catch(err){console.error("Failed to create cache directory:",err)}}function adjustClientModulePath(modulePath,importStatement,componentFilePath=null){if(modulePath.startsWith("/_vexjs/"))return{path:modulePath,importStatement};const isRelative=(modulePath.startsWith("./")||modulePath.startsWith("../"))&&componentFilePath,isAtAlias=modulePath.startsWith("@/")||modulePath==="@";if(isRelative||isAtAlias){let resolvedPath;if(isAtAlias)resolvedPath=path.resolve(SRC_DIR,modulePath.replace(/^@\//,"").replace(/^@$/,""));else{const componentDir=path.dirname(componentFilePath);resolvedPath=path.resolve(componentDir,modulePath)}path.extname(resolvedPath)||(existsSync(resolvedPath+".js")?resolvedPath+=".js":existsSync(path.join(resolvedPath,"index.js"))?resolvedPath=path.join(resolvedPath,"index.js"):resolvedPath+=".js");const adjustedPath2=`/_vexjs/user/${path.relative(SRC_DIR,resolvedPath).replace(/\\/g,"/")}`,adjustedImportStatement2=importStatement.replace(modulePath,adjustedPath2);return{path:adjustedPath2,importStatement:adjustedImportStatement2}}let relative=modulePath.replace(/^vex\//,""),adjustedPath=`/_vexjs/services/${relative}`;const fsPath=path.join(CLIENT_SERVICES_DIR,relative);existsSync(fsPath)&&statSync(fsPath).isDirectory()?adjustedPath+="/index.js":path.extname(adjustedPath)||(adjustedPath+=".js");const adjustedImportStatement=importStatement.replace(modulePath,adjustedPath);return{path:adjustedPath,importStatement:adjustedImportStatement}}function getRelativePath(from,to){return path.relative(from,to)}function getDirectoryName(filePath){return path.dirname(filePath)}const layoutPathsCache=new Map;process.env.NODE_ENV!=="production"&&watch(PAGES_DIR,{recursive:!0},(_,filename)=>{(filename==="layout.vex"||filename?.endsWith(`${path.sep}layout.vex`))&&layoutPathsCache.clear()});async function _getLayoutPaths(pagePath){const layouts=[],relativePath=getRelativePath(PAGES_DIR,pagePath),pathSegments=getDirectoryName(relativePath).split(path.sep),baseLayout=path.join(PAGES_DIR,"layout.vex");await fileExists(baseLayout)&&layouts.push(baseLayout);let currentPath=PAGES_DIR;for(const segment of pathSegments){if(segment==="."||segment==="..")continue;currentPath=path.join(currentPath,segment);const layoutPath=path.join(currentPath,"layout.vex");await fileExists(layoutPath)&&layouts.push(layoutPath)}return layouts}async function getLayoutPaths(pagePath){if(layoutPathsCache.has(pagePath))return layoutPathsCache.get(pagePath);const result=await _getLayoutPaths(pagePath);return layoutPathsCache.set(pagePath,result),result}function formatFileContent(content){return content.trim()}async function writeFile(filePath,content){const formattedContent=formatFileContent(content);return fs.writeFile(filePath,formattedContent,"utf-8")}function readFile(filePath){return fs.readFile(filePath,"utf-8")}async function fileExists(filePath){try{return await fs.access(filePath),!0}catch{return!1}}function getAutogeneratedComponentName(componentPath){return`_${componentPath.replace(ROOT_DIR+path.sep,"").split(path.sep).filter(Boolean).join("_").replaceAll(".vex","").replaceAll(path.sep,"_").replaceAll("-","_").replaceAll(":","")}`}function generateComponentId(componentPath,options={}){const{length=8,prefix=!0}=options,relativePath=componentPath.replace(ROOT_DIR+path.sep,""),hash=crypto.createHash("sha256").update(relativePath).digest("hex").slice(0,length),baseName=getAutogeneratedComponentName(componentPath).replace(/^_/,"");return prefix?`_${baseName}_${hash}`:hash}const getPagePath=pageName=>path.resolve(PAGES_DIR,pageName,"page.vex"),getRootTemplate=async()=>{try{return await fs.access(ROOT_HTML_USER),await fs.readFile(ROOT_HTML_USER,"utf-8")}catch{return await fs.readFile(ROOT_HTML_DEFAULT,"utf-8")}};async function readDirectoryRecursive(dir){const entries=await fs.readdir(dir,{withFileTypes:!0}),files=[];for(const entry of entries){const fullpath=path.join(dir,entry.name);entry.isDirectory()?files.push(...await readDirectoryRecursive(fullpath)):files.push({path:fullpath.replace(ROOT_DIR,""),fullpath,name:entry.name})}return files}const getComponentNameFromPath=(fullFilepath,fileName)=>{const filePath=fullFilepath.replace(ROOT_DIR+path.sep,"");if(filePath.startsWith(path.join("pages",path.sep))){const segments=filePath.split(path.sep);return segments.length===2?segments[0].replace(".vex",""):segments[segments.length-2].replace(".vex","")}return fileName.replace(".vex","")};async function getComponentHtmlDisk({componentPath}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",[existsHtml,existsMeta]=await Promise.all([fileExists(filePath),fileExists(metaPath)]);if(!existsMeta||!existsHtml)return{html:null,meta:null};const[html,meta]=await Promise.all([fs.readFile(filePath,"utf-8"),fs.readFile(metaPath,"utf-8")]).then(([htmlContent,metaContent])=>[htmlContent,JSON.parse(metaContent)]);return{html,meta}}async function saveComponentHtmlDisk({componentPath,html}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",meta={generatedAt:Date.now(),isStale:!1,path:componentPath};await Promise.all([writeFile(filePath,html,"utf-8"),writeFile(metaPath,JSON.stringify(meta),"utf-8")])}async function markComponentHtmlStale({componentPath}){const metaPath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html")+".meta.json";if(!await fileExists(metaPath))return;const meta=JSON.parse(await fs.readFile(metaPath,"utf-8"));meta.isStale=!0,await writeFile(metaPath,JSON.stringify(meta),"utf-8")}async function saveServerRoutesFile(serverRoutes){await writeFile(path.join(GENERATED_DIR,"_routes.js"),`// Auto-generated by prebuild \u2014 do not edit manually.
1
+ import fs from"fs/promises";import{watch,existsSync,statSync,readFileSync}from"fs";import path from"path";import crypto from"crypto";import{fileURLToPath,pathToFileURL}from"url";import esbuild from"esbuild";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),FRAMEWORK_DIR=path.resolve(__dirname,"..",".."),PROJECT_ROOT=process.cwd(),ROOT_DIR=PROJECT_ROOT;let _vexConfig={};try{_vexConfig=JSON.parse(readFileSync(path.join(PROJECT_ROOT,"vex.config.json"),"utf-8"))}catch{}const SRC_DIR=path.resolve(PROJECT_ROOT,_vexConfig.srcDir||"."),WATCH_IGNORE=new Set(["dist","build","out",".output",".vexjs","public","node_modules",".git",".svn","coverage",".nyc_output",".next",".nuxt",".svelte-kit",".astro","tmp","temp",".cache",".claude",...(_vexConfig.watchIgnore||[]).filter(p=>!/[\/\*\.]/.test(p))]),WATCH_IGNORE_FILES=(_vexConfig.watchIgnore||[]).filter(p=>/[\/\*\.]/.test(p)),PAGES_DIR=path.resolve(SRC_DIR,"pages"),SERVER_APP_DIR=path.join(FRAMEWORK_DIR,"server"),CLIENT_DIR=path.join(FRAMEWORK_DIR,"client"),CLIENT_SERVICES_DIR=path.join(CLIENT_DIR,"services"),GENERATED_DIR=path.join(PROJECT_ROOT,".vexjs"),CACHE_DIR=path.join(GENERATED_DIR,"_cache"),CLIENT_COMPONENTS_DIR=path.join(GENERATED_DIR,"_components"),USER_GENERATED_DIR=path.join(GENERATED_DIR,"user"),ROOT_HTML_USER=path.join(PROJECT_ROOT,"root.html"),ROOT_HTML_DEFAULT=path.join(FRAMEWORK_DIR,"server","root.html"),ROOT_HTML_DIR=ROOT_HTML_USER;async function minifyServicesDir(src,dest){const jsFiles=[],otherFiles=[],collect=async(srcDir,destDir)=>{const entries=await fs.readdir(srcDir,{withFileTypes:!0});await Promise.all(entries.map(async entry=>{const srcPath=path.join(srcDir,entry.name),destPath=path.join(destDir,entry.name);entry.isDirectory()?(await fs.mkdir(destPath,{recursive:!0}),await collect(srcPath,destPath)):entry.name.endsWith(".js")?jsFiles.push({in:srcPath,out:destPath}):otherFiles.push({src:srcPath,dest:destPath})}))};await collect(src,dest),await Promise.all([esbuild.build({entryPoints:jsFiles.map(f=>f.in),bundle:!1,format:"esm",platform:"browser",minify:!0,legalComments:"none",outdir:dest,outbase:src,logLevel:"silent"}),...otherFiles.map(f=>fs.copyFile(f.src,f.dest))])}async function initializeDirectories(){try{const servicesDir=path.join(GENERATED_DIR,"services");return await Promise.all([fs.mkdir(GENERATED_DIR,{recursive:!0}),fs.mkdir(CACHE_DIR,{recursive:!0}),fs.mkdir(CLIENT_COMPONENTS_DIR,{recursive:!0}),fs.mkdir(USER_GENERATED_DIR,{recursive:!0}),fs.mkdir(servicesDir,{recursive:!0})]),process.env.NODE_ENV==="production"?await minifyServicesDir(CLIENT_SERVICES_DIR,servicesDir):await fs.cp(CLIENT_SERVICES_DIR,servicesDir,{recursive:!0}),!0}catch(err){console.error("Failed to create cache directory:",err)}}function adjustClientModulePath(modulePath,importStatement,componentFilePath=null){if(modulePath.startsWith("/_vexjs/"))return{path:modulePath,importStatement};const isRelative=(modulePath.startsWith("./")||modulePath.startsWith("../"))&&componentFilePath,isAtAlias=modulePath.startsWith("@/")||modulePath==="@";if(isRelative||isAtAlias){let resolvedPath;if(isAtAlias)resolvedPath=path.resolve(SRC_DIR,modulePath.replace(/^@\//,"").replace(/^@$/,""));else{const componentDir=path.dirname(componentFilePath);resolvedPath=path.resolve(componentDir,modulePath)}path.extname(resolvedPath)||(existsSync(resolvedPath+".js")?resolvedPath+=".js":existsSync(path.join(resolvedPath,"index.js"))?resolvedPath=path.join(resolvedPath,"index.js"):resolvedPath+=".js");const adjustedPath2=`/_vexjs/user/${path.relative(SRC_DIR,resolvedPath).replace(/\\/g,"/")}`,adjustedImportStatement2=importStatement.replace(modulePath,adjustedPath2);return{path:adjustedPath2,importStatement:adjustedImportStatement2}}let relative=modulePath.replace(/^vex\//,""),adjustedPath=`/_vexjs/services/${relative}`;const fsPath=path.join(CLIENT_SERVICES_DIR,relative);existsSync(fsPath)&&statSync(fsPath).isDirectory()?adjustedPath+="/index.js":path.extname(adjustedPath)||(adjustedPath+=".js");const adjustedImportStatement=importStatement.replace(modulePath,adjustedPath);return{path:adjustedPath,importStatement:adjustedImportStatement}}function getRelativePath(from,to){return path.relative(from,to)}function getDirectoryName(filePath){return path.dirname(filePath)}const layoutPathsCache=new Map;process.env.NODE_ENV!=="production"&&watch(PAGES_DIR,{recursive:!0},(_,filename)=>{(filename==="layout.vex"||filename?.endsWith(`${path.sep}layout.vex`))&&layoutPathsCache.clear()});async function _getLayoutPaths(pagePath){const layouts=[],relativePath=getRelativePath(PAGES_DIR,pagePath),pathSegments=getDirectoryName(relativePath).split(path.sep),baseLayout=path.join(PAGES_DIR,"layout.vex");await fileExists(baseLayout)&&layouts.push(baseLayout);let currentPath=PAGES_DIR;for(const segment of pathSegments){if(segment==="."||segment==="..")continue;currentPath=path.join(currentPath,segment);const layoutPath=path.join(currentPath,"layout.vex");await fileExists(layoutPath)&&layouts.push(layoutPath)}return layouts}async function getLayoutPaths(pagePath){if(layoutPathsCache.has(pagePath))return layoutPathsCache.get(pagePath);const result=await _getLayoutPaths(pagePath);return layoutPathsCache.set(pagePath,result),result}function formatFileContent(content){return content.trim()}async function writeFile(filePath,content){const formattedContent=formatFileContent(content);return fs.writeFile(filePath,formattedContent,"utf-8")}function readFile(filePath){return fs.readFile(filePath,"utf-8")}async function fileExists(filePath){try{return await fs.access(filePath),!0}catch{return!1}}function getAutogeneratedComponentName(componentPath){return`_${componentPath.replace(ROOT_DIR+path.sep,"").split(path.sep).filter(Boolean).join("_").replaceAll(".vex","").replaceAll(path.sep,"_").replaceAll("-","_").replaceAll(":","")}`}function generateComponentId(componentPath,options={}){const{length=8,prefix=!0}=options,relativePath=componentPath.replace(ROOT_DIR+path.sep,""),hash=crypto.createHash("sha256").update(relativePath).digest("hex").slice(0,length),baseName=getAutogeneratedComponentName(componentPath).replace(/^_/,"");return prefix?`_${baseName}_${hash}`:hash}const getPagePath=pageName=>path.resolve(PAGES_DIR,pageName,"page.vex"),getRootTemplate=async()=>{try{return await fs.access(ROOT_HTML_USER),await fs.readFile(ROOT_HTML_USER,"utf-8")}catch{return await fs.readFile(ROOT_HTML_DEFAULT,"utf-8")}};async function readDirectoryRecursive(dir){const entries=await fs.readdir(dir,{withFileTypes:!0}),files=[];for(const entry of entries){const fullpath=path.join(dir,entry.name);entry.isDirectory()?files.push(...await readDirectoryRecursive(fullpath)):files.push({path:fullpath.replace(ROOT_DIR,""),fullpath,name:entry.name})}return files}const getComponentNameFromPath=(fullFilepath,fileName)=>{const filePath=fullFilepath.replace(ROOT_DIR+path.sep,"");if(filePath.startsWith(path.join("pages",path.sep))){const segments=filePath.split(path.sep);return segments.length===2?segments[0].replace(".vex",""):segments[segments.length-2].replace(".vex","")}return fileName.replace(".vex","")};async function getComponentHtmlDisk({componentPath}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",[existsHtml,existsMeta]=await Promise.all([fileExists(filePath),fileExists(metaPath)]);if(!existsMeta||!existsHtml)return{html:null,meta:null};const[html,meta]=await Promise.all([fs.readFile(filePath,"utf-8"),fs.readFile(metaPath,"utf-8")]).then(([htmlContent,metaContent])=>[htmlContent,JSON.parse(metaContent)]);return{html,meta}}async function saveComponentHtmlDisk({componentPath,html}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",meta={generatedAt:Date.now(),isStale:!1,path:componentPath};await Promise.all([writeFile(filePath,html,"utf-8"),writeFile(metaPath,JSON.stringify(meta),"utf-8")])}async function markComponentHtmlStale({componentPath}){const metaPath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html")+".meta.json";if(!await fileExists(metaPath))return;const meta=JSON.parse(await fs.readFile(metaPath,"utf-8"));meta.isStale=!0,await writeFile(metaPath,JSON.stringify(meta),"utf-8")}async function saveServerRoutesFile(serverRoutes){await writeFile(path.join(GENERATED_DIR,"_routes.js"),`// Auto-generated by prebuild \u2014 do not edit manually.
2
2
  export const routes = ${JSON.stringify(serverRoutes,null,2)};
3
3
  `)}async function saveClientRoutesFile(clientRoutes){const clientFileCode=`
4
4
  import { loadRouteComponent } from './cache.js';
@@ -25,4 +25,4 @@ export const routes = ${JSON.stringify(serverRoutes,null,2)};
25
25
  ${clientRoutes.join(`,
26
26
  `)}
27
27
  ];
28
- `;await writeFile(path.join(GENERATED_DIR,"services","_routes.js"),clientFileCode)}function getOriginalRoutePath(filePath){let route=filePath.replace(PAGES_DIR,"").replace("/page.vex","");return route.startsWith("/")||(route="/"+route),route}async function getPageFiles({layouts=!1}={}){return(await readDirectoryRecursive(PAGES_DIR)).filter(file=>file.fullpath.endsWith("page.vex")||layouts&&file.name==="layout.vex")}function getRoutePath(filePath){let route=filePath.replace(PAGES_DIR,"").replace("/page.vex","");return route=route.replace(/\[([^\]]+)\]/g,":$1"),route.startsWith("/")||(route="/"+route),route}async function saveClientComponentModule(componentName,jsModuleCode){const outputPath=path.join(CLIENT_COMPONENTS_DIR,`${componentName}.js`);await writeFile(outputPath,jsModuleCode,"utf-8")}async function getImportData(importPath,callerFilePath=null){let resolvedPath;importPath.startsWith("vex/server/")?resolvedPath=path.resolve(FRAMEWORK_DIR,importPath.replace("vex/server/","server/")):importPath.startsWith("vex/")?resolvedPath=path.resolve(FRAMEWORK_DIR,"client/services",importPath.replace("vex/","")):importPath.startsWith("@/")||importPath==="@"?resolvedPath=path.resolve(SRC_DIR,importPath.replace(/^@\//,"").replace(/^@$/,"")):(importPath.startsWith("./")||importPath.startsWith("../"))&&callerFilePath?resolvedPath=path.resolve(path.dirname(callerFilePath),importPath):resolvedPath=path.resolve(ROOT_DIR,importPath),existsSync(resolvedPath)&&statSync(resolvedPath).isDirectory()&&(resolvedPath=path.join(resolvedPath,"index.js"));const fileUrl=pathToFileURL(resolvedPath).href;return{path:resolvedPath,fileUrl,importPath}}export{CLIENT_COMPONENTS_DIR,CLIENT_DIR,CLIENT_SERVICES_DIR,PAGES_DIR,PROJECT_ROOT,ROOT_HTML_DIR,SERVER_APP_DIR,SRC_DIR,USER_GENERATED_DIR,WATCH_IGNORE,WATCH_IGNORE_FILES,adjustClientModulePath,fileExists,generateComponentId,getComponentHtmlDisk,getComponentNameFromPath,getImportData,getLayoutPaths,getOriginalRoutePath,getPageFiles,getPagePath,getRelativePath,getRootTemplate,getRoutePath,initializeDirectories,markComponentHtmlStale,readDirectoryRecursive,readFile,saveClientComponentModule,saveClientRoutesFile,saveComponentHtmlDisk,saveServerRoutesFile,writeFile};
28
+ `;await writeFile(path.join(GENERATED_DIR,"services","_routes.js"),clientFileCode)}function getOriginalRoutePath(filePath){let route=filePath.replace(PAGES_DIR,"").replace("/page.vex","");return route.startsWith("/")||(route="/"+route),route}async function getPageFiles({layouts=!1}={}){return(await readDirectoryRecursive(PAGES_DIR)).filter(file=>file.fullpath.endsWith("page.vex")||layouts&&file.name==="layout.vex")}function getRoutePath(filePath){let route=filePath.replace(PAGES_DIR,"").replace("/page.vex","");return route=route.replace(/\[([^\]]+)\]/g,":$1"),route.startsWith("/")||(route="/"+route),route}async function saveClientComponentModule(componentName,jsModuleCode){const outputPath=path.join(CLIENT_COMPONENTS_DIR,`${componentName}.js`);await writeFile(outputPath,jsModuleCode,"utf-8")}async function getImportData(importPath,callerFilePath=null){let resolvedPath;if(importPath.startsWith("vex/server/"))resolvedPath=path.resolve(FRAMEWORK_DIR,importPath.replace("vex/server/","server/"));else if(importPath.startsWith("vex/"))resolvedPath=path.resolve(FRAMEWORK_DIR,"client/services",importPath.replace("vex/",""));else if(importPath.startsWith("@/")||importPath==="@")resolvedPath=path.resolve(SRC_DIR,importPath.replace(/^@\//,"").replace(/^@$/,""));else if((importPath.startsWith("./")||importPath.startsWith("../"))&&callerFilePath)resolvedPath=path.resolve(path.dirname(callerFilePath),importPath);else{if(!importPath.startsWith(".")&&!importPath.startsWith("/"))return{path:importPath,fileUrl:importPath,importPath};resolvedPath=path.resolve(ROOT_DIR,importPath)}resolvedPath&&existsSync(resolvedPath)&&statSync(resolvedPath).isDirectory()&&(resolvedPath=path.join(resolvedPath,"index.js"));const fileUrl=pathToFileURL(resolvedPath).href;return{path:resolvedPath,fileUrl,importPath}}export{CLIENT_COMPONENTS_DIR,CLIENT_DIR,CLIENT_SERVICES_DIR,PAGES_DIR,PROJECT_ROOT,ROOT_HTML_DIR,SERVER_APP_DIR,SRC_DIR,USER_GENERATED_DIR,WATCH_IGNORE,WATCH_IGNORE_FILES,adjustClientModulePath,fileExists,generateComponentId,getComponentHtmlDisk,getComponentNameFromPath,getImportData,getLayoutPaths,getOriginalRoutePath,getPageFiles,getPagePath,getRelativePath,getRootTemplate,getRoutePath,initializeDirectories,markComponentHtmlStale,readDirectoryRecursive,readFile,saveClientComponentModule,saveClientRoutesFile,saveComponentHtmlDisk,saveServerRoutesFile,writeFile};
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@cfdez11/vex",
3
- "version": "0.10.5",
3
+ "version": "0.10.9",
4
4
  "description": "A vanilla JavaScript meta-framework with file-based routing, SSR/CSR/SSG/ISR and Vue-like reactivity",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/tu-usuario/vexjs"
8
+ },
9
+ "homepage": "https://www.vexjs.com/",
5
10
  "type": "module",
6
11
  "main": "./dist/server/index.js",
7
12
  "exports": {