@joist/ssr 4.0.1-next.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -8
- package/src/lib/applicator.test.ts +34 -20
- package/src/lib/applicator.ts +12 -9
- package/src/lib/template-cache.test.ts +8 -8
- package/src/lib/template-cache.ts +4 -4
- package/src/lib/template-loader.test.ts +15 -12
- package/src/lib/template-loader.ts +1 -1
- package/src/lib.ts +6 -3
- package/target/lib/applicator.d.ts +13 -0
- package/target/lib/applicator.js +43 -0
- package/target/lib/applicator.js.map +1 -0
- package/target/lib/applicator.test.d.ts +1 -0
- package/target/lib/applicator.test.js +109 -0
- package/target/lib/applicator.test.js.map +1 -0
- package/target/lib/template-cache.d.ts +9 -0
- package/target/lib/template-cache.js +19 -0
- package/target/lib/template-cache.js.map +1 -0
- package/target/lib/template-cache.test.d.ts +1 -0
- package/target/lib/template-cache.test.js +13 -0
- package/target/lib/template-cache.test.js.map +1 -0
- package/target/lib/template-loader.d.ts +11 -0
- package/target/lib/template-loader.js +26 -0
- package/target/lib/template-loader.js.map +1 -0
- package/target/lib/template-loader.test.d.ts +1 -0
- package/target/lib/template-loader.test.js +13 -0
- package/target/lib/template-loader.test.js.map +1 -0
- package/target/lib.d.ts +3 -0
- package/target/lib.js +4 -0
- package/target/lib.js.map +1 -0
- package/target/testing/elements/my-element/my-element.d.ts +1 -0
- package/target/testing/elements/my-element/my-element.js +2 -0
- package/target/testing/elements/my-element/my-element.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joist/ssr",
|
|
3
|
-
"version": "4.0.1
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./target/lib.js",
|
|
6
6
|
"module": "./target/lib.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"./*": {
|
|
12
|
-
"import": "./target/lib/*.js"
|
|
13
|
-
}
|
|
8
|
+
".": "./target/lib.js",
|
|
9
|
+
"./*": "./target/lib/*",
|
|
10
|
+
"./package.json": "./package.json"
|
|
14
11
|
},
|
|
15
12
|
"files": [
|
|
16
13
|
"src",
|
|
@@ -49,7 +46,7 @@
|
|
|
49
46
|
]
|
|
50
47
|
},
|
|
51
48
|
"test": {
|
|
52
|
-
"command": "
|
|
49
|
+
"command": "mocha target/**/*.test.js",
|
|
53
50
|
"files": [
|
|
54
51
|
"target/**"
|
|
55
52
|
],
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { assert } from "chai";
|
|
2
2
|
|
|
3
|
-
import { Applicator } from
|
|
4
|
-
import { NoopTemplateCache } from
|
|
5
|
-
import { TemplateLoader } from
|
|
3
|
+
import { Applicator } from "./applicator.js";
|
|
4
|
+
import { NoopTemplateCache } from "./template-cache.js";
|
|
5
|
+
import type { TemplateLoader } from "./template-loader.js";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
it("should apply declarative shadow dom to specified elements", async () => {
|
|
8
8
|
class MockTemplateLoader implements TemplateLoader {
|
|
9
9
|
loadCSS(tag: string): Promise<string | null> {
|
|
10
10
|
return Promise.resolve(`:host { content: 'css for ${tag}' }`);
|
|
@@ -14,7 +14,10 @@ test('should apply declarative shadow dom to specified elements', async (t) => {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const applicator = new Applicator(
|
|
17
|
+
const applicator = new Applicator(
|
|
18
|
+
new NoopTemplateCache(),
|
|
19
|
+
new MockTemplateLoader(),
|
|
20
|
+
);
|
|
18
21
|
|
|
19
22
|
const document = /*html*/ `
|
|
20
23
|
<html>
|
|
@@ -28,9 +31,13 @@ test('should apply declarative shadow dom to specified elements', async (t) => {
|
|
|
28
31
|
</html>
|
|
29
32
|
`;
|
|
30
33
|
|
|
31
|
-
const res = await applicator.apply(document, [
|
|
34
|
+
const res = await applicator.apply(document, [
|
|
35
|
+
"mock-header",
|
|
36
|
+
"mock-content",
|
|
37
|
+
"mock-footer",
|
|
38
|
+
]);
|
|
32
39
|
|
|
33
|
-
|
|
40
|
+
assert.equal(
|
|
34
41
|
trim(res),
|
|
35
42
|
trim(`
|
|
36
43
|
<html>
|
|
@@ -59,11 +66,11 @@ test('should apply declarative shadow dom to specified elements', async (t) => {
|
|
|
59
66
|
</mock-footer>
|
|
60
67
|
</body>
|
|
61
68
|
</html>
|
|
62
|
-
`)
|
|
69
|
+
`),
|
|
63
70
|
);
|
|
64
71
|
});
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
it("should apply declarative shadow dom recursively", async () => {
|
|
67
74
|
class MockTemplateLoader implements TemplateLoader {
|
|
68
75
|
async loadCSS(tag: string): Promise<string | null> {
|
|
69
76
|
return `:host { content: 'css for ${tag}' }`;
|
|
@@ -71,24 +78,31 @@ test('should apply declarative shadow dom recursively', async (t) => {
|
|
|
71
78
|
|
|
72
79
|
async loadHTML(tag: string): Promise<string | null> {
|
|
73
80
|
switch (tag) {
|
|
74
|
-
case
|
|
75
|
-
return
|
|
81
|
+
case "mock-foo":
|
|
82
|
+
return "<mock-bar></mock-bar>";
|
|
76
83
|
|
|
77
|
-
case
|
|
78
|
-
return
|
|
84
|
+
case "mock-bar":
|
|
85
|
+
return "<mock-baz></mock-baz>";
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
return `<div>html for ${tag}</div>`;
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
const applicator = new Applicator(
|
|
92
|
+
const applicator = new Applicator(
|
|
93
|
+
new NoopTemplateCache(),
|
|
94
|
+
new MockTemplateLoader(),
|
|
95
|
+
);
|
|
86
96
|
|
|
87
|
-
const document =
|
|
97
|
+
const document = "<mock-foo></mock-foo>";
|
|
88
98
|
|
|
89
|
-
const res = await applicator.apply(document, [
|
|
99
|
+
const res = await applicator.apply(document, [
|
|
100
|
+
"mock-foo",
|
|
101
|
+
"mock-bar",
|
|
102
|
+
"mock-baz",
|
|
103
|
+
]);
|
|
90
104
|
|
|
91
|
-
|
|
105
|
+
assert.equal(
|
|
92
106
|
trim(res),
|
|
93
107
|
trim(`
|
|
94
108
|
<html>
|
|
@@ -113,10 +127,10 @@ test('should apply declarative shadow dom recursively', async (t) => {
|
|
|
113
127
|
</mock-foo>
|
|
114
128
|
</body>
|
|
115
129
|
</html>
|
|
116
|
-
`)
|
|
130
|
+
`),
|
|
117
131
|
);
|
|
118
132
|
});
|
|
119
133
|
|
|
120
134
|
function trim(value: string) {
|
|
121
|
-
return value.replace(/\s+/g,
|
|
135
|
+
return value.replace(/\s+/g, "").replace(/(\r\n|\n|\r)/gm, "");
|
|
122
136
|
}
|
package/src/lib/applicator.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { CheerioAPI, load } from
|
|
1
|
+
import { type CheerioAPI, load } from "cheerio";
|
|
2
2
|
|
|
3
|
-
import { TemplateCache } from
|
|
4
|
-
import { TemplateLoader } from
|
|
3
|
+
import type { TemplateCache } from "./template-cache.js";
|
|
4
|
+
import type { TemplateLoader } from "./template-loader.js";
|
|
5
5
|
|
|
6
6
|
export interface ApplicatorOpts {
|
|
7
7
|
templateCache: TemplateCache;
|
|
@@ -17,7 +17,7 @@ export class Applicator {
|
|
|
17
17
|
this.#templateLoader = templateLoader;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async apply(document: string, elements: string[]) {
|
|
20
|
+
async apply(document: string, elements: string[]): Promise<string> {
|
|
21
21
|
const $ = load(document);
|
|
22
22
|
|
|
23
23
|
return this.build($, elements);
|
|
@@ -33,12 +33,15 @@ export class Applicator {
|
|
|
33
33
|
|
|
34
34
|
if (!elementTemplate) {
|
|
35
35
|
const template = await this.#buildTemplate(element);
|
|
36
|
-
elementTemplate = await this.build(
|
|
36
|
+
elementTemplate = await this.build(
|
|
37
|
+
load(template, null, false),
|
|
38
|
+
elements,
|
|
39
|
+
);
|
|
37
40
|
|
|
38
41
|
await this.#templateCache.set(element, elementTemplate);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
if (node.find(
|
|
44
|
+
if (node.find("> template[shadowrootmode]").length === 0) {
|
|
42
45
|
node.prepend(elementTemplate);
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -50,12 +53,12 @@ export class Applicator {
|
|
|
50
53
|
async #buildTemplate(tag: string) {
|
|
51
54
|
const [html, styles] = await Promise.all([
|
|
52
55
|
this.#templateLoader.loadHTML(tag),
|
|
53
|
-
this.#templateLoader.loadCSS(tag)
|
|
56
|
+
this.#templateLoader.loadCSS(tag),
|
|
54
57
|
]);
|
|
55
58
|
|
|
56
59
|
return `<template shadowroot="open" shadowrootmode="open">
|
|
57
|
-
${styles ? `<style>${styles}</style>` :
|
|
58
|
-
${html ||
|
|
60
|
+
${styles ? `<style>${styles}</style>` : ""}
|
|
61
|
+
${html || ""}
|
|
59
62
|
</template>
|
|
60
63
|
`;
|
|
61
64
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { assert } from "chai";
|
|
2
2
|
|
|
3
|
-
import { NoopTemplateCache, TemplateCache } from
|
|
3
|
+
import { NoopTemplateCache, TemplateCache } from "./template-cache.js";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
it("should cache (in memory)", async () => {
|
|
6
6
|
const cache = new TemplateCache();
|
|
7
7
|
|
|
8
|
-
await cache.set(
|
|
8
|
+
await cache.set("foo-bar", "<h1>Hello World</h1>");
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
assert.equal(await cache.get("foo-bar"), "<h1>Hello World</h1>");
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
it("should never cache (noop)", async () => {
|
|
14
14
|
const cache = new NoopTemplateCache();
|
|
15
15
|
|
|
16
|
-
await cache.set(
|
|
16
|
+
await cache.set("foo-bar", "<h1>Hello World</h1>");
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
assert.equal(await cache.get("foo-bar"), undefined);
|
|
19
19
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export class TemplateCache {
|
|
2
2
|
#cache = new Map<string, string>();
|
|
3
3
|
|
|
4
|
-
async get(key: string) {
|
|
4
|
+
async get(key: string): Promise<string | undefined> {
|
|
5
5
|
return this.#cache.get(key);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
async set(key: string, val: string) {
|
|
8
|
+
async set(key: string, val: string): Promise<this> {
|
|
9
9
|
this.#cache.set(key, val);
|
|
10
10
|
|
|
11
11
|
return this;
|
|
@@ -13,11 +13,11 @@ export class TemplateCache {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export class NoopTemplateCache extends TemplateCache {
|
|
16
|
-
async get(_: string) {
|
|
16
|
+
async get(_: string): Promise<undefined> {
|
|
17
17
|
return undefined;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async set(_key: string, _val: string) {
|
|
20
|
+
async set(_key: string, _val: string): Promise<this> {
|
|
21
21
|
return this;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { fileURLToPath } from
|
|
3
|
-
import {
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { assert } from "chai";
|
|
4
4
|
|
|
5
|
-
import { FileSysTemplateLoader } from
|
|
5
|
+
import { FileSysTemplateLoader } from "./template-loader.js";
|
|
6
6
|
|
|
7
|
-
const dirname = join(
|
|
7
|
+
const dirname = join(
|
|
8
|
+
fileURLToPath(new URL(".", import.meta.url)),
|
|
9
|
+
"../../src/testing",
|
|
10
|
+
);
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
it("FileSysTemplateLoader: should read from defined paths", async () => {
|
|
10
13
|
const loader = new FileSysTemplateLoader(
|
|
11
|
-
(tag) => join(dirname,
|
|
12
|
-
(tag) => join(dirname,
|
|
14
|
+
(tag) => join(dirname, "elements", tag, `${tag}.html`),
|
|
15
|
+
(tag) => join(dirname, "elements", tag, `${tag}.css`),
|
|
13
16
|
);
|
|
14
17
|
|
|
15
|
-
const html = await loader.loadHTML(
|
|
16
|
-
const css = await loader.loadCSS(
|
|
18
|
+
const html = await loader.loadHTML("my-element");
|
|
19
|
+
const css = await loader.loadCSS("my-element");
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
assert.equal(html?.trim(), "<h2>Hello World</h2>\n\n<slot></slot>");
|
|
22
|
+
assert.equal(css?.trim(), ":host {\n display: flex;\n}");
|
|
20
23
|
});
|
package/src/lib.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export { Applicator } from
|
|
2
|
-
export { TemplateCache, NoopTemplateCache } from
|
|
3
|
-
export {
|
|
1
|
+
export { Applicator } from "./lib/applicator.js";
|
|
2
|
+
export { TemplateCache, NoopTemplateCache } from "./lib/template-cache.js";
|
|
3
|
+
export {
|
|
4
|
+
TemplateLoader,
|
|
5
|
+
FileSysTemplateLoader,
|
|
6
|
+
} from "./lib/template-loader.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type CheerioAPI } from "cheerio";
|
|
2
|
+
import type { TemplateCache } from "./template-cache.js";
|
|
3
|
+
import type { TemplateLoader } from "./template-loader.js";
|
|
4
|
+
export interface ApplicatorOpts {
|
|
5
|
+
templateCache: TemplateCache;
|
|
6
|
+
templateLoader: TemplateLoader;
|
|
7
|
+
}
|
|
8
|
+
export declare class Applicator {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(templateCache: TemplateCache, templateLoader: TemplateLoader);
|
|
11
|
+
apply(document: string, elements: string[]): Promise<string>;
|
|
12
|
+
build($: CheerioAPI, elements: string[]): Promise<string>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { load } from "cheerio";
|
|
2
|
+
export class Applicator {
|
|
3
|
+
#templateCache;
|
|
4
|
+
#templateLoader;
|
|
5
|
+
constructor(templateCache, templateLoader) {
|
|
6
|
+
this.#templateCache = templateCache;
|
|
7
|
+
this.#templateLoader = templateLoader;
|
|
8
|
+
}
|
|
9
|
+
async apply(document, elements) {
|
|
10
|
+
const $ = load(document);
|
|
11
|
+
return this.build($, elements);
|
|
12
|
+
}
|
|
13
|
+
async build($, elements) {
|
|
14
|
+
for (let i = 0; i < elements.length; i++) {
|
|
15
|
+
const element = elements[i];
|
|
16
|
+
const node = $(element);
|
|
17
|
+
if (node.length) {
|
|
18
|
+
let elementTemplate = await this.#templateCache.get(element);
|
|
19
|
+
if (!elementTemplate) {
|
|
20
|
+
const template = await this.#buildTemplate(element);
|
|
21
|
+
elementTemplate = await this.build(load(template, null, false), elements);
|
|
22
|
+
await this.#templateCache.set(element, elementTemplate);
|
|
23
|
+
}
|
|
24
|
+
if (node.find("> template[shadowrootmode]").length === 0) {
|
|
25
|
+
node.prepend(elementTemplate);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return $.html();
|
|
30
|
+
}
|
|
31
|
+
async #buildTemplate(tag) {
|
|
32
|
+
const [html, styles] = await Promise.all([
|
|
33
|
+
this.#templateLoader.loadHTML(tag),
|
|
34
|
+
this.#templateLoader.loadCSS(tag),
|
|
35
|
+
]);
|
|
36
|
+
return `<template shadowroot="open" shadowrootmode="open">
|
|
37
|
+
${styles ? `<style>${styles}</style>` : ""}
|
|
38
|
+
${html || ""}
|
|
39
|
+
</template>
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=applicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applicator.js","sourceRoot":"","sources":["../../src/lib/applicator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,IAAI,EAAE,MAAM,SAAS,CAAC;AAUhD,MAAM,OAAO,UAAU;IACrB,cAAc,CAAgB;IAC9B,eAAe,CAAiB;IAEhC,YAAY,aAA4B,EAAE,cAA8B;QACtE,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QACpC,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,QAAgB,EAAE,QAAkB;QAC9C,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEzB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,CAAa,EAAE,QAAkB;QAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;YAExB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,eAAe,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAE7D,IAAI,CAAC,eAAe,EAAE,CAAC;oBACrB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;oBACpD,eAAe,GAAG,MAAM,IAAI,CAAC,KAAK,CAChC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,EAC3B,QAAQ,CACT,CAAC;oBAEF,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;gBAC1D,CAAC;gBAED,IAAI,IAAI,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzD,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,GAAW;QAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC;YAClC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC;SAClC,CAAC,CAAC;QAEH,OAAO;QACH,MAAM,CAAC,CAAC,CAAC,UAAU,MAAM,UAAU,CAAC,CAAC,CAAC,EAAE;QACxC,IAAI,IAAI,EAAE;;GAEf,CAAC;IACF,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { assert } from "chai";
|
|
2
|
+
import { Applicator } from "./applicator.js";
|
|
3
|
+
import { NoopTemplateCache } from "./template-cache.js";
|
|
4
|
+
it("should apply declarative shadow dom to specified elements", async () => {
|
|
5
|
+
class MockTemplateLoader {
|
|
6
|
+
loadCSS(tag) {
|
|
7
|
+
return Promise.resolve(`:host { content: 'css for ${tag}' }`);
|
|
8
|
+
}
|
|
9
|
+
loadHTML(tag) {
|
|
10
|
+
return Promise.resolve(`<div>html for ${tag}</div>`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const applicator = new Applicator(new NoopTemplateCache(), new MockTemplateLoader());
|
|
14
|
+
const document = `
|
|
15
|
+
<html>
|
|
16
|
+
<head></head>
|
|
17
|
+
|
|
18
|
+
<body>
|
|
19
|
+
<mock-header></mock-header>
|
|
20
|
+
<mock-content></mock-content>
|
|
21
|
+
<mock-footer></mock-footer>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
24
|
+
`;
|
|
25
|
+
const res = await applicator.apply(document, [
|
|
26
|
+
"mock-header",
|
|
27
|
+
"mock-content",
|
|
28
|
+
"mock-footer",
|
|
29
|
+
]);
|
|
30
|
+
assert.equal(trim(res), trim(`
|
|
31
|
+
<html>
|
|
32
|
+
<head></head>
|
|
33
|
+
|
|
34
|
+
<body>
|
|
35
|
+
<mock-header>
|
|
36
|
+
<template shadowroot="open" shadowrootmode="open">
|
|
37
|
+
<style>:host { content: 'css for mock-header'}</style>
|
|
38
|
+
<div>html for mock-header</div>
|
|
39
|
+
</template>
|
|
40
|
+
</mock-header>
|
|
41
|
+
|
|
42
|
+
<mock-content>
|
|
43
|
+
<template shadowroot="open" shadowrootmode="open">
|
|
44
|
+
<style>:host { content: 'css for mock-content'}</style>
|
|
45
|
+
<div>html for mock-content</div>
|
|
46
|
+
</template>
|
|
47
|
+
</mock-content>
|
|
48
|
+
|
|
49
|
+
<mock-footer>
|
|
50
|
+
<template shadowroot="open" shadowrootmode="open">
|
|
51
|
+
<style>:host { content: 'css for mock-footer'}</style>
|
|
52
|
+
<div>html for mock-footer</div>
|
|
53
|
+
</template>
|
|
54
|
+
</mock-footer>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
|
57
|
+
`));
|
|
58
|
+
});
|
|
59
|
+
it("should apply declarative shadow dom recursively", async () => {
|
|
60
|
+
class MockTemplateLoader {
|
|
61
|
+
async loadCSS(tag) {
|
|
62
|
+
return `:host { content: 'css for ${tag}' }`;
|
|
63
|
+
}
|
|
64
|
+
async loadHTML(tag) {
|
|
65
|
+
switch (tag) {
|
|
66
|
+
case "mock-foo":
|
|
67
|
+
return "<mock-bar></mock-bar>";
|
|
68
|
+
case "mock-bar":
|
|
69
|
+
return "<mock-baz></mock-baz>";
|
|
70
|
+
}
|
|
71
|
+
return `<div>html for ${tag}</div>`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const applicator = new Applicator(new NoopTemplateCache(), new MockTemplateLoader());
|
|
75
|
+
const document = "<mock-foo></mock-foo>";
|
|
76
|
+
const res = await applicator.apply(document, [
|
|
77
|
+
"mock-foo",
|
|
78
|
+
"mock-bar",
|
|
79
|
+
"mock-baz",
|
|
80
|
+
]);
|
|
81
|
+
assert.equal(trim(res), trim(`
|
|
82
|
+
<html>
|
|
83
|
+
<head></head>
|
|
84
|
+
|
|
85
|
+
<body>
|
|
86
|
+
<mock-foo>
|
|
87
|
+
<template shadowroot="open" shadowrootmode="open">
|
|
88
|
+
<style>:host { content: 'css for mock-foo'}</style>
|
|
89
|
+
<mock-bar>
|
|
90
|
+
<template shadowroot="open" shadowrootmode="open">
|
|
91
|
+
<style>:host { content: 'css for mock-bar'}</style>
|
|
92
|
+
<mock-baz>
|
|
93
|
+
<template shadowroot="open" shadowrootmode="open">
|
|
94
|
+
<style>:host { content: 'css for mock-baz'}</style>
|
|
95
|
+
<div>html for mock-baz</div>
|
|
96
|
+
</template>
|
|
97
|
+
</mock-baz>
|
|
98
|
+
</template>
|
|
99
|
+
</mock-bar>
|
|
100
|
+
</template>
|
|
101
|
+
</mock-foo>
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
104
|
+
`));
|
|
105
|
+
});
|
|
106
|
+
function trim(value) {
|
|
107
|
+
return value.replace(/\s+/g, "").replace(/(\r\n|\n|\r)/gm, "");
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=applicator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applicator.test.js","sourceRoot":"","sources":["../../src/lib/applicator.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IACzE,MAAM,kBAAkB;QACtB,OAAO,CAAC,GAAW;YACjB,OAAO,OAAO,CAAC,OAAO,CAAC,6BAA6B,GAAG,KAAK,CAAC,CAAC;QAChE,CAAC;QACD,QAAQ,CAAC,GAAW;YAClB,OAAO,OAAO,CAAC,OAAO,CAAC,iBAAiB,GAAG,QAAQ,CAAC,CAAC;QACvD,CAAC;KACF;IAED,MAAM,UAAU,GAAG,IAAI,UAAU,CAC/B,IAAI,iBAAiB,EAAE,EACvB,IAAI,kBAAkB,EAAE,CACzB,CAAC;IAEF,MAAM,QAAQ,GAAY;;;;;;;;;;GAUzB,CAAC;IAEF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE;QAC3C,aAAa;QACb,cAAc;QACd,aAAa;KACd,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CACV,IAAI,CAAC,GAAG,CAAC,EACT,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BN,CAAC,CACD,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;IAC/D,MAAM,kBAAkB;QACtB,KAAK,CAAC,OAAO,CAAC,GAAW;YACvB,OAAO,6BAA6B,GAAG,KAAK,CAAC;QAC/C,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,GAAW;YACxB,QAAQ,GAAG,EAAE,CAAC;gBACZ,KAAK,UAAU;oBACb,OAAO,uBAAuB,CAAC;gBAEjC,KAAK,UAAU;oBACb,OAAO,uBAAuB,CAAC;YACnC,CAAC;YAED,OAAO,iBAAiB,GAAG,QAAQ,CAAC;QACtC,CAAC;KACF;IAED,MAAM,UAAU,GAAG,IAAI,UAAU,CAC/B,IAAI,iBAAiB,EAAE,EACvB,IAAI,kBAAkB,EAAE,CACzB,CAAC;IAEF,MAAM,QAAQ,GAAG,uBAAuB,CAAC;IAEzC,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE;QAC3C,UAAU;QACV,UAAU;QACV,UAAU;KACX,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CACV,IAAI,CAAC,GAAG,CAAC,EACT,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;GAuBN,CAAC,CACD,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,SAAS,IAAI,CAAC,KAAa;IACzB,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class TemplateCache {
|
|
2
|
+
#private;
|
|
3
|
+
get(key: string): Promise<string | undefined>;
|
|
4
|
+
set(key: string, val: string): Promise<this>;
|
|
5
|
+
}
|
|
6
|
+
export declare class NoopTemplateCache extends TemplateCache {
|
|
7
|
+
get(_: string): Promise<undefined>;
|
|
8
|
+
set(_key: string, _val: string): Promise<this>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class TemplateCache {
|
|
2
|
+
#cache = new Map();
|
|
3
|
+
async get(key) {
|
|
4
|
+
return this.#cache.get(key);
|
|
5
|
+
}
|
|
6
|
+
async set(key, val) {
|
|
7
|
+
this.#cache.set(key, val);
|
|
8
|
+
return this;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class NoopTemplateCache extends TemplateCache {
|
|
12
|
+
async get(_) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
async set(_key, _val) {
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=template-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-cache.js","sourceRoot":"","sources":["../../src/lib/template-cache.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,aAAa;IACxB,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEnC,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,GAAW;QAChC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAE1B,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,aAAa;IAClD,KAAK,CAAC,GAAG,CAAC,CAAS;QACjB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,IAAY;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { assert } from "chai";
|
|
2
|
+
import { NoopTemplateCache, TemplateCache } from "./template-cache.js";
|
|
3
|
+
it("should cache (in memory)", async () => {
|
|
4
|
+
const cache = new TemplateCache();
|
|
5
|
+
await cache.set("foo-bar", "<h1>Hello World</h1>");
|
|
6
|
+
assert.equal(await cache.get("foo-bar"), "<h1>Hello World</h1>");
|
|
7
|
+
});
|
|
8
|
+
it("should never cache (noop)", async () => {
|
|
9
|
+
const cache = new NoopTemplateCache();
|
|
10
|
+
await cache.set("foo-bar", "<h1>Hello World</h1>");
|
|
11
|
+
assert.equal(await cache.get("foo-bar"), undefined);
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=template-cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-cache.test.js","sourceRoot":"","sources":["../../src/lib/template-cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEvE,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;IACxC,MAAM,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;IAElC,MAAM,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAEnD,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,sBAAsB,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;IACzC,MAAM,KAAK,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAEtC,MAAM,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAEnD,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TemplateLoader {
|
|
2
|
+
loadHTML(tag: string): Promise<string | null>;
|
|
3
|
+
loadCSS(tag: string): Promise<string | null>;
|
|
4
|
+
}
|
|
5
|
+
export type PathFn = (tag: string) => string;
|
|
6
|
+
export declare class FileSysTemplateLoader implements TemplateLoader {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(html: PathFn, css: PathFn);
|
|
9
|
+
loadHTML(tag: string): Promise<string | null>;
|
|
10
|
+
loadCSS(tag: string): Promise<string | null>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
export class FileSysTemplateLoader {
|
|
3
|
+
#html;
|
|
4
|
+
#css;
|
|
5
|
+
constructor(html, css) {
|
|
6
|
+
this.#html = html;
|
|
7
|
+
this.#css = css;
|
|
8
|
+
}
|
|
9
|
+
async loadHTML(tag) {
|
|
10
|
+
try {
|
|
11
|
+
return await readFile(this.#html(tag)).then((res) => res.toString());
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async loadCSS(tag) {
|
|
18
|
+
try {
|
|
19
|
+
return await readFile(this.#css(tag)).then((res) => res.toString());
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=template-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-loader.js","sourceRoot":"","sources":["../../src/lib/template-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAY5C,MAAM,OAAO,qBAAqB;IAChC,KAAK,CAAS;IACd,IAAI,CAAS;IAEb,YAAY,IAAY,EAAE,GAAW;QACnC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAW;QACxB,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { assert } from "chai";
|
|
4
|
+
import { FileSysTemplateLoader } from "./template-loader.js";
|
|
5
|
+
const dirname = join(fileURLToPath(new URL(".", import.meta.url)), "../../src/testing");
|
|
6
|
+
it("FileSysTemplateLoader: should read from defined paths", async () => {
|
|
7
|
+
const loader = new FileSysTemplateLoader((tag) => join(dirname, "elements", tag, `${tag}.html`), (tag) => join(dirname, "elements", tag, `${tag}.css`));
|
|
8
|
+
const html = await loader.loadHTML("my-element");
|
|
9
|
+
const css = await loader.loadCSS("my-element");
|
|
10
|
+
assert.equal(html?.trim(), "<h2>Hello World</h2>\n\n<slot></slot>");
|
|
11
|
+
assert.equal(css?.trim(), ":host {\n display: flex;\n}");
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=template-loader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-loader.test.js","sourceRoot":"","sources":["../../src/lib/template-loader.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,GAAG,IAAI,CAClB,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAC5C,mBAAmB,CACpB,CAAC;AAEF,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;IACrE,MAAM,MAAM,GAAG,IAAI,qBAAqB,CACtC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,EACtD,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAAC,CACtD,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAE/C,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,uCAAuC,CAAC,CAAC;IACpE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,8BAA8B,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC"}
|
package/target/lib.d.ts
ADDED
package/target/lib.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAEL,qBAAqB,GACtB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"my-element.js","sourceRoot":"","sources":["../../../../src/testing/elements/my-element/my-element.ts"],"names":[],"mappings":""}
|