@emkodev/emroute 1.12.5 → 1.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -124
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,15 +3,19 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
File-based
|
|
6
|
+
File-based, storage-agnostic router with triple rendering. Zero dependencies.
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://emroute.emko.dev"><strong>emroute.emko.dev →</strong></a>
|
|
7
11
|
</p>
|
|
8
12
|
|
|
9
13
|
---
|
|
10
14
|
|
|
11
|
-
Every route renders three ways from the same component:
|
|
12
|
-
in the browser,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
Every route renders three ways from the same component: a **Single Page App**
|
|
16
|
+
in the browser, **server-rendered HTML**, and **plain Markdown**. No separate
|
|
17
|
+
API layer — prefix any route with `/md/` and get text that LLMs, scripts, and
|
|
18
|
+
`curl` can consume directly.
|
|
15
19
|
|
|
16
20
|
```
|
|
17
21
|
GET /projects/42 → SPA (hydrated in browser)
|
|
@@ -25,136 +29,29 @@ GET /md/projects/42 → plain Markdown
|
|
|
25
29
|
npm add @emkodev/emroute # or bun add, pnpm add, yarn add
|
|
26
30
|
```
|
|
27
31
|
|
|
28
|
-
Works on **Node**, **Bun**, and **Deno**.
|
|
29
|
-
use TypeScript source directly.
|
|
30
|
-
|
|
31
|
-
For markdown rendering, add [@emkodev/emkoma](doc/08c-setup-emkoma.md) (built
|
|
32
|
-
for emroute) or bring your own — [marked](doc/08a-setup-marked.md) and
|
|
33
|
-
[markdown-it](doc/08b-setup-markdown-it.md) both work.
|
|
34
|
-
|
|
35
|
-
## How It Works
|
|
36
|
-
|
|
37
|
-
<p align="center">
|
|
38
|
-
<img src="https://raw.githubusercontent.com/emko-io/emroute/main/doc/diagram-full.png" alt="emroute architecture" width="480" height="480">
|
|
39
|
-
</p>
|
|
40
|
-
|
|
41
|
-
One component, three rendering paths:
|
|
42
|
-
|
|
43
|
-
<p align="center">
|
|
44
|
-
<img src="https://raw.githubusercontent.com/emko-io/emroute/main/doc/diagram-flow-spa.png" alt="SPA flow" width="320" height="320">
|
|
45
|
-
<img src="https://raw.githubusercontent.com/emko-io/emroute/main/doc/diagram-flow-ssr-html.png" alt="SSR HTML flow" width="320" height="320">
|
|
46
|
-
<img src="https://raw.githubusercontent.com/emko-io/emroute/main/doc/diagram-flow-ssr-md.png" alt="SSR Markdown flow" width="320" height="320">
|
|
47
|
-
</p>
|
|
32
|
+
Works on **Node**, **Bun**, and **Deno**.
|
|
48
33
|
|
|
49
|
-
|
|
50
|
-
delivery. The SSR Markdown flow calls `renderMarkdown()` instead, bypassing
|
|
51
|
-
HTML entirely for plain text output.
|
|
34
|
+
## Quick taste
|
|
52
35
|
|
|
53
36
|
Routes are files. The filesystem is the config.
|
|
54
37
|
|
|
55
38
|
```
|
|
56
39
|
routes/
|
|
57
40
|
index.page.md → /
|
|
58
|
-
about.page.html → /about
|
|
59
41
|
projects.page.md → /projects
|
|
60
|
-
projects/
|
|
61
|
-
[id].page.ts → /projects/:id
|
|
62
|
-
[id]/
|
|
63
|
-
tasks.page.ts → /projects/:id/tasks
|
|
64
|
-
404.page.html → not found
|
|
65
|
-
index.error.ts → root error handler
|
|
42
|
+
projects/[id].page.ts → /projects/:id
|
|
66
43
|
```
|
|
67
44
|
|
|
68
|
-
A route can be a `.md` file, an `.html` template, a `.ts` component, or
|
|
69
|
-
combination. When a `.page.ts` exists, it controls data fetching and
|
|
70
|
-
When it doesn't, the framework renders the
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
import { PageComponent } from '@emkodev/emroute';
|
|
74
|
-
|
|
75
|
-
class ProjectPage extends PageComponent<{ id: string }, ProjectData> {
|
|
76
|
-
override readonly name = 'project';
|
|
77
|
-
|
|
78
|
-
override async getData({ params }: this['DataArgs']) {
|
|
79
|
-
const res = await fetch(`/api/projects/${params.id}`);
|
|
80
|
-
return res.json();
|
|
81
|
-
}
|
|
45
|
+
A route can be a `.md` file, an `.html` template, a `.ts` component, or any
|
|
46
|
+
combination. When a `.page.ts` exists, it controls data fetching and
|
|
47
|
+
rendering. When it doesn't, the framework renders the companion file
|
|
48
|
+
directly.
|
|
82
49
|
|
|
83
|
-
|
|
84
|
-
// context.files.html has the companion .page.html template if it exists
|
|
85
|
-
const template = context.files?.html ?? `<h1>${data.name}</h1>`;
|
|
86
|
-
return template.replaceAll('{{id}}', params.id) + '<router-slot></router-slot>';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
override renderMarkdown({ data, context }: this['RenderArgs']) {
|
|
90
|
-
// context.files.md has the companion .page.md content if it exists
|
|
91
|
-
return context.files?.md ?? `# ${data.name}\n\nStatus: ${data.status}`;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export default new ProjectPage();
|
|
96
|
-
```
|
|
50
|
+
## Documentation
|
|
97
51
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
- **File-based routing** with dynamic segments (`[id]`), catch-all directories, and nested layouts via `<router-slot>`. Routes follow REST conventions: exact routes are terminal resources, catch-all directories own their namespace
|
|
101
|
-
- **Triple rendering** — SPA, SSR HTML, SSR Markdown from one component
|
|
102
|
-
- **Companion files** — `.page.html`, `.page.md`, `.page.css` loaded automatically and passed through context
|
|
103
|
-
- **Widgets** — interactive islands with their own data lifecycle, error handling, and optional file companions (`.html`, `.md`, `.css`). Auto-discovered from a `widgets/` directory or registered manually. `this.element` gives opt-in DOM access in the browser. `<widget-foo lazy>` defers loading until visible via `IntersectionObserver`
|
|
104
|
-
- **View Transitions** — SPA route changes animate via `document.startViewTransition()`. Progressive enhancement with CSS-only customization
|
|
105
|
-
- **Scoped CSS** — companion `.widget.css` files auto-wrapped in `@scope (widget-{name}) { ... }`
|
|
106
|
-
- **Shadow DOM** — unified Declarative Shadow DOM architecture for SSR and SPA. Widgets render into shadow roots for true CSS encapsulation and Web Components spec compliance
|
|
107
|
-
- **SSR hydration** — server-rendered HTML adopted by the SPA without re-rendering. Widgets can implement `hydrate(args)` to attach event listeners after SSR adoption, receiving `{ data, params, context }`
|
|
108
|
-
- **Error boundaries** — scoped error handlers per route prefix, plus status pages (`404.page.html`) and a root fallback
|
|
109
|
-
- **Extensible context** — inject app-level services (RPC clients, auth, feature flags) into every component via `extendContext` on the router. Type-safe access through module augmentation or a per-component generic
|
|
110
|
-
- **Declarative overlays** — popovers, modals, and toasts with zero JS via Invoker Commands API and CSS keyframe animations. Programmatic API available for dynamic content
|
|
111
|
-
- **Zero dependencies** — native APIs only (URLPattern, custom elements, Navigation API). No framework runtime, no virtual DOM, no build-time magic
|
|
112
|
-
- **Pluggable markdown** — `<mark-down>` custom element with a swappable parser interface; bring your own renderer
|
|
113
|
-
- **Redirects** — declarative `.redirect.ts` files with 301/302 support
|
|
114
|
-
- **Configurable base paths** — `/html/` and `/md/` prefixes are configurable via `BasePath`
|
|
115
|
-
- **SPA modes** — `'root'` (default), `'leaf'`, `'none'`, or `'only'` to control how the server handles non-file requests and SSR endpoints
|
|
116
|
-
- **Sitemap generation** — opt-in `sitemap.xml` from the routes manifest with support for dynamic route enumerators
|
|
117
|
-
- **On-the-fly transpilation** — `BunFsRuntime` serves `.ts` files as transpiled JavaScript with companion files inlined. No build step required for development. `buildClientBundles()` is an optional production optimization
|
|
118
|
-
|
|
119
|
-
## Runtimes
|
|
120
|
-
|
|
121
|
-
The router is storage-agnostic — it reads routes through a `Runtime` abstraction,
|
|
122
|
-
not the filesystem directly. emroute ships four runtimes:
|
|
123
|
-
|
|
124
|
-
- **`BunFsRuntime`** — Bun-native APIs (`Bun.file()`, `Bun.write()`,
|
|
125
|
-
`Bun.Transpiler`). The default choice for Bun projects.
|
|
126
|
-
- **`UniversalFsRuntime`** — `node:` APIs only. Works on Node, Deno, and Bun.
|
|
127
|
-
- **`BunSqliteRuntime`** — stores routes in a SQLite database. Proves the
|
|
128
|
-
storage-agnostic design: no filesystem needed.
|
|
129
|
-
- **`FetchRuntime`** — browser runtime that fetches files from a remote server.
|
|
130
|
-
Powers the SPA in `root` and `only` modes.
|
|
131
|
-
|
|
132
|
-
Bun runs TypeScript source directly. Node and Deno use the compiled JS from
|
|
133
|
-
`dist/`. See [ADR-0017](doc/architecture/ADR-0017-move-to-bun-ecosystem.md) for
|
|
134
|
-
the full analysis.
|
|
135
|
-
|
|
136
|
-
## Getting Started
|
|
137
|
-
|
|
138
|
-
Pick your runtime: [Bun](doc/01a-setup-bun.md) | [Node](doc/01b-setup-node.md) | [Deno](doc/01c-setup-deno.md)
|
|
52
|
+
Everything else — setup, routing, widgets, SSR, hydration, the runtime
|
|
53
|
+
abstraction, design decisions — lives at **[emroute.emko.dev](https://emroute.emko.dev)**.
|
|
139
54
|
|
|
140
|
-
##
|
|
55
|
+
## License
|
|
141
56
|
|
|
142
|
-
|
|
143
|
-
- [First route](doc/02-first-route.md) — route files and rendering modes
|
|
144
|
-
- [Pages](doc/03-pages.md) — page components, companion files, data fetching
|
|
145
|
-
- [Routing](doc/04-routing.md) — dynamic segments, catch-all, redirects
|
|
146
|
-
- [Nesting](doc/05-nesting.md) — layouts, slots, passthrough pages, tips and tricks
|
|
147
|
-
- [Widgets](doc/06-widgets.md) — interactive islands with data lifecycle
|
|
148
|
-
- [Server](doc/07-server.md) — `Emroute.create`, composition, static files
|
|
149
|
-
- [Markdown renderers](doc/08-markdown-renderer.md) — pluggable parser interface and setup
|
|
150
|
-
- [Runtime](doc/09-runtime.md) — abstract runtime, UniversalFsRuntime, BunFsRuntime, BunSqliteRuntime
|
|
151
|
-
- [SPA modes](doc/10-spa-mode.md) — none, leaf, root, only
|
|
152
|
-
- [Error handling](doc/11-error-handling.md) — widget errors, boundaries, status pages
|
|
153
|
-
- [Shadow DOM](doc/12-shadow-dom.md) — unified architecture, SSR hydration
|
|
154
|
-
- [Hono integration](doc/13-hono.md) — using emroute with Hono
|
|
155
|
-
|
|
156
|
-
### For contributors and architects
|
|
157
|
-
|
|
158
|
-
- [Architectural decisions](doc/architecture/) — ADR-0001 through ADR-0017
|
|
159
|
-
|
|
160
|
-
<img src="https://raw.githubusercontent.com/emko-io/emroute/main/doc/logo-full.png" alt="emroute" width="197" height="40">
|
|
57
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emkodev/emroute",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.6",
|
|
4
4
|
"description": "File-based (but storage-agnostic) router with triple rendering (SPA, SSR HTML, SSR Markdown). Zero dependencies.",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"author": "emko.dev",
|
|
@@ -137,6 +137,6 @@
|
|
|
137
137
|
"test:browser:root": "bun test test/browser/root",
|
|
138
138
|
"test:browser:only": "bun test test/browser/only",
|
|
139
139
|
"lint": "eslint .",
|
|
140
|
-
"dev": "echo 'Use a custom server.ts — see
|
|
140
|
+
"dev": "echo 'Use a custom server.ts — see https://emroute.emko.dev/html/setup'"
|
|
141
141
|
}
|
|
142
142
|
}
|