@reckona/mreact-compiler 0.0.87 → 0.0.88

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-compiler",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
4
4
  "description": "Compiler passes and OXC-backed JSX analysis for mreact.",
5
5
  "keywords": [
6
6
  "compiler",
@@ -50,6 +50,6 @@
50
50
  "dependencies": {
51
51
  "oxc-parser": "0.129.0",
52
52
  "oxc-transform": "0.129.0",
53
- "@reckona/mreact-shared": "0.0.87"
53
+ "@reckona/mreact-shared": "0.0.88"
54
54
  }
55
55
  }
@@ -74,7 +74,7 @@ export function emitServerStream(
74
74
  );
75
75
  const compatRenderToStringHelperName = allocateHelperName(ir, "_renderCompatToString");
76
76
  const streamNodeHelperName = allocateHelperName(ir, "_renderStreamNode");
77
- const clientBoundaryHelperName = usesClientBoundary(ir)
77
+ const clientBoundaryHelperName = usesClientBoundary(ir, options.serverHydration === true)
78
78
  ? allocateHelperName(ir, "_renderClientBoundary")
79
79
  : undefined;
80
80
  const spreadAttributesHelperName = allocateHelperName(ir, "_renderSpreadAttributes");
@@ -258,8 +258,8 @@ function hasRawJsxDynamicRender(ir: ModuleIr): boolean {
258
258
  return ir.components.some((component) => containsRawJsxDynamicRender(component.root));
259
259
  }
260
260
 
