@alloy-js/core 0.21.0-dev.0 → 0.21.0-dev.10
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.
- package/dist/src/binder.d.ts +3 -3
- package/dist/src/binder.d.ts.map +1 -1
- package/dist/src/binder.js +18 -4
- package/dist/src/binder.js.map +1 -1
- package/dist/src/components/Declaration.js +1 -1
- package/dist/src/components/Declaration.js.map +1 -1
- package/dist/src/components/MemberDeclaration.js +1 -1
- package/dist/src/components/MemberDeclaration.js.map +1 -1
- package/dist/src/components/ReferenceOrContent.d.ts +1 -1
- package/dist/src/components/ReferenceOrContent.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/library-symbol-reference.d.ts +8 -0
- package/dist/src/library-symbol-reference.d.ts.map +1 -0
- package/dist/src/library-symbol-reference.js +5 -0
- package/dist/src/library-symbol-reference.js.map +1 -0
- package/dist/src/name-policy.d.ts +2 -2
- package/dist/src/name-policy.d.ts.map +1 -1
- package/dist/src/name-policy.js +14 -1
- package/dist/src/name-policy.js.map +1 -1
- package/dist/src/pretty-string/pretty-string.d.ts +47 -0
- package/dist/src/pretty-string/pretty-string.d.ts.map +1 -0
- package/dist/src/pretty-string/pretty-string.js +100 -0
- package/dist/src/pretty-string/pretty-string.js.map +1 -0
- package/dist/src/pretty-string/pretty-string.test.d.ts +2 -0
- package/dist/src/pretty-string/pretty-string.test.d.ts.map +1 -0
- package/dist/src/pretty-string/pretty-string.test.js +38 -0
- package/dist/src/pretty-string/pretty-string.test.js.map +1 -0
- package/dist/src/refkey.d.ts +12 -8
- package/dist/src/refkey.d.ts.map +1 -1
- package/dist/src/refkey.js +40 -12
- package/dist/src/refkey.js.map +1 -1
- package/dist/src/render.d.ts.map +1 -1
- package/dist/src/render.js +10 -4
- package/dist/src/render.js.map +1 -1
- package/dist/src/runtime/component.d.ts +22 -2
- package/dist/src/runtime/component.d.ts.map +1 -1
- package/dist/src/runtime/component.js +18 -0
- package/dist/src/runtime/component.js.map +1 -1
- package/dist/src/symbols/output-scope.d.ts +1 -0
- package/dist/src/symbols/output-scope.d.ts.map +1 -1
- package/dist/src/symbols/output-scope.js +4 -0
- package/dist/src/symbols/output-scope.js.map +1 -1
- package/dist/src/symbols/output-symbol.d.ts +10 -0
- package/dist/src/symbols/output-symbol.d.ts.map +1 -1
- package/dist/src/symbols/output-symbol.js +33 -0
- package/dist/src/symbols/output-symbol.js.map +1 -1
- package/dist/src/tracer.d.ts.map +1 -1
- package/dist/src/tracer.js +2 -2
- package/dist/src/tracer.js.map +1 -1
- package/dist/test/refkey.test.js +11 -1
- package/dist/test/refkey.test.js.map +1 -1
- package/dist/test/rendering/basic.test.js +22 -0
- package/dist/test/rendering/basic.test.js.map +1 -1
- package/dist/test/symbols/output-scope.test.js +4 -3
- package/dist/test/symbols/output-scope.test.js.map +1 -1
- package/dist/test/symbols/resolution.test.js +29 -1
- package/dist/test/symbols/resolution.test.js.map +1 -1
- package/dist/testing/create-test-wrapper.d.ts +22 -0
- package/dist/testing/create-test-wrapper.d.ts.map +1 -0
- package/dist/testing/create-test-wrapper.js +60 -0
- package/dist/testing/create-test-wrapper.js.map +1 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/binder.ts +34 -6
- package/src/components/Declaration.tsx +1 -1
- package/src/components/MemberDeclaration.tsx +1 -1
- package/src/index.ts +1 -0
- package/src/library-symbol-reference.ts +20 -0
- package/src/name-policy.ts +22 -3
- package/src/pretty-string/pretty-string.test.ts +47 -0
- package/src/pretty-string/pretty-string.ts +130 -0
- package/src/refkey.ts +67 -26
- package/src/render.ts +11 -3
- package/src/runtime/component.ts +33 -1
- package/src/symbols/output-scope.ts +7 -0
- package/src/symbols/output-symbol.ts +44 -0
- package/src/tracer.ts +2 -5
- package/temp/api.json +792 -88
- package/test/refkey.test.ts +12 -1
- package/test/rendering/basic.test.tsx +35 -1
- package/test/symbols/output-scope.test.ts +4 -4
- package/test/symbols/resolution.test.ts +42 -1
- package/testing/create-test-wrapper.tsx +70 -0
- package/testing/index.ts +1 -0
- 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 {
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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");
|
|
@@ -79,7 +79,7 @@ export function Declaration(props: DeclarationProps) {
|
|
|
79
79
|
const scope = useScope();
|
|
80
80
|
if (!(scope instanceof BasicScope)) {
|
|
81
81
|
throw new Error(
|
|
82
|
-
|
|
82
|
+
`Declaration component cannot create a symbol in a non-basic scope: ${scope}`,
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -91,7 +91,7 @@ export function MemberDeclaration(props: MemberDeclarationProps) {
|
|
|
91
91
|
const scopeContext = useMemberContext()!;
|
|
92
92
|
if (!(scopeContext.ownerSymbol instanceof BasicSymbol)) {
|
|
93
93
|
throw new Error(
|
|
94
|
-
|
|
94
|
+
`MemberDeclaration component cannot create a symbol in a non-basic scope: ${scopeContext.ownerSymbol}`,
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
97
|
const space =
|
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/name-policy.ts
CHANGED
|
@@ -6,22 +6,41 @@ export interface NamePolicy<TElements extends string> {
|
|
|
6
6
|
* Apply the language policy to the provided name for the provided element
|
|
7
7
|
* type.
|
|
8
8
|
*/
|
|
9
|
-
getName(originalName: string, element: TElements): string;
|
|
9
|
+
getName(originalName: string, element: TElements | undefined): string;
|
|
10
10
|
/**
|
|
11
11
|
* Get a function that takes a name and applies the naming policy to it.
|
|
12
12
|
*/
|
|
13
|
-
for(element: TElements): NamePolicyGetter;
|
|
13
|
+
for(element: TElements | undefined): NamePolicyGetter;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function createNamePolicy<T extends string>(
|
|
17
17
|
namer: (name: string, elements: T) => string,
|
|
18
18
|
): NamePolicy<T> {
|
|
19
|
+
const forCache = new Map<string, NamePolicyGetter>();
|
|
20
|
+
const noopGetter = (name: string) => name;
|
|
21
|
+
|
|
19
22
|
return {
|
|
20
23
|
getName(name, element) {
|
|
24
|
+
if (!element) {
|
|
25
|
+
return name;
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
return namer(name, element);
|
|
22
29
|
},
|
|
30
|
+
|
|
23
31
|
for(element) {
|
|
24
|
-
|
|
32
|
+
if (!element) {
|
|
33
|
+
return noopGetter;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (forCache.has(element)) {
|
|
37
|
+
return forCache.get(element)!;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getter = (name: string) => namer(name, element);
|
|
41
|
+
forCache.set(element, getter);
|
|
42
|
+
|
|
43
|
+
return getter;
|
|
25
44
|
},
|
|
26
45
|
};
|
|
27
46
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { expect, it } from "vitest";
|
|
2
|
+
import { pret, PrettyString } from "./pretty-string.js";
|
|
3
|
+
|
|
4
|
+
function expectRender(
|
|
5
|
+
pretty: PrettyString,
|
|
6
|
+
expected: {
|
|
7
|
+
toString: string;
|
|
8
|
+
toAnsi: string;
|
|
9
|
+
},
|
|
10
|
+
) {
|
|
11
|
+
expect(pretty.toString()).toBe(expected.toString);
|
|
12
|
+
expect(pretty.toAnsi()).toBe(expected.toAnsi);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
it("interpolate basic string", () => {
|
|
16
|
+
const result = pret`foo ${"bar"} baz`;
|
|
17
|
+
expectRender(result, {
|
|
18
|
+
toString: "foo bar baz",
|
|
19
|
+
toAnsi: "foo bar baz",
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// cspell:ignore mbar
|
|
24
|
+
it("interpolate pretty segment", () => {
|
|
25
|
+
const result = pret`foo ${pret.red("bar")} baz`;
|
|
26
|
+
expectRender(result, {
|
|
27
|
+
toString: "foo bar baz",
|
|
28
|
+
toAnsi: "foo \x1b[31mbar\x1b[39m baz",
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("interpolate another pretty string with more formatting", () => {
|
|
33
|
+
const a = pret`foo ${pret.red("bar")} baz`;
|
|
34
|
+
const result = pret`Hi, ${pret.bgGreen(a)}`;
|
|
35
|
+
expectRender(result, {
|
|
36
|
+
toString: "Hi, foo bar baz",
|
|
37
|
+
toAnsi: "Hi, \x1b[42mfoo \x1b[31mbar\x1b[39m baz\x1b[49m",
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("use full rgb", () => {
|
|
42
|
+
const result = pret`foo ${pret.rgb(123, 45, 67, "bar")} baz`;
|
|
43
|
+
expectRender(result, {
|
|
44
|
+
toString: "foo bar baz",
|
|
45
|
+
toAnsi: "foo \x1b[38;2;123;45;67mbar\x1b[39m baz",
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { inspect } from "../inspect.js";
|
|
2
|
+
|
|
3
|
+
export function pret(
|
|
4
|
+
strings: TemplateStringsArray,
|
|
5
|
+
...keys: PrettyStringSegment[]
|
|
6
|
+
): PrettyString {
|
|
7
|
+
const result: PrettyStringSegment[] = [strings[0]];
|
|
8
|
+
keys.forEach((key, i) => {
|
|
9
|
+
result.push(key);
|
|
10
|
+
result.push(strings[i + 1]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
return new PrettyString(result);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
pret.black = (x: PrettyStringSegment) => new PrettyStringColored(x, "black");
|
|
17
|
+
pret.red = (x: PrettyStringSegment) => new PrettyStringColored(x, "red");
|
|
18
|
+
pret.green = (x: PrettyStringSegment) => new PrettyStringColored(x, "green");
|
|
19
|
+
pret.yellow = (x: PrettyStringSegment) => new PrettyStringColored(x, "yellow");
|
|
20
|
+
pret.blue = (x: PrettyStringSegment) => new PrettyStringColored(x, "blue");
|
|
21
|
+
pret.magenta = (x: PrettyStringSegment) =>
|
|
22
|
+
new PrettyStringColored(x, "magenta");
|
|
23
|
+
pret.cyan = (x: PrettyStringSegment) => new PrettyStringColored(x, "cyan");
|
|
24
|
+
pret.white = (x: PrettyStringSegment) => new PrettyStringColored(x, "white");
|
|
25
|
+
pret.gray = (x: PrettyStringSegment) => new PrettyStringColored(x, "gray");
|
|
26
|
+
|
|
27
|
+
pret.bgBlack = (x: PrettyStringSegment) =>
|
|
28
|
+
new PrettyStringColored(x, "bgBlack");
|
|
29
|
+
pret.bgRed = (x: PrettyStringSegment) => new PrettyStringColored(x, "bgRed");
|
|
30
|
+
pret.bgGreen = (x: PrettyStringSegment) =>
|
|
31
|
+
new PrettyStringColored(x, "bgGreen");
|
|
32
|
+
pret.bgYellow = (x: PrettyStringSegment) =>
|
|
33
|
+
new PrettyStringColored(x, "bgYellow");
|
|
34
|
+
pret.bgBlue = (x: PrettyStringSegment) => new PrettyStringColored(x, "bgBlue");
|
|
35
|
+
pret.bgMagenta = (x: PrettyStringSegment) =>
|
|
36
|
+
new PrettyStringColored(x, "bgMagenta");
|
|
37
|
+
pret.bgCyan = (x: PrettyStringSegment) => new PrettyStringColored(x, "bgCyan");
|
|
38
|
+
pret.bgWhite = (x: PrettyStringSegment) =>
|
|
39
|
+
new PrettyStringColored(x, "bgWhite");
|
|
40
|
+
|
|
41
|
+
pret.rgb = (r: number, g: number, b: number, x: PrettyStringSegment) =>
|
|
42
|
+
new PrettyStringColored(x, { kind: "rgb", r, g, b });
|
|
43
|
+
|
|
44
|
+
const ansiColors = {
|
|
45
|
+
reset: ["\x1b[0m", "\x1b[0m"],
|
|
46
|
+
bold: ["\x1b[1m", "\x1b[22m"],
|
|
47
|
+
dim: ["\x1b[2m", "\x1b[22m"],
|
|
48
|
+
italic: ["\x1b[3m", "\x1b[23m"],
|
|
49
|
+
underline: ["\x1b[4m", "\x1b[24m"],
|
|
50
|
+
inverse: ["\x1b[7m", "\x1b[27m"],
|
|
51
|
+
hidden: ["\x1b[8m", "\x1b[28m"],
|
|
52
|
+
strikethrough: ["\x1b[9m", "\x1b[29m"],
|
|
53
|
+
black: ["\x1b[30m", "\x1b[39m"],
|
|
54
|
+
red: ["\x1b[31m", "\x1b[39m"],
|
|
55
|
+
green: ["\x1b[32m", "\x1b[39m"],
|
|
56
|
+
yellow: ["\x1b[33m", "\x1b[39m"],
|
|
57
|
+
blue: ["\x1b[34m", "\x1b[39m"],
|
|
58
|
+
magenta: ["\x1b[35m", "\x1b[39m"],
|
|
59
|
+
cyan: ["\x1b[36m", "\x1b[39m"],
|
|
60
|
+
white: ["\x1b[37m", "\x1b[39m"],
|
|
61
|
+
gray: ["\x1b[90m", "\x1b[39m"],
|
|
62
|
+
bgBlack: ["\x1b[40m", "\x1b[49m"],
|
|
63
|
+
bgRed: ["\x1b[41m", "\x1b[49m"],
|
|
64
|
+
bgGreen: ["\x1b[42m", "\x1b[49m"],
|
|
65
|
+
bgYellow: ["\x1b[43m", "\x1b[49m"],
|
|
66
|
+
bgBlue: ["\x1b[44m", "\x1b[49m"],
|
|
67
|
+
bgMagenta: ["\x1b[45m", "\x1b[49m"],
|
|
68
|
+
bgCyan: ["\x1b[46m", "\x1b[49m"],
|
|
69
|
+
bgWhite: ["\x1b[47m", "\x1b[49m"],
|
|
70
|
+
} as Record<string, [string, string]>;
|
|
71
|
+
|
|
72
|
+
export type PrettyStringSegment = string | PrettyStringColored | PrettyString;
|
|
73
|
+
|
|
74
|
+
export interface RgbColor {
|
|
75
|
+
kind: "rgb";
|
|
76
|
+
r: number;
|
|
77
|
+
g: number;
|
|
78
|
+
b: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type ColorCodes = keyof typeof ansiColors;
|
|
82
|
+
export type Color = ColorCodes | RgbColor;
|
|
83
|
+
export class PrettyStringColored {
|
|
84
|
+
#value: PrettyStringSegment;
|
|
85
|
+
#color: Color;
|
|
86
|
+
|
|
87
|
+
constructor(value: PrettyStringSegment, color: Color) {
|
|
88
|
+
this.#value = value;
|
|
89
|
+
this.#color = color;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
toString(): string {
|
|
93
|
+
return this.#value.toString();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
toAnsi(): string {
|
|
97
|
+
if (typeof this.#color === "string") {
|
|
98
|
+
const [start, end] = ansiColors[this.#color];
|
|
99
|
+
return `${start}${typeof this.#value === "string" ? this.#value : this.#value.toAnsi()}${end}`;
|
|
100
|
+
}
|
|
101
|
+
const { r, g, b } = this.#color;
|
|
102
|
+
const start = `\x1b[38;2;${r};${g};${b}m`;
|
|
103
|
+
const end = `\x1b[39m`;
|
|
104
|
+
return `${start}${typeof this.#value === "string" ? this.#value : this.#value.toAnsi()}${end}`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class PrettyString {
|
|
109
|
+
#segments: PrettyStringSegment[];
|
|
110
|
+
|
|
111
|
+
constructor(segments: PrettyStringSegment[]) {
|
|
112
|
+
this.#segments = segments;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
toString(): string {
|
|
116
|
+
return this.#segments
|
|
117
|
+
.map((s) => (typeof s === "string" ? s : s.toString()))
|
|
118
|
+
.join("");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
toAnsi(): string {
|
|
122
|
+
return this.#segments
|
|
123
|
+
.map((s) => (typeof s === "string" ? s : s.toAnsi()))
|
|
124
|
+
.join("");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
[inspect.custom]() {
|
|
128
|
+
return this.toAnsi();
|
|
129
|
+
}
|
|
130
|
+
}
|
package/src/refkey.ts
CHANGED
|
@@ -14,15 +14,20 @@ function getObjectKey(value: WeakKey): string {
|
|
|
14
14
|
return key;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
export const REFKEYABLE: unique symbol = Symbol("Alloy.REFKEYABLE");
|
|
18
18
|
|
|
19
|
-
export type
|
|
20
|
-
[
|
|
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
|
|
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
|
|
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
|
-
[
|
|
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
|
|
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,
|
|
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):
|
|
86
|
-
if (
|
|
87
|
-
|
|
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
|
-
[
|
|
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:
|
|
172
|
-
...members: [
|
|
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,18 +192,44 @@ export function memberRefkey(
|
|
|
177
192
|
|
|
178
193
|
if (members.length === 1) {
|
|
179
194
|
return {
|
|
180
|
-
base,
|
|
181
|
-
member:
|
|
182
|
-
|
|
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 [
|
|
209
|
+
...(members.slice(0, -1) as [Refkeyable, ...Refkeyable[]]),
|
|
190
210
|
),
|
|
191
|
-
member:
|
|
192
|
-
[
|
|
211
|
+
member: typeof lastMember === "string" ? lastMember : toRefkey(lastMember),
|
|
212
|
+
[REFKEYABLE]() {
|
|
213
|
+
return this;
|
|
214
|
+
},
|
|
193
215
|
};
|
|
194
216
|
}
|
|
217
|
+
|
|
218
|
+
export function inspectRefkey(refkey: Refkey): string {
|
|
219
|
+
const unwrapped = refkey[REFKEYABLE]();
|
|
220
|
+
|
|
221
|
+
const text =
|
|
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}]`;
|
|
229
|
+
|
|
230
|
+
return text;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function unresolvedRefkey(refkey: Refkey): string {
|
|
234
|
+
return `<Unresolved Symbol: ${inspectRefkey(refkey)}>`;
|
|
235
|
+
}
|
package/src/render.ts
CHANGED
|
@@ -14,13 +14,15 @@ import {
|
|
|
14
14
|
root,
|
|
15
15
|
untrack,
|
|
16
16
|
} from "./reactivity.js";
|
|
17
|
-
import {
|
|
17
|
+
import { isRefkeyable, toRefkey } from "./refkey.js";
|
|
18
18
|
import {
|
|
19
19
|
Child,
|
|
20
20
|
Children,
|
|
21
21
|
Component,
|
|
22
22
|
isComponentCreator,
|
|
23
|
+
isRenderableObject,
|
|
23
24
|
Props,
|
|
25
|
+
RENDERABLE,
|
|
24
26
|
} from "./runtime/component.js";
|
|
25
27
|
import { IntrinsicElement, isIntrinsicElement } from "./runtime/intrinsic.js";
|
|
26
28
|
import { flushJobs, flushJobsAsync } from "./scheduler.js";
|
|
@@ -539,15 +541,19 @@ function normalizeChild(child: Child): NormalizedChildren {
|
|
|
539
541
|
return "";
|
|
540
542
|
} else if (isRef(child)) {
|
|
541
543
|
return () => child.value as () => Child;
|
|
542
|
-
} else if (
|
|
544
|
+
} else if (isRefkeyable(child)) {
|
|
545
|
+
const refkey = toRefkey(child);
|
|
543
546
|
return () => {
|
|
544
547
|
const sfContext = useContext(SourceFileContext);
|
|
545
548
|
if (!sfContext || !sfContext.reference) {
|
|
546
549
|
throw new Error("Can only emit references inside of source files");
|
|
547
550
|
}
|
|
548
551
|
|
|
549
|
-
return sfContext.reference({ refkey
|
|
552
|
+
return sfContext.reference({ refkey });
|
|
550
553
|
};
|
|
554
|
+
} else if (isRenderableObject(child)) {
|
|
555
|
+
// For custom renderable objects, we will just normalize them to a bound function.
|
|
556
|
+
return child[RENDERABLE].bind(child);
|
|
551
557
|
} else if (isCustomContext(child)) {
|
|
552
558
|
return child;
|
|
553
559
|
} else if (isIntrinsicElement(child)) {
|
|
@@ -573,6 +579,8 @@ function debugPrintChild(child: Children): string {
|
|
|
573
579
|
return "$ref";
|
|
574
580
|
} else if (isIntrinsicElement(child)) {
|
|
575
581
|
return `<${child.name}>`;
|
|
582
|
+
} else if (isRenderableObject(child)) {
|
|
583
|
+
return `CustomChildElement(${JSON.stringify(child)})`;
|
|
576
584
|
} else {
|
|
577
585
|
return JSON.stringify(child);
|
|
578
586
|
}
|
package/src/runtime/component.ts
CHANGED
|
@@ -1,9 +1,41 @@
|
|
|
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
|
+
export const RENDERABLE = Symbol.for("Alloy.CustomElement");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A renderable object is any object that has an `[ay.RENDERABLE]` method that
|
|
10
|
+
* returns children. This is used to allow custom object types to be used as
|
|
11
|
+
* children in Alloy components.
|
|
12
|
+
*/
|
|
13
|
+
export interface RenderableObject {
|
|
14
|
+
/**
|
|
15
|
+
* Renders this object to children.
|
|
16
|
+
*/
|
|
17
|
+
[RENDERABLE](): Children;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns true if the item is a renderable object, meaning it has an `[ay.RENDERABLE]`
|
|
22
|
+
* method.
|
|
23
|
+
*
|
|
24
|
+
* @param item - The item to check.
|
|
25
|
+
* @returns True if the item is a renderable object.
|
|
26
|
+
*/
|
|
27
|
+
export function isRenderableObject(item: unknown): item is RenderableObject {
|
|
28
|
+
return (
|
|
29
|
+
typeof item === "object" &&
|
|
30
|
+
item !== null &&
|
|
31
|
+
RENDERABLE in item &&
|
|
32
|
+
typeof (item as any)[RENDERABLE] === "function"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
6
36
|
export type Child =
|
|
37
|
+
| RenderableObject
|
|
38
|
+
| RefkeyableObject
|
|
7
39
|
| string
|
|
8
40
|
| boolean
|
|
9
41
|
| number
|
|
@@ -254,4 +254,11 @@ export abstract class OutputScope {
|
|
|
254
254
|
() => `${this.constructor.name} ${this.name}[${this.id}]${ownerSymbol}`,
|
|
255
255
|
);
|
|
256
256
|
}
|
|
257
|
+
|
|
258
|
+
toString() {
|
|
259
|
+
const ownerSymbol = this.ownerSymbol ? ` for ${this.ownerSymbol}` : "";
|
|
260
|
+
return untrack(
|
|
261
|
+
() => `${this.constructor.name} ${this.name}[${this.id}]${ownerSymbol}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
257
264
|
}
|