@alloy-js/core 0.3.0 → 0.5.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 (147) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/src/binder.d.ts +43 -18
  3. package/dist/src/binder.d.ts.map +1 -1
  4. package/dist/src/binder.js +120 -16
  5. package/dist/src/binder.js.map +1 -1
  6. package/dist/src/components/Declaration.d.ts +2 -2
  7. package/dist/src/components/Declaration.d.ts.map +1 -1
  8. package/dist/src/components/Declaration.js.map +1 -1
  9. package/dist/src/components/Indent.d.ts +2 -2
  10. package/dist/src/components/Indent.d.ts.map +1 -1
  11. package/dist/src/components/Indent.js.map +1 -1
  12. package/dist/src/components/MemberDeclaration.d.ts +2 -2
  13. package/dist/src/components/MemberDeclaration.d.ts.map +1 -1
  14. package/dist/src/components/MemberDeclaration.js.map +1 -1
  15. package/dist/src/components/MemberName.d.ts +1 -1
  16. package/dist/src/components/MemberName.d.ts.map +1 -1
  17. package/dist/src/components/MemberScope.d.ts +2 -2
  18. package/dist/src/components/MemberScope.d.ts.map +1 -1
  19. package/dist/src/components/Name.d.ts +1 -1
  20. package/dist/src/components/Name.d.ts.map +1 -1
  21. package/dist/src/components/Output.d.ts +2 -2
  22. package/dist/src/components/Output.d.ts.map +1 -1
  23. package/dist/src/components/Output.js +6 -2
  24. package/dist/src/components/Output.js.map +1 -1
  25. package/dist/src/components/Scope.d.ts +2 -2
  26. package/dist/src/components/Scope.d.ts.map +1 -1
  27. package/dist/src/components/Scope.js.map +1 -1
  28. package/dist/src/components/SourceDirectory.d.ts +3 -3
  29. package/dist/src/components/SourceDirectory.d.ts.map +1 -1
  30. package/dist/src/components/SourceDirectory.js +1 -1
  31. package/dist/src/components/SourceDirectory.js.map +1 -1
  32. package/dist/src/components/SourceFile.d.ts +3 -3
  33. package/dist/src/components/SourceFile.d.ts.map +1 -1
  34. package/dist/src/components/SourceFile.js +1 -1
  35. package/dist/src/components/SourceFile.js.map +1 -1
  36. package/dist/src/components/stc/index.d.ts +18 -18
  37. package/dist/src/context/assignment.d.ts.map +1 -1
  38. package/dist/src/context/assignment.js +2 -2
  39. package/dist/src/context/assignment.js.map +1 -1
  40. package/dist/src/context/binder.d.ts.map +1 -1
  41. package/dist/src/context/binder.js +2 -2
  42. package/dist/src/context/binder.js.map +1 -1
  43. package/dist/src/context/declaration.d.ts.map +1 -1
  44. package/dist/src/context/declaration.js +2 -2
  45. package/dist/src/context/declaration.js.map +1 -1
  46. package/dist/src/context/indent.d.ts.map +1 -1
  47. package/dist/src/context/indent.js +2 -2
  48. package/dist/src/context/indent.js.map +1 -1
  49. package/dist/src/context/member-declaration.d.ts +1 -0
  50. package/dist/src/context/member-declaration.d.ts.map +1 -1
  51. package/dist/src/context/member-declaration.js +5 -2
  52. package/dist/src/context/member-declaration.js.map +1 -1
  53. package/dist/src/context/member-scope.d.ts.map +1 -1
  54. package/dist/src/context/member-scope.js +2 -2
  55. package/dist/src/context/member-scope.js.map +1 -1
  56. package/dist/src/context/name-policy.d.ts.map +1 -1
  57. package/dist/src/context/name-policy.js +2 -2
  58. package/dist/src/context/name-policy.js.map +1 -1
  59. package/dist/src/context/scope.d.ts.map +1 -1
  60. package/dist/src/context/scope.js +2 -2
  61. package/dist/src/context/scope.js.map +1 -1
  62. package/dist/src/context/source-directory.d.ts.map +1 -1
  63. package/dist/src/context/source-directory.js +2 -2
  64. package/dist/src/context/source-directory.js.map +1 -1
  65. package/dist/src/context/source-file.d.ts.map +1 -1
  66. package/dist/src/context/source-file.js +2 -2
  67. package/dist/src/context/source-file.js.map +1 -1
  68. package/dist/src/context.d.ts +5 -2
  69. package/dist/src/context.d.ts.map +1 -1
  70. package/dist/src/context.js +11 -4
  71. package/dist/src/context.js.map +1 -1
  72. package/dist/src/debug.d.ts +14 -0
  73. package/dist/src/debug.d.ts.map +1 -0
  74. package/dist/src/debug.js +163 -0
  75. package/dist/src/debug.js.map +1 -0
  76. package/dist/src/index.d.ts +3 -1
  77. package/dist/src/index.d.ts.map +1 -1
  78. package/dist/src/index.js +3 -1
  79. package/dist/src/index.js.map +1 -1
  80. package/dist/src/jsx-runtime.d.ts +27 -4
  81. package/dist/src/jsx-runtime.d.ts.map +1 -1
  82. package/dist/src/jsx-runtime.js +20 -7
  83. package/dist/src/jsx-runtime.js.map +1 -1
  84. package/dist/src/render.d.ts +1 -1
  85. package/dist/src/render.d.ts.map +1 -1
  86. package/dist/src/render.js +15 -13
  87. package/dist/src/render.js.map +1 -1
  88. package/dist/src/slot.d.ts +15 -0
  89. package/dist/src/slot.d.ts.map +1 -0
  90. package/dist/src/slot.js +51 -0
  91. package/dist/src/slot.js.map +1 -0
  92. package/dist/src/tap.d.ts +19 -0
  93. package/dist/src/tap.d.ts.map +1 -0
  94. package/dist/src/tap.js +39 -0
  95. package/dist/src/tap.js.map +1 -0
  96. package/dist/src/utils.d.ts +1 -6
  97. package/dist/src/utils.d.ts.map +1 -1
  98. package/dist/src/utils.js +1 -34
  99. package/dist/src/utils.js.map +1 -1
  100. package/dist/src/write-output.browser.d.ts +2 -0
  101. package/dist/src/write-output.browser.d.ts.map +1 -0
  102. package/dist/src/write-output.browser.js +4 -0
  103. package/dist/src/write-output.browser.js.map +1 -0
  104. package/dist/src/write-output.d.ts +7 -0
  105. package/dist/src/write-output.d.ts.map +1 -0
  106. package/dist/src/write-output.js +34 -0
  107. package/dist/src/write-output.js.map +1 -0
  108. package/dist/test/components/slot.test.d.ts +2 -0
  109. package/dist/test/components/slot.test.d.ts.map +1 -0
  110. package/dist/testing/render.d.ts +1 -1
  111. package/dist/testing/render.d.ts.map +1 -1
  112. package/dist/tsconfig.tsbuildinfo +1 -1
  113. package/package.json +10 -6
  114. package/src/binder.ts +214 -50
  115. package/src/components/Declaration.tsx +1 -1
  116. package/src/components/Indent.tsx +1 -1
  117. package/src/components/MemberDeclaration.tsx +1 -1
  118. package/src/components/MemberScope.tsx +1 -1
  119. package/src/components/Output.tsx +4 -2
  120. package/src/components/Scope.tsx +1 -1
  121. package/src/components/SourceDirectory.tsx +2 -2
  122. package/src/components/SourceFile.tsx +2 -6
  123. package/src/context/assignment.ts +6 -2
  124. package/src/context/binder.ts +7 -2
  125. package/src/context/declaration.ts +2 -2
  126. package/src/context/indent.ts +13 -6
  127. package/src/context/member-declaration.ts +10 -2
  128. package/src/context/member-scope.ts +6 -2
  129. package/src/context/name-policy.ts +6 -2
  130. package/src/context/scope.ts +7 -2
  131. package/src/context/source-directory.ts +2 -2
  132. package/src/context/source-file.ts +2 -2
  133. package/src/context.ts +15 -4
  134. package/src/debug.ts +209 -0
  135. package/src/index.ts +3 -1
  136. package/src/jsx-runtime.ts +46 -11
  137. package/src/render.ts +21 -19
  138. package/src/slot.ts +90 -0
  139. package/src/tap.ts +69 -0
  140. package/src/utils.ts +2 -34
  141. package/src/write-output.browser.ts +3 -0
  142. package/src/write-output.ts +33 -0
  143. package/temp/api.json +1848 -203
  144. package/test/components/slot.test.tsx +172 -0
  145. package/test/rendering/basic.test.tsx +1 -1
  146. package/test/symbols.test.ts +161 -3
  147. package/testing/render.ts +1 -1
