@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/package.json CHANGED
@@ -1,28 +1,24 @@
1
1
  {
2
2
  "name": "@alloy-js/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "",
5
5
  "main": "./dist/src/index.js",
6
6
  "exports": {
7
7
  ".": {
8
- "development": "./src/index.ts",
8
+ "browser": "./dist/src/index.browser.js",
9
9
  "import": "./dist/src/index.js"
10
10
  },
11
11
  "./jsx-runtime": {
12
12
  "types": "./dist/src/jsx-runtime.d.ts",
13
- "development": "./src/jsx-runtime.ts",
14
13
  "import": "./dist/src/jsx-runtime.js"
15
14
  },
16
15
  "./testing": {
17
- "development": "./testing/index.ts",
18
16
  "import": "./dist/testing/index.js"
19
17
  },
20
18
  "./stc": {
21
- "development": "./src/components/stc/index.ts",
22
19
  "import": "./dist/src/components/stc/index.js"
23
20
  },
24
21
  "./components": {
25
- "development": "./src/components/index.ts",
26
22
  "import": "./dist/src/components/index.js"
27
23
  }
28
24
  },
@@ -40,7 +36,8 @@
40
36
  "chalk": "^5.3.0",
41
37
  "cli-table3": "^0.6.5",
42
38
  "pathe": "^1.1.2",
43
- "@alloy-js/babel-preset": "~0.1.1"
39
+ "prettier": "^3.5.1",
40
+ "@alloy-js/babel-preset": "~0.2.0"
44
41
  },
45
42
  "devDependencies": {
46
43
  "@babel/cli": "^7.24.7",
@@ -49,7 +46,8 @@
49
46
  "@rollup/plugin-typescript": "^11.1.6",
50
47
  "concurrently": "^8.2.2",
51
48
  "typescript": "^5.7.3",
52
- "vitest": "^3.0.4"
49
+ "vitest": "^3.0.4",
50
+ "vite": "^6.0.1"
53
51
  },
54
52
  "type": "module",
