@alloy-js/core 0.4.0 → 0.6.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/CHANGELOG.md +53 -0
- package/babel.config.cjs +4 -1
- package/dist/src/binder.d.ts +20 -13
- package/dist/src/binder.d.ts.map +1 -1
- package/dist/src/binder.js +33 -15
- package/dist/src/binder.js.map +1 -1
- package/dist/src/code.d.ts +2 -2
- package/dist/src/code.d.ts.map +1 -1
- package/dist/src/code.js +4 -4
- package/dist/src/code.js.map +1 -1
- package/dist/src/components/Block.d.ts +25 -0
- package/dist/src/components/Block.d.ts.map +1 -0
- package/dist/src/components/Block.js +25 -0
- package/dist/src/components/Block.js.map +1 -0
- package/dist/src/components/Declaration.d.ts +1 -1
- package/dist/src/components/Declaration.d.ts.map +1 -1
- package/dist/src/components/Declaration.js +4 -0
- package/dist/src/components/Declaration.js.map +1 -1
- package/dist/src/components/For.d.ts +44 -0
- package/dist/src/components/For.d.ts.map +1 -0
- package/dist/src/components/For.js +41 -0
- package/dist/src/components/For.js.map +1 -0
- package/dist/src/components/Indent.d.ts +6 -10
- package/dist/src/components/Indent.d.ts.map +1 -1
- package/dist/src/components/Indent.js +7 -18
- package/dist/src/components/Indent.js.map +1 -1
- package/dist/src/components/List.d.ts +38 -0
- package/dist/src/components/List.d.ts.map +1 -0
- package/dist/src/components/List.js +40 -0
- package/dist/src/components/List.js.map +1 -0
- package/dist/src/components/MemberDeclaration.d.ts +1 -1
- package/dist/src/components/MemberDeclaration.d.ts.map +1 -1
- package/dist/src/components/MemberDeclaration.js.map +1 -1
- package/dist/src/components/MemberName.d.ts +1 -1
- package/dist/src/components/MemberName.d.ts.map +1 -1
- package/dist/src/components/MemberName.js +1 -1
- package/dist/src/components/MemberName.js.map +1 -1
- package/dist/src/components/MemberScope.d.ts +1 -1
- package/dist/src/components/MemberScope.d.ts.map +1 -1
- package/dist/src/components/MemberScope.js.map +1 -1
- package/dist/src/components/Name.d.ts +1 -1
- package/dist/src/components/Name.d.ts.map +1 -1
- package/dist/src/components/Name.js +1 -1
- package/dist/src/components/Name.js.map +1 -1
- package/dist/src/components/Output.d.ts +3 -2
- package/dist/src/components/Output.d.ts.map +1 -1
- package/dist/src/components/Output.js +12 -2
- package/dist/src/components/Output.js.map +1 -1
- package/dist/src/components/Scope.d.ts +1 -1
- package/dist/src/components/Scope.d.ts.map +1 -1
- package/dist/src/components/Scope.js.map +1 -1
- package/dist/src/components/Show.d.ts +8 -0
- package/dist/src/components/Show.d.ts.map +1 -0
- package/dist/src/components/Show.js +4 -0
- package/dist/src/components/Show.js.map +1 -0
- package/dist/src/components/SourceDirectory.d.ts +2 -2
- package/dist/src/components/SourceDirectory.d.ts.map +1 -1
- package/dist/src/components/SourceDirectory.js +1 -0
- package/dist/src/components/SourceDirectory.js.map +1 -1
- package/dist/src/components/SourceFile.d.ts +4 -8
- package/dist/src/components/SourceFile.d.ts.map +1 -1
- package/dist/src/components/SourceFile.js +6 -13
- package/dist/src/components/SourceFile.js.map +1 -1
- package/dist/src/components/StatementList.d.ts +9 -0
- package/dist/src/components/StatementList.d.ts.map +1 -0
- package/dist/src/components/StatementList.js +17 -0
- package/dist/src/components/StatementList.js.map +1 -0
- package/dist/src/components/Switch.d.ts +41 -0
- package/dist/src/components/Switch.d.ts.map +1 -0
- package/dist/src/components/Switch.js +41 -0
- package/dist/src/components/Switch.js.map +1 -0
- package/dist/src/components/Wrap.d.ts +20 -0
- package/dist/src/components/Wrap.d.ts.map +1 -0
- package/dist/src/components/Wrap.js +15 -0
- package/dist/src/components/Wrap.js.map +1 -0
- package/dist/src/components/index.d.ts +8 -1
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/components/index.js +7 -0
- package/dist/src/components/index.js.map +1 -1
- package/dist/src/components/stc/index.d.ts +89 -18
- package/dist/src/components/stc/index.d.ts.map +1 -1
- package/dist/src/components/stc/index.js +17 -1
- package/dist/src/components/stc/index.js.map +1 -1
- package/dist/src/context/index.d.ts +0 -1
- package/dist/src/context/index.d.ts.map +1 -1
- package/dist/src/context/index.js +0 -1
- package/dist/src/context/index.js.map +1 -1
- package/dist/src/context/member-declaration.d.ts +1 -0
- package/dist/src/context/member-declaration.d.ts.map +1 -1
- package/dist/src/context/member-declaration.js +4 -1
- package/dist/src/context/member-declaration.js.map +1 -1
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +3 -3
- package/dist/src/context.js.map +1 -1
- package/dist/src/index.browser.d.ts +3 -0
- package/dist/src/index.browser.d.ts.map +1 -0
- package/dist/src/index.browser.js +3 -0
- package/dist/src/index.browser.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/jsx-runtime.d.ts +151 -7
- package/dist/src/jsx-runtime.d.ts.map +1 -1
- package/dist/src/jsx-runtime.js +113 -12
- package/dist/src/jsx-runtime.js.map +1 -1
- package/dist/src/render.d.ts +107 -132
- package/dist/src/render.d.ts.map +1 -1
- package/dist/src/render.js +272 -178
- package/dist/src/render.js.map +1 -1
- package/dist/src/stc.d.ts +14 -0
- package/dist/src/stc.d.ts.map +1 -0
- package/dist/src/stc.js +52 -0
- package/dist/src/stc.js.map +1 -0
- package/dist/src/tap.d.ts +19 -0
- package/dist/src/tap.d.ts.map +1 -0
- package/dist/src/tap.js +39 -0
- package/dist/src/tap.js.map +1 -0
- package/dist/src/utils.d.ts +22 -15
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +95 -59
- package/dist/src/utils.js.map +1 -1
- package/dist/test/browser-build.test.d.ts +2 -0
- package/dist/test/browser-build.test.d.ts.map +1 -0
- package/dist/test/components/block.test.d.ts +2 -0
- package/dist/test/components/block.test.d.ts.map +1 -0
- package/dist/test/components/declaration.test.d.ts +2 -0
- package/dist/test/components/declaration.test.d.ts.map +1 -0
- package/dist/test/components/list.test.d.ts +2 -0
- package/dist/test/components/list.test.d.ts.map +1 -0
- package/dist/test/components/wrap.test.d.ts +2 -0
- package/dist/test/components/wrap.test.d.ts.map +1 -0
- package/dist/test/control-flow/for.test.d.ts +2 -0
- package/dist/test/control-flow/for.test.d.ts.map +1 -0
- package/dist/test/control-flow/match.test.d.ts +2 -0
- package/dist/test/control-flow/match.test.d.ts.map +1 -0
- package/dist/test/control-flow/show.test.d.ts +2 -0
- package/dist/test/control-flow/show.test.d.ts.map +1 -0
- package/dist/test/reactivity/cleanup.test.d.ts +2 -0
- package/dist/test/reactivity/cleanup.test.d.ts.map +1 -0
- package/dist/test/reactivity/memo.test.d.ts +2 -0
- package/dist/test/reactivity/memo.test.d.ts.map +1 -0
- package/dist/test/reactivity/untrack.test.d.ts +2 -0
- package/dist/test/reactivity/untrack.test.d.ts.map +1 -0
- package/dist/test/rendering/formatting.test.d.ts +2 -0
- package/dist/test/rendering/formatting.test.d.ts.map +1 -0
- package/dist/test/rendering/memoization.test.d.ts +2 -0
- package/dist/test/rendering/memoization.test.d.ts.map +1 -0
- package/dist/test/split-props.test.d.ts +2 -0
- package/dist/test/split-props.test.d.ts.map +1 -0
- package/dist/test/stc.test.d.ts.map +1 -1
- package/dist/test/utils.test.d.ts.map +1 -1
- package/dist/testing/extend-expect.js +4 -4
- package/dist/testing/extend-expect.js.map +1 -1
- package/dist/testing/render.d.ts +2 -3
- package/dist/testing/render.d.ts.map +1 -1
- package/dist/testing/render.js +2 -4
- package/dist/testing/render.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -9
- package/src/binder.ts +60 -50
- package/src/code.ts +17 -12
- package/src/components/Block.tsx +44 -0
- package/src/components/Declaration.tsx +10 -4
- package/src/components/For.tsx +81 -0
- package/src/components/Indent.tsx +20 -27
- package/src/components/List.tsx +94 -0
- package/src/components/MemberDeclaration.tsx +9 -6
- package/src/components/MemberScope.tsx +4 -2
- package/src/components/Output.tsx +27 -14
- package/src/components/Scope.tsx +4 -2
- package/src/components/Show.tsx +11 -0
- package/src/components/SourceDirectory.tsx +6 -2
- package/src/components/SourceFile.tsx +13 -17
- package/src/components/StatementList.tsx +16 -0
- package/src/components/Switch.tsx +62 -0
- package/src/components/Wrap.tsx +29 -0
- package/src/components/index.tsx +8 -1
- package/src/components/stc/index.ts +18 -1
- package/src/context/index.ts +0 -1
- package/src/context/member-declaration.ts +9 -1
- package/src/context.ts +2 -3
- package/src/index.browser.ts +2 -0
- package/src/index.ts +2 -0
- package/src/jsx-runtime.ts +265 -23
- package/src/render.ts +382 -200
- package/src/stc.ts +95 -0
- package/src/tap.ts +69 -0
- package/src/utils.ts +162 -95
- package/temp/api.json +8042 -1886
- package/test/browser-build.test.ts +91 -0
- package/test/children.test.tsx +8 -10
- package/test/components/block.test.tsx +48 -0
- package/test/components/declaration.test.tsx +37 -0
- package/test/components/list.test.tsx +91 -0
- package/test/components/slot.test.tsx +31 -25
- package/test/components/source-file.test.tsx +11 -31
- package/test/components/wrap.test.tsx +42 -0
- package/test/control-flow/for.test.tsx +194 -0
- package/test/control-flow/match.test.tsx +49 -0
- package/test/control-flow/show.test.tsx +25 -0
- package/test/name-policy.test.tsx +5 -5
- package/test/reactivity/cleanup.test.tsx +91 -0
- package/test/reactivity/memo.test.tsx +17 -0
- package/test/reactivity/ref-rendering.test.tsx +3 -8
- package/test/reactivity/test.test.tsx +7 -6
- package/test/reactivity/untrack.test.ts +33 -0
- package/test/rendering/basic.test.tsx +25 -47
- package/test/rendering/code.test.tsx +3 -3
- package/test/rendering/formatting.test.tsx +487 -0
- package/test/rendering/indent.test.tsx +42 -529
- package/test/rendering/memoization.test.tsx +30 -0
- package/test/split-props.test.ts +87 -0
- package/test/stc.test.tsx +29 -8
- package/test/symbols.test.ts +132 -3
- package/test/utils.test.tsx +129 -20
- package/testing/extend-expect.ts +14 -4
- package/testing/render.ts +2 -4
- package/testing/vitest.d.ts +6 -1
- package/vitest.config.ts +1 -1
- package/dist/src/context/indent.d.ts +0 -5
- package/dist/src/context/indent.d.ts.map +0 -1
- package/dist/src/context/indent.js +0 -8
- package/dist/src/context/indent.js.map +0 -1
- package/dist/test/rendering/linebreaks.test.d.ts +0 -2
- package/dist/test/rendering/linebreaks.test.d.ts.map +0 -1
- package/src/context/indent.ts +0 -17
- package/test/rendering/linebreaks.test.tsx +0 -72
package/src/render.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { isRef } from "@vue/reactivity";
|
|
2
|
-
import {
|
|
2
|
+
import { Doc, doc } from "prettier";
|
|
3
|
+
import prettier from "prettier/doc.js";
|
|
3
4
|
import { useContext } from "./context.js";
|
|
4
|
-
import { IndentContext } from "./context/indent.js";
|
|
5
5
|
import { SourceFileContext } from "./context/source-file.js";
|
|
6
6
|
import {
|
|
7
7
|
Child,
|
|
8
8
|
Children,
|
|
9
9
|
Context,
|
|
10
|
+
CustomContext,
|
|
10
11
|
effect,
|
|
11
12
|
getContext,
|
|
13
|
+
getElementCache,
|
|
14
|
+
IntrinsicElement,
|
|
12
15
|
isComponentCreator,
|
|
16
|
+
isCustomContext,
|
|
17
|
+
isIntrinsicElement,
|
|
13
18
|
popStack,
|
|
14
19
|
printRenderStack,
|
|
15
20
|
pushStack,
|
|
@@ -17,138 +22,106 @@ import {
|
|
|
17
22
|
untrack,
|
|
18
23
|
} from "./jsx-runtime.js";
|
|
19
24
|
import { isRefkey } from "./refkey.js";
|
|
25
|
+
const {
|
|
26
|
+
builders: {
|
|
27
|
+
align,
|
|
28
|
+
breakParent,
|
|
29
|
+
dedent,
|
|
30
|
+
dedentToRoot,
|
|
31
|
+
fill,
|
|
32
|
+
group,
|
|
33
|
+
hardline,
|
|
34
|
+
indent,
|
|
35
|
+
line,
|
|
36
|
+
lineSuffix,
|
|
37
|
+
lineSuffixBoundary,
|
|
38
|
+
literalline,
|
|
39
|
+
markAsRoot,
|
|
40
|
+
softline,
|
|
41
|
+
ifBreak,
|
|
42
|
+
},
|
|
43
|
+
} = prettier;
|
|
20
44
|
|
|
21
45
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* Children) are three distinct types of things:
|
|
46
|
+
* Turning components into source text involves three different trees produced
|
|
47
|
+
* sequentially:
|
|
25
48
|
*
|
|
26
|
-
* 1.
|
|
27
|
-
* 2.
|
|
28
|
-
* 3.
|
|
29
|
-
* property accesses and function calls that might be reactive.
|
|
49
|
+
* 1. Component tree, built by the nesting of components
|
|
50
|
+
* 2. Rendered text tree, produced by *rendering* the component tree
|
|
51
|
+
* 3. Document tree, produced by *printing* the rendered text tree
|
|
30
52
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* component tree, invoking components, wrapping memos, doing whitespace
|
|
34
|
-
* normalization, and other activities. There are four types of nodes in the
|
|
35
|
-
* render tree.
|
|
53
|
+
* Finally, the document tree is converted to text via `prettier`. Let's look at
|
|
54
|
+
* each of these trees and the conversions in detail.
|
|
36
55
|
*
|
|
37
|
-
*
|
|
38
|
-
* types are either converted to the empty string or stringified as
|
|
39
|
-
* appropriate.
|
|
40
|
-
* 2. Components, which are possibly wrapped if they are indented.
|
|
41
|
-
* 3. Memos, which are wrapped in a reactive effect which updates its render
|
|
42
|
-
* tree nodes when its value changes.
|
|
43
|
-
* 4. Arrays of these things.
|
|
56
|
+
* # Component tree
|
|
44
57
|
*
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
-
* memo, or array, the contents of that substitution are indented appropriately.
|
|
48
|
-
* This is accomplished by wrapping those substitutions in an indent component.
|
|
58
|
+
* The component tree is built by JSX or STC templates. The nodes in this tree
|
|
59
|
+
* are defined by the type `Child` and are one of the following
|
|
49
60
|
*
|
|
50
|
-
*
|
|
51
|
-
* follows:
|
|
61
|
+
* ## Primitive values
|
|
52
62
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* Recursively normalize nested array elements.
|
|
57
|
-
* 2. Use the first text node to determine the literal indent level of the
|
|
58
|
-
* children. Remove all preceding whitespace - any indent of the first
|
|
59
|
-
* line is provided in the text nodes preceding the reference to this
|
|
60
|
-
* component. If the first element is not a literal string, then no
|
|
61
|
-
* literal indent is applied, and all indentation within the component
|
|
62
|
-
* becomes significant.
|
|
63
|
-
* 3. For each child of the component, render it:
|
|
64
|
-
* 1. If it is a string, reindent it by splitting on lines and replacing
|
|
65
|
-
* the detected literal whitespace with the current indent level,
|
|
66
|
-
* skipping the first line. If the string ends with a larger literal
|
|
67
|
-
* indent than the detected literal indent, then a subsequent child
|
|
68
|
-
* will be indented.
|
|
69
|
-
* 2. If it's a component, if the next child should be indented, create an
|
|
70
|
-
* Indent component and wrap the component's children in it.
|
|
71
|
-
* 3. If it's a function, if the next child should be indented, wrap it in
|
|
72
|
-
* an indent component. Any elements processed as a result of executing
|
|
73
|
-
* the memo are treated as first elements in a child array are with
|
|
74
|
-
* respect to establishing literal indent level and whitespace trimming
|
|
75
|
-
* behavior.
|
|
76
|
-
* 4. If it's an array, if the next child should be indented, create an
|
|
77
|
-
* Indent component and wrap it the array in it.
|
|
63
|
+
* Strings in the tree are placed into the rendered text tree as-is. Numbers are
|
|
64
|
+
* converted to strings. Falsy primitive values and booleans are converted to
|
|
65
|
+
* empty strings (and may cause a line break to be ignored, see below).
|
|
78
66
|
*
|
|
79
|
-
*
|
|
67
|
+
* ## Nullary functions
|
|
80
68
|
*
|
|
81
|
-
*
|
|
69
|
+
* Nullary functions represent computed or reactive values in the component
|
|
70
|
+
* tree, such as expressions placed into a JSX template with curly brackets.
|
|
71
|
+
* Nullary functions return `Children` which are then recursively rendered.
|
|
72
|
+
* Nullary functions are invoked in an effect which will update the rendered
|
|
73
|
+
* text tree when any reactive dependencies change.
|
|
82
74
|
*
|
|
83
|
-
*
|
|
84
|
-
* ```
|
|
85
|
-
* <Indent>
|
|
86
|
-
* <Foo />
|
|
87
|
-
* <Foo />
|
|
88
|
-
* </Indent>
|
|
89
|
-
* ```
|
|
75
|
+
* ## Component creators
|
|
90
76
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* "\n ",
|
|
98
|
-
* createComponent(Foo, {}),
|
|
99
|
-
* "\n ",
|
|
100
|
-
* createComponent(Foo, {}),
|
|
101
|
-
* "\n"
|
|
102
|
-
* ]
|
|
103
|
-
* }
|
|
104
|
-
* })
|
|
105
|
-
* ]
|
|
106
|
-
* ```
|
|
77
|
+
* Component creators are a special kind of nullary function which instantiate
|
|
78
|
+
* components in order to get their children which are then recursively
|
|
79
|
+
* rendered. Component creators have some special rendering behavior, such as
|
|
80
|
+
* tracking the stack of rendered components. Like other nullary functions,
|
|
81
|
+
* component creators are invoked in an effect which will update the rendered
|
|
82
|
+
* text tree when any reactive dependencies change.
|
|
107
83
|
*
|
|
108
|
-
*
|
|
109
|
-
* ```
|
|
110
|
-
* [ // node for Indent
|
|
111
|
-
* [ // node for Context Provider
|
|
112
|
-
* " ", // indent from the children of Indent
|
|
113
|
-
* [ // component for Foo
|
|
114
|
-
* "Foo" // result of calling Foo
|
|
115
|
-
* ],
|
|
116
|
-
* "\n ", // indent and line break from the children of Ident
|
|
117
|
-
* [ "Foo" ] // second foo component
|
|
118
|
-
* ]
|
|
119
|
-
* ]
|
|
120
|
-
* ```
|
|
121
|
-
* ### Rendered text
|
|
122
|
-
* ```
|
|
123
|
-
* FooFoo
|
|
124
|
-
* ```
|
|
84
|
+
* ## Refs
|
|
125
85
|
*
|
|
126
|
-
*
|
|
86
|
+
* Refs are wrapped in a nullary function and rendered in an effect which will
|
|
87
|
+
* update the rendered text tree when the ref's value changes. This is
|
|
88
|
+
* essentially a syntactic convenience, allowing JSX templates to contain
|
|
89
|
+
* `\{ someRef \}` instead of `\{ someRef.value \}`.
|
|
127
90
|
*
|
|
128
|
-
*
|
|
129
|
-
* ```
|
|
130
|
-
* <>
|
|
131
|
-
* base
|
|
132
|
-
* <Foo /> <Foo />
|
|
133
|
-
* </>
|
|
134
|
-
* ```
|
|
91
|
+
* ## Refkey
|
|
135
92
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
93
|
+
* Refkeys are replaced with a component creator for the Reference component
|
|
94
|
+
* associated with the current source file. This allows creating references by
|
|
95
|
+
* placing them directly in the component tree e.g. `{ someRefkey }`.
|
|
96
|
+
*
|
|
97
|
+
* ## CustomContext
|
|
98
|
+
*
|
|
99
|
+
* CustomContext is a special kind of component which allows rendering children
|
|
100
|
+
* within a custom reactive context. This enables components to manually manage
|
|
101
|
+
* the lifetime of their reactive contexts.
|
|
102
|
+
*
|
|
103
|
+
* ## IntrinsicElement
|
|
104
|
+
*
|
|
105
|
+
* Various intrinsic elements exist to control formatting. These elements
|
|
106
|
+
* provide Print Hooks that are called during Printing.
|
|
107
|
+
*
|
|
108
|
+
* # Rendered Text Tree
|
|
109
|
+
*
|
|
110
|
+
* This tree is a nested array structure containing the rendered output of all
|
|
111
|
+
* the components in the component tree. This structure is updated reactively
|
|
112
|
+
* when reactive dependencies change. The nodes in this tree are predominantly
|
|
113
|
+
* strings, but can also be Print Hooks.
|
|
114
|
+
*
|
|
115
|
+
* This tree is built by the `renderTree` function.
|
|
116
|
+
*
|
|
117
|
+
* # Document Tree
|
|
118
|
+
*
|
|
119
|
+
* This tree is constructed by calling `printTree` on the rendered text tree.
|
|
120
|
+
* The rendered text tree is walked and the appropriate Prettier builders are
|
|
121
|
+
* called to produce a document tree. The result is then passed to Prettier's
|
|
122
|
+
* `printDocToString` function to produce the final source text.
|
|
149
123
|
*/
|
|
150
124
|
|
|
151
|
-
//
|
|
152
125
|
export interface OutputDirectory {
|
|
153
126
|
kind: "directory";
|
|
154
127
|
path: string;
|
|
@@ -162,23 +135,60 @@ export interface OutputFile {
|
|
|
162
135
|
filetype: string;
|
|
163
136
|
}
|
|
164
137
|
|
|
165
|
-
const nodesToContext = new WeakMap<
|
|
138
|
+
const nodesToContext = new WeakMap<RenderedTextTree, Context>();
|
|
166
139
|
|
|
167
|
-
export function getContextForRenderNode(node:
|
|
140
|
+
export function getContextForRenderNode(node: RenderedTextTree) {
|
|
168
141
|
return nodesToContext.get(node);
|
|
169
142
|
}
|
|
170
|
-
export type RenderStructure = {};
|
|
171
143
|
|
|
172
|
-
export
|
|
144
|
+
export const printHookTag = Symbol();
|
|
145
|
+
|
|
146
|
+
export interface PrintHook {
|
|
147
|
+
[printHookTag]: true;
|
|
148
|
+
transform?(tree: RenderedTextTree): RenderedTextTree;
|
|
149
|
+
print?(
|
|
150
|
+
tree: RenderedTextTree,
|
|
151
|
+
print: (subtree: RenderedTextTree) => Doc,
|
|
152
|
+
): Doc;
|
|
153
|
+
subtree: RenderedTextTree;
|
|
154
|
+
}
|
|
173
155
|
|
|
174
|
-
function
|
|
156
|
+
export function createRenderTreeHook(
|
|
157
|
+
subtree: RenderedTextTree,
|
|
158
|
+
hooks: Omit<PrintHook, typeof printHookTag | "subtree">,
|
|
159
|
+
): PrintHook {
|
|
160
|
+
return {
|
|
161
|
+
[printHookTag]: true,
|
|
162
|
+
subtree,
|
|
163
|
+
...hooks,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function isPrintHook(type: unknown): type is PrintHook {
|
|
168
|
+
return typeof type === "object" && type !== null && printHookTag in type;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type RenderedTextTree = (string | RenderedTextTree | PrintHook)[];
|
|
172
|
+
|
|
173
|
+
function traceRender(phase: string, message: () => string) {
|
|
175
174
|
return false;
|
|
176
|
-
//
|
|
175
|
+
//console.log(`[\x1b[34m${phase}\x1b[0m]: ${message()}`);
|
|
177
176
|
}
|
|
178
177
|
|
|
179
|
-
export function render(
|
|
178
|
+
export function render(
|
|
179
|
+
children: Children,
|
|
180
|
+
options?: PrintTreeOptions,
|
|
181
|
+
): OutputDirectory {
|
|
180
182
|
const tree = renderTree(children);
|
|
181
183
|
let rootDirectory: OutputDirectory | undefined = undefined;
|
|
184
|
+
|
|
185
|
+
// when passing Output, the first render tree child is the Output component.
|
|
186
|
+
const rootRenderOptions =
|
|
187
|
+
Array.isArray(tree) ?
|
|
188
|
+
(getContextForRenderNode(tree[0] as RenderedTextTree)?.meta
|
|
189
|
+
?.printOptions ?? {})
|
|
190
|
+
: {};
|
|
191
|
+
|
|
182
192
|
collectSourceFiles(undefined, tree);
|
|
183
193
|
|
|
184
194
|
if (!rootDirectory) {
|
|
@@ -191,7 +201,7 @@ export function render(children: Children): OutputDirectory {
|
|
|
191
201
|
|
|
192
202
|
function collectSourceFiles(
|
|
193
203
|
currentDirectory: OutputDirectory | undefined,
|
|
194
|
-
root:
|
|
204
|
+
root: RenderedTextTree,
|
|
195
205
|
) {
|
|
196
206
|
if (!Array.isArray(root)) {
|
|
197
207
|
return;
|
|
@@ -222,11 +232,25 @@ export function render(children: Children): OutputDirectory {
|
|
|
222
232
|
"Source file doesn't have parent directory. Make sure you have used the Output component.",
|
|
223
233
|
);
|
|
224
234
|
}
|
|
235
|
+
|
|
225
236
|
const sourceFile: OutputFile = {
|
|
226
237
|
kind: "file",
|
|
227
238
|
path: context.meta?.sourceFile.path,
|
|
228
239
|
filetype: context.meta?.sourceFile.filetype,
|
|
229
|
-
contents: (root
|
|
240
|
+
contents: printTree(root, {
|
|
241
|
+
printWidth:
|
|
242
|
+
options?.printWidth ??
|
|
243
|
+
context.meta?.printOptions?.printWidth ??
|
|
244
|
+
rootRenderOptions.printWidth,
|
|
245
|
+
tabWidth:
|
|
246
|
+
options?.tabWidth ??
|
|
247
|
+
context.meta?.printOptions?.tabWidth ??
|
|
248
|
+
rootRenderOptions.tabWidth,
|
|
249
|
+
useTabs:
|
|
250
|
+
options?.useTabs ??
|
|
251
|
+
context.meta?.printOptions?.useTabs ??
|
|
252
|
+
rootRenderOptions.useTabs,
|
|
253
|
+
}),
|
|
230
254
|
};
|
|
231
255
|
|
|
232
256
|
currentDirectory.contents.push(sourceFile);
|
|
@@ -236,20 +260,17 @@ export function render(children: Children): OutputDirectory {
|
|
|
236
260
|
|
|
237
261
|
function recurse(cwd: OutputDirectory | undefined) {
|
|
238
262
|
for (const child of root) {
|
|
239
|
-
collectSourceFiles(cwd, child as
|
|
263
|
+
collectSourceFiles(cwd, child as RenderedTextTree);
|
|
240
264
|
}
|
|
241
265
|
}
|
|
242
266
|
}
|
|
243
267
|
}
|
|
244
268
|
|
|
245
269
|
export function renderTree(children: Children) {
|
|
246
|
-
const rootElem:
|
|
247
|
-
const state: RenderState = {
|
|
248
|
-
newline: false,
|
|
249
|
-
};
|
|
270
|
+
const rootElem: RenderedTextTree = [];
|
|
250
271
|
try {
|
|
251
272
|
root(() => {
|
|
252
|
-
renderWorker(rootElem, children
|
|
273
|
+
renderWorker(rootElem, children);
|
|
253
274
|
});
|
|
254
275
|
} catch (e) {
|
|
255
276
|
printRenderStack();
|
|
@@ -259,92 +280,191 @@ export function renderTree(children: Children) {
|
|
|
259
280
|
return rootElem;
|
|
260
281
|
}
|
|
261
282
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function renderWorker(
|
|
267
|
-
node: RenderTextTree,
|
|
268
|
-
children: Children,
|
|
269
|
-
state: RenderState,
|
|
270
|
-
) {
|
|
271
|
-
traceRender("render", dumpChildren(children));
|
|
283
|
+
function renderWorker(node: RenderedTextTree, children: Children) {
|
|
284
|
+
traceRender("render", () => dumpChildren(children));
|
|
272
285
|
|
|
273
286
|
if (Array.isArray(node)) {
|
|
274
287
|
nodesToContext.set(node, getContext()!);
|
|
275
288
|
}
|
|
276
289
|
|
|
277
|
-
const indent = useContext(IndentContext)!;
|
|
278
290
|
if (Array.isArray(children)) {
|
|
279
|
-
for (const child of children) {
|
|
280
|
-
appendChild(node, child
|
|
291
|
+
for (const child of (children as any).flat(Infinity)) {
|
|
292
|
+
appendChild(node, child);
|
|
281
293
|
}
|
|
282
294
|
} else {
|
|
283
|
-
appendChild(node, children
|
|
295
|
+
appendChild(node, children);
|
|
284
296
|
}
|
|
285
297
|
}
|
|
286
298
|
|
|
287
|
-
function appendChild(
|
|
288
|
-
|
|
289
|
-
rawChild: Child,
|
|
290
|
-
indentState: IndentState,
|
|
291
|
-
state: RenderState,
|
|
292
|
-
) {
|
|
293
|
-
traceRender("appendChild", printChild(rawChild));
|
|
299
|
+
function appendChild(node: RenderedTextTree, rawChild: Child) {
|
|
300
|
+
traceRender("appendChild", () => debugPrintChild(rawChild));
|
|
294
301
|
const child = normalizeChild(rawChild);
|
|
295
302
|
|
|
296
303
|
if (typeof child === "string") {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
304
|
+
node.push(child);
|
|
305
|
+
} else {
|
|
306
|
+
const cache = getElementCache();
|
|
307
|
+
if (cache.has(child as any)) {
|
|
308
|
+
traceRender("appendChild:cached", () => debugPrintChild(child));
|
|
309
|
+
node.push(cache.get(child as any)!);
|
|
310
|
+
return;
|
|
301
311
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
312
|
+
if (isCustomContext(child)) {
|
|
313
|
+
traceRender("appendChild:custom-context", () => debugPrintChild(child));
|
|
314
|
+
child.useCustomContext((children) => {
|
|
315
|
+
const newNode: RenderedTextTree = [];
|
|
316
|
+
renderWorker(newNode, children);
|
|
317
|
+
node.push(newNode);
|
|
318
|
+
cache.set(child, newNode);
|
|
319
|
+
});
|
|
320
|
+
} else if (isIntrinsicElement(child)) {
|
|
321
|
+
// don't need a new context here because intrinsics are never reactive
|
|
322
|
+
traceRender("appendChild:intrinsic-element", () =>
|
|
323
|
+
debugPrintChild(child),
|
|
324
|
+
);
|
|
325
|
+
const newNode: RenderedTextTree = [];
|
|
326
|
+
|
|
327
|
+
function formatHookWithChildren(command: (doc: Doc) => Doc) {
|
|
328
|
+
node.push(
|
|
329
|
+
createRenderTreeHook(newNode, {
|
|
330
|
+
print(tree, print) {
|
|
331
|
+
return command(print(tree));
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
);
|
|
335
|
+
renderWorker(newNode, (child as any).props.children);
|
|
310
336
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const index = node.length;
|
|
321
|
-
effect((prev: any) => {
|
|
322
|
-
traceRender("memoEffect:run", "");
|
|
323
|
-
let res = child();
|
|
324
|
-
while (typeof res === "function" && !isComponentCreator(res)) {
|
|
325
|
-
res = res();
|
|
337
|
+
|
|
338
|
+
function formatHook(command: Doc) {
|
|
339
|
+
return node.push(
|
|
340
|
+
createRenderTreeHook(newNode, {
|
|
341
|
+
print() {
|
|
342
|
+
return command;
|
|
343
|
+
},
|
|
344
|
+
}),
|
|
345
|
+
);
|
|
326
346
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
347
|
+
|
|
348
|
+
switch (child.name) {
|
|
349
|
+
case "indent":
|
|
350
|
+
return formatHookWithChildren(indent);
|
|
351
|
+
case "fill":
|
|
352
|
+
return formatHookWithChildren(fill as any);
|
|
353
|
+
case "group":
|
|
354
|
+
node.push(
|
|
355
|
+
createRenderTreeHook(newNode, {
|
|
356
|
+
print(tree, print) {
|
|
357
|
+
return group(print(tree), {
|
|
358
|
+
id: child.props.id,
|
|
359
|
+
shouldBreak: child.props.shouldBreak,
|
|
360
|
+
});
|
|
361
|
+
},
|
|
362
|
+
}),
|
|
363
|
+
);
|
|
364
|
+
renderWorker(newNode, child.props.children);
|
|
365
|
+
return;
|
|
366
|
+
case "line":
|
|
367
|
+
case "br":
|
|
368
|
+
return formatHook(line);
|
|
369
|
+
case "hbr":
|
|
370
|
+
case "hardline":
|
|
371
|
+
return formatHook(hardline);
|
|
372
|
+
case "sbr":
|
|
373
|
+
case "softline":
|
|
374
|
+
return formatHook(softline);
|
|
375
|
+
case "literalline":
|
|
376
|
+
case "lbr":
|
|
377
|
+
return formatHook(literalline);
|
|
378
|
+
case "align":
|
|
379
|
+
node.push(
|
|
380
|
+
createRenderTreeHook(newNode, {
|
|
381
|
+
print(tree, print) {
|
|
382
|
+
return align(
|
|
383
|
+
(child.props as any).width ?? (child.props as any).string!,
|
|
384
|
+
print(tree),
|
|
385
|
+
);
|
|
386
|
+
},
|
|
387
|
+
}),
|
|
388
|
+
);
|
|
389
|
+
renderWorker(newNode, (child as any).props.children);
|
|
390
|
+
return;
|
|
391
|
+
case "lineSuffix":
|
|
392
|
+
return formatHookWithChildren(lineSuffix);
|
|
393
|
+
case "lineSuffixBoundary":
|
|
394
|
+
return formatHook(lineSuffixBoundary);
|
|
395
|
+
case "breakParent":
|
|
396
|
+
return formatHook(breakParent);
|
|
397
|
+
case "dedent":
|
|
398
|
+
return formatHookWithChildren(dedent);
|
|
399
|
+
case "dedentToRoot":
|
|
400
|
+
return formatHookWithChildren(dedentToRoot);
|
|
401
|
+
case "markAsRoot":
|
|
402
|
+
return formatHookWithChildren(markAsRoot);
|
|
403
|
+
case "ifBreak":
|
|
404
|
+
node.push(
|
|
405
|
+
createRenderTreeHook(newNode, {
|
|
406
|
+
print(tree, print) {
|
|
407
|
+
return ifBreak(
|
|
408
|
+
print((tree as RenderedTextTree[])[0]),
|
|
409
|
+
print((tree as RenderedTextTree[])[1]),
|
|
410
|
+
);
|
|
411
|
+
},
|
|
412
|
+
}),
|
|
413
|
+
);
|
|
414
|
+
newNode.push([], []);
|
|
415
|
+
renderWorker(
|
|
416
|
+
newNode[0] as RenderedTextTree[],
|
|
417
|
+
(child as any).props.children,
|
|
418
|
+
);
|
|
419
|
+
renderWorker(
|
|
420
|
+
newNode[1] as RenderedTextTree[],
|
|
421
|
+
(child as any).props.flatContents,
|
|
422
|
+
);
|
|
423
|
+
return;
|
|
424
|
+
default:
|
|
425
|
+
throw new Error("Unknown intrinsic element");
|
|
426
|
+
}
|
|
427
|
+
} else if (isComponentCreator(child)) {
|
|
428
|
+
effect(() => {
|
|
429
|
+
traceRender("appendChild:component", () => debugPrintChild(child));
|
|
430
|
+
const componentRoot: RenderedTextTree = [];
|
|
431
|
+
pushStack(child.component, child.props);
|
|
432
|
+
renderWorker(componentRoot, untrack(child));
|
|
433
|
+
popStack();
|
|
434
|
+
node.push(componentRoot);
|
|
435
|
+
cache.set(child, componentRoot);
|
|
436
|
+
traceRender("appendChild:component-done", () => debugPrintChild(child));
|
|
437
|
+
});
|
|
438
|
+
} else if (typeof child === "function") {
|
|
439
|
+
traceRender("appendChild:memo", () => child.toString());
|
|
440
|
+
const index = node.length;
|
|
441
|
+
effect(() => {
|
|
442
|
+
traceRender("memoEffect:run", () => "");
|
|
443
|
+
let res = child();
|
|
444
|
+
while (typeof res === "function" && !isComponentCreator(res)) {
|
|
445
|
+
res = res();
|
|
446
|
+
}
|
|
447
|
+
const newNodes: RenderedTextTree = [];
|
|
448
|
+
renderWorker(newNodes, res);
|
|
449
|
+
node[index] = newNodes;
|
|
450
|
+
cache.set(child, newNodes);
|
|
451
|
+
return newNodes;
|
|
452
|
+
});
|
|
453
|
+
traceRender("appendChild:memo-done", () => "");
|
|
454
|
+
} else {
|
|
455
|
+
throw new Error("Unexpected child type");
|
|
456
|
+
}
|
|
338
457
|
}
|
|
339
458
|
}
|
|
340
459
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
460
|
+
type NormalizedChildren = NormalizedChild | NormalizedChildren[];
|
|
461
|
+
type NormalizedChild =
|
|
462
|
+
| string
|
|
463
|
+
| (() => Child | Children)
|
|
464
|
+
| CustomContext
|
|
465
|
+
| IntrinsicElement;
|
|
346
466
|
|
|
347
|
-
function normalizeChild(child: Child):
|
|
467
|
+
function normalizeChild(child: Child): NormalizedChildren {
|
|
348
468
|
if (Array.isArray(child)) {
|
|
349
469
|
return child.map(normalizeChild);
|
|
350
470
|
} else if (typeof child === "string" || typeof child === "function") {
|
|
@@ -366,24 +486,86 @@ function normalizeChild(child: Child): NormalizedChild {
|
|
|
366
486
|
|
|
367
487
|
return sfContext.reference({ refkey: child });
|
|
368
488
|
};
|
|
489
|
+
} else if (isCustomContext(child)) {
|
|
490
|
+
return child;
|
|
491
|
+
} else if (isIntrinsicElement(child)) {
|
|
492
|
+
return child;
|
|
369
493
|
} else {
|
|
370
494
|
return String(child);
|
|
371
495
|
}
|
|
372
496
|
}
|
|
373
497
|
|
|
374
|
-
function dumpChildren(children:
|
|
498
|
+
function dumpChildren(children: Children): string {
|
|
375
499
|
if (Array.isArray(children)) {
|
|
376
|
-
return `[ ${children.map(
|
|
500
|
+
return `[ ${children.map(debugPrintChild).join(", ")} ]`;
|
|
377
501
|
}
|
|
378
|
-
return
|
|
502
|
+
return debugPrintChild(children);
|
|
379
503
|
}
|
|
380
504
|
|
|
381
|
-
function
|
|
505
|
+
function debugPrintChild(child: Children): string {
|
|
382
506
|
if (isComponentCreator(child)) {
|
|
383
507
|
return "<" + child.component.name + ">";
|
|
384
508
|
} else if (typeof child === "function") {
|
|
385
509
|
return "$memo";
|
|
510
|
+
} else if (isRef(child)) {
|
|
511
|
+
return "$ref";
|
|
386
512
|
} else {
|
|
387
513
|
return JSON.stringify(child);
|
|
388
514
|
}
|
|
389
515
|
}
|
|
516
|
+
|
|
517
|
+
export interface PrintTreeOptions {
|
|
518
|
+
/**
|
|
519
|
+
* The number of characters the printer will wrap on. Defaults to 100
|
|
520
|
+
* characters.
|
|
521
|
+
*/
|
|
522
|
+
printWidth?: number;
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Whether to use tabs instead of spaces for indentation. Defaults to false.
|
|
526
|
+
*/
|
|
527
|
+
useTabs?: boolean;
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* The number of spaces to use for indentation. Defaults to 2 spaces.
|
|
531
|
+
*/
|
|
532
|
+
tabWidth?: number;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const defaultPrintTreeOptions: PrintTreeOptions = {
|
|
536
|
+
printWidth: 80,
|
|
537
|
+
tabWidth: 2,
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
export function printTree(tree: RenderedTextTree, options?: PrintTreeOptions) {
|
|
541
|
+
options = {
|
|
542
|
+
...defaultPrintTreeOptions,
|
|
543
|
+
...Object.fromEntries(
|
|
544
|
+
Object.entries(options ?? {}).filter(([_, v]) => v !== undefined),
|
|
545
|
+
),
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
const d = printTreeWorker(tree);
|
|
549
|
+
return doc.printer.printDocToString(d, options as doc.printer.Options)
|
|
550
|
+
.formatted;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function printTreeWorker(tree: RenderedTextTree): Doc {
|
|
554
|
+
const doc: Doc = [];
|
|
555
|
+
for (const node of tree) {
|
|
556
|
+
if (typeof node === "string") {
|
|
557
|
+
const normalizedNode = node
|
|
558
|
+
.split(/\r?\n/)
|
|
559
|
+
.flatMap((line, index, array) =>
|
|
560
|
+
index < array.length - 1 ? [line, hardline] : [line],
|
|
561
|
+
);
|
|
562
|
+
doc.push(normalizedNode);
|
|
563
|
+
} else if (isPrintHook(node)) {
|
|
564
|
+
doc.push(node.print!(node.subtree, printTreeWorker));
|
|
565
|
+
} else {
|
|
566
|
+
doc.push(printTreeWorker(node));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return doc;
|
|
571
|
+
}
|