@alloy-js/core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/babel.config.cjs +4 -1
  3. package/dist/src/binder.d.ts +5 -0
  4. package/dist/src/binder.d.ts.map +1 -1
  5. package/dist/src/binder.js +14 -0
  6. package/dist/src/binder.js.map +1 -1
  7. package/dist/src/code.d.ts +2 -2
  8. package/dist/src/code.d.ts.map +1 -1
  9. package/dist/src/code.js +4 -4
  10. package/dist/src/code.js.map +1 -1
  11. package/dist/src/components/Block.d.ts +25 -0
  12. package/dist/src/components/Block.d.ts.map +1 -0
  13. package/dist/src/components/Block.js +25 -0
  14. package/dist/src/components/Block.js.map +1 -0
  15. package/dist/src/components/Declaration.d.ts.map +1 -1
  16. package/dist/src/components/Declaration.js +4 -0
  17. package/dist/src/components/Declaration.js.map +1 -1
  18. package/dist/src/components/For.d.ts +44 -0
  19. package/dist/src/components/For.d.ts.map +1 -0
  20. package/dist/src/components/For.js +41 -0
  21. package/dist/src/components/For.js.map +1 -0
  22. package/dist/src/components/Indent.d.ts +5 -9
  23. package/dist/src/components/Indent.d.ts.map +1 -1
  24. package/dist/src/components/Indent.js +7 -18
  25. package/dist/src/components/Indent.js.map +1 -1
  26. package/dist/src/components/List.d.ts +38 -0
  27. package/dist/src/components/List.d.ts.map +1 -0
  28. package/dist/src/components/List.js +40 -0
  29. package/dist/src/components/List.js.map +1 -0
  30. package/dist/src/components/MemberDeclaration.d.ts.map +1 -1
  31. package/dist/src/components/MemberDeclaration.js.map +1 -1
  32. package/dist/src/components/MemberName.js +1 -1
  33. package/dist/src/components/MemberName.js.map +1 -1
  34. package/dist/src/components/MemberScope.d.ts.map +1 -1
  35. package/dist/src/components/MemberScope.js.map +1 -1
  36. package/dist/src/components/Name.js +1 -1
  37. package/dist/src/components/Name.js.map +1 -1
  38. package/dist/src/components/Output.d.ts +2 -1
  39. package/dist/src/components/Output.d.ts.map +1 -1
  40. package/dist/src/components/Output.js +9 -1
  41. package/dist/src/components/Output.js.map +1 -1
  42. package/dist/src/components/Scope.d.ts.map +1 -1
  43. package/dist/src/components/Scope.js.map +1 -1
  44. package/dist/src/components/Show.d.ts +8 -0
  45. package/dist/src/components/Show.d.ts.map +1 -0
  46. package/dist/src/components/Show.js +4 -0
  47. package/dist/src/components/Show.js.map +1 -0
  48. package/dist/src/components/SourceDirectory.d.ts.map +1 -1
  49. package/dist/src/components/SourceDirectory.js +1 -0
  50. package/dist/src/components/SourceDirectory.js.map +1 -1
  51. package/dist/src/components/SourceFile.d.ts +2 -6
  52. package/dist/src/components/SourceFile.d.ts.map +1 -1
  53. package/dist/src/components/SourceFile.js +6 -13
  54. package/dist/src/components/SourceFile.js.map +1 -1
  55. package/dist/src/components/StatementList.d.ts +9 -0
  56. package/dist/src/components/StatementList.d.ts.map +1 -0
  57. package/dist/src/components/StatementList.js +17 -0
  58. package/dist/src/components/StatementList.js.map +1 -0
  59. package/dist/src/components/Switch.d.ts +41 -0
  60. package/dist/src/components/Switch.d.ts.map +1 -0
  61. package/dist/src/components/Switch.js +41 -0
  62. package/dist/src/components/Switch.js.map +1 -0
  63. package/dist/src/components/Wrap.d.ts +20 -0
  64. package/dist/src/components/Wrap.d.ts.map +1 -0
  65. package/dist/src/components/Wrap.js +15 -0
  66. package/dist/src/components/Wrap.js.map +1 -0
  67. package/dist/src/components/index.d.ts +8 -1
  68. package/dist/src/components/index.d.ts.map +1 -1
  69. package/dist/src/components/index.js +7 -0
  70. package/dist/src/components/index.js.map +1 -1
  71. package/dist/src/components/stc/index.d.ts +77 -6
  72. package/dist/src/components/stc/index.d.ts.map +1 -1
  73. package/dist/src/components/stc/index.js +17 -1
  74. package/dist/src/components/stc/index.js.map +1 -1
  75. package/dist/src/context/index.d.ts +0 -1
  76. package/dist/src/context/index.d.ts.map +1 -1
  77. package/dist/src/context/index.js +0 -1
  78. package/dist/src/context/index.js.map +1 -1
  79. package/dist/src/context.d.ts.map +1 -1
  80. package/dist/src/context.js +3 -3
  81. package/dist/src/context.js.map +1 -1
  82. package/dist/src/index.browser.d.ts +3 -0
  83. package/dist/src/index.browser.d.ts.map +1 -0
  84. package/dist/src/index.browser.js +3 -0
  85. package/dist/src/index.browser.js.map +1 -0
  86. package/dist/src/index.d.ts +1 -0
  87. package/dist/src/index.d.ts.map +1 -1
  88. package/dist/src/index.js +1 -0
  89. package/dist/src/index.js.map +1 -1
  90. package/dist/src/jsx-runtime.d.ts +133 -8
  91. package/dist/src/jsx-runtime.d.ts.map +1 -1
  92. package/dist/src/jsx-runtime.js +102 -12
  93. package/dist/src/jsx-runtime.js.map +1 -1
  94. package/dist/src/render.d.ts +107 -132
  95. package/dist/src/render.d.ts.map +1 -1
  96. package/dist/src/render.js +269 -177
  97. package/dist/src/render.js.map +1 -1
  98. package/dist/src/stc.d.ts +14 -0
  99. package/dist/src/stc.d.ts.map +1 -0
  100. package/dist/src/stc.js +52 -0
  101. package/dist/src/stc.js.map +1 -0
  102. package/dist/src/utils.d.ts +22 -15
  103. package/dist/src/utils.d.ts.map +1 -1
  104. package/dist/src/utils.js +95 -59
  105. package/dist/src/utils.js.map +1 -1
  106. package/dist/test/browser-build.test.d.ts +2 -0
  107. package/dist/test/browser-build.test.d.ts.map +1 -0
  108. package/dist/test/components/block.test.d.ts +2 -0
  109. package/dist/test/components/block.test.d.ts.map +1 -0
  110. package/dist/test/components/declaration.test.d.ts +2 -0
  111. package/dist/test/components/declaration.test.d.ts.map +1 -0
  112. package/dist/test/components/list.test.d.ts +2 -0
  113. package/dist/test/components/list.test.d.ts.map +1 -0
  114. package/dist/test/components/wrap.test.d.ts +2 -0
  115. package/dist/test/components/wrap.test.d.ts.map +1 -0
  116. package/dist/test/control-flow/for.test.d.ts +2 -0
  117. package/dist/test/control-flow/for.test.d.ts.map +1 -0
  118. package/dist/test/control-flow/match.test.d.ts +2 -0
  119. package/dist/test/control-flow/match.test.d.ts.map +1 -0
  120. package/dist/test/control-flow/show.test.d.ts +2 -0
  121. package/dist/test/control-flow/show.test.d.ts.map +1 -0
  122. package/dist/test/reactivity/cleanup.test.d.ts +2 -0
  123. package/dist/test/reactivity/cleanup.test.d.ts.map +1 -0
  124. package/dist/test/reactivity/memo.test.d.ts +2 -0
  125. package/dist/test/reactivity/memo.test.d.ts.map +1 -0
  126. package/dist/test/reactivity/untrack.test.d.ts +2 -0
  127. package/dist/test/reactivity/untrack.test.d.ts.map +1 -0
  128. package/dist/test/rendering/formatting.test.d.ts +2 -0
  129. package/dist/test/rendering/formatting.test.d.ts.map +1 -0
  130. package/dist/test/rendering/memoization.test.d.ts +2 -0
  131. package/dist/test/rendering/memoization.test.d.ts.map +1 -0
  132. package/dist/test/split-props.test.d.ts +2 -0
  133. package/dist/test/split-props.test.d.ts.map +1 -0
  134. package/dist/test/stc.test.d.ts.map +1 -1
  135. package/dist/test/utils.test.d.ts.map +1 -1
  136. package/dist/testing/extend-expect.js +4 -4
  137. package/dist/testing/extend-expect.js.map +1 -1
  138. package/dist/testing/render.d.ts +2 -3
  139. package/dist/testing/render.d.ts.map +1 -1
  140. package/dist/testing/render.js +2 -4
  141. package/dist/testing/render.js.map +1 -1
  142. package/dist/tsconfig.tsbuildinfo +1 -1
  143. package/package.json +6 -8
  144. package/src/binder.ts +21 -0
  145. package/src/code.ts +17 -12
  146. package/src/components/Block.tsx +44 -0
  147. package/src/components/Declaration.tsx +10 -4
  148. package/src/components/For.tsx +81 -0
  149. package/src/components/Indent.tsx +20 -27
  150. package/src/components/List.tsx +94 -0
  151. package/src/components/MemberDeclaration.tsx +9 -6
  152. package/src/components/MemberScope.tsx +4 -2
  153. package/src/components/Output.tsx +25 -13
  154. package/src/components/Scope.tsx +4 -2
  155. package/src/components/Show.tsx +11 -0
  156. package/src/components/SourceDirectory.tsx +5 -1
  157. package/src/components/SourceFile.tsx +12 -16
  158. package/src/components/StatementList.tsx +16 -0
  159. package/src/components/Switch.tsx +62 -0
  160. package/src/components/Wrap.tsx +29 -0
  161. package/src/components/index.tsx +8 -1
  162. package/src/components/stc/index.ts +18 -1
  163. package/src/context/index.ts +0 -1
  164. package/src/context.ts +2 -3
  165. package/src/index.browser.ts +2 -0
  166. package/src/index.ts +1 -0
  167. package/src/jsx-runtime.ts +241 -23
  168. package/src/render.ts +378 -198
  169. package/src/stc.ts +95 -0
  170. package/src/utils.ts +162 -95
  171. package/temp/api.json +8318 -3301
  172. package/test/browser-build.test.ts +91 -0
  173. package/test/children.test.tsx +8 -10
  174. package/test/components/block.test.tsx +48 -0
  175. package/test/components/declaration.test.tsx +37 -0
  176. package/test/components/list.test.tsx +91 -0
  177. package/test/components/slot.test.tsx +31 -25
  178. package/test/components/source-file.test.tsx +11 -31
  179. package/test/components/wrap.test.tsx +42 -0
  180. package/test/control-flow/for.test.tsx +194 -0
  181. package/test/control-flow/match.test.tsx +49 -0
  182. package/test/control-flow/show.test.tsx +25 -0
  183. package/test/name-policy.test.tsx +5 -5
  184. package/test/reactivity/cleanup.test.tsx +91 -0
  185. package/test/reactivity/memo.test.tsx +17 -0
  186. package/test/reactivity/ref-rendering.test.tsx +3 -8
  187. package/test/reactivity/test.test.tsx +7 -6
  188. package/test/reactivity/untrack.test.ts +33 -0
  189. package/test/rendering/basic.test.tsx +25 -47
  190. package/test/rendering/code.test.tsx +3 -3
  191. package/test/rendering/formatting.test.tsx +487 -0
  192. package/test/rendering/indent.test.tsx +42 -529
  193. package/test/rendering/memoization.test.tsx +30 -0
  194. package/test/split-props.test.ts +87 -0
  195. package/test/stc.test.tsx +29 -8
  196. package/test/symbols.test.ts +73 -0
  197. package/test/utils.test.tsx +129 -20
  198. package/testing/extend-expect.ts +14 -4
  199. package/testing/render.ts +2 -4
  200. package/testing/vitest.d.ts +6 -1
  201. package/vitest.config.ts +1 -1
  202. package/dist/src/context/indent.d.ts +0 -5
  203. package/dist/src/context/indent.d.ts.map +0 -1
  204. package/dist/src/context/indent.js +0 -8
  205. package/dist/src/context/indent.js.map +0 -1
  206. package/dist/test/rendering/linebreaks.test.d.ts +0 -2
  207. package/dist/test/rendering/linebreaks.test.d.ts.map +0 -1
  208. package/src/context/indent.ts +0 -17
  209. package/test/rendering/linebreaks.test.tsx +0 -72
