@alloy-js/core 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/src/binder.js +33 -2
- package/dist/src/code.js +1 -2
- package/dist/src/components/Block.js +2 -5
- package/dist/src/components/Declaration.js +2 -4
- package/dist/src/components/For.d.ts +1 -1
- package/dist/src/components/For.d.ts.map +1 -1
- package/dist/src/components/For.js +1 -2
- package/dist/src/components/Indent.js +2 -4
- package/dist/src/components/List.js +2 -5
- package/dist/src/components/MemberDeclaration.js +2 -4
- package/dist/src/components/MemberName.js +1 -2
- package/dist/src/components/MemberScope.js +2 -4
- package/dist/src/components/Name.js +1 -2
- package/dist/src/components/Output.js +2 -4
- package/dist/src/components/Prose.js +1 -2
- package/dist/src/components/ReferenceOrContent.d.ts +8 -0
- package/dist/src/components/ReferenceOrContent.d.ts.map +1 -0
- package/dist/src/components/ReferenceOrContent.js +11 -0
- package/dist/src/components/Scope.js +2 -4
- package/dist/src/components/Show.js +1 -2
- package/dist/src/components/SourceDirectory.js +2 -4
- package/dist/src/components/SourceFile.js +2 -5
- package/dist/src/components/StatementList.js +2 -4
- package/dist/src/components/Switch.d.ts +1 -1
- package/dist/src/components/Switch.d.ts.map +1 -1
- package/dist/src/components/Switch.js +1 -2
- package/dist/src/components/Wrap.js +2 -4
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/components/index.js +2 -2
- package/dist/src/components/stc/index.d.ts +1 -0
- package/dist/src/components/stc/index.d.ts.map +1 -1
- package/dist/src/components/stc/index.js +2 -2
- package/dist/src/components/stc/sti.js +1 -2
- package/dist/src/context/assignment.js +1 -2
- package/dist/src/context/binder.js +1 -2
- package/dist/src/context/declaration.js +1 -2
- package/dist/src/context/index.js +1 -2
- package/dist/src/context/member-declaration.js +1 -2
- package/dist/src/context/member-scope.js +1 -2
- package/dist/src/context/name-policy.js +1 -2
- package/dist/src/context/scope.js +1 -2
- package/dist/src/context/source-directory.js +1 -2
- package/dist/src/context/source-file.js +1 -2
- package/dist/src/context.js +1 -2
- package/dist/src/debug.js +13 -15
- package/dist/src/index.browser.js +1 -2
- package/dist/src/index.js +1 -2
- package/dist/src/jsx-runtime.d.ts +1 -1
- package/dist/src/jsx-runtime.d.ts.map +1 -1
- package/dist/src/jsx-runtime.js +1 -2
- package/dist/src/name-policy.js +1 -2
- package/dist/src/refkey.js +1 -2
- package/dist/src/render.js +1 -2
- package/dist/src/slot.js +1 -2
- package/dist/src/stc.js +1 -2
- package/dist/src/sti.js +1 -2
- package/dist/src/tap.js +1 -2
- package/dist/src/tsdoc-metadata.json +1 -1
- package/dist/src/utils.js +2 -4
- package/dist/src/write-output.browser.js +1 -2
- package/dist/src/write-output.js +1 -2
- package/dist/test/browser-build.test.js +85 -0
- package/dist/test/children.test.js +27 -0
- package/dist/test/components/block.test.js +45 -0
- package/dist/test/components/declaration.test.js +30 -0
- package/dist/test/components/list.test.js +86 -0
- package/dist/test/components/prose.test.js +25 -0
- package/dist/test/components/reference-or-content.test.d.ts +2 -0
- package/dist/test/components/reference-or-content.test.d.ts.map +1 -0
- package/dist/test/components/reference-or-content.test.js +149 -0
- package/dist/test/components/slot.test.js +134 -0
- package/dist/test/components/source-file.test.js +64 -0
- package/dist/test/components/wrap.test.js +35 -0
- package/dist/test/control-flow/for.test.js +185 -0
- package/dist/test/control-flow/match.test.js +67 -0
- package/dist/test/control-flow/show.test.js +29 -0
- package/dist/test/name-policy.test.js +19 -0
- package/dist/test/props-with-defaults.test.js +93 -0
- package/dist/test/reactivity/cleanup.test.js +77 -0
- package/dist/test/reactivity/memo.test.js +16 -0
- package/dist/test/reactivity/ref-rendering.test.js +37 -0
- package/dist/test/reactivity/test.test.js +61 -0
- package/dist/test/reactivity/untrack.test.js +23 -0
- package/dist/test/refkey.test.js +25 -0
- package/dist/test/rendering/basic.test.js +96 -0
- package/dist/test/rendering/code.test.js +55 -0
- package/dist/test/rendering/formatting.test.js +402 -0
- package/dist/test/rendering/indent.test.js +90 -0
- package/dist/test/rendering/memoization.test.js +25 -0
- package/dist/test/rendering/refkeys.test.js +32 -0
- package/dist/test/split-props.test.js +77 -0
- package/dist/test/stc.test.js +34 -0
- package/dist/test/symbols.test.js +504 -0
- package/dist/test/utils.test.js +221 -0
- package/dist/testing/extend-expect.js +1 -2
- package/dist/testing/index.js +1 -2
- package/dist/testing/render.js +1 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -22
- package/src/components/For.tsx +2 -2
- package/src/components/ReferenceOrContent.tsx +22 -0
- package/src/components/index.tsx +1 -0
- package/src/components/stc/index.ts +1 -0
- package/src/debug.ts +12 -13
- package/src/jsx-runtime.ts +2 -2
- package/temp/api.json +208 -7
- package/test/components/reference-or-content.test.tsx +138 -0
- package/babel.config.cjs +0 -4
- package/dist/src/binder.js.map +0 -1
- package/dist/src/code.js.map +0 -1
- package/dist/src/components/Block.js.map +0 -1
- package/dist/src/components/Declaration.js.map +0 -1
- package/dist/src/components/For.js.map +0 -1
- package/dist/src/components/Indent.js.map +0 -1
- package/dist/src/components/List.js.map +0 -1
- package/dist/src/components/MemberDeclaration.js.map +0 -1
- package/dist/src/components/MemberName.js.map +0 -1
- package/dist/src/components/MemberScope.js.map +0 -1
- package/dist/src/components/Name.js.map +0 -1
- package/dist/src/components/Output.js.map +0 -1
- package/dist/src/components/Prose.js.map +0 -1
- package/dist/src/components/Scope.js.map +0 -1
- package/dist/src/components/Show.js.map +0 -1
- package/dist/src/components/SourceDirectory.js.map +0 -1
- package/dist/src/components/SourceFile.js.map +0 -1
- package/dist/src/components/StatementList.js.map +0 -1
- package/dist/src/components/Switch.js.map +0 -1
- package/dist/src/components/Wrap.js.map +0 -1
- package/dist/src/components/index.js.map +0 -1
- package/dist/src/components/stc/index.js.map +0 -1
- package/dist/src/components/stc/sti.js.map +0 -1
- package/dist/src/context/assignment.js.map +0 -1
- package/dist/src/context/binder.js.map +0 -1
- package/dist/src/context/declaration.js.map +0 -1
- package/dist/src/context/index.js.map +0 -1
- package/dist/src/context/member-declaration.js.map +0 -1
- package/dist/src/context/member-scope.js.map +0 -1
- package/dist/src/context/name-policy.js.map +0 -1
- package/dist/src/context/scope.js.map +0 -1
- package/dist/src/context/source-directory.js.map +0 -1
- package/dist/src/context/source-file.js.map +0 -1
- package/dist/src/context.js.map +0 -1
- package/dist/src/debug.js.map +0 -1
- package/dist/src/index.browser.js.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/jsx-runtime.js.map +0 -1
- package/dist/src/name-policy.js.map +0 -1
- package/dist/src/refkey.js.map +0 -1
- package/dist/src/render.js.map +0 -1
- package/dist/src/slot.js.map +0 -1
- package/dist/src/stc.js.map +0 -1
- package/dist/src/sti.js.map +0 -1
- package/dist/src/tap.js.map +0 -1
- package/dist/src/utils.js.map +0 -1
- package/dist/src/write-output.browser.js.map +0 -1
- package/dist/src/write-output.js.map +0 -1
- package/dist/testing/extend-expect.js.map +0 -1
- package/dist/testing/index.js.map +0 -1
- package/dist/testing/render.js.map +0 -1
- package/dist/testing/vitest.d.js.map +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { memo as _$memo, createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { it } from "vitest";
|
|
3
|
+
import { Output } from "../../src/components/Output.js";
|
|
4
|
+
import { SourceFile } from "../../src/components/SourceFile.js";
|
|
5
|
+
import { Declaration, Name, refkey, Scope, useBinder } from "../../src/index.js";
|
|
6
|
+
import { render } from "../../src/render.js";
|
|
7
|
+
import { defineSlot, rename, replace } from "../../src/slot.js";
|
|
8
|
+
import "../../testing/extend-expect.js";
|
|
9
|
+
it("works with string keys", () => {
|
|
10
|
+
const FunctionSlot = defineSlot(query => query.name);
|
|
11
|
+
function MyFunctionComponent(props) {
|
|
12
|
+
const FunctionSlotInstance = FunctionSlot.create(props.name, {
|
|
13
|
+
...props,
|
|
14
|
+
additionalProp: "hi"
|
|
15
|
+
}, ["function ", _$memo(() => props.name), "() ", "{", "console.log(\"hello world\");", "}"]);
|
|
16
|
+
return _$createComponent(FunctionSlotInstance, {});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// extension.tsx
|
|
20
|
+
replace(FunctionSlot.find({
|
|
21
|
+
name: "foo"
|
|
22
|
+
}), props => {
|
|
23
|
+
return ["// original", _$memo(() => props.original)];
|
|
24
|
+
});
|
|
25
|
+
const tree = render(_$createComponent(Output, {
|
|
26
|
+
get children() {
|
|
27
|
+
return _$createComponent(SourceFile, {
|
|
28
|
+
path: "test.ts",
|
|
29
|
+
filetype: "ts",
|
|
30
|
+
get children() {
|
|
31
|
+
return _$createComponent(MyFunctionComponent, {
|
|
32
|
+
name: "foo"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}));
|
|
38
|
+
console.log(tree.contents[0].contents);
|
|
39
|
+
});
|
|
40
|
+
it("works with symbols", () => {
|
|
41
|
+
const FunctionSlot = defineSlot(query => {
|
|
42
|
+
const binder = useBinder();
|
|
43
|
+
return binder.resolveFQN(query.fqn);
|
|
44
|
+
});
|
|
45
|
+
function MyFunctionComponent(props) {
|
|
46
|
+
const binder = useBinder();
|
|
47
|
+
const sym = binder.createSymbol({
|
|
48
|
+
name: props.name,
|
|
49
|
+
refkey: refkey()
|
|
50
|
+
});
|
|
51
|
+
const FunctionSlotInstance = FunctionSlot.create(sym, {
|
|
52
|
+
...props,
|
|
53
|
+
additionalProp: "hi"
|
|
54
|
+
}, _$createComponent(Declaration, {
|
|
55
|
+
symbol: sym,
|
|
56
|
+
get children() {
|
|
57
|
+
return ["function ", _$createComponent(Name, {}), "() ", "{", "console.log(\"hello world\");", "}"];
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
60
|
+
return _$createComponent(FunctionSlotInstance, {});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// extension.tsx
|
|
64
|
+
replace(FunctionSlot.find({
|
|
65
|
+
fqn: "foo.bar"
|
|
66
|
+
}), props => {
|
|
67
|
+
return ["// original", _$memo(() => props.original)];
|
|
68
|
+
});
|
|
69
|
+
const tree = render(_$createComponent(Output, {
|
|
70
|
+
get children() {
|
|
71
|
+
return _$createComponent(SourceFile, {
|
|
72
|
+
path: "test.ts",
|
|
73
|
+
filetype: "ts",
|
|
74
|
+
get children() {
|
|
75
|
+
return _$createComponent(Scope, {
|
|
76
|
+
name: "foo",
|
|
77
|
+
get children() {
|
|
78
|
+
return _$createComponent(MyFunctionComponent, {
|
|
79
|
+
name: "bar"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}));
|
|
87
|
+
console.log(tree.contents[0].contents);
|
|
88
|
+
});
|
|
89
|
+
it("can rename", () => {
|
|
90
|
+
const FunctionSlot = defineSlot(query => {
|
|
91
|
+
const binder = useBinder();
|
|
92
|
+
return binder.resolveFQN(query.fqn);
|
|
93
|
+
});
|
|
94
|
+
function MyFunctionComponent(props) {
|
|
95
|
+
const binder = useBinder();
|
|
96
|
+
const sym = binder.createSymbol({
|
|
97
|
+
name: props.name,
|
|
98
|
+
refkey: refkey()
|
|
99
|
+
});
|
|
100
|
+
const FunctionSlotInstance = FunctionSlot.create(sym, {
|
|
101
|
+
...props,
|
|
102
|
+
additionalProp: "hi"
|
|
103
|
+
}, _$createComponent(Declaration, {
|
|
104
|
+
symbol: sym,
|
|
105
|
+
get children() {
|
|
106
|
+
return ["function ", _$createComponent(Name, {}), "() ", "{", "console.log(\"hello world\");", "}"];
|
|
107
|
+
}
|
|
108
|
+
}));
|
|
109
|
+
return _$createComponent(FunctionSlotInstance, {});
|
|
110
|
+
}
|
|
111
|
+
rename(() => {
|
|
112
|
+
const binder = useBinder();
|
|
113
|
+
return binder.resolveFQN("foo.bar");
|
|
114
|
+
}, "bazxxx");
|
|
115
|
+
const tree = render(_$createComponent(Output, {
|
|
116
|
+
get children() {
|
|
117
|
+
return _$createComponent(SourceFile, {
|
|
118
|
+
path: "test.ts",
|
|
119
|
+
filetype: "ts",
|
|
120
|
+
get children() {
|
|
121
|
+
return _$createComponent(Scope, {
|
|
122
|
+
name: "foo",
|
|
123
|
+
get children() {
|
|
124
|
+
return _$createComponent(MyFunctionComponent, {
|
|
125
|
+
name: "bar"
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}));
|
|
133
|
+
console.log(tree.contents[0].contents);
|
|
134
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createComponent as _$createComponent, memo as _$memo } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { computed, Output, render, renderTree, SourceFile, useContext } from "@alloy-js/core";
|
|
3
|
+
import { expect, it } from "vitest";
|
|
4
|
+
import { SourceDirectoryContext } from "../../src/context/source-directory.js";
|
|
5
|
+
import "../../testing/extend-expect.js";
|
|
6
|
+
import { d } from "../../testing/render.js";
|
|
7
|
+
it("tracks its content", () => {
|
|
8
|
+
let context;
|
|
9
|
+
function Test() {
|
|
10
|
+
context = useContext(SourceDirectoryContext);
|
|
11
|
+
}
|
|
12
|
+
const _ = renderTree(_$createComponent(Output, {
|
|
13
|
+
get children() {
|
|
14
|
+
return [_$createComponent(Test, {}), _$createComponent(SourceFile, {
|
|
15
|
+
path: "hi.txt",
|
|
16
|
+
filetype: "text",
|
|
17
|
+
children: "hello!"
|
|
18
|
+
})];
|
|
19
|
+
}
|
|
20
|
+
}));
|
|
21
|
+
expect(context.contents.length).toEqual(1);
|
|
22
|
+
});
|
|
23
|
+
it("has reactive context", () => {
|
|
24
|
+
function TrackContents() {
|
|
25
|
+
const sdContext = useContext(SourceDirectoryContext);
|
|
26
|
+
const allFiles = computed(() => {
|
|
27
|
+
return sdContext.contents.map(v => v.path).join(" ");
|
|
28
|
+
});
|
|
29
|
+
return _$createComponent(SourceFile, {
|
|
30
|
+
path: "contents.txt",
|
|
31
|
+
filetype: "text",
|
|
32
|
+
get children() {
|
|
33
|
+
return allFiles.value;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const tree = render(_$createComponent(Output, {
|
|
38
|
+
get children() {
|
|
39
|
+
return [_$createComponent(SourceFile, {
|
|
40
|
+
path: "hi.txt",
|
|
41
|
+
filetype: "text",
|
|
42
|
+
children: "hello!"
|
|
43
|
+
}), _$createComponent(TrackContents, {})];
|
|
44
|
+
}
|
|
45
|
+
}));
|
|
46
|
+
expect(tree.contents[1].contents).toEqual("hi.txt contents.txt");
|
|
47
|
+
});
|
|
48
|
+
it("Includes header", () => {
|
|
49
|
+
const header = ["# This is a header"];
|
|
50
|
+
const tree = render(_$createComponent(Output, {
|
|
51
|
+
get children() {
|
|
52
|
+
return _$createComponent(SourceFile, {
|
|
53
|
+
path: "hi.txt",
|
|
54
|
+
filetype: "text",
|
|
55
|
+
header: header,
|
|
56
|
+
children: "hello!"
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
60
|
+
expect(tree.contents[0].contents).toEqual(d`
|
|
61
|
+
# This is a header
|
|
62
|
+
hello!
|
|
63
|
+
`);
|
|
64
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { memo as _$memo, createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { ref } from "@vue/reactivity";
|
|
3
|
+
import { expect, it } from "vitest";
|
|
4
|
+
import { Wrap } from "../../src/components/Wrap.js";
|
|
5
|
+
import { printTree, renderTree } from "../../src/render.js";
|
|
6
|
+
import "../../testing/extend-expect.js";
|
|
7
|
+
function Wrapper(props) {
|
|
8
|
+
return ["[", _$memo(() => props.children), "]"];
|
|
9
|
+
}
|
|
10
|
+
it("conditionally wraps", () => {
|
|
11
|
+
const template = [_$createComponent(Wrap, {
|
|
12
|
+
when: true,
|
|
13
|
+
"with": Wrapper,
|
|
14
|
+
children: "testing"
|
|
15
|
+
}), _$createComponent(Wrap, {
|
|
16
|
+
when: false,
|
|
17
|
+
"with": Wrapper,
|
|
18
|
+
children: "testing"
|
|
19
|
+
})];
|
|
20
|
+
expect(template).toRenderTo(`[testing]testing`);
|
|
21
|
+
});
|
|
22
|
+
it("works reactively", () => {
|
|
23
|
+
const doWrap = ref(false);
|
|
24
|
+
const template = [_$createComponent(Wrap, {
|
|
25
|
+
get when() {
|
|
26
|
+
return doWrap.value;
|
|
27
|
+
},
|
|
28
|
+
"with": Wrapper,
|
|
29
|
+
children: "testing"
|
|
30
|
+
})];
|
|
31
|
+
const tree = renderTree(template);
|
|
32
|
+
expect(printTree(tree)).toEqual(`testing`);
|
|
33
|
+
doWrap.value = true;
|
|
34
|
+
expect(printTree(tree)).toEqual(`testing`);
|
|
35
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { createComponent as _$createComponent, memo as _$memo } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import "@alloy-js/core/testing";
|
|
3
|
+
import { d } from "@alloy-js/core/testing";
|
|
4
|
+
import { expect, it } from "vitest";
|
|
5
|
+
import { For } from "../../src/components/For.js";
|
|
6
|
+
import { onCleanup, printTree, reactive, renderTree } from "../../src/index.js";
|
|
7
|
+
it("works", () => {
|
|
8
|
+
const messages = ["hi", "bye"];
|
|
9
|
+
const template = _$createComponent(For, {
|
|
10
|
+
each: messages,
|
|
11
|
+
children: message => [message, ", Jose!"]
|
|
12
|
+
});
|
|
13
|
+
expect(template).toRenderTo(`
|
|
14
|
+
hi, Jose!
|
|
15
|
+
bye, Jose!
|
|
16
|
+
`);
|
|
17
|
+
});
|
|
18
|
+
it("handles map entries", () => {
|
|
19
|
+
const map = new Map([["a", {
|
|
20
|
+
name: "foo"
|
|
21
|
+
}]]);
|
|
22
|
+
const entries = Array.from(map.entries());
|
|
23
|
+
const template = _$createComponent(For, {
|
|
24
|
+
each: entries,
|
|
25
|
+
children: ([key, value]) => [key, ": ", _$memo(() => value.name)]
|
|
26
|
+
});
|
|
27
|
+
expect(template).toRenderTo(`
|
|
28
|
+
a: foo
|
|
29
|
+
`);
|
|
30
|
+
});
|
|
31
|
+
it("handles iterators", () => {
|
|
32
|
+
const iterator = new Map([["a", {
|
|
33
|
+
name: "foo"
|
|
34
|
+
}]]).entries();
|
|
35
|
+
const template = _$createComponent(For, {
|
|
36
|
+
each: iterator,
|
|
37
|
+
children: ([key, value]) => [key, ": ", _$memo(() => value.name)]
|
|
38
|
+
});
|
|
39
|
+
expect(template).toRenderTo(`
|
|
40
|
+
a: foo
|
|
41
|
+
`);
|
|
42
|
+
});
|
|
43
|
+
it("handles maps", () => {
|
|
44
|
+
const map = new Map([["a", {
|
|
45
|
+
name: "foo"
|
|
46
|
+
}]]);
|
|
47
|
+
const template = _$createComponent(For, {
|
|
48
|
+
each: map,
|
|
49
|
+
children: (key, value) => [key, ": ", _$memo(() => value.name)]
|
|
50
|
+
});
|
|
51
|
+
expect(template).toRenderTo(`
|
|
52
|
+
a: foo
|
|
53
|
+
`);
|
|
54
|
+
});
|
|
55
|
+
it("doesn't rerender mappers", () => {
|
|
56
|
+
const messages = reactive(["hi", "bye"]);
|
|
57
|
+
let count = 0;
|
|
58
|
+
const template = _$createComponent(For, {
|
|
59
|
+
each: messages,
|
|
60
|
+
children: () => ["item ", count++]
|
|
61
|
+
});
|
|
62
|
+
const tree = renderTree(template);
|
|
63
|
+
expect(count).toBe(2);
|
|
64
|
+
messages.push("maybe");
|
|
65
|
+
expect(count).toBe(3);
|
|
66
|
+
expect(printTree(tree)).toBe(d`
|
|
67
|
+
item 0
|
|
68
|
+
item 1
|
|
69
|
+
item 2
|
|
70
|
+
`);
|
|
71
|
+
});
|
|
72
|
+
it("doesn't rerender mappers with sets", () => {
|
|
73
|
+
const messages = reactive(new Set(["hi", "bye"]));
|
|
74
|
+
let count = 0;
|
|
75
|
+
const template = _$createComponent(For, {
|
|
76
|
+
each: messages,
|
|
77
|
+
children: () => ["item ", count++]
|
|
78
|
+
});
|
|
79
|
+
const tree = renderTree(template);
|
|
80
|
+
expect(count).toBe(2);
|
|
81
|
+
messages.add("maybe");
|
|
82
|
+
expect(count).toBe(3);
|
|
83
|
+
expect(printTree(tree)).toBe(d`
|
|
84
|
+
item 0
|
|
85
|
+
item 1
|
|
86
|
+
item 2
|
|
87
|
+
`);
|
|
88
|
+
});
|
|
89
|
+
it("doesn't rerender mappers with maps", () => {
|
|
90
|
+
const messages = reactive(new Map([["hi", "one"], ["bye", "two"]]));
|
|
91
|
+
let count = 0;
|
|
92
|
+
const template = _$createComponent(For, {
|
|
93
|
+
each: messages,
|
|
94
|
+
children: () => ["item ", count++]
|
|
95
|
+
});
|
|
96
|
+
const tree = renderTree(template);
|
|
97
|
+
expect(count).toBe(2);
|
|
98
|
+
messages.set("maybe", "three");
|
|
99
|
+
expect(count).toBe(3);
|
|
100
|
+
expect(printTree(tree)).toBe(d`
|
|
101
|
+
item 0
|
|
102
|
+
item 1
|
|
103
|
+
item 2
|
|
104
|
+
`);
|
|
105
|
+
});
|
|
106
|
+
it("doesn't rerender mappers (with splice)", () => {
|
|
107
|
+
const messages = reactive(["hi", "maybe", "bye"]);
|
|
108
|
+
let count = 0;
|
|
109
|
+
const template = _$createComponent(For, {
|
|
110
|
+
each: messages,
|
|
111
|
+
children: msg => ["item ", count++]
|
|
112
|
+
});
|
|
113
|
+
const tree = renderTree(template);
|
|
114
|
+
expect(count).toBe(3);
|
|
115
|
+
messages.splice(1, 1);
|
|
116
|
+
// A sufficiently smart mapJoin would be able to handle this case...
|
|
117
|
+
// but for now we re-render everything after the splice point.
|
|
118
|
+
expect(count).toBe(4);
|
|
119
|
+
expect(printTree(tree)).toBe(d`
|
|
120
|
+
item 0
|
|
121
|
+
item 3
|
|
122
|
+
`);
|
|
123
|
+
});
|
|
124
|
+
it("cleans up things which end up removed (with push)", () => {
|
|
125
|
+
const cleanups = [];
|
|
126
|
+
function Letter(props) {
|
|
127
|
+
onCleanup(() => {
|
|
128
|
+
cleanups.push(props.letter);
|
|
129
|
+
});
|
|
130
|
+
return ["Letter ", _$memo(() => props.letter)];
|
|
131
|
+
}
|
|
132
|
+
const items = reactive(["a", "b"]);
|
|
133
|
+
const template = _$createComponent(For, {
|
|
134
|
+
each: items,
|
|
135
|
+
children: item => _$createComponent(Letter, {
|
|
136
|
+
letter: item
|
|
137
|
+
})
|
|
138
|
+
});
|
|
139
|
+
const tree = renderTree(template);
|
|
140
|
+
expect(cleanups).toEqual([]);
|
|
141
|
+
expect(printTree(tree)).toBe(d`
|
|
142
|
+
Letter a
|
|
143
|
+
Letter b
|
|
144
|
+
`);
|
|
145
|
+
items.pop();
|
|
146
|
+
expect(cleanups).toEqual(["b"]);
|
|
147
|
+
expect(printTree(tree)).toBe(d`
|
|
148
|
+
Letter a
|
|
149
|
+
`);
|
|
150
|
+
items.pop();
|
|
151
|
+
expect(cleanups).toEqual(["b", "a"]);
|
|
152
|
+
expect(printTree(tree)).toBe("");
|
|
153
|
+
});
|
|
154
|
+
it("cleans up things which end up removed (with splice)", () => {
|
|
155
|
+
const cleanups = [];
|
|
156
|
+
function Letter(props) {
|
|
157
|
+
onCleanup(() => {
|
|
158
|
+
cleanups.push(props.letter);
|
|
159
|
+
});
|
|
160
|
+
return ["Letter ", _$memo(() => props.letter)];
|
|
161
|
+
}
|
|
162
|
+
const items = reactive(["a", "b", "c"]);
|
|
163
|
+
const template = _$createComponent(For, {
|
|
164
|
+
each: items,
|
|
165
|
+
children: item => _$createComponent(Letter, {
|
|
166
|
+
letter: item
|
|
167
|
+
})
|
|
168
|
+
});
|
|
169
|
+
const tree = renderTree(template);
|
|
170
|
+
expect(cleanups).toEqual([]);
|
|
171
|
+
expect(printTree(tree)).toBe(d`
|
|
172
|
+
Letter a
|
|
173
|
+
Letter b
|
|
174
|
+
Letter c
|
|
175
|
+
`);
|
|
176
|
+
items.splice(1, 1);
|
|
177
|
+
|
|
178
|
+
// A sufficiently smart mapJoin would be able to handle this case...
|
|
179
|
+
// but for now we re-render everything after the splice point.
|
|
180
|
+
expect(cleanups).toEqual(["b", "c"]);
|
|
181
|
+
expect(printTree(tree)).toBe(d`
|
|
182
|
+
Letter a
|
|
183
|
+
Letter c
|
|
184
|
+
`);
|
|
185
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import "@alloy-js/core/testing";
|
|
3
|
+
import { ref } from "@vue/reactivity";
|
|
4
|
+
import { expect, it } from "vitest";
|
|
5
|
+
import { Match, Switch } from "../../src/components/Switch.js";
|
|
6
|
+
import { printTree, renderTree } from "../../src/render.js";
|
|
7
|
+
it("selects the true branch", () => {
|
|
8
|
+
const template = _$createComponent(Switch, {
|
|
9
|
+
get children() {
|
|
10
|
+
return [_$createComponent(Match, {
|
|
11
|
+
when: true,
|
|
12
|
+
children: "true"
|
|
13
|
+
}), _$createComponent(Match, {
|
|
14
|
+
"else": true,
|
|
15
|
+
children: "false"
|
|
16
|
+
})];
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
expect(template).toRenderTo(`true`);
|
|
20
|
+
});
|
|
21
|
+
it("selects the else branch", () => {
|
|
22
|
+
const template = _$createComponent(Switch, {
|
|
23
|
+
get children() {
|
|
24
|
+
return [_$createComponent(Match, {
|
|
25
|
+
when: false,
|
|
26
|
+
children: "true"
|
|
27
|
+
}), _$createComponent(Match, {
|
|
28
|
+
"else": true,
|
|
29
|
+
children: "false"
|
|
30
|
+
})];
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
expect(template).toRenderTo(`false`);
|
|
34
|
+
});
|
|
35
|
+
it("renders to nothing when no branch matches", () => {
|
|
36
|
+
const template = _$createComponent(Switch, {
|
|
37
|
+
get children() {
|
|
38
|
+
return _$createComponent(Match, {
|
|
39
|
+
when: false,
|
|
40
|
+
children: "true"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
expect(template).toRenderTo(``);
|
|
45
|
+
});
|
|
46
|
+
it("works with reactivity", () => {
|
|
47
|
+
const count = ref(0);
|
|
48
|
+
const template = _$createComponent(Switch, {
|
|
49
|
+
get children() {
|
|
50
|
+
return [_$createComponent(Match, {
|
|
51
|
+
get when() {
|
|
52
|
+
return count.value % 2 === 0;
|
|
53
|
+
},
|
|
54
|
+
children: "even"
|
|
55
|
+
}), _$createComponent(Match, {
|
|
56
|
+
"else": true,
|
|
57
|
+
children: "odd"
|
|
58
|
+
})];
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const tree = renderTree(template);
|
|
62
|
+
expect(printTree(tree)).toBe(`even`);
|
|
63
|
+
count.value++;
|
|
64
|
+
expect(printTree(tree)).toBe(`odd`);
|
|
65
|
+
count.value++;
|
|
66
|
+
expect(printTree(tree)).toBe(`even`);
|
|
67
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import "@alloy-js/core/testing";
|
|
3
|
+
import { ref } from "@vue/reactivity";
|
|
4
|
+
import { expect, it } from "vitest";
|
|
5
|
+
import { Show } from "../../src/components/Show.js";
|
|
6
|
+
import { printTree, renderTree } from "../../src/render.js";
|
|
7
|
+
it("selects the true branch", () => {
|
|
8
|
+
const template = _$createComponent(Show, {
|
|
9
|
+
when: true,
|
|
10
|
+
children: "true"
|
|
11
|
+
});
|
|
12
|
+
expect(template).toRenderTo(`true`);
|
|
13
|
+
});
|
|
14
|
+
it("works with reactivity", () => {
|
|
15
|
+
const count = ref(0);
|
|
16
|
+
const template = _$createComponent(Show, {
|
|
17
|
+
get when() {
|
|
18
|
+
return count.value % 2 === 0;
|
|
19
|
+
},
|
|
20
|
+
fallback: "odd",
|
|
21
|
+
children: "even"
|
|
22
|
+
});
|
|
23
|
+
const tree = renderTree(template);
|
|
24
|
+
expect(printTree(tree)).toBe(`even`);
|
|
25
|
+
count.value++;
|
|
26
|
+
expect(printTree(tree)).toBe(`odd`);
|
|
27
|
+
count.value++;
|
|
28
|
+
expect(printTree(tree)).toBe(`even`);
|
|
29
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { createNamePolicy, Output, useNamePolicy } from "@alloy-js/core";
|
|
3
|
+
import { expect, it } from "vitest";
|
|
4
|
+
import "../testing/extend-expect.js";
|
|
5
|
+
it("is applied by output", () => {
|
|
6
|
+
const policy = createNamePolicy(name => {
|
|
7
|
+
return "name" + name;
|
|
8
|
+
});
|
|
9
|
+
function Foo() {
|
|
10
|
+
const namer = useNamePolicy();
|
|
11
|
+
return namer.getName("hi", "name");
|
|
12
|
+
}
|
|
13
|
+
expect(_$createComponent(Output, {
|
|
14
|
+
namePolicy: policy,
|
|
15
|
+
get children() {
|
|
16
|
+
return _$createComponent(Foo, {});
|
|
17
|
+
}
|
|
18
|
+
})).toRenderTo("namehi");
|
|
19
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { effect, reactive } from "@vue/reactivity";
|
|
2
|
+
import { expect, it, vi } from "vitest";
|
|
3
|
+
import { defaultProps } from "../src/jsx-runtime.js";
|
|
4
|
+
it("applies defaults to regular object props", () => {
|
|
5
|
+
const props = {
|
|
6
|
+
a: 1,
|
|
7
|
+
b: 2,
|
|
8
|
+
c: undefined
|
|
9
|
+
};
|
|
10
|
+
const dProps = defaultProps(props, {
|
|
11
|
+
a: 10,
|
|
12
|
+
c: 20
|
|
13
|
+
});
|
|
14
|
+
expect(dProps).toEqual({
|
|
15
|
+
a: 1,
|
|
16
|
+
b: 2,
|
|
17
|
+
c: 20
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
it("applies defaults with getters without invoking them", () => {
|
|
21
|
+
const getterA = vi.fn(() => 1);
|
|
22
|
+
const getterB = vi.fn(() => undefined);
|
|
23
|
+
const props = {
|
|
24
|
+
get a() {
|
|
25
|
+
return getterA();
|
|
26
|
+
},
|
|
27
|
+
get b() {
|
|
28
|
+
return getterB();
|
|
29
|
+
},
|
|
30
|
+
c: 3
|
|
31
|
+
};
|
|
32
|
+
const dProps = defaultProps(props, {
|
|
33
|
+
b: 20,
|
|
34
|
+
c: 30
|
|
35
|
+
});
|
|
36
|
+
expect(getterA).not.toHaveBeenCalled();
|
|
37
|
+
expect(getterB).not.toHaveBeenCalled();
|
|
38
|
+
const value = dProps.b;
|
|
39
|
+
expect(getterB).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(value).toEqual(20);
|
|
41
|
+
});
|
|
42
|
+
it("applies defaults to reactives without observing them initially", () => {
|
|
43
|
+
const props = reactive({
|
|
44
|
+
a: 1,
|
|
45
|
+
b: 2,
|
|
46
|
+
c: undefined
|
|
47
|
+
});
|
|
48
|
+
const defaults = defaultProps(props, {
|
|
49
|
+
c: 10
|
|
50
|
+
});
|
|
51
|
+
expect(defaults.a).toBe(1);
|
|
52
|
+
expect(defaults.b).toBe(2);
|
|
53
|
+
expect(defaults.c).toBe(10);
|
|
54
|
+
});
|
|
55
|
+
it("ensures effect is not triggered by defaults but by accessing reactive props", () => {
|
|
56
|
+
const props = reactive({
|
|
57
|
+
a: 1,
|
|
58
|
+
b: 2,
|
|
59
|
+
c: undefined
|
|
60
|
+
});
|
|
61
|
+
let withDefaults;
|
|
62
|
+
const splitEffect = vi.fn(() => {
|
|
63
|
+
withDefaults = defaultProps(props, {
|
|
64
|
+
c: 10
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
effect(splitEffect);
|
|
68
|
+
expect(splitEffect).toHaveBeenCalledTimes(1);
|
|
69
|
+
const observeEffect = vi.fn(() => {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
71
|
+
withDefaults.a;
|
|
72
|
+
});
|
|
73
|
+
effect(observeEffect);
|
|
74
|
+
expect(observeEffect).toHaveBeenCalledTimes(1);
|
|
75
|
+
props.a = 2;
|
|
76
|
+
expect(splitEffect).toHaveBeenCalledTimes(1);
|
|
77
|
+
expect(observeEffect).toHaveBeenCalledTimes(2);
|
|
78
|
+
});
|
|
79
|
+
it("applies defaults to reactives", () => {
|
|
80
|
+
const props = reactive({
|
|
81
|
+
a: 1,
|
|
82
|
+
b: 2,
|
|
83
|
+
c: undefined
|
|
84
|
+
});
|
|
85
|
+
const withDefaults = defaultProps(props, {
|
|
86
|
+
c: 10
|
|
87
|
+
});
|
|
88
|
+
expect(withDefaults.c).toBe(10);
|
|
89
|
+
props.c = 20;
|
|
90
|
+
expect(withDefaults.c).toBe(20);
|
|
91
|
+
props.c = undefined;
|
|
92
|
+
expect(withDefaults.c).toBe(10);
|
|
93
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { effect, memo, onCleanup, createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { ref } from "@vue/reactivity";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { renderTree } from "../../src/render.js";
|
|
5
|
+
describe("memo cleanup", () => {
|
|
6
|
+
it("cleans up when memo value is recomputed", () => {
|
|
7
|
+
const r = ref(1);
|
|
8
|
+
let callCount = 0;
|
|
9
|
+
const m = memo(() => {
|
|
10
|
+
onCleanup(() => {
|
|
11
|
+
callCount++;
|
|
12
|
+
});
|
|
13
|
+
return r.value;
|
|
14
|
+
});
|
|
15
|
+
expect(m()).toBe(1);
|
|
16
|
+
expect(callCount).toBe(0);
|
|
17
|
+
r.value = 2;
|
|
18
|
+
expect(m()).toBe(2);
|
|
19
|
+
expect(callCount).toBe(1);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe("effect cleanup", () => {
|
|
23
|
+
it("cleans up when the effect is run", () => {
|
|
24
|
+
const r = ref(1);
|
|
25
|
+
let cleanedUp = false;
|
|
26
|
+
effect(() => {
|
|
27
|
+
onCleanup(() => {
|
|
28
|
+
cleanedUp = true;
|
|
29
|
+
});
|
|
30
|
+
return r.value;
|
|
31
|
+
});
|
|
32
|
+
expect(cleanedUp).toBe(false);
|
|
33
|
+
r.value = 2;
|
|
34
|
+
expect(cleanedUp).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("element cleanup", () => {
|
|
38
|
+
it("should clean up when the element is unmounted", () => {
|
|
39
|
+
let cleanedUp = false;
|
|
40
|
+
function Component() {
|
|
41
|
+
onCleanup(() => {
|
|
42
|
+
cleanedUp = true;
|
|
43
|
+
});
|
|
44
|
+
return "hi!";
|
|
45
|
+
}
|
|
46
|
+
const el = ref(_$createComponent(Component, {}));
|
|
47
|
+
const template = [el];
|
|
48
|
+
renderTree(template);
|
|
49
|
+
el.value = "";
|
|
50
|
+
expect(cleanedUp).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
it("should clean up when the element is unmounted, recursively", () => {
|
|
53
|
+
let cleanedUpC1 = false;
|
|
54
|
+
let cleanedUpC2 = false;
|
|
55
|
+
function C1(props) {
|
|
56
|
+
onCleanup(() => {
|
|
57
|
+
cleanedUpC1 = true;
|
|
58
|
+
});
|
|
59
|
+
return props.children;
|
|
60
|
+
}
|
|
61
|
+
function C2() {
|
|
62
|
+
onCleanup(() => {
|
|
63
|
+
cleanedUpC2 = true;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const el = ref(_$createComponent(C1, {
|
|
67
|
+
get children() {
|
|
68
|
+
return _$createComponent(C2, {});
|
|
69
|
+
}
|
|
70
|
+
}));
|
|
71
|
+
const template = [el];
|
|
72
|
+
renderTree(template);
|
|
73
|
+
el.value = "";
|
|
74
|
+
expect(cleanedUpC1).toBe(true);
|
|
75
|
+
expect(cleanedUpC2).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
});
|