@@ -1,4 +1,4 @@
1
- import { ComponentContext, createContext } from "../context.js";
1
+ import { ComponentContext, createNamedContext } from "../context.js";
2
2
  import { SourceFileContext } from "./source-file.js";
3
3
 
4
4
  export interface SourceDirectoryContext {
@@ -8,4 +8,4 @@ export interface SourceDirectoryContext {
8
8
  }
9
9
 
10
10
  export const SourceDirectoryContext: ComponentContext<SourceDirectoryContext> =
11
- createContext();
11
+ createNamedContext("SourceDirectory");
@@ -1,4 +1,4 @@
1
- import { ComponentContext, createContext } from "../context.js";
1
+ import { ComponentContext, createNamedContext } from "../context.js";
2
2
  import { ComponentDefinition } from "../jsx-runtime.js";
3
3
  import { Refkey } from "../refkey.js";
4
4
 
@@ -9,4 +9,4 @@ export interface SourceFileContext {
9
9
  }
10
10
 
11
11
  export const SourceFileContext: ComponentContext<SourceFileContext> =
12
- createContext();
12
+ createNamedContext("SourceFile");
package/src/context.ts CHANGED
@@ -1,16 +1,17 @@
1
+ import { shallowRef } from "@vue/reactivity";
1
2
  import {
2
3
  Children,
3
4
  ComponentDefinition,
4
5
  effect,
5
6
  getContext,
6
7
  untrack,
7
- } from "@alloy-js/core/jsx-runtime";
8
- import { shallowRef } from "@vue/reactivity";
8
+ } from "./jsx-runtime.js";
9
9
 