package/src/stc.ts ADDED
@@ -0,0 +1,95 @@
1
+ import {
2
+ Children,
3
+ ComponentCreator,
4
+ ComponentDefinition,
5
+ createIntrinsic,
6
+ IntrinsicElementBase,
7
+ JSX,
8
+ } from "@alloy-js/core/jsx-runtime";
9
+ import { code } from "./code.js";
10
+
11
+ export function sti<T extends keyof JSX.IntrinsicElements>(name: T) {
12
+ return (
13
+ ...args: unknown extends T ? []
14
+ : {} extends Omit<JSX.IntrinsicElements[T], "children"> ?
15
+ [props?: JSX.IntrinsicElements[T]]
16
+ : [props: JSX.IntrinsicElements[T]]
17
+ ) => {
18
+ const props: JSX.IntrinsicElements[T] | undefined = args[0];
19
+ const fn = () => createIntrinsic(name, props!);
20
+ fn.children = (
21
+ ...children: Children[]
22
+ ): (() => IntrinsicElementBase<T>) => {
23
+ const propsWithChildren = {
24
+ ...(props ?? {}),
25
+ children,
26
+ };
27
+
28
+ return () => createIntrinsic(name, propsWithChildren as any);
29
+ };
30
+
31
+ fn.code = (
32
+ template: TemplateStringsArray,
33
+ ...substitutions: Children[]
34
+ ): (() => IntrinsicElementBase<T>) => {
35
+ const propsWithChildren = {
36
+ ...(args[0] ?? {}),
37
+ children: code(template, ...substitutions),
38
+ };
39
+
40
+ return () => createIntrinsic(name, propsWithChildren as any);
41
+ };
42
+ return fn;
43
+ };
44
+ }
45
+
46
+ export type MakeChildrenOptional<T extends object> =
47
+ T extends { children?: any } ?
48
+ Omit<T, "children"> & Partial<Pick<T, "children">>
49
+ : T;
50
+
51
+ export function stc<T extends {}>(Component: ComponentDefinition<T>) {
52
+ return (
53
+ ...args: unknown extends T ? []
54
+ : {} extends Omit<T, "children"> ? [props?: MakeChildrenOptional<T>]
55
+ : [props: MakeChildrenOptional<T>]
56
+ ) => {
57
+ const fn: ComponentCreator<T> & {
58
+ code(
59
+ template: TemplateStringsArray,
60
+ ...substitutions: Children[]
61
+ ): ComponentCreator<T>;
62
+ children(...children: Children[]): ComponentCreator<T>;
63
+ } = () => Component(args[0] as any);
64
+ fn.component = Component;
65
+ fn.props = args[0]!;
66
+ fn.code = (
67
+ template: TemplateStringsArray,
68
+ ...substitutions: Children[]
69
+ ): ComponentCreator<T> => {
70
+ const propsWithChildren = {
71
+ ...(args[0] ?? {}),
72
+ children: code(template, ...substitutions),
73
+ };
74
+
75
+ const fn = () => Component(propsWithChildren as any);
76
+ fn.component = Component;
77
+ fn.props = args[0]!;
78
+ return fn;
79
+ };
80
+
81
+ fn.children = (...children: Children[]): ComponentCreator<T> => {
82
+ const propsWithChildren = {
83
+ ...(args[0] ?? {}),
84
+ children,
85
+ };
86
+
87
+ const fn = () => Component(propsWithChildren as any);
88
+ fn.component = Component;
89
+ fn.props = args[0]!;
90
+ return fn;
91
+ };
92
+
93
+ return fn;
94
+ };
95
+ }
package/src/utils.ts CHANGED
@@ -1,11 +1,15 @@
1
- import { code } from "./code.js";
1
+ import { toRaw } from "@vue/reactivity";
2
2
  import {
3
- Child,
4
3
  Children,
5
4
  ComponentCreator,
6
- ComponentDefinition,
5
+ createCustomContext,
6
+ CustomContext,
7
+ Disposable,
7
8
  isComponentCreator,
8
9
  memo,
10
+ onCleanup,
11
+ root,
12
+ untrack,
9
13
  } from "./jsx-runtime.js";
