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