@alloy-js/core 0.1.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.
- package/LICENSE.txt +7 -0
- package/api-extractor.json +11 -0
- package/babel.config.cjs +4 -0
- package/dist/src/binder.d.ts +333 -0
- package/dist/src/binder.d.ts.map +1 -0
- package/dist/src/binder.js +444 -0
- package/dist/src/binder.js.map +1 -0
- package/dist/src/code.d.ts +3 -0
- package/dist/src/code.d.ts.map +1 -0
- package/dist/src/code.js +156 -0
- package/dist/src/code.js.map +1 -0
- package/dist/src/components/Declaration.d.ts +29 -0
- package/dist/src/components/Declaration.d.ts.map +1 -0
- package/dist/src/components/Declaration.js +47 -0
- package/dist/src/components/Declaration.js.map +1 -0
- package/dist/src/components/Indent.d.ts +13 -0
- package/dist/src/components/Indent.d.ts.map +1 -0
- package/dist/src/components/Indent.js +23 -0
- package/dist/src/components/Indent.js.map +1 -0
- package/dist/src/components/MemberDeclaration.d.ts +30 -0
- package/dist/src/components/MemberDeclaration.d.ts.map +1 -0
- package/dist/src/components/MemberDeclaration.js +52 -0
- package/dist/src/components/MemberDeclaration.js.map +1 -0
- package/dist/src/components/MemberName.d.ts +2 -0
- package/dist/src/components/MemberName.d.ts.map +1 -0
- package/dist/src/components/MemberName.js +11 -0
- package/dist/src/components/MemberName.js.map +1 -0
- package/dist/src/components/MemberScope.d.ts +27 -0
- package/dist/src/components/MemberScope.d.ts.map +1 -0
- package/dist/src/components/MemberScope.js +28 -0
- package/dist/src/components/MemberScope.js.map +1 -0
- package/dist/src/components/Name.d.ts +2 -0
- package/dist/src/components/Name.d.ts.map +1 -0
- package/dist/src/components/Name.js +11 -0
- package/dist/src/components/Name.js.map +1 -0
- package/dist/src/components/Output.d.ts +31 -0
- package/dist/src/components/Output.d.ts.map +1 -0
- package/dist/src/components/Output.js +44 -0
- package/dist/src/components/Output.js.map +1 -0
- package/dist/src/components/Scope.d.ts +10 -0
- package/dist/src/components/Scope.d.ts.map +1 -0
- package/dist/src/components/Scope.js +25 -0
- package/dist/src/components/Scope.js.map +1 -0
- package/dist/src/components/SourceDirectory.d.ts +7 -0
- package/dist/src/components/SourceDirectory.d.ts.map +1 -0
- package/dist/src/components/SourceDirectory.js +38 -0
- package/dist/src/components/SourceDirectory.js.map +1 -0
- package/dist/src/components/SourceFile.d.ts +12 -0
- package/dist/src/components/SourceFile.d.ts.map +1 -0
- package/dist/src/components/SourceFile.js +26 -0
- package/dist/src/components/SourceFile.js.map +1 -0
- package/dist/src/components/index.d.ts +11 -0
- package/dist/src/components/index.d.ts.map +1 -0
- package/dist/src/components/index.js +11 -0
- package/dist/src/components/index.js.map +1 -0
- package/dist/src/components/stc/index.d.ts +26 -0
- package/dist/src/components/stc/index.d.ts.map +1 -0
- package/dist/src/components/stc/index.js +9 -0
- package/dist/src/components/stc/index.js.map +1 -0
- package/dist/src/context/assignment.d.ts +39 -0
- package/dist/src/context/assignment.d.ts.map +1 -0
- package/dist/src/context/assignment.js +39 -0
- package/dist/src/context/assignment.js.map +1 -0
- package/dist/src/context/binder.d.ts +9 -0
- package/dist/src/context/binder.d.ts.map +1 -0
- package/dist/src/context/binder.js +12 -0
- package/dist/src/context/binder.js.map +1 -0
- package/dist/src/context/declaration.d.ts +4 -0
- package/dist/src/context/declaration.d.ts.map +1 -0
- package/dist/src/context/declaration.js +3 -0
- package/dist/src/context/declaration.js.map +1 -0
- package/dist/src/context/indent.d.ts +5 -0
- package/dist/src/context/indent.d.ts.map +1 -0
- package/dist/src/context/indent.js +8 -0
- package/dist/src/context/indent.js.map +1 -0
- package/dist/src/context/index.d.ts +11 -0
- package/dist/src/context/index.d.ts.map +1 -0
- package/dist/src/context/index.js +11 -0
- package/dist/src/context/index.js.map +1 -0
- package/dist/src/context/member-declaration.d.ts +9 -0
- package/dist/src/context/member-declaration.d.ts.map +1 -0
- package/dist/src/context/member-declaration.js +9 -0
- package/dist/src/context/member-declaration.js.map +1 -0
- package/dist/src/context/member-scope.d.ts +13 -0
- package/dist/src/context/member-scope.d.ts.map +1 -0
- package/dist/src/context/member-scope.js +12 -0
- package/dist/src/context/member-scope.js.map +1 -0
- package/dist/src/context/name-policy.d.ts +5 -0
- package/dist/src/context/name-policy.d.ts.map +1 -0
- package/dist/src/context/name-policy.js +10 -0
- package/dist/src/context/name-policy.js.map +1 -0
- package/dist/src/context/scope.d.ts +5 -0
- package/dist/src/context/scope.d.ts.map +1 -0
- package/dist/src/context/scope.js +6 -0
- package/dist/src/context/scope.js.map +1 -0
- package/dist/src/context/source-directory.d.ts +9 -0
- package/dist/src/context/source-directory.d.ts.map +1 -0
- package/dist/src/context/source-directory.js +3 -0
- package/dist/src/context/source-directory.js.map +1 -0
- package/dist/src/context/source-file.d.ts +12 -0
- package/dist/src/context/source-file.d.ts.map +1 -0
- package/dist/src/context/source-file.js +3 -0
- package/dist/src/context/source-file.js.map +1 -0
- package/dist/src/context.d.ts +13 -0
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +30 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +13 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/jsx-runtime.d.ts +43 -0
- package/dist/src/jsx-runtime.d.ts.map +1 -0
- package/dist/src/jsx-runtime.js +172 -0
- package/dist/src/jsx-runtime.js.map +1 -0
- package/dist/src/name-policy.d.ts +5 -0
- package/dist/src/name-policy.d.ts.map +1 -0
- package/dist/src/name-policy.js +8 -0
- package/dist/src/name-policy.js.map +1 -0
- package/dist/src/refkey.d.ts +9 -0
- package/dist/src/refkey.d.ts.map +1 -0
- package/dist/src/refkey.js +44 -0
- package/dist/src/refkey.js.map +1 -0
- package/dist/src/render.d.ts +147 -0
- package/dist/src/render.d.ts.map +1 -0
- package/dist/src/render.js +317 -0
- package/dist/src/render.js.map +1 -0
- package/dist/src/tsdoc-metadata.json +11 -0
- package/dist/src/utils.d.ts +80 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +219 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/test/children.test.d.ts +2 -0
- package/dist/test/children.test.d.ts.map +1 -0
- package/dist/test/components/source-file.test.d.ts +2 -0
- package/dist/test/components/source-file.test.d.ts.map +1 -0
- package/dist/test/name-policy.test.d.ts +2 -0
- package/dist/test/name-policy.test.d.ts.map +1 -0
- package/dist/test/reactivity/ref-rendering.test.d.ts +2 -0
- package/dist/test/reactivity/ref-rendering.test.d.ts.map +1 -0
- package/dist/test/reactivity/test.test.d.ts +2 -0
- package/dist/test/reactivity/test.test.d.ts.map +1 -0
- package/dist/test/refkey.test.d.ts +2 -0
- package/dist/test/refkey.test.d.ts.map +1 -0
- package/dist/test/rendering/basic.test.d.ts +2 -0
- package/dist/test/rendering/basic.test.d.ts.map +1 -0
- package/dist/test/rendering/code.test.d.ts +2 -0
- package/dist/test/rendering/code.test.d.ts.map +1 -0
- package/dist/test/rendering/indent.test.d.ts +2 -0
- package/dist/test/rendering/indent.test.d.ts.map +1 -0
- package/dist/test/rendering/linebreaks.test.d.ts +2 -0
- package/dist/test/rendering/linebreaks.test.d.ts.map +1 -0
- package/dist/test/rendering/refkeys.test.d.ts +2 -0
- package/dist/test/rendering/refkeys.test.d.ts.map +1 -0
- package/dist/test/stc.test.d.ts +2 -0
- package/dist/test/stc.test.d.ts.map +1 -0
- package/dist/test/symbols.test.d.ts +2 -0
- package/dist/test/symbols.test.d.ts.map +1 -0
- package/dist/test/utils.test.d.ts +2 -0
- package/dist/test/utils.test.d.ts.map +1 -0
- package/dist/testing/extend-expect.d.ts +2 -0
- package/dist/testing/extend-expect.d.ts.map +1 -0
- package/dist/testing/extend-expect.js +22 -0
- package/dist/testing/extend-expect.js.map +1 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +3 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/render.d.ts +7 -0
- package/dist/testing/render.d.ts.map +1 -0
- package/dist/testing/render.js +25 -0
- package/dist/testing/render.js.map +1 -0
- package/dist/testing/vitest.d.js +1 -0
- package/dist/testing/vitest.d.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +64 -0
- package/src/binder.ts +838 -0
- package/src/code.ts +220 -0
- package/src/components/Declaration.tsx +53 -0
- package/src/components/Indent.tsx +33 -0
- package/src/components/MemberDeclaration.tsx +62 -0
- package/src/components/MemberName.tsx +11 -0
- package/src/components/MemberScope.tsx +40 -0
- package/src/components/Name.tsx +11 -0
- package/src/components/Output.tsx +69 -0
- package/src/components/Scope.tsx +27 -0
- package/src/components/SourceDirectory.tsx +43 -0
- package/src/components/SourceFile.tsx +33 -0
- package/src/components/index.tsx +10 -0
- package/src/components/stc/index.ts +9 -0
- package/src/context/assignment.ts +57 -0
- package/src/context/binder.ts +14 -0
- package/src/context/declaration.ts +5 -0
- package/src/context/indent.ts +10 -0
- package/src/context/index.ts +10 -0
- package/src/context/member-declaration.ts +10 -0
- package/src/context/member-scope.ts +17 -0
- package/src/context/name-policy.ts +13 -0
- package/src/context/scope.ts +8 -0
- package/src/context/source-directory.ts +11 -0
- package/src/context/source-file.ts +12 -0
- package/src/context.ts +53 -0
- package/src/index.ts +21 -0
- package/src/jsx-runtime.ts +266 -0
- package/src/name-policy.ts +13 -0
- package/src/refkey.ts +62 -0
- package/src/render.ts +389 -0
- package/src/utils.ts +288 -0
- package/temp/api.json +8840 -0
- package/test/children.test.tsx +33 -0
- package/test/components/source-file.test.tsx +45 -0
- package/test/name-policy.test.tsx +19 -0
- package/test/reactivity/ref-rendering.test.tsx +50 -0
- package/test/reactivity/test.test.tsx +83 -0
- package/test/refkey.test.ts +32 -0
- package/test/rendering/basic.test.tsx +156 -0
- package/test/rendering/code.test.tsx +62 -0
- package/test/rendering/indent.test.tsx +608 -0
- package/test/rendering/linebreaks.test.tsx +72 -0
- package/test/rendering/refkeys.test.tsx +35 -0
- package/test/stc.test.tsx +21 -0
- package/test/symbols.test.ts +406 -0
- package/test/utils.test.tsx +150 -0
- package/testing/extend-expect.ts +20 -0
- package/testing/index.ts +2 -0
- package/testing/render.ts +37 -0
- package/testing/vitest.d.ts +10 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +18 -0
package/src/code.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// this code is split into a tokenizer and a parser of sorts because I feel like
|
|
2
|
+
// it should be psosible to share logic between this and the babel transform, but
|
|
3
|
+
// this is an exercise for the future.
|
|
4
|
+
import { Child, Indent } from "@alloy-js/core";
|
|
5
|
+
|
|
6
|
+
interface IndentLevelData {
|
|
7
|
+
kind: "indent";
|
|
8
|
+
children: (string | Child | IndentLevelData)[];
|
|
9
|
+
pendingLines: string[];
|
|
10
|
+
}
|
|
11
|
+
export function code(
|
|
12
|
+
template: TemplateStringsArray,
|
|
13
|
+
...substitutions: Child[]
|
|
14
|
+
) {
|
|
15
|
+
const indentNodes: IndentLevelData[] = [
|
|
16
|
+
{
|
|
17
|
+
kind: "indent",
|
|
18
|
+
children: [],
|
|
19
|
+
pendingLines: [],
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
for (const child of childTokens(template, substitutions)) {
|
|
24
|
+
if (child.indentationLevel > indentNodes.length - 1) {
|
|
25
|
+
// indentation level increased (can only ever increase by 1)
|
|
26
|
+
pushIndent();
|
|
27
|
+
} else if (child.indentationLevel < indentNodes.length - 1) {
|
|
28
|
+
popIndent(child);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const currentIndent = indentNodes.at(-1)!;
|
|
32
|
+
if (child.kind === "line") {
|
|
33
|
+
currentIndent.pendingLines.push(child.line);
|
|
34
|
+
} else {
|
|
35
|
+
flushLines();
|
|
36
|
+
currentIndent.children.push(child.value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
popIndent();
|
|
40
|
+
flushLines();
|
|
41
|
+
|
|
42
|
+
function childNodesFor(indentNode: IndentLevelData): Child[] {
|
|
43
|
+
return indentNode.children.map((child) => {
|
|
44
|
+
if (
|
|
45
|
+
typeof child === "object" &&
|
|
46
|
+
child !== null &&
|
|
47
|
+
(child as any).kind === "indent"
|
|
48
|
+
) {
|
|
49
|
+
return () =>
|
|
50
|
+
Indent({ children: childNodesFor(child as IndentLevelData) });
|
|
51
|
+
} else {
|
|
52
|
+
return child as Child;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return childNodesFor(indentNodes[0]);
|
|
58
|
+
|
|
59
|
+
function pushIndent() {
|
|
60
|
+
flushLines();
|
|
61
|
+
const newIndent: IndentLevelData = {
|
|
62
|
+
kind: "indent",
|
|
63
|
+
children: [],
|
|
64
|
+
pendingLines: [""],
|
|
65
|
+
};
|
|
66
|
+
indentNodes.at(-1)!.children.push(newIndent as any);
|
|
67
|
+
indentNodes.push(newIndent);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function popIndent(child?: ChildToken) {
|
|
71
|
+
// indentation level decreased (can decrease by many)
|
|
72
|
+
const times =
|
|
73
|
+
child ?
|
|
74
|
+
indentNodes.length - child.indentationLevel - 1
|
|
75
|
+
: indentNodes.length - 1;
|
|
76
|
+
for (let i = 0; i < times; i++) {
|
|
77
|
+
flushLines();
|
|
78
|
+
indentNodes.pop();
|
|
79
|
+
}
|
|
80
|
+
if (child) {
|
|
81
|
+
// need a linebreak after dedenting
|
|
82
|
+
indentNodes.at(-1)!.pendingLines.push("");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function flushLines() {
|
|
86
|
+
const currentIndent = indentNodes.at(-1)!;
|
|
87
|
+
currentIndent.children.push(currentIndent.pendingLines.join("\n"));
|
|
88
|
+
currentIndent.pendingLines = [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type ChildToken = LineToken | OtherToken;
|
|
93
|
+
interface ChildTokenBase {
|
|
94
|
+
kind: string;
|
|
95
|
+
indentationLevel: number;
|
|
96
|
+
newline: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface LineToken extends ChildTokenBase {
|
|
100
|
+
kind: "line";
|
|
101
|
+
line: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface OtherToken extends ChildTokenBase {
|
|
105
|
+
kind: "other";
|
|
106
|
+
value: Child;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function* childTokens(
|
|
110
|
+
template: TemplateStringsArray,
|
|
111
|
+
substitutions: Child[],
|
|
112
|
+
): IterableIterator<ChildToken> {
|
|
113
|
+
let newline = false;
|
|
114
|
+
const indentStack: { level: number; literalIndent: string }[] = [
|
|
115
|
+
{
|
|
116
|
+
level: -1,
|
|
117
|
+
literalIndent: "",
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
yield* processLiteralString(
|
|
122
|
+
template[0],
|
|
123
|
+
substitutions.length === 0 ? "only" : "first",
|
|
124
|
+
);
|
|
125
|
+
newline = false;
|
|
126
|
+
for (let i = 0; i < substitutions.length; i++) {
|
|
127
|
+
const indentationLevel = currentIndent().level;
|
|
128
|
+
yield {
|
|
129
|
+
kind: "other",
|
|
130
|
+
value: substitutions[i],
|
|
131
|
+
indentationLevel: indentationLevel < 0 ? 0 : indentationLevel,
|
|
132
|
+
newline,
|
|
133
|
+
};
|
|
134
|
+
yield* processLiteralString(
|
|
135
|
+
template[i + 1],
|
|
136
|
+
i === substitutions.length - 1 ? "last" : undefined,
|
|
137
|
+
);
|
|
138
|
+
newline = false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function processLiteralString(
|
|
142
|
+
child: string,
|
|
143
|
+
pos?: "first" | "last" | "only",
|
|
144
|
+
) {
|
|
145
|
+
const lines = child.split("\n");
|
|
146
|
+
const lineTokens: LineToken[] = [];
|
|
147
|
+
if ((pos === "first" || pos === "only") && lines[0].match(/^\s*$/)) {
|
|
148
|
+
// remove leading whitespace.
|
|
149
|
+
lines.shift();
|
|
150
|
+
newline = true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
(pos === "last" || pos === "only") &&
|
|
155
|
+
lines[lines.length - 1].match(/^\s*$/)
|
|
156
|
+
) {
|
|
157
|
+
// remove trailing whitespace
|
|
158
|
+
lines.pop();
|
|
159
|
+
if (lines.length === 1 && lines[0].trimStart() === "") {
|
|
160
|
+
// an empty line following a component most likely
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// eslint-disable-next-line prefer-const
|
|
166
|
+
for (let [lineNum, line] of lines.entries()) {
|
|
167
|
+
if (lineNum > 0) {
|
|
168
|
+
newline = true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (newline) {
|
|
172
|
+
if (line === "" && currentIndent().level > -1) {
|
|
173
|
+
// for empty lines we want to just continue with the current indentation
|
|
174
|
+
lineTokens.push({
|
|
175
|
+
kind: "line",
|
|
176
|
+
line: "",
|
|
177
|
+
indentationLevel: currentIndent().level,
|
|
178
|
+
newline,
|
|
179
|
+
});
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const lineIndent = line.match(/^\s+/)?.[0] ?? "";
|
|
183
|
+
const startIndent = currentIndent();
|
|
184
|
+
if (lineIndent.length > startIndent.literalIndent.length) {
|
|
185
|
+
indentStack.push({
|
|
186
|
+
level: startIndent.level + 1,
|
|
187
|
+
literalIndent: lineIndent,
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
while (currentIndent().literalIndent.length > lineIndent.length) {
|
|
191
|
+
indentStack.pop();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
line = line.trimStart();
|
|
195
|
+
|
|
196
|
+
const indentationLevel = currentIndent().level;
|
|
197
|
+
lineTokens.push({
|
|
198
|
+
kind: "line",
|
|
199
|
+
line,
|
|
200
|
+
indentationLevel: indentationLevel < 0 ? 0 : indentationLevel,
|
|
201
|
+
newline,
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
const indentationLevel = currentIndent().level;
|
|
205
|
+
lineTokens.push({
|
|
206
|
+
kind: "line",
|
|
207
|
+
line,
|
|
208
|
+
indentationLevel: indentationLevel < 0 ? 0 : indentationLevel,
|
|
209
|
+
newline,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return lineTokens;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function currentIndent() {
|
|
218
|
+
return indentStack.at(-1)!;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Children } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { OutputSymbol } from "../binder.js";
|
|
3
|
+
import { useContext } from "../context.js";
|
|
4
|
+
import { BinderContext } from "../context/binder.js";
|
|
5
|
+
import { DeclarationContext } from "../context/declaration.js";
|
|
6
|
+
import { Refkey, refkey } from "../refkey.js";
|
|
7
|
+
|
|
8
|
+
export interface DeclarationProps {
|
|
9
|
+
name?: string;
|
|
10
|
+
refkey?: Refkey;
|
|
11
|
+
symbol?: OutputSymbol;
|
|
12
|
+
children?: Children;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Declares a symbol in the current scope for this component's children.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
*
|
|
20
|
+
* This component must be called in one of two ways: with a name and an optional
|
|
21
|
+
* refkey, or else passing in the symbol. When called with a name and refkey, a
|
|
22
|
+
* symbol will be created in the current scope (provided by
|
|
23
|
+
* {@link ScopeContext}) with that name and refkey. If a refkey is not provided,
|
|
24
|
+
* `refkey(props.name)` is used.
|
|
25
|
+
*
|
|
26
|
+
* When called with a symbol, that symbol is merely exposed via
|
|
27
|
+
* {@link DeclarationContext }. It is assumed that the caller of this component
|
|
28
|
+
* has created the symbol with the `createSymbol` API on the
|
|
29
|
+
* {@link BinderContext }.
|
|
30
|
+
*
|
|
31
|
+
* @see {@link BinderContext}
|
|
32
|
+
*/
|
|
33
|
+
export function Declaration(props: DeclarationProps) {
|
|
34
|
+
const binder = useContext(BinderContext);
|
|
35
|
+
if (!binder) {
|
|
36
|
+
throw new Error("Need binder context to create declarations");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let declaration;
|
|
40
|
+
if (props.symbol) {
|
|
41
|
+
declaration = props.symbol;
|
|
42
|
+
} else {
|
|
43
|
+
const rk = props.refkey ? props.refkey : refkey(props.name);
|
|
44
|
+
declaration = binder.createSymbol({
|
|
45
|
+
name: props.name!,
|
|
46
|
+
refkey: rk,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return <DeclarationContext.Provider value={declaration}>
|
|
51
|
+
{props.children}
|
|
52
|
+
</DeclarationContext.Provider>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Children } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { useContext } from "../context.js";
|
|
3
|
+
import { IndentContext } from "../context/indent.js";
|
|
4
|
+
|
|
5
|
+
export interface IndentProps {
|
|
6
|
+
children?: Children;
|
|
7
|
+
indent?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IndentState {
|
|
11
|
+
level: number;
|
|
12
|
+
indent: string;
|
|
13
|
+
indentString: string; // awful name
|
|
14
|
+
noLeading?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function Indent(props: IndentProps) {
|
|
18
|
+
const previousIndent = useContext(IndentContext) ?? {
|
|
19
|
+
level: 0,
|
|
20
|
+
indent: props.indent ?? " ",
|
|
21
|
+
indentString: "",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const level = previousIndent.level + 1;
|
|
25
|
+
|
|
26
|
+
const currentIndent = {
|
|
27
|
+
level,
|
|
28
|
+
indent: props.indent ?? previousIndent.indent,
|
|
29
|
+
indentString: (props.indent ?? previousIndent.indent).repeat(level),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return <IndentContext.Provider value={currentIndent}>{props.children}</IndentContext.Provider>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Children } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { OutputSymbol, OutputSymbolFlags } from "../binder.js";
|
|
3
|
+
import { useContext } from "../context.js";
|
|
4
|
+
import { BinderContext } from "../context/binder.js";
|
|
5
|
+
import { MemberDeclarationContext } from "../context/member-declaration.js";
|
|
6
|
+
import { Refkey, refkey } from "../refkey.js";
|
|
7
|
+
|
|
8
|
+
export interface MemberDeclarationProps {
|
|
9
|
+
name?: string;
|
|
10
|
+
refkey?: Refkey;
|
|
11
|
+
symbol?: OutputSymbol;
|
|
12
|
+
children?: Children;
|
|
13
|
+
static?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Declares a symbol in the current member scope for this component's children.
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
*
|
|
21
|
+
* This component must be called in one of two ways: with a name and an optional
|
|
22
|
+
* refkey, or else passing in the symbol. When called with a name and refkey, a
|
|
23
|
+
* symbol will be created in the current scope (provided by
|
|
24
|
+
* {@link MemberDeclarationContext}) with that name and refkey. If a refkey is not
|
|
25
|
+
* provided, `refkey(props.name)` is used.
|
|
26
|
+
*
|
|
27
|
+
* When called with a symbol, that symbol is merely exposed via
|
|
28
|
+
* {@link MemberDeclarationContext}. It is assumed that the caller of this component
|
|
29
|
+
* has created the symbol with the `createSymbol` API on the
|
|
30
|
+
* {@link BinderContext}.
|
|
31
|
+
*
|
|
32
|
+
* @see {@link BinderContext}
|
|
33
|
+
*/
|
|
34
|
+
export function MemberDeclaration(props: MemberDeclarationProps) {
|
|
35
|
+
const binder = useContext(BinderContext);
|
|
36
|
+
if (!binder) {
|
|
37
|
+
throw new Error("Need binder context to create declarations");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let declaration;
|
|
41
|
+
if (props.symbol) {
|
|
42
|
+
declaration = props.symbol;
|
|
43
|
+
} else {
|
|
44
|
+
if (!props.name) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Must provide a member name, or else provide a member symbol",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const rk = props.refkey ? props.refkey : refkey(props.name);
|
|
50
|
+
declaration = binder.createSymbol({
|
|
51
|
+
name: props.name!,
|
|
52
|
+
refkey: rk,
|
|
53
|
+
flags: props.static ?
|
|
54
|
+
OutputSymbolFlags.StaticMember
|
|
55
|
+
: OutputSymbolFlags.InstanceMember,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return <MemberDeclarationContext.Provider value={declaration}>
|
|
60
|
+
{props.children}
|
|
61
|
+
</MemberDeclarationContext.Provider>;
|
|
62
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useContext } from "../context.js";
|
|
2
|
+
import { MemberDeclarationContext } from "../context/member-declaration.js";
|
|
3
|
+
|
|
4
|
+
export function MemberName() {
|
|
5
|
+
const declSymbol = useContext(MemberDeclarationContext);
|
|
6
|
+
if (!declSymbol) {
|
|
7
|
+
return "(no member declaration context)";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return <>{declSymbol.name}</>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Children } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { OutputSymbol } from "../binder.js";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
import { OutputSymbolFlags } from "../binder.js";
|
|
6
|
+
import { MemberScopeContext } from "../context/member-scope.js";
|
|
7
|
+
export interface MemberScopeProps {
|
|
8
|
+
/**
|
|
9
|
+
* The name of the member scope.
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The symbol that owns these members. This symbol must have either
|
|
15
|
+
* {@link OutputSymbolFlags.InstanceMemberContainer} or
|
|
16
|
+
* {@link OutputSymbolFlags.StaticMemberContainer}.
|
|
17
|
+
*/
|
|
18
|
+
owner: OutputSymbol;
|
|
19
|
+
children?: Children;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Declare a member scope, which is a scope that holds instance and static
|
|
24
|
+
* members. This scope is then used for nested instance or static member
|
|
25
|
+
* declarations and resolution of instance members.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
*
|
|
29
|
+
* The member scope contains scopes for both instance and static members.
|
|
30
|
+
* However, it does not affect the resolution of static members.
|
|
31
|
+
*/
|
|
32
|
+
export function MemberScope(props: MemberScopeProps) {
|
|
33
|
+
const context: MemberScopeContext = {
|
|
34
|
+
instanceMembers: props.owner.instanceMemberScope,
|
|
35
|
+
staticMembers: props.owner.staticMemberScope,
|
|
36
|
+
};
|
|
37
|
+
return <MemberScopeContext.Provider value={context}>
|
|
38
|
+
{props.children}
|
|
39
|
+
</MemberScopeContext.Provider>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useContext } from "../context.js";
|
|
2
|
+
import { DeclarationContext } from "../context/declaration.js";
|
|
3
|
+
|
|
4
|
+
export function Name() {
|
|
5
|
+
const declSymbol = useContext(DeclarationContext);
|
|
6
|
+
if (!declSymbol) {
|
|
7
|
+
return "";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return <>{declSymbol.name}</>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Children } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import {
|
|
3
|
+
createOutputBinder,
|
|
4
|
+
getSymbolCreator,
|
|
5
|
+
SymbolCreator,
|
|
6
|
+
} from "../binder.js";
|
|
7
|
+
import { BinderContext } from "../context/binder.js";
|
|
8
|
+
import { NamePolicyContext } from "../context/name-policy.js";
|
|
9
|
+
import { NamePolicy } from "../name-policy.js";
|
|
10
|
+
import { SourceDirectory } from "./SourceDirectory.js";
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
12
|
+
import { SourceFile } from "./SourceFile.js";
|
|
13
|
+
|
|
14
|
+
export interface OutputProps {
|
|
15
|
+
children?: Children;
|
|
16
|
+
/**
|
|
17
|
+
* External libraries whose symbols should be available for reference.
|
|
18
|
+
*/
|
|
19
|
+
externals?: SymbolCreator[];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Name policy to use for this output.
|
|
23
|
+
*/
|
|
24
|
+
namePolicy?: NamePolicy<string>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Policy for handling multiple symbols declared with the same name.
|
|
28
|
+
*/
|
|
29
|
+
nameConflictResolver?: (name: string, symbols: any[]) => void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The base path for the output contents. Defaults to "."
|
|
33
|
+
*/
|
|
34
|
+
basePath?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* This component is the root component for all your emitted output. Place your
|
|
39
|
+
* various {@link SourceDirectory} and {@link SourceFile} components inside
|
|
40
|
+
* `Output` to create directories and files in your output directory.
|
|
41
|
+
*
|
|
42
|
+
* @see {@link NamePolicyContext}
|
|
43
|
+
*/
|
|
44
|
+
export function Output(props: OutputProps) {
|
|
45
|
+
const basePath = props.basePath ?? "./";
|
|
46
|
+
const binder = createOutputBinder({
|
|
47
|
+
nameConflictResolver: props.nameConflictResolver,
|
|
48
|
+
});
|
|
49
|
+
const dir =
|
|
50
|
+
<SourceDirectory path={basePath}>
|
|
51
|
+
{props.children}
|
|
52
|
+
</SourceDirectory>;
|
|
53
|
+
|
|
54
|
+
if (props.externals) {
|
|
55
|
+
for (const global of props.externals) {
|
|
56
|
+
getSymbolCreator(global)(binder);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return <BinderContext.Provider value={binder}>
|
|
61
|
+
{
|
|
62
|
+
props.namePolicy ?
|
|
63
|
+
<NamePolicyContext.Provider value={props.namePolicy}>
|
|
64
|
+
{dir}
|
|
65
|
+
</NamePolicyContext.Provider> :
|
|
66
|
+
dir
|
|
67
|
+
}
|
|
68
|
+
</BinderContext.Provider>;
|
|
69
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Children } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { OutputScope } from "../binder.js";
|
|
3
|
+
import { useContext } from "../context.js";
|
|
4
|
+
import { BinderContext } from "../context/binder.js";
|
|
5
|
+
import { ScopeContext } from "../context/scope.js";
|
|
6
|
+
|
|
7
|
+
export interface ScopeProps {
|
|
8
|
+
kind?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
value?: OutputScope;
|
|
11
|
+
children?: Children;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function Scope(props: ScopeProps) {
|
|
15
|
+
let scope: OutputScope;
|
|
16
|
+
if (props.value) {
|
|
17
|
+
scope = props.value;
|
|
18
|
+
} else {
|
|
19
|
+
const kind = props.kind ?? "file";
|
|
20
|
+
const binder = useContext(BinderContext)!;
|
|
21
|
+
scope = binder.createScope({ kind, name: props.name! });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return <ScopeContext.Provider value={scope}>
|
|
25
|
+
{props.children}
|
|
26
|
+
</ScopeContext.Provider>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Children, getContext } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { shallowReactive } from "@vue/reactivity";
|
|
3
|
+
import { join } from "pathe";
|
|
4
|
+
import { useContext } from "../context.js";
|
|
5
|
+
import { SourceDirectoryContext } from "../context/source-directory.js";
|
|
6
|
+
|
|
7
|
+
export interface SourceDirectoryProps {
|
|
8
|
+
path: string;
|
|
9
|
+
children?: Children[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function SourceDirectory(props: SourceDirectoryProps) {
|
|
13
|
+
const parentDir = useContext(SourceDirectoryContext);
|
|
14
|
+
// todo: this can probably just use context.
|
|
15
|
+
const sdPath = parentDir ? join(parentDir.path, props.path) : props.path;
|
|
16
|
+
const nodeContext = getContext()!;
|
|
17
|
+
const context = createSourceDirectoryContext(props.path, parentDir);
|
|
18
|
+
|
|
19
|
+
nodeContext.meta ??= {};
|
|
20
|
+
nodeContext.meta.directory = {
|
|
21
|
+
path: sdPath,
|
|
22
|
+
};
|
|
23
|
+
return <SourceDirectoryContext.Provider value={context}>{props.children}</SourceDirectoryContext.Provider>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createSourceDirectoryContext(
|
|
27
|
+
path: string = "./",
|
|
28
|
+
parentDir?: SourceDirectoryContext,
|
|
29
|
+
): SourceDirectoryContext {
|
|
30
|
+
const contents = shallowReactive([] as any);
|
|
31
|
+
const context: SourceDirectoryContext = {
|
|
32
|
+
path: parentDir ? join(parentDir.path, path) : path,
|
|
33
|
+
contents,
|
|
34
|
+
addContent(content) {
|
|
35
|
+
contents.push(content);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
if (parentDir) {
|
|
39
|
+
parentDir.addContent(context);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return context;
|
|
43
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Children,
|
|
3
|
+
ComponentDefinition,
|
|
4
|
+
getContext,
|
|
5
|
+
} from "@alloy-js/core/jsx-runtime";
|
|
6
|
+
import { join } from "pathe";
|
|
7
|
+
import { useContext } from "../context.js";
|
|
8
|
+
import { SourceDirectoryContext } from "../context/source-directory.js";
|
|
9
|
+
import { SourceFileContext } from "../context/source-file.js";
|
|
10
|
+
import { Refkey } from "../refkey.js";
|
|
11
|
+
|
|
12
|
+
export interface SourceFileProps {
|
|
13
|
+
path: string;
|
|
14
|
+
filetype: string;
|
|
15
|
+
children?: Children[];
|
|
16
|
+
reference?: ComponentDefinition<{ refkey: Refkey }>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function SourceFile(props: SourceFileProps) {
|
|
20
|
+
const parentDirectory = useContext(SourceDirectoryContext)!;
|
|
21
|
+
const context: SourceFileContext = {
|
|
22
|
+
path: join(parentDirectory ? parentDirectory.path : "", props.path),
|
|
23
|
+
filetype: props.filetype,
|
|
24
|
+
reference: props.reference,
|
|
25
|
+
};
|
|
26
|
+
parentDirectory?.addContent(context);
|
|
27
|
+
const nodeContext = getContext()!;
|
|
28
|
+
nodeContext.meta ??= {};
|
|
29
|
+
nodeContext.meta.sourceFile = context;
|
|
30
|
+
return <SourceFileContext.Provider value={context}>
|
|
31
|
+
{props.children}
|
|
32
|
+
</SourceFileContext.Provider>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./Declaration.js";
|
|
2
|
+
export * from "./Indent.js";
|
|
3
|
+
export * from "./MemberDeclaration.jsx";
|
|
4
|
+
export * from "./MemberName.jsx";
|
|
5
|
+
export * from "./MemberScope.jsx";
|
|
6
|
+
export * from "./Name.jsx";
|
|
7
|
+
export * from "./Output.js";
|
|
8
|
+
export * from "./Scope.js";
|
|
9
|
+
export * from "./SourceDirectory.js";
|
|
10
|
+
export * from "./SourceFile.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { stc } from "../../utils.js";
|
|
2
|
+
import * as base from "../index.js";
|
|
3
|
+
|
|
4
|
+
export const Declaration = stc(base.Declaration);
|
|
5
|
+
export const Indent = stc(base.Indent);
|
|
6
|
+
export const Output = stc(base.Output);
|
|
7
|
+
export const Scope = stc(base.Scope);
|
|
8
|
+
export const SourceDirectory = stc(base.SourceDirectory);
|
|
9
|
+
export const SourceFile = stc(base.SourceFile);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { OutputSymbol } from "../binder.js";
|
|
2
|
+
import { ComponentContext, createContext, useContext } from "../context.js";
|
|
3
|
+
|
|
4
|
+
export interface AssignmentContext {
|
|
5
|
+
/**
|
|
6
|
+
* The symbol that is the target of the current assignment.
|
|
7
|
+
*/
|
|
8
|
+
target: OutputSymbol;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Whether the symbol has had a value assigned to it. Once the symbol has been
|
|
12
|
+
* assigned, subsequent assignments will have no effect.
|
|
13
|
+
*/
|
|
14
|
+
isAssigned: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* AssignmentContext provides the symbol that is the target of the current
|
|
19
|
+
* assignment.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
*
|
|
23
|
+
* When a variable is declared, the symbol for the variable doesn't yet know
|
|
24
|
+
* what value it will hold, because that depends on the assignment to the
|
|
25
|
+
* variable in the variable declaration's initializer. This context provides the
|
|
26
|
+
* symbol that is the target of the current assignment, so that children of an
|
|
27
|
+
* assignment or initializer can provide additional symbol information.
|
|
28
|
+
*
|
|
29
|
+
* For example, when assigning an object value expression to a variable, the
|
|
30
|
+
* object value expression should use assignment context to provide the member
|
|
31
|
+
* symbols for the object value's properties.
|
|
32
|
+
*/
|
|
33
|
+
export const AssignmentContext: ComponentContext<AssignmentContext> =
|
|
34
|
+
createContext();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates a new {@link (AssignmentContext:interface)}.
|
|
38
|
+
*
|
|
39
|
+
* @param target - The symbol that is the target of the current assignment.
|
|
40
|
+
* @returns A new {@link (AssignmentContext:interface)}.
|
|
41
|
+
*/
|
|
42
|
+
export function createAssignmentContext(
|
|
43
|
+
target: OutputSymbol,
|
|
44
|
+
): AssignmentContext {
|
|
45
|
+
return {
|
|
46
|
+
isAssigned: false,
|
|
47
|
+
target,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getAssignmentSymbol() {
|
|
52
|
+
const assignmentContext = useContext(AssignmentContext);
|
|
53
|
+
if (assignmentContext) {
|
|
54
|
+
return assignmentContext.target;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|