55
53
  "scripts": {
package/src/binder.ts CHANGED
@@ -265,6 +265,12 @@ export interface Binder {
265
265
  */
266
266
  createSymbol<T extends OutputSymbol>(args: CreateSymbolOptions<T>): T;
267
267
 
268
+ /**
269
+ * Delete the given symbol. The symbol will be removed from its parent's
270
+ * scope. Any resolutions to this symbol will become undefined.
271
+ */
272
+ deleteSymbol(symbol: OutputSymbol): void;
273
+
268
274
  /**
269
275
  * Instantiate the static members of a symbol into the instance members of
270
276
  * another symbol.
@@ -415,6 +421,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
415
421
  const binder: Binder = {
416
422
  createScope,
417
423
  createSymbol,
424
+ deleteSymbol,
418
425
  resolveDeclarationByKey,
419
426
  addStaticMembersToSymbol,
420
427
  addInstanceMembersToSymbol,
@@ -584,6 +591,18 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
584
591
  return symbol;
585
592
  }
586
593
 
594
+ function deleteSymbol(symbol: OutputSymbol) {
595
+ symbol.scope.symbols.delete(symbol);
596
+
597
+ if (!refkey) {
598
+ return;
599
+ }
600
+
601
+ const resolution = waitingDeclarations.get(symbol.refkey);
602
+ if (!resolution) return;
603
+ resolution.value = undefined;
604
+ }
605
+
587
606
  function instantiateSymbolInto(source: OutputSymbol, target: OutputSymbol) {
588
607
  if (~source.flags & OutputSymbolFlags.InstanceMemberContainer) {
589
608
  throw new Error("Can only instantiate symbols with instance members");
@@ -800,12 +819,14 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
800
819
  const refkey = symbol.refkey;
801
820
  if (!refkey) return;
802
821
 
822
+ // notify those waiting for this refkey
803
823
  knownDeclarations.set(refkey, symbol);
804
824
  if (waitingDeclarations.has(refkey)) {
805
825
  const signal = waitingDeclarations.get(refkey)!;
806
826
  signal.value = symbol;
807
827
  }
808
828
 
829
+ // notify those waiting for this symbol name
809
830
  const waitingScope = waitingSymbolNames.get(symbol.scope);
810
831
  if (waitingScope) {
811
832
  const waitingName = waitingScope.get(symbol.name);
package/src/code.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  // this code is split into a tokenizer and a parser of sorts because I feel like
2
2
  // it should be psosible to share logic between this and the babel transform, but
3
3
  // this is an exercise for the future.
4
- import { Child, Indent } from "@alloy-js/core";
5
-
4
+ import { hbr, indent } from "./components/stc/index.js";
5
+ import { Child, Children } from "./jsx-runtime.js";
6
6
  interface IndentLevelData {
7
7
  kind: "indent";
8
- children: (string | Child | IndentLevelData)[];
8
+ children: (string | Children | IndentLevelData)[];
9
9
  pendingLines: string[];
10
10
  }
11
11
  export function code(
12
12
  template: TemplateStringsArray,
13
- ...substitutions: Child[]
13
+ ...substitutions: Children[]
14
14
  ) {
15
15
  const indentNodes: IndentLevelData[] = [
16
16
  {
@@ -39,6 +39,8 @@ export function code(
39
39
  popIndent();
40
40
  flushLines();
41
41
 
42
+ return childNodesFor(indentNodes[0]);
43
+
42
44
  function childNodesFor(indentNode: IndentLevelData): Child[] {
43
45
  return indentNode.children.map((child) => {
44
46
  if (
@@ -46,16 +48,13 @@ export function code(
46
48
  child !== null &&
47
49
  (child as any).kind === "indent"
48
50
  ) {
49
- return () =>
50
- Indent({ children: childNodesFor(child as IndentLevelData) });
51
+ return indent({ children: childNodesFor(child as IndentLevelData) });
51
52
  } else {
52
53
  return child as Child;
53
54
  }
54
55
  });
55
56
  }
56
57
 
57
- return childNodesFor(indentNodes[0]);
58
-
59
58
  function pushIndent() {
60
59
  flushLines();
61
60
  const newIndent: IndentLevelData = {
@@ -63,7 +62,7 @@ export function code(
63
62
  children: [],
64
63
  pendingLines: [""],
65
64
  };
66
- indentNodes.at(-1)!.children.push(newIndent as any);
65
+ indentNodes.at(-1)!.children.push(newIndent);
67
66
  indentNodes.push(newIndent);
68
67
  }
69
68
 
@@ -84,7 +83,13 @@ export function code(
84
83
  }
85
84
  function flushLines() {
86
85
  const currentIndent = indentNodes.at(-1)!;
87
- currentIndent.children.push(currentIndent.pendingLines.join("\n"));
86
+ currentIndent.children.push(
87
+ ...currentIndent.pendingLines
88
+ .map((str, index) =>
89
+ index < currentIndent.pendingLines.length - 1 ? [str, hbr()] : [str],
90
+ )
91
+ .flat(),
92
+ );
88
93
  currentIndent.pendingLines = [];
89
94
  }
90
95
  }
@@ -103,12 +108,12 @@ interface LineToken extends ChildTokenBase {
103
108
 
104
109
  interface OtherToken extends ChildTokenBase {
105
110
  kind: "other";
106
- value: Child;
111
+ value: Children;
107
112
  }
108
113
 
109
114
  function* childTokens(
110
115
  template: TemplateStringsArray,
111
- substitutions: Child[],
116
+ substitutions: Children[],
112
117
  ): IterableIterator<ChildToken> {
113
118
  let newline = false;
114
119
  const indentStack: { level: number; literalIndent: string }[] = [
@@ -0,0 +1,44 @@
1
+ import { Children, childrenArray, computed, Indent } from "@alloy-js/core";
2
+
3
+ export interface BlockProps {
4
+ /**
5
+ * The opening punctuation of the block. Defaults to "\{".
6
+ */
7
+ opener?: string;
8
+
9
+ /**
10
+ * The closing punctuation of the block. Defaults to "\}".
11
+ */
12
+ closer?: string;
13
+
14
+ /**
15
+ * Whether the block starts on a new line. When true, a hardline is added
16
+ * prior to the block.
17
+ */
18
+ newline?: boolean;
19
+
20
+ /** The block's contents */
21
+ children?: Children;
22
+ }
23
+
24
+ /**
25
+ * Create an indented block of source text. The block has `opener` text which is
26
+ * added prior to the block, which defaults to "\{", and `closer` text which is
27
+ * added after the block, which defaults to "\}".
28
+ */
29
+ export function Block(props: BlockProps) {
30
+ const childCount = computed(() => childrenArray(() => props.children).length);
31
+ return (
32
+ <group>
33
+ {props.newline && <br />}
34
+ {props.opener ?? "{"}
35
+ <Indent break={childCount.value > 0 ? "hard" : "soft"}>
36
+ {props.children}
37
+ </Indent>
38
+ {childCount.value > 0 ?
39
+ <hbr />
40
+ : <sbr />}
41
+ {props.closer ?? "}"}
42
+ </group>
43
+ );
44
+ }
@@ -2,7 +2,7 @@ import { OutputSymbol } from "../binder.js";
2
2
  import { useContext } from "../context.js";
3
3
  import { BinderContext } from "../context/binder.js";
4
4
  import { DeclarationContext } from "../context/declaration.js";
5
- import { Children } from "../jsx-runtime.js";
5
+ import { Children, onCleanup } from "../jsx-runtime.js";
6
6
  import { Refkey, refkey } from "../refkey.js";
7
7
 
8
8
  export interface DeclarationProps {
@@ -45,9 +45,15 @@ export function Declaration(props: DeclarationProps) {
45
45
  name: props.name!,
46
46
  refkey: rk,
47
47
  });
48
+
49
+ onCleanup(() => {
50
+ binder.deleteSymbol(declaration!);
51
+ });
48
52
  }
