@emkodev/emroute 1.10.0 → 1.12.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/widget.component.ts +3 -8
- package/core/pipeline/pipeline.ts +13 -5
- package/core/util/html.util.ts +1 -1
- package/core/util/js.util.ts +8 -0
- package/core/util/widget-resolve.util.ts +4 -2
- package/dist/core/component/widget.component.js +3 -7
- package/dist/core/component/widget.component.js.map +1 -1
- package/dist/core/pipeline/pipeline.d.ts +1 -0
- package/dist/core/pipeline/pipeline.js +12 -5
- package/dist/core/pipeline/pipeline.js.map +1 -1
- package/dist/core/util/html.util.js +1 -1
- package/dist/core/util/html.util.js.map +1 -1
- package/dist/core/util/js.util.d.ts +5 -0
- package/dist/core/util/js.util.js +8 -0
- package/dist/core/util/js.util.js.map +1 -0
- package/dist/core/util/widget-resolve.util.js +4 -2
- package/dist/core/util/widget-resolve.util.js.map +1 -1
- package/dist/emroute.js +120 -32
- package/dist/emroute.js.map +11 -10
- package/dist/runtime/abstract.runtime.d.ts +13 -1
- package/dist/runtime/abstract.runtime.js +34 -22
- package/dist/runtime/abstract.runtime.js.map +1 -1
- package/dist/runtime/bun/fs/bun-fs.runtime.d.ts +4 -0
- package/dist/runtime/bun/fs/bun-fs.runtime.js +20 -1
- package/dist/runtime/bun/fs/bun-fs.runtime.js.map +1 -1
- package/dist/server/build.util.d.ts +9 -15
- package/dist/server/build.util.js +9 -148
- package/dist/server/build.util.js.map +1 -1
- package/dist/server/dev.server.d.ts +25 -0
- package/dist/server/dev.server.js +81 -0
- package/dist/server/dev.server.js.map +1 -0
- package/dist/src/element/component.element.d.ts +12 -0
- package/dist/src/element/component.element.js +55 -3
- package/dist/src/element/component.element.js.map +1 -1
- package/dist/src/util/html.util.d.ts +5 -0
- package/dist/src/util/html.util.js +38 -1
- package/dist/src/util/html.util.js.map +1 -1
- package/package.json +2 -2
- package/runtime/abstract.runtime.ts +38 -23
- package/runtime/bun/fs/bun-fs.runtime.ts +23 -1
- package/server/build.util.ts +9 -169
- package/src/element/component.element.ts +63 -3
- package/src/util/html.util.ts +46 -1
package/dist/emroute.js
CHANGED
|
@@ -15,8 +15,10 @@ function unescapeHtml(text) {
|
|
|
15
15
|
return text.replaceAll("`", "`").replaceAll("'", "'").replaceAll(""", '"').replaceAll(">", ">").replaceAll("<", "<").replaceAll("&", "&");
|
|
16
16
|
}
|
|
17
17
|
function scopeWidgetCss(css, widgetName) {
|
|
18
|
-
return `@
|
|
18
|
+
return `@layer emroute {
|
|
19
|
+
@scope (widget-${widgetName}) {
|
|
19
20
|
${css}
|
|
21
|
+
}
|
|
20
22
|
}`;
|
|
21
23
|
}
|
|
22
24
|
var STATUS_MESSAGES = {
|
|
@@ -27,14 +29,34 @@ var STATUS_MESSAGES = {
|
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
// dist/src/util/html.util.js
|
|
32
|
+
class SsrCSSStyleSheet {
|
|
33
|
+
cssText = "";
|
|
34
|
+
replaceSync(css) {
|
|
35
|
+
this.cssText = css;
|
|
36
|
+
}
|
|
37
|
+
replace(css) {
|
|
38
|
+
this.cssText = css;
|
|
39
|
+
return Promise.resolve(this);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
var CSSStyleSheetBase = globalThis.CSSStyleSheet ?? SsrCSSStyleSheet;
|
|
43
|
+
|
|
30
44
|
class SsrShadowRoot {
|
|
31
45
|
host;
|
|
32
46
|
_innerHTML = "";
|
|
47
|
+
_adoptedStyleSheets = [];
|
|
33
48
|
constructor(host) {
|
|
34
49
|
this.host = host;
|
|
35
50
|
}
|
|
51
|
+
get adoptedStyleSheets() {
|
|
52
|
+
return this._adoptedStyleSheets;
|
|
53
|
+
}
|
|
54
|
+
set adoptedStyleSheets(sheets) {
|
|
55
|
+
this._adoptedStyleSheets = sheets;
|
|
56
|
+
}
|
|
36
57
|
get innerHTML() {
|
|
37
|
-
|
|
58
|
+
const adopted = this._adoptedStyleSheets.filter((s) => s.cssText).map((s) => `<style>${s.cssText}</style>`).join("");
|
|
59
|
+
return adopted + this._innerHTML;
|
|
38
60
|
}
|
|
39
61
|
set innerHTML(value) {
|
|
40
62
|
this._innerHTML = value;
|
|
@@ -57,6 +79,10 @@ class SsrShadowRoot {
|
|
|
57
79
|
}
|
|
58
80
|
}
|
|
59
81
|
|
|
82
|
+
class SsrElementInternals {
|
|
83
|
+
states = new Set;
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
class SsrHTMLElement {
|
|
61
87
|
_innerHTML = "";
|
|
62
88
|
_shadowRoot = null;
|
|
@@ -97,6 +123,9 @@ class SsrHTMLElement {
|
|
|
97
123
|
this._shadowRoot = new SsrShadowRoot(this);
|
|
98
124
|
return this._shadowRoot;
|
|
99
125
|
}
|
|
126
|
+
attachInternals() {
|
|
127
|
+
return new SsrElementInternals;
|
|
128
|
+
}
|
|
100
129
|
getAttribute(name) {
|
|
101
130
|
return this._attributes.get(name) ?? null;
|
|
102
131
|
}
|
|
@@ -212,10 +241,12 @@ function filterUndefined(obj) {
|
|
|
212
241
|
|
|
213
242
|
class ComponentElement extends HTMLElementBase {
|
|
214
243
|
static lazyLoaders = new Map;
|
|
244
|
+
static sheetCache = new Map;
|
|
215
245
|
static extendContext;
|
|
216
246
|
static setContextProvider(provider) {
|
|
217
247
|
ComponentElement.extendContext = provider;
|
|
218
248
|
}
|
|
249
|
+
static CUSTOM_STATES = ["lazy", "loading", "hydrating", "ready", "error"];
|
|
219
250
|
component;
|
|
220
251
|
effectiveFiles;
|
|
221
252
|
params = null;
|
|
@@ -226,15 +257,23 @@ class ComponentElement extends HTMLElementBase {
|
|
|
226
257
|
deferred = null;
|
|
227
258
|
abortController = null;
|
|
228
259
|
intersectionObserver = null;
|
|
260
|
+
internals;
|
|
229
261
|
dataPromise = null;
|
|
230
262
|
constructor(component, files) {
|
|
231
263
|
super();
|
|
232
264
|
this.component = component;
|
|
233
265
|
this.effectiveFiles = files;
|
|
266
|
+
this.internals = this.attachInternals();
|
|
234
267
|
if (!this.shadowRoot) {
|
|
235
268
|
this.attachShadow({ mode: "open" });
|
|
236
269
|
}
|
|
237
270
|
}
|
|
271
|
+
setCustomState(next) {
|
|
272
|
+
for (const s of ComponentElement.CUSTOM_STATES) {
|
|
273
|
+
this.internals.states.delete(s);
|
|
274
|
+
}
|
|
275
|
+
this.internals.states.add(next);
|
|
276
|
+
}
|
|
238
277
|
static register(component, files) {
|
|
239
278
|
const tagName = `widget-${component.name}`;
|
|
240
279
|
if (!globalThis.customElements || customElements.get(tagName)) {
|
|
@@ -316,7 +355,6 @@ class ComponentElement extends HTMLElementBase {
|
|
|
316
355
|
}
|
|
317
356
|
}
|
|
318
357
|
this.component.element = this;
|
|
319
|
-
this.style.contentVisibility = "auto";
|
|
320
358
|
this.abortController = new AbortController;
|
|
321
359
|
const signal = this.abortController.signal;
|
|
322
360
|
const params = {};
|
|
@@ -351,8 +389,10 @@ class ComponentElement extends HTMLElementBase {
|
|
|
351
389
|
...filteredFiles ? { files: filteredFiles } : {}
|
|
352
390
|
};
|
|
353
391
|
this.context = ComponentElement.extendContext ? ComponentElement.extendContext(base) : base;
|
|
392
|
+
this.adoptCss();
|
|
354
393
|
if (this.hasAttribute(SSR_ATTR)) {
|
|
355
394
|
this.removeAttribute(SSR_ATTR);
|
|
395
|
+
this.setCustomState("hydrating");
|
|
356
396
|
const lightText = this.textContent?.trim();
|
|
357
397
|
if (lightText) {
|
|
358
398
|
try {
|
|
@@ -365,12 +405,16 @@ class ComponentElement extends HTMLElementBase {
|
|
|
365
405
|
const args = { data: this.data, params: this.params, context: this.context };
|
|
366
406
|
queueMicrotask(() => {
|
|
367
407
|
this.component.hydrate(args);
|
|
408
|
+
this.setCustomState("ready");
|
|
368
409
|
});
|
|
410
|
+
} else {
|
|
411
|
+
this.setCustomState("ready");
|
|
369
412
|
}
|
|
370
413
|
this.signalReady();
|
|
371
414
|
return;
|
|
372
415
|
}
|
|
373
416
|
if (this.hasAttribute(LAZY_ATTR)) {
|
|
417
|
+
this.setCustomState("lazy");
|
|
374
418
|
this.intersectionObserver = new IntersectionObserver((entries) => {
|
|
375
419
|
const entry = entries[0];
|
|
376
420
|
if (entry.isIntersecting) {
|
|
@@ -392,6 +436,9 @@ class ComponentElement extends HTMLElementBase {
|
|
|
392
436
|
this.abortController?.abort();
|
|
393
437
|
this.abortController = null;
|
|
394
438
|
this.state = "idle";
|
|
439
|
+
for (const s of ComponentElement.CUSTOM_STATES) {
|
|
440
|
+
this.internals.states.delete(s);
|
|
441
|
+
}
|
|
395
442
|
this.data = null;
|
|
396
443
|
this.context = undefined;
|
|
397
444
|
this.dataPromise = null;
|
|
@@ -409,11 +456,36 @@ class ComponentElement extends HTMLElementBase {
|
|
|
409
456
|
async loadFiles() {
|
|
410
457
|
return this.effectiveFiles ?? {};
|
|
411
458
|
}
|
|
459
|
+
static baseSheet = null;
|
|
460
|
+
static getBaseSheet() {
|
|
461
|
+
if (!ComponentElement.baseSheet) {
|
|
462
|
+
ComponentElement.baseSheet = new CSSStyleSheetBase;
|
|
463
|
+
ComponentElement.baseSheet.replaceSync(":host { container-type: inline-size; content-visibility: auto; }");
|
|
464
|
+
}
|
|
465
|
+
return ComponentElement.baseSheet;
|
|
466
|
+
}
|
|
467
|
+
adoptCss() {
|
|
468
|
+
const css = this.effectiveFiles?.css;
|
|
469
|
+
const base = ComponentElement.getBaseSheet();
|
|
470
|
+
if (!css) {
|
|
471
|
+
this.shadowRoot.adoptedStyleSheets = [base];
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const name = this.component.name;
|
|
475
|
+
let sheet = ComponentElement.sheetCache.get(name);
|
|
476
|
+
if (!sheet) {
|
|
477
|
+
sheet = new CSSStyleSheetBase;
|
|
478
|
+
sheet.replaceSync(scopeWidgetCss(css, name));
|
|
479
|
+
ComponentElement.sheetCache.set(name, sheet);
|
|
480
|
+
}
|
|
481
|
+
this.shadowRoot.adoptedStyleSheets = [base, sheet];
|
|
482
|
+
}
|
|
412
483
|
async loadData() {
|
|
413
484
|
if (this.params === null)
|
|
414
485
|
return;
|
|
415
486
|
const signal = this.abortController?.signal;
|
|
416
487
|
this.state = "loading";
|
|
488
|
+
this.setCustomState("loading");
|
|
417
489
|
this.render();
|
|
418
490
|
try {
|
|
419
491
|
const promise = this.component.getData({
|
|
@@ -426,6 +498,7 @@ class ComponentElement extends HTMLElementBase {
|
|
|
426
498
|
if (signal?.aborted)
|
|
427
499
|
return;
|
|
428
500
|
this.state = "ready";
|
|
501
|
+
this.setCustomState("ready");
|
|
429
502
|
} catch (e) {
|
|
430
503
|
if (e instanceof DOMException && e.name === "AbortError")
|
|
431
504
|
return;
|
|
@@ -439,6 +512,7 @@ class ComponentElement extends HTMLElementBase {
|
|
|
439
512
|
}
|
|
440
513
|
setError(message) {
|
|
441
514
|
this.state = "error";
|
|
515
|
+
this.setCustomState("error");
|
|
442
516
|
this.errorMessage = message;
|
|
443
517
|
this.render();
|
|
444
518
|
this.signalReady();
|
|
@@ -696,21 +770,26 @@ class Pipeline {
|
|
|
696
770
|
return hierarchy;
|
|
697
771
|
}
|
|
698
772
|
async findWidgetModulePath(name) {
|
|
773
|
+
return (await this.findWidgetEntry(name))?.modulePath;
|
|
774
|
+
}
|
|
775
|
+
async findWidgetEntry(name) {
|
|
699
776
|
const response = await this.runtime.query(WIDGETS_MANIFEST_PATH);
|
|
700
777
|
if (response.status === 404)
|
|
701
778
|
return;
|
|
702
779
|
const entries = await response.json();
|
|
703
|
-
return entries.find((e) => e.name === name)
|
|
780
|
+
return entries.find((e) => e.name === name);
|
|
704
781
|
}
|
|
705
782
|
async loadWidgetModule(name) {
|
|
706
|
-
const
|
|
707
|
-
if (!
|
|
783
|
+
const entry = await this.findWidgetEntry(name);
|
|
784
|
+
if (!entry)
|
|
708
785
|
return;
|
|
709
|
-
const mod = await this.loadModule(
|
|
786
|
+
const mod = await this.loadModule(entry.modulePath);
|
|
710
787
|
const component = this.extractWidgetComponent(mod);
|
|
711
788
|
if (!component)
|
|
712
789
|
return;
|
|
713
|
-
|
|
790
|
+
const inlined = this.getModuleFiles(mod);
|
|
791
|
+
const files = inlined ?? (entry.files ? await this.loadFiles(entry.files) : {});
|
|
792
|
+
return { component, files };
|
|
714
793
|
}
|
|
715
794
|
async loadWidget(name) {
|
|
716
795
|
return (await this.loadWidgetModule(name))?.component;
|
|
@@ -847,7 +926,9 @@ function resolveWidgetTags(html, getWidget, routeInfo, contextProvider, logger =
|
|
|
847
926
|
};
|
|
848
927
|
const context = contextProvider ? contextProvider(baseContext) : baseContext;
|
|
849
928
|
const data = await widget.getData({ params, context });
|
|
850
|
-
const
|
|
929
|
+
const hostStyle = "<style>:host { container-type: inline-size; content-visibility: auto; }</style>";
|
|
930
|
+
const cssStyle = files?.css ? `<style>${scopeWidgetCss(files.css, widgetName)}</style>` : "";
|
|
931
|
+
const rendered = hostStyle + cssStyle + widget.renderHTML({ data, params, context });
|
|
851
932
|
wrappers.set(match, {
|
|
852
933
|
tagName: `widget-${widgetName}`,
|
|
853
934
|
attrs: attrsString ? ` ${attrsString}` : "",
|
|
@@ -1605,6 +1686,11 @@ function resolveTargetNode(node, name, isRoot) {
|
|
|
1605
1686
|
return node.children[name];
|
|
1606
1687
|
}
|
|
1607
1688
|
|
|
1689
|
+
// dist/core/util/js.util.js
|
|
1690
|
+
function escapeTemplateLiteral(s) {
|
|
1691
|
+
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1608
1694
|
// dist/runtime/abstract.runtime.js
|
|
1609
1695
|
var CONTENT_TYPES = new Map([
|
|
1610
1696
|
[".html", "text/html; charset=utf-8"],
|
|
@@ -1912,26 +1998,12 @@ class Runtime {
|
|
|
1912
1998
|
} catch {
|
|
1913
1999
|
return;
|
|
1914
2000
|
}
|
|
1915
|
-
const companionExts = kind === "element" ? [] : ["html", "md", "css"];
|
|
1916
|
-
const files = {};
|
|
1917
|
-
for (const ext of companionExts) {
|
|
1918
|
-
const companionPath = tsPath.replace(/\.ts$/, `.${ext}`);
|
|
1919
|
-
try {
|
|
1920
|
-
files[ext] = await this.query(companionPath, { as: "text" });
|
|
1921
|
-
} catch {}
|
|
1922
|
-
}
|
|
1923
2001
|
let jsCode;
|
|
1924
2002
|
try {
|
|
1925
|
-
jsCode = await this.
|
|
2003
|
+
jsCode = await this.transpileModule(tsPath, tsSource);
|
|
1926
2004
|
} catch {
|
|
1927
2005
|
return;
|
|
1928
2006
|
}
|
|
1929
|
-
if (Object.keys(files).length > 0) {
|
|
1930
|
-
const entries = Object.entries(files).map(([k, v]) => `${k}: \`${v.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$")}\``).join(", ");
|
|
1931
|
-
jsCode += `
|
|
1932
|
-
export const __files = { ${entries} };
|
|
1933
|
-
`;
|
|
1934
|
-
}
|
|
1935
2007
|
await this.handle(jsPath, { method: "PUT", body: jsCode });
|
|
1936
2008
|
}
|
|
1937
2009
|
loadModule(_path) {
|
|
@@ -1940,6 +2012,27 @@ export const __files = { ${entries} };
|
|
|
1940
2012
|
transpile(_source) {
|
|
1941
2013
|
throw new Error(`transpile not implemented for ${this.constructor.name}`);
|
|
1942
2014
|
}
|
|
2015
|
+
async transpileModule(path, source) {
|
|
2016
|
+
let js = await this.transpile(source);
|
|
2017
|
+
const basePath = path.replace(/\.ts$/, "");
|
|
2018
|
+
const companions = ["html", "md", "css"];
|
|
2019
|
+
const entries = [];
|
|
2020
|
+
for (const ext of companions) {
|
|
2021
|
+
try {
|
|
2022
|
+
const content = await this.query(basePath + "." + ext, { as: "text" });
|
|
2023
|
+
entries.push(` ${ext}: \`${escapeTemplateLiteral(content)}\``);
|
|
2024
|
+
} catch {}
|
|
2025
|
+
}
|
|
2026
|
+
if (entries.length > 0) {
|
|
2027
|
+
js += `
|
|
2028
|
+
export const __files = {
|
|
2029
|
+
${entries.join(`,
|
|
2030
|
+
`)}
|
|
2031
|
+
};
|
|
2032
|
+
`;
|
|
2033
|
+
}
|
|
2034
|
+
return js;
|
|
2035
|
+
}
|
|
1943
2036
|
async mergeWidgetIntoManifest(filePath, widgetsDir) {
|
|
1944
2037
|
const relativePath = filePath.slice(widgetsDir.length + 1);
|
|
1945
2038
|
const parts = relativePath.split("/");
|
|
@@ -2448,16 +2541,11 @@ function buildLazyLoaders(tree, widgetEntries, elementEntries, runtime) {
|
|
|
2448
2541
|
class WidgetComponent extends Component {
|
|
2449
2542
|
renderHTML(args) {
|
|
2450
2543
|
const files = args.context.files;
|
|
2451
|
-
const style = files?.css ? `<style>${scopeWidgetCss(files.css, this.name)}</style>
|
|
2452
|
-
` : "";
|
|
2453
2544
|
if (files?.html) {
|
|
2454
|
-
return
|
|
2545
|
+
return files.html;
|
|
2455
2546
|
}
|
|
2456
2547
|
if (files?.md) {
|
|
2457
|
-
return
|
|
2458
|
-
}
|
|
2459
|
-
if (style) {
|
|
2460
|
-
return style + super.renderHTML(args);
|
|
2548
|
+
return `<mark-down>${escapeHtml(files.md)}</mark-down>`;
|
|
2461
2549
|
}
|
|
2462
2550
|
return super.renderHTML(args);
|
|
2463
2551
|
}
|
|
@@ -3467,5 +3555,5 @@ export {
|
|
|
3467
3555
|
BreadcrumbWidget
|
|
3468
3556
|
};
|
|
3469
3557
|
|
|
3470
|
-
//# debugId=
|
|
3558
|
+
//# debugId=9D7A4A39ADB77FE864756E2164756E21
|
|
3471
3559
|
//# sourceMappingURL=emroute.js.map
|