10
14
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
15
  import { OutputDirectory, OutputFile, render } from "./render.js";
@@ -14,14 +18,19 @@ export interface JoinOptions {
14
18
  /**
15
19
  * The string to place between each element.
16
20
  */
17
- joiner?: string;
21
+ joiner?: Children;
18
22
 
19
23
  /**
20
24
  * When true, the joiner is placed at the end of the array. When a string,
21
25
  * that string is placed at the end of the array. The ender is only emitted
22
26
  * when the array has at least one element.
23
27
  */
24
- ender?: string | boolean;
28
+ ender?: Children;
29
+
30
+ /**
31
+ * When true, falsy values with the exception of 0 are skipped.
32
+ */
33
+ skipFalsy?: boolean;
25
34
  }
26
35
  const defaultJoinOptions: JoinOptions = {
27
36
  joiner: "\n",
@@ -40,10 +49,10 @@ const defaultJoinOptions: JoinOptions = {
40
49
  *
41
50
  */
42
51
  export function mapJoin<T, U, V>(
43
- src: Map<T, U>,
44
- cb: (key: T, value: U) => V,
52
+ src: () => Map<T, U>,
53
+ cb: (key: T, value: U, index: number) => V,
45
54
  options?: JoinOptions,
46
- ): (V | string)[];
55
+ ): () => (V | string | undefined | CustomContext)[];
47
56
  /**
48
57
  * Map a array or iterator to another array using a mapper and place a joiner
49
58
  * between each element. Defaults to joining with a newline.
@@ -55,47 +64,122 @@ export function mapJoin<T, U, V>(
55
64
  * @returns The mapped and joined array.
56
65
  */
57
66
  export function mapJoin<T, V>(
58
- src: T[] | IterableIterator<T>,
59
- cb: (value: T) => V,
67
+ src: () => T[] | IterableIterator<T>,
68
+ cb: (value: T, index: number) => V,
60
69
  options?: JoinOptions,
61
- ): (V | string)[];
70
+ ): () => (V | string | undefined | CustomContext)[];
62
71
  export function mapJoin<T, U, V>(
63
- src: Map<T, U> | T[] | Iterable<T>,
64
- cb: (key: T, value?: U) => V,
72
+ src: () => Map<T, U> | T[] | Iterable<T>,
73
+ cb: (key: T, valueOrIndex: U | number, index: number) => V,
65
74
  rawOptions: JoinOptions = {},
66
- ): (V | string)[] {
75
+ ): () => Children {
67
76
  const options = { ...defaultJoinOptions, ...rawOptions };
68
- const ender = options.ender === true ? options.joiner : options.ender;
77
+ const ender =
78
+ options.ender === true ? options.joiner : options.ender || undefined;
79
+ const currentItems: (T | [T, U])[] = [];
80
+ const disposables: Disposable[] = [];
81
+ const mapped: Children[] = [];
82
+ let previousItemsLen = 0;
69
83
 
70
- const mapped: (V | string)[] = [];
71
- if (typeof (src as any).next === "function") {
72
- src = Array.from(src as Iterable<T>);
73
- }
84
+ onCleanup(() => {
85
+ for (const d of disposables) d();
86
+ });
74
87
 
75
- if (Array.isArray(src)) {
76
- for (const [index, item] of src.entries()) {
77
- mapped.push(cb(item));
78
- if (index !== src.length - 1) {
79
- mapped.push(options.joiner!);
80
- }
88
+ return () => {
89
+ const itemsSource = src();
90
+ // need to unpack reactives for branding checks
91
+ const itemsSourceRaw = toRaw(itemsSource);
92
+ let items =
93
+ Array.isArray(itemsSourceRaw) ? (itemsSource as T[]) : [...itemsSource];
94
+ if (options.skipFalsy) {
95
+ items = items.filter(
96
+ (item) =>
97
+ item !== null && item !== undefined && typeof item !== "boolean",
98
+ );
81
99
  }
82
- if (src.length > 0 && ender) {
83
- mapped.push(ender);
84
- }
85
- } else {
86
- const entries = [...(src as Map<T, U>).entries()];
87
- for (const [index, [key, value]] of entries.entries()) {
88
- mapped.push(cb(key, value));
89
- if (index !== entries.length - 1) {
90
- mapped.push(options.joiner!);
100
+ // this is important to access here in reactive context so we are
101
+ // notified of new items from reactives.
102
+ const itemsLen = items.length;
103
+ const compare: any = getCompareFunction(itemsSource);
104
+ const mapper: any = getMapperFunction(itemsSource);
105
+
106
+ return untrack(() => {
107
+ let startIndex = 0;
108
+ for (
109
+ ;
110
+ startIndex < itemsLen && startIndex < currentItems.length;
111
+ startIndex++
112
+ ) {
113
+ if (!compare(items[startIndex], currentItems[startIndex])) {
114
+ break;
115
+ }
91
116
  }
92
- }
93
- if (entries.length > 0 && ender) {
94
- mapped.push(ender);
95
- }
117
+
118
+ if (startIndex > 0 && startIndex < itemsLen) {
119
+ // need to update the previous joiner (might be ender or absent)
120
+ mapped[startIndex * 2 - 1] = options.joiner;
121
+ }
122
+
123
+ for (; startIndex < itemsLen; startIndex++) {
124
+ currentItems[startIndex] = items[startIndex];
125
+ if (disposables[startIndex]) {
126
+ disposables[startIndex]();
127
+ }
128
+ const cleanupIndex = startIndex;
129
+ mapped[startIndex * 2] = createCustomContext((cb) => {
130
+ return root((disposer) => {
131
+ disposables[cleanupIndex] = disposer;
132
+ disposer();
133
+ cb(mapper(items[cleanupIndex], cleanupIndex));
134
+ });
135
+ });
136
+
137
+ mapped[startIndex * 2 + 1] =
138
+ startIndex < items.length - 1 ? options.joiner : ender;
139
+ }
140
+
141
+ mapped.length = startIndex * 2;
142
+ mapped[mapped.length - 1] = ender;
143
+ for (; startIndex < previousItemsLen; startIndex++) {
144
+ disposables[startIndex]?.();
145
+ }
146
+
147
+ previousItemsLen = itemsLen;
148
+
149
+ return mapped;
150
+ });
151
+ };
152
+
153
+ function getCompareFunction(itemsSource: Map<T, U> | T[] | Iterable<T>) {
154
+ return Array.isArray(itemsSource) || isIterable(itemsSource) ?
155
+ compareArray
156
+ : compareMap;
157
+ }
158
+
159
+ function getMapperFunction(itemsSource: Map<T, U> | T[] | Iterable<T>) {
160
+ return Array.isArray(itemsSource) || isIterable(itemsSource) ?
161
+ mapArray
162
+ : mapMap;
163
+ }
164
+ function compareArray(elem1: T, elem2: T) {
165
+ return elem1 === elem2;
166
+ }
167
+
168
+ function compareMap(record1: [T, U], record2: [T, U]) {
169
+ return record1[0] === record2[0] && record1[1] === record2[1];
170
+ }
171
+
172
+ function mapArray(item: T, index: number) {
173
+ return (cb as any)(item, index);
96
174
  }
97
175
 
98
- return mapped;
176
+ function mapMap(item: [T, U], index: number) {
177
+ return cb(item[0], item[1], index);
178
+ }
179
+
180
+ function isIterable<T>(x: unknown): x is Iterable<T> {
181
+ return typeof (x as any).next === "function";
182
+ }
99
183
  }
100
184
 
101
185
  /**
@@ -105,12 +189,12 @@ export function mapJoin<T, U, V>(
105
189
  * @see mapJoin for mapping before joining.
106
190
  * @returns The joined array
107
191
  */
108
- export function join<T>(
192
+ export function join<T extends Children>(
109
193
  src: T[] | Iterator<T>,
110
194
  options: JoinOptions = {},
111
- ): (T | string)[] {
195
+ ): Children {
112
196
  const mergedOptions = { ...defaultJoinOptions, ...options };
113
- const joined = [];
197
+ const joined: Children[] = [];
114
198
  const ender =
115
199
  mergedOptions.ender === true ? mergedOptions.joiner : mergedOptions.ender;
116
200
  src = Array.from(src as Iterable<T>);
@@ -129,12 +213,28 @@ export function join<T>(
129
213
  return joined;
130
214
  }
131
215
 
216
+ export interface ChildrenOptions {
217
+ /**
218
+ * When true, fragments and arrays are not flattened.
219
+ */
220
+ preserveFragments?: boolean;
221
+ }
132
222
  /**
133
223
  * Returns a memo which is a list of all the provided children.
134
224
  * If you want this as an array, see {@link childrenArray}.
135
225
  */
136
- export function children(fn: () => Children): () => Children {
137
- return memo(() => collectChildren(fn()));
226
+ export function children(
227
+ fn: () => Children,
228
+ options: ChildrenOptions = {},
229
+ ): () => Children {
230
+ return memo(() => {
231
+ const children = fn();
232
+ if (options.preserveFragments && Array.isArray(children)) {
233
+ return children.map(collectChildren);
234
+ } else {
235
+ return collectChildren(children);
236
+ }
237
+ });
138
238
 
139
239
  function collectChildren(children: Children): Children {
140
240
  if (Array.isArray(children)) {
@@ -150,8 +250,11 @@ export function children(fn: () => Children): () => Children {
150
250
  }
151
251
  }
152
252
 
153
- export function childrenArray(fn: () => Children): Child[] {
154
- const c = children(fn)();
253
+ export function childrenArray(
254
+ fn: () => Children,
255
+ options?: ChildrenOptions,
256
+ ): Children[] {
257
+ const c = children(fn, options)();
155
258
  if (Array.isArray(c)) {
156
259
  return c;
157
260
  } else if (c === undefined) {
@@ -161,7 +264,7 @@ export function childrenArray(fn: () => Children): Child[] {
161
264
  }
162
265
  }
163
266
 
164
- export function findKeyedChild(children: Child[], tag: symbol) {
267
+ export function findKeyedChild(children: Children[], tag: symbol) {
165
268
  for (const child of children) {
166
269
  if (isKeyedChild(child) && child.tag === tag) {
167
270
  return child;
@@ -171,58 +274,22 @@ export function findKeyedChild(children: Child[], tag: symbol) {
171
274
  return null;
172
275
  }
173
276
 
174
- export function findUnkeyedChildren(children: Child[]) {
175
- return children.filter((child) => !isKeyedChild(child));
277
+ export function findKeyedChildren(children: Children[], tag: symbol) {
278
+ const keyedChildren: ComponentCreator[] = [];
279
+ for (const child of children) {
280
+ if (isKeyedChild(child) && child.tag === tag) {
281
+ keyedChildren.push(child);
282
+ }
283
+ }
284
+ return keyedChildren;
176
285
  }
177
286
 
178
- export function isKeyedChild(child: Child): child is ComponentCreator {
179
- return isComponentCreator(child) && !!child.tag;
287
+ export function findUnkeyedChildren(children: Children[]) {
288
+ return children.filter((child) => !isKeyedChild(child));
180
289
  }
181
290
 
182
- export function stc<T extends {}>(Component: ComponentDefinition<T>) {
183
- return (
184
- ...args: unknown extends T ? []
185
- : {} extends Omit<T, "children"> ? [props?: T]
186
- : [props: T]
187
- ) => {
188
- const fn: ComponentCreator<T> & {
189
- code(
190
- template: TemplateStringsArray,
191
- ...substitutions: Children[]
192
- ): ComponentCreator<T>;
193
- children(...children: Children[]): ComponentCreator<T>;
194
- } = () => Component(args[0]!);
195
- fn.component = Component;
196
- fn.props = args[0]!;
197
- fn.code = (
198
- template: TemplateStringsArray,
199
- ...substitutions: Children[]
200
- ): ComponentCreator<T> => {
201
- const propsWithChildren = {
202
- ...(args[0] ?? {}),
203
- children: code(template, ...substitutions),
204
- };
205
-
206
- const fn = () => Component(propsWithChildren as any);
207
- fn.component = Component;
208
- fn.props = args[0]!;
209
- return fn;
210
- };
211
-
212
- fn.children = (...children: Children[]): ComponentCreator<T> => {
213
- const propsWithChildren = {
214
- ...(args[0] ?? {}),
215
- children,
216
- };
217
-
218
- const fn = () => Component(propsWithChildren as any);
219
- fn.component = Component;
220
- fn.props = args[0]!;
221
- return fn;
222
- };
223
-
224
- return fn;
225
- };
291
+ export function isKeyedChild(child: Children): child is ComponentCreator {
292
+ return isComponentCreator(child) && !!child.tag;
226
293
  }
227
294
 
228
295
  /**