@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.
Files changed (46) hide show
  1. package/core/component/abstract.component.ts +112 -0
  2. package/core/renderer/html.renderer.ts +8 -3
  3. package/core/renderer/md.renderer.ts +4 -3
  4. package/core/renderer/ssr.renderer.ts +3 -6
  5. package/core/router/route.trie.ts +4 -4
  6. package/core/server/emroute.server.ts +5 -27
  7. package/core/util/md.util.ts +3 -3
  8. package/core/util/widget-resolve.util.ts +3 -4
  9. package/core/widget/widget.parser.ts +2 -2
  10. package/core/widget/widget.registry.ts +17 -5
  11. package/dist/core/component/abstract.component.d.ts +27 -0
  12. package/dist/core/component/abstract.component.js +65 -0
  13. package/dist/core/component/abstract.component.js.map +1 -1
  14. package/dist/core/renderer/html.renderer.js +9 -3
  15. package/dist/core/renderer/html.renderer.js.map +1 -1
  16. package/dist/core/renderer/md.renderer.js +4 -3
  17. package/dist/core/renderer/md.renderer.js.map +1 -1
  18. package/dist/core/renderer/ssr.renderer.d.ts +0 -10
  19. package/dist/core/renderer/ssr.renderer.js +0 -2
  20. package/dist/core/renderer/ssr.renderer.js.map +1 -1
  21. package/dist/core/router/route.trie.js +1 -1
  22. package/dist/core/router/route.trie.js.map +1 -1
  23. package/dist/core/server/emroute.server.js +4 -26
  24. package/dist/core/server/emroute.server.js.map +1 -1
  25. package/dist/core/util/md.util.js.map +1 -1
  26. package/dist/core/util/widget-resolve.util.d.ts +1 -5
  27. package/dist/core/util/widget-resolve.util.js +2 -2
  28. package/dist/core/util/widget-resolve.util.js.map +1 -1
  29. package/dist/core/widget/widget.parser.js +1 -1
  30. package/dist/core/widget/widget.parser.js.map +1 -1
  31. package/dist/core/widget/widget.registry.d.ts +3 -2
  32. package/dist/core/widget/widget.registry.js +12 -5
  33. package/dist/core/widget/widget.registry.js.map +1 -1
  34. package/dist/emroute.js +122 -72
  35. package/dist/emroute.js.map +14 -14
  36. package/dist/runtime/abstract.runtime.js +18 -10
  37. package/dist/runtime/abstract.runtime.js.map +1 -1
  38. package/dist/src/element/component.element.d.ts +3 -8
  39. package/dist/src/element/component.element.js +10 -24
  40. package/dist/src/element/component.element.js.map +1 -1
  41. package/dist/src/renderer/spa/emroute.app.js +1 -1
  42. package/dist/src/renderer/spa/emroute.app.js.map +1 -1
  43. package/package.json +1 -1
  44. package/runtime/abstract.runtime.ts +33 -25
  45. package/src/element/component.element.ts +9 -30
  46. 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, declared) => {
74
- const files = this.widgetFiles[name] ?? declared;
75
- return files ? this.pipeline.loadFiles(files) : Promise.resolve({});
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 filePaths = this.widgetFiles[block.widgetName] ?? widget.files;
106
- if (filePaths) {
107
- files = await this.pipeline.loadFiles(filePaths);
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].route.pattern;
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(segments[index]);
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 = config.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 { registry, widgetFiles };
277
+ return registry;
300
278
  }
301
279
 
302
280
  private static buildHtmlShell(title: string, htmlBase: string): string {
@@ -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].startsWith('```')) {
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].replaceAll(inlineRe, `](${prefix}`);
25
- lines[i] = lines[i].replaceAll(refRe, `$1${prefix}`);
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, widget.files);
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.trim();
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 widgets = new Map<string, WidgetComponent>();
16
+ private entries = new Map<string, RegistryEntry>();
12
17
 
