@reckona/mreact-compiler 0.0.66 → 0.0.67

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.
@@ -0,0 +1,127 @@
1
+ import { readArray, readObject, readSource, unwrapOxcParentheses } from "./oxc-node-utils.js";
2
+ import { readOxcJsxTagName } from "./oxc-jsx-attributes.js";
3
+ import { normalizeOxcJsxText } from "./oxc-jsx-text.js";
4
+
5
+ export function lowerOxcDomNodeExpression(
6
+ code: string,
7
+ node: Record<string, unknown>,
8
+ ): string | undefined {
9
+ const unwrapped = unwrapOxcParentheses(node);
10
+
11
+ if (unwrapped.type === "ConditionalExpression") {
12
+ const whenTrue = lowerOxcDomNodeExpression(code, readObject(unwrapped.consequent));
13
+ const whenFalse = lowerOxcDomNodeExpression(code, readObject(unwrapped.alternate));
14
+
15
+ if (whenTrue !== undefined && whenFalse !== undefined) {
16
+ return `((${readSource(code, readObject(unwrapped.test))}) ? ${whenTrue} : ${whenFalse})`;
17
+ }
18
+ }
19
+
20
+ if (unwrapped.type === "Literal" && (unwrapped.value === null || unwrapped.value === false)) {
21
+ return 'document.createTextNode("")';
22
+ }
23
+
24
+ node = unwrapped;
25
+
26
+ if (node.type !== "JSXElement") {
27
+ return undefined;
28
+ }
29
+
30
+ const openingElement = readObject(node.openingElement);
31
+ const tagName = readOxcJsxTagName(readObject(openingElement.name));
32
+
33
+ if (!/^[a-z]/.test(tagName)) {
34
+ return undefined;
35
+ }
36
+
37
+ return [
38
+ "(() => {",
39
+ ` const _node = document.createElement(${JSON.stringify(tagName)});`,
40
+ ...lowerOxcDomAttributes(code, readArray(openingElement.attributes)),
41
+ ...lowerOxcDomChildren(code, readArray(node.children)),
42
+ " return _node;",
43
+ "})()",
44
+ ].join("\n");
45
+ }
46
+
47
+ function lowerOxcDomAttributes(code: string, attributes: readonly unknown[]): string[] {
48
+ return attributes.flatMap((attribute): string[] => {
49
+ const object = readObject(attribute);
50
+
51
+ if (object.type !== "JSXAttribute") {
52
+ return [];
53
+ }
54
+
55
+ const name = String(readObject(object.name).name);
56
+ const domName = htmlAttributeAliases[name] ?? name;
57
+ const value = readObject(object.value);
58
+
59
+ if (Object.keys(value).length === 0) {
60
+ return [` _node.setAttribute(${JSON.stringify(domName)}, "");`];
61
+ }
62
+
63
+ if (value.type === "Literal") {
64
+ return [` _node.setAttribute(${JSON.stringify(domName)}, ${JSON.stringify(value.value)});`];
65
+ }
66
+
67
+ if (value.type === "JSXExpressionContainer") {
68
+ return [
69
+ ` _node.setAttribute(${JSON.stringify(domName)}, String(${readSource(code, readObject(value.expression))}));`,
70
+ ];
71
+ }
72
+
73
+ return [];
74
+ });
75
+ }
76
+
77
+ const htmlAttributeAliases: Record<string, string> = {
78
+ acceptCharset: "accept-charset",
79
+ autoFocus: "autofocus",
80
+ autoPlay: "autoplay",
81
+ charSet: "charset",
82
+ className: "class",
83
+ colSpan: "colspan",
84
+ contentEditable: "contenteditable",
85
+ crossOrigin: "crossorigin",
86
+ encType: "enctype",
87
+ formAction: "formaction",
88
+ frameBorder: "frameborder",
89
+ htmlFor: "for",
90
+ httpEquiv: "http-equiv",
91
+ maxLength: "maxlength",
92
+ minLength: "minlength",
93
+ noValidate: "novalidate",
94
+ playsInline: "playsinline",
95
+ readOnly: "readonly",
96
+ rowSpan: "rowspan",
97
+ spellCheck: "spellcheck",
98
+ srcDoc: "srcdoc",
99
+ srcSet: "srcset",
100
+ tabIndex: "tabindex",
101
+ useMap: "usemap",
102
+ };
103
+
104
+ function lowerOxcDomChildren(code: string, children: readonly unknown[]): string[] {
105
+ return children.flatMap((child, index): string[] => {
106
+ const object = readObject(child);
107
+
108
+ if (object.type === "JSXText") {
109
+ const value =
110
+ typeof object.value === "string"
111
+ ? normalizeOxcJsxText(object.value, children, index)
112
+ : "";
113
+ return value === "" ? [] : [` _node.append(${JSON.stringify(value)});`];
114
+ }
115
+
116
+ if (object.type === "JSXExpressionContainer") {
117
+ return [` _node.append(String(${readSource(code, readObject(object.expression))}));`];
118
+ }
119
+
120
+ if (object.type === "JSXElement") {
121
+ const lowered = lowerOxcDomNodeExpression(code, object);
122
+ return lowered === undefined ? [] : [` _node.append(${lowered});`];
123
+ }
124
+
125
+ return [];
126
+ });
127
+ }
@@ -0,0 +1,42 @@
1
+ import type { JsxNodeIr } from "./ir.js";
2
+ import { readArray, readObject, unwrapOxcParentheses } from "./oxc-node-utils.js";
3
+
4
+ export function readOxcReturnExpressionFromStatement(
5
+ statement: unknown,
6
+ ): Record<string, unknown> | undefined {
7
+ const object = readObject(statement);
8
+
9
+ if (object.type === "ReturnStatement") {
10
+ return unwrapOxcParentheses(readObject(object.argument));
11
+ }
12
+
13
+ if (object.type === "BlockStatement") {
14
+ const returnStatement = readArray(object.body)
15
+ .map(readObject)
16
+ .find((child) => child.type === "ReturnStatement");
17
+ return returnStatement === undefined
18
+ ? undefined
19
+ : unwrapOxcParentheses(readObject(returnStatement.argument));
20
+ }
21
+
22
+ return undefined;
23
+ }
24
+
25
+ export function isOxcJsxBranch(expression: Record<string, unknown>): boolean {
26
+ const unwrappedExpression = unwrapOxcParentheses(expression);
27
+ return unwrappedExpression.type === "JSXElement" || unwrappedExpression.type === "JSXFragment";
28
+ }
29
+
30
+ export function findOxcKeyCodeInChildren(children: readonly JsxNodeIr[]): string | undefined {
31
+ if (children.length !== 1) {
32
+ return undefined;
33
+ }
34
+
35
+ const child = children[0];
36
+
37
+ if (child?.kind === "element" || child?.kind === "component") {
38
+ return child.keyCode;
39
+ }
40
+
41
+ return undefined;
42
+ }
@@ -0,0 +1,110 @@
1
+ import type { AttributeIr } from "./ir.js";
2
+ import { unsupportedRefAttributeDiagnostic } from "./diagnostics.js";
3
+ import { getOxcLocation, readObject, readSource, unwrapOxcParentheses } from "./oxc-node-utils.js";
4
+ import type { CompileTarget, Diagnostic } from "./types.js";
5
+
6
+ export function readOxcJsxTagName(node: Record<string, unknown>): string {
7
+ if (typeof node.name === "string") {
8
+ return node.name;
9
+ }
10
+
11
+ if (node.type === "JSXMemberExpression") {
12
+ const objectName = readOxcJsxTagName(readObject(node.object));
13
+ const propertyName = readOxcJsxTagName(readObject(node.property));
14
+ return `${objectName}.${propertyName}`;
15
+ }
16
+
17
+ return "";
18
+ }
19
+
20
+ export function analyzeOxcAttribute(
21
+ code: string,
22
+ attr: unknown,
23
+ target: CompileTarget,
24
+ diagnostics: Pick<Diagnostic, "level" | "code" | "message" | "loc">[],
25
+ options: {
26
+ allowRef?: boolean;
27
+ resolveExpressionCode?: (expression: Record<string, unknown>) => string;
28
+ } = {},
29
+ ): AttributeIr[] {
30
+ const object = readObject(attr);
31
+
32
+ if (object.type === "JSXSpreadAttribute") {
33
+ return [{ kind: "spread-attr", code: readSource(code, readObject(object.argument)) }];
34
+ }
35
+
36
+ if (object.type !== "JSXAttribute") {
37
+ return [];
38
+ }
39
+
40
+ const name = String(readObject(object.name).name);
41
+ const value = readObject(object.value);
42
+
43
+ if (name === "ref" && options.allowRef !== true) {
44
+ diagnostics.push(unsupportedRefAttributeDiagnostic(getOxcLocation(code, object.name)));
45
+ }
46
+
47
+ if (value.type === "Literal") {
48
+ return [{ kind: "static-attr", name, value: String(value.value) }];
49
+ }
50
+
51
+ if (value.type === "JSXExpressionContainer") {
52
+ const expression = readObject(value.expression);
53
+ const expressionCode = options.resolveExpressionCode?.(expression) ?? readSource(code, expression);
54
+
55
+ if (/^on[A-Z]/.test(name)) {
56
+ if (target === "server") {
57
+ const loc = getOxcLocation(code, object.name);
58
+ diagnostics.push({
59
+ level: "error",
60
+ code: "MR_UNSUPPORTED_SERVER_EVENT_HANDLER",
61
+ message: `Server target does not support event handler '${name}'.`,
62
+ ...(loc === undefined ? {} : { loc }),
63
+ });
64
+ }
65
+
66
+ return [
67
+ {
68
+ kind: "event",
69
+ name,
70
+ eventName: name.slice(2).toLowerCase(),
71
+ code: expressionCode,
72
+ },
73
+ ];
74
+ }
75
+
76
+ return [{ kind: "dynamic-attr", name, code: expressionCode }];
77
+ }
78
+
79
+ return [{ kind: "static-attr", name, value: "" }];
80
+ }
81
+
82
+ export function findOxcJsxAttributeCode(
83
+ code: string,
84
+ attributes: readonly unknown[],
85
+ name: string,
86
+ ): string | undefined {
87
+ for (const attr of attributes) {
88
+ const object = readObject(attr);
89
+
90
+ if (object.type !== "JSXAttribute" || String(readObject(object.name).name) !== name) {
91
+ continue;
92
+ }
93
+
94
+ const value = readObject(object.value);
95
+
96
+ if (Object.keys(value).length === 0) {
97
+ return "true";
98
+ }
99
+
100
+ if (value.type === "Literal") {
101
+ return JSON.stringify(value.value);
102
+ }
103
+
104
+ if (value.type === "JSXExpressionContainer") {
105
+ return readSource(code, unwrapOxcParentheses(readObject(value.expression)));
106
+ }
107
+ }
108
+
109
+ return undefined;
110
+ }
@@ -0,0 +1,84 @@
1
+ export function normalizeOxcJsxText(
2
+ rawValue: string,
3
+ siblings: readonly unknown[],
4
+ index: number,
5
+ ): string {
6
+ const value = rawValue.replace(/\s+/g, " ");
7
+
8
+ if (value.trim() === "") {
9
+ const isSameLineSeparator = !/[\r\n]/.test(rawValue);
10
+ return isSameLineSeparator &&
11
+ siblings[index - 1] !== undefined &&
12
+ siblings[index + 1] !== undefined
13
+ ? " "
14
+ : "";
15
+ }
16
+
17
+ const previousSibling = siblings[index - 1];
18
+ const nextSibling = siblings[index + 1];
19
+ const leadingWhitespace = rawValue.match(/^\s*/)?.[0] ?? "";
20
+ const trailingWhitespace = rawValue.match(/\s*$/)?.[0] ?? "";
21
+ const preserveLeadingSpace = previousSibling !== undefined && !/[\r\n]/.test(leadingWhitespace);
22
+ const preserveTrailingSpace = nextSibling !== undefined && !/[\r\n]/.test(trailingWhitespace);
23
+
24
+ return value
25
+ .replace(/^\s+/, preserveLeadingSpace ? " " : "")
26
+ .replace(/\s+$/, preserveTrailingSpace ? " " : "")
27
+ .replace(htmlEntityPattern, decodeHtmlEntity);
28
+ }
29
+
30
+ const htmlEntityPattern = /&(#\d+|#x[\da-fA-F]+|[A-Za-z][A-Za-z\d]+);/g;
31
+
32
+ const namedHtmlEntities: Record<string, string> = {
33
+ amp: "&",
34
+ apos: "'",
35
+ bull: "\u2022",
36
+ cent: "\u00a2",
37
+ copy: "\u00a9",
38
+ deg: "\u00b0",
39
+ divide: "\u00f7",
40
+ euro: "\u20ac",
41
+ gt: ">",
42
+ hellip: "\u2026",
43
+ laquo: "\u00ab",
44
+ ldquo: "\u201c",
45
+ lsaquo: "\u2039",
46
+ lsquo: "\u2018",
47
+ lt: "<",
48
+ mdash: "\u2014",
49
+ middot: "\u00b7",
50
+ nbsp: "\u00a0",
51
+ ndash: "\u2013",
52
+ pound: "\u00a3",
53
+ quot: "\"",
54
+ raquo: "\u00bb",
55
+ rdquo: "\u201d",
56
+ reg: "\u00ae",
57
+ rsaquo: "\u203a",
58
+ rsquo: "\u2019",
59
+ times: "\u00d7",
60
+ trade: "\u2122",
61
+ yen: "\u00a5",
62
+ };
63
+
64
+ function decodeHtmlEntity(entity: string, body: string): string {
65
+ if (body.startsWith("#x") || body.startsWith("#X")) {
66
+ return decodeNumericHtmlEntity(entity, body.slice(2), 16);
67
+ }
68
+
69
+ if (body.startsWith("#")) {
70
+ return decodeNumericHtmlEntity(entity, body.slice(1), 10);
71
+ }
72
+
73
+ return namedHtmlEntities[body] ?? entity;
74
+ }
75
+
76
+ function decodeNumericHtmlEntity(entity: string, value: string, radix: number): string {
77
+ const codePoint = Number.parseInt(value, radix);
78
+
79
+ if (!Number.isFinite(codePoint) || codePoint < 0 || codePoint > 0x10ffff) {
80
+ return entity;
81
+ }
82
+
83
+ return String.fromCodePoint(codePoint);
84
+ }
@@ -0,0 +1,319 @@
1
+ import type { OxcBodyStatementJsxMode } from "./oxc-analysis-types.js";
2
+ import { type OxcBodyLowerers } from "./oxc-body-lowering.js";
3
+ import {
4
+ analyzeOxcExpressionChild,
5
+ type OxcChildAnalysisContext,
6
+ } from "./oxc-child-analysis.js";
7
+ import { markOxcCompatRuntimeReferences } from "./oxc-component-references.js";
8
+ import { lowerOxcDomNodeExpression } from "./oxc-dom-lowering.js";
9
+ import { readOxcJsxTagName } from "./oxc-jsx-attributes.js";
10
+ import { normalizeOxcJsxText } from "./oxc-jsx-text.js";
11
+ import { readArray, readObject, readSource, unwrapOxcParentheses } from "./oxc-node-utils.js";
12
+ import {
13
+ emitOxcCompatObjectChildren,
14
+ emitOxcServerStringChildren,
15
+ } from "./oxc-runtime-emit.js";
16
+ import type { ClientReferenceIr } from "./ir.js";
17
+ import type { CompileTarget, Diagnostic } from "./types.js";
18
+
19
+ const oxcNestedBodyLowerers: OxcBodyLowerers = {
20
+ lowerDomNodeExpression: lowerOxcDomNodeExpression,
21
+ lowerCompatObjectExpression: lowerOxcCompatObjectExpression,
22
+ lowerServerStringExpression: lowerOxcServerStringExpression,
23
+ };
24
+
25
+ export function lowerOxcCompatObjectExpression(
26
+ code: string,
27
+ expression: Record<string, unknown>,
28
+ componentNames: Set<string>,
29
+ target: CompileTarget,
30
+ diagnostics: Diagnostic[],
31
+ ): string | undefined {
32
+ const children = analyzeOxcExpressionChild(
33
+ code,
34
+ expression,
35
+ createOxcNestedChildAnalysisContext(componentNames, target, diagnostics, "compat-object"),
36
+ "compat-object",
37
+ );
38
+
39
+ if (children.length === 0) {
40
+ return "null";
41
+ }
42
+
43
+ return emitOxcCompatObjectChildren(children);
44
+ }
45
+
46
+ export function lowerOxcCompatReactNodeExpression(
47
+ code: string,
48
+ expression: Record<string, unknown>,
49
+ componentNames: Set<string>,
50
+ target: CompileTarget,
51
+ diagnostics: Diagnostic[],
52
+ ): string | undefined {
53
+ const unwrapped = unwrapOxcParentheses(expression);
54
+
55
+ if (unwrapped.type === "JSXElement" || unwrapped.type === "JSXFragment") {
56
+ return lowerOxcCompatObjectExpression(code, unwrapped, componentNames, target, diagnostics);
57
+ }
58
+
59
+ if (unwrapped.type === "ArrayExpression") {
60
+ return `[${readArray(unwrapped.elements)
61
+ .map((element) => {
62
+ const elementObject = unwrapOxcParentheses(readObject(element));
63
+ return (
64
+ lowerOxcCompatReactNodeExpression(
65
+ code,
66
+ elementObject,
67
+ componentNames,
68
+ target,
69
+ diagnostics,
70
+ ) ?? readSource(code, elementObject)
71
+ );
72
+ })
73
+ .join(", ")}]`;
74
+ }
75
+
76
+ return undefined;
77
+ }
78
+
79
+ export function lowerOxcNestedJsxExpression(
80
+ code: string,
81
+ expression: Record<string, unknown>,
82
+ componentNames: Set<string>,
83
+ target: CompileTarget,
84
+ diagnostics: Diagnostic[],
85
+ bodyStatementJsx: OxcBodyStatementJsxMode,
86
+ ): string | undefined {
87
+ const source = readSource(code, expression);
88
+ const expressionStart = typeof expression.start === "number" ? expression.start : 0;
89
+ const replacements: Array<{ start: number; end: number; value: string }> = [];
90
+
91
+ visitOxcExpressionJsxRoots(expression, (node) => {
92
+ const start = typeof node.start === "number" ? node.start : undefined;
93
+ const end = typeof node.end === "number" ? node.end : undefined;
94
+
95
+ if (start === undefined || end === undefined) {
96
+ return;
97
+ }
98
+
99
+ const lowered =
100
+ bodyStatementJsx === "compat-object"
101
+ ? lowerOxcCompatReactNodeExpression(code, node, componentNames, target, diagnostics)
102
+ : bodyStatementJsx === "server-string"
103
+ ? lowerOxcServerStringExpression(code, node, componentNames, target, diagnostics)
104
+ : lowerOxcReactiveValueExpression(code, node, componentNames);
105
+
106
+ if (lowered !== undefined) {
107
+ replacements.push({ start, end, value: lowered });
108
+ }
109
+ });
110
+
111
+ if (replacements.length === 0) {
112
+ return undefined;
113
+ }
114
+
115
+ let lowered = source;
116
+
117
+ for (const replacement of replacements.sort((left, right) => right.start - left.start)) {
118
+ const start = replacement.start - expressionStart;
119
+ const end = replacement.end - expressionStart;
120
+ lowered = `${lowered.slice(0, start)}${replacement.value}${lowered.slice(end)}`;
121
+ }
122
+
123
+ return lowered;
124
+ }
125
+
126
+ function visitOxcExpressionJsxRoots(
127
+ node: Record<string, unknown>,
128
+ visit: (node: Record<string, unknown>) => void,
129
+ ): void {
130
+ const unwrapped = unwrapOxcParentheses(node);
131
+
132
+ if (unwrapped.type === "JSXElement" || unwrapped.type === "JSXFragment") {
133
+ visit(unwrapped);
134
+ return;
135
+ }
136
+
137
+ for (const value of Object.values(unwrapped)) {
138
+ if (Array.isArray(value)) {
139
+ for (const item of value) {
140
+ const object = readObject(item);
141
+ if (Object.keys(object).length > 0) {
142
+ visitOxcExpressionJsxRoots(object, visit);
143
+ }
144
+ }
145
+ continue;
146
+ }
147
+
148
+ if (typeof value === "object" && value !== null) {
149
+ const object = readObject(value);
150
+ if (Object.keys(object).length > 0) {
151
+ visitOxcExpressionJsxRoots(object, visit);
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ export function lowerOxcReactiveValueExpression(
158
+ code: string,
159
+ expression: Record<string, unknown>,
160
+ componentNames: Set<string>,
161
+ ): string | undefined {
162
+ const unwrapped = unwrapOxcParentheses(expression);
163
+
164
+ if (unwrapped.type === "JSXFragment") {
165
+ const children = readArray(unwrapped.children)
166
+ .map((child, index, siblings) =>
167
+ lowerOxcReactiveChildValue(code, readObject(child), componentNames, siblings, index),
168
+ )
169
+ .filter((child): child is string => child !== undefined);
170
+
171
+ return [
172
+ "(() => {",
173
+ " const _fragment = document.createDocumentFragment();",
174
+ ...children.map((child) => ` _fragment.append(${child});`),
175
+ " return _fragment;",
176
+ "})()",
177
+ ].join("\n");
178
+ }
179
+
180
+ if (unwrapped.type !== "JSXElement") {
181
+ return undefined;
182
+ }
183
+
184
+ const openingElement = readObject(unwrapped.openingElement);
185
+ const tagName = readOxcJsxTagName(readObject(openingElement.name));
186
+
187
+ if (/^[a-z]/.test(tagName)) {
188
+ return lowerOxcDomNodeExpression(code, unwrapped);
189
+ }
190
+
191
+ if (!componentNames.has(tagName)) {
192
+ return undefined;
193
+ }
194
+
195
+ return `${tagName}(${lowerOxcReactiveComponentProps(code, unwrapped, componentNames)})`;
196
+ }
197
+
198
+ function lowerOxcReactiveComponentProps(
199
+ code: string,
200
+ node: Record<string, unknown>,
201
+ componentNames: Set<string>,
202
+ ): string {
203
+ const openingElement = readObject(node.openingElement);
204
+ const entries = readArray(openingElement.attributes).flatMap((attribute): string[] => {
205
+ const object = readObject(attribute);
206
+
207
+ if (object.type === "JSXSpreadAttribute") {
208
+ return [`...(${readSource(code, readObject(object.argument))})`];
209
+ }
210
+
211
+ if (object.type !== "JSXAttribute") {
212
+ return [];
213
+ }
214
+
215
+ const name = String(readObject(object.name).name);
216
+ const value = readObject(object.value);
217
+
218
+ if (Object.keys(value).length === 0) {
219
+ return [`${JSON.stringify(name)}: true`];
220
+ }
221
+
222
+ if (value.type === "Literal") {
223
+ return [`${JSON.stringify(name)}: ${JSON.stringify(value.value)}`];
224
+ }
225
+
226
+ if (value.type === "JSXExpressionContainer") {
227
+ const expression = readObject(value.expression);
228
+ return [
229
+ `${JSON.stringify(name)}: ${
230
+ lowerOxcNestedJsxExpression(code, expression, componentNames, "client", [], "dom-node") ??
231
+ readSource(code, expression)
232
+ }`,
233
+ ];
234
+ }
235
+
236
+ return [];
237
+ });
238
+ const children = readArray(node.children)
239
+ .map((child, index, siblings) =>
240
+ lowerOxcReactiveChildValue(code, readObject(child), componentNames, siblings, index),
241
+ )
242
+ .filter((child): child is string => child !== undefined);
243
+
244
+ if (children.length === 1) {
245
+ entries.push(`"children": ${children[0]}`);
246
+ } else if (children.length > 1) {
247
+ entries.push(`"children": [${children.join(", ")}]`);
248
+ }
249
+
250
+ return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }`;
251
+ }
252
+
253
+ function lowerOxcReactiveChildValue(
254
+ code: string,
255
+ child: Record<string, unknown>,
256
+ componentNames: Set<string>,
257
+ siblings: readonly unknown[],
258
+ index: number,
259
+ ): string | undefined {
260
+ if (child.type === "JSXText") {
261
+ const value =
262
+ typeof child.value === "string" ? normalizeOxcJsxText(child.value, siblings, index) : "";
263
+ return value === "" ? undefined : JSON.stringify(value);
264
+ }
265
+
266
+ if (child.type === "JSXExpressionContainer") {
267
+ const expression = readObject(child.expression);
268
+ return (
269
+ lowerOxcNestedJsxExpression(code, expression, componentNames, "client", [], "dom-node") ??
270
+ readSource(code, expression)
271
+ );
272
+ }
273
+
274
+ return lowerOxcReactiveValueExpression(code, child, componentNames);
275
+ }
276
+
277
+ export function lowerOxcServerStringExpression(
278
+ code: string,
279
+ expression: Record<string, unknown>,
280
+ componentNames: Set<string>,
281
+ target: CompileTarget,
282
+ diagnostics: Diagnostic[],
283
+ compatRuntimeReferences: ReadonlyMap<string, ClientReferenceIr> = new Map(),
284
+ ): string | undefined {
285
+ const children = analyzeOxcExpressionChild(
286
+ code,
287
+ expression,
288
+ createOxcNestedChildAnalysisContext(componentNames, target, diagnostics, "server-string"),
289
+ "server-string",
290
+ );
291
+
292
+ if (children.length === 0) {
293
+ return '""';
294
+ }
295
+
296
+ if (compatRuntimeReferences.size > 0) {
297
+ for (const child of children) {
298
+ markOxcCompatRuntimeReferences(child, compatRuntimeReferences);
299
+ }
300
+ }
301
+
302
+ return emitOxcServerStringChildren(children);
303
+ }
304
+
305
+ function createOxcNestedChildAnalysisContext(
306
+ componentNames: Set<string>,
307
+ target: CompileTarget,
308
+ diagnostics: Diagnostic[],
309
+ bodyStatementJsx: OxcBodyStatementJsxMode,
310
+ ): OxcChildAnalysisContext {
311
+ return {
312
+ componentNames,
313
+ target,
314
+ diagnostics,
315
+ bodyStatementJsx,
316
+ bodyLowerers: oxcNestedBodyLowerers,
317
+ lowerNestedJsxExpression: lowerOxcNestedJsxExpression,
318
+ };
319
+ }