@reckona/mreact-compiler 0.0.81 → 0.0.83
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/README.md +15 -0
- package/dist/boundary-graph.d.ts +66 -0
- package/dist/boundary-graph.d.ts.map +1 -0
- package/dist/boundary-graph.js +568 -0
- package/dist/boundary-graph.js.map +1 -0
- package/dist/emit-boundary-lowering.d.ts +43 -0
- package/dist/emit-boundary-lowering.d.ts.map +1 -0
- package/dist/emit-boundary-lowering.js +63 -0
- package/dist/emit-boundary-lowering.js.map +1 -0
- package/dist/emit-code-builder.d.ts +9 -0
- package/dist/emit-code-builder.d.ts.map +1 -0
- package/dist/emit-code-builder.js +17 -0
- package/dist/emit-code-builder.js.map +1 -0
- package/dist/emit-server-stream.d.ts.map +1 -1
- package/dist/emit-server-stream.js +64 -84
- package/dist/emit-server-stream.js.map +1 -1
- package/dist/emit-server.d.ts.map +1 -1
- package/dist/emit-server.js +12 -1
- package/dist/emit-server.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +5 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +14 -1
- package/dist/internal.js.map +1 -1
- package/dist/types.d.ts +16 -69
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/boundary-graph.ts +904 -0
- package/src/emit-boundary-lowering.ts +165 -0
- package/src/emit-code-builder.ts +27 -0
- package/src/emit-server-stream.ts +61 -152
- package/src/emit-server.ts +12 -1
- package/src/index.ts +16 -0
- package/src/internal.ts +25 -1
- package/src/types.ts +32 -79
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
export type NestedAppendEmitter<Part> = (
|
|
2
|
+
parts: readonly Part[],
|
|
3
|
+
sinkName: string,
|
|
4
|
+
compatRenderToStringHelperName: string,
|
|
5
|
+
) => string;
|
|
6
|
+
|
|
7
|
+
export interface BoundaryLoweringContext<Part> {
|
|
8
|
+
compatRenderToStringHelperName: string;
|
|
9
|
+
emitNestedAppendStatements: NestedAppendEmitter<Part>;
|
|
10
|
+
sinkName: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AsyncBoundaryPart<Part> {
|
|
14
|
+
awaitId?: string;
|
|
15
|
+
catchName?: string;
|
|
16
|
+
catchParts?: readonly Part[];
|
|
17
|
+
parts: readonly Part[];
|
|
18
|
+
valueCode: string;
|
|
19
|
+
valueName: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface OutOfOrderBoundaryPart<Part> extends AsyncBoundaryPart<Part> {
|
|
23
|
+
id: string;
|
|
24
|
+
hydration: boolean;
|
|
25
|
+
placeholderParts: readonly Part[];
|
|
26
|
+
placeholderTagCode?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ReactSuspenseBoundaryPart<Part> {
|
|
30
|
+
parts: readonly Part[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ReactSuspenseOutOfOrderBoundaryPart<Part> extends AsyncBoundaryPart<Part> {
|
|
34
|
+
boundaryId: string;
|
|
35
|
+
fallbackParts: readonly Part[];
|
|
36
|
+
nonce?: string;
|
|
37
|
+
scriptSrc?: string;
|
|
38
|
+
segmentId: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function emitAsyncBoundary<Part>(
|
|
42
|
+
part: AsyncBoundaryPart<Part>,
|
|
43
|
+
context: BoundaryLoweringContext<Part> & {
|
|
44
|
+
asyncBoundaryHelperName: string;
|
|
45
|
+
},
|
|
46
|
+
): string {
|
|
47
|
+
const optionFields: string[] = [];
|
|
48
|
+
|
|
49
|
+
if (part.catchName !== undefined && part.catchParts !== undefined) {
|
|
50
|
+
optionFields.push(
|
|
51
|
+
`catch: (${context.sinkName}, ${part.catchName}) => {\n${context.emitNestedAppendStatements(part.catchParts, context.sinkName, context.compatRenderToStringHelperName)}\n }`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (part.awaitId !== undefined) {
|
|
56
|
+
optionFields.push(`hydrationAwaitId: ${JSON.stringify(part.awaitId)}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const optionsExpression = optionFields.length === 0 ? "" : `, { ${optionFields.join(", ")} }`;
|
|
60
|
+
|
|
61
|
+
return [
|
|
62
|
+
` await ${context.asyncBoundaryHelperName}(${context.sinkName}, (${part.valueCode}), async (${context.sinkName}, ${part.valueName}) => {`,
|
|
63
|
+
context.emitNestedAppendStatements(
|
|
64
|
+
part.parts,
|
|
65
|
+
context.sinkName,
|
|
66
|
+
context.compatRenderToStringHelperName,
|
|
67
|
+
),
|
|
68
|
+
` }${optionsExpression});`,
|
|
69
|
+
].join("\n");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function emitOutOfOrderBoundary<Part>(
|
|
73
|
+
part: OutOfOrderBoundaryPart<Part>,
|
|
74
|
+
context: BoundaryLoweringContext<Part> & {
|
|
75
|
+
outOfOrderBoundaryHelperName: string;
|
|
76
|
+
},
|
|
77
|
+
): string {
|
|
78
|
+
const catchOption =
|
|
79
|
+
part.catchName === undefined || part.catchParts === undefined
|
|
80
|
+
? ""
|
|
81
|
+
: `,\n catch: (${context.sinkName}, ${part.catchName}) => {\n${context.emitNestedAppendStatements(part.catchParts, context.sinkName, context.compatRenderToStringHelperName)}\n }`;
|
|
82
|
+
|
|
83
|
+
const hydrationAwaitIdOption =
|
|
84
|
+
part.awaitId === undefined ? "" : `,\n hydrationAwaitId: ${JSON.stringify(part.awaitId)}`;
|
|
85
|
+
const placeholderTagOption =
|
|
86
|
+
part.placeholderTagCode === undefined ? "" : `,\n placeholderTag: (${part.placeholderTagCode})`;
|
|
87
|
+
|
|
88
|
+
return [
|
|
89
|
+
` ${context.outOfOrderBoundaryHelperName}(${context.sinkName}, ${JSON.stringify(part.id)}, (${part.valueCode}), async (${context.sinkName}, ${part.valueName}) => {`,
|
|
90
|
+
context.emitNestedAppendStatements(
|
|
91
|
+
part.parts,
|
|
92
|
+
context.sinkName,
|
|
93
|
+
context.compatRenderToStringHelperName,
|
|
94
|
+
),
|
|
95
|
+
` }, {`,
|
|
96
|
+
...(part.hydration ? [` hydration: true,`] : []),
|
|
97
|
+
` placeholder: (${context.sinkName}) => {`,
|
|
98
|
+
context.emitNestedAppendStatements(
|
|
99
|
+
part.placeholderParts,
|
|
100
|
+
context.sinkName,
|
|
101
|
+
context.compatRenderToStringHelperName,
|
|
102
|
+
),
|
|
103
|
+
` }${catchOption}${hydrationAwaitIdOption}${placeholderTagOption}`,
|
|
104
|
+
` });`,
|
|
105
|
+
].join("\n");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function emitReactSuspenseBoundary<Part>(
|
|
109
|
+
part: ReactSuspenseBoundaryPart<Part>,
|
|
110
|
+
context: BoundaryLoweringContext<Part> & {
|
|
111
|
+
reactSuspenseBoundaryHelperName: string;
|
|
112
|
+
},
|
|
113
|
+
): string {
|
|
114
|
+
return [
|
|
115
|
+
` await ${context.reactSuspenseBoundaryHelperName}(${context.sinkName}, async (${context.sinkName}) => {`,
|
|
116
|
+
context.emitNestedAppendStatements(
|
|
117
|
+
part.parts,
|
|
118
|
+
context.sinkName,
|
|
119
|
+
context.compatRenderToStringHelperName,
|
|
120
|
+
),
|
|
121
|
+
` });`,
|
|
122
|
+
].join("\n");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function emitReactSuspenseOutOfOrderBoundary<Part>(
|
|
126
|
+
part: ReactSuspenseOutOfOrderBoundaryPart<Part>,
|
|
127
|
+
context: BoundaryLoweringContext<Part> & {
|
|
128
|
+
reactSuspenseOutOfOrderBoundaryHelperName: string;
|
|
129
|
+
},
|
|
130
|
+
): string {
|
|
131
|
+
const options = [
|
|
132
|
+
` fallback: (${context.sinkName}) => {`,
|
|
133
|
+
context.emitNestedAppendStatements(
|
|
134
|
+
part.fallbackParts,
|
|
135
|
+
context.sinkName,
|
|
136
|
+
context.compatRenderToStringHelperName,
|
|
137
|
+
),
|
|
138
|
+
` },`,
|
|
139
|
+
...(part.catchName === undefined || part.catchParts === undefined
|
|
140
|
+
? []
|
|
141
|
+
: [
|
|
142
|
+
` catch: (${context.sinkName}, ${part.catchName}) => {`,
|
|
143
|
+
context.emitNestedAppendStatements(
|
|
144
|
+
part.catchParts,
|
|
145
|
+
context.sinkName,
|
|
146
|
+
context.compatRenderToStringHelperName,
|
|
147
|
+
),
|
|
148
|
+
` },`,
|
|
149
|
+
]),
|
|
150
|
+
...(part.nonce === undefined ? [] : [` nonce: ${JSON.stringify(part.nonce)},`]),
|
|
151
|
+
...(part.scriptSrc === undefined ? [] : [` src: ${JSON.stringify(part.scriptSrc)},`]),
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
return [
|
|
155
|
+
` ${context.reactSuspenseOutOfOrderBoundaryHelperName}(${context.sinkName}, ${JSON.stringify(part.boundaryId)}, ${JSON.stringify(part.segmentId)}, (${part.valueCode}), async (${context.sinkName}, ${part.valueName}) => {`,
|
|
156
|
+
context.emitNestedAppendStatements(
|
|
157
|
+
part.parts,
|
|
158
|
+
context.sinkName,
|
|
159
|
+
context.compatRenderToStringHelperName,
|
|
160
|
+
),
|
|
161
|
+
` }, {`,
|
|
162
|
+
...options,
|
|
163
|
+
` });`,
|
|
164
|
+
].join("\n");
|
|
165
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface CodeBuilder {
|
|
2
|
+
section(code: string | undefined, options?: CodeBuilderSectionOptions): void;
|
|
3
|
+
toString(): string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface CodeBuilderSectionOptions {
|
|
7
|
+
leadingBlankLines?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createCodeBuilder(): CodeBuilder {
|
|
11
|
+
const sections: string[] = [];
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
section(code, options = {}) {
|
|
15
|
+
if (code === undefined || code === "") {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const leadingBlankLines = options.leadingBlankLines ?? 1;
|
|
20
|
+
const prefix = sections.length === 0 ? "" : "\n".repeat(leadingBlankLines + 1);
|
|
21
|
+
sections.push(`${prefix}${code}`);
|
|
22
|
+
},
|
|
23
|
+
toString() {
|
|
24
|
+
return sections.length === 0 ? "\n" : `${sections.join("")}\n`;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -8,6 +8,13 @@ import type {
|
|
|
8
8
|
} from "./ir.js";
|
|
9
9
|
import type { RuntimeImport, ServerBootstrapMode, ServerEscapeOptions } from "./types.js";
|
|
10
10
|
import { emitEscapeHtmlHelper } from "./emit-escape-helper.js";
|
|
11
|
+
import { createCodeBuilder } from "./emit-code-builder.js";
|
|
12
|
+
import {
|
|
13
|
+
emitAsyncBoundary as emitLoweredAsyncBoundary,
|
|
14
|
+
emitOutOfOrderBoundary as emitLoweredOutOfOrderBoundary,
|
|
15
|
+
emitReactSuspenseBoundary as emitLoweredReactSuspenseBoundary,
|
|
16
|
+
emitReactSuspenseOutOfOrderBoundary as emitLoweredReactSuspenseOutOfOrderBoundary,
|
|
17
|
+
} from "./emit-boundary-lowering.js";
|
|
11
18
|
import { escapeHtmlAttribute as escapeHtml } from "@reckona/mreact-shared/html-escape";
|
|
12
19
|
import {
|
|
13
20
|
htmlAttributeName,
|
|
@@ -160,22 +167,28 @@ export function emitServerStream(
|
|
|
160
167
|
const importsBlock = [importLine, escapeImport, userImports, moduleStatements].filter(Boolean).join("\n");
|
|
161
168
|
const needsSpreadAttributesHelper = components.includes(spreadAttributesHelperName);
|
|
162
169
|
const urlSafeBlock =
|
|
163
|
-
components.includes(urlSafeHelperName) || needsSpreadAttributesHelper
|
|
164
|
-
? `\n\n${urlSafeHelper}`
|
|
165
|
-
: "";
|
|
170
|
+
components.includes(urlSafeHelperName) || needsSpreadAttributesHelper ? urlSafeHelper : "";
|
|
166
171
|
const clientBoundaryBlock =
|
|
167
172
|
clientBoundaryHelperName === undefined || !components.includes(clientBoundaryHelperName)
|
|
168
173
|
? ""
|
|
169
|
-
:
|
|
174
|
+
: emitClientBoundaryHelper(clientBoundaryHelperName);
|
|
170
175
|
const spreadAttributesBlock = needsSpreadAttributesHelper
|
|
171
|
-
?
|
|
176
|
+
? emitSpreadAttributesHelper(spreadAttributesHelperName, escapeHelperName, urlSafeHelperName)
|
|
172
177
|
: "";
|
|
173
178
|
const streamNodeBlock = components.includes(streamNodeHelperName)
|
|
174
|
-
?
|
|
179
|
+
? emitStreamNodeHelper(streamNodeHelperName)
|
|
175
180
|
: "";
|
|
181
|
+
const code = createCodeBuilder();
|
|
182
|
+
code.section(importsBlock);
|
|
183
|
+
code.section(helper);
|
|
184
|
+
code.section(urlSafeBlock);
|
|
185
|
+
code.section(clientBoundaryBlock);
|
|
186
|
+
code.section(spreadAttributesBlock);
|
|
187
|
+
code.section(streamNodeBlock);
|
|
188
|
+
code.section(components);
|
|
176
189
|
|
|
177
190
|
return {
|
|
178
|
-
code:
|
|
191
|
+
code: code.toString(),
|
|
179
192
|
imports,
|
|
180
193
|
};
|
|
181
194
|
}
|
|
@@ -532,39 +545,39 @@ function emitAppendStatements(
|
|
|
532
545
|
try {
|
|
533
546
|
return coalesceAdjacentStaticParts(collected).map((part) => {
|
|
534
547
|
if (part.kind === "async-boundary") {
|
|
535
|
-
return
|
|
536
|
-
part,
|
|
537
|
-
sinkName,
|
|
548
|
+
return emitLoweredAsyncBoundary(part, {
|
|
538
549
|
asyncBoundaryHelperName,
|
|
539
550
|
compatRenderToStringHelperName,
|
|
540
|
-
|
|
551
|
+
emitNestedAppendStatements,
|
|
552
|
+
sinkName,
|
|
553
|
+
});
|
|
541
554
|
}
|
|
542
555
|
|
|
543
556
|
if (part.kind === "out-of-order-boundary") {
|
|
544
|
-
return
|
|
545
|
-
part,
|
|
546
|
-
sinkName,
|
|
547
|
-
outOfOrderBoundaryHelperName,
|
|
557
|
+
return emitLoweredOutOfOrderBoundary(part, {
|
|
548
558
|
compatRenderToStringHelperName,
|
|
549
|
-
|
|
559
|
+
emitNestedAppendStatements,
|
|
560
|
+
outOfOrderBoundaryHelperName,
|
|
561
|
+
sinkName,
|
|
562
|
+
});
|
|
550
563
|
}
|
|
551
564
|
|
|
552
565
|
if (part.kind === "react-suspense-boundary") {
|
|
553
|
-
return
|
|
554
|
-
part,
|
|
555
|
-
sinkName,
|
|
556
|
-
reactSuspenseBoundaryHelperName,
|
|
566
|
+
return emitLoweredReactSuspenseBoundary(part, {
|
|
557
567
|
compatRenderToStringHelperName,
|
|
558
|
-
|
|
568
|
+
emitNestedAppendStatements,
|
|
569
|
+
reactSuspenseBoundaryHelperName,
|
|
570
|
+
sinkName,
|
|
571
|
+
});
|
|
559
572
|
}
|
|
560
573
|
|
|
561
574
|
if (part.kind === "react-suspense-out-of-order-boundary") {
|
|
562
|
-
return
|
|
563
|
-
part,
|
|
564
|
-
sinkName,
|
|
565
|
-
reactSuspenseOutOfOrderBoundaryHelperName,
|
|
575
|
+
return emitLoweredReactSuspenseOutOfOrderBoundary(part, {
|
|
566
576
|
compatRenderToStringHelperName,
|
|
567
|
-
|
|
577
|
+
emitNestedAppendStatements,
|
|
578
|
+
reactSuspenseOutOfOrderBoundaryHelperName,
|
|
579
|
+
sinkName,
|
|
580
|
+
});
|
|
568
581
|
}
|
|
569
582
|
|
|
570
583
|
if (part.kind === "component") {
|
|
@@ -852,116 +865,12 @@ function emitListPartAsStringExpression(
|
|
|
852
865
|
return `(() => { const _arr = (${part.itemsCode}); let _listOut = ""; for (let _i = 0, _len = _arr.length; _i < _len; _i++) { const ${part.itemName} = _arr[_i];${part.indexName === undefined ? "" : ` const ${part.indexName} = _i;`}${part.arrayName === undefined ? "" : ` const ${part.arrayName} = _arr;`}${part.bodyStatements.length === 0 ? "" : ` ${part.bodyStatements.join(" ")}`} ${concatLines.join(" ")} } return _listOut; })()`;
|
|
853
866
|
}
|
|
854
867
|
|
|
855
|
-
function emitAsyncBoundary(
|
|
856
|
-
part: Extract<HtmlPart, { kind: "async-boundary" }>,
|
|
857
|
-
sinkName: string,
|
|
858
|
-
asyncBoundaryHelperName: string,
|
|
859
|
-
compatRenderToStringHelperName: string,
|
|
860
|
-
): string {
|
|
861
|
-
const optionFields: string[] = [];
|
|
862
|
-
|
|
863
|
-
if (part.catchName !== undefined && part.catchParts !== undefined) {
|
|
864
|
-
optionFields.push(
|
|
865
|
-
`catch: (${sinkName}, ${part.catchName}) => {\n${emitNestedAppendStatements(part.catchParts, sinkName, compatRenderToStringHelperName)}\n }`,
|
|
866
|
-
);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
if (part.awaitId !== undefined) {
|
|
870
|
-
optionFields.push(`hydrationAwaitId: ${JSON.stringify(part.awaitId)}`);
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
const optionsExpression = optionFields.length === 0
|
|
874
|
-
? ""
|
|
875
|
-
: `, { ${optionFields.join(", ")} }`;
|
|
876
|
-
|
|
877
|
-
return [
|
|
878
|
-
` await ${asyncBoundaryHelperName}(${sinkName}, (${part.valueCode}), async (${sinkName}, ${part.valueName}) => {`,
|
|
879
|
-
emitNestedAppendStatements(part.parts, sinkName, compatRenderToStringHelperName),
|
|
880
|
-
` }${optionsExpression});`,
|
|
881
|
-
].join("\n");
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
function emitOutOfOrderBoundary(
|
|
885
|
-
part: Extract<HtmlPart, { kind: "out-of-order-boundary" }>,
|
|
886
|
-
sinkName: string,
|
|
887
|
-
outOfOrderBoundaryHelperName: string,
|
|
888
|
-
compatRenderToStringHelperName: string,
|
|
889
|
-
): string {
|
|
890
|
-
const catchOption =
|
|
891
|
-
part.catchName === undefined || part.catchParts === undefined
|
|
892
|
-
? ""
|
|
893
|
-
: `,\n catch: (${sinkName}, ${part.catchName}) => {\n${emitNestedAppendStatements(part.catchParts, sinkName, compatRenderToStringHelperName)}\n }`;
|
|
894
|
-
|
|
895
|
-
const hydrationAwaitIdOption =
|
|
896
|
-
part.awaitId === undefined
|
|
897
|
-
? ""
|
|
898
|
-
: `,\n hydrationAwaitId: ${JSON.stringify(part.awaitId)}`;
|
|
899
|
-
const placeholderTagOption =
|
|
900
|
-
part.placeholderTagCode === undefined
|
|
901
|
-
? ""
|
|
902
|
-
: `,\n placeholderTag: (${part.placeholderTagCode})`;
|
|
903
|
-
|
|
904
|
-
return [
|
|
905
|
-
` ${outOfOrderBoundaryHelperName}(${sinkName}, ${JSON.stringify(part.id)}, (${part.valueCode}), async (${sinkName}, ${part.valueName}) => {`,
|
|
906
|
-
emitNestedAppendStatements(part.parts, sinkName, compatRenderToStringHelperName),
|
|
907
|
-
` }, {`,
|
|
908
|
-
...(part.hydration ? [` hydration: true,`] : []),
|
|
909
|
-
` placeholder: (${sinkName}) => {`,
|
|
910
|
-
emitNestedAppendStatements(part.placeholderParts, sinkName, compatRenderToStringHelperName),
|
|
911
|
-
` }${catchOption}${hydrationAwaitIdOption}${placeholderTagOption}`,
|
|
912
|
-
` });`,
|
|
913
|
-
].join("\n");
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
function emitReactSuspenseBoundary(
|
|
917
|
-
part: Extract<HtmlPart, { kind: "react-suspense-boundary" }>,
|
|
918
|
-
sinkName: string,
|
|
919
|
-
reactSuspenseBoundaryHelperName: string,
|
|
920
|
-
compatRenderToStringHelperName: string,
|
|
921
|
-
): string {
|
|
922
|
-
return [
|
|
923
|
-
` await ${reactSuspenseBoundaryHelperName}(${sinkName}, async (${sinkName}) => {`,
|
|
924
|
-
emitNestedAppendStatements(part.parts, sinkName, compatRenderToStringHelperName),
|
|
925
|
-
` });`,
|
|
926
|
-
].join("\n");
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
function emitReactSuspenseOutOfOrderBoundary(
|
|
930
|
-
part: Extract<HtmlPart, { kind: "react-suspense-out-of-order-boundary" }>,
|
|
931
|
-
sinkName: string,
|
|
932
|
-
reactSuspenseOutOfOrderBoundaryHelperName: string,
|
|
933
|
-
compatRenderToStringHelperName: string,
|
|
934
|
-
): string {
|
|
935
|
-
const options = [
|
|
936
|
-
` fallback: (${sinkName}) => {`,
|
|
937
|
-
emitNestedAppendStatements(part.fallbackParts, sinkName, compatRenderToStringHelperName),
|
|
938
|
-
` },`,
|
|
939
|
-
...(part.catchName === undefined || part.catchParts === undefined
|
|
940
|
-
? []
|
|
941
|
-
: [
|
|
942
|
-
` catch: (${sinkName}, ${part.catchName}) => {`,
|
|
943
|
-
emitNestedAppendStatements(part.catchParts, sinkName, compatRenderToStringHelperName),
|
|
944
|
-
` },`,
|
|
945
|
-
]),
|
|
946
|
-
...(part.nonce === undefined ? [] : [` nonce: ${stringLiteral(part.nonce)},`]),
|
|
947
|
-
...(part.scriptSrc === undefined ? [] : [` src: ${stringLiteral(part.scriptSrc)},`]),
|
|
948
|
-
];
|
|
949
|
-
|
|
950
|
-
return [
|
|
951
|
-
` ${reactSuspenseOutOfOrderBoundaryHelperName}(${sinkName}, ${JSON.stringify(part.boundaryId)}, ${JSON.stringify(part.segmentId)}, (${part.valueCode}), async (${sinkName}, ${part.valueName}) => {`,
|
|
952
|
-
emitNestedAppendStatements(part.parts, sinkName, compatRenderToStringHelperName),
|
|
953
|
-
` }, {`,
|
|
954
|
-
...options,
|
|
955
|
-
` });`,
|
|
956
|
-
].join("\n");
|
|
957
|
-
}
|
|
958
|
-
|
|
959
868
|
function emitNestedAppendStatements(
|
|
960
|
-
parts: HtmlSyncPart[],
|
|
869
|
+
parts: readonly HtmlSyncPart[],
|
|
961
870
|
sinkName: string,
|
|
962
871
|
compatRenderToStringHelperName: string,
|
|
963
872
|
): string {
|
|
964
|
-
return coalesceAdjacentStaticParts(parts)
|
|
873
|
+
return coalesceAdjacentStaticParts([...parts])
|
|
965
874
|
.map((part) => emitSyncPartAsAppendStatement(part, sinkName, compatRenderToStringHelperName, " "))
|
|
966
875
|
.join("\n");
|
|
967
876
|
}
|
|
@@ -974,39 +883,39 @@ function emitNestedStreamAppendStatements(
|
|
|
974
883
|
return coalesceAdjacentStaticParts(parts)
|
|
975
884
|
.map((part) => {
|
|
976
885
|
if (part.kind === "async-boundary") {
|
|
977
|
-
return
|
|
978
|
-
|
|
979
|
-
sinkName,
|
|
980
|
-
currentAsyncBoundaryHelperName,
|
|
886
|
+
return emitLoweredAsyncBoundary(part, {
|
|
887
|
+
asyncBoundaryHelperName: currentAsyncBoundaryHelperName,
|
|
981
888
|
compatRenderToStringHelperName,
|
|
982
|
-
|
|
889
|
+
emitNestedAppendStatements,
|
|
890
|
+
sinkName,
|
|
891
|
+
}).replace(/^/gm, " ");
|
|
983
892
|
}
|
|
984
893
|
|
|
985
894
|
if (part.kind === "out-of-order-boundary") {
|
|
986
|
-
return
|
|
987
|
-
part,
|
|
988
|
-
sinkName,
|
|
989
|
-
currentOutOfOrderBoundaryHelperName,
|
|
895
|
+
return emitLoweredOutOfOrderBoundary(part, {
|
|
990
896
|
compatRenderToStringHelperName,
|
|
991
|
-
|
|
897
|
+
emitNestedAppendStatements,
|
|
898
|
+
outOfOrderBoundaryHelperName: currentOutOfOrderBoundaryHelperName,
|
|
899
|
+
sinkName,
|
|
900
|
+
}).replace(/^/gm, " ");
|
|
992
901
|
}
|
|
993
902
|
|
|
994
903
|
if (part.kind === "react-suspense-boundary") {
|
|
995
|
-
return
|
|
996
|
-
part,
|
|
997
|
-
sinkName,
|
|
998
|
-
currentReactSuspenseBoundaryHelperName,
|
|
904
|
+
return emitLoweredReactSuspenseBoundary(part, {
|
|
999
905
|
compatRenderToStringHelperName,
|
|
1000
|
-
|
|
906
|
+
emitNestedAppendStatements,
|
|
907
|
+
reactSuspenseBoundaryHelperName: currentReactSuspenseBoundaryHelperName,
|
|
908
|
+
sinkName,
|
|
909
|
+
}).replace(/^/gm, " ");
|
|
1001
910
|
}
|
|
1002
911
|
|
|
1003
912
|
if (part.kind === "react-suspense-out-of-order-boundary") {
|
|
1004
|
-
return
|
|
1005
|
-
part,
|
|
1006
|
-
sinkName,
|
|
1007
|
-
currentReactSuspenseOutOfOrderBoundaryHelperName,
|
|
913
|
+
return emitLoweredReactSuspenseOutOfOrderBoundary(part, {
|
|
1008
914
|
compatRenderToStringHelperName,
|
|
1009
|
-
|
|
915
|
+
emitNestedAppendStatements,
|
|
916
|
+
reactSuspenseOutOfOrderBoundaryHelperName: currentReactSuspenseOutOfOrderBoundaryHelperName,
|
|
917
|
+
sinkName,
|
|
918
|
+
}).replace(/^/gm, " ");
|
|
1010
919
|
}
|
|
1011
920
|
|
|
1012
921
|
return emitSyncPartAsAppendStatement(
|
package/src/emit-server.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from "./ir.js";
|
|
8
8
|
import type { RuntimeImport, ServerEscapeOptions } from "./types.js";
|
|
9
9
|
import { emitEscapeHtmlHelper } from "./emit-escape-helper.js";
|
|
10
|
+
import { createCodeBuilder } from "./emit-code-builder.js";
|
|
10
11
|
import { escapeHtmlAttribute as escapeHtml } from "@reckona/mreact-shared/html-escape";
|
|
11
12
|
import {
|
|
12
13
|
htmlAttributeName,
|
|
@@ -131,9 +132,19 @@ export function emitServer(
|
|
|
131
132
|
reactNodeRenderHelperName,
|
|
132
133
|
);
|
|
133
134
|
const moduleStatements = emitModuleStatements(ir);
|
|
135
|
+
const code = createCodeBuilder();
|
|
136
|
+
code.section(userImports);
|
|
137
|
+
code.section(escapeImport);
|
|
138
|
+
code.section(contextImport);
|
|
139
|
+
code.section(moduleStatements);
|
|
140
|
+
code.section(helper);
|
|
141
|
+
code.section(urlSafeBlock);
|
|
142
|
+
code.section(clientBoundaryBlock);
|
|
143
|
+
code.section(spreadAttributesBlock);
|
|
144
|
+
code.section(components);
|
|
134
145
|
|
|
135
146
|
return {
|
|
136
|
-
code:
|
|
147
|
+
code: code.toString(),
|
|
137
148
|
imports: collectContextImports(
|
|
138
149
|
contextProviderHelperName,
|
|
139
150
|
contextConsumerHelperName,
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export {
|
|
|
18
18
|
} from "./internal.js";
|
|
19
19
|
export type {
|
|
20
20
|
StaticExportReference,
|
|
21
|
+
StaticExportSpecifierReference,
|
|
21
22
|
FormActionExpressionReference,
|
|
22
23
|
FormActionReference,
|
|
23
24
|
StaticImportReference,
|
|
@@ -26,6 +27,21 @@ export type {
|
|
|
26
27
|
ClientRouteStaticImportReference,
|
|
27
28
|
TopLevelExportRenderInfo,
|
|
28
29
|
} from "./internal.js";
|
|
30
|
+
export { analyzeBoundaryGraph } from "./boundary-graph.js";
|
|
31
|
+
export type {
|
|
32
|
+
BoundaryClassification,
|
|
33
|
+
BoundaryGraphClientBoundary,
|
|
34
|
+
BoundaryGraphEntry,
|
|
35
|
+
BoundaryGraphEntryKind,
|
|
36
|
+
BoundaryGraphExport,
|
|
37
|
+
BoundaryGraphInput,
|
|
38
|
+
BoundaryGraphModule,
|
|
39
|
+
BoundaryGraphResult,
|
|
40
|
+
BoundaryGraphServerActionSite,
|
|
41
|
+
BoundaryGraphTraceEvent,
|
|
42
|
+
BoundaryGraphTraceKind,
|
|
43
|
+
BoundaryGraphTraceReason,
|
|
44
|
+
} from "./boundary-graph.js";
|
|
29
45
|
export { formatDiagnostic } from "./diagnostics.js";
|
|
30
46
|
export { transform } from "./transform.js";
|
|
31
47
|
export type {
|
package/src/internal.ts
CHANGED
|
@@ -48,9 +48,15 @@ export interface ClientRouteStaticImportReference extends StaticImportReference
|
|
|
48
48
|
export interface StaticExportReference {
|
|
49
49
|
exportedNames: string[];
|
|
50
50
|
exportAll: boolean;
|
|
51
|
+
specifiers: StaticExportSpecifierReference[];
|
|
51
52
|
source: string;
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
export interface StaticExportSpecifierReference {
|
|
56
|
+
exportedName: string;
|
|
57
|
+
localName: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
export interface TopLevelExportRenderInfo {
|
|
55
61
|
calledComponentRoots: string[];
|
|
56
62
|
clientRuntime: boolean;
|
|
@@ -1179,7 +1185,7 @@ function staticExportReference(statement: Record<string, unknown>): StaticExport
|
|
|
1179
1185
|
const source = sourceValue(statement)[0];
|
|
1180
1186
|
return source === undefined
|
|
1181
1187
|
? []
|
|
1182
|
-
: [{ exportedNames: [], exportAll: true, source }];
|
|
1188
|
+
: [{ exportedNames: [], exportAll: true, specifiers: [], source }];
|
|
1183
1189
|
}
|
|
1184
1190
|
|
|
1185
1191
|
if (statement.type !== "ExportNamedDeclaration" || statement.exportKind === "type") {
|
|
@@ -1195,11 +1201,29 @@ function staticExportReference(statement: Record<string, unknown>): StaticExport
|
|
|
1195
1201
|
{
|
|
1196
1202
|
exportedNames: exportedNames(statement),
|
|
1197
1203
|
exportAll: false,
|
|
1204
|
+
specifiers: staticExportSpecifierReferences(statement),
|
|
1198
1205
|
source,
|
|
1199
1206
|
},
|
|
1200
1207
|
];
|
|
1201
1208
|
}
|
|
1202
1209
|
|
|
1210
|
+
function staticExportSpecifierReferences(
|
|
1211
|
+
statement: Record<string, unknown>,
|
|
1212
|
+
): StaticExportSpecifierReference[] {
|
|
1213
|
+
const specifiers = Array.isArray(statement.specifiers)
|
|
1214
|
+
? statement.specifiers.map(readObject)
|
|
1215
|
+
: [];
|
|
1216
|
+
|
|
1217
|
+
return specifiers.flatMap((specifier) => {
|
|
1218
|
+
const exportedName = exportedNameForSpecifier(specifier);
|
|
1219
|
+
const localName = localNameForExportSpecifier(specifier);
|
|
1220
|
+
|
|
1221
|
+
return exportedName === undefined || localName === undefined
|
|
1222
|
+
? []
|
|
1223
|
+
: [{ exportedName, localName }];
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1203
1227
|
function sourceValue(statement: Record<string, unknown>): string[] {
|
|
1204
1228
|
const source = readOptionalObject(statement.source);
|
|
1205
1229
|
const value = source?.value;
|