@emkodev/emroute 1.7.3 → 1.8.0-beta.2
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 +1 -1
- package/core/component/abstract.component.ts +74 -0
- package/{src → core}/component/page.component.ts +3 -61
- package/core/component/widget.component.ts +54 -0
- package/core/pipeline/pipeline.ts +224 -0
- package/{src/renderer/ssr → core/renderer}/html.renderer.ts +26 -47
- package/{src/renderer/ssr → core/renderer}/md.renderer.ts +22 -41
- package/{src/renderer/ssr → core/renderer}/ssr.renderer.ts +44 -58
- package/{src/route → core/router}/route.resolver.ts +1 -10
- package/core/router/route.trie.ts +175 -0
- package/core/runtime/abstract.runtime.ts +47 -0
- package/core/server/emroute.server.ts +324 -0
- package/core/type/component.type.ts +39 -0
- package/core/type/element.type.ts +10 -0
- package/core/type/logger.type.ts +20 -0
- package/core/type/markdown.type.ts +8 -0
- package/core/type/route-tree.type.ts +28 -0
- package/core/type/route.type.ts +75 -0
- package/core/type/widget.type.ts +27 -0
- package/core/util/html.util.ts +50 -0
- package/{src → core}/util/md.util.ts +3 -5
- package/{src/route → core/util}/route-tree.util.ts +0 -2
- package/{src → core}/util/widget-resolve.util.ts +15 -46
- package/{src → core}/widget/widget.parser.ts +2 -23
- package/core/widget/widget.registry.ts +36 -0
- package/dist/core/component/abstract.component.d.ts +48 -0
- package/dist/core/component/abstract.component.js +42 -0
- package/dist/core/component/abstract.component.js.map +1 -0
- package/dist/core/component/page.component.d.ts +23 -0
- package/dist/core/component/page.component.js +49 -0
- package/dist/core/component/page.component.js.map +1 -0
- package/dist/core/component/widget.component.d.ts +17 -0
- package/dist/core/component/widget.component.js +37 -0
- package/dist/core/component/widget.component.js.map +1 -0
- package/dist/core/pipeline/pipeline.d.ts +61 -0
- package/dist/core/pipeline/pipeline.js +189 -0
- package/dist/core/pipeline/pipeline.js.map +1 -0
- package/dist/{src/renderer/ssr → core/renderer}/html.renderer.d.ts +8 -24
- package/dist/{src/renderer/ssr → core/renderer}/html.renderer.js +20 -35
- package/dist/core/renderer/html.renderer.js.map +1 -0
- package/dist/{src/renderer/ssr → core/renderer}/md.renderer.d.ts +6 -21
- package/dist/{src/renderer/ssr → core/renderer}/md.renderer.js +16 -32
- package/dist/core/renderer/md.renderer.js.map +1 -0
- package/dist/{src/renderer/ssr → core/renderer}/ssr.renderer.d.ts +11 -27
- package/dist/{src/renderer/ssr → core/renderer}/ssr.renderer.js +33 -37
- package/dist/core/renderer/ssr.renderer.js.map +1 -0
- package/dist/{src/route → core/router}/route.resolver.d.ts +1 -8
- package/dist/{src/route → core/router}/route.resolver.js +0 -1
- package/dist/core/router/route.resolver.js.map +1 -0
- package/dist/core/router/route.trie.d.ts +32 -0
- package/dist/core/router/route.trie.js +152 -0
- package/dist/core/router/route.trie.js.map +1 -0
- package/dist/core/runtime/abstract.runtime.d.ts +32 -0
- package/dist/core/runtime/abstract.runtime.js +26 -0
- package/dist/core/runtime/abstract.runtime.js.map +1 -0
- package/dist/core/server/emroute.server.d.ts +48 -0
- package/dist/core/server/emroute.server.js +239 -0
- package/dist/core/server/emroute.server.js.map +1 -0
- package/dist/core/server/server.type.d.ts +45 -0
- package/dist/core/server/server.type.js +11 -0
- package/dist/core/server/server.type.js.map +1 -0
- package/dist/core/type/component.type.d.ts +37 -0
- package/dist/core/type/component.type.js +7 -0
- package/dist/core/type/component.type.js.map +1 -0
- package/dist/core/type/element.type.d.ts +9 -0
- package/dist/core/type/element.type.js +5 -0
- package/dist/core/type/element.type.js.map +1 -0
- package/dist/core/type/logger.type.d.ts +14 -0
- package/dist/core/type/logger.type.js +8 -0
- package/dist/core/type/logger.type.js.map +1 -0
- package/dist/core/type/markdown.type.d.ts +7 -0
- package/dist/core/type/markdown.type.js +5 -0
- package/dist/core/type/markdown.type.js.map +1 -0
- package/dist/{src → core}/type/route-tree.type.d.ts +0 -12
- package/dist/{src → core}/type/route-tree.type.js +0 -1
- package/dist/core/type/route-tree.type.js.map +1 -0
- package/dist/core/type/route.type.d.ts +62 -0
- package/dist/core/type/route.type.js +7 -0
- package/dist/core/type/route.type.js.map +1 -0
- package/dist/core/type/widget.type.d.ts +27 -0
- package/dist/core/type/widget.type.js +5 -0
- package/dist/core/type/widget.type.js.map +1 -0
- package/dist/core/util/html.util.d.ts +14 -0
- package/dist/core/util/html.util.js +43 -0
- package/dist/core/util/html.util.js.map +1 -0
- package/dist/{src → core}/util/md.util.d.ts +0 -1
- package/dist/{src → core}/util/md.util.js +0 -2
- package/dist/core/util/md.util.js.map +1 -0
- package/dist/{src/route → core/util}/route-tree.util.js +0 -2
- package/dist/core/util/route-tree.util.js.map +1 -0
- package/dist/core/util/widget-resolve.util.d.ts +28 -0
- package/dist/{src → core}/util/widget-resolve.util.js +12 -42
- package/dist/core/util/widget-resolve.util.js.map +1 -0
- package/dist/{src → core}/widget/widget.parser.d.ts +0 -13
- package/dist/{src → core}/widget/widget.parser.js +1 -22
- package/dist/core/widget/widget.parser.js.map +1 -0
- package/dist/core/widget/widget.registry.d.ts +14 -0
- package/dist/core/widget/widget.registry.js +26 -0
- package/dist/core/widget/widget.registry.js.map +1 -0
- package/dist/emroute.js +1092 -1220
- package/dist/emroute.js.map +36 -5
- package/dist/runtime/abstract.runtime.d.ts +41 -7
- package/dist/runtime/abstract.runtime.js +404 -9
- package/dist/runtime/abstract.runtime.js.map +1 -1
- package/dist/runtime/bun/fs/bun-fs.runtime.d.ts +1 -0
- package/dist/runtime/bun/fs/bun-fs.runtime.js +15 -1
- package/dist/runtime/bun/fs/bun-fs.runtime.js.map +1 -1
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.d.ts +2 -0
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js +8 -0
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js.map +1 -1
- package/dist/runtime/fetch.runtime.d.ts +3 -3
- package/dist/runtime/fetch.runtime.js +3 -3
- package/dist/runtime/sitemap.generator.d.ts +1 -1
- package/dist/runtime/sitemap.generator.js +1 -1
- package/dist/runtime/sitemap.generator.js.map +1 -1
- package/dist/runtime/universal/fs/universal-fs.runtime.d.ts +1 -0
- package/dist/runtime/universal/fs/universal-fs.runtime.js +15 -1
- package/dist/runtime/universal/fs/universal-fs.runtime.js.map +1 -1
- package/dist/server/build.util.d.ts +9 -10
- package/dist/server/build.util.js +11 -31
- package/dist/server/build.util.js.map +1 -1
- package/dist/server/codegen.util.d.ts +1 -1
- package/dist/server/emroute.server.d.ts +8 -35
- package/dist/server/emroute.server.js +7 -351
- package/dist/server/emroute.server.js.map +1 -1
- package/dist/server/esbuild-manifest.plugin.js +1 -1
- package/dist/server/esbuild-manifest.plugin.js.map +1 -1
- package/dist/server/server-api.type.d.ts +3 -71
- package/dist/server/server-api.type.js +1 -8
- package/dist/server/server-api.type.js.map +1 -1
- package/dist/src/element/component.element.d.ts +6 -14
- package/dist/src/element/component.element.js +13 -40
- package/dist/src/element/component.element.js.map +1 -1
- package/dist/src/element/markdown.element.d.ts +2 -2
- package/dist/src/element/markdown.element.js +3 -2
- package/dist/src/element/markdown.element.js.map +1 -1
- package/dist/src/index.d.ts +15 -14
- package/dist/src/index.js +8 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/renderer/spa/emroute.app.d.ts +50 -0
- package/dist/src/renderer/spa/emroute.app.js +246 -0
- package/dist/src/renderer/spa/emroute.app.js.map +1 -0
- package/dist/src/renderer/spa/mod.d.ts +17 -16
- package/dist/src/renderer/spa/mod.js +9 -9
- package/dist/src/renderer/spa/mod.js.map +1 -1
- package/dist/src/renderer/spa/thin-client.d.ts +3 -3
- package/dist/src/renderer/spa/thin-client.js +7 -7
- package/dist/src/renderer/spa/thin-client.js.map +1 -1
- package/dist/src/route/route.core.d.ts +3 -3
- package/dist/src/util/html.util.d.ts +5 -22
- package/dist/src/util/html.util.js +8 -56
- package/dist/src/util/html.util.js.map +1 -1
- package/dist/src/widget/breadcrumb.widget.d.ts +2 -2
- package/dist/src/widget/breadcrumb.widget.js +2 -2
- package/dist/src/widget/breadcrumb.widget.js.map +1 -1
- package/dist/src/widget/page-title.widget.d.ts +1 -1
- package/dist/src/widget/page-title.widget.js +1 -1
- package/dist/src/widget/page-title.widget.js.map +1 -1
- package/package.json +8 -8
- package/runtime/abstract.runtime.ts +433 -17
- package/runtime/bun/fs/bun-fs.runtime.ts +15 -1
- package/runtime/bun/sqlite/bun-sqlite.runtime.ts +9 -0
- package/runtime/fetch.runtime.ts +3 -3
- package/runtime/sitemap.generator.ts +2 -2
- package/runtime/universal/fs/universal-fs.runtime.ts +15 -1
- package/server/build.util.ts +17 -43
- package/server/codegen.util.ts +1 -1
- package/server/emroute.server.ts +12 -426
- package/src/element/component.element.ts +14 -54
- package/src/element/markdown.element.ts +4 -3
- package/src/index.ts +22 -19
- package/src/renderer/spa/{thin-client.ts → emroute.app.ts} +19 -20
- package/src/renderer/spa/mod.ts +22 -22
- package/src/util/html.util.ts +16 -61
- package/src/widget/breadcrumb.widget.ts +3 -3
- package/src/widget/page-title.widget.ts +1 -1
- package/dist/src/component/abstract.component.d.ts +0 -199
- package/dist/src/component/abstract.component.js +0 -84
- package/dist/src/component/abstract.component.js.map +0 -1
- package/dist/src/component/page.component.d.ts +0 -74
- package/dist/src/component/page.component.js +0 -107
- package/dist/src/component/page.component.js.map +0 -1
- package/dist/src/component/widget.component.d.ts +0 -47
- package/dist/src/component/widget.component.js +0 -69
- package/dist/src/component/widget.component.js.map +0 -1
- package/dist/src/renderer/ssr/html.renderer.js.map +0 -1
- package/dist/src/renderer/ssr/md.renderer.js.map +0 -1
- package/dist/src/renderer/ssr/ssr.renderer.js.map +0 -1
- package/dist/src/route/route-tree.util.js.map +0 -1
- package/dist/src/route/route.matcher.d.ts +0 -86
- package/dist/src/route/route.matcher.js +0 -214
- package/dist/src/route/route.matcher.js.map +0 -1
- package/dist/src/route/route.resolver.js.map +0 -1
- package/dist/src/route/route.trie.d.ts +0 -38
- package/dist/src/route/route.trie.js +0 -206
- package/dist/src/route/route.trie.js.map +0 -1
- package/dist/src/type/element.type.d.ts +0 -19
- package/dist/src/type/element.type.js +0 -9
- package/dist/src/type/element.type.js.map +0 -1
- package/dist/src/type/logger.type.d.ts +0 -17
- package/dist/src/type/logger.type.js +0 -9
- package/dist/src/type/logger.type.js.map +0 -1
- package/dist/src/type/markdown.type.d.ts +0 -20
- package/dist/src/type/markdown.type.js +0 -2
- package/dist/src/type/markdown.type.js.map +0 -1
- package/dist/src/type/route-tree.type.js.map +0 -1
- package/dist/src/type/route.type.d.ts +0 -94
- package/dist/src/type/route.type.js +0 -8
- package/dist/src/type/route.type.js.map +0 -1
- package/dist/src/type/widget.type.d.ts +0 -55
- package/dist/src/type/widget.type.js +0 -10
- package/dist/src/type/widget.type.js.map +0 -1
- package/dist/src/util/logger.util.d.ts +0 -26
- package/dist/src/util/logger.util.js +0 -80
- package/dist/src/util/logger.util.js.map +0 -1
- package/dist/src/util/md.util.js.map +0 -1
- package/dist/src/util/widget-resolve.util.d.ts +0 -52
- package/dist/src/util/widget-resolve.util.js.map +0 -1
- package/dist/src/widget/widget.parser.js.map +0 -1
- package/dist/src/widget/widget.registry.d.ts +0 -23
- package/dist/src/widget/widget.registry.js +0 -42
- package/dist/src/widget/widget.registry.js.map +0 -1
- package/runtime/bun/esbuild-runtime-loader.plugin.ts +0 -112
- package/server/esbuild-manifest.plugin.ts +0 -209
- package/server/server-api.type.ts +0 -101
- package/src/component/abstract.component.ts +0 -231
- package/src/component/widget.component.ts +0 -85
- package/src/route/route.core.ts +0 -371
- package/src/route/route.trie.ts +0 -265
- package/src/type/element.type.ts +0 -22
- package/src/type/logger.type.ts +0 -24
- package/src/type/markdown.type.ts +0 -21
- package/src/type/route-tree.type.ts +0 -51
- package/src/type/route.type.ts +0 -124
- package/src/type/widget.type.ts +0 -65
- package/src/util/logger.util.ts +0 -83
- package/src/widget/widget.registry.ts +0 -51
- /package/dist/{src/route → core/util}/route-tree.util.d.ts +0 -0
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* esbuild Virtual Manifest Plugin
|
|
3
|
-
*
|
|
4
|
-
* Intercepts `emroute:routes` and `emroute:widgets` import specifiers.
|
|
5
|
-
* Reads JSON manifests from the runtime and generates TypeScript modules
|
|
6
|
-
* with `moduleLoaders` (dynamic `import()` calls) in-memory — no .g.ts
|
|
7
|
-
* files on disk.
|
|
8
|
-
*
|
|
9
|
-
* This is the single source of truth: JSON manifest → esbuild bundle.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { Runtime } from '../runtime/abstract.runtime.ts';
|
|
13
|
-
import { ROUTES_MANIFEST_PATH, WIDGETS_MANIFEST_PATH } from '../runtime/abstract.runtime.ts';
|
|
14
|
-
import { EMROUTE_VIRTUAL_NS } from './build.util.ts';
|
|
15
|
-
|
|
16
|
-
/** Escape a string for use inside a single-quoted JS/TS string literal. */
|
|
17
|
-
function esc(value: string): string {
|
|
18
|
-
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ManifestPluginOptions {
|
|
22
|
-
runtime: Runtime;
|
|
23
|
-
/**
|
|
24
|
-
* Directory prefix to strip from module paths so that import() calls
|
|
25
|
-
* are relative to the entry point (e.g. 'routes/' strips '/routes/').
|
|
26
|
-
*/
|
|
27
|
-
stripPrefix?: string;
|
|
28
|
-
/** Absolute directory for resolving relative import() paths in generated code. */
|
|
29
|
-
resolveDir: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
-
type EsbuildPlugin = any;
|
|
34
|
-
|
|
35
|
-
export function createManifestPlugin(options: ManifestPluginOptions): EsbuildPlugin {
|
|
36
|
-
const { runtime, stripPrefix = '', resolveDir } = options;
|
|
37
|
-
|
|
38
|
-
const strip = (p: string): string =>
|
|
39
|
-
stripPrefix && p.startsWith(stripPrefix) ? p.slice(stripPrefix.length) : p;
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
name: 'emroute-manifest',
|
|
43
|
-
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
-
setup(build: any) {
|
|
46
|
-
// ── Resolve virtual specifiers ──────────────────────────────────
|
|
47
|
-
build.onResolve(
|
|
48
|
-
{ filter: /^emroute:/ },
|
|
49
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
-
(args: any) => ({ path: args.path, namespace: EMROUTE_VIRTUAL_NS }),
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
// ── Load virtual modules ────────────────────────────────────────
|
|
54
|
-
build.onLoad(
|
|
55
|
-
{ filter: /.*/, namespace: EMROUTE_VIRTUAL_NS },
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
-
async (args: any) => {
|
|
58
|
-
if (args.path === 'emroute:routes') {
|
|
59
|
-
return { contents: await generateRoutesModule(), loader: 'ts' as const, resolveDir };
|
|
60
|
-
}
|
|
61
|
-
if (args.path === 'emroute:widgets') {
|
|
62
|
-
return { contents: await generateWidgetsModule(), loader: 'ts' as const, resolveDir };
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
},
|
|
66
|
-
);
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// ── Routes module generator ───────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
async function generateRoutesModule(): Promise<string> {
|
|
73
|
-
const response = await runtime.query(ROUTES_MANIFEST_PATH);
|
|
74
|
-
if (response.status === 404) {
|
|
75
|
-
return `import type { RouteNode } from '@emkodev/emroute';
|
|
76
|
-
export const routeTree: RouteNode = {};
|
|
77
|
-
export const moduleLoaders: Record<string, () => Promise<unknown>> = {};
|
|
78
|
-
`;
|
|
79
|
-
}
|
|
80
|
-
const raw = await response.json();
|
|
81
|
-
|
|
82
|
-
// Walk the RouteNode tree to collect all .ts module paths for import() loaders
|
|
83
|
-
const tsModulePaths = new Set<string>();
|
|
84
|
-
collectModulePaths(raw, tsModulePaths);
|
|
85
|
-
|
|
86
|
-
// Serialize the tree with stripped file paths
|
|
87
|
-
const strippedTree = stripTreePaths(raw);
|
|
88
|
-
|
|
89
|
-
const moduleLoadersCode = [...tsModulePaths]
|
|
90
|
-
.map((p) => {
|
|
91
|
-
const key = strip(p);
|
|
92
|
-
const rel = key.replace(/^\.?\//, '');
|
|
93
|
-
return ` '${esc(key)}': () => import('./${esc(rel)}'),`;
|
|
94
|
-
})
|
|
95
|
-
.join('\n');
|
|
96
|
-
|
|
97
|
-
return `import type { RouteNode } from '@emkodev/emroute';
|
|
98
|
-
|
|
99
|
-
export const routeTree: RouteNode = ${JSON.stringify(strippedTree, null, 2)};
|
|
100
|
-
|
|
101
|
-
export const moduleLoaders: Record<string, () => Promise<unknown>> = {
|
|
102
|
-
${moduleLoadersCode}
|
|
103
|
-
};
|
|
104
|
-
`;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Recursively collect .ts module paths from a RouteNode tree.
|
|
109
|
-
*/
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
-
function collectModulePaths(node: any, paths: Set<string>): void {
|
|
112
|
-
const modulePath = node.files?.ts ?? node.files?.js;
|
|
113
|
-
if (modulePath) paths.add(modulePath);
|
|
114
|
-
if (node.errorBoundary) paths.add(node.errorBoundary);
|
|
115
|
-
if (node.redirect) paths.add(node.redirect);
|
|
116
|
-
if (node.children) {
|
|
117
|
-
for (const child of Object.values(node.children)) {
|
|
118
|
-
collectModulePaths(child, paths);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (node.dynamic) collectModulePaths(node.dynamic.child, paths);
|
|
122
|
-
if (node.wildcard) collectModulePaths(node.wildcard.child, paths);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Deep-clone a RouteNode tree with stripped file paths.
|
|
127
|
-
*/
|
|
128
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
|
-
function stripTreePaths(node: any): any {
|
|
130
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
-
const out: any = {};
|
|
132
|
-
|
|
133
|
-
if (node.files) {
|
|
134
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
135
|
-
out.files = {} as any;
|
|
136
|
-
for (const [ext, path] of Object.entries(node.files)) {
|
|
137
|
-
if (path) out.files[ext] = strip(path as string);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (node.errorBoundary) out.errorBoundary = strip(node.errorBoundary);
|
|
142
|
-
if (node.redirect) out.redirect = strip(node.redirect);
|
|
143
|
-
|
|
144
|
-
if (node.children) {
|
|
145
|
-
out.children = {};
|
|
146
|
-
for (const [seg, child] of Object.entries(node.children)) {
|
|
147
|
-
out.children[seg] = stripTreePaths(child);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (node.dynamic) {
|
|
152
|
-
out.dynamic = { param: node.dynamic.param, child: stripTreePaths(node.dynamic.child) };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (node.wildcard) {
|
|
156
|
-
out.wildcard = { param: node.wildcard.param, child: stripTreePaths(node.wildcard.child) };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return out;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// ── Widgets module generator ──────────────────────────────────────
|
|
163
|
-
|
|
164
|
-
async function generateWidgetsModule(): Promise<string> {
|
|
165
|
-
const response = await runtime.query(WIDGETS_MANIFEST_PATH);
|
|
166
|
-
if (response.status === 404) {
|
|
167
|
-
return `export const widgetsManifest = { widgets: [], moduleLoaders: {} };`;
|
|
168
|
-
}
|
|
169
|
-
const entries = await response.json();
|
|
170
|
-
|
|
171
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
|
-
const widgetEntries = (entries as any[]).map((e) => {
|
|
173
|
-
const filesStr = e.files
|
|
174
|
-
? `\n files: { ${
|
|
175
|
-
Object.entries(e.files)
|
|
176
|
-
.filter(([_, v]) => v)
|
|
177
|
-
.map(([k, v]) => `${k}: '${esc(strip(v as string))}'`)
|
|
178
|
-
.join(', ')
|
|
179
|
-
} },`
|
|
180
|
-
: '';
|
|
181
|
-
|
|
182
|
-
return ` {
|
|
183
|
-
name: '${esc(e.name)}',
|
|
184
|
-
modulePath: '${esc(strip(e.modulePath))}',
|
|
185
|
-
tagName: '${esc(e.tagName)}',${filesStr}
|
|
186
|
-
}`;
|
|
187
|
-
}).join(',\n');
|
|
188
|
-
|
|
189
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
|
-
const loaderEntries = (entries as any[]).map((e) => {
|
|
191
|
-
const key = strip(e.modulePath);
|
|
192
|
-
const rel = key.replace(/^\.?\//, '');
|
|
193
|
-
return ` '${esc(key)}': () => import('./${esc(rel)}'),`;
|
|
194
|
-
}).join('\n');
|
|
195
|
-
|
|
196
|
-
return `import type { WidgetsManifest } from '@emkodev/emroute';
|
|
197
|
-
|
|
198
|
-
export const widgetsManifest: WidgetsManifest = {
|
|
199
|
-
widgets: [
|
|
200
|
-
${widgetEntries}
|
|
201
|
-
],
|
|
202
|
-
|
|
203
|
-
moduleLoaders: {
|
|
204
|
-
${loaderEntries}
|
|
205
|
-
},
|
|
206
|
-
};
|
|
207
|
-
`;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server API Types
|
|
3
|
-
*
|
|
4
|
-
* Interfaces for the emroute server.
|
|
5
|
-
* Consumers use `createEmrouteServer()` to get a server that handles
|
|
6
|
-
* SSR rendering, static file serving, and route matching.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { RouteNode } from '../src/type/route-tree.type.ts';
|
|
10
|
-
import type { MarkdownRenderer } from '../src/type/markdown.type.ts';
|
|
11
|
-
import type { SpaMode, WidgetManifestEntry } from '../src/type/widget.type.ts';
|
|
12
|
-
import type { ElementManifestEntry } from '../src/type/element.type.ts';
|
|
13
|
-
import type { ContextProvider } from '../src/component/abstract.component.ts';
|
|
14
|
-
import type { BasePath } from '../src/route/route.core.ts';
|
|
15
|
-
import type { WidgetRegistry } from '../src/widget/widget.registry.ts';
|
|
16
|
-
import type { SsrHtmlRouter } from '../src/renderer/ssr/html.renderer.ts';
|
|
17
|
-
import type { SsrMdRouter } from '../src/renderer/ssr/md.renderer.ts';
|
|
18
|
-
|
|
19
|
-
// ── SSR Render Result ──────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
/** Result of rendering a URL through an SSR renderer. */
|
|
22
|
-
export interface SsrRenderResult {
|
|
23
|
-
/** Rendered content (HTML or Markdown) */
|
|
24
|
-
content: string;
|
|
25
|
-
/** HTTP status code */
|
|
26
|
-
status: number;
|
|
27
|
-
/** Page title (from the leaf route's getTitle) */
|
|
28
|
-
title?: string;
|
|
29
|
-
/** Redirect target URL (for 301/302 responses) */
|
|
30
|
-
redirect?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ── Server ─────────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Config for `createEmrouteServer()`.
|
|
37
|
-
*
|
|
38
|
-
* The server reads manifests from the Runtime and handles SSR rendering,
|
|
39
|
-
* static file serving, and route matching.
|
|
40
|
-
*/
|
|
41
|
-
export interface EmrouteServerConfig {
|
|
42
|
-
/** Pre-built route tree (alternative to reading from runtime) */
|
|
43
|
-
routeTree?: RouteNode;
|
|
44
|
-
|
|
45
|
-
/** Pre-built widget registry (alternative to reading from runtime) */
|
|
46
|
-
widgets?: WidgetRegistry;
|
|
47
|
-
|
|
48
|
-
/** SPA mode — controls which routers are constructed and what gets served */
|
|
49
|
-
spa?: SpaMode;
|
|
50
|
-
|
|
51
|
-
/** Base paths for SSR endpoints (default: { html: '/html', md: '/md' }) */
|
|
52
|
-
basePath?: BasePath;
|
|
53
|
-
|
|
54
|
-
/** Page title (fallback when no route provides one) */
|
|
55
|
-
title?: string;
|
|
56
|
-
|
|
57
|
-
/** Markdown renderer for server-side <mark-down> expansion */
|
|
58
|
-
markdownRenderer?: MarkdownRenderer;
|
|
59
|
-
|
|
60
|
-
/** Enrich every ComponentContext with app-level services. */
|
|
61
|
-
extendContext?: ContextProvider;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Pre-bundled module loaders (route + widget modules).
|
|
65
|
-
* When provided, skips `runtime.loadModule()` — used in the browser
|
|
66
|
-
* where modules are already bundled into app.js.
|
|
67
|
-
*/
|
|
68
|
-
moduleLoaders?: Record<string, () => Promise<unknown>>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* An emroute server instance.
|
|
73
|
-
*
|
|
74
|
-
* Handles SSR rendering, static file serving, and route matching.
|
|
75
|
-
* Use `handleRequest(req)` to compose with your own request handling.
|
|
76
|
-
*/
|
|
77
|
-
export interface EmrouteServer {
|
|
78
|
-
/**
|
|
79
|
-
* Handle an HTTP request for SSR routes and bare paths.
|
|
80
|
-
* Returns `null` for unmatched file requests — consumer handles 404.
|
|
81
|
-
*/
|
|
82
|
-
handleRequest(req: Request): Promise<Response | null>;
|
|
83
|
-
|
|
84
|
-
/** The SSR HTML router (null in 'only' mode — no server rendering). */
|
|
85
|
-
readonly htmlRouter: SsrHtmlRouter | null;
|
|
86
|
-
|
|
87
|
-
/** The SSR Markdown router (null in 'only' mode). */
|
|
88
|
-
readonly mdRouter: SsrMdRouter | null;
|
|
89
|
-
|
|
90
|
-
/** The resolved route tree. */
|
|
91
|
-
readonly routeTree: RouteNode;
|
|
92
|
-
|
|
93
|
-
/** Discovered widget entries. */
|
|
94
|
-
readonly widgetEntries: WidgetManifestEntry[];
|
|
95
|
-
|
|
96
|
-
/** Discovered element entries. */
|
|
97
|
-
readonly elementEntries: ElementManifestEntry[];
|
|
98
|
-
|
|
99
|
-
/** The resolved HTML shell. */
|
|
100
|
-
readonly shell: string;
|
|
101
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified Component Architecture
|
|
3
|
-
*
|
|
4
|
-
* Everything is a Component: pages and widgets.
|
|
5
|
-
* Components render differently based on context:
|
|
6
|
-
* - /md/* → Markdown (LLMs, text clients)
|
|
7
|
-
* - /html/* → Pre-rendered HTML (SSR)
|
|
8
|
-
* - SPA → Hydrated custom elements
|
|
9
|
-
*
|
|
10
|
-
* Precedence (like .ts/.html/.md):
|
|
11
|
-
* - renderHTML() if defined → full HTML control
|
|
12
|
-
* - renderMarkdown() → converted to HTML via markdown renderer
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import type { RouteInfo } from '../type/route.type.ts';
|
|
16
|
-
import { escapeHtml } from '../util/html.util.ts';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Context passed to components during rendering.
|
|
20
|
-
* Extends RouteInfo (pathname, pattern, params)
|
|
21
|
-
* with pre-loaded file content and an abort signal.
|
|
22
|
-
*
|
|
23
|
-
* Consumers can extend this interface via module augmentation
|
|
24
|
-
* to add app-level services (RPC clients, auth, feature flags, etc.).
|
|
25
|
-
*/
|
|
26
|
-
/** Shape of companion file contents (html, md, css). Used by generated `.page.files.g.ts` modules. */
|
|
27
|
-
export type FileContents = { html?: string; md?: string; css?: string };
|
|
28
|
-
|
|
29
|
-
export interface ComponentContext extends RouteInfo {
|
|
30
|
-
/** @deprecated Use context.url.pathname */
|
|
31
|
-
readonly pathname: string;
|
|
32
|
-
/** @deprecated Use context.url.searchParams */
|
|
33
|
-
readonly searchParams: URLSearchParams;
|
|
34
|
-
readonly files?: Readonly<FileContents>;
|
|
35
|
-
readonly signal?: AbortSignal;
|
|
36
|
-
/** True when this component is the leaf (matched) route, false when rendered as a layout parent. */
|
|
37
|
-
readonly isLeaf?: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Callback that enriches the base ComponentContext with app-level services.
|
|
42
|
-
* Registered once at router creation; called for every context construction.
|
|
43
|
-
*
|
|
44
|
-
* **1. Register** — always spread `base` to preserve routing/file/signal data:
|
|
45
|
-
* ```ts
|
|
46
|
-
* createSpaHtmlRouter(manifest, {
|
|
47
|
-
* extendContext: (base) => ({ ...base, rpc: myRpcClient }),
|
|
48
|
-
* });
|
|
49
|
-
* ```
|
|
50
|
-
*
|
|
51
|
-
* **2. Access** — expose custom properties to components via module augmentation:
|
|
52
|
-
* ```ts
|
|
53
|
-
* declare module '@emkodev/emroute' {
|
|
54
|
-
* interface ComponentContext { rpc: RpcClient; }
|
|
55
|
-
* }
|
|
56
|
-
* ```
|
|
57
|
-
* or per-component via the third generic:
|
|
58
|
-
* ```ts
|
|
59
|
-
* class MyPage extends PageComponent<Params, Data, AppContext> {}
|
|
60
|
-
* ```
|
|
61
|
-
*/
|
|
62
|
-
export type ContextProvider = (base: ComponentContext) => ComponentContext;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Render context determines how components are rendered.
|
|
66
|
-
*/
|
|
67
|
-
export type RenderContext = 'markdown' | 'html' | 'spa';
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Abstract base class for all components.
|
|
71
|
-
*
|
|
72
|
-
* Subclasses must implement:
|
|
73
|
-
* - name: unique identifier for custom element tag
|
|
74
|
-
* - getData(): fetch/compute data
|
|
75
|
-
* - renderMarkdown(): render as markdown
|
|
76
|
-
*
|
|
77
|
-
* Optional override:
|
|
78
|
-
* - renderHTML(): custom HTML rendering (defaults to markdown→HTML conversion)
|
|
79
|
-
* - validateParams(): params validation
|
|
80
|
-
*
|
|
81
|
-
* @typeParam TContext — custom context shape; defaults to ComponentContext.
|
|
82
|
-
* Use with `extendContext` on the router to inject app-level services.
|
|
83
|
-
* See {@link ContextProvider} for details.
|
|
84
|
-
*/
|
|
85
|
-
export abstract class Component<
|
|
86
|
-
TParams = unknown,
|
|
87
|
-
TData = unknown,
|
|
88
|
-
TContext extends ComponentContext = ComponentContext,
|
|
89
|
-
> {
|
|
90
|
-
/** Type carrier for getData args — use as `this['DataArgs']` in overrides. */
|
|
91
|
-
declare readonly DataArgs: {
|
|
92
|
-
params: TParams;
|
|
93
|
-
signal?: AbortSignal;
|
|
94
|
-
context: TContext;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/** Type carrier for render args — use as `this['RenderArgs']` in overrides. */
|
|
98
|
-
declare readonly RenderArgs: {
|
|
99
|
-
data: TData | null;
|
|
100
|
-
params: TParams;
|
|
101
|
-
context: TContext;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/** Unique name in kebab-case. Used for custom element: `<widget-{name}>` */
|
|
105
|
-
abstract readonly name: string;
|
|
106
|
-
|
|
107
|
-
/** Host element reference, set by ComponentElement in the browser. */
|
|
108
|
-
element?: HTMLElement | undefined;
|
|
109
|
-
|
|
110
|
-
/** Associated file paths for pre-loaded content (html, md, css). */
|
|
111
|
-
readonly files?: { html?: string; md?: string; css?: string };
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* When true, SSR serializes the getData() result into the element's
|
|
115
|
-
* light DOM so the client can access it immediately in hydrate()
|
|
116
|
-
* without re-fetching.
|
|
117
|
-
*
|
|
118
|
-
* Default is false — hydrate() receives `data: null`. Most widgets
|
|
119
|
-
* don't need this because the rendered Shadow DOM already contains
|
|
120
|
-
* the visual representation of the data.
|
|
121
|
-
*
|
|
122
|
-
* If you find yourself parsing the shadow DOM in hydrate() trying to
|
|
123
|
-
* reconstruct the original data object, set this to true instead.
|
|
124
|
-
* The server-fetched data will be available as `args.data` in hydrate().
|
|
125
|
-
*/
|
|
126
|
-
readonly exposeSsrData?: boolean;
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Fetch or compute data based on params.
|
|
130
|
-
* Called server-side for SSR, client-side for SPA.
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* ```ts
|
|
134
|
-
* override async getData({ params, signal }: this['DataArgs']) {
|
|
135
|
-
* const res = await fetch(`/api/${params.id}`, { signal });
|
|
136
|
-
* return res.json();
|
|
137
|
-
* }
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
abstract getData(args: this['DataArgs']): Promise<TData | null>;
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Render as markdown.
|
|
144
|
-
* This is the canonical content representation.
|
|
145
|
-
*
|
|
146
|
-
* @example
|
|
147
|
-
* ```ts
|
|
148
|
-
* override renderMarkdown({ data }: this['RenderArgs']) {
|
|
149
|
-
* return `# ${data?.title}`;
|
|
150
|
-
* }
|
|
151
|
-
* ```
|
|
152
|
-
*/
|
|
153
|
-
abstract renderMarkdown(args: this['RenderArgs']): string;
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Render as HTML for browser context.
|
|
157
|
-
*
|
|
158
|
-
* Default implementation converts renderMarkdown() output to HTML.
|
|
159
|
-
* Override for custom HTML rendering with rich styling/interactivity.
|
|
160
|
-
*/
|
|
161
|
-
renderHTML(args: this['RenderArgs']): string {
|
|
162
|
-
if (args.data === null) {
|
|
163
|
-
return `<div data-component="${this.name}">Loading...</div>`;
|
|
164
|
-
}
|
|
165
|
-
// Default: wrap markdown in a container
|
|
166
|
-
// The actual markdown→HTML conversion happens at render time
|
|
167
|
-
const markdown = this.renderMarkdown({
|
|
168
|
-
data: args.data,
|
|
169
|
-
params: args.params,
|
|
170
|
-
context: args.context,
|
|
171
|
-
});
|
|
172
|
-
return `<div data-component="${this.name}" data-markdown>${escapeHtml(markdown)}</div>`;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Hydration hook called after SSR content is adopted or after SPA rendering.
|
|
177
|
-
* Use to attach event listeners to existing DOM without re-rendering.
|
|
178
|
-
*
|
|
179
|
-
* @example
|
|
180
|
-
* ```ts
|
|
181
|
-
* override hydrate({ data, params, context }: this['RenderArgs']) {
|
|
182
|
-
* const button = this.element?.querySelector('button');
|
|
183
|
-
* button?.addEventListener('click', () => this.deleteItem(data.id));
|
|
184
|
-
* }
|
|
185
|
-
* ```
|
|
186
|
-
*/
|
|
187
|
-
hydrate?(args: this['RenderArgs']): void;
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Cleanup hook called when the component is removed from the DOM.
|
|
191
|
-
* Use for clearing timers, removing event listeners, unmounting
|
|
192
|
-
* third-party renderers, closing connections, etc.
|
|
193
|
-
*
|
|
194
|
-
* Intentionally synchronous (called from disconnectedCallback). You can
|
|
195
|
-
* fire async cleanup here, but it will not be awaited.
|
|
196
|
-
*/
|
|
197
|
-
destroy?(): void;
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Validate params.
|
|
201
|
-
* @returns Error message if invalid, undefined if valid.
|
|
202
|
-
*/
|
|
203
|
-
validateParams?(params: TParams): string | undefined;
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Render error state.
|
|
207
|
-
*/
|
|
208
|
-
renderError(args: { error: unknown; params: TParams }): string {
|
|
209
|
-
const msg = args.error instanceof Error ? args.error.message : String(args.error);
|
|
210
|
-
return `<div data-component="${this.name}">Error: ${escapeHtml(msg)}</div>`;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Render error as markdown.
|
|
215
|
-
*/
|
|
216
|
-
renderMarkdownError(error: unknown): string {
|
|
217
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
218
|
-
return `> **Error** (\`${this.name}\`): ${msg}`;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Component manifest entry for code generation.
|
|
224
|
-
*/
|
|
225
|
-
export interface ComponentManifestEntry {
|
|
226
|
-
name: string;
|
|
227
|
-
modulePath: string;
|
|
228
|
-
tagName: string;
|
|
229
|
-
type: 'page' | 'widget';
|
|
230
|
-
pattern?: string;
|
|
231
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WidgetComponent — embeddable unit within page content.
|
|
3
|
-
*
|
|
4
|
-
* Everything reusable that is not a page is a Widget.
|
|
5
|
-
* Widgets render across all contexts (HTML, Markdown, SPA) and are
|
|
6
|
-
* resolved by name via WidgetRegistry.
|
|
7
|
-
*
|
|
8
|
-
* Pages live in the routes manifest. Widgets live in the registry.
|
|
9
|
-
*
|
|
10
|
-
* Default rendering fallback chains (parallel to PageComponent):
|
|
11
|
-
* - renderHTML: html file → md file in <mark-down> → base Component default
|
|
12
|
-
* - renderMarkdown: md file → ''
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { Component, type ComponentContext } from './abstract.component.ts';
|
|
16
|
-
import { escapeHtml, scopeWidgetCss } from '../util/html.util.ts';
|
|
17
|
-
|
|
18
|
-
export abstract class WidgetComponent<
|
|
19
|
-
TParams = unknown,
|
|
20
|
-
TData = unknown,
|
|
21
|
-
TContext extends ComponentContext = ComponentContext,
|
|
22
|
-
> extends Component<TParams, TData, TContext> {
|
|
23
|
-
/**
|
|
24
|
-
* Render widget as HTML.
|
|
25
|
-
*
|
|
26
|
-
* Fallback chain:
|
|
27
|
-
* 1. html file content from context
|
|
28
|
-
* 2. md file content wrapped in `<mark-down>`
|
|
29
|
-
* 3. base Component default (markdown→HTML conversion)
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* override renderHTML({ data, params }: this['RenderArgs']) {
|
|
34
|
-
* return `<span>${params.coin}: $${data?.price}</span>`;
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
override renderHTML(
|
|
39
|
-
args: this['RenderArgs'],
|
|
40
|
-
): string {
|
|
41
|
-
const files = args.context.files;
|
|
42
|
-
// @scope needed for SSR Light DOM output; redundant but harmless in SPA Shadow DOM
|
|
43
|
-
const style = files?.css ? `<style>${scopeWidgetCss(files.css, this.name)}</style>\n` : '';
|
|
44
|
-
|
|
45
|
-
if (files?.html) {
|
|
46
|
-
return style + files.html;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (files?.md) {
|
|
50
|
-
return `${style}<mark-down>${escapeHtml(files.md)}</mark-down>`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (style) {
|
|
54
|
-
return style + super.renderHTML(args);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return super.renderHTML(args);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Render widget as Markdown.
|
|
62
|
-
*
|
|
63
|
-
* Fallback chain:
|
|
64
|
-
* 1. md file content from context
|
|
65
|
-
* 2. empty string
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```ts
|
|
69
|
-
* override renderMarkdown({ data, params }: this['RenderArgs']) {
|
|
70
|
-
* return `**${params.coin}**: $${data?.price}`;
|
|
71
|
-
* }
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
override renderMarkdown(
|
|
75
|
-
args: this['RenderArgs'],
|
|
76
|
-
): string {
|
|
77
|
-
const files = args.context.files;
|
|
78
|
-
|
|
79
|
-
if (files?.md) {
|
|
80
|
-
return files.md;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return '';
|
|
84
|
-
}
|
|
85
|
-
}
|