@emkodev/emroute 1.8.0-beta.1 → 1.8.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.
- package/core/component/abstract.component.ts +112 -0
- package/core/renderer/html.renderer.ts +8 -3
- package/core/renderer/md.renderer.ts +4 -3
- package/core/renderer/ssr.renderer.ts +3 -6
- package/core/router/route.trie.ts +4 -4
- package/core/server/emroute.server.ts +5 -27
- package/core/util/md.util.ts +3 -3
- package/core/util/widget-resolve.util.ts +3 -4
- package/core/widget/widget.parser.ts +2 -2
- package/core/widget/widget.registry.ts +17 -5
- package/dist/core/component/abstract.component.d.ts +27 -0
- package/dist/core/component/abstract.component.js +65 -0
- package/dist/core/component/abstract.component.js.map +1 -1
- package/dist/core/renderer/html.renderer.js +9 -3
- package/dist/core/renderer/html.renderer.js.map +1 -1
- package/dist/core/renderer/md.renderer.js +4 -3
- package/dist/core/renderer/md.renderer.js.map +1 -1
- package/dist/core/renderer/ssr.renderer.d.ts +0 -10
- package/dist/core/renderer/ssr.renderer.js +0 -2
- package/dist/core/renderer/ssr.renderer.js.map +1 -1
- package/dist/core/router/route.trie.js +1 -1
- package/dist/core/router/route.trie.js.map +1 -1
- package/dist/core/server/emroute.server.js +4 -26
- package/dist/core/server/emroute.server.js.map +1 -1
- package/dist/core/util/md.util.js.map +1 -1
- package/dist/core/util/widget-resolve.util.d.ts +1 -5
- package/dist/core/util/widget-resolve.util.js +2 -2
- package/dist/core/util/widget-resolve.util.js.map +1 -1
- package/dist/core/widget/widget.parser.js +1 -1
- package/dist/core/widget/widget.parser.js.map +1 -1
- package/dist/core/widget/widget.registry.d.ts +3 -2
- package/dist/core/widget/widget.registry.js +12 -5
- package/dist/core/widget/widget.registry.js.map +1 -1
- package/dist/emroute.js +122 -72
- package/dist/emroute.js.map +14 -14
- package/dist/runtime/abstract.runtime.js +18 -10
- package/dist/runtime/abstract.runtime.js.map +1 -1
- package/dist/src/element/component.element.d.ts +3 -8
- package/dist/src/element/component.element.js +10 -24
- package/dist/src/element/component.element.js.map +1 -1
- package/dist/src/renderer/spa/emroute.app.js +1 -1
- package/dist/src/renderer/spa/emroute.app.js.map +1 -1
- package/package.json +1 -1
- package/runtime/abstract.runtime.ts +33 -25
- package/src/element/component.element.ts +9 -30
- package/src/renderer/spa/emroute.app.ts +1 -1
|
@@ -62,6 +62,118 @@ export abstract class Component<
|
|
|
62
62
|
destroy?(): void;
|
|
63
63
|
validateParams?(params: TParams): string | undefined;
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* @experimental
|
|
67
|
+
*
|
|
68
|
+
* Parse a template and return a reusable fill function.
|
|
69
|
+
* Call once, apply many times with different slot values.
|
|
70
|
+
*
|
|
71
|
+
* **String signatures** (SSR + SPA `renderHTML`/`renderMarkdown`):
|
|
72
|
+
* - `experimentalUseTemplate(html, id)` — Extracts `<template id>`, fills `<slot name>`.
|
|
73
|
+
* - `experimentalUseTemplate(md, id)` — Extracts `` ```template:id ``, fills `slot:name`.
|
|
74
|
+
*
|
|
75
|
+
* **DOM signature** (browser `hydrate()`):
|
|
76
|
+
* - `experimentalUseTemplate(id)` — Finds `<template>` in shadow DOM,
|
|
77
|
+
* returns a function that clones and fills slots as `DocumentFragment`.
|
|
78
|
+
*
|
|
79
|
+
* Markdown templates work in both `renderMarkdown()` and `renderHTML()`.
|
|
80
|
+
* For HTML output from a markdown-only companion, wrap the result in `<mark-down>`:
|
|
81
|
+
* ```typescript
|
|
82
|
+
* override renderHTML({ data, context }: this['RenderArgs']): string {
|
|
83
|
+
* const card = this.experimentalUseTemplate(context.files!.md!, 'card');
|
|
84
|
+
* return `<mark-down>${escapeHtml(data.items.map(card).join('\n'))}</mark-down>`;
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Throws if the template id is not found in the source.
|
|
89
|
+
*/
|
|
90
|
+
experimentalUseTemplate(source: string, id: string): (slots?: Record<string, string>) => string;
|
|
91
|
+
experimentalUseTemplate(id: string): (slots?: Record<string, string>) => DocumentFragment;
|
|
92
|
+
experimentalUseTemplate(sourceOrId: string, id?: string): (slots?: Record<string, string>) => string | DocumentFragment {
|
|
93
|
+
// DOM path: single arg = template id, find in shadow DOM
|
|
94
|
+
if (id === undefined) {
|
|
95
|
+
const templateId = sourceOrId;
|
|
96
|
+
const shadowRoot = (this.element as HTMLElement | undefined)?.shadowRoot;
|
|
97
|
+
const template = shadowRoot?.querySelector<HTMLTemplateElement>(`template#${templateId}`);
|
|
98
|
+
if (!template) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`[${this.name}] Template "#${templateId}" not found in shadow DOM. ` +
|
|
101
|
+
'Ensure the <template> element exists in the companion HTML.',
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return (slots) => {
|
|
106
|
+
const clone = template.content.cloneNode(true) as DocumentFragment;
|
|
107
|
+
if (!slots) return clone;
|
|
108
|
+
|
|
109
|
+
for (const [name, content] of Object.entries(slots)) {
|
|
110
|
+
const selector = name === 'default' ? 'slot:not([name])' : `slot[name="${name}"]`;
|
|
111
|
+
const slot = clone.querySelector(selector);
|
|
112
|
+
if (!slot) continue;
|
|
113
|
+
const temp = document.createElement('template');
|
|
114
|
+
temp.innerHTML = content;
|
|
115
|
+
slot.replaceWith(temp.content);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return clone;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const source = sourceOrId;
|
|
123
|
+
|
|
124
|
+
// Markdown path: ```template:id ... ```
|
|
125
|
+
const mdPattern = new RegExp(
|
|
126
|
+
'```template:' + id + '\\n([\\s\\S]*?)```',
|
|
127
|
+
);
|
|
128
|
+
const mdMatch = source.match(mdPattern);
|
|
129
|
+
if (mdMatch) {
|
|
130
|
+
const skeleton = mdMatch[1]!;
|
|
131
|
+
|
|
132
|
+
return (slots) => {
|
|
133
|
+
if (!slots) return skeleton;
|
|
134
|
+
let result = skeleton;
|
|
135
|
+
for (const [name, content] of Object.entries(slots)) {
|
|
136
|
+
result = result.replaceAll(`slot:${name}`, content);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// HTML path: <template id="...">...</template>
|
|
143
|
+
const htmlPattern = new RegExp(
|
|
144
|
+
`<template\\s+id=["']${id}["'][^>]*>([\\s\\S]*?)</template>`,
|
|
145
|
+
);
|
|
146
|
+
const htmlMatch = source.match(htmlPattern);
|
|
147
|
+
if (!htmlMatch) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`[${this.name}] Template "${id}" not found in source. ` +
|
|
150
|
+
'Expected <template id="' + id + '"> in HTML or ```template:' + id + ' in markdown.',
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const skeleton = htmlMatch[1]!;
|
|
155
|
+
|
|
156
|
+
return (slots) => {
|
|
157
|
+
if (!slots) return skeleton;
|
|
158
|
+
|
|
159
|
+
let result = skeleton;
|
|
160
|
+
|
|
161
|
+
for (const [name, content] of Object.entries(slots)) {
|
|
162
|
+
if (name === 'default') continue;
|
|
163
|
+
result = result.replaceAll(
|
|
164
|
+
new RegExp(`<slot\\s+name=["']${name}["'][^>]*>[\\s\\S]*?</slot>`, 'g'),
|
|
165
|
+
content,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if ('default' in slots) {
|
|
170
|
+
result = result.replaceAll(/<slot\s*>[\s\S]*?<\/slot>/g, slots['default']!);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
65
177
|
renderError(args: { error: unknown; params: TParams }): string {
|
|
66
178
|
const msg = args.error instanceof Error ? args.error.message : String(args.error);
|
|
67
179
|
return `<div data-component="${this.name}">Error: ${escapeHtml(msg)}</div>`;
|
|
@@ -70,9 +70,14 @@ export class SsrHtmlRenderer extends SsrRenderer {
|
|
|
70
70
|
content,
|
|
71
71
|
this.widgets,
|
|
72
72
|
routeInfo,
|
|
73
|
-
(name
|
|
74
|
-
const
|
|
75
|
-
|
|
73
|
+
async (name) => {
|
|
74
|
+
const modulePath = this.widgets!.getModulePath(name);
|
|
75
|
+
if (modulePath) {
|
|
76
|
+
const mod = await this.pipeline.loadModule(modulePath);
|
|
77
|
+
const inlined = this.pipeline.getModuleFiles(mod);
|
|
78
|
+
if (inlined) return inlined;
|
|
79
|
+
}
|
|
80
|
+
return {};
|
|
76
81
|
},
|
|
77
82
|
this.pipeline.contextProvider,
|
|
78
83
|
this.logger,
|
|
@@ -102,9 +102,10 @@ export class SsrMdRenderer extends SsrRenderer {
|
|
|
102
102
|
|
|
103
103
|
try {
|
|
104
104
|
let files: { html?: string; md?: string } | undefined;
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
|
|
105
|
+
const modulePath = this.widgets!.getModulePath(block.widgetName);
|
|
106
|
+
if (modulePath) {
|
|
107
|
+
const mod = await this.pipeline.loadModule(modulePath);
|
|
108
|
+
files = this.pipeline.getModuleFiles(mod);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
const baseContext: ComponentContext = {
|
|
@@ -20,7 +20,6 @@ import type { WidgetRegistry } from '../widget/widget.registry.ts';
|
|
|
20
20
|
/** Options for SSR renderers. */
|
|
21
21
|
export interface SsrRendererOptions {
|
|
22
22
|
widgets?: WidgetRegistry;
|
|
23
|
-
widgetFiles?: Record<string, { html?: string; md?: string; css?: string }>;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
/**
|
|
@@ -29,7 +28,6 @@ export interface SsrRendererOptions {
|
|
|
29
28
|
export abstract class SsrRenderer {
|
|
30
29
|
protected readonly pipeline: Pipeline;
|
|
31
30
|
protected widgets: WidgetRegistry | null;
|
|
32
|
-
protected widgetFiles: Record<string, { html?: string; md?: string; css?: string }>;
|
|
33
31
|
protected abstract readonly label: string;
|
|
34
32
|
|
|
35
33
|
protected readonly logger: Logger;
|
|
@@ -38,7 +36,6 @@ export abstract class SsrRenderer {
|
|
|
38
36
|
this.pipeline = pipeline;
|
|
39
37
|
this.logger = pipeline.logger;
|
|
40
38
|
this.widgets = options.widgets ?? null;
|
|
41
|
-
this.widgetFiles = options.widgetFiles ?? {};
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
/**
|
|
@@ -140,7 +137,7 @@ export abstract class SsrRenderer {
|
|
|
140
137
|
|
|
141
138
|
const segments: { route: RouteConfig; isLeaf: boolean }[] = [];
|
|
142
139
|
for (let i = 0; i < hierarchy.length; i++) {
|
|
143
|
-
const routePattern = hierarchy[i]
|
|
140
|
+
const routePattern = hierarchy[i]!;
|
|
144
141
|
let route = await this.pipeline.findRoute(routePattern);
|
|
145
142
|
|
|
146
143
|
if (!route && routePattern === '/') {
|
|
@@ -164,7 +161,7 @@ export abstract class SsrRenderer {
|
|
|
164
161
|
let lastRenderedPattern = '';
|
|
165
162
|
|
|
166
163
|
for (let i = 0; i < segments.length; i++) {
|
|
167
|
-
const { content, title } = results[i]
|
|
164
|
+
const { content, title } = results[i]!;
|
|
168
165
|
|
|
169
166
|
if (title) {
|
|
170
167
|
pageTitle = title;
|
|
@@ -184,7 +181,7 @@ export abstract class SsrRenderer {
|
|
|
184
181
|
result = injected;
|
|
185
182
|
}
|
|
186
183
|
|
|
187
|
-
lastRenderedPattern = segments[i]
|
|
184
|
+
lastRenderedPattern = segments[i]!.route.pattern;
|
|
188
185
|
}
|
|
189
186
|
|
|
190
187
|
result = this.stripSlots(result);
|
|
@@ -107,7 +107,7 @@ export class RouteTrie implements RouteResolver {
|
|
|
107
107
|
return undefined;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
const segment = segments[index]
|
|
110
|
+
const segment = segments[index]!;
|
|
111
111
|
|
|
112
112
|
// Static
|
|
113
113
|
const staticChild = node.children?.[segment];
|
|
@@ -130,9 +130,9 @@ export class RouteTrie implements RouteResolver {
|
|
|
130
130
|
// Wildcard
|
|
131
131
|
if (node.wildcard && (node.wildcard.child.files || node.wildcard.child.redirect)) {
|
|
132
132
|
const { param, child } = node.wildcard;
|
|
133
|
-
let rest = this.safeDecode(
|
|
133
|
+
let rest = this.safeDecode(segment);
|
|
134
134
|
for (let i = index + 1; i < segments.length; i++) {
|
|
135
|
-
rest += '/' + this.safeDecode(segments[i]);
|
|
135
|
+
rest += '/' + this.safeDecode(segments[i]!);
|
|
136
136
|
}
|
|
137
137
|
const wp = pattern === '/' ? `/:${param}*` : `${pattern}/:${param}*`;
|
|
138
138
|
return {
|
|
@@ -155,7 +155,7 @@ export class RouteTrie implements RouteResolver {
|
|
|
155
155
|
return node.errorBoundary ?? deepest;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const segment = segments[index]
|
|
158
|
+
const segment = segments[index]!;
|
|
159
159
|
|
|
160
160
|
const staticChild = node.children?.[segment];
|
|
161
161
|
if (staticChild) {
|
|
@@ -89,20 +89,12 @@ export class Emroute {
|
|
|
89
89
|
// ── Widgets ───────────────────────────────────────────────────────
|
|
90
90
|
|
|
91
91
|
let widgets: WidgetRegistry | undefined = config.widgets;
|
|
92
|
-
let widgetFiles: Record<string, { html?: string; md?: string; css?: string }> = {};
|
|
93
92
|
|
|
94
93
|
const widgetsResponse = await runtime.query(WIDGETS_MANIFEST_PATH);
|
|
95
94
|
if (widgetsResponse.status !== 404) {
|
|
96
95
|
const entries: WidgetManifestEntry[] = await widgetsResponse.json();
|
|
97
|
-
if (config.widgets) {
|
|
98
|
-
widgets =
|
|
99
|
-
for (const entry of entries) {
|
|
100
|
-
if (entry.files) widgetFiles[entry.name] = entry.files;
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
const imported = await Emroute.importWidgets(entries, runtime);
|
|
104
|
-
widgets = imported.registry;
|
|
105
|
-
widgetFiles = imported.widgetFiles;
|
|
96
|
+
if (!config.widgets) {
|
|
97
|
+
widgets = await Emroute.importWidgets(entries, runtime);
|
|
106
98
|
}
|
|
107
99
|
}
|
|
108
100
|
|
|
@@ -115,12 +107,10 @@ export class Emroute {
|
|
|
115
107
|
ssrHtmlRenderer = new SsrHtmlRenderer(pipeline, {
|
|
116
108
|
...(config.markdownRenderer ? { markdownRenderer: config.markdownRenderer } : {}),
|
|
117
109
|
...(widgets ? { widgets } : {}),
|
|
118
|
-
widgetFiles,
|
|
119
110
|
});
|
|
120
111
|
|
|
121
112
|
ssrMdRenderer = new SsrMdRenderer(pipeline, {
|
|
122
113
|
...(widgets ? { widgets } : {}),
|
|
123
|
-
widgetFiles,
|
|
124
114
|
});
|
|
125
115
|
}
|
|
126
116
|
|
|
@@ -266,12 +256,8 @@ export class Emroute {
|
|
|
266
256
|
private static async importWidgets(
|
|
267
257
|
entries: WidgetManifestEntry[],
|
|
268
258
|
runtime: Runtime,
|
|
269
|
-
): Promise<{
|
|
270
|
-
registry: WidgetRegistry;
|
|
271
|
-
widgetFiles: Record<string, { html?: string; md?: string; css?: string }>;
|
|
272
|
-
}> {
|
|
259
|
+
): Promise<WidgetRegistry> {
|
|
273
260
|
const registry = new WidgetRegistry();
|
|
274
|
-
const widgetFiles: Record<string, { html?: string; md?: string; css?: string }> = {};
|
|
275
261
|
|
|
276
262
|
for (const entry of entries) {
|
|
277
263
|
try {
|
|
@@ -282,21 +268,13 @@ export class Emroute {
|
|
|
282
268
|
const mod = await runtime.loadModule(runtimePath) as Record<string, unknown>;
|
|
283
269
|
const instance = Emroute.extractWidgetExport(mod);
|
|
284
270
|
if (!instance) continue;
|
|
285
|
-
registry.add(instance);
|
|
286
|
-
|
|
287
|
-
const inlined = mod.__files;
|
|
288
|
-
if (inlined && typeof inlined === 'object') {
|
|
289
|
-
widgetFiles[entry.name] = inlined as { html?: string; md?: string; css?: string };
|
|
290
|
-
} else if (entry.files) {
|
|
291
|
-
widgetFiles[entry.name] = entry.files;
|
|
292
|
-
}
|
|
271
|
+
registry.add(instance, runtimePath);
|
|
293
272
|
} catch (e) {
|
|
294
273
|
console.error(`[emroute] Failed to load widget ${entry.modulePath}:`, e);
|
|
295
|
-
if (entry.files) widgetFiles[entry.name] = entry.files;
|
|
296
274
|
}
|
|
297
275
|
}
|
|
298
276
|
|
|
299
|
-
return
|
|
277
|
+
return registry;
|
|
300
278
|
}
|
|
301
279
|
|
|
302
280
|
private static buildHtmlShell(title: string, htmlBase: string): string {
|
package/core/util/md.util.ts
CHANGED
|
@@ -15,14 +15,14 @@ export function rewriteMdLinks(markdown: string, base: string, skipPrefixes: str
|
|
|
15
15
|
let inCodeBlock = false;
|
|
16
16
|
|
|
17
17
|
for (let i = 0; i < lines.length; i++) {
|
|
18
|
-
if (lines[i]
|
|
18
|
+
if (lines[i]!.startsWith('```')) {
|
|
19
19
|
inCodeBlock = !inCodeBlock;
|
|
20
20
|
continue;
|
|
21
21
|
}
|
|
22
22
|
if (inCodeBlock) continue;
|
|
23
23
|
|
|
24
|
-
lines[i] = lines[i]
|
|
25
|
-
lines[i] = lines[i]
|
|
24
|
+
lines[i] = lines[i]!.replaceAll(inlineRe, `](${prefix}`);
|
|
25
|
+
lines[i] = lines[i]!.replaceAll(refRe, `$1${prefix}`);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return lines.join('\n');
|
|
@@ -57,7 +57,6 @@ export function resolveWidgetTags(
|
|
|
57
57
|
routeInfo: RouteInfo,
|
|
58
58
|
loadFiles?: (
|
|
59
59
|
widgetName: string,
|
|
60
|
-
declaredFiles?: { html?: string; md?: string; css?: string },
|
|
61
60
|
) => Promise<{ html?: string; md?: string; css?: string }>,
|
|
62
61
|
contextProvider?: ContextProvider,
|
|
63
62
|
logger: Logger = defaultLogger,
|
|
@@ -77,7 +76,7 @@ export function resolveWidgetTags(
|
|
|
77
76
|
};
|
|
78
77
|
|
|
79
78
|
const resolve = async (match: RegExpExecArray): Promise<string> => {
|
|
80
|
-
const widgetName = match.groups!.name
|
|
79
|
+
const widgetName = match.groups!.name!;
|
|
81
80
|
const attrsString = match.groups!.attrs?.trim() ?? '';
|
|
82
81
|
const widget = registry.get(widgetName);
|
|
83
82
|
|
|
@@ -88,7 +87,7 @@ export function resolveWidgetTags(
|
|
|
88
87
|
try {
|
|
89
88
|
let files: { html?: string; md?: string; css?: string } | undefined;
|
|
90
89
|
if (loadFiles) {
|
|
91
|
-
files = await loadFiles(widgetName
|
|
90
|
+
files = await loadFiles(widgetName);
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
const baseContext: ComponentContext = {
|
|
@@ -146,7 +145,7 @@ export function parseAttrsToParams(attrsString: string): Record<string, unknown>
|
|
|
146
145
|
/(?<attr>[a-z][a-z0-9-]*)(?:="(?<dq>[^"]*)"|='(?<sq>[^']*)'|=(?<uq>[^\s>]+))?/gi;
|
|
147
146
|
for (const match of attrsString.matchAll(attrPattern)) {
|
|
148
147
|
const { attr: attrName, dq, sq, uq } = match.groups!;
|
|
149
|
-
if (attrName === SSR_ATTR || attrName === LAZY_ATTR) continue;
|
|
148
|
+
if (!attrName || attrName === SSR_ATTR || attrName === LAZY_ATTR) continue;
|
|
150
149
|
const key = attrName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
151
150
|
const rawValue = dq ?? sq ?? uq;
|
|
152
151
|
if (rawValue === undefined) {
|
|
@@ -19,12 +19,12 @@ export function parseWidgetBlocks(markdown: string): ParsedWidgetBlock[] {
|
|
|
19
19
|
for (const match of markdown.matchAll(WIDGET_PATTERN)) {
|
|
20
20
|
const fullMatch = match[0];
|
|
21
21
|
const { name: widgetName, params: paramsRaw } = match.groups!;
|
|
22
|
-
const paramsJson = paramsRaw
|
|
22
|
+
const paramsJson = paramsRaw!.trim();
|
|
23
23
|
const startIndex = match.index;
|
|
24
24
|
|
|
25
25
|
const block: ParsedWidgetBlock = {
|
|
26
26
|
fullMatch,
|
|
27
|
-
widgetName
|
|
27
|
+
widgetName: widgetName!,
|
|
28
28
|
params: null,
|
|
29
29
|
startIndex,
|
|
30
30
|
endIndex: startIndex + fullMatch.length,
|
|
@@ -7,18 +7,30 @@
|
|
|
7
7
|
|
|
8
8
|
import type { WidgetComponent } from '../component/widget.component.ts';
|
|
9
9
|
|
|
10
|
+
interface RegistryEntry {
|
|
11
|
+
widget: WidgetComponent;
|
|
12
|
+
modulePath?: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
export class WidgetRegistry {
|
|
11
|
-
private
|
|
16
|
+
private entries = new Map<string, RegistryEntry>();
|
|
12
17
|
|
|
13
|
-
add(widget: WidgetComponent): void {
|
|
14
|
-
this.
|
|
18
|
+
add(widget: WidgetComponent, modulePath?: string): void {
|
|
19
|
+
this.entries.set(widget.name, { widget, modulePath });
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
get(name: string): WidgetComponent | undefined {
|
|
18
|
-
return this.
|
|
23
|
+
return this.entries.get(name)?.widget;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getModulePath(name: string): string | undefined {
|
|
27
|
+
return this.entries.get(name)?.modulePath;
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
[Symbol.iterator](): IterableIterator<WidgetComponent> {
|
|
22
|
-
|
|
31
|
+
const entries = this.entries.values();
|
|
32
|
+
return (function* () {
|
|
33
|
+
for (const entry of entries) yield entry.widget;
|
|
34
|
+
})();
|
|
23
35
|
}
|
|
24
36
|
}
|
|
@@ -40,6 +40,33 @@ export declare abstract class Component<TParams = unknown, TData = unknown, TCon
|
|
|
40
40
|
hydrate?(args: this['RenderArgs']): void;
|
|
41
41
|
destroy?(): void;
|
|
42
42
|
validateParams?(params: TParams): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* @experimental
|
|
45
|
+
*
|
|
46
|
+
* Parse a template and return a reusable fill function.
|
|
47
|
+
* Call once, apply many times with different slot values.
|
|
48
|
+
*
|
|
49
|
+
* **String signatures** (SSR + SPA `renderHTML`/`renderMarkdown`):
|
|
50
|
+
* - `experimentalUseTemplate(html, id)` — Extracts `<template id>`, fills `<slot name>`.
|
|
51
|
+
* - `experimentalUseTemplate(md, id)` — Extracts `` ```template:id ``, fills `slot:name`.
|
|
52
|
+
*
|
|
53
|
+
* **DOM signature** (browser `hydrate()`):
|
|
54
|
+
* - `experimentalUseTemplate(id)` — Finds `<template>` in shadow DOM,
|
|
55
|
+
* returns a function that clones and fills slots as `DocumentFragment`.
|
|
56
|
+
*
|
|
57
|
+
* Markdown templates work in both `renderMarkdown()` and `renderHTML()`.
|
|
58
|
+
* For HTML output from a markdown-only companion, wrap the result in `<mark-down>`:
|
|
59
|
+
* ```typescript
|
|
60
|
+
* override renderHTML({ data, context }: this['RenderArgs']): string {
|
|
61
|
+
* const card = this.experimentalUseTemplate(context.files!.md!, 'card');
|
|
62
|
+
* return `<mark-down>${escapeHtml(data.items.map(card).join('\n'))}</mark-down>`;
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* Throws if the template id is not found in the source.
|
|
67
|
+
*/
|
|
68
|
+
experimentalUseTemplate(source: string, id: string): (slots?: Record<string, string>) => string;
|
|
69
|
+
experimentalUseTemplate(id: string): (slots?: Record<string, string>) => DocumentFragment;
|
|
43
70
|
renderError(args: {
|
|
44
71
|
error: unknown;
|
|
45
72
|
params: TParams;
|
|
@@ -30,6 +30,71 @@ export class Component {
|
|
|
30
30
|
});
|
|
31
31
|
return `<div data-component="${this.name}" data-markdown>${escapeHtml(markdown)}</div>`;
|
|
32
32
|
}
|
|
33
|
+
experimentalUseTemplate(sourceOrId, id) {
|
|
34
|
+
// DOM path: single arg = template id, find in shadow DOM
|
|
35
|
+
if (id === undefined) {
|
|
36
|
+
const templateId = sourceOrId;
|
|
37
|
+
const shadowRoot = this.element?.shadowRoot;
|
|
38
|
+
const template = shadowRoot?.querySelector(`template#${templateId}`);
|
|
39
|
+
if (!template) {
|
|
40
|
+
throw new Error(`[${this.name}] Template "#${templateId}" not found in shadow DOM. ` +
|
|
41
|
+
'Ensure the <template> element exists in the companion HTML.');
|
|
42
|
+
}
|
|
43
|
+
return (slots) => {
|
|
44
|
+
const clone = template.content.cloneNode(true);
|
|
45
|
+
if (!slots)
|
|
46
|
+
return clone;
|
|
47
|
+
for (const [name, content] of Object.entries(slots)) {
|
|
48
|
+
const selector = name === 'default' ? 'slot:not([name])' : `slot[name="${name}"]`;
|
|
49
|
+
const slot = clone.querySelector(selector);
|
|
50
|
+
if (!slot)
|
|
51
|
+
continue;
|
|
52
|
+
const temp = document.createElement('template');
|
|
53
|
+
temp.innerHTML = content;
|
|
54
|
+
slot.replaceWith(temp.content);
|
|
55
|
+
}
|
|
56
|
+
return clone;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const source = sourceOrId;
|
|
60
|
+
// Markdown path: ```template:id ... ```
|
|
61
|
+
const mdPattern = new RegExp('```template:' + id + '\\n([\\s\\S]*?)```');
|
|
62
|
+
const mdMatch = source.match(mdPattern);
|
|
63
|
+
if (mdMatch) {
|
|
64
|
+
const skeleton = mdMatch[1];
|
|
65
|
+
return (slots) => {
|
|
66
|
+
if (!slots)
|
|
67
|
+
return skeleton;
|
|
68
|
+
let result = skeleton;
|
|
69
|
+
for (const [name, content] of Object.entries(slots)) {
|
|
70
|
+
result = result.replaceAll(`slot:${name}`, content);
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// HTML path: <template id="...">...</template>
|
|
76
|
+
const htmlPattern = new RegExp(`<template\\s+id=["']${id}["'][^>]*>([\\s\\S]*?)</template>`);
|
|
77
|
+
const htmlMatch = source.match(htmlPattern);
|
|
78
|
+
if (!htmlMatch) {
|
|
79
|
+
throw new Error(`[${this.name}] Template "${id}" not found in source. ` +
|
|
80
|
+
'Expected <template id="' + id + '"> in HTML or ```template:' + id + ' in markdown.');
|
|
81
|
+
}
|
|
82
|
+
const skeleton = htmlMatch[1];
|
|
83
|
+
return (slots) => {
|
|
84
|
+
if (!slots)
|
|
85
|
+
return skeleton;
|
|
86
|
+
let result = skeleton;
|
|
87
|
+
for (const [name, content] of Object.entries(slots)) {
|
|
88
|
+
if (name === 'default')
|
|
89
|
+
continue;
|
|
90
|
+
result = result.replaceAll(new RegExp(`<slot\\s+name=["']${name}["'][^>]*>[\\s\\S]*?</slot>`, 'g'), content);
|
|
91
|
+
}
|
|
92
|
+
if ('default' in slots) {
|
|
93
|
+
result = result.replaceAll(/<slot\s*>[\s\S]*?<\/slot>/g, slots['default']);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
33
98
|
renderError(args) {
|
|
34
99
|
const msg = args.error instanceof Error ? args.error.message : String(args.error);
|
|
35
100
|
return `<div data-component="${this.name}">Error: ${escapeHtml(msg)}</div>`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abstract.component.js","sourceRoot":"","sources":["../../../core/component/abstract.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,MAAM,OAAgB,SAAS;IAmB7B,sEAAsE;IACtE,OAAO,CAA2B;IAElC,oEAAoE;IAC3D,KAAK,CAAgD;IAE9D;;;;OAIG;IACM,aAAa,CAAW;IAKjC,UAAU,CAAC,IAAwB;QACjC,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,wBAAwB,IAAI,CAAC,IAAI,oBAAoB,CAAC;QAC/D,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC;YACnC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;QACH,OAAO,wBAAwB,IAAI,CAAC,IAAI,mBAAmB,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1F,CAAC;
|
|
1
|
+
{"version":3,"file":"abstract.component.js","sourceRoot":"","sources":["../../../core/component/abstract.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -38,9 +38,15 @@ export class SsrHtmlRenderer extends SsrRenderer {
|
|
|
38
38
|
content = this.attributeSlots(content, route.pattern);
|
|
39
39
|
// Resolve <widget-*> tags
|
|
40
40
|
if (this.widgets) {
|
|
41
|
-
content = await resolveWidgetTags(content, this.widgets, routeInfo, (name
|
|
42
|
-
const
|
|
43
|
-
|
|
41
|
+
content = await resolveWidgetTags(content, this.widgets, routeInfo, async (name) => {
|
|
42
|
+
const modulePath = this.widgets.getModulePath(name);
|
|
43
|
+
if (modulePath) {
|
|
44
|
+
const mod = await this.pipeline.loadModule(modulePath);
|
|
45
|
+
const inlined = this.pipeline.getModuleFiles(mod);
|
|
46
|
+
if (inlined)
|
|
47
|
+
return inlined;
|
|
48
|
+
}
|
|
49
|
+
return {};
|
|
44
50
|
}, this.pipeline.contextProvider, this.logger);
|
|
45
51
|
}
|
|
46
52
|
return { content, ...(title !== undefined ? { title } : {}) };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.renderer.js","sourceRoot":"","sources":["../../../core/renderer/html.renderer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AAOzE,MAAM,OAAO,eAAgB,SAAQ,WAAW;IAClB,KAAK,GAAG,UAAU,CAAC;IACvC,gBAAgB,CAA0B;IAC1C,aAAa,GAAyB,IAAI,CAAC;IAEnD,YAAY,QAAkB,EAAE,UAAkC,EAAE;QAClE,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,IAAI,CAAC;QAEzD,IAAI,IAAI,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC;IACH,CAAC;IAEkB,UAAU,CAAC,MAAc,EAAE,KAAa,EAAE,aAAqB;QAChF,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC,OAAO,CACnB,IAAI,MAAM,CAAC,mCAAmC,OAAO,uBAAuB,CAAC,EAC7E,KAAK,CACN,CAAC;IACJ,CAAC;IAEkB,UAAU,CAAC,MAAc;QAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,oCAAoC,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;IAEkB,KAAK,CAAC,kBAAkB,CACzC,SAAoB,EACpB,KAAkB,EAClB,MAAgB,EAChB,MAAoB;QAEpB,IAAI,KAAK,CAAC,UAAU,KAAK,kBAAkB,CAAC,UAAU,EAAE,CAAC;YACvD,OAAO,EAAE,OAAO,EAAE,yBAAyB,KAAK,CAAC,OAAO,kBAAkB,EAAE,CAAC;QAC/E,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrG,IAAI,OAAO,GAAG,UAAU,CAAC;QAEzB,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE7C,8DAA8D;QAC9D,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAEtD,0BAA0B;QAC1B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,GAAG,MAAM,iBAAiB,CAC/B,OAAO,EACP,IAAI,CAAC,OAAO,EACZ,SAAS,EACT,
|
|
1
|
+
{"version":3,"file":"html.renderer.js","sourceRoot":"","sources":["../../../core/renderer/html.renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -63,9 +63,10 @@ export class SsrMdRenderer extends SsrRenderer {
|
|
|
63
63
|
}
|
|
64
64
|
try {
|
|
65
65
|
let files;
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
68
|
-
|
|
66
|
+
const modulePath = this.widgets.getModulePath(block.widgetName);
|
|
67
|
+
if (modulePath) {
|
|
68
|
+
const mod = await this.pipeline.loadModule(modulePath);
|
|
69
|
+
files = this.pipeline.getModuleFiles(mod);
|
|
69
70
|
}
|
|
70
71
|
const baseContext = {
|
|
71
72
|
...routeInfo,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"md.renderer.js","sourceRoot":"","sources":["../../../core/renderer/md.renderer.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"md.renderer.js","sourceRoot":"","sources":["../../../core/renderer/md.renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,11 +13,6 @@ import type { WidgetRegistry } from '../widget/widget.registry.ts';
|
|
|
13
13
|
/** Options for SSR renderers. */
|
|
14
14
|
export interface SsrRendererOptions {
|
|
15
15
|
widgets?: WidgetRegistry;
|
|
16
|
-
widgetFiles?: Record<string, {
|
|
17
|
-
html?: string;
|
|
18
|
-
md?: string;
|
|
19
|
-
css?: string;
|
|
20
|
-
}>;
|
|
21
16
|
}
|
|
22
17
|
/**
|
|
23
18
|
* Abstract SSR renderer with shared routing pipeline.
|
|
@@ -25,11 +20,6 @@ export interface SsrRendererOptions {
|
|
|
25
20
|
export declare abstract class SsrRenderer {
|
|
26
21
|
protected readonly pipeline: Pipeline;
|
|
27
22
|
protected widgets: WidgetRegistry | null;
|
|
28
|
-
protected widgetFiles: Record<string, {
|
|
29
|
-
html?: string;
|
|
30
|
-
md?: string;
|
|
31
|
-
css?: string;
|
|
32
|
-
}>;
|
|
33
23
|
protected abstract readonly label: string;
|
|
34
24
|
protected readonly logger: Logger;
|
|
35
25
|
constructor(pipeline: Pipeline, options?: SsrRendererOptions);
|
|
@@ -13,13 +13,11 @@ import { assertSafeRedirect } from "../util/html.util.js";
|
|
|
13
13
|
export class SsrRenderer {
|
|
14
14
|
pipeline;
|
|
15
15
|
widgets;
|
|
16
|
-
widgetFiles;
|
|
17
16
|
logger;
|
|
18
17
|
constructor(pipeline, options = {}) {
|
|
19
18
|
this.pipeline = pipeline;
|
|
20
19
|
this.logger = pipeline.logger;
|
|
21
20
|
this.widgets = options.widgets ?? null;
|
|
22
|
-
this.widgetFiles = options.widgetFiles ?? {};
|
|
23
21
|
}
|
|
24
22
|
/**
|
|
25
23
|
* Render a URL to a content string.
|