49
53
 
50
- return <DeclarationContext.Provider value={declaration}>
51
- {props.children}
52
- </DeclarationContext.Provider>;
54
+ return (
55
+ <DeclarationContext.Provider value={declaration}>
56
+ {props.children}
57
+ </DeclarationContext.Provider>
58
+ );
53
59
  }
@@ -0,0 +1,81 @@
1
+ import { Children, memo } from "@alloy-js/core/jsx-runtime";
2
+ import { isRef, Ref } from "@vue/reactivity";
3
+ import { mapJoin } from "../utils.js";
4
+ import { BaseListProps, baseListPropsToMapJoinArgs } from "./List.jsx";
5
+
6
+ export type ForCallbackArgs<T> =
7
+ T extends Ref<infer U> ? ForCallbackArgs<U>
8
+ : T extends () => infer U ? ForCallbackArgs<U>
9
+ : T extends (infer U)[] ? [value: U]
10
+ : T extends Map<infer U, infer V> ? [key: U, value: V]
11
+ : T extends Set<infer U> ? [value: U]
12
+ : [];
13
+
14
+ export interface ForProps<
15
+ T extends
16
+ | ForSupportedCollections
17
+ | (() => ForSupportedCollections)
18
+ | Ref<ForSupportedCollections>,
19
+ U extends Children,
20
+ > extends BaseListProps {
21
+ /**
22
+ * The array to iterate over.
23
+ */
24
+ each: T;
25
+
26
+ /**
27
+ * A function to call for each item.
28
+ */
29
+ children: (...args: [...ForCallbackArgs<T>, index: number]) => U;
30
+ }
31
+
32
+ export type ForSupportedCollections = any[] | Map<any, any> | Set<any>;
33
+ /**
34
+ * The For component iterates over the provided array and invokes the child
35
+ * callback for each item. It can optionally be provided with a `joiner` which
36
+ * is placed between each item, and an `ender` which is placed after the last
37
+ * item when there is at least one item.
38
+ *
39
+ * @example
40
+ *
41
+ * ```tsx
42
+ * const items = ["apple", "pear", "plum"];
43
+ * return <For each={items}>
44
+ * {(item) => <>Fruit: {item}</>}
45
+ * </For>
46
+ * ```
47
+ *
48
+ * @remarks
49
+ *
50
+ * When the `each` prop is a reactive (e.g. a reactive array, or ref to an
51
+ * array), `For` will automatically update when the array changes. When doing
52
+ * so, it will attempt to avoid re-rendering items which have not changed. For
53
+ * example, when appending an item to a reactive array, existing items will not
54
+ * be re-rendered. Note that presently the implementation is fairly simple -
55
+ * when making modifications to the middle of an array it likely that every
56
+ * element after the modification will be rerendered.
57
+ *
58
+ * @see {@link (mapJoin:1)} for mapping arrays to elements outside of JSX templates.
59
+ */
60
+ export function For<
61
+ T extends
62
+ | ForSupportedCollections
63
+ | (() => ForSupportedCollections)
64
+ | Ref<ForSupportedCollections>,
65
+ U extends Children,
66
+ >(props: ForProps<T, U>) {
67
+ const cb = props.children;
68
+ const options = baseListPropsToMapJoinArgs(props);
69
+ options.skipFalsy = true;
70
+ return memo(() => {
71
+ const maybeRef = props.each;
72
+
73
+ return (mapJoin as any)(
74
+ typeof maybeRef === "function" ? maybeRef : (
75
+ () => (isRef(maybeRef) ? maybeRef.value : maybeRef)
76
+ ),
77
+ cb,
78
+ options,
79
+ );
80
+ });
81
+ }
@@ -1,33 +1,26 @@
1
- import { useContext } from "../context.js";
2
- import { IndentContext } from "../context/indent.js";
3
- import { Children } from "../jsx-runtime.js";
1
+ import { Children } from "@alloy-js/core/jsx-runtime";
4
2
 
