@alloy-js/core 0.9.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.
Files changed (162) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/src/binder.js +33 -2
  3. package/dist/src/code.js +1 -2
  4. package/dist/src/components/Block.js +2 -5
  5. package/dist/src/components/Declaration.js +2 -4
  6. package/dist/src/components/For.d.ts +1 -1
  7. package/dist/src/components/For.d.ts.map +1 -1
  8. package/dist/src/components/For.js +1 -2
  9. package/dist/src/components/Indent.js +2 -4
  10. package/dist/src/components/List.js +2 -5
  11. package/dist/src/components/MemberDeclaration.js +2 -4
  12. package/dist/src/components/MemberName.js +1 -2
  13. package/dist/src/components/MemberScope.js +2 -4
  14. package/dist/src/components/Name.js +1 -2
  15. package/dist/src/components/Output.js +2 -4
  16. package/dist/src/components/Prose.js +1 -2
  17. package/dist/src/components/ReferenceOrContent.d.ts +8 -0
  18. package/dist/src/components/ReferenceOrContent.d.ts.map +1 -0
  19. package/dist/src/components/ReferenceOrContent.js +11 -0
  20. package/dist/src/components/Scope.js +2 -4
  21. package/dist/src/components/Show.js +1 -2
  22. package/dist/src/components/SourceDirectory.js +2 -4
  23. package/dist/src/components/SourceFile.js +2 -5
  24. package/dist/src/components/StatementList.js +2 -4
  25. package/dist/src/components/Switch.d.ts +1 -1
  26. package/dist/src/components/Switch.d.ts.map +1 -1
  27. package/dist/src/components/Switch.js +1 -2
  28. package/dist/src/components/Wrap.js +2 -4
  29. package/dist/src/components/index.d.ts +1 -0
  30. package/dist/src/components/index.d.ts.map +1 -1
  31. package/dist/src/components/index.js +2 -2
  32. package/dist/src/components/stc/index.d.ts +1 -0
  33. package/dist/src/components/stc/index.d.ts.map +1 -1
  34. package/dist/src/components/stc/index.js +2 -2
  35. package/dist/src/components/stc/sti.js +1 -2
  36. package/dist/src/context/assignment.js +1 -2
  37. package/dist/src/context/binder.js +1 -2
  38. package/dist/src/context/declaration.js +1 -2
  39. package/dist/src/context/index.js +1 -2
  40. package/dist/src/context/member-declaration.js +1 -2
  41. package/dist/src/context/member-scope.js +1 -2
  42. package/dist/src/context/name-policy.js +1 -2
  43. package/dist/src/context/scope.js +1 -2
  44. package/dist/src/context/source-directory.js +1 -2
  45. package/dist/src/context/source-file.js +1 -2
  46. package/dist/src/context.js +1 -2
  47. package/dist/src/debug.js +13 -15
  48. package/dist/src/index.browser.js +1 -2
  49. package/dist/src/index.js +1 -2
  50. package/dist/src/jsx-runtime.d.ts +1 -1
  51. package/dist/src/jsx-runtime.d.ts.map +1 -1
  52. package/dist/src/jsx-runtime.js +1 -2
  53. package/dist/src/name-policy.js +1 -2
  54. package/dist/src/refkey.js +1 -2
  55. package/dist/src/render.js +1 -2
  56. package/dist/src/slot.js +1 -2
  57. package/dist/src/stc.js +1 -2
  58. package/dist/src/sti.js +1 -2
  59. package/dist/src/tap.js +1 -2
  60. package/dist/src/tsdoc-metadata.json +1 -1
  61. package/dist/src/utils.js +2 -4
  62. package/dist/src/write-output.browser.js +1 -2
  63. package/dist/src/write-output.js +1 -2
  64. package/dist/test/browser-build.test.js +85 -0
  65. package/dist/test/children.test.js +27 -0
  66. package/dist/test/components/block.test.js +45 -0
  67. package/dist/test/components/declaration.test.js +30 -0
  68. package/dist/test/components/list.test.js +86 -0
  69. package/dist/test/components/prose.test.js +25 -0
  70. package/dist/test/components/reference-or-content.test.d.ts +2 -0
  71. package/dist/test/components/reference-or-content.test.d.ts.map +1 -0
  72. package/dist/test/components/reference-or-content.test.js +149 -0
  73. package/dist/test/components/slot.test.js +134 -0
  74. package/dist/test/components/source-file.test.js +64 -0
  75. package/dist/test/components/wrap.test.js +35 -0
  76. package/dist/test/control-flow/for.test.js +185 -0
  77. package/dist/test/control-flow/match.test.js +67 -0
  78. package/dist/test/control-flow/show.test.js +29 -0
  79. package/dist/test/name-policy.test.js +19 -0
  80. package/dist/test/props-with-defaults.test.js +93 -0
  81. package/dist/test/reactivity/cleanup.test.js +77 -0
  82. package/dist/test/reactivity/memo.test.js +16 -0
  83. package/dist/test/reactivity/ref-rendering.test.js +37 -0
  84. package/dist/test/reactivity/test.test.js +61 -0
  85. package/dist/test/reactivity/untrack.test.js +23 -0
  86. package/dist/test/refkey.test.js +25 -0
  87. package/dist/test/rendering/basic.test.js +96 -0
  88. package/dist/test/rendering/code.test.js +55 -0
  89. package/dist/test/rendering/formatting.test.js +402 -0
  90. package/dist/test/rendering/indent.test.js +90 -0
  91. package/dist/test/rendering/memoization.test.js +25 -0
  92. package/dist/test/rendering/refkeys.test.js +32 -0
  93. package/dist/test/split-props.test.js +77 -0
  94. package/dist/test/stc.test.js +34 -0
  95. package/dist/test/symbols.test.js +504 -0
  96. package/dist/test/utils.test.js +221 -0
  97. package/dist/testing/extend-expect.js +1 -2
  98. package/dist/testing/index.js +1 -2
  99. package/dist/testing/render.js +1 -2
  100. package/dist/tsconfig.tsbuildinfo +1 -1
  101. package/package.json +14 -22
  102. package/src/components/For.tsx +2 -2
  103. package/src/components/ReferenceOrContent.tsx +22 -0
  104. package/src/components/index.tsx +1 -0
  105. package/src/components/stc/index.ts +1 -0
  106. package/src/debug.ts +12 -13
  107. package/src/jsx-runtime.ts +2 -2
  108. package/temp/api.json +208 -7
  109. package/test/components/reference-or-content.test.tsx +138 -0
  110. package/babel.config.cjs +0 -4
  111. package/dist/src/binder.js.map +0 -1
  112. package/dist/src/code.js.map +0 -1
  113. package/dist/src/components/Block.js.map +0 -1
  114. package/dist/src/components/Declaration.js.map +0 -1
  115. package/dist/src/components/For.js.map +0 -1
  116. package/dist/src/components/Indent.js.map +0 -1
  117. package/dist/src/components/List.js.map +0 -1
  118. package/dist/src/components/MemberDeclaration.js.map +0 -1
  119. package/dist/src/components/MemberName.js.map +0 -1
  120. package/dist/src/components/MemberScope.js.map +0 -1
  121. package/dist/src/components/Name.js.map +0 -1
  122. package/dist/src/components/Output.js.map +0 -1
  123. package/dist/src/components/Prose.js.map +0 -1
  124. package/dist/src/components/Scope.js.map +0 -1
  125. package/dist/src/components/Show.js.map +0 -1
  126. package/dist/src/components/SourceDirectory.js.map +0 -1
  127. package/dist/src/components/SourceFile.js.map +0 -1
  128. package/dist/src/components/StatementList.js.map +0 -1
  129. package/dist/src/components/Switch.js.map +0 -1
  130. package/dist/src/components/Wrap.js.map +0 -1
  131. package/dist/src/components/index.js.map +0 -1
  132. package/dist/src/components/stc/index.js.map +0 -1
  133. package/dist/src/components/stc/sti.js.map +0 -1
  134. package/dist/src/context/assignment.js.map +0 -1
  135. package/dist/src/context/binder.js.map +0 -1
  136. package/dist/src/context/declaration.js.map +0 -1
  137. package/dist/src/context/index.js.map +0 -1
  138. package/dist/src/context/member-declaration.js.map +0 -1
  139. package/dist/src/context/member-scope.js.map +0 -1
  140. package/dist/src/context/name-policy.js.map +0 -1
  141. package/dist/src/context/scope.js.map +0 -1
  142. package/dist/src/context/source-directory.js.map +0 -1
  143. package/dist/src/context/source-file.js.map +0 -1
  144. package/dist/src/context.js.map +0 -1
  145. package/dist/src/debug.js.map +0 -1
  146. package/dist/src/index.browser.js.map +0 -1
  147. package/dist/src/index.js.map +0 -1
  148. package/dist/src/jsx-runtime.js.map +0 -1
  149. package/dist/src/name-policy.js.map +0 -1
  150. package/dist/src/refkey.js.map +0 -1
  151. package/dist/src/render.js.map +0 -1
  152. package/dist/src/slot.js.map +0 -1
  153. package/dist/src/stc.js.map +0 -1
  154. package/dist/src/sti.js.map +0 -1
  155. package/dist/src/tap.js.map +0 -1
  156. package/dist/src/utils.js.map +0 -1
  157. package/dist/src/write-output.browser.js.map +0 -1
  158. package/dist/src/write-output.js.map +0 -1
  159. package/dist/testing/extend-expect.js.map +0 -1
  160. package/dist/testing/index.js.map +0 -1
  161. package/dist/testing/render.js.map +0 -1
  162. package/dist/testing/vitest.d.js.map +0 -1
