@emkodev/emroute 1.0.3 → 1.6.0-beta.1

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 +142 -12
  3. package/package.json +48 -7
  4. package/runtime/abstract.runtime.ts +439 -0
  5. package/runtime/bun/esbuild-runtime-loader.plugin.ts +94 -0
  6. package/runtime/bun/fs/bun-fs.runtime.ts +244 -0
  7. package/runtime/bun/sqlite/bun-sqlite.runtime.ts +278 -0
  8. package/runtime/sitemap.generator.ts +180 -0
  9. package/server/codegen.util.ts +66 -0
  10. package/server/emroute.server.ts +401 -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,152 @@
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
+ ## How It Works
32
+
33
+ <p align="center">
34
+ <img src="doc/diagram-full.png" alt="emroute architecture" width="480" height="480">
35
+ </p>
36
+
37
+ One component, three rendering paths:
38
+
39
+ <p align="center">
40
+ <img src="doc/diagram-flow-spa.png" alt="SPA flow" width="320" height="320">
41
+ <img src="doc/diagram-flow-ssr-html.png" alt="SSR HTML flow" width="320" height="320">
42
+ <img src="doc/diagram-flow-ssr-md.png" alt="SSR Markdown flow" width="320" height="320">
43
+ </p>
44
+
45
+ The SPA and SSR HTML flows both call `renderHTML()` — same output, different
46
+ delivery. The SSR Markdown flow calls `renderMarkdown()` instead, bypassing
47
+ HTML entirely for plain text output.
48
+
49
+ Routes are files. The filesystem is the config.
50
+
51
+ ```
52
+ routes/
53
+ index.page.md → /
54
+ about.page.html → /about
55
+ projects.page.md → /projects
56
+ projects/
57
+ [id].page.ts → /projects/:id
58
+ [id]/
59
+ tasks.page.ts → /projects/:id/tasks
60
+ 404.page.html → not found
61
+ index.error.ts → root error handler
62
+ ```
12
63
 
13
- # Node.js / Bun
14
- npx jsr add @emkodev/emroute
64
+ A route can be a `.md` file, an `.html` template, a `.ts` component, or a
65
+ combination. When a `.page.ts` exists, it controls data fetching and rendering.
66
+ When it doesn't, the framework renders the `.html` or `.md` file directly.
67
+
68
+ ```ts
69
+ import { PageComponent } from '@emkodev/emroute';
70
+
71
+ class ProjectPage extends PageComponent<{ id: string }, ProjectData> {
72
+ override readonly name = 'project';
73
+
74
+ override async getData({ params }: this['DataArgs']) {
75
+ const res = await fetch(`/api/projects/${params.id}`);
76
+ return res.json();
77
+ }
78
+
79
+ override renderHTML({ data, params, context }: this['RenderArgs']) {
80
+ // context.files.html has the companion .page.html template if it exists
81
+ const template = context.files?.html ?? `<h1>${data.name}</h1>`;
82
+ return template.replaceAll('{{id}}', params.id) + '<router-slot></router-slot>';
83
+ }
84
+
85
+ override renderMarkdown({ data, context }: this['RenderArgs']) {
86
+ // context.files.md has the companion .page.md content if it exists
87
+ return context.files?.md ?? `# ${data.name}\n\nStatus: ${data.status}`;
88
+ }
89
+ }
90
+
91
+ export default new ProjectPage();
15
92
  ```
16
93
 
94
+ ## Features
95
+
96
+ - **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
97
+ - **Triple rendering** — SPA, SSR HTML, SSR Markdown from one component
98
+ - **Companion files** — `.page.html`, `.page.md`, `.page.css` loaded automatically and passed through context
99
+ - **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`
100
+ - **View Transitions** — SPA route changes animate via `document.startViewTransition()`. Progressive enhancement with CSS-only customization
101
+ - **Scoped CSS** — companion `.widget.css` files auto-wrapped in `@scope (widget-{name}) { ... }`
102
+ - **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
103
+ - **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 }`
104
+ - **Error boundaries** — scoped error handlers per route prefix, plus status pages (`404.page.html`) and a root fallback
105
+ - **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
106
+ - **Declarative overlays** — popovers, modals, and toasts with zero JS via Invoker Commands API and CSS keyframe animations. Programmatic API available for dynamic content
107
+ - **Zero dependencies** — native APIs only (URLPattern, custom elements, Navigation API). No framework runtime, no virtual DOM, no build-time magic
108
+ - **Pluggable markdown** — `<mark-down>` custom element with a swappable parser interface; bring your own renderer
109
+ - **Redirects** — declarative `.redirect.ts` files with 301/302 support
110
+ - **Configurable base paths** — `/html/` and `/md/` prefixes are configurable via `BasePath`
111
+ - **SPA modes** — `'root'` (default), `'leaf'`, `'none'`, or `'only'` to control how the server handles non-file requests and SSR endpoints
112
+ - **Sitemap generation** — opt-in `sitemap.xml` from the routes manifest with support for dynamic route enumerators
113
+ - **Dev server** — zero-config: auto-generates `main.ts`, `index.html`, and route/widget manifests. File watcher with hot reload and bundle serving
114
+
115
+ ## Why Bun?
116
+
117
+ emroute 1.5.x shipped on JSR (Deno's registry). Starting with 1.6.0, emroute
118
+ publishes to npm and targets Bun as the primary runtime.
119
+
120
+ **TL;DR:** JSR's design freezes the entire module graph at publish time. This
121
+ breaks dynamic `import()` of consumer dependencies, peer dependency
122
+ deduplication, and runtime resolution of package entry points for bundling — all
123
+ things a framework with plugin architecture needs. The npm/`node_modules` model
124
+ handles them with zero friction.
125
+
126
+ Full analysis with documentation and issue references:
127
+ [ADR-0017 — Move to Bun ecosystem](doc/architecture/ADR-0017-move-to-bun-ecosystem.md).
128
+
129
+ ## Getting Started
130
+
131
+ See [Setup](doc/01-setup.md) and [First Route](doc/02-first-route.md).
132
+
17
133
  ## Documentation
18
134
 
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)
135
+ - [Setup](doc/01-setup.md) — install and create a server
136
+ - [First route](doc/02-first-route.md) — create your first page
137
+ - [Pages](doc/03-pages.md) — page components, companion files, data fetching
138
+ - [Routing](doc/04-routing.md) — dynamic segments, catch-all, redirects
139
+ - [Nesting](doc/05-nesting.md) — layouts, slots, passthrough pages, tips and tricks
140
+ - [Widgets](doc/06-widgets.md) — interactive islands with data lifecycle
141
+ - [Server](doc/07-server.md) — `createEmrouteServer`, composition, static files
142
+ - [Markdown renderers](doc/08-markdown-renderer.md) — pluggable parser interface and setup
143
+ - [Runtime](doc/09-runtime.md) — abstract runtime, BunFsRuntime, BunSqliteRuntime
144
+ - [SPA modes](doc/10-spa-mode.md) — none, leaf, root, only
145
+ - [Error handling](doc/11-error-handling.md) — widget errors, boundaries, status pages
146
+ - [Shadow DOM](doc/12-shadow-dom.md) — unified architecture, SSR hydration
147
+
148
+ ### For contributors and architects
149
+
150
+ - [Architectural decisions](doc/architecture/) — ADR-0001 through ADR-0017
151
+
152
+ <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-beta.1",
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
  }