@torpor/view 0.0.9 → 0.0.10
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/CHANGELOG.md +6 -0
- package/dist/compile.cjs +27 -5
- package/dist/compile.cjs.map +1 -1
- package/dist/compile.d.cts +1 -0
- package/dist/compile.d.ts +1 -0
- package/dist/compile.js +27 -5
- package/dist/compile.js.map +1 -1
- package/dist/index.cjs +12 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +1 -1
- package/src/compile/build/client/BuildStatus.ts +1 -0
- package/src/compile/build/client/buildCode.ts +1 -0
- package/src/compile/build/client/buildFragment.ts +1 -1
- package/src/compile/build/client/buildFragmentText.ts +26 -4
- package/src/compile/types/nodes/Fragment.ts +1 -0
- package/src/render/getFragment.ts +13 -3
- package/test/console/components/temp/{Console-client-2f206752ce59e59fba9bb91263b4102fd515951e.ts → Console-client-254d7d4799471401334d698b1631ab1cb5b70558.ts} +1 -1
- package/test/svg/components/Shape.d.ts +18 -0
- package/test/svg/components/Shape.torp +12 -0
- package/test/svg/components/output/Shape-client.ts +61 -0
- package/test/svg/components/output/Shape-server.ts +23 -0
- package/test/svg/components/temp/Shape-client-ea698ed1b7d0c3615adff35fa4956f22840f5fda.ts +61 -0
- package/test/svg/components/temp/Shape-server-eb32049cfb7c058b2c4079c5f86ef00651ff6d69.ts +23 -0
- package/test/svg/svg.test.ts +50 -0
package/package.json
CHANGED
|
@@ -74,7 +74,7 @@ export default function buildFragment(
|
|
|
74
74
|
status.imports.add("t_fragment");
|
|
75
75
|
const fragmentText = fragment.text.replaceAll("`", "\\`").replaceAll(/\s+/g, " ");
|
|
76
76
|
b.append(
|
|
77
|
-
`const ${fragmentName} = t_fragment($parent.ownerDocument!, t_fragments, ${fragment.number}, \`${fragmentText}
|
|
77
|
+
`const ${fragmentName} = t_fragment($parent.ownerDocument!, t_fragments, ${fragment.number}, \`${fragmentText}\`${fragment.ns ? ", true" : ""});`,
|
|
78
78
|
);
|
|
79
79
|
let fragmentPath = { parent: null, type: "fragment", children: [] };
|
|
80
80
|
let varPaths = new Map<string, string>();
|
|
@@ -45,7 +45,11 @@ function buildNodeFragmentText(
|
|
|
45
45
|
break;
|
|
46
46
|
}
|
|
47
47
|
case "element": {
|
|
48
|
+
// NOTE: We don't support nested SVG for now
|
|
49
|
+
let ns = (node as ElementNode).tagName === "svg";
|
|
50
|
+
if (ns) status.ns = true;
|
|
48
51
|
buildElementFragmentText(node as ElementNode, status, fragments, currentFragment!);
|
|
52
|
+
if (ns) status.ns = false;
|
|
49
53
|
break;
|
|
50
54
|
}
|
|
51
55
|
case "text": {
|
|
@@ -63,7 +67,7 @@ function buildNodeFragmentText(
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
function buildRootFragmentText(node: RootNode, status: BuildStatus, fragments: Fragment[]) {
|
|
66
|
-
node.fragment = { number: fragments.length, text: "", events: [], animations: [] };
|
|
70
|
+
node.fragment = { number: fragments.length, text: "", ns: status.ns, events: [], animations: [] };
|
|
67
71
|
fragments.push(node.fragment);
|
|
68
72
|
for (let child of node.children) {
|
|
69
73
|
buildNodeFragmentText(child, status, fragments, node.fragment);
|
|
@@ -94,7 +98,13 @@ function buildControlFragmentText(
|
|
|
94
98
|
}
|
|
95
99
|
default: {
|
|
96
100
|
// Add a new fragment if it's a control branch and it has children
|
|
97
|
-
node.fragment = {
|
|
101
|
+
node.fragment = {
|
|
102
|
+
number: fragments.length,
|
|
103
|
+
text: "",
|
|
104
|
+
ns: status.ns,
|
|
105
|
+
events: [],
|
|
106
|
+
animations: [],
|
|
107
|
+
};
|
|
98
108
|
fragments.push(node.fragment);
|
|
99
109
|
for (let child of node.children) {
|
|
100
110
|
buildNodeFragmentText(child, status, fragments, node.fragment);
|
|
@@ -114,7 +124,13 @@ function buildComponentFragmentText(
|
|
|
114
124
|
|
|
115
125
|
// Add fragments for slots if there are children
|
|
116
126
|
if (node.children.length) {
|
|
117
|
-
node.fragment = {
|
|
127
|
+
node.fragment = {
|
|
128
|
+
number: fragments.length,
|
|
129
|
+
text: "",
|
|
130
|
+
ns: status.ns,
|
|
131
|
+
events: [],
|
|
132
|
+
animations: [],
|
|
133
|
+
};
|
|
118
134
|
fragments.push(node.fragment);
|
|
119
135
|
for (let child of node.children) {
|
|
120
136
|
// TODO: Make sure it's not a :fill node
|
|
@@ -172,7 +188,13 @@ function buildSpecialFragmentText(
|
|
|
172
188
|
}
|
|
173
189
|
case ":fill": {
|
|
174
190
|
// Add a new fragment for filled slot content
|
|
175
|
-
node.fragment = {
|
|
191
|
+
node.fragment = {
|
|
192
|
+
number: fragments.length,
|
|
193
|
+
text: "",
|
|
194
|
+
ns: status.ns,
|
|
195
|
+
events: [],
|
|
196
|
+
animations: [],
|
|
197
|
+
};
|
|
176
198
|
fragments.push(node.fragment);
|
|
177
199
|
for (let child of node.children) {
|
|
178
200
|
buildNodeFragmentText(child, status, fragments, node.fragment);
|
|
@@ -3,12 +3,22 @@ export default function getFragment(
|
|
|
3
3
|
array: DocumentFragment[],
|
|
4
4
|
index: number,
|
|
5
5
|
html: string,
|
|
6
|
+
ns?: boolean,
|
|
6
7
|
): DocumentFragment {
|
|
7
8
|
// Create the fragment in the array if it hasn't yet been used
|
|
8
9
|
if (array[index] === undefined) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
// TODO: Should pass a string for the ns, when we support other element types
|
|
11
|
+
if (ns) {
|
|
12
|
+
const template = document.createElementNS("http://www.w3.org/2000/svg", "template");
|
|
13
|
+
template.innerHTML = html;
|
|
14
|
+
const fragment = new DocumentFragment();
|
|
15
|
+
fragment.append(...template.childNodes);
|
|
16
|
+
array[index] = fragment;
|
|
17
|
+
} else {
|
|
18
|
+
const template = document.createElement("template");
|
|
19
|
+
template.innerHTML = html;
|
|
20
|
+
array[index] = template.content;
|
|
21
|
+
}
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
// Get the fragment from the array
|
|
@@ -18,7 +18,7 @@ export default function Console(
|
|
|
18
18
|
/* User interface */
|
|
19
19
|
const t_fragments: DocumentFragment[] = [];
|
|
20
20
|
|
|
21
|
-
const t_fragment_0 = t_fragment($parent.ownerDocument!, t_fragments, 0, `<div> </div
|
|
21
|
+
const t_fragment_0 = t_fragment($parent.ownerDocument!, t_fragments, 0, `<div> </div>`, false);
|
|
22
22
|
// @ts-ignore
|
|
23
23
|
const t_div_1 = t_root(t_fragment_0) as HTMLDivElement;
|
|
24
24
|
/* @console */
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type SlotRender } from "@torpor/view";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mounts or hydrates the component into the supplied parent node.
|
|
5
|
+
* @param $parent -- The parent node.
|
|
6
|
+
* @param $anchor -- The node to mount the component before.
|
|
7
|
+
* @param $props -- The values that have been passed into the component as properties.
|
|
8
|
+
* @param $context -- Values that have been passed into the component from its ancestors.
|
|
9
|
+
* @param $slots -- Functions for rendering children into slot nodes within the component.
|
|
10
|
+
*/
|
|
11
|
+
declare function Shape(
|
|
12
|
+
$parent: ParentNode,
|
|
13
|
+
$anchor: Node | null,
|
|
14
|
+
$props: { name: string },
|
|
15
|
+
$context?: Record<PropertyKey, any>,
|
|
16
|
+
$slots?: Record<string, SlotRender>,
|
|
17
|
+
): void;
|
|
18
|
+
export default Shape;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
export default function Shape($props: { name: string }) {
|
|
3
|
+
@render {
|
|
4
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" role="img">
|
|
5
|
+
@if ($props.name === "rect") {
|
|
6
|
+
<rect width="100" height="100" fill="red"></rect>
|
|
7
|
+
} else {
|
|
8
|
+
<circle r="45" cx="50" cy="50" fill="red"></circle>
|
|
9
|
+
}
|
|
10
|
+
</svg>
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type SlotRender } from "../../../../src/types/SlotRender";
|
|
2
|
+
import t_add_fragment from "../../../../src/render/addFragment";
|
|
3
|
+
import t_anchor from "../../../../src/render/nodeAnchor";
|
|
4
|
+
import t_child from "../../../../src/render/nodeChild";
|
|
5
|
+
import t_fragment from "../../../../src/render/getFragment";
|
|
6
|
+
import t_next from "../../../../src/render/nodeNext";
|
|
7
|
+
import t_range from "../../../../src/render/newRange";
|
|
8
|
+
import t_root from "../../../../src/render/nodeRoot";
|
|
9
|
+
import t_run_branch from "../../../../src/render/runControlBranch";
|
|
10
|
+
import t_run_control from "../../../../src/render/runControl";
|
|
11
|
+
|
|
12
|
+
export default function Shape(
|
|
13
|
+
$parent: ParentNode,
|
|
14
|
+
$anchor: Node | null,
|
|
15
|
+
$props: { name: string },
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
$context?: Record<PropertyKey, any>,
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
$slots?: Record<string, SlotRender>
|
|
20
|
+
) {
|
|
21
|
+
$props ??= {};
|
|
22
|
+
|
|
23
|
+
/* User interface */
|
|
24
|
+
const t_fragments: DocumentFragment[] = [];
|
|
25
|
+
|
|
26
|
+
const t_fragment_0 = t_fragment($parent.ownerDocument!, t_fragments, 0, `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" role="img"> <!> </svg>`);
|
|
27
|
+
// @ts-ignore
|
|
28
|
+
const t_svg_1 = t_root(t_fragment_0) as HTMLElement;
|
|
29
|
+
const t_if_anchor_1 = t_anchor(t_next(t_child(t_svg_1))) as HTMLElement;
|
|
30
|
+
|
|
31
|
+
/* @if */
|
|
32
|
+
const t_if_range_1 = t_range();
|
|
33
|
+
t_run_control(t_if_range_1, t_if_anchor_1, (t_before) => {
|
|
34
|
+
if ($props.name === "rect") {
|
|
35
|
+
t_run_branch(t_if_range_1, 0, () => {
|
|
36
|
+
const t_fragment_1 = t_fragment($parent.ownerDocument!, t_fragments, 1, ` <rect width="100" height="100" fill="red"></rect> `, true);
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
const t_root_1 = t_root(t_fragment_1);
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
const t_text_1 = t_next(t_next(t_root_1));
|
|
41
|
+
t_add_fragment(t_fragment_1, t_svg_1, t_before);
|
|
42
|
+
t_next(t_text_1);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
t_run_branch(t_if_range_1, 1, () => {
|
|
47
|
+
const t_fragment_2 = t_fragment($parent.ownerDocument!, t_fragments, 2, ` <circle r="45" cx="50" cy="50" fill="red"></circle> `, true);
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
const t_root_2 = t_root(t_fragment_2);
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
const t_text_2 = t_next(t_next(t_root_2));
|
|
52
|
+
t_add_fragment(t_fragment_2, t_svg_1, t_before);
|
|
53
|
+
t_next(t_text_2);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
t_add_fragment(t_fragment_0, $parent, $anchor);
|
|
59
|
+
t_next(t_svg_1);
|
|
60
|
+
|
|
61
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ServerSlotRender } from "../../../../src/types/ServerSlotRender";
|
|
2
|
+
|
|
3
|
+
export default function Shape(
|
|
4
|
+
$props: { name: string },
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
$context?: Record<PropertyKey, any>,
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
$slots?: Record<string, ServerSlotRender>
|
|
9
|
+
) {
|
|
10
|
+
|
|
11
|
+
/* User interface */
|
|
12
|
+
let $output = "";
|
|
13
|
+
$output += `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" role="img"> <![>`;
|
|
14
|
+
if ($props.name === "rect") {
|
|
15
|
+
$output += ` <rect width="100" height="100" fill="red"></rect> `;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
$output += ` <circle r="45" cx="50" cy="50" fill="red"></circle> `;
|
|
19
|
+
}
|
|
20
|
+
$output += `<!]><!> </svg>`;
|
|
21
|
+
|
|
22
|
+
return $output;
|
|
23
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type SlotRender } from "../../../../src/types/SlotRender";
|
|
2
|
+
import t_add_fragment from "../../../../src/render/addFragment";
|
|
3
|
+
import t_anchor from "../../../../src/render/nodeAnchor";
|
|
4
|
+
import t_child from "../../../../src/render/nodeChild";
|
|
5
|
+
import t_fragment from "../../../../src/render/getFragment";
|
|
6
|
+
import t_next from "../../../../src/render/nodeNext";
|
|
7
|
+
import t_range from "../../../../src/render/newRange";
|
|
8
|
+
import t_root from "../../../../src/render/nodeRoot";
|
|
9
|
+
import t_run_branch from "../../../../src/render/runControlBranch";
|
|
10
|
+
import t_run_control from "../../../../src/render/runControl";
|
|
11
|
+
|
|
12
|
+
export default function Shape(
|
|
13
|
+
$parent: ParentNode,
|
|
14
|
+
$anchor: Node | null,
|
|
15
|
+
$props: { name: string },
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
$context?: Record<PropertyKey, any>,
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
$slots?: Record<string, SlotRender>
|
|
20
|
+
) {
|
|
21
|
+
$props ??= {};
|
|
22
|
+
|
|
23
|
+
/* User interface */
|
|
24
|
+
const t_fragments: DocumentFragment[] = [];
|
|
25
|
+
|
|
26
|
+
const t_fragment_0 = t_fragment($parent.ownerDocument!, t_fragments, 0, `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" role="img"> <!> </svg>`);
|
|
27
|
+
// @ts-ignore
|
|
28
|
+
const t_svg_1 = t_root(t_fragment_0) as HTMLElement;
|
|
29
|
+
const t_if_anchor_1 = t_anchor(t_next(t_child(t_svg_1))) as HTMLElement;
|
|
30
|
+
|
|
31
|
+
/* @if */
|
|
32
|
+
const t_if_range_1 = t_range();
|
|
33
|
+
t_run_control(t_if_range_1, t_if_anchor_1, (t_before) => {
|
|
34
|
+
if ($props.name === "rect") {
|
|
35
|
+
t_run_branch(t_if_range_1, 0, () => {
|
|
36
|
+
const t_fragment_1 = t_fragment($parent.ownerDocument!, t_fragments, 1, ` <rect width="100" height="100" fill="red"></rect> `, true);
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
const t_root_1 = t_root(t_fragment_1);
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
const t_text_1 = t_next(t_next(t_root_1));
|
|
41
|
+
t_add_fragment(t_fragment_1, t_svg_1, t_before);
|
|
42
|
+
t_next(t_text_1);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
t_run_branch(t_if_range_1, 1, () => {
|
|
47
|
+
const t_fragment_2 = t_fragment($parent.ownerDocument!, t_fragments, 2, ` <circle r="45" cx="50" cy="50" fill="red"></circle> `, true);
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
const t_root_2 = t_root(t_fragment_2);
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
const t_text_2 = t_next(t_next(t_root_2));
|
|
52
|
+
t_add_fragment(t_fragment_2, t_svg_1, t_before);
|
|
53
|
+
t_next(t_text_2);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
t_add_fragment(t_fragment_0, $parent, $anchor);
|
|
59
|
+
t_next(t_svg_1);
|
|
60
|
+
|
|
61
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ServerSlotRender } from "../../../../src/types/ServerSlotRender";
|
|
2
|
+
|
|
3
|
+
export default function Shape(
|
|
4
|
+
$props: { name: string },
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
$context?: Record<PropertyKey, any>,
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
$slots?: Record<string, ServerSlotRender>
|
|
9
|
+
) {
|
|
10
|
+
|
|
11
|
+
/* User interface */
|
|
12
|
+
let $output = "";
|
|
13
|
+
$output += `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" role="img"> <![>`;
|
|
14
|
+
if ($props.name === "rect") {
|
|
15
|
+
$output += ` <rect width="100" height="100" fill="red"></rect> `;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
$output += ` <circle r="45" cx="50" cy="50" fill="red"></circle> `;
|
|
19
|
+
}
|
|
20
|
+
$output += `<!]><!> </svg>`;
|
|
21
|
+
|
|
22
|
+
return $output;
|
|
23
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { queryByRole } from "@testing-library/dom";
|
|
2
|
+
import "@testing-library/jest-dom/vitest";
|
|
3
|
+
import { beforeAll, expect, test } from "vitest";
|
|
4
|
+
import $watch from "../../src/render/$watch";
|
|
5
|
+
import buildOutputFiles from "../buildOutputFiles";
|
|
6
|
+
import hydrateComponent from "../hydrateComponent";
|
|
7
|
+
import importComponent from "../importComponent";
|
|
8
|
+
import mountComponent from "../mountComponent";
|
|
9
|
+
|
|
10
|
+
const componentPath = "./test/svg/components/Shape";
|
|
11
|
+
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
buildOutputFiles(componentPath);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
interface State {
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test("svg -- mounted", async () => {
|
|
21
|
+
const $state = $watch({ name: "rect" });
|
|
22
|
+
|
|
23
|
+
const container = document.createElement("div");
|
|
24
|
+
const component = await importComponent(componentPath, "client");
|
|
25
|
+
mountComponent(container, component, $state);
|
|
26
|
+
|
|
27
|
+
check(container, $state);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("svg -- hydrated", async () => {
|
|
31
|
+
const $state = $watch({ name: "rect" });
|
|
32
|
+
|
|
33
|
+
const container = document.createElement("div");
|
|
34
|
+
const clientComponent = await importComponent(componentPath, "client");
|
|
35
|
+
const serverComponent = await importComponent(componentPath, "server");
|
|
36
|
+
hydrateComponent(container, clientComponent, serverComponent, $state);
|
|
37
|
+
|
|
38
|
+
check(container, $state);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function check(container: HTMLElement, state: State) {
|
|
42
|
+
const img = queryByRole(container, "img");
|
|
43
|
+
expect(img).not.toBeNull();
|
|
44
|
+
|
|
45
|
+
expect(img).toContainHTML('<rect width="100" height="100" fill="red"></rect>');
|
|
46
|
+
|
|
47
|
+
state.name = "circle";
|
|
48
|
+
|
|
49
|
+
expect(img).toContainHTML('<circle r="45" cx="50" cy="50" fill="red"></circle>');
|
|
50
|
+
}
|