@alloy-js/core 0.21.0-dev.3 → 0.21.0-dev.9

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 (58) hide show
  1. package/dist/src/binder.d.ts +3 -3
  2. package/dist/src/binder.d.ts.map +1 -1
  3. package/dist/src/binder.js +18 -4
  4. package/dist/src/binder.js.map +1 -1
  5. package/dist/src/components/ReferenceOrContent.d.ts +1 -1
  6. package/dist/src/components/ReferenceOrContent.d.ts.map +1 -1
  7. package/dist/src/index.d.ts +1 -0
  8. package/dist/src/index.d.ts.map +1 -1
  9. package/dist/src/index.js +1 -0
  10. package/dist/src/index.js.map +1 -1
  11. package/dist/src/library-symbol-reference.d.ts +8 -0
  12. package/dist/src/library-symbol-reference.d.ts.map +1 -0
  13. package/dist/src/library-symbol-reference.js +5 -0
  14. package/dist/src/library-symbol-reference.js.map +1 -0
  15. package/dist/src/refkey.d.ts +10 -8
  16. package/dist/src/refkey.d.ts.map +1 -1
  17. package/dist/src/refkey.js +34 -13
  18. package/dist/src/refkey.js.map +1 -1
  19. package/dist/src/render.d.ts.map +1 -1
  20. package/dist/src/render.js +4 -3
  21. package/dist/src/render.js.map +1 -1
  22. package/dist/src/runtime/component.d.ts +2 -2
  23. package/dist/src/runtime/component.d.ts.map +1 -1
  24. package/dist/src/runtime/component.js.map +1 -1
  25. package/dist/src/symbols/output-symbol.d.ts +9 -0
  26. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  27. package/dist/src/symbols/output-symbol.js +30 -0
  28. package/dist/src/symbols/output-symbol.js.map +1 -1
  29. package/dist/test/refkey.test.js +11 -1
  30. package/dist/test/refkey.test.js.map +1 -1
  31. package/dist/test/symbols/output-scope.test.js +4 -3
  32. package/dist/test/symbols/output-scope.test.js.map +1 -1
  33. package/dist/test/symbols/resolution.test.js +29 -1
  34. package/dist/test/symbols/resolution.test.js.map +1 -1
  35. package/dist/testing/create-test-wrapper.d.ts +22 -0
  36. package/dist/testing/create-test-wrapper.d.ts.map +1 -0
  37. package/dist/testing/create-test-wrapper.js +60 -0
  38. package/dist/testing/create-test-wrapper.js.map +1 -0
  39. package/dist/testing/index.d.ts +1 -0
  40. package/dist/testing/index.d.ts.map +1 -1
  41. package/dist/testing/index.js +1 -0
  42. package/dist/testing/index.js.map +1 -1
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +1 -1
  45. package/src/binder.ts +34 -6
  46. package/src/index.ts +1 -0
  47. package/src/library-symbol-reference.ts +20 -0
  48. package/src/refkey.ts +57 -29
  49. package/src/render.ts +4 -3
  50. package/src/runtime/component.ts +2 -1
  51. package/src/symbols/output-symbol.ts +40 -0
  52. package/temp/api.json +447 -45
  53. package/test/refkey.test.ts +12 -1
  54. package/test/symbols/output-scope.test.ts +4 -4
  55. package/test/symbols/resolution.test.ts +42 -1
  56. package/testing/create-test-wrapper.tsx +70 -0
  57. package/testing/index.ts +1 -0
  58. package/tsconfig.json +1 -0
package/src/binder.ts CHANGED
@@ -3,7 +3,14 @@ import { useBinder } from "./context/binder.js";
3
3
  import { useMemberContext } from "./context/member-scope.js";
4
4
  import { useScope } from "./context/scope.js";
5
5
  import { effect } from "./reactivity.js";