13
- add(widget: WidgetComponent): void {
14
- this.widgets.set(widget.name, widget);
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.widgets.get(name);
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
- return this.widgets.values();
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;IAMD,WAAW,CAAC,IAAyC;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClF,OAAO,wBAAwB,IAAI,CAAC,IAAI,YAAY,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC9E,CAAC;IAED,mBAAmB,CAAC,KAAc;QAChC,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO,kBAAkB,IAAI,CAAC,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClD,CAAC;CACF"}
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;IAiCD,uBAAuB,CAAC,UAAkB,EAAE,EAAW;QACrD,yDAAyD;QACzD,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACrB,MAAM,UAAU,GAAG,UAAU,CAAC;YAC9B,MAAM,UAAU,GAAI,IAAI,CAAC,OAAmC,EAAE,UAAU,CAAC;YACzE,MAAM,QAAQ,GAAG,UAAU,EAAE,aAAa,CAAsB,YAAY,UAAU,EAAE,CAAC,CAAC;YAC1F,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CACb,IAAI,IAAI,CAAC,IAAI,gBAAgB,UAAU,6BAA6B;oBACpE,6DAA6D,CAC9D,CAAC;YACJ,CAAC;YAED,OAAO,CAAC,KAAK,EAAE,EAAE;gBACf,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAqB,CAAC;gBACnE,IAAI,CAAC,KAAK;oBAAE,OAAO,KAAK,CAAC;gBAEzB,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACpD,MAAM,QAAQ,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,cAAc,IAAI,IAAI,CAAC;oBAClF,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;oBAC3C,IAAI,CAAC,IAAI;wBAAE,SAAS;oBACpB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;oBAChD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC;oBACzB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACjC,CAAC;gBAED,OAAO,KAAK,CAAC;YACf,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC;QAE1B,wCAAwC;QACxC,MAAM,SAAS,GAAG,IAAI,MAAM,CAC1B,cAAc,GAAG,EAAE,GAAG,oBAAoB,CAC3C,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;YAE7B,OAAO,CAAC,KAAK,EAAE,EAAE;gBACf,IAAI,CAAC,KAAK;oBAAE,OAAO,QAAQ,CAAC;gBAC5B,IAAI,MAAM,GAAG,QAAQ,CAAC;gBACtB,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACpD,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;gBACtD,CAAC;gBACD,OAAO,MAAM,CAAC;YAChB,CAAC,CAAC;QACJ,CAAC;QAED,+CAA+C;QAC/C,MAAM,WAAW,GAAG,IAAI,MAAM,CAC5B,uBAAuB,EAAE,mCAAmC,CAC7D,CAAC;QACF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,IAAI,IAAI,CAAC,IAAI,eAAe,EAAE,yBAAyB;gBACvD,yBAAyB,GAAG,EAAE,GAAG,4BAA4B,GAAG,EAAE,GAAG,eAAe,CACrF,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAE,CAAC;QAE/B,OAAO,CAAC,KAAK,EAAE,EAAE;YACf,IAAI,CAAC,KAAK;gBAAE,OAAO,QAAQ,CAAC;YAE5B,IAAI,MAAM,GAAG,QAAQ,CAAC;YAEtB,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpD,IAAI,IAAI,KAAK,SAAS;oBAAE,SAAS;gBACjC,MAAM,GAAG,MAAM,CAAC,UAAU,CACxB,IAAI,MAAM,CAAC,qBAAqB,IAAI,6BAA6B,EAAE,GAAG,CAAC,EACvE,OAAO,CACR,CAAC;YACJ,CAAC;YAED,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;gBACvB,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,4BAA4B,EAAE,KAAK,CAAC,SAAS,CAAE,CAAC,CAAC;YAC9E,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,IAAyC;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClF,OAAO,wBAAwB,IAAI,CAAC,IAAI,YAAY,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC9E,CAAC;IAED,mBAAmB,CAAC,KAAc;QAChC,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO,kBAAkB,IAAI,CAAC,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClD,CAAC;CACF"}
@@ -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, declared) => {
42
- const files = this.widgetFiles[name] ?? declared;
43
- return files ? this.pipeline.loadFiles(files) : Promise.resolve({});
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":"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"}
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 filePaths = this.widgetFiles[block.widgetName] ?? widget.files;
67
- if (filePaths) {
68
- files = await this.pipeline.loadFiles(filePaths);
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":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AAEzE,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAE9C,SAAS,eAAe,CAAC,OAAe;IACtC,OAAO,kCAAkC,OAAO,YAAY,CAAC;AAC/D,CAAC;AAID,MAAM,OAAO,aAAc,SAAQ,WAAW;IAChB,KAAK,GAAG,QAAQ,CAAC;IAE7C,YAAY,QAAkB,EAAE,UAAgC,EAAE;QAChE,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC;IAEkB,UAAU,CAAC,MAAc,EAAE,KAAa,EAAE,aAAqB;QAChF,OAAO,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/D,CAAC;IAEkB,UAAU,CAAC,MAAc;QAC1C,OAAO,MAAM;aACV,OAAO,CAAC,sCAAsC,EAAE,EAAE,CAAC;aACnD,IAAI,EAAE,CAAC;IACZ,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,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,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,8DAA8D;QAC9D,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,EAAE,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAE9E,+BAA+B;QAC/B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC1D,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAChE,CAAC;IAEkB,aAAa,CAC9B,SAAwB,EACxB,IAAiC;QAEjC,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAEkB,cAAc,CAAC,EAAU;QAC1C,OAAO,gBAAgB,EAAE,EAAE,CAAC;IAC9B,CAAC;IAEkB,gBAAgB,CAAC,MAAc,EAAE,GAAQ;QAC1D,OAAO,KAAK,eAAe,CAAC,MAAM,CAAC,IAAI,OAAO,eAAe,GAAG,CAAC,QAAQ,IAAI,CAAC;IAChF,CAAC;IAEkB,eAAe,CAAC,MAAe,EAAE,GAAQ;QAC1D,OAAO,sCAAsC,GAAG,CAAC,QAAQ,IAAI,CAAC;IAChE,CAAC;IAEO,cAAc,CACpB,OAAe,EACf,SAAoB;QAEpB,OAAO,kBAAkB,CACvB,OAAO,EACP,iBAAiB,EACjB,KAAK,EAAE,KAAK,EAAE,EAAE;YACd,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBACtC,OAAO,kBAAkB,KAAK,CAAC,UAAU,QAAQ,KAAK,CAAC,UAAU,EAAE,CAAC;YACtE,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACnD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,iCAAiC,KAAK,CAAC,UAAU,IAAI,CAAC;YAC/D,CAAC;YAED,IAAI,CAAC;gBACH,IAAI,KAAiD,CAAC;gBACtD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACjE,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;oBACvD,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;gBAC5C,CAAC;gBAED,MAAM,WAAW,GAAqB;oBACpC,GAAG,SAAS;oBACZ,QAAQ,EAAE,SAAS,CAAC,GAAG,CAAC,QAAQ;oBAChC,YAAY,EAAE,SAAS,CAAC,GAAG,CAAC,YAAY;oBACxC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC5B,CAAC;gBACF,MAAM,OAAO,GAAqB,IAAI,CAAC,QAAQ,CAAC,eAAe;oBAC7D,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,WAAW,CAAC;oBAC5C,CAAC,CAAC,WAAW,CAAC;gBAChB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;gBACrE,OAAO,MAAM,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YACxE,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;YACvC,CAAC;QACH,CAAC,EACD,mBAAmB,EACnB,CAAC,EACD,IAAI,CAAC,MAAM,CACZ,CAAC;IACJ,CAAC;CACF"}
@@ -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.