10
10
  export interface ComponentContext<T> {
11
11
  id: symbol;
12
12
  default: T | undefined;
13
13
  Provider: ComponentDefinition<ContextProviderProps<T>>;
14
+ name?: string;
14
15
  }
15
16
 
16
17
  export interface ContextProviderProps<T = unknown> {
@@ -31,13 +32,17 @@ export function useContext<T>(context: ComponentContext<T>): T | undefined {
31
32
  return context.default;
32
33
  }
33
34
 
35
+ export const contextsByKey = new Map<symbol, ComponentContext<any>>();
36
+
34
37
  export function createContext<T = unknown>(
35
38
  defaultValue?: T,
39
+ name?: string,
36
40
  ): ComponentContext<T> {
37
- const id = Symbol("context");
38
- return {
41
+ const id = Symbol(name ?? "context");
42
+ const ctx = {
39
43
  id,
40
44
  default: defaultValue,
45
+ name,
41
46
  Provider(props: ContextProviderProps<T>) {
42
47
  const context = getContext();
43
48
 
@@ -50,4 +55,10 @@ export function createContext<T = unknown>(
50
55
  return rendered.value;
51
56
  },
52
57
  };
58
+ contextsByKey.set(id, ctx);
59
+ return ctx;
60
+ }
61
+
62
+ export function createNamedContext<T>(name: string, defaultValue?: T) {
63
+ return createContext<T>(defaultValue, name);
53
64
  }
package/src/debug.ts ADDED
@@ -0,0 +1,209 @@
1
+ import { isReactive } from "@vue/reactivity";
2
+ import { Chalk } from "chalk";
3
+ import Table from "cli-table3";
4
+ import { contextsByKey } from "./context.js";
5
+ import { Context, getContext } from "./jsx-runtime.js";
6
+
7
+ interface DebugInterface {
8
+ component: {
9
+ stack(): void;
10
+ tree(): void;
11
+ watch(): void;
12
+ render(): void;
13
+ context(): void;
14
+ };
15
+ }
16
+
17
+ const debug: DebugInterface = {
18
+ component: {
19
+ stack: debugStack,
20
+ tree() {
21
+ console.log("tree");
22
+ },
23
+ watch() {
24
+ console.log("watch");
25
+ },
26
+ render() {
27
+ console.log("render");
28
+ },
29
+ context: debugContext,
30
+ },
31
+ };
32
+
33
+ function debugStack() {
34
+ let currentContext = getContext();
35
+ let foundContexts: Context[] = [];
36
+ while (currentContext !== null) {
37
+ if (
38
+ currentContext.context &&
39
+ Object.getOwnPropertySymbols(currentContext.context)[0]
40
+ ) {
41
+ foundContexts.push(currentContext);
42
+ }
43
+
44
+ if (
45
+ currentContext.componentOwner &&
46
+ currentContext.componentOwner.component.name !== "Provider"
47
+ ) {
48
+ process.stdout.write(
49
+ style.component.name(currentContext.componentOwner.component.name) +
50
+ "\n",
51
+ );
52
+ const table = kvTable();
53
+ const props = currentContext.componentOwner.props;
54
+
55
+ table.push([
56
+ { hAlign: "right", content: "props" },
57
+ props && Object.keys(props).length > 0 ?
58
+ dumpValue(props)
59
+ : chalk.gray("(none)"),
60
+ ]);
61
+
62
+ table.push([
63
+ { hAlign: "right", content: "contexts" },
64
+ foundContexts.length > 0 ?
65
+ foundContexts.map((c) => printContext(c, true)).join("\n")
66
+ : chalk.gray("(none)"),
67
+ ]);
68
+
69
+ process.stdout.write(table.toString() + "\n\n");
70
+ foundContexts = [];
71
+ }
72
+
73
+ currentContext = currentContext.owner;
74
+ }
75
+ }
76
+
77
+ function debugContext() {
78
+ let currentContext = getContext();
79
+ while (currentContext !== null) {
80
+ console.log(printContext(currentContext));
81
+ currentContext = currentContext.owner;
82
+ }
83
+ }
84
+ function printContext(context: Context, omitOwner: boolean = false) {
85
+ if (!context.context) return "";
86
+ const key = Object.getOwnPropertySymbols(context.context)[0];
87
+ if (!key) return "";
88
+ const contextDefinition = contextsByKey.get(key);
89
+ const contextName = contextDefinition?.name ?? "unknown context";
90
+ const value = context.context[key];
91
+
92
+ let output = style.context.name(contextName);
93
+ if (!omitOwner) {
94
+ const owner = findContextOwner(context);
95
+ output += " provided by " + style.component.name(owner);
96
+ }
97
+
98
+ output += "\n" + dumpValue(value) + "\n";
99
+
100
+ return output;
101
+ }
102
+
103
+ function findContextOwner(context: Context) {
104
+ let currentContext: Context | null = context;
105
+ while (
106
+ currentContext &&
107
+ (currentContext.componentOwner === undefined ||
108
+ currentContext.componentOwner.component.name === "Provider")
109
+ ) {
110
+ currentContext = currentContext.owner;
111
+ }
112
+
113
+ return currentContext?.componentOwner?.component.name ?? "unknown";
114
+ }
115
+ declare global {
116
+ // eslint-disable-next-line no-var
117
+ var debug: DebugInterface;
118
+ }
119
+
120
+ globalThis.debug = debug;
121
+
122
+ const chalk = new Chalk();
123
+ const style = {
124
+ value: {
125
+ primitive(value: string | number | boolean | null | undefined) {
126
+ switch (typeof value) {
127
+ case "string":
128
+ return chalk.blue(`"${value}"`);
129
+ case "object":
130
+ case "undefined":
131
+ return chalk.gray(String(value));
132
+ default:
133
+ return chalk.blue(String(value));
134
+ }
135
+ },
136
+ symbol(value: symbol) {
137
+ return chalk.gray(String(value));
138
+ },
139
+ },
140
+ context: {
141
+ name(name: string) {
142
+ return chalk.bgGray(` ${chalk.white(name)} `);
143
+ },
144
+ },
145
+ component: {
146
+ name(name: string) {
147
+ return chalk.bgBlue(` <${chalk.white(name)}> `);
148
+ },
149
+ },
150
+ };
151
+
152
+ function reactiveTag(value: unknown) {
153
+ if (isReactive(value)) {
154
+ return " " + chalk.greenBright(`reactive`) + " ";
155
+ }
156
+ return "";
157
+ }
158
+
159
+ function dumpValue(value: unknown, level = 0) {
160
+ switch (typeof value) {
161
+ case "boolean":
162
+ case "string":
163
+ case "number":
164
+ return style.value.primitive(value) + reactiveTag(value);
165
+ case "symbol":
166
+ return style.value.symbol(value) + reactiveTag(value);
167
+ case "object":
168
+ if (value === null) {
169
+ return style.value.primitive(null) + reactiveTag(value);
170
+ } else {
171
+ if (level > 0) return chalk.gray(`{ ... }` + reactiveTag(value));
172
+
173
+ const table = kvTable(" ");
174
+
175
+ for (const [key, propValue] of Object.entries(value)) {
176
+ table.push([{ content: key }, dumpValue(propValue, level + 1)]);
177
+ }
178
+
179
+ return table.toString();
180
+ }
181
+ case "function":
182
+ return chalk.gray("ƒ ()");
183
+ case "undefined":
184
+ return style.value.primitive(undefined);
185
+ }
186
+ }
187
+
188
+ function kvTable(sep = " ") {
189
+ return new Table({
190
+ chars: {
191
+ top: "",
192
+ "top-mid": "",
193
+ "top-left": "",
194
+ "top-right": "",
195
+ bottom: "",
196
+ "bottom-mid": "",
197
+ "bottom-left": "",
198
+ "bottom-right": "",
199
+ left: "",
200
+ "left-mid": "",
201
+ mid: "",
202
+ "mid-mid": "",
203
+ right: "",
204
+ "right-mid": "",
205
+ middle: sep,
206
+ },
207
+ style: { "padding-left": 0, "padding-right": 0 },
208
+ });
209
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from "@alloy-js/core/jsx-runtime";
2
1
  export {
3
2
  computed,
4
3
  isProxy,
@@ -18,4 +17,7 @@ export * from "./jsx-runtime.js";
18
17
  export * from "./name-policy.js";
19
18
  export * from "./refkey.js";
20
19
  export * from "./render.js";
20
+ export * from "./tap.js";
21
21
  export * from "./utils.js";
22
+ export * from "./write-output.js";
23
+ import "./debug.js";
@@ -30,6 +30,12 @@ export interface Context {
30
30
 
31
31
  // store random info about the node
32
32
  meta?: Record<string, any>;
33
+
34
+ /**
35
+ * When this context was created by a component, this will
36
+ * be the component that created it.
37
+ */
38
+ componentOwner?: ComponentCreator<unknown>;
33
39
  }
34
40
 
35
41
  let globalContext: Context | null = null;
@@ -37,13 +43,16 @@ export function getContext() {
37
43
  return globalContext;
38
44
  }
39
45
 
40
- export function root<T>(fn: (d: Disposable) => T, src?: string): T {
46
+ export function root<T>(
47
+ fn: (d: Disposable) => T,
48
+ componentOwner?: ComponentCreator<any>,
49
+ ): T {
41
50
  globalContext = {
42
- src,
51
+ componentOwner,
43
52
  disposables: [],
44
53
  owner: globalContext,
45
54
  context: {},
46
- } as any;
55
+ };
47
56
  let ret;
48
57
  try {
49
58
  ret = untrack(() =>
@@ -108,7 +117,7 @@ export function effect<T>(fn: (prev?: T) => T, current?: T) {
108
117
  cleanup(() => cleanupFn(true));
109
118
  }
110
119
 
111
- function cleanup(fn: Disposable) {
120
+ export function cleanup(fn: Disposable) {
112
121
  if (globalContext != null) {
113
122
  globalContext.disposables.push(fn);
114
123
  }
@@ -120,13 +129,14 @@ export type Child =
120
129
  | number
121
130
  | undefined
122
131
  | null
123
- | (() => Child | Children)
132
+ | void
133
+ | (() => Children)
124
134
  | Child[]
125
135
  | Ref
126
136
  | Refkey;
127
137
 
128
138
  export type Children = Child | Child[];
129
- export type Props = Record<string, unknown>;
139
+ export type Props = Record<string, any>;
130
140
 
131
141
  export interface ComponentDefinition<TProps = Props> {
132
142
  (props: TProps): Child | Children;
@@ -148,20 +158,18 @@ const renderStack: {
148
158
  props: Props;
149
159
  }[] = [];
150
160
 
151
- export const shouldDebug = !!process.env.ALLOY_DEBUG;
152
-
153
161
  export function pushStack(component: Component<any>, props: Props) {
154
- if (!shouldDebug) return;
162
+ if (!shouldDebug()) return;
155
163
  renderStack.push({ component, props });
156
164
  }
157
165
 
158
166
  export function popStack() {
159
- if (!shouldDebug) return;
167
+ if (!shouldDebug()) return;
160
168
  renderStack.pop();
161
169
  }
162
170
 
163
171
  export function printRenderStack() {
164
- if (!shouldDebug) return;
172
+ if (!shouldDebug()) return;
165
173
 
166
174
  // eslint-disable-next-line no-console
167
175
  console.error("Error rendering:");
@@ -205,6 +213,29 @@ export function isComponentCreator(item: unknown): item is ComponentCreator {
205
213
  return typeof item === "function" && (item as any).component;
206
214
  }
207
215
 
216
+ /**
217
+ * This namespace is predominantly for interop with React tooling in VSCode
218
+ * and controls the type of JSX elements, components, and the like.
219
+ */
220
+ // eslint-disable-next-line @typescript-eslint/no-namespace
221
+ export namespace JSX {
222
+ export interface IntrinsicElements {}
223
+ export type ElementType = ComponentDefinition<any>;
224
+ export type Element = Children;
225
+ export interface ElementChildrenAttribute {
226
+ children: {};
227
+ }
228
+ export interface ElementAttributesProperty {
229
+ props: {};
230
+ }
231
+ }
232
+
233
+ export function jsx(type: Component<any>, props: Record<string, unknown>) {
234
+ return createComponent(type, props);
235
+ }
236
+
237
+ export const jsxs = jsx;
238
+
208
239
  export function createComponent<TProps extends Props = Props>(
209
240
  C: Component<TProps>,
210
241
  props: TProps,
@@ -264,3 +295,7 @@ export function mergeProps(...sources: any): any {
264
295
  }
265
296
  return target;
266
297
  }
298
+
299
+ function shouldDebug() {
300
+ return typeof process !== "undefined" && !!process.env?.ALLOY_DEBUG;
301
+ }
package/src/render.ts CHANGED
@@ -1,3 +1,8 @@
1
+ import { isRef } from "@vue/reactivity";
2
+ import { Indent, IndentState } from "./components/Indent.js";
3
+ import { useContext } from "./context.js";
4
+ import { IndentContext } from "./context/indent.js";
5
+ import { SourceFileContext } from "./context/source-file.js";
1
6
  import {
2
7
  Child,
3
8
  Children,
@@ -10,12 +15,7 @@ import {
10
15
  pushStack,
11
16
  root,
12
17
  untrack,
13
- } from "@alloy-js/core/jsx-runtime";
14
- import { isRef } from "@vue/reactivity";
15
- import { Indent, IndentState } from "./components/Indent.js";
16
- import { useContext } from "./context.js";
17
- import { IndentContext } from "./context/indent.js";
18
- import { SourceFileContext } from "./context/source-file.js";
18
+ } from "./jsx-runtime.js";
19
19
  import { isRefkey } from "./refkey.js";
20
20
 
21
21
  /**
@@ -171,7 +171,7 @@ export type RenderStructure = {};
171
171
 
172
172
  export type RenderTextTree = (string | RenderTextTree)[];
173
173
 
174
- function traceRender(phase: string, message: string) {
174
+ function traceRender(phase: string, message: () => string) {
175
175
  return false;
176
176
  // console.log(`[\x1b[34m${phase}\x1b[0m]: ${message}`);
177
177
  }
@@ -250,7 +250,7 @@ export function renderTree(children: Children) {
250
250
  try {
251
251
  root(() => {
252
252
  renderWorker(rootElem, children, state);
253
- }, "render worker");
253
+ });
254
254
  } catch (e) {
255
255
  printRenderStack();
256
256
  throw e;
@@ -268,7 +268,7 @@ function renderWorker(
268
268
  children: Children,
269
269
  state: RenderState,
270
270
  ) {
271
- traceRender("render", dumpChildren(children));
271
+ traceRender("render", () => dumpChildren(children));
272
272
 
273
273
  if (Array.isArray(node)) {
274
274
  nodesToContext.set(node, getContext()!);
@@ -290,7 +290,7 @@ function appendChild(
290
290
  indentState: IndentState,
291
291
  state: RenderState,
292
292
  ) {
293
- traceRender("appendChild", printChild(rawChild));
293
+ traceRender("appendChild", () => printChild(rawChild));
294
294
  const child = normalizeChild(rawChild);
295
295
 
296
296
  if (typeof child === "string") {
@@ -300,11 +300,11 @@ function appendChild(
300
300
  state.newline = false;
301
301
  }
302
302
  const reindented = reindent(child, indentState.indentString);
303
- traceRender("appendChild:string", JSON.stringify(reindented));
303
+ traceRender("appendChild:string", () => JSON.stringify(reindented));
304
304
  node.push(reindented);
305
305
  } else if (isComponentCreator(child)) {
306
306
  root(() => {
307
- traceRender("appendChild:component", printChild(child));
307
+ traceRender("appendChild:component", () => printChild(child));
308
308
  if (child.component === Indent && state.newline) {
309
309
  node.push(indentState.indent);
310
310
  }
@@ -313,13 +313,13 @@ function appendChild(
313
313
  renderWorker(componentRoot, untrack(child), state);
314
314
  popStack();
315
315
  node.push(componentRoot);
316
- traceRender("appendChild:component-done", printChild(child));
317
- }, child.component.name);
316
+ traceRender("appendChild:component-done", () => printChild(child));
317
+ }, child);
318
318
  } else if (typeof child === "function") {
319
- traceRender("appendChild:memo", child.toString());
319
+ traceRender("appendChild:memo", () => child.toString());
320
320
  const index = node.length;
321
321
  effect((prev: any) => {
322
- traceRender("memoEffect:run", "");
322
+ traceRender("memoEffect:run", () => "");
323
323
  let res = child();
324
324
  while (typeof res === "function" && !isComponentCreator(res)) {
325
325
  res = res();
@@ -330,11 +330,11 @@ function appendChild(
330
330
  node[index] = newNodes;
331
331
  return newNodes;
332
332
  });
333
- traceRender("appendChild:memo-done", "");
333
+ traceRender("appendChild:memo-done", () => "");
334
334
  } else {
335
- traceRender("appendChild:array", dumpChildren(child));
335
+ traceRender("appendChild:array", () => dumpChildren(child));
336
336
  renderWorker(node, child, state);
337
- traceRender("appendChild:array-done", dumpChildren(child));
337
+ traceRender("appendChild:array-done", () => dumpChildren(child));
338
338
  }
339
339
  }
340
340
 
@@ -383,6 +383,8 @@ function printChild(child: Child): string {
383
383
  return "<" + child.component.name + ">";
384
384
  } else if (typeof child === "function") {
385
385
  return "$memo";
386
+ } else if (isRef(child)) {
387
+ return "$ref";
386
388
  } else {
387
389
  return JSON.stringify(child);
388
390
  }
package/src/slot.ts ADDED
@@ -0,0 +1,90 @@
1
+ import { ref, Ref } from "@vue/reactivity";
2
+ import { OutputSymbol } from "./binder.js";
3
+ import {
4
+ Children,
5
+ Component,
6
+ ComponentDefinition,
7
+ effect,
8
+ memo,
9
+ } from "./jsx-runtime.js";
10
+
11
+ export interface SlotInstance extends ComponentDefinition {}
12
+
13
+ export interface SlotDefinition<TSlotProps> {
14
+ find: (...args: any[]) => () => Ref<unknown>;
15
+ create(
16
+ key: unknown,
17
+ props: TSlotProps,
18
+ defaultContent?: Children,
19
+ ): ComponentDefinition;
20
+ }
21
+
22
+ export type SlotKey = Ref<unknown>;
23
+
24
+ const slotMappers = new Map<unknown, ComponentDefinition<any>>();
25
+
26
+ export function defineSlot<
27
+ TSlotProps,
28
+ TFinder extends (...args: any[]) => Ref<unknown> | unknown = (
29
+ ...args: any[]
30
+ ) => Ref<unknown> | unknown,
31
+ >(finder: TFinder): SlotDefinition<TSlotProps> {
32
+ return {
33
+ find: ((...args: any[]) =>
34
+ () =>
35
+ ref(finder(...args))) as any,
36
+ create(key, props, defaultContent) {
37
+ return function () {
38
+ return memo(() => {
39
+ if (key === undefined) {
40
+ return defaultContent;
41
+ }
42
+
43
+ const component = slotMappers.get(key);
44
+ if (!component) {
45
+ return defaultContent;
46
+ }
47
+
48
+ return component({ ...props, original: defaultContent });
49
+ });
50
+ };
51
+ },
52
+ };
53
+ }
54
+
55
+ export const extensionEffects: (() => void)[] = [];
56
+
57
+ export function replace<T>(
58
+ slotKeyFn: () => SlotKey,
59
+ replacement: Component<T>,
60
+ ) {
61
+ extensionEffects.push(() => {
62
+ effect((prev: SlotKey | undefined) => {
63
+ if (prev) {
64
+ slotMappers.delete(prev.value);
65
+ }
66
+
67
+ const slotKey = slotKeyFn();
68
+ if (slotKey.value === undefined) {
69
+ return slotKey;
70
+ }
71
+
72
+ slotMappers.set(slotKey.value, replacement);
73
+
74
+ return slotKey;
75
+ });
76
+ });
77
+ }
78
+
79
+ export function rename(
80
+ slotKeyFn: () => Ref<OutputSymbol | undefined>,
81
+ newName: string,
82
+ ) {
83
+ extensionEffects.push(() => {
84
+ effect(() => {
85
+ const sym = slotKeyFn().value;
86
+ if (!sym) return;
87
+ sym.name = newName;
88
+ });
89
+ });
90
+ }
package/src/tap.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { ShallowRef, shallowRef } from "@vue/reactivity";
2
+ import { OutputScope, OutputSymbol } from "./binder.js";
3
+ import { useContext } from "./context.js";
4
+ import { DeclarationContext } from "./context/declaration.js";
5
+ import { MemberDeclarationContext } from "./context/member-declaration.js";
6
+ import { useScope } from "./context/scope.js";
7
+ import { SourceFileContext } from "./context/source-file.js";
8
+ import { ComponentDefinition } from "./jsx-runtime.js";
9
+
10
+ export interface Tap<T> extends ComponentDefinition {
11
+ ref: ShallowRef<T | undefined>;
12
+ }
13
+
14
+ export interface Tapper<T> {
15
+ (): T | undefined;
16
+ }
17
+
18
+ export interface Handler<T> {
19
+ (value: T): void;
20
+ }
21
+
22
+ export function createTap<T = unknown>(
23
+ tapper: Tapper<T>,
24
+ handler?: Handler<T>,
25
+ ): Tap<T> {
26
+ const signal = shallowRef<T | undefined>(undefined);
27
+ const fn = () => {
28
+ signal.value = tapper();
29
+ if (handler && signal.value !== undefined) {
30
+ handler(signal.value);
31
+ }
32
+
33
+ return "";
34
+ };
35
+
36
+ fn.ref = signal;
37
+
38
+ return fn;
39
+ }
40
+
41
+ export function createDeclarationTap<
42
+ TSymbol extends OutputSymbol = OutputSymbol,
43
+ >(handler?: Handler<TSymbol>) {
44
+ return createTap<TSymbol>(() => {
45
+ return useContext(DeclarationContext) as any;
46
+ }, handler);
47
+ }
48
+
49
+ export function createMemberTap<TSymbol extends OutputSymbol = OutputSymbol>(
50
+ handler?: Handler<TSymbol>,
51
+ ) {
52
+ return createTap<TSymbol>(() => {
53
+ return useContext(MemberDeclarationContext) as any;
54
+ }, handler);
55
+ }
56
+
57
+ export function createScopeTap<TScope extends OutputScope = OutputScope>(
58
+ handler?: Handler<TScope>,
59
+ ) {
60
+ return createTap<TScope>(() => {
61
+ return useScope() as any;
62
+ }, handler);
63
+ }
64
+
65
+ export function createSourceFileTap(handler?: Handler<SourceFileContext>) {
66
+ return createTap(() => {
67
+ return useContext(SourceFileContext);
68
+ }, handler);
69
+ }