@inglorious/ssx 1.4.2 → 1.4.4

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
@@ -1,724 +1,746 @@
1
- # @inglorious/ssx
2
-
3
- [![NPM version](https://img.shields.io/npm/v/@inglorious/ssx.svg)](https://www.npmjs.com/package/@inglorious/ssx)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
-
6
- **Static Site Xecution** - Build blazing-fast static sites with [@inglorious/web](https://www.npmjs.com/package/@inglorious/web), complete with server-side rendering, client-side hydration, and zero-config routing.
7
-
8
- SSX takes your entity-based web apps and generates optimized static HTML with full hydration support. Think Next.js SSG or Astro, but with the simplicity and predictability of Inglorious Web's entity architecture.
9
-
10
- ---
11
-
12
- ## Why SSX?
13
-
14
- ### ⚡️ Fast by Default
15
-
16
- - **Pre-rendered HTML** - Every page is built at compile time
17
- - **Instant load times** - No waiting for server responses
18
- - **CDN-ready** - Deploy anywhere static files are served
19
- - **Perfect Lighthouse scores** - SEO and performance out of the box
20
-
21
- ### 🎯 Simple Architecture
22
-
23
- - **No server required** - Pure static files
24
- - **No complex build configs** - Convention over configuration
25
- - **File-based routing** - Pages are just files in `src/pages/`
26
- - **Entity-based state** - Same familiar patterns from @inglorious/web
27
-
28
- ### 🔥 Modern DX
29
-
30
- - **Hot reload dev server** - See changes instantly
31
- - **Lazy-loaded routes** - Code splitting automatically
32
- - **lit-html hydration** - Interactive UI without the bloat
33
- - **TypeScript Ready** - Write your pages and entities in TypeScript.
34
- - **Image Optimization** - Automatic compression for static assets.
35
- - **Markdown Support** - Built-in support for `.md` pages with code highlighting and math.
36
-
37
- ### 🚀 Production Ready
38
-
39
- - **Automatic code splitting** - Per-page bundles
40
- - **Optimized builds** - Minified, tree-shaken output
41
- - **Source maps** - Debug production like development
42
- - **Error boundaries** - Graceful failure handling
43
-
44
- ---
45
-
46
- ## Quick Start
47
-
48
- ### Installation
49
-
50
- ```bash
51
- npm install @inglorious/ssx @inglorious/web
52
- ```
53
-
54
- ### Create Your First Site
55
-
56
- ```bash
57
- npx @inglorious/create-app my-site --template ssx-js
58
- cd my-site
59
- npm run dev
60
- ```
61
-
62
- Or manually:
63
-
64
- ### Create Your First Site (TypeScript)
65
-
66
- ```typescript
67
- // src/pages/index.ts
68
- import { html } from "@inglorious/web"
69
-
70
- // You can import API for type safety, though it's optional
71
- // import type { API } from "@inglorious/web"
72
-
73
- export const index = {
74
- render(/* entity: any, api: API */) {
75
- return html`
76
- <div>
77
- <h1>Welcome to SSX!</h1>
78
- <p>This page was pre-rendered at build time.</p>
79
- <nav>
80
- <a href="/about">About</a>
81
- </nav>
82
- </div>
83
- `
84
- },
85
- }
86
- ```
87
-
88
- ### Create Your First Site (JavaScript)
89
-
90
- ```javascript
91
- // src/pages/index.js
92
- import { html } from "@inglorious/web"
93
-
94
- export const index = {
95
- render() {
96
- return html`
97
- <div>
98
- <h1>Welcome to SSX!</h1>
99
- <p>This page was pre-rendered at build time.</p>
100
- <nav>
101
- <a href="/about">About</a>
102
- </nav>
103
- </div>
104
- `
105
- },
106
- }
107
-
108
- export const metadata = {
109
- title: "Home",
110
- meta: {
111
- description: "Welcome to our site",
112
- "og:image": "/og-image.png",
113
- },
114
- }
115
- ```
116
-
117
- ### Development
118
-
119
- ```bash
120
- npm run dev
121
- # → Dev server at http://localhost:3000
122
- ```
123
-
124
- ### Build
125
-
126
- ```bash
127
- npm run build
128
- # → Static site in dist/
129
- ```
130
-
131
- ### Deploy
132
-
133
- ```bash
134
- npm run preview
135
- # → Preview production build
136
- ```
137
-
138
- Deploy `dist/` to:
139
-
140
- - **Vercel** - Zero config
141
- - **Netlify** - Drop folder
142
- - **GitHub Pages** - Push and done
143
- - **Cloudflare Pages** - Instant edge
144
- - **Any CDN** - It's just files!
145
-
146
- ---
147
-
148
- ## Features
149
-
150
- ### 🗺️ Sitemap & RSS Generation
151
-
152
- SSX automatically generates `sitemap.xml` and `rss.xml` based on your pages. Configure them in `src/site.config.js`:
153
-
154
- ```javascript
155
- export default {
156
- // Basic metadata
157
- title: "My Awesome Site",
158
- meta: {
159
- description: "A site built with SSX",
160
- "og:type": "website",
161
- "og:site_name": "My Site",
162
- },
163
-
164
- // Sitemap configuration
165
- sitemap: {
166
- hostname: "https://myblog.com",
167
- filter: (page) => !["/admin", "/draft-*", "/test"].includes(page.pattern),
168
- defaults: {
169
- changefreq: "weekly",
170
- priority: 0.5,
171
- },
172
- },
173
-
174
- // RSS configuration
175
- rss: {
176
- title: "My Blog",
177
- description: "Latest posts from my blog",
178
- link: "https://myblog.com",
179
- feedPath: "/feed.xml",
180
- language: "en",
181
- copyright: "© 2026 My Blog",
182
- maxItems: 10,
183
- filter: (page) => page.path.startsWith("/posts/"),
184
- },
185
- }
186
- ```
187
-
188
- Pages with a `published` date in metadata are included in RSS feeds.
189
-
190
- ### 📁 File-Based Routing
191
-
192
- Your file structure defines your routes:
193
-
194
- ```
195
- src/pages/
196
- ├── index.js → /
197
- ├── about.js → /about
198
- ├── blog.js → /blog
199
- └── posts/
200
- └── _slug.js → /posts/:slug
201
- ```
202
-
203
- Dynamic routes use underscore prefix: `_id.js`, `_slug.js`, etc.
204
-
205
- ### ⚛️ Entity-Based State And Behavior
206
-
207
- ```javascript
208
- // src/pages/about.js
209
- import { html } from "@inglorious/web"
210
-
211
- export const about = {
212
- click(entity) {
213
- entity.name += "!"
214
- },
215
-
216
- render(entity, api) {
217
- return html`<h1>
218
- About
219
- <span @click=${() => api.notify(`#${entity.id}:click`)}
220
- >${entity.name}</span
221
- >
222
- </h1>`
223
- },
224
- }
225
- ```
226
-
227
- ```javascript
228
- // src/store/entities.js
229
- export const entities = {
230
- about: {
231
- type: "about",
232
- name: "Us",
233
- },
234
- }
235
- ```
236
-
237
- ### 🔄 Data Loading
238
-
239
- Load data at build time with the `load` export:
240
-
241
- ```javascript
242
- // src/pages/blog.js
243
- import { html } from "@inglorious/web"
244
-
245
- export const blog = {
246
- render(entity) {
247
- return html`
248
- <h1>Blog Posts</h1>
249
- <ul>
250
- ${entity.posts?.map(
251
- (post) => html`
252
- <li>
253
- <a href="/posts/${post.id}">${post.title}</a>
254
- </li>
255
- `,
256
- )}
257
- </ul>
258
- `
259
- },
260
- }
261
-
262
- // SSR: Load data during build
263
- export async function load(entity) {
264
- const response = await fetch("https://api.example.com/posts")
265
- entity.posts = await response.json()
266
- }
267
-
268
- export const title = "Blog"
269
- ```
270
-
271
- The `load` function runs on the server during build. Data is serialized into the HTML and available immediately on the client.
272
-
273
- ### 🎨 Dynamic Routes with `staticPaths`
274
-
275
- Generate multiple pages from data:
276
-
277
- ```javascript
278
- // src/pages/posts/_slug.js
279
- import { html } from "@inglorious/web"
280
-
281
- export const post = {
282
- render(entity) {
283
- return html`
284
- <article>
285
- <h1>${entity.post.title}</h1>
286
- <div>${entity.post.body}</div>
287
- </article>
288
- `
289
- },
290
- }
291
-
292
- // Load data for a specific post
293
- export async function load(entity, page) {
294
- const response = await fetch(
295
- `https://api.example.com/posts/${page.params.slug}`,
296
- )
297
- entity.post = await response.json()
298
- }
299
-
300
- // Tell SSX which pages to generate
301
- export async function staticPaths() {
302
- const response = await fetch(`https://api.example.com/posts`)
303
- const posts = await response.json()
304
-
305
- return posts.map((post) => ({
306
- params: { slug: post.slug },
307
- path: `/posts/${post.slug}`,
308
- }))
309
- }
310
-
311
- export const metadata = (entity) => ({
312
- title: entity.post.title ?? "Post",
313
- meta: {
314
- description: entity.post.excerpt,
315
- },
316
- })
317
- ```
318
-
319
- ### 📄 Page Metadata
320
-
321
- Export metadata for HTML `<head>`. The `metadata` export can be a plain object or a function:
322
-
323
- ```javascript
324
- export const index = {
325
- render() {
326
- return html`<h1>Home</h1>`
327
- },
328
- }
329
-
330
- // Static metadata
331
- export const metadata = {
332
- title: "My Site",
333
- meta: {
334
- description: "An awesome static site",
335
- "og:image": "/og-image.png",
336
- },
337
- }
338
-
339
- // Or dynamic metadata (uses entity data)
340
- export const metadata = (entity) => ({
341
- title: `${entity.user.name}'s Profile`,
342
- meta: {
343
- description: entity.user.bio,
344
- "og:image": entity.user.avatar,
345
- },
346
- })
347
- ```
348
-
349
- ### 🔥 Client-Side Hydration
350
-
351
- Pages hydrate automatically with lit-html. Interactivity works immediately:
352
-
353
- ```javascript
354
- export const counter = {
355
- click(entity) {
356
- entity.count++
357
- },
358
-
359
- render(entity, api) {
360
- return html`
361
- <div>
362
- <p>Count: ${entity.count}</p>
363
- <button @click=${() => api.notify(`#${entity.id}:click`)}>
364
- Increment
365
- </button>
366
- </div>
367
- `
368
- },
369
- }
370
- ```
371
-
372
- The HTML is pre-rendered on the server. When JavaScript loads, lit-html hydrates the existing DOM and wires up event handlers. No flash of unstyled content, no duplicate rendering.
373
-
374
- ### 🧭 Client-Side Navigation
375
-
376
- After hydration, navigation is instant:
377
-
378
- ```javascript
379
- // Links navigate without page reload
380
- ;<a href="/about">About</a> // Client-side routing
381
-
382
- // Programmatic navigation
383
- api.notify("navigate", "/posts")
384
-
385
- // With options
386
- api.notify("navigate", {
387
- to: "/posts/123",
388
- replace: true,
389
- })
390
- ```
391
-
392
- Routes are lazy-loaded on demand, keeping initial bundle size small.
393
-
394
- ### 🖼️ Image Optimization
395
-
396
- SSX includes built-in image optimization using `vite-plugin-image-optimizer`.
397
-
398
- - **Automatic compression** - PNG, JPEG, GIF, SVG, WebP, and AVIF are compressed at build time.
399
- - **Lossless & Lossy** - Configurable settings via `vite` config in `site.config.js`.
400
-
401
- ### 📝 Markdown Support
402
-
403
- SSX treats `.md` files as first-class pages. You can create `src/pages/post.md` and it will be rendered automatically.
404
-
405
- - **Frontmatter** - Metadata is exported as `metadata`.
406
- - **Code Highlighting** - Built-in syntax highlighting with `highlight.js`.
407
- - **Math Support** - LaTeX support via `katex` (use `$E=mc^2$` or `$$...$$`).
408
- - **Mermaid Diagrams** - Use `mermaid` code blocks (requires client-side mermaid.js).
409
-
410
- Configure the syntax highlighting theme in `site.config.js`:
411
-
412
- ```javascript
413
- export default {
414
- markdown: {
415
- theme: "monokai", // default: "github-dark"
416
- },
417
- }
418
- ```
419
-
420
- ```markdown
421
- ---
422
- title: My Post
423
- ---
424
-
425
- # Hello World
426
-
427
- This is a markdown page.
428
- ```
429
-
430
- ---
431
-
432
- ## CLI
433
-
434
- SSX provides a simple CLI for building and developing:
435
-
436
- ### `ssx build`
437
-
438
- Builds your static site:
439
-
440
- ```bash
441
- pnpm ssx build [options]
442
-
443
- Options:
444
- -c, --config <file> Config file (default: "site.config.js")
445
- -r, --root <dir> Source root directory (default: "src")
446
- -o, --out <dir> Output directory (default: "dist")
447
- -i, --incremental Enable incremental builds (default: true)
448
- -f, --force Force clean build, ignore cache
449
- ```
450
-
451
- ### `preview`
452
-
453
- Serves the built static site on port 3000 through the `serve` NPM package.
454
-
455
- ```bash
456
- pnpm preview
457
- ```
458
-
459
- ### `ssx dev`
460
-
461
- Starts the Vite development server on port 3000 with hot reload:
462
-
463
- ```bash
464
- pnpm ssx dev [options]
465
-
466
- Options:
467
- -c, --config <file> Config file (default: "site.config.js")
468
- -r, --root <dir> Source root directory (default: "src")
469
- -p, --port <port> Dev server port (default: 3000)
470
- ```
471
-
472
- ---
473
-
474
- ## Project Structure
475
-
476
- ```
477
- my-site/
478
- ├── src/
479
- │ ├── pages/ # File-based routes
480
- │ │ ├── index.js # Home page
481
- │ │ ├── about.js # About page
482
- │ │ └── posts/
483
- ├── index.js # /posts
484
- │ │ └── _id.js # /posts/:id
485
- │ ├── store/ # Store configuration
486
- │ │ └── entities.js # Entity definitions
487
- └── types/ # Custom entity types (optional)
488
- ├── dist/ # Build output
489
- ├── package.json
490
- └── site.config.js # Site configuration
491
- ```
492
-
493
- ---
494
-
495
- ## Comparison to Other Tools
496
-
497
- | Feature | SSX | Next.js (SSG) | Astro | Eleventy |
498
- | ------------------ | ----------- | ------------- | ------ | -------- |
499
- | Pre-rendered HTML | ✅ | ✅ | ✅ | ✅ |
500
- | Client hydration | ✅ lit-html | ✅ React | ✅ Any | ❌ |
501
- | Client routing | | | | |
502
- | Lazy loading | | | | |
503
- | Entity-based state | ✅ | | | |
504
- | Zero config | ✅ | | | ❌ |
505
- | Framework agnostic | | | ✅ | |
506
-
507
- SSX is perfect if you:
508
-
509
- - Want static site performance
510
- - Love entity-based architecture
511
- - Prefer convention over configuration
512
- - Need full client-side interactivity
513
- - Don't want React/Vue lock-in
514
-
515
- ---
516
-
517
- ## Advanced Usage
518
-
519
- ### Site Configuration
520
-
521
- Customize SSX behavior in `src/site.config.js`:
522
-
523
- ```javascript
524
- export default {
525
- // Basic metadata
526
- lang: "en",
527
- charset: "UTF-8",
528
- title: "My Awesome Site",
529
- meta: {
530
- description: "A site built with SSX",
531
- "og:type": "website",
532
- },
533
-
534
- // Global assets
535
- styles: ["./styles/reset.css", "./styles/theme.css"],
536
- scripts: ["./scripts/analytics.js"],
537
-
538
- // Build options
539
- basePath: "/",
540
- rootDir: "src",
541
- outDir: "dist",
542
- publicDir: "public",
543
- favicon: "/favicon.ico",
544
-
545
- // Router config
546
- router: {
547
- trailingSlash: false,
548
- scrollBehavior: "smooth",
549
- },
550
-
551
- // Vite config passthrough
552
- vite: {
553
- server: {
554
- port: 3000,
555
- open: true,
556
- },
557
- },
558
-
559
- // Build hooks
560
- hooks: {
561
- beforeBuild: async (config) => console.log("Starting build..."),
562
- afterBuild: async (result) => console.log(`Built ${result.pages} pages`),
563
- },
564
- }
565
- ```
566
-
567
- ### Environment Variables
568
-
569
- Use Vite's environment variables:
570
-
571
- ```javascript
572
- // Access in your code
573
- const apiUrl = import.meta.env.VITE_API_URL
574
-
575
- // .env file
576
- VITE_API_URL=https://api.example.com
577
- ```
578
-
579
- ### Custom 404 Page
580
-
581
- Create a fallback route:
582
-
583
- ```javascript
584
- // src/pages/404.js
585
- export const notFound = {
586
- render() {
587
- return html`
588
- <div>
589
- <h1>404 - Page Not Found</h1>
590
- <a href="/">Go Home</a>
591
- </div>
592
- `
593
- },
594
- }
595
-
596
- export const metadata = {
597
- title: "404",
598
- }
599
- ```
600
-
601
- Register it in your router:
602
-
603
- ```javascript
604
- // src/store/entities.js
605
- import { setRoutes } from "@inglorious/web/router"
606
-
607
- setRoutes({
608
- // ... other routes
609
- "*": "notFound", // Fallback
610
- })
611
- ```
612
-
613
- ### Incremental Builds
614
-
615
- SSX enables incremental builds by default. Only changed pages are rebuilt, dramatically speeding up your build process:
616
-
617
- ```bash
618
- ssx build
619
- # Only changed pages are rebuilt
620
-
621
- ssx build --force
622
- # Force a clean rebuild of all pages
623
- ```
624
-
625
- Incremental builds respect your page dependencies and invalidate cache when dependencies change.
626
-
627
- ---
628
-
629
- ## API Reference
630
-
631
- ### Build API
632
-
633
- ```javascript
634
- import { build } from "@inglorious/ssx/build"
635
-
636
- await build({
637
- rootDir: "src",
638
- outDir: "dist",
639
- configFile: "site.config.js",
640
- incremental: true,
641
- clean: false,
642
- })
643
- ```
644
-
645
- ### Dev Server API
646
-
647
- ```javascript
648
- import { dev } from "@inglorious/ssx/dev"
649
-
650
- await dev({
651
- rootDir: "src",
652
- port: 3000,
653
- configFile: "site.config.js",
654
- })
655
- ```
656
-
657
- ---
658
-
659
- <!-- ## Examples
660
-
661
- Check out these example projects:
662
-
663
- - **[Basic Blog](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-blog)** - Simple blog with posts
664
- - **[Documentation Site](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-docs)** - Multi-page docs
665
- - **[E-commerce](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-shop)** - Product catalog
666
- - **[Portfolio](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-portfolio)** - Personal portfolio
667
-
668
- --- -->
669
-
670
- ## Roadmap
671
-
672
- - [x] TypeScript support
673
- - [x] Image optimization
674
- - [ ] API routes (serverless functions)
675
- - [x] Markdown support
676
- - [ ] i18n helpers
677
-
678
- ---
679
-
680
- ## Philosophy
681
-
682
- SSX embraces the philosophy of [@inglorious/web](https://www.npmjs.com/package/@inglorious/web):
683
-
684
- - **Simplicity over cleverness** - Obvious beats clever
685
- - **Convention over configuration** - Sensible defaults
686
- - **Predictability over magic** - Explicit is better than implicit
687
- - **Standards over abstractions** - Use the platform
688
-
689
- Static site generation should be simple. SSX makes it simple.
690
-
691
- ---
692
-
693
- ## Contributing
694
-
695
- Contributions are welcome! Please read our [Contributing Guidelines](../../CONTRIBUTING.md) first.
696
-
697
- ---
698
-
699
- ## License
700
-
701
- **MIT License** - Free and open source
702
-
703
- Created by [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
704
-
705
- ---
706
-
707
- ## Related Packages
708
-
709
- - [@inglorious/web](https://www.npmjs.com/package/@inglorious/web) - Entity-based web framework
710
- - [@inglorious/store](https://www.npmjs.com/package/@inglorious/store) - State management
711
- - [@inglorious/engine](https://www.npmjs.com/package/@inglorious/engine) - Game engine
712
-
713
- ---
714
-
715
- ## Support
716
-
717
- - 📖 [Documentation](https://inglorious-engine.vercel.app)
718
- - 💬 [Discord Community](https://discord.gg/Byx85t2eFp)
719
- - 🐛 [Issue Tracker](https://github.com/IngloriousCoderz/inglorious-forge/issues)
720
- - 📧 [Email Support](mailto:antony.mistretta@gmail.com)
721
-
722
- ---
723
-
724
- **Build static sites the Inglorious way. Simple. Predictable. Fast.** 🚀
1
+ # @inglorious/ssx
2
+
3
+ [![NPM version](https://img.shields.io/npm/v/@inglorious/ssx.svg)](https://www.npmjs.com/package/@inglorious/ssx)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ **Static Site Xecution** - Build blazing-fast static sites with [@inglorious/web](https://www.npmjs.com/package/@inglorious/web), complete with server-side rendering, client-side hydration, and zero-config routing.
7
+
8
+ SSX takes your entity-based web apps and generates optimized static HTML with full hydration support. Think Next.js SSG or Astro, but with the simplicity and predictability of Inglorious Web's entity architecture.
9
+
10
+ ---
11
+
12
+ ## Why SSX?
13
+
14
+ ### ⚡️ Fast by Default
15
+
16
+ - **Pre-rendered HTML** - Every page is built at compile time
17
+ - **Instant load times** - No waiting for server responses
18
+ - **CDN-ready** - Deploy anywhere static files are served
19
+ - **Perfect Lighthouse scores** - SEO and performance out of the box
20
+
21
+ ### 🎯 Simple Architecture
22
+
23
+ - **No server required** - Pure static files
24
+ - **No complex build configs** - Convention over configuration
25
+ - **File-based routing** - Pages are just files in `src/pages/`
26
+ - **Entity-based state** - Same familiar patterns from @inglorious/web
27
+
28
+ ### 🔥 Modern DX
29
+
30
+ - **Hot reload dev server** - See changes instantly
31
+ - **Lazy-loaded routes** - Code splitting automatically
32
+ - **lit-html hydration** - Interactive UI without the bloat
33
+ - **TypeScript ready** - Write your pages and entities in TypeScript
34
+ - **Image optimization** - Automatic compression for static assets
35
+ - **Markdown support** - Built-in support for `.md` pages with code highlighting and math
36
+
37
+ ### 🚀 Production Ready
38
+
39
+ - **Automatic code splitting** - Per-page bundles
40
+ - **Optimized builds** - Minified, tree-shaken output
41
+ - **Source maps** - Debug production like development
42
+ - **Error boundaries** - Graceful failure handling
43
+
44
+ ---
45
+
46
+ ## Quick Start
47
+
48
+ ### Installation
49
+
50
+ ```bash
51
+ npm install @inglorious/ssx @inglorious/web
52
+ ```
53
+
54
+ ### Create Your First Site
55
+
56
+ ```bash
57
+ npx @inglorious/create-app my-site --template ssx-js
58
+ cd my-site
59
+ npm run dev
60
+ ```
61
+
62
+ Or manually:
63
+
64
+ ### TypeScript Example
65
+
66
+ ```typescript
67
+ // src/pages/index.ts
68
+ import { html } from "@inglorious/web"
69
+
70
+ // You can import API for type safety, though it's optional
71
+ // import type { API } from "@inglorious/web"
72
+
73
+ export const index = {
74
+ render(/* entity: any, api: API */) {
75
+ return html`
76
+ <div>
77
+ <h1>Welcome to SSX!</h1>
78
+ <p>This page was pre-rendered at build time.</p>
79
+ <nav>
80
+ <a href="/about">About</a>
81
+ </nav>
82
+ </div>
83
+ `
84
+ },
85
+ }
86
+ ```
87
+
88
+ ### JavaScript Example
89
+
90
+ ```javascript
91
+ // src/pages/index.js
92
+ import { html } from "@inglorious/web"
93
+
94
+ export const index = {
95
+ render() {
96
+ return html`
97
+ <div>
98
+ <h1>Welcome to SSX!</h1>
99
+ <p>This page was pre-rendered at build time.</p>
100
+ <nav>
101
+ <a href="/about">About</a>
102
+ </nav>
103
+ </div>
104
+ `
105
+ },
106
+ }
107
+
108
+ export const metadata = {
109
+ title: "Home",
110
+ meta: {
111
+ description: "Welcome to our site",
112
+ "og:image": "/og-image.png",
113
+ },
114
+ }
115
+ ```
116
+
117
+ ### Development
118
+
119
+ ```bash
120
+ npm run dev
121
+ # → Dev server at http://localhost:3000
122
+ ```
123
+
124
+ ### Build
125
+
126
+ ```bash
127
+ npm run build
128
+ # → Static site in dist/
129
+ ```
130
+
131
+ ### Deploy
132
+
133
+ ```bash
134
+ npm run preview
135
+ # → Preview production build
136
+ ```
137
+
138
+ Deploy `dist/` to:
139
+
140
+ - **Vercel** - Zero config
141
+ - **Netlify** - Drop folder
142
+ - **GitHub Pages** - Push and done
143
+ - **Cloudflare Pages** - Instant edge
144
+ - **Any CDN** - It's just files!
145
+
146
+ ---
147
+
148
+ ## Features
149
+
150
+ ### 🗺️ Sitemap & RSS Generation
151
+
152
+ SSX automatically generates `sitemap.xml` and `rss.xml` based on your pages. Configure them in `src/site.config.js`:
153
+
154
+ ```javascript
155
+ export default {
156
+ // Basic metadata
157
+ title: "My Awesome Site",
158
+ meta: {
159
+ description: "A site built with SSX",
160
+ "og:type": "website",
161
+ "og:site_name": "My Site",
162
+ },
163
+
164
+ // Sitemap configuration
165
+ sitemap: {
166
+ hostname: "https://myblog.com",
167
+ filter: (page) => !["/admin", "/draft-*", "/test"].includes(page.pattern),
168
+ defaults: {
169
+ changefreq: "weekly",
170
+ priority: 0.5,
171
+ },
172
+ },
173
+
174
+ // RSS configuration
175
+ rss: {
176
+ title: "My Blog",
177
+ description: "Latest posts from my blog",
178
+ link: "https://myblog.com",
179
+ feedPath: "/feed.xml",
180
+ language: "en",
181
+ copyright: "© 2026 My Blog",
182
+ maxItems: 10,
183
+ filter: (page) => page.path.startsWith("/posts/"),
184
+ },
185
+ }
186
+ ```
187
+
188
+ Pages with a `published` date in metadata are included in RSS feeds.
189
+
190
+ ### 📁 File-Based Routing
191
+
192
+ Your file structure defines your routes:
193
+
194
+ ```
195
+ src/pages/
196
+ ├── index.js → /
197
+ ├── about.js → /about
198
+ ├── blog.js → /blog
199
+ └── posts/
200
+ └── _slug.js → /posts/:slug
201
+ ```
202
+
203
+ Dynamic routes use underscore prefix: `_id.js`, `_slug.js`, etc.
204
+
205
+ ### ⚛️ Entity-Based State and Behavior
206
+
207
+ ```javascript
208
+ // src/pages/about.js
209
+ import { html } from "@inglorious/web"
210
+
211
+ export const about = {
212
+ click(entity) {
213
+ entity.name += "!"
214
+ },
215
+
216
+ render(entity, api) {
217
+ return html`<h1>
218
+ About
219
+ <span @click=${() => api.notify(`#${entity.id}:click`)}
220
+ >${entity.name}</span
221
+ >
222
+ </h1>`
223
+ },
224
+ }
225
+ ```
226
+
227
+ ```javascript
228
+ // src/store/entities.js
229
+ export const entities = {
230
+ about: {
231
+ type: "about",
232
+ name: "Us",
233
+ },
234
+ }
235
+ ```
236
+
237
+ ### 🔄 Data Loading
238
+
239
+ Load data at build time with the `load` export:
240
+
241
+ ```javascript
242
+ // src/pages/blog.js
243
+ import { html } from "@inglorious/web"
244
+
245
+ export const blog = {
246
+ render(entity) {
247
+ return html`
248
+ <h1>Blog Posts</h1>
249
+ <ul>
250
+ ${entity.posts?.map(
251
+ (post) => html`
252
+ <li>
253
+ <a href="/posts/${post.id}">${post.title}</a>
254
+ </li>
255
+ `,
256
+ )}
257
+ </ul>
258
+ `
259
+ },
260
+ }
261
+
262
+ // SSR: Load data during build
263
+ export async function load(entity) {
264
+ const response = await fetch("https://api.example.com/posts")
265
+ entity.posts = await response.json()
266
+ }
267
+
268
+ export const metadata = {
269
+ title: "Blog",
270
+ }
271
+ ```
272
+
273
+ The `load` function runs on the server during build. Data is serialized into the HTML and available immediately on the client.
274
+
275
+ ### 🎨 Dynamic Routes with `staticPaths`
276
+
277
+ Generate multiple pages from data:
278
+
279
+ ```javascript
280
+ // src/pages/posts/_slug.js
281
+ import { html } from "@inglorious/web"
282
+
283
+ export const post = {
284
+ render(entity) {
285
+ return html`
286
+ <article>
287
+ <h1>${entity.post.title}</h1>
288
+ <div>${entity.post.body}</div>
289
+ </article>
290
+ `
291
+ },
292
+ }
293
+
294
+ // Load data for a specific post
295
+ export async function load(entity, page) {
296
+ const response = await fetch(
297
+ `https://api.example.com/posts/${page.params.slug}`,
298
+ )
299
+ entity.post = await response.json()
300
+ }
301
+
302
+ // Tell SSX which pages to generate
303
+ export async function staticPaths() {
304
+ const response = await fetch(`https://api.example.com/posts`)
305
+ const posts = await response.json()
306
+
307
+ return posts.map((post) => ({
308
+ params: { slug: post.slug },
309
+ path: `/posts/${post.slug}`,
310
+ }))
311
+ }
312
+
313
+ export const metadata = (entity) => ({
314
+ title: entity.post?.title ?? "Post",
315
+ meta: {
316
+ description: entity.post?.excerpt,
317
+ },
318
+ })
319
+ ```
320
+
321
+ ### 📄 Page Metadata
322
+
323
+ Export metadata for HTML `<head>`. The `metadata` export can be a plain object or a function:
324
+
325
+ ```javascript
326
+ export const index = {
327
+ render() {
328
+ return html`<h1>Home</h1>`
329
+ },
330
+ }
331
+
332
+ // Static metadata
333
+ export const metadata = {
334
+ title: "My Site",
335
+ meta: {
336
+ description: "An awesome static site",
337
+ "og:image": "/og-image.png",
338
+ },
339
+ }
340
+
341
+ // Or dynamic metadata (uses entity data)
342
+ export const metadata = (entity) => ({
343
+ title: `${entity.user.name}'s Profile`,
344
+ meta: {
345
+ description: entity.user.bio,
346
+ "og:image": entity.user.avatar,
347
+ },
348
+ })
349
+ ```
350
+
351
+ ### 🔥 Client-Side Hydration
352
+
353
+ Pages hydrate automatically with lit-html. Interactivity works immediately:
354
+
355
+ ```javascript
356
+ export const counter = {
357
+ click(entity) {
358
+ entity.count++
359
+ },
360
+
361
+ render(entity, api) {
362
+ return html`
363
+ <div>
364
+ <p>Count: ${entity.count}</p>
365
+ <button @click=${() => api.notify(`#${entity.id}:click`)}>
366
+ Increment
367
+ </button>
368
+ </div>
369
+ `
370
+ },
371
+ }
372
+ ```
373
+
374
+ The HTML is pre-rendered on the server. When JavaScript loads, lit-html hydrates the existing DOM and wires up event handlers. No flash of unstyled content, no duplicate rendering.
375
+
376
+ ### 🧭 Client-Side Navigation
377
+
378
+ After hydration, navigation is instant:
379
+
380
+ ```javascript
381
+ // Links navigate without page reload
382
+ ;<a href="/about">About</a> // Client-side routing
383
+
384
+ // Programmatic navigation
385
+ api.notify("navigate", "/posts")
386
+
387
+ // With options
388
+ api.notify("navigate", {
389
+ to: "/posts/123",
390
+ replace: true,
391
+ })
392
+ ```
393
+
394
+ Routes are lazy-loaded on demand, keeping initial bundle size small.
395
+
396
+ ### 🖼️ Image Optimization
397
+
398
+ SSX includes built-in image optimization using `vite-plugin-image-optimizer`.
399
+
400
+ - **Automatic compression** - PNG, JPEG, GIF, SVG, WebP, and AVIF are compressed at build time
401
+ - **Lossless & lossy** - Configurable settings via `vite` config in `site.config.js`
402
+
403
+ ### 📝 Markdown Support
404
+
405
+ SSX treats `.md` files as first-class pages. You can create `src/pages/post.md` and it will be rendered automatically.
406
+
407
+ - **Frontmatter** - Metadata is exported as `metadata`
408
+ - **Code highlighting** - Built-in syntax highlighting with `highlight.js`
409
+ - **Math support** - LaTeX support via `katex` (use `$E=mc^2$` or `$$...$$`)
410
+ - **Mermaid diagrams** - Use `mermaid` code blocks (requires client-side mermaid.js)
411
+
412
+ Configure the syntax highlighting theme in `site.config.js`:
413
+
414
+ ```javascript
415
+ export default {
416
+ markdown: {
417
+ theme: "monokai", // default: "github-dark"
418
+ },
419
+ }
420
+ ```
421
+
422
+ Example markdown file:
423
+
424
+ ```markdown
425
+ ---
426
+ title: My Post
427
+ ---
428
+
429
+ # Hello World
430
+
431
+ This is a markdown page.
432
+ ```
433
+
434
+ ---
435
+
436
+ ## CLI
437
+
438
+ SSX provides a simple CLI for building and developing:
439
+
440
+ ### `ssx build`
441
+
442
+ Builds your static site:
443
+
444
+ ```bash
445
+ pnpm ssx build [options]
446
+
447
+ Options:
448
+ -c, --config <file> Config file (default: "site.config.js")
449
+ -r, --root <dir> Source root directory (default: "src")
450
+ -o, --out <dir> Output directory (default: "dist")
451
+ -i, --incremental Enable incremental builds (default: true)
452
+ -f, --force Force clean build, ignore cache
453
+ ```
454
+
455
+ ### `ssx dev`
456
+
457
+ Starts the Vite development server on port 3000 with hot reload:
458
+
459
+ ```bash
460
+ pnpm ssx dev [options]
461
+
462
+ Options:
463
+ -c, --config <file> Config file (default: "site.config.js")
464
+ -r, --root <dir> Source root directory (default: "src")
465
+ -p, --port <port> Dev server port (default: 3000)
466
+ ```
467
+
468
+ ### `preview`
469
+
470
+ Serves the built static site on port 3000 through the `serve` NPM package:
471
+
472
+ ```bash
473
+ pnpm preview
474
+ ```
475
+
476
+ ---
477
+
478
+ ## Project Structure
479
+
480
+ ```
481
+ my-site/
482
+ ├── src/
483
+ │ ├── pages/ # File-based routes
484
+ │ │ ├── index.js # Home page
485
+ ├── about.js # About page
486
+ │ │ └── posts/
487
+ │ ├── index.js # /posts
488
+ │ │ └── _id.js # /posts/:id
489
+ ├── store/ # Store configuration
490
+ │ │ └── entities.js # Entity definitions
491
+ │ └── types/ # Custom entity types (optional)
492
+ ├── dist/ # Build output
493
+ ├── package.json
494
+ └── site.config.js # Site configuration
495
+ ```
496
+
497
+ ---
498
+
499
+ ## Comparison to Other Tools
500
+
501
+ | Feature | SSX | Next.js (SSG) | Astro | Eleventy |
502
+ | ------------------ | ----------- | ------------- | ------ | -------- |
503
+ | Pre-rendered HTML | ✅ | | | |
504
+ | Client hydration | ✅ lit-html | ✅ React | ✅ Any | ❌ |
505
+ | Client routing | | | ✅ | |
506
+ | Lazy loading | ✅ | ✅ | ✅ | ❌ |
507
+ | Entity-based state | ✅ | ❌ | ❌ | ❌ |
508
+ | Zero config | ✅ | ❌ | ❌ | ❌ |
509
+ | Framework agnostic | ❌ | ❌ | ✅ | ✅ |
510
+
511
+ SSX is perfect if you:
512
+
513
+ - Want static site performance
514
+ - Love entity-based architecture
515
+ - Prefer convention over configuration
516
+ - Need full client-side interactivity
517
+ - Don't want React/Vue lock-in
518
+
519
+ ---
520
+
521
+ ## Advanced Usage
522
+
523
+ ### Site Configuration
524
+
525
+ Customize SSX behavior in `src/site.config.js`:
526
+
527
+ ```javascript
528
+ export default {
529
+ // Basic metadata
530
+ lang: "en",
531
+ charset: "UTF-8",
532
+ title: "My Awesome Site",
533
+ meta: {
534
+ description: "A site built with SSX",
535
+ "og:type": "website",
536
+ },
537
+
538
+ // Global assets
539
+ styles: ["./styles/reset.css", "./styles/theme.css"],
540
+ scripts: ["./scripts/analytics.js"],
541
+
542
+ // Build options
543
+ basePath: "/",
544
+ rootDir: "src",
545
+ outDir: "dist",
546
+ publicDir: "public",
547
+ favicon: "/favicon.ico",
548
+
549
+ // Router config
550
+ router: {
551
+ trailingSlash: false,
552
+ scrollBehavior: "smooth",
553
+ },
554
+
555
+ // Vite config passthrough
556
+ vite: {
557
+ server: {
558
+ port: 3000,
559
+ open: true,
560
+ },
561
+ },
562
+
563
+ // Build hooks
564
+ hooks: {
565
+ beforeBuild: async (config) => console.log("Starting build..."),
566
+ afterBuild: async (result) => console.log(`Built ${result.pages} pages`),
567
+ },
568
+ }
569
+ ```
570
+
571
+ ### Environment Variables
572
+
573
+ Use Vite's environment variables:
574
+
575
+ ```javascript
576
+ // Access in your code
577
+ const apiUrl = import.meta.env.VITE_API_URL
578
+
579
+ // .env file
580
+ VITE_API_URL=https://api.example.com
581
+ ```
582
+
583
+ ### Custom 404 Page
584
+
585
+ Create a fallback route:
586
+
587
+ ```javascript
588
+ // src/pages/404.js
589
+ export const notFound = {
590
+ render() {
591
+ return html`
592
+ <div>
593
+ <h1>404 - Page Not Found</h1>
594
+ <a href="/">Go Home</a>
595
+ </div>
596
+ `
597
+ },
598
+ }
599
+
600
+ export const metadata = {
601
+ title: "404",
602
+ }
603
+ ```
604
+
605
+ Register it in your router:
606
+
607
+ ```javascript
608
+ // src/store/entities.js
609
+ import { setRoutes } from "@inglorious/web/router"
610
+
611
+ setRoutes({
612
+ // ... other routes
613
+ "*": "notFound", // Fallback
614
+ })
615
+ ```
616
+
617
+ ### Incremental Builds
618
+
619
+ SSX enables incremental builds by default. Only changed pages are rebuilt, dramatically speeding up your build process:
620
+
621
+ ```bash
622
+ ssx build
623
+ # Only changed pages are rebuilt
624
+
625
+ ssx build --force
626
+ # Force a clean rebuild of all pages
627
+ ```
628
+
629
+ Incremental builds respect your page dependencies and invalidate the cache when dependencies change.
630
+
631
+ ---
632
+
633
+ ## Component Compatibility
634
+
635
+ ### Fully Supported
636
+
637
+ - All Inglorious Web components (`table`, `list`, `select`, `form`)
638
+ - Custom components using lit-html templates
639
+ - Plain HTML and CSS
640
+
641
+ ### Limited Support
642
+
643
+ - Third-party Web Components (Shoelace, Material Web, etc.)
644
+ - Will not appear in pre-rendered HTML
645
+ - Require client-side JavaScript to initialize
646
+ - Best used for client-only interactive features
647
+ - Consider using Inglorious Web components for SSG content
648
+
649
+ ---
650
+
651
+ ## API Reference
652
+
653
+ ### Build API
654
+
655
+ ```javascript
656
+ import { build } from "@inglorious/ssx/build"
657
+
658
+ await build({
659
+ rootDir: "src",
660
+ outDir: "dist",
661
+ configFile: "site.config.js",
662
+ incremental: true,
663
+ clean: false,
664
+ })
665
+ ```
666
+
667
+ ### Dev Server API
668
+
669
+ ```javascript
670
+ import { dev } from "@inglorious/ssx/dev"
671
+
672
+ await dev({
673
+ rootDir: "src",
674
+ port: 3000,
675
+ configFile: "site.config.js",
676
+ })
677
+ ```
678
+
679
+ ---
680
+
681
+ <!-- ## Examples
682
+
683
+ Check out these example projects:
684
+
685
+ - **[Basic Blog](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-blog)** - Simple blog with posts
686
+ - **[Documentation Site](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-docs)** - Multi-page docs
687
+ - **[E-commerce](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-shop)** - Product catalog
688
+ - **[Portfolio](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/ssx-portfolio)** - Personal portfolio
689
+
690
+ --- -->
691
+
692
+ ## Roadmap
693
+
694
+ - [x] TypeScript support
695
+ - [x] Image optimization
696
+ - [ ] API routes (serverless functions)
697
+ - [x] Markdown support
698
+ - [ ] i18n helpers
699
+
700
+ ---
701
+
702
+ ## Philosophy
703
+
704
+ SSX embraces the philosophy of [@inglorious/web](https://www.npmjs.com/package/@inglorious/web):
705
+
706
+ - **Simplicity over cleverness** - Obvious beats clever
707
+ - **Convention over configuration** - Sensible defaults
708
+ - **Predictability over magic** - Explicit is better than implicit
709
+ - **Standards over abstractions** - Use the platform
710
+
711
+ Static site generation should be simple. SSX makes it simple.
712
+
713
+ ---
714
+
715
+ ## Contributing
716
+
717
+ Contributions are welcome! Please read our [Contributing Guidelines](../../CONTRIBUTING.md) first.
718
+
719
+ ---
720
+
721
+ ## License
722
+
723
+ **MIT License** - Free and open source
724
+
725
+ Created by [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
726
+
727
+ ---
728
+
729
+ ## Related Packages
730
+
731
+ - [@inglorious/web](https://www.npmjs.com/package/@inglorious/web) - Entity-based web framework
732
+ - [@inglorious/store](https://www.npmjs.com/package/@inglorious/store) - State management
733
+ - [@inglorious/engine](https://www.npmjs.com/package/@inglorious/engine) - Game engine
734
+
735
+ ---
736
+
737
+ ## Support
738
+
739
+ - 📖 [Documentation](https://inglorious-engine.vercel.app)
740
+ - 💬 [Discord Community](https://discord.gg/Byx85t2eFp)
741
+ - 🐛 [Issue Tracker](https://github.com/IngloriousCoderz/inglorious-forge/issues)
742
+ - 📧 [Email Support](mailto:antony.mistretta@gmail.com)
743
+
744
+ ---
745
+
746
+ **Build static sites the Inglorious way. Simple. Predictable. Fast.** 🚀