5
3
  export interface IndentProps {
6
- children?: Children;
7
- indent?: string;
4
+ children: Children;
5
+ nobreak?: boolean;
6
+ break?: "space" | "soft" | "hard";
7
+ trailingBreak?: boolean;
8
8
  }
9
-
10
- export interface IndentState {
11
- level: number;
12
- indent: string;
13
- indentString: string; // awful name
14
- noLeading?: boolean;
15
- }
16
-
17
9
  export function Indent(props: IndentProps) {
18
- const previousIndent = useContext(IndentContext) ?? {
19
- level: 0,
20
- indent: props.indent ?? " ",
21
- indentString: "",
22
- };
23
-
24
- const level = previousIndent.level + 1;
25
-
26
- const currentIndent = {
27
- level,
28
- indent: props.indent ?? previousIndent.indent,
29
- indentString: (props.indent ?? previousIndent.indent).repeat(level),
30
- };
10
+ const breakStyle = props.break ?? "hard";
11
+ const breakElem =
12
+ props.nobreak ? ""
13
+ : breakStyle === "hard" ? <hbr />
14
+ : breakStyle === "soft" ? <sbr />
15
+ : <br />;
31
16
 
32
- return <IndentContext.Provider value={currentIndent}>{props.children}</IndentContext.Provider>;
17
+ return (
18
+ <>
19
+ <indent>
20
+ {breakElem}
21
+ {props.children}
22
+ </indent>
23
+ {props.trailingBreak && breakElem}
24
+ </>
25
+ );
33
26
  }