@@ -0,0 +1,504 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createOutputBinder, OutputScopeFlags, OutputSymbolFlags } from "../src/binder.js";
3
+ import { refkey } from "../src/refkey.js";
4
+ it("works", () => {
5
+ const binder = createOutputBinder();
6
+ const scope = binder.createScope({
7
+ kind: "foo",
8
+ name: "scope",
9
+ parent: binder.globalScope
10
+ });
11
+ const symbol = binder.createSymbol({
12
+ name: "sym",
13
+ scope
14
+ });
15
+ expect([...scope.getSymbolNames()]).toEqual(["sym"]);
16
+ symbol.name = "bar";
17
+ expect([...scope.getSymbolNames()]).toEqual(["bar"]);
18
+ });
19
+ it("resolves symbol conflicts", () => {
20
+ const binder = createOutputBinder();
21
+ const scope = binder.createScope({
22
+ kind: "foo",
23
+ name: "scope",
24
+ parent: binder.globalScope
25
+ });
26
+ const _s1 = binder.createSymbol({
27
+ name: "sym",
28
+ scope
29
+ });
30
+ const s2 = binder.createSymbol({
31
+ name: "sym",
32
+ scope
33
+ });
34
+ expect(s2.name).toEqual("sym_2");
35
+ });
36
+ function createScopeTree(binder, tree) {
37
+ const createdItems = {
38
+ symbols: {},
39
+ scopes: {}
40
+ };
41
+ for (const [name, desc] of Object.entries(tree)) {
42
+ createScope(name, desc);
43
+ }
44
+ return createdItems;
45
+ function createScope(name, descriptor, parent = binder.globalScope) {
46
+ const scope = binder.createScope({
47
+ kind: "useless",
48
+ name,
49
+ parent,
50
+ flags: descriptor.flags ?? OutputScopeFlags.None
51
+ });
52
+ createdItems.scopes[name] = scope;
53
+ for (const [name, desc] of Object.entries(descriptor.symbols)) {
54
+ createSymbol(name, desc, scope);
55
+ }
56
+ for (const [name, desc] of Object.entries(descriptor.scopes ?? {})) {
57
+ createScope(name, desc, scope);
58
+ }
59
+ }
60
+ function createSymbol(name, descriptor, parent) {
61
+ const symbol = binder.createSymbol({
62
+ name,
63
+ scope: parent,
64
+ refkey: descriptor.refkey ?? refkey(),
65
+ flags: descriptor.flags ?? OutputSymbolFlags.None
66
+ });
67
+ createdItems.symbols[name] = symbol;
68
+ if (descriptor.instanceMembers) {
69
+ for (const [name, desc] of Object.entries(descriptor.instanceMembers)) {
70
+ createSymbol(name, desc, symbol.instanceMemberScope);
71
+ }
72
+ }
73
+ if (descriptor.staticMembers) {
74
+ for (const [name, desc] of Object.entries(descriptor.staticMembers)) {
75
+ createSymbol(name, desc, symbol.staticMemberScope);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ describe("static members", () => {
81
+ it("resolves static symbols", () => {
82
+ const binder = createOutputBinder();
83
+ const {
84
+ scopes: {
85
+ root
86
+ },
87
+ symbols: {
88
+ root: rootSym,
89
+ static: staticSym
90
+ }
91
+ } = createScopeTree(binder, {
92
+ root: {
93
+ symbols: {
94
+ root: {
95
+ flags: OutputSymbolFlags.InstanceMemberContainer | OutputSymbolFlags.StaticMemberContainer,
96
+ staticMembers: {
97
+ static: {
98
+ flags: OutputSymbolFlags.StaticMember
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ });
105
+ const resolution = binder.resolveDeclarationByKey(root, undefined, staticSym.refkeys[0]);
106
+ expect(resolution.value).toBeDefined();
107
+ const {
108
+ commonScope,
109
+ pathUp,
110
+ pathDown,
111
+ targetDeclaration,
112
+ memberPath
113
+ } = resolution.value;
114
+ expect(commonScope).toBe(root);
115
+ expect(targetDeclaration).toBe(rootSym);
116
+ expect(pathDown).toEqual([]);
117
+ expect(pathUp).toEqual([]);
118
+ expect(memberPath.map(s => s.name)).toEqual(["root", "static"]);
119
+ });
120
+ it("resolves deeply nested static symbols", () => {
121
+ const binder = createOutputBinder();
122
+ const {
123
+ scopes: {
124
+ root
125
+ },
126
+ symbols: {
127
+ root: rootSym,
128
+ nested_static
129
+ }
130
+ } = createScopeTree(binder, {
131
+ root: {
132
+ symbols: {
133
+ root: {
134
+ flags: OutputSymbolFlags.InstanceMemberContainer | OutputSymbolFlags.StaticMemberContainer,
135
+ staticMembers: {
136
+ static: {
137
+ flags: OutputSymbolFlags.StaticMember | OutputSymbolFlags.StaticMemberContainer,
138
+ staticMembers: {
139
+ nested_static: {
140
+ flags: OutputSymbolFlags.StaticMember
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ });
149
+ const resolution = binder.resolveDeclarationByKey(root, undefined, nested_static.refkeys[0]);
150
+ expect(resolution.value).toBeDefined();
151
+ const {
152
+ commonScope,
153
+ pathUp,
154
+ pathDown,
155
+ targetDeclaration,
156
+ memberPath
157
+ } = resolution.value;
158
+ expect(commonScope).toBe(root);
159
+ expect(targetDeclaration).toBe(rootSym);
160
+ expect(pathDown).toEqual([]);
161
+ expect(pathUp).toEqual([]);
162
+ expect(memberPath.map(s => s.name)).toEqual(["root", "static", "nested_static"]);
163
+ });
164
+ it("resolves static symbols lazily", () => {
165
+ const staticSymRefkey = refkey();
166
+ const binder = createOutputBinder();
167
+ const {
168
+ scopes: {
169
+ root
170
+ },
171
+ symbols: {
172
+ root: rootSym
173
+ }
174
+ } = createScopeTree(binder, {
175
+ root: {
176
+ symbols: {
177
+ root: {
178
+ flags: OutputSymbolFlags.InstanceMemberContainer | OutputSymbolFlags.StaticMemberContainer
179
+ }
180
+ }
181
+ }
182
+ });
183
+ const resolution = binder.resolveDeclarationByKey(root, undefined, staticSymRefkey);
184
+ expect(resolution.value).toBeUndefined();
185
+ binder.createSymbol({
186
+ name: "static",
187
+ scope: rootSym.staticMemberScope,
188
+ refkey: staticSymRefkey,
189
+ flags: OutputSymbolFlags.StaticMember
190
+ });
191
+ expect(resolution.value).toBeDefined();
192
+ const {
193
+ commonScope,
194
+ pathUp,
195
+ pathDown,
196
+ targetDeclaration,
197
+ memberPath
198
+ } = resolution.value;
199
+ expect(commonScope).toBe(root);
200
+ expect(targetDeclaration).toBe(rootSym);
201
+ expect(pathDown).toEqual([]);
202
+ expect(pathUp).toEqual([]);
203
+ expect(memberPath.map(s => s.name)).toEqual(["root", "static"]);
204
+ });
205
+ });
206
+ describe("instance members", () => {
207
+ it("resolves", () => {
208
+ const binder = createOutputBinder();
209
+ const {
210
+ symbols: {
211
+ root: rootSym,
212
+ instance
213
+ }
214
+ } = createScopeTree(binder, {
215
+ root: {
216
+ symbols: {
217
+ root: {
218
+ flags: OutputSymbolFlags.InstanceMemberContainer,
219
+ instanceMembers: {
220
+ instance: {
221
+ flags: OutputSymbolFlags.InstanceMember
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+ });
228
+ const resolution = binder.resolveDeclarationByKey(undefined, rootSym.instanceMemberScope, instance.refkeys[0]);
229
+ expect(resolution.value).toBeDefined();
230
+ const {
231
+ commonScope,
232
+ pathUp,
233
+ pathDown,
234
+ targetDeclaration,
235
+ memberPath
236
+ } = resolution.value;
237
+ expect(commonScope).toBe(rootSym.instanceMemberScope);
238
+ expect(targetDeclaration).toBe(instance);
239
+ expect(pathDown).toEqual([]);
240
+ expect(pathUp).toEqual([]);
241
+ expect(memberPath).toEqual([instance]);
242
+ });
243
+ });
244
+ describe("instantiating members", () => {
245
+ it("instantiates static symbols", () => {
246
+ const binder = createOutputBinder();
247
+ const {
248
+ symbols: {
249
+ rootSymbol,
250
+ instance,
251
+ instantiation
252
+ }
253
+ } = createScopeTree(binder, {
254
+ rootScope: {
255
+ symbols: {
256
+ rootSymbol: {
257
+ flags: OutputSymbolFlags.InstanceMemberContainer,
258
+ instanceMembers: {
259
+ instance: {
260
+ flags: OutputSymbolFlags.StaticMember
261
+ }
262
+ }
263
+ },
264
+ instantiation: {}
265
+ }
266
+ }
267
+ });
268
+ binder.instantiateSymbolInto(rootSymbol, instantiation);
269
+ expect(instantiation.flags & OutputSymbolFlags.InstanceMemberContainer).toBeTruthy();
270
+ expect(instantiation.instanceMemberScope).toBeDefined();
271
+ const expectedRefkey = refkey(instantiation.refkeys[0], instance.refkeys[0]);
272
+ expect(instantiation.instanceMemberScope.symbolsByRefkey.get(expectedRefkey)).toBeDefined();
273
+ });
274
+ it("instantiates static symbols that are added after the instantiation", () => {
275
+ const binder = createOutputBinder();
276
+ const {
277
+ symbols: {
278
+ rootSymbol,
279
+ instance,
280
+ instantiation
281
+ }
282
+ } = createScopeTree(binder, {
283
+ rootScope: {
284
+ symbols: {
285
+ rootSymbol: {
286
+ flags: OutputSymbolFlags.InstanceMemberContainer,
287
+ instanceMembers: {
288
+ instance: {
289
+ flags: OutputSymbolFlags.StaticMember
290
+ }
291
+ }
292
+ },
293
+ instantiation: {}
294
+ }
295
+ }
296
+ });
297
+ binder.instantiateSymbolInto(rootSymbol, instantiation);
298
+ expect(instantiation.flags & OutputSymbolFlags.InstanceMemberContainer).toBeTruthy();
299
+ expect(instantiation.instanceMemberScope).toBeDefined();
300
+ const expectedRefkey = refkey(instantiation.refkeys[0], instance.refkeys[0]);
301
+ expect(instantiation.instanceMemberScope.symbolsByRefkey.get(expectedRefkey)).toBeDefined();
302
+ const newInstanceMemberRefkey = refkey();
303
+ binder.createSymbol({
304
+ name: "newInstanceMember",
305
+ scope: rootSymbol.instanceMemberScope,
306
+ refkey: newInstanceMemberRefkey,
307
+ flags: OutputSymbolFlags.InstanceMember
308
+ });
309
+ const newExpectedRefkey = refkey(instantiation.refkeys[0], newInstanceMemberRefkey);
310
+ expect(instantiation.instanceMemberScope.symbolsByRefkey.get(newExpectedRefkey)).toBeDefined();
311
+ });
312
+ });
313
+ describe("symbol name resolution", () => {
314
+ it("resolves static symbols", () => {
315
+ const binder = createOutputBinder();
316
+ const {
317
+ symbols: {
318
+ static: staticSym
319
+ }
320
+ } = createScopeTree(binder, {
321
+ root: {
322
+ symbols: {
323
+ root: {
324
+ flags: OutputSymbolFlags.InstanceMemberContainer | OutputSymbolFlags.StaticMemberContainer,
325
+ staticMembers: {
326
+ static: {
327
+ flags: OutputSymbolFlags.StaticMember
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+ });
334
+ const result = binder.resolveFQN("root.root.static");
335
+ expect(result.value).toEqual(staticSym);
336
+ });
337
+ it("resolves static symbols that are added later", () => {
338
+ const binder = createOutputBinder();
339
+ const result = binder.resolveFQN("root.root.static");
340
+ expect(result.value).toBeUndefined();
341
+ const {
342
+ symbols: {
343
+ static: staticSym
344
+ }
345
+ } = createScopeTree(binder, {
346
+ root: {
347
+ symbols: {
348
+ root: {
349
+ flags: OutputSymbolFlags.InstanceMemberContainer | OutputSymbolFlags.StaticMemberContainer,
350
+ staticMembers: {
351
+ static: {
352
+ flags: OutputSymbolFlags.StaticMember
353
+ }
354
+ }
355
+ }
356
+ }
357
+ }
358
+ });
359
+ expect(result.value).toEqual(staticSym);
360
+ });
361
+ it("resolves instance symbols", () => {
362
+ const binder = createOutputBinder();
363
+ const {
364
+ symbols: {
365
+ instance
366
+ }
367
+ } = createScopeTree(binder, {
368
+ root: {
369
+ symbols: {
370
+ root: {
371
+ flags: OutputSymbolFlags.InstanceMemberContainer,
372
+ instanceMembers: {
373
+ instance: {
374
+ flags: OutputSymbolFlags.InstanceMember
375
+ }
376
+ }
377
+ }
378
+ }
379
+ }
380
+ });
381
+ const result = binder.resolveFQN("root.root#instance");
382
+ expect(result.value).toEqual(instance);
383
+ });
384
+ it("resolves instance symbols that are added later", () => {
385
+ const binder = createOutputBinder();
386
+ const result = binder.resolveFQN("root.root#instance");
387
+ expect(result.value).toBeUndefined();
388
+ const {
389
+ symbols: {
390
+ instance
391
+ }
392
+ } = createScopeTree(binder, {
393
+ root: {
394
+ symbols: {
395
+ root: {
396
+ flags: OutputSymbolFlags.InstanceMemberContainer,
397
+ instanceMembers: {
398
+ instance: {
399
+ flags: OutputSymbolFlags.InstanceMember
400
+ }
401
+ }
402
+ }
403
+ }
404
+ }
405
+ });
406
+ expect(result.value).toEqual(instance);
407
+ });
408
+ });
409
+ describe("refkey resolution", () => {
410
+ it("resolves existing symbols by refkey", () => {
411
+ const key = refkey();
412
+ const binder = createOutputBinder();
413
+ const sym = binder.createSymbol({
414
+ name: "foo",
415
+ refkey: key,
416
+ scope: binder.globalScope
417
+ });
418
+ const resolvedSym = binder.resolveDeclarationByKey(undefined, undefined, key);
419
+ expect(resolvedSym.value?.targetDeclaration).toBe(sym);
420
+ });
421
+ it("resolves symbols by refkey when symbol is added later", () => {
422
+ const key = refkey();
423
+ const binder = createOutputBinder();
424
+ const resolvedSym = binder.resolveDeclarationByKey(undefined, undefined, key);
425
+ const sym = binder.createSymbol({
426
+ name: "foo",
427
+ refkey: key,
428
+ scope: binder.globalScope
429
+ });
430
+ expect(resolvedSym.value?.targetDeclaration).toBe(sym);
431
+ });
432
+ it("resolves symbols by refkey when refkey is added later", () => {
433
+ const key = refkey();
434
+ const binder = createOutputBinder();
435
+ const resolvedSym = binder.resolveDeclarationByKey(undefined, undefined, key);
436
+ const sym = binder.createSymbol({
437
+ name: "foo",
438
+ scope: binder.globalScope
439
+ });
440
+ expect(resolvedSym.value).toBe(undefined);
441
+ sym.refkeys[0] = key;
442
+ expect(resolvedSym.value?.targetDeclaration).toBe(sym);
443
+ });
444
+ });
445
+ describe("Deleting symbols", () => {
446
+ it("updates resolutions", () => {
447
+ const key = refkey();
448
+ const binder = createOutputBinder();
449
+ const resolvedSym = binder.resolveDeclarationByKey(undefined, undefined, key);
450
+ const sym = binder.createSymbol({
451
+ name: "foo",
452
+ scope: binder.globalScope
453
+ });
454
+ expect(resolvedSym.value).toBe(undefined);
455
+ sym.refkeys[0] = key;
456
+ expect(resolvedSym.value?.targetDeclaration).toBe(sym);
457
+ binder.deleteSymbol(sym);
458
+ expect(resolvedSym.value).toBe(undefined);
459
+ });
460
+ it("removes from parent scopes", () => {
461
+ const binder = createOutputBinder();
462
+ const result = binder.resolveFQN("root.root#instance");
463
+ expect(result.value).toBeUndefined();
464
+ const {
465
+ symbols: {
466
+ instance,
467
+ root,
468
+ staticc
469
+ },
470
+ scopes: {
471
+ rootScope
472
+ }
473
+ } = createScopeTree(binder, {
474
+ rootScope: {
475
+ symbols: {
476
+ root: {
477
+ flags: OutputSymbolFlags.InstanceMemberContainer | OutputSymbolFlags.StaticMemberContainer,
478
+ instanceMembers: {
479
+ instance: {
480
+ flags: OutputSymbolFlags.InstanceMember
481
+ }
482
+ },
483
+ staticMembers: {
484
+ staticc: {
485
+ flags: OutputSymbolFlags.StaticMember
486
+ }
487
+ }
488
+ }
489
+ }
490
+ }
491
+ });
492
+ const staticScope = root.staticMemberScope;
493
+ const instanceScope = root.instanceMemberScope;
494
+ expect(staticScope.symbols.size).toBe(1);
495
+ binder.deleteSymbol(staticc);
496
+ expect(staticScope.symbols.size).toBe(0);
497
+ expect(instanceScope.symbols.size).toBe(1);
498
+ binder.deleteSymbol(instance);
499
+ expect(instanceScope.symbols.size).toBe(0);
500
+ expect(rootScope.symbols.size).toBe(1);
501
+ binder.deleteSymbol(root);
502
+ expect(rootScope.symbols.size).toBe(0);
503
+ });
504
+ });
@@ -0,0 +1,221 @@
1
+ import { memo as _$memo, createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
2
+ import { computed, ref, triggerRef } from "@vue/reactivity";
3
+ import { describe, expect, it } from "vitest";
4
+ import { renderTree } from "../src/render.js";
5
+ import { children, join, mapJoin } from "../src/utils.js";
6
+ import "../testing/extend-expect.js";
7
+ describe("mapJoin", () => {
8
+ it("can map a map", () => {
9
+ const map = new Map([["a", 1], ["b", 2]]);
10
+ function Foo(props) {
11
+ return ["Key: ", _$memo(() => props.key), ", Value: ", _$memo(() => props.value)];
12
+ }
13
+ const joined = mapJoin(() => map, (key, value) => _$createComponent(Foo, {
14
+ key: key,
15
+ value: value
16
+ }));
17
+ expect(joined()).toRenderTo(`
18
+ Key: a, Value: 1
19
+ Key: b, Value: 2
20
+ `);
21
+ });
22
+ it("can map an array", () => {
23
+ const arr = [1, 2];
24
+ function Foo(props) {
25
+ return ["Value: ", _$memo(() => props.value)];
26
+ }
27
+ const joined = mapJoin(() => arr, value => _$createComponent(Foo, {
28
+ value: value
29
+ }));
30
+ expect(joined()).toRenderTo(`
31
+ Value: 1
32
+ Value: 2
33
+ `);
34
+ });
35
+ it("can map an array reactively (without render)", () => {
36
+ const arr = ref([1, 2]);
37
+ function Foo(props) {
38
+ return ["Value: ", _$memo(() => props.value)];
39
+ }
40
+ const joined = mapJoin(() => arr.value, value => {
41
+ return _$createComponent(Foo, {
42
+ value: value
43
+ });
44
+ });
45
+ const len = computed(() => {
46
+ return joined().length;
47
+ });
48
+ expect(len.value).toBe(4);
49
+ arr.value.push(3);
50
+ triggerRef(arr);
51
+ expect(len.value).toBe(6);
52
+ });
53
+ it("can map an array reactively (with render)", () => {
54
+ let callCount = 0;
55
+ const arr = ref([1, 2]);
56
+ function Foo(props) {
57
+ callCount++;
58
+ return ["Value: ", _$memo(() => props.value)];
59
+ }
60
+ const joined = mapJoin(() => arr.value, value => {
61
+ return _$createComponent(Foo, {
62
+ value: value
63
+ });
64
+ });
65
+ renderTree(joined);
66
+ expect(callCount).toBe(2);
67
+ arr.value.push(3);
68
+ triggerRef(arr);
69
+ expect(callCount).toBe(3);
70
+ });
71
+ it("can map a joiner", () => {
72
+ const arr = [1, 2];
73
+ function Foo(props) {
74
+ return ["Value: ", _$memo(() => props.value)];
75
+ }
76
+ const joined = mapJoin(() => arr, value => _$createComponent(Foo, {
77
+ value: value
78
+ }), {
79
+ joiner: "-"
80
+ });
81
+ expect(joined).toRenderTo(`
82
+ Value: 1-Value: 2
83
+ `);
84
+ });
85
+ it("can map an ender", () => {
86
+ const arr = [1, 2];
87
+ function Foo(props) {
88
+ return ["Value: ", _$memo(() => props.value)];
89
+ }
90
+ const joined = mapJoin(() => arr, value => _$createComponent(Foo, {
91
+ value: value
92
+ }), {
93
+ joiner: "-",
94
+ ender: ";"
95
+ });
96
+ expect(joined).toRenderTo(`
97
+ Value: 1-Value: 2;
98
+ `);
99
+ });
100
+ it("can map using an iterator", () => {
101
+ const arr = [1, 2].values();
102
+ function Foo(props) {
103
+ return ["Value: ", _$memo(() => props.value)];
104
+ }
105
+ const joined = mapJoin(() => arr, value => _$createComponent(Foo, {
106
+ value: value
107
+ }), {
108
+ joiner: "-",
109
+ ender: ";"
110
+ });
111
+ expect(joined).toRenderTo(`
112
+ Value: 1-Value: 2;
113
+ `);
114
+ });
115
+ });
116
+ describe("join", () => {
117
+ it("can join an array", () => {
118
+ const arr = [_$createComponent(Foo, {
119
+ value: 1
120
+ }), _$createComponent(Foo, {
121
+ value: 2
122
+ })];
123
+ function Foo(props) {
124
+ return ["Value: ", _$memo(() => props.value)];
125
+ }
126
+ const joined = join(arr);
127
+ expect(joined).toRenderTo(`
128
+ Value: 1
129
+ Value: 2
130
+ `);
131
+ });
132
+ it("can join an array with a joiner", () => {
133
+ const arr = [_$createComponent(Foo, {
134
+ value: 1
135
+ }), _$createComponent(Foo, {
136
+ value: 2
137
+ })];
138
+ function Foo(props) {
139
+ return ["Value: ", _$memo(() => props.value)];
140
+ }
141
+ const joined = join(arr, {
142
+ joiner: "-"
143
+ });
144
+ expect(joined).toRenderTo(`
145
+ Value: 1-Value: 2
146
+ `);
147
+ });
148
+ it("can join an array with an ender", () => {
149
+ const arr = [_$createComponent(Foo, {
150
+ value: 1
151
+ }), _$createComponent(Foo, {
152
+ value: 2
153
+ })];
154
+ function Foo(props) {
155
+ return ["Value: ", _$memo(() => props.value)];
156
+ }
157
+ const joined = join(arr, {
158
+ joiner: "-",
159
+ ender: ";"
160
+ });
161
+ expect(joined).toRenderTo(`
162
+ Value: 1-Value: 2;
163
+ `);
164
+ });
165
+ it("can join using an iterator", () => {
166
+ const arrIter = [_$createComponent(Foo, {
167
+ value: 1
168
+ }), _$createComponent(Foo, {
169
+ value: 2
170
+ })].values();
171
+ function Foo(props) {
172
+ return ["Value: ", _$memo(() => props.value)];
173
+ }
174
+ const joined = join(arrIter, {
175
+ joiner: "-",
176
+ ender: ";"
177
+ });
178
+ expect(joined).toRenderTo(`
179
+ Value: 1-Value: 2;
180
+ `);
181
+ });
182
+ });
183
+ describe("children", () => {
184
+ let resolvedChildren;
185
+ function ResolveChildren(props) {
186
+ resolvedChildren = children(() => props.children, {
187
+ preserveFragments: props.preserveFragments
188
+ })();
189
+ }
190
+ it("resolves a single child", () => {
191
+ renderTree(_$createComponent(ResolveChildren, {
192
+ children: "1"
193
+ }));
194
+ expect(resolvedChildren).toEqual("1");
195
+ });
196
+ it("resolves multiple children", () => {
197
+ renderTree(_$createComponent(ResolveChildren, {
198
+ get children() {
199
+ return ["1", "2", "3"];
200
+ }
201
+ }));
202
+ expect(resolvedChildren).toEqual(["1", "2", "3"]);
203
+ });
204
+ it("resolves fragments by default", () => {
205
+ renderTree(_$createComponent(ResolveChildren, {
206
+ get children() {
207
+ return [["1"], ["2"], ["3"]];
208
+ }
209
+ }));
210
+ expect(resolvedChildren).toEqual(["1", "2", "3"]);
211
+ });
212
+ it("preserves fragments if asked", () => {
213
+ renderTree(_$createComponent(ResolveChildren, {
214
+ preserveFragments: true,
215
+ get children() {
216
+ return [["1"], ["2"], ["3"]];
217
+ }
218
+ }));
219
+ expect(resolvedChildren).toEqual([["1"], ["2"], ["3"]]);
220
+ });
221
+ });