@emkodev/emroute 1.0.3 → 1.6.0

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.
Files changed (46) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +147 -12
  3. package/package.json +48 -7
  4. package/runtime/abstract.runtime.ts +441 -0
  5. package/runtime/bun/esbuild-runtime-loader.plugin.ts +94 -0
  6. package/runtime/bun/fs/bun-fs.runtime.ts +245 -0
  7. package/runtime/bun/sqlite/bun-sqlite.runtime.ts +279 -0
  8. package/runtime/sitemap.generator.ts +180 -0
  9. package/server/codegen.util.ts +66 -0
  10. package/server/emroute.server.ts +398 -0
  11. package/server/esbuild-manifest.plugin.ts +243 -0
  12. package/server/scanner.util.ts +243 -0
  13. package/server/server-api.type.ts +90 -0
  14. package/src/component/abstract.component.ts +229 -0
  15. package/src/component/page.component.ts +134 -0
  16. package/src/component/widget.component.ts +85 -0
  17. package/src/element/component.element.ts +353 -0
  18. package/src/element/markdown.element.ts +107 -0
  19. package/src/element/slot.element.ts +31 -0
  20. package/src/index.ts +61 -0
  21. package/src/overlay/mod.ts +10 -0
  22. package/src/overlay/overlay.css.ts +170 -0
  23. package/src/overlay/overlay.service.ts +348 -0
  24. package/src/overlay/overlay.type.ts +38 -0
  25. package/src/renderer/spa/base.renderer.ts +186 -0
  26. package/src/renderer/spa/hash.renderer.ts +215 -0
  27. package/src/renderer/spa/html.renderer.ts +382 -0
  28. package/src/renderer/spa/mod.ts +76 -0
  29. package/src/renderer/ssr/html.renderer.ts +159 -0
  30. package/src/renderer/ssr/md.renderer.ts +142 -0
  31. package/src/renderer/ssr/ssr.renderer.ts +286 -0
  32. package/src/route/route.core.ts +316 -0
  33. package/src/route/route.matcher.ts +260 -0
  34. package/src/type/logger.type.ts +24 -0
  35. package/src/type/markdown.type.ts +21 -0
  36. package/src/type/navigation-api.d.ts +95 -0
  37. package/src/type/route.type.ts +149 -0
  38. package/src/type/widget.type.ts +65 -0
  39. package/src/util/html.util.ts +186 -0
  40. package/src/util/logger.util.ts +83 -0
  41. package/src/util/widget-resolve.util.ts +197 -0
  42. package/src/web-doc/index.md +15 -0
  43. package/src/widget/breadcrumb.widget.ts +106 -0
  44. package/src/widget/page-title.widget.ts +52 -0
  45. package/src/widget/widget.parser.ts +89 -0
  46. package/src/widget/widget.registry.ts +51 -0
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, emko.dev
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -1,22 +1,157 @@
1
- # @emkodev/emroute
1
+ <p align="center">
2
+ <img src="doc/logo-full.png" alt="emroute" width="197" height="40">
3
+ </p>
2
4
 
3
- File-based router with triple rendering (SPA, SSR HTML, SSR Markdown). Zero dependencies.
5
+ <p align="center">
6
+ File-based router with triple rendering. Zero dependencies.
7
+ </p>
4
8
 
5
- ## Install
9
+ ---
6
10
 