6
- import { isMemberRefkey, refkey, Refkey } from "./refkey.js";
6
+ import {
7
+ isMemberRefkey,
8
+ MemberRefkey,
9
+ refkey,
10
+ Refkey,
11
+ Refkeyable,
12
+ toRefkey,
13
+ } from "./refkey.js";
7
14
  import { OutputScope } from "./symbols/output-scope.js";
8
15
  import { type OutputSymbol } from "./symbols/output-symbol.js";
9
16
  import {
@@ -49,7 +56,7 @@ export interface Binder {
49
56
  * ref is undefined if the symbol has not been created yet.
50
57
  */
51
58
  getSymbolForRefkey<TSymbol extends OutputSymbol>(
52
- refkey: Refkey,
59
+ refkey: Refkeyable,
53
60
  ): Ref<TSymbol | undefined>;
54
61
 
55
62
  /**
@@ -392,8 +399,10 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
392
399
  }
393
400
 
394
401
  function getSymbolForRefkey<TSymbol extends OutputSymbol = OutputSymbol>(
395
- refkey: Refkey,
402
+ refkeyable: Refkeyable,
396
403
  ) {
404
+ const refkey = toRefkey(refkeyable);
405
+
397
406
  if (waitingDeclarations.has(refkey)) {
398
407
  return waitingDeclarations.get(refkey)! as ShallowRef<TSymbol>;
399
408
  }
@@ -404,7 +413,9 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
404
413
  const baseSymbolRef: ShallowRef<TSymbol | undefined> =
405
414
  getSymbolForRefkey<TSymbol>(refkey.base);
406
415
  const memberSymbolRef: ShallowRef<TSymbol | undefined> =
407
- getSymbolForRefkey<TSymbol>(refkey.member);
416
+ getMemberSymbolFromMemberRefkey(refkey) as ShallowRef<
417
+ TSymbol | undefined
418
+ >;
408
419
 
409
420
  symbolRef = computed(() => {
410
421
  // even though we don't necessarily need the base symbol to be available
@@ -558,7 +569,7 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
558
569
  return [
559
570
  ...getMemberPathFromRefkey(refkey.base),
560
571
  {
561
- symbol: getSymbolForRefkey(refkey.member).value!,
572
+ symbol: getMemberSymbolFromMemberRefkey(refkey).value!,
562
573
  isMemberAccess: true,
563
574
  },
564
575
  ];
@@ -572,6 +583,23 @@ export function createOutputBinder(options: BinderOptions = {}): Binder {
572
583
  ];
573
584
  }
574
585
 
586
+ function getMemberSymbolFromMemberRefkey(
587
+ refkey: MemberRefkey,
588
+ ): Ref<OutputSymbol | undefined> {
589
+ if (typeof refkey.member === "string") {
590
+ return computed(() => {
591
+ const baseSymbol = getSymbolForRefkey(refkey.base).value;
592
+ if (!baseSymbol) {
593
+ return undefined;
594
+ }
595
+
596
+ return baseSymbol.resolveMemberByName(refkey.member as string);
597
+ });
598
+ } else {
599
+ return getSymbolForRefkey(refkey.member);
600
+ }
601
+ }
602
+
575
603
  function resolveMember(
576
604
  base: OutputSymbol,
577
605
  member: OutputSymbol,
@@ -692,7 +720,7 @@ export function resolve<
692
720
  * not found. The symbol you're looking for may not have been declared yet. When the symbol
693
721
  * is declared, the ref will be updated with the symbol.
694
722
  */
695
- export function symbolForRefkey(refkey: Refkey) {
723
+ export function symbolForRefkey(refkey: Refkeyable) {
696
724
  const binder = useBinder();
697
725
  if (!binder) {
698
726
  throw new Error("Can't resolve refkey without a binder");
package/src/index.ts CHANGED
@@ -27,6 +27,7 @@ export * from "./code.js";
27
27
  export * from "./components/index.js";
28
28
  export * from "./context.js";
29
29
  export * from "./context/index.js";
30
+ export * from "./library-symbol-reference.js";
30
31
  export * from "./name-policy.js";
31
32
  export * from "./props-combinators.js";
32
33
  export * from "./reactive-union-set.js";
@@ -0,0 +1,20 @@
1
+ import { RefkeyableObject } from "./refkey.js";
2
+ import { OutputSymbol } from "./symbols/output-symbol.js";
3
+
4
+ export const TO_SYMBOL: unique symbol = Symbol(
5
+ "Alloy.RefkeyableObject.TO_SYMBOL",
6
+ );
7
+
8
+ export interface LibrarySymbolReference extends RefkeyableObject {
9
+ [TO_SYMBOL](): OutputSymbol;
10
+ }
11
+
12
+ export function isLibrarySymbolReference(
13
+ value: unknown,
14
+ ): value is LibrarySymbolReference {
15
+ return (
16
+ typeof value === "object" &&
17
+ value !== null &&
18
+ Object.hasOwn(value, TO_SYMBOL)
19
+ );
20
+ }
package/src/refkey.ts CHANGED
@@ -14,15 +14,20 @@ function getObjectKey(value: WeakKey): string {
14
14
  return key;
15
15
  }
16
16
 
17
- const RefkeySym: unique symbol = Symbol();
17
+ export const REFKEYABLE: unique symbol = Symbol("Alloy.REFKEYABLE");
18
18
 
19
- export type RefkeyBase = {
20
- [RefkeySym]: true;
19
+ export type RefkeyableObject = {
20
+ [REFKEYABLE](): Refkey;
21
21
  };
22
22
 
23
+ export function toRefkey(refkey: Refkeyable) {
24
+ return refkey[REFKEYABLE]();
25
+ }
26
+
27
+ export type Refkeyable = RefkeyableObject | Refkey;
23
28
  export type Refkey = SymbolRefkey | MemberRefkey | Namekey;
24
29
 
25
- export interface SymbolRefkey extends RefkeyBase {
30
+ export interface SymbolRefkey extends RefkeyableObject {
26
31
  key: string;
27
32
  }
28
33
 
@@ -37,15 +42,17 @@ export interface Namekey<TOptions extends NamekeyOptions = NamekeyOptions>
37
42
  options: TOptions;
38
43
  }
39
44
 
40
- export interface MemberRefkey extends RefkeyBase {
45
+ export interface MemberRefkey extends RefkeyableObject {
41
46
  base: Refkey;
42
- member: Refkey;
47
+ member: Refkey | string;
43
48
  }
44
49
 
45
50
  function createSymbolRefkey(key: string): SymbolRefkey {
46
51
  const refkey: SymbolRefkey = {
47
52
  key,
48
- [RefkeySym]: true,
53
+ [REFKEYABLE]() {
54
+ return this;
55
+ },
49
56
  };
50
57
 
51
58
  markRaw(refkey);
@@ -53,11 +60,16 @@ function createSymbolRefkey(key: string): SymbolRefkey {
53
60
  return refkey;
54
61
  }
55
62
 
56
- export function isRefkey(value: unknown): value is Refkey {
63
+ export function isRefkeyable(value: unknown): value is RefkeyableObject {
57
64
  return (
58
65
  typeof value === "object" &&
59
66
  value !== null &&
60
- Object.hasOwn(value, RefkeySym)
67
+ Object.hasOwn(value, REFKEYABLE)
68
+ );
69
+ }
70
+ export function isRefkey(value: unknown): value is Refkey {
71
+ return (
72
+ isRefkeyable(value) && (value as RefkeyableObject)[REFKEYABLE]() === value
61
73
  );
62
74
  }
63
75
 
@@ -66,11 +78,7 @@ export function isSymbolRefkey(value: unknown): value is SymbolRefkey {
66
78
  }
67
79
 
68
80
  export function isMemberRefkey(value: unknown): value is MemberRefkey {
69
- return (
70
- isRefkey(value) &&
71
- Object.hasOwn(value, "base") &&
72
- Object.hasOwn(value, "member")
73
- );
81
+ return isRefkey(value) && Object.hasOwn(value, "base");
74
82
  }
75
83
 
76
84
  export function isNamekey(value: unknown): value is Namekey {
@@ -82,9 +90,14 @@ export function isNamekey(value: unknown): value is Namekey {
82
90
  * return the key of that refkey, objects get a unique key for that specific object,
83
91
  * and otherwise the key is based on the value.
84
92
  */
85
- function getKey(value: unknown): SymbolRefkey["key"] {
86
- if (isSymbolRefkey(value)) {
87
- return value.key;
93
+ function getKey(value: unknown): string {
94
+ if (isRefkeyable(value)) {
95
+ const refkey = toRefkey(value);
96
+ if (isSymbolRefkey(refkey)) {
97
+ return refkey.key;
98
+ } else {
99
+ return getObjectKey(value);
100
+ }
88
101
  } else if (typeof value === "object" && value !== null) {
89
102
  return getObjectKey(value);
90
103
  } else {
@@ -142,7 +155,9 @@ export function namekey(name: string, options: NamekeyOptions = {}): Namekey {
142
155
  key: getObjectKey({}),
143
156
  name,
144
157
  options,
145
- [RefkeySym]: true,
158
+ [REFKEYABLE]() {
159
+ return this;
160
+ },
146
161
  };
147
162
  }
148
163
  /**
@@ -168,8 +183,8 @@ export function namekey(name: string, options: NamekeyOptions = {}): Namekey {
168
183
  * `refkey(rk1, rk3)`.
169
184
  */
170
185
  export function memberRefkey(
171
- base: Refkey,
172
- ...members: [Refkey, ...Refkey[]]
186
+ base: Refkeyable,
187
+ ...members: [Refkeyable | string, ...(Refkeyable | string)[]]
173
188
  ): MemberRefkey {
174
189
  if (members.length < 1) {
175
190
  throw new Error("memberRefkey needs at least one member");
@@ -177,27 +192,40 @@ export function memberRefkey(
177
192
 
178
193
  if (members.length === 1) {
179
194
  return {
180
- base,
181
- member: members[0],
182
- [RefkeySym]: true,
195
+ base: toRefkey(base),
196
+ member:
197
+ typeof members[0] === "string" ? members[0] : toRefkey(members[0]),
198
+ [REFKEYABLE]() {
199
+ return this;
200
+ },
183
201
  };
184
202
  }
185
203
 
204
+ const lastMember = members.at(-1)!;
205
+
186
206
  return {
187
207
  base: memberRefkey(
188
208
  base,
189
- ...(members.slice(0, -1) as [Refkey, ...Refkey[]]),
209
+ ...(members.slice(0, -1) as [Refkeyable, ...Refkeyable[]]),
190
210
  ),
191
- member: members.at(-1)!,
192
- [RefkeySym]: true,
211
+ member: typeof lastMember === "string" ? lastMember : toRefkey(lastMember),
212
+ [REFKEYABLE]() {
213
+ return this;
214
+ },
193
215
  };
194
216
  }
195
217
 
196
218
  export function inspectRefkey(refkey: Refkey): string {
219
+ const unwrapped = refkey[REFKEYABLE]();
220
+
197
221
  const text =
198
- isMemberRefkey(refkey) ?
199
- `memberRefkey[${inspectRefkey(refkey.base)} -> ${inspectRefkey(refkey.member)}]`
200
- : `refkey[${refkey.key}]`;
222
+ isMemberRefkey(unwrapped) ?
223
+ `memberRefkey[${inspectRefkey(unwrapped.base)} -> ${
224
+ typeof unwrapped.member === "string" ?
225
+ unwrapped.member
226
+ : inspectRefkey(unwrapped.member)
227
+ }]`
228
+ : `refkey[${unwrapped.key}]`;
201
229
 
202
230
  return text;
203
231
  }
package/src/render.ts CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  root,
15
15
  untrack,
16
16
  } from "./reactivity.js";
17
- import { isRefkey } from "./refkey.js";
17
+ import { isRefkeyable, toRefkey } from "./refkey.js";
18
18
  import {
19
19
  Child,
20
20
  Children,
@@ -541,14 +541,15 @@ function normalizeChild(child: Child): NormalizedChildren {
541
541
  return "";
542
542
  } else if (isRef(child)) {
543
543
  return () => child.value as () => Child;
544
- } else if (isRefkey(child)) {
544
+ } else if (isRefkeyable(child)) {
545
+ const refkey = toRefkey(child);
545
546
  return () => {
546
547
  const sfContext = useContext(SourceFileContext);
547
548
  if (!sfContext || !sfContext.reference) {
548
549
  throw new Error("Can only emit references inside of source files");
549
550
  }
550
551
 
551
- return sfContext.reference({ refkey: child });
552
+ return sfContext.reference({ refkey });
552
553
  };
553
554
  } else if (isRenderableObject(child)) {
554
555
  // For custom renderable objects, we will just normalize them to a bound function.
@@ -1,6 +1,6 @@
1
1
  import { Ref } from "@vue/reactivity";
2
2
  import { CustomContext } from "../reactivity.js";
3
- import { Refkey } from "../refkey.js";
3
+ import { Refkey, RefkeyableObject } from "../refkey.js";
4
4
  import { IntrinsicElement } from "./intrinsic.js";
5
5
 
6
6
  export const RENDERABLE = Symbol.for("Alloy.CustomElement");
@@ -35,6 +35,7 @@ export function isRenderableObject(item: unknown): item is RenderableObject {
35
35
 
36
36
  export type Child =
37
37
  | RenderableObject
38
+ | RefkeyableObject
38
39
  | string
39
40
  | boolean
40
41
  | number
@@ -94,6 +94,11 @@ export interface OutputSymbolOptions {
94
94
  * also ignoring name conflict resolution.
95
95
  */
96
96
  ignoreNameConflict?: boolean;
97
+
98
+ /**
99
+ * Provide a function which lazy-initializes members when an enumeration of members are needed.
100
+ */
101
+ lazyMemberInitializer?: () => void;
97
102
  }
98
103
 
99
104
  let symbolCount = 0;
@@ -459,6 +464,40 @@ export abstract class OutputSymbol {
459
464
  return this.#namePolicy;
460
465
  }
461
466
 
467
+ #lazyMemberInitializer: (() => void) | undefined;
468
+ #lazyMembersInitialized: boolean = false;
469
+
470
+ #initializeMembers() {
471
+ if (this.#lazyMemberInitializer && !this.#lazyMembersInitialized) {
472
+ this.#lazyMemberInitializer();
473
+ }
474
+ this.#lazyMembersInitialized = true;
475
+ }
476
+
477
+ /**
478
+ * Get a member symbol by name from this symbol's member spaces. Checks member
479
+ * spaces in order until it finds a member with that name.
480
+ */
481
+ resolveMemberByName(name: string): OutputSymbol | undefined {
482
+ this.#initializeMembers();
483
+
484
+ if (this.isTyped) {
485
+ if (!this.hasTypeSymbol) {
486
+ return undefined;
487
+ }
488
+
489
+ return this.type!.resolveMemberByName(name);
490
+ }
491
+ for (const space of this.memberSpaces) {
492
+ const member = space.symbolNames.get(name);
493
+ if (member) {
494
+ return member;
495
+ }
496
+ }
497
+
498
+ return undefined;
499
+ }
500
+
462
501
  // Tell \@vue/reactivity that this symbol should never be wrapped in a reactive
463
502
  // proxy.
464
503
  [ReactiveFlags.SKIP] = true;
@@ -502,6 +541,7 @@ export abstract class OutputSymbol {
502
541
  this.#isTransient = !!options.transient;
503
542
  this.#isTyped = !!options.type;
504
543
  this.type = options.type;
544
+ this.#lazyMemberInitializer = options.lazyMemberInitializer;
505
545
 
506
546
  this.#handleNewSpaces(this.#spaces);
507
547
  const constructor = this.constructor as typeof OutputSymbol;