@@ -0,0 +1,94 @@
1
+ import { Children, memo, splitProps } from "@alloy-js/core/jsx-runtime";
2
+ import { childrenArray, JoinOptions } from "../utils.js";
3
+ import { For } from "./For.jsx";
4
+
5
+ export type BreakKind = "none" | "space" | "soft" | "hard" | "literal";
6
+
7
+ export interface BaseListProps {
8
+ /** Text to place between each element */
9
+ joiner?: Children;
10
+
11
+ /** Place a comma between each element */
12
+ comma?: boolean;
13
+
14
+ /** Place a semicolon between each element */
15
+ semicolon?: boolean;
16
+
17
+ line?: boolean;
18
+ softline?: boolean;
19
+ hardline?: boolean;
20
+ literalline?: boolean;
21
+ space?: boolean;
22
+
23
+ /**
24
+ * Text to place at the end of the list when there is at least one item. If
25
+ * set to true, the joiner is used.
26
+ **/
27
+ ender?: Children;
28
+
29
+ /**
30
+ * Place the join punctuation at the end, but without a line break.
31
+ */
32
+ enderPunctuation?: boolean;
33
+ }
34
+
35
+ export function baseListPropsToMapJoinArgs(props: BaseListProps): JoinOptions {
36
+ let joiner, punctuation;
37
+ if ("joiner" in props) {
38
+ joiner = props.joiner;
39
+ } else {
40
+ punctuation =
41
+ props.comma ? ","
42
+ : props.semicolon ? ";"
43
+ : "";
44
+
45
+ joiner = (
46
+ <>
47
+ {punctuation}
48
+ {props.softline ?
49
+ <sbr />
50
+ : props.hardline ?
51
+ <hbr />
52
+ : props.literalline ?
53
+ <lbr />
54
+ : props.line ?
55
+ <br />
56
+ : props.space ?
57
+ <> </>
58
+ : <hbr />}
59
+ </>
60
+ );
61
+ }
62
+
63
+ const ender =
64
+ "ender" in props ? props.ender
65
+ : props.enderPunctuation ? punctuation
66
+ : undefined;
67
+
68
+ return { joiner, ender };
69
+ }
70
+
71
+ export interface ListProps extends BaseListProps {
72
+ children?: Children;
73
+ }
74
+
75
+ /**
76
+ * Create a list of children with text between each child. The text to join with
77
+ * is specified by providing either `joiner` children, or providing boolean
78
+ * props for the punctuation and line breaks. The default joiner is no
79
+ * punctuation and a hard break. The `ender` prop can provide text to place at
80
+ * the end of the list when there is at least one child.
81
+ */
82
+ export function List(props: ListProps) {
83
+ const [rest, forProps] = splitProps(props, ["children"]);
84
+ const resolvedChildren = memo(() =>
85
+ childrenArray(() => rest.children, {
86
+ preserveFragments: true,
87
+ }),
88
+ );
89
+ return (
90
+ <For each={resolvedChildren} {...forProps}>
91
+ {(child) => child}
92
+ </For>
93
+ );
94
+ }
@@ -50,13 +50,16 @@ export function MemberDeclaration(props: MemberDeclarationProps) {
50
50
  declaration = binder.createSymbol({
51
51
  name: props.name!,
52
52
  refkey: rk,
53
- flags: props.static ?
54
- OutputSymbolFlags.StaticMember
55
- : OutputSymbolFlags.InstanceMember,
53
+ flags:
54
+ props.static ?
55
+ OutputSymbolFlags.StaticMember
56
+ : OutputSymbolFlags.InstanceMember,
56
57
  });
57
58
  }
58
59
 
59
- return <MemberDeclarationContext.Provider value={declaration}>
60
- {props.children}
61
- </MemberDeclarationContext.Provider>;
60
+ return (
61
+ <MemberDeclarationContext.Provider value={declaration}>
62
+ {props.children}
63
+ </MemberDeclarationContext.Provider>
64
+ );
62
65
  }
@@ -34,7 +34,9 @@ export function MemberScope(props: MemberScopeProps) {
34
34
  instanceMembers: props.owner.instanceMemberScope,
35
35
  staticMembers: props.owner.staticMemberScope,
36
36
  };
37
- return <MemberScopeContext.Provider value={context}>
37
+ return (
38
+ <MemberScopeContext.Provider value={context}>
38
39
  {props.children}
39
- </MemberScopeContext.Provider>;
40
+ </MemberScopeContext.Provider>
41
+ );
40
42
  }