7
- This package is published on [JSR](https://jsr.io/@emkodev/emroute):
11
+ Every route renders three ways from the same component: as a **Single Page App**
12
+ in the browser, as **server-rendered HTML**, and as **plain Markdown**. No
13
+ separate API layer needed — prefix any route with `/md/` and get text that LLMs,
14
+ scripts, and `curl` can consume directly.
15
+
16
+ ```
17
+ GET /projects/42 → SPA (hydrated in browser)
18
+ GET /html/projects/42 → pre-rendered HTML
19
+ GET /md/projects/42 → plain Markdown
20
+ ```
21
+
22
+ ## Install
8
23
 
9
24
  ```bash
10
- # Deno
11
- deno add jsr:@emkodev/emroute
25
+ bun add @emkodev/emroute
26
+ ```
27
+
28
+ > emroute ships TypeScript source. Your toolchain must handle `.ts` imports
29
+ > (Bun, tsx, esbuild, etc.).
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.
12
34
 
13
- # Node.js / Bun
14
- npx jsr add @emkodev/emroute
35
+ ## How It Works
36
+
37
+ <p align="center">
38
+ <img src="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="doc/diagram-flow-spa.png" alt="SPA flow" width="320" height="320">
45
+ <img src="doc/diagram-flow-ssr-html.png" alt="SSR HTML flow" width="320" height="320">
46
+ <img src="doc/diagram-flow-ssr-md.png" alt="SSR Markdown flow" width="320" height="320">
47
+ </p>
48
+
49
+ The SPA and SSR HTML flows both call `renderHTML()` — same output, different
50
+ delivery. The SSR Markdown flow calls `renderMarkdown()` instead, bypassing
51
+ HTML entirely for plain text output.
52
+
53
+ Routes are files. The filesystem is the config.
54
+
55
+ ```
56
+ routes/
57
+ index.page.md → /
58
+ about.page.html → /about
59
+ 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
15
66
  ```
16
67
 
68
+ A route can be a `.md` file, an `.html` template, a `.ts` component, or a
69
+ combination. When a `.page.ts` exists, it controls data fetching and rendering.
70
+ When it doesn't, the framework renders the `.html` or `.md` file directly.
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
+ }
82
+
83
+ override renderHTML({ data, params, context }: this['RenderArgs']) {
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
+ ```
97
+
98
+ ## Features
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
+ - **Dev server** — zero-config: auto-generates `main.ts`, `index.html`, and route/widget manifests. File watcher with hot reload and bundle serving
118
+
119
+ ## Why Bun?
120
+
121
+ emroute 1.5.x shipped on JSR (Deno's registry). Starting with 1.6.0, emroute
122
+ publishes to npm and targets Bun as the primary runtime.
123
+
124
+ **TL;DR:** JSR's design freezes the entire module graph at publish time. This
125
+ breaks dynamic `import()` of consumer dependencies, peer dependency
126
+ deduplication, and runtime resolution of package entry points for bundling — all
127
+ things a framework with plugin architecture needs. The npm/`node_modules` model
128
+ handles them with zero friction.
129
+
130
+ Full analysis with documentation and issue references:
131
+ [ADR-0017 — Move to Bun ecosystem](doc/architecture/ADR-0017-move-to-bun-ecosystem.md).
132
+
133
+ ## Getting Started
134
+
135
+ See [Setup](doc/01-setup.md) and [First Route](doc/02-first-route.md).
136
+
17
137
  ## Documentation
18
138
 
19
- - [GitHub](https://github.com/vedokme/emroute)
20
- - [JSR](https://jsr.io/@emkodev/emroute)
21
- - [Quick start](https://jsr.io/@emkodev/emroute/doc/quick-start.md)
22
- - [Guide](https://jsr.io/@emkodev/emroute/doc/guide.md)
139
+ - [Setup](doc/01-setup.md) — install and create a server
140
+ - [First route](doc/02-first-route.md) — create your first page
141
+ - [Pages](doc/03-pages.md) — page components, companion files, data fetching
142
+ - [Routing](doc/04-routing.md) — dynamic segments, catch-all, redirects
143
+ - [Nesting](doc/05-nesting.md) — layouts, slots, passthrough pages, tips and tricks
144
+ - [Widgets](doc/06-widgets.md) — interactive islands with data lifecycle
145
+ - [Server](doc/07-server.md) — `createEmrouteServer`, composition, static files
146
+ - [Markdown renderers](doc/08-markdown-renderer.md) — pluggable parser interface and setup
147
+ - [Runtime](doc/09-runtime.md) — abstract runtime, BunFsRuntime, BunSqliteRuntime
148
+ - [SPA modes](doc/10-spa-mode.md) — none, leaf, root, only
149
+ - [Error handling](doc/11-error-handling.md) — widget errors, boundaries, status pages
150
+ - [Shadow DOM](doc/12-shadow-dom.md) — unified architecture, SSR hydration
151
+ - [Hono integration](doc/13-hono.md) — using emroute with Hono
152
+
153
+ ### For contributors and architects
154
+
155
+ - [Architectural decisions](doc/architecture/) — ADR-0001 through ADR-0017
156
+
157
+ <img src="doc/logo-full.png" alt="emroute" width="197" height="40">
package/package.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "name": "@emkodev/emroute",
3
- "version": "1.0.3",
3
+ "version": "1.6.0",
4
4
  "description": "File-based router with triple rendering (SPA, SSR HTML, SSR Markdown). Zero dependencies.",
5
5
  "license": "BSD-3-Clause",
6
6
  "author": "emko.dev",
7
- "homepage": "https://jsr.io/@emkodev/emroute",
8
- "repository": {
9
- "type": "git",
10
- "url": "https://github.com/vedokme/emroute.git"
11
- },
7
+ "type": "module",
12
8
  "keywords": [
13
9
  "router",
14
10
  "routing",
@@ -16,7 +12,52 @@
16
12
  "ssr",
17
13
  "file-based",
18
14
  "framework",
15
+ "bun",
19
16
  "typescript",
20
17
  "markdown"
21
- ]
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/vedokme/emroute.git"
22
+ },
23
+ "files": [
24
+ "src/",
25
+ "server/*.ts",
26
+ "runtime/",
27
+ "LICENSE",
28
+ "README.md"
29
+ ],
30
+ "exports": {
31
+ ".": "./src/index.ts",
32
+ "./spa": "./src/renderer/spa/mod.ts",
33
+ "./overlay": "./src/overlay/mod.ts",
34
+ "./ssr/html": "./src/renderer/ssr/html.renderer.ts",
35
+ "./ssr/md": "./src/renderer/ssr/md.renderer.ts",
36
+ "./server": "./server/emroute.server.ts",
37
+ "./runtime": "./runtime/abstract.runtime.ts",
38
+ "./runtime/sitemap": "./runtime/sitemap.generator.ts",
39
+ "./runtime/bun/fs": "./runtime/bun/fs/bun-fs.runtime.ts",
40
+ "./runtime/bun/sqlite": "./runtime/bun/sqlite/bun-sqlite.runtime.ts"
41
+ },
42
+ "devDependencies": {
43
+ "@eslint/js": "^10.0.1",
44
+ "@types/bun": "^1.3.9",
45
+ "esbuild": "^0.27.3",
46
+ "eslint": "^10.0.2",
47
+ "playwright": "^1.58.2",
48
+ "typescript-eslint": "^8.56.1"
49
+ },
50
+ "scripts": {
51
+ "check": "bun x tsc --noEmit",
52
+ "test": "bun test test/unit",
53
+ "test:unit": "bun test test/unit",
54
+ "test:integration": "bun test test/integration",
55
+ "test:browser": "bun test test/browser",
56
+ "test:browser:none": "bun test test/browser/none",
57
+ "test:browser:leaf": "bun test test/browser/leaf",
58
+ "test:browser:root": "bun test test/browser/root",
59
+ "test:browser:only": "bun test test/browser/only",
60
+ "lint": "eslint .",
61
+ "dev": "echo 'Use a custom server.ts — see doc/01-setup.md'"
62
+ }
22
63
  }