261
- function usesClientBoundary(ir: ModuleIr): boolean {
262
- return ir.components.some((component) => containsClientBoundary(component.root));
261
+ function usesClientBoundary(ir: ModuleIr, serverHydration: boolean): boolean {
262
+ return ir.components.some((component) => containsClientBoundary(component.root, serverHydration));
263
263
  }
264
264
 
265
265
  function emitClientBoundaryHelper(name: string): string {
@@ -1374,7 +1374,7 @@ function collectHtmlParts(
1374
1374
  ];
1375
1375
  }
1376
1376
 
1377
- if (isClientBoundaryPlaceholder(node)) {
1377
+ if (isClientBoundaryPlaceholder(node, state.hydration)) {
1378
1378
  const helperName = currentClientBoundaryHelperName;
1379
1379
  if (helperName !== undefined) {
1380
1380
  return [
@@ -2272,30 +2272,34 @@ function containsCompatComponent(node: JsxNodeIr): boolean {
2272
2272
  return false;
2273
2273
  }
2274
2274
 
2275
- function containsClientBoundary(node: JsxNodeIr): boolean {
2276
- if (node.kind === "component" && isClientBoundaryPlaceholder(node)) {
2275
+ function containsClientBoundary(node: JsxNodeIr, serverHydration: boolean): boolean {
2276
+ if (node.kind === "component" && isClientBoundaryPlaceholder(node, serverHydration)) {
2277
2277
  return true;
2278
2278
  }
2279
2279
 
2280
2280
  if (node.kind === "component") {
2281
2281
  return (
2282
- node.children.some(containsClientBoundary) ||
2282
+ node.children.some((child) => containsClientBoundary(child, serverHydration)) ||
2283
2283
  node.props.some(
2284
- (prop) => prop.kind === "render-prop" && prop.children.some(containsClientBoundary),
2284
+ (prop) =>
2285
+ prop.kind === "render-prop" &&
2286
+ prop.children.some((child) => containsClientBoundary(child, serverHydration)),
2285
2287
  )
2286
2288
  );
2287
2289
  }
2288
2290
 
2289
2291
  if (node.kind === "conditional") {
2290
- return [...node.whenTrue, ...node.whenFalse].some(containsClientBoundary);
2292
+ return [...node.whenTrue, ...node.whenFalse].some((child) =>
2293
+ containsClientBoundary(child, serverHydration),
2294
+ );
2291
2295
  }
2292
2296
 
2293
2297
  if (node.kind === "list") {
2294
- return node.children.some(containsClientBoundary);
2298
+ return node.children.some((child) => containsClientBoundary(child, serverHydration));
2295
2299
  }
2296
2300
 
2297
2301
  if (node.kind === "element" || node.kind === "fragment") {
2298
- return node.children.some(containsClientBoundary);
2302
+ return node.children.some((child) => containsClientBoundary(child, serverHydration));
2299
2303
  }
2300
2304
 
2301
2305
  if (node.kind === "async-boundary") {
@@ -2303,7 +2307,7 @@ function containsClientBoundary(node: JsxNodeIr): boolean {
2303
2307
  ...node.children,
2304
2308
  ...(node.placeholderChildren ?? []),
2305
2309
  ...(node.catchChildren ?? []),
2306
- ].some(containsClientBoundary);
2310
+ ].some((child) => containsClientBoundary(child, serverHydration));
2307
2311
  }
2308
2312
 
2309
2313
  return false;
@@ -2420,8 +2424,11 @@ function emitPropName(name: string): string {
2420
2424
  return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
2421
2425
  }
2422
2426
 
2423
- function isClientBoundaryPlaceholder(node: Extract<JsxNodeIr, { kind: "component" }>): boolean {
2424
- return node.clientReference !== undefined && !isCompatClientReference(node);
2427
+ function isClientBoundaryPlaceholder(
2428
+ node: Extract<JsxNodeIr, { kind: "component" }>,
2429
+ serverHydration = true,
2430
+ ): boolean {
2431
+ return node.clientReference !== undefined && (!serverHydration || !isCompatClientReference(node));
2425
2432
  }
2426
2433
 
2427
2434
  function isCompatClientReference(node: Extract<JsxNodeIr, { kind: "component" }>): boolean {
@@ -1416,11 +1416,7 @@ function emitComponentCallExpression(
1416
1416
  }
1417
1417
 
1418
1418
  function isClientBoundaryPlaceholder(node: Extract<JsxNodeIr, { kind: "component" }>): boolean {
1419
- return node.clientReference !== undefined && !isCompatClientReference(node);
1420
- }
1421
-
1422
- function isCompatClientReference(node: Extract<JsxNodeIr, { kind: "component" }>): boolean {
1423
- return node.clientReference !== undefined && /\.(?:compat)\.[cm]?[jt]sx?$/.test(node.clientReference.moduleId);
1419
+ return node.clientReference !== undefined;
1424
1420
  }
1425
1421
 
1426
1422
  function clientBoundaryPlaceholder(node: Extract<JsxNodeIr, { kind: "component" }>): string {
@@ -120,6 +120,19 @@ export function collectOxcCompatRuntimeImportComponents(
120
120
  return names;
121
121
  }
122
122
 
123
+ export function collectOxcCompatReactNodeComponentReferences(
124
+ program: unknown,
125
+ ): Map<string, ClientReferenceIr> {
126
+ const names = new Map<string, ClientReferenceIr>();
127
+
128
+ for (const statement of readArray(readObject(program).body)) {
129
+ collectOxcCompatReactNodeImportReferences(statement, names);
130
+ collectOxcCompatReactNodeClassReferences(statement, names);
131
+ }
132
+
133
+ return names;
134
+ }
135
+
123
136
  export function markOxcAsyncComponentReferences(
124
137
  node: JsxNodeIr,
125
138
  asyncComponentNames: Set<string>,
@@ -166,6 +179,21 @@ export function markOxcCompatRuntimeReferences(
166
179
  });
167
180
  }
168
181
 
182
+ export function markOxcCompatReactNodeReferences(
183
+ node: JsxNodeIr,
184
+ references: ReadonlyMap<string, ClientReferenceIr>,
185
+ ): void {
186
+ visitOxcNode(node, (child) => {
187
+ if (child.kind !== "component") {
188
+ return;
189
+ }
190
+
191
+ if (findOxcCompatReactNodeReference(child.name, references) !== undefined) {
192
+ child.runtime = "compat";
193
+ }
194
+ });
195
+ }
196
+
169
197
  function isOxcClientBoundaryImport(
170
198
  statement: Record<string, unknown>,
171
199
  inferredBoundaryImports: ReadonlySet<string>,
@@ -197,6 +225,99 @@ function findOxcClientReference(
197
225
  };
198
226
  }
199
227
 
228
+ function collectOxcCompatReactNodeImportReferences(
229
+ statement: unknown,
230
+ names: Map<string, ClientReferenceIr>,
231
+ ): void {
232
+ const object = readObject(statement);
233
+
234
+ if (object.type !== "ImportDeclaration" || object.importKind === "type") {
235
+ return;
236
+ }
237
+
238
+ const moduleId = String(readObject(object.source).value ?? "");
239
+
240
+ for (const specifier of readArray(object.specifiers)) {
241
+ const specifierObject = readObject(specifier);
242
+
243
+ if (specifierObject.importKind === "type") {
244
+ continue;
245
+ }
246
+
247
+ const local = readObject(specifierObject.local);
248
+ const localName = typeof local.name === "string" ? local.name : undefined;
249
+
250
+ if (localName === undefined || !/^[A-Z]/.test(localName)) {
251
+ continue;
252
+ }
253
+
254
+ if (specifierObject.type === "ImportDefaultSpecifier") {
255
+ names.set(localName, { moduleId, exportName: "default" });
256
+ continue;
257
+ }
258
+
259
+ if (specifierObject.type === "ImportNamespaceSpecifier") {
260
+ names.set(localName, { moduleId, exportName: "*" });
261
+ continue;
262
+ }
263
+
264
+ if (specifierObject.type === "ImportSpecifier") {
265
+ const imported = readObject(specifierObject.imported);
266
+ names.set(localName, {
267
+ moduleId,
268
+ exportName: String(imported.name ?? localName),
269
+ });
270
+ }
271
+ }
272
+ }
273
+
274
+ function collectOxcCompatReactNodeClassReferences(
275
+ statement: unknown,
276
+ names: Map<string, ClientReferenceIr>,
277
+ ): void {
278
+ const object = readObject(statement);
279
+ const declaration =
280
+ object.type === "ExportNamedDeclaration" || object.type === "ExportDefaultDeclaration"
281
+ ? readOptionalObject(object.declaration)
282
+ : object;
283
+ const className = readOxcClassComponentName(declaration);
284
+
285
+ if (className !== undefined) {
286
+ names.set(className, { moduleId: "", exportName: className });
287
+ }
288
+ }
289
+
290
+ function readOxcClassComponentName(
291
+ node: Record<string, unknown> | undefined,
292
+ ): string | undefined {
293
+ if (node?.type === "ClassDeclaration" || node?.type === "ClassExpression") {
294
+ const id = readOptionalObject(node.id);
295
+ const name = typeof id?.name === "string" ? id.name : undefined;
296
+ return name !== undefined && /^[A-Z]/.test(name) ? name : undefined;
297
+ }
298
+
299
+ if (node?.type !== "VariableDeclaration") {
300
+ return undefined;
301
+ }
302
+
303
+ for (const declarationValue of readArray(node.declarations)) {
304
+ const declaration = readOptionalObject(declarationValue);
305
+ const id = readOptionalObject(declaration?.id);
306
+ const init = readOptionalObject(declaration?.init);
307
+ const name = typeof id?.name === "string" ? id.name : undefined;
308
+
309
+ if (
310
+ name !== undefined &&
311
+ /^[A-Z]/.test(name) &&
312
+ (init?.type === "ClassExpression" || init?.type === "ClassDeclaration")
313
+ ) {
314
+ return name;
315
+ }
316
+ }
317
+
318
+ return undefined;
319
+ }
320
+
200
321
  function collectOxcClientReferenceAliases(
201
322
  node: unknown,
202
323
  references: Map<string, ClientReferenceIr>,
@@ -547,6 +668,29 @@ function findOxcCompatRuntimeReference(
547
668
  : undefined;
548
669
  }
549
670
 
671
+ function findOxcCompatReactNodeReference(
672
+ name: string,
673
+ references: ReadonlyMap<string, ClientReferenceIr>,
674
+ ): ClientReferenceIr | undefined {
675
+ const direct = references.get(name);
676
+
677
+ if (direct !== undefined) {
678
+ return direct;
679
+ }
680
+
681
+ const [rootName, ...memberNames] = name.split(".");
682
+ const rootReference = rootName === undefined ? undefined : references.get(rootName);
683
+
684
+ if (rootReference === undefined || rootReference.exportName !== "*" || memberNames.length === 0) {
685
+ return undefined;
686
+ }
687
+
688
+ return {
689
+ moduleId: rootReference.moduleId,
690
+ exportName: memberNames.join("."),
691
+ };
692
+ }
693
+
550
694
  function compatRuntimeExports(moduleId: string): ReadonlySet<string> | undefined {
551
695
  if (moduleId === "@reckona/mreact-router") {
552
696
  return routerEntryCompatRuntimeExports;
package/src/oxc.ts CHANGED
@@ -57,9 +57,11 @@ import {
57
57
  } from "./oxc-component-detection.js";
58
58
  import {
59
59
  collectOxcClientBoundaryImportComponents,
60
+ collectOxcCompatReactNodeComponentReferences,
60
61
  collectOxcCompatRuntimeImportComponents,
61
62
  markOxcAsyncComponentReferences,
62
63
  markOxcClientReferences,
64
+ markOxcCompatReactNodeReferences,
63
65
  markOxcCompatRuntimeReferences,
64
66
  } from "./oxc-component-references.js";
65
67
  import { normalizeOxcExpressionCode, stripOxcGeneratedImports } from "./oxc-code-utils.js";
@@ -236,6 +238,10 @@ function analyzeOxcToIr(
236
238
  new Set(options?.clientBoundaryImports ?? []),
237
239
  );
238
240
  const compatRuntimeImports = collectOxcCompatRuntimeImportComponents(program);
241
+ const compatReactNodeReferences =
242
+ options?.compatReactNodeReturnRenderMode === "react-node"
243
+ ? collectOxcCompatReactNodeComponentReferences(program)
244
+ : undefined;
239
245
  const bodyLowerers = createOxcBodyLowerers(compatRuntimeImports);
240
246
  const moduleRenderValueBindings = collectOxcBodyJsxBindingNames(body);
241
247
  const reactiveDerivedFunctionNames = collectOxcReactiveDerivedFunctionNames(body);
@@ -334,6 +340,9 @@ function analyzeOxcToIr(
334
340
  markOxcAsyncComponentReferences(component.root, asyncComponentNames);
335
341
  markOxcClientReferences(component.root, clientBoundaryImports);
336
342
  markOxcCompatRuntimeReferences(component.root, compatRuntimeImports);
343
+ if (compatReactNodeReferences !== undefined) {
344
+ markOxcCompatReactNodeReferences(component.root, compatReactNodeReferences);
345
+ }
337
346
  validateOxcNestedAwait(component.root, diagnostics);
338
347
  validateOxcAwaitCompatComponents(component.root, diagnostics, {
339
348
  allowCompatComponents: options?.awaitCompatComponents === "lower",