@@ -5,15 +5,16 @@ import {
5
5
  } from "../binder.js";
6
6
  import { BinderContext } from "../context/binder.js";
7
7
  import { NamePolicyContext } from "../context/name-policy.js";
8
- import { Children } from "../jsx-runtime.js";
8
+ import { Children, getContext } from "../jsx-runtime.js";
9
9
  import { NamePolicy } from "../name-policy.js";
10
+ import { PrintTreeOptions } from "../render.js";
10
11
  import { extensionEffects } from "../slot.js";
11
12
  import { SourceDirectory } from "./SourceDirectory.js";
12
13
 
13
14
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
14
15
  import { SourceFile } from "./SourceFile.js";
15
16
 
16
- export interface OutputProps {
17
+ export interface OutputProps extends PrintTreeOptions {
17
18
  children?: Children;
18
19
  /**
19
20
  * External libraries whose symbols should be available for reference.
@@ -48,10 +49,18 @@ export function Output(props: OutputProps) {
48
49
  const binder = createOutputBinder({
49
50
  nameConflictResolver: props.nameConflictResolver,
50
51
  });
51
- const dir =
52
- <SourceDirectory path={basePath}>
53
- {props.children}
54
- </SourceDirectory>;
52
+
53
+ const nodeContext = getContext()!;
54
+ nodeContext.meta ??= {};
55
+ nodeContext.meta.printOptions = {
56
+ printWidth: props.printWidth,
57
+ tabWidth: props.tabWidth,
58
+ useTabs: props.useTabs,
59
+ };
60
+
61
+ const dir = (
62
+ <SourceDirectory path={basePath}>{props.children}</SourceDirectory>
63
+ );
55
64
 
56
65
  if (props.externals) {
57
66
  for (const global of props.externals) {
@@ -59,13 +68,16 @@ export function Output(props: OutputProps) {
59
68
  }
60
69
  }
61
70
 
62
- return <BinderContext.Provider value={binder}>
63
- {() => { extensionEffects.forEach(e => e())}}{
64
- props.namePolicy ?
71
+ return (
72
+ <BinderContext.Provider value={binder}>
73
+ {() => {
74
+ extensionEffects.forEach((e) => e());
75
+ }}
76
+ {props.namePolicy ?
65
77
  <NamePolicyContext.Provider value={props.namePolicy}>
66
78
  {dir}
67
- </NamePolicyContext.Provider> :
68
- dir
69
- }
70
- </BinderContext.Provider>;
79
+ </NamePolicyContext.Provider>
80
+ : dir}
81
+ </BinderContext.Provider>
82
+ );
71
83
  }
@@ -21,7 +21,9 @@ export function Scope(props: ScopeProps) {
21
21
  scope = binder.createScope({ kind, name: props.name! });
22
22
  }
23
23
 
24
- return <ScopeContext.Provider value={scope}>
24
+ return (
25
+ <ScopeContext.Provider value={scope}>
25
26
  {props.children}
26
- </ScopeContext.Provider>;
27
+ </ScopeContext.Provider>
28
+ );
27
29
  }
@@ -0,0 +1,11 @@
1
+ import { Children } from "@alloy-js/core/jsx-runtime";
2
+
3
+ export interface ShowProps {
4
+ children: Children;
5
+ fallback?: Children;
6
+ when: boolean | undefined | null;
7
+ }
8
+
9
+ export function Show(props: ShowProps) {
10
+ return () => (props.when ? props.children : props.fallback);
11
+ }
@@ -20,7 +20,11 @@ export function SourceDirectory(props: SourceDirectoryProps) {
20
20
  nodeContext.meta.directory = {
21
21
  path: sdPath,
22
22
  };
23
- return <SourceDirectoryContext.Provider value={context}>{props.children}</SourceDirectoryContext.Provider>;
23
+ return (
24
+ <SourceDirectoryContext.Provider value={context}>
25
+ {props.children}
26
+ </SourceDirectoryContext.Provider>
27
+ );
24
28
  }
25
29
 
26
30
  function createSourceDirectoryContext(