@reckona/mreact-compiler 0.0.96 → 0.0.98

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.
Files changed (57) hide show
  1. package/dist/diagnostics.d.ts +1 -0
  2. package/dist/diagnostics.d.ts.map +1 -1
  3. package/dist/diagnostics.js +8 -0
  4. package/dist/diagnostics.js.map +1 -1
  5. package/dist/emit-client.js +14 -9
  6. package/dist/emit-client.js.map +1 -1
  7. package/dist/emit-compat.js +5 -1
  8. package/dist/emit-compat.js.map +1 -1
  9. package/dist/emit-server-stream.d.ts.map +1 -1
  10. package/dist/emit-server-stream.js +70 -6
  11. package/dist/emit-server-stream.js.map +1 -1
  12. package/dist/emit-server.d.ts.map +1 -1
  13. package/dist/emit-server.js +69 -13
  14. package/dist/emit-server.js.map +1 -1
  15. package/dist/internal.d.ts.map +1 -1
  16. package/dist/internal.js +7 -4
  17. package/dist/internal.js.map +1 -1
  18. package/dist/ir.d.ts +1 -0
  19. package/dist/ir.d.ts.map +1 -1
  20. package/dist/ir.js.map +1 -1
  21. package/dist/oxc-child-analysis.d.ts.map +1 -1
  22. package/dist/oxc-child-analysis.js +44 -10
  23. package/dist/oxc-child-analysis.js.map +1 -1
  24. package/dist/oxc-component-detection.d.ts +5 -2
  25. package/dist/oxc-component-detection.d.ts.map +1 -1
  26. package/dist/oxc-component-detection.js +80 -3
  27. package/dist/oxc-component-detection.js.map +1 -1
  28. package/dist/oxc-component-props.d.ts +1 -1
  29. package/dist/oxc-component-props.d.ts.map +1 -1
  30. package/dist/oxc-component-props.js +1 -1
  31. package/dist/oxc-component-props.js.map +1 -1
  32. package/dist/oxc-component-references.d.ts.map +1 -1
  33. package/dist/oxc-component-references.js +247 -12
  34. package/dist/oxc-component-references.js.map +1 -1
  35. package/dist/oxc-runtime-emit.d.ts.map +1 -1
  36. package/dist/oxc-runtime-emit.js +10 -2
  37. package/dist/oxc-runtime-emit.js.map +1 -1
  38. package/dist/oxc.d.ts.map +1 -1
  39. package/dist/oxc.js +109 -20
  40. package/dist/oxc.js.map +1 -1
  41. package/dist/transform.js +29 -11
  42. package/dist/transform.js.map +1 -1
  43. package/package.json +2 -2
  44. package/src/diagnostics.ts +10 -0
  45. package/src/emit-client.ts +20 -10
  46. package/src/emit-compat.ts +6 -1
  47. package/src/emit-server-stream.ts +96 -6
  48. package/src/emit-server.ts +93 -29
  49. package/src/internal.ts +9 -4
  50. package/src/ir.ts +1 -0
  51. package/src/oxc-child-analysis.ts +66 -21
  52. package/src/oxc-component-detection.ts +145 -2
  53. package/src/oxc-component-props.ts +2 -1
  54. package/src/oxc-component-references.ts +366 -11
  55. package/src/oxc-runtime-emit.ts +12 -2
  56. package/src/oxc.ts +167 -5
  57. package/src/transform.ts +42 -10
@@ -22,7 +22,12 @@ function emitOxcServerStringNode(node: JsxNodeIr): string {
22
22
  }
23
23
 
24
24
  if (node.kind === "conditional") {
25
- return `((${node.conditionCode}) ? ${emitOxcServerStringChildren(node.whenTrue)} : ${emitOxcServerStringChildren(node.whenFalse)})`;
25
+ const whenTrue = emitOxcServerStringChildren(node.whenTrue);
26
+ const whenFalse = emitOxcServerStringChildren(node.whenFalse);
27
+
28
+ return node.conditionValueName === undefined
29
+ ? `((${node.conditionCode}) ? ${whenTrue} : ${whenFalse})`
30
+ : `(() => { const ${node.conditionValueName} = (${node.conditionCode}); return ${node.conditionValueName} ? ${whenTrue} : ${whenFalse}; })()`;
26
31
  }
27
32
 
28
33
  if (node.kind === "list") {
@@ -99,7 +104,12 @@ function emitOxcCompatObjectNode(node: JsxNodeIr): string {
99
104
  }
100
105
 
101
106
  if (node.kind === "conditional") {
102
- return `(${node.conditionCode}) ? ${emitOxcCompatObjectChildren(node.whenTrue)} : ${emitOxcCompatObjectChildren(node.whenFalse)}`;
107
+ const whenTrue = emitOxcCompatObjectChildren(node.whenTrue);
108
+ const whenFalse = emitOxcCompatObjectChildren(node.whenFalse);
109
+
110
+ return node.conditionValueName === undefined
111
+ ? `(${node.conditionCode}) ? ${whenTrue} : ${whenFalse}`
112
+ : `(() => { const ${node.conditionValueName} = (${node.conditionCode}); return ${node.conditionValueName} ? ${whenTrue} : ${whenFalse}; })()`;
103
113
  }
104
114
 
105
115
  if (node.kind === "list") {
package/src/oxc.ts CHANGED
@@ -43,11 +43,14 @@ import {
43
43
  collectOxcAsyncComponentNames,
44
44
  collectOxcExportedComponents,
45
45
  collectOxcExportedFunctionNames,
46
+ collectOxcLocalJsxReturnFunctionNames,
46
47
  collectOxcPlainComponentNames,
47
48
  hasComponentReturn,
49
+ hasLocalJsxHelperCallReturn,
48
50
  hasOxcFunctionLikeComponentReturn,
49
51
  isOxcExportedFunctionLike,
50
52
  isOxcComponentCallExpression,
53
+ isOxcLocalJsxHelperCallExpression,
51
54
  isJsxRoot,
52
55
  isOxcJsxComponentStatement,
53
56
  isOxcUnsupportedExportedFunction,
@@ -242,6 +245,12 @@ function analyzeOxcToIr(
242
245
  options?.compatReactNodeReturnRenderMode === "react-node"
243
246
  ? collectOxcCompatReactNodeComponentReferences(program)
244
247
  : undefined;
248
+ const localJsxReturnFunctionNames =
249
+ target === "server" ? collectOxcLocalJsxReturnFunctionNames(program) : new Set<string>();
250
+ const localJsxHelperHtmlParameters =
251
+ target === "server"
252
+ ? collectLocalJsxHelperHtmlParameters(program, localJsxReturnFunctionNames)
253
+ : new Map<string, Set<number>>();
245
254
  const bodyLowerers = createOxcBodyLowerers(compatRuntimeImports);
246
255
  const moduleRenderValueBindings = collectOxcBodyJsxBindingNames(body);
247
256
  const reactiveDerivedFunctionNames = collectOxcReactiveDerivedFunctionNames(body);
@@ -263,7 +272,7 @@ function analyzeOxcToIr(
263
272
  }
264
273
 
265
274
  if (
266
- isOxcJsxComponentStatement(statement) ||
275
+ isOxcJsxComponentStatement(statement, localJsxReturnFunctionNames) ||
267
276
  (options?.compatReactNodeReturn === true && isOxcExportedFunctionLike(statement))
268
277
  ) {
269
278
  const declaration = readObject(readObject(statement).declaration);
@@ -275,7 +284,7 @@ function analyzeOxcToIr(
275
284
  }
276
285
  continue;
277
286
  } else {
278
- if (isOxcUnsupportedExportedFunction(statement, options)) {
287
+ if (isOxcUnsupportedExportedFunction(statement, options, localJsxReturnFunctionNames)) {
279
288
  diagnostics.push({
280
289
  level: "error",
281
290
  code: "MR_UNSUPPORTED_COMPONENT_RETURN",
@@ -333,6 +342,8 @@ function analyzeOxcToIr(
333
342
  componentCallNames,
334
343
  bodyLowerers,
335
344
  reactiveDerivedFunctionNames,
345
+ localJsxReturnFunctionNames,
346
+ localJsxHelperHtmlParameters,
336
347
  ),
337
348
  );
338
349
 
@@ -387,6 +398,88 @@ function componentCallNamesFromProgram(program: unknown): Set<string> {
387
398
  ]);
388
399
  }
389
400
 
401
+ function collectLocalJsxHelperHtmlParameters(
402
+ program: unknown,
403
+ localJsxReturnFunctionNames: ReadonlySet<string>,
404
+ ): Map<string, Set<number>> {
405
+ const parameters = new Map<string, Set<number>>();
406
+
407
+ if (localJsxReturnFunctionNames.size === 0) {
408
+ return parameters;
409
+ }
410
+
411
+ for (const statement of readArray(readObject(program).body)) {
412
+ const object = readObject(statement);
413
+ const declaration =
414
+ object.type === "ExportNamedDeclaration" || object.type === "ExportDefaultDeclaration"
415
+ ? readObject(object.declaration)
416
+ : object;
417
+ const body = readObject(declaration.body);
418
+
419
+ if (body.type !== "BlockStatement") {
420
+ continue;
421
+ }
422
+
423
+ for (const returnExpression of collectOxcReturnExpressions(body)) {
424
+ const callExpression = unwrapOxcParentheses(returnExpression);
425
+ if (callExpression.type !== "CallExpression") {
426
+ continue;
427
+ }
428
+
429
+ const callee = unwrapOxcParentheses(readObject(callExpression.callee));
430
+ if (
431
+ callee.type !== "Identifier" ||
432
+ typeof callee.name !== "string" ||
433
+ !localJsxReturnFunctionNames.has(callee.name)
434
+ ) {
435
+ continue;
436
+ }
437
+ const calleeName = callee.name;
438
+
439
+ readArray(callExpression.arguments).forEach((argument, index) => {
440
+ if (!containsOxcJsxSyntax(readObject(argument))) {
441
+ return;
442
+ }
443
+
444
+ const indexes = parameters.get(calleeName) ?? new Set<number>();
445
+ indexes.add(index);
446
+ parameters.set(calleeName, indexes);
447
+ });
448
+ }
449
+ }
450
+
451
+ return parameters;
452
+ }
453
+
454
+ function collectOxcReturnExpressions(statement: Record<string, unknown>): Record<string, unknown>[] {
455
+ if (statement.type === "ReturnStatement") {
456
+ return [unwrapOxcParentheses(readObject(statement.argument))];
457
+ }
458
+
459
+ if (statement.type === "BlockStatement") {
460
+ return readArray(statement.body).flatMap((child) =>
461
+ collectOxcReturnExpressions(readObject(child)),
462
+ );
463
+ }
464
+
465
+ if (statement.type === "IfStatement") {
466
+ return [
467
+ ...collectOxcReturnExpressions(readObject(statement.consequent)),
468
+ ...collectOxcReturnExpressions(readObject(statement.alternate)),
469
+ ];
470
+ }
471
+
472
+ if (statement.type === "SwitchStatement") {
473
+ return readArray(statement.cases).flatMap((switchCase) =>
474
+ readArray(readObject(switchCase).consequent).flatMap((child) =>
475
+ collectOxcReturnExpressions(readObject(child)),
476
+ ),
477
+ );
478
+ }
479
+
480
+ return [];
481
+ }
482
+
390
483
  function analyzeOxcComponent(
391
484
  code: string,
392
485
  statement: unknown,
@@ -400,6 +493,8 @@ function analyzeOxcComponent(
400
493
  componentCallNames: Set<string> | undefined,
401
494
  bodyLowerers: OxcBodyLowerers,
402
495
  reactiveDerivedFunctionNames: ReadonlySet<string>,
496
+ localJsxReturnFunctionNames: ReadonlySet<string>,
497
+ localJsxHelperHtmlParameters: ReadonlyMap<string, ReadonlySet<number>>,
403
498
  ): ComponentIr[] {
404
499
  const object = readObject(statement);
405
500
 
@@ -428,6 +523,8 @@ function analyzeOxcComponent(
428
523
  componentCallNames,
429
524
  bodyLowerers,
430
525
  reactiveDerivedFunctionNames,
526
+ localJsxReturnFunctionNames,
527
+ localJsxHelperHtmlParameters,
431
528
  true,
432
529
  ),
433
530
  ];
@@ -457,6 +554,8 @@ function analyzeOxcComponent(
457
554
  componentCallNames,
458
555
  bodyLowerers,
459
556
  reactiveDerivedFunctionNames,
557
+ localJsxReturnFunctionNames,
558
+ localJsxHelperHtmlParameters,
460
559
  ),
461
560
  exported: false,
462
561
  },
@@ -488,13 +587,17 @@ function analyzeOxcComponent(
488
587
  componentCallNames,
489
588
  bodyLowerers,
490
589
  reactiveDerivedFunctionNames,
590
+ localJsxReturnFunctionNames,
591
+ localJsxHelperHtmlParameters,
491
592
  ),
492
593
  ];
493
594
  }
494
595
 
495
596
  if (
496
597
  declaration.type !== "FunctionDeclaration" ||
497
- (!compatReactNodeReturn && !hasComponentReturn(declaration.body))
598
+ (!compatReactNodeReturn &&
599
+ !hasComponentReturn(declaration.body) &&
600
+ !hasLocalJsxHelperCallReturn(declaration.body, localJsxReturnFunctionNames))
498
601
  ) {
499
602
  return [];
500
603
  }
@@ -521,10 +624,40 @@ function analyzeOxcComponent(
521
624
  componentCallNames,
522
625
  bodyLowerers,
523
626
  reactiveDerivedFunctionNames,
627
+ localJsxReturnFunctionNames,
628
+ localJsxHelperHtmlParameters,
524
629
  ),
525
630
  ];
526
631
  }
527
632
 
633
+ function lowerOxcLocalJsxHelperCallExpressionCode(
634
+ code: string,
635
+ expression: Record<string, unknown>,
636
+ componentNames: Set<string>,
637
+ target: CompileTarget,
638
+ diagnostics: Diagnostic[],
639
+ bodyLowerers: OxcBodyLowerers,
640
+ ): string {
641
+ if (expression.type !== "CallExpression") {
642
+ return readSource(code, expression);
643
+ }
644
+
645
+ const args = readArray(expression.arguments).map((argument) => {
646
+ const object = unwrapOxcParentheses(readObject(argument));
647
+ return containsOxcJsxSyntax(object)
648
+ ? (bodyLowerers.lowerServerStringExpression(
649
+ code,
650
+ object,
651
+ componentNames,
652
+ target,
653
+ diagnostics,
654
+ ) ?? readSource(code, argument))
655
+ : readSource(code, argument);
656
+ });
657
+
658
+ return `${readSource(code, readObject(expression.callee))}(${args.join(", ")})`;
659
+ }
660
+
528
661
  function analyzeOxcFunctionLikeComponent(
529
662
  code: string,
530
663
  name: string,
@@ -540,6 +673,8 @@ function analyzeOxcFunctionLikeComponent(
540
673
  componentCallNames: Set<string> | undefined,
541
674
  bodyLowerers: OxcBodyLowerers,
542
675
  reactiveDerivedFunctionNames: ReadonlySet<string>,
676
+ localJsxReturnFunctionNames: ReadonlySet<string>,
677
+ localJsxHelperHtmlParameters: ReadonlyMap<string, ReadonlySet<number>>,
543
678
  exportDefault = false,
544
679
  ): ComponentIr {
545
680
  const functionBody = readObject(functionLike.body);
@@ -563,6 +698,15 @@ function analyzeOxcFunctionLikeComponent(
563
698
  const parameters = readArray(functionLike.params).map((param) =>
564
699
  readOxcParameterName(code, param),
565
700
  );
701
+ const htmlParameterNames = new Set(
702
+ [...(localJsxHelperHtmlParameters.get(name) ?? [])]
703
+ .map((index) => parameters[index])
704
+ .filter((parameter): parameter is string => parameter !== undefined),
705
+ );
706
+ const bodyComponentNames =
707
+ /^[a-z]/.test(name) && componentNames.has(name)
708
+ ? new Set([...componentNames].filter((componentName) => componentName !== name))
709
+ : componentNames;
566
710
  const bodyStatements = body
567
711
  .filter(
568
712
  (bodyStatement) =>
@@ -576,7 +720,7 @@ function analyzeOxcFunctionLikeComponent(
576
720
  lowerOxcBodyStatementJsx(
577
721
  code,
578
722
  bodyStatement,
579
- componentNames,
723
+ bodyComponentNames,
580
724
  target,
581
725
  diagnostics,
582
726
  bodyStatementJsx,
@@ -590,7 +734,7 @@ function analyzeOxcFunctionLikeComponent(
590
734
  reactiveDerivedFunctionNames,
591
735
  );
592
736
  const childAnalysisContext = createOxcChildAnalysisContext(
593
- componentNames,
737
+ bodyComponentNames,
594
738
  target,
595
739
  diagnostics,
596
740
  bodyStatementJsx,
@@ -617,6 +761,23 @@ function analyzeOxcFunctionLikeComponent(
617
761
  ? analyzeOxcJsxNode(code, returnExpression, childAnalysisContext)
618
762
  : isOxcComponentCallExpression(returnExpression)
619
763
  ? analyzeOxcComponentCallExpression(code, returnExpression)
764
+ : isOxcLocalJsxHelperCallExpression(returnExpression, localJsxReturnFunctionNames)
765
+ ? {
766
+ kind: "expr" as const,
767
+ code: normalizeOxcExpressionCode(
768
+ bodyStatementJsx === "server-string"
769
+ ? lowerOxcLocalJsxHelperCallExpressionCode(
770
+ code,
771
+ returnExpression,
772
+ bodyComponentNames,
773
+ target,
774
+ diagnostics,
775
+ bodyLowerers,
776
+ )
777
+ : readSource(code, returnExpression),
778
+ ),
779
+ renderMode: "html" as const,
780
+ }
620
781
  : analyzeOxcDynamicRootReturn(
621
782
  code,
622
783
  returnExpression,
@@ -654,6 +815,7 @@ function analyzeOxcFunctionLikeComponent(
654
815
  bodyStatement !== earlyIfRootReturn?.fallthroughStatement,
655
816
  ),
656
817
  ),
818
+ ...htmlParameterNames,
657
819
  ]),
658
820
  bodyStatementJsx === "server-string" ? "html" : "dynamic",
659
821
  );
package/src/transform.ts CHANGED
@@ -207,10 +207,11 @@ function createSegmentMappings(outputCode: string, sourceCode: string): Generate
207
207
  let previousSourceLine = 0;
208
208
  let previousSourceColumn = 0;
209
209
  let previousNameIndex = 0;
210
+ const searchState: SourceMapSearchState = { tokenOffsets: new Map() };
210
211
 
211
212
  for (const [lineIndex, generatedLine] of generatedLines.entries()) {
212
213
  let previousGeneratedColumn = 0;
213
- const segments = collectSourceMapSegments(generatedLine, lineIndex, sourceLines);
214
+ const segments = collectSourceMapSegments(generatedLine, lineIndex, sourceLines, searchState);
214
215
 
215
216
  lines.push(
216
217
  segments
@@ -249,10 +250,15 @@ interface SourceMapSegment {
249
250
  name?: string;
250
251
  }
251
252
 
253
+ interface SourceMapSearchState {
254
+ tokenOffsets: Map<string, number>;
255
+ }
256
+
252
257
  function collectSourceMapSegments(
253
258
  generatedLine: string,
254
259
  generatedLineIndex: number,
255
260
  sourceLines: readonly string[],
261
+ searchState: SourceMapSearchState,
256
262
  ): SourceMapSegment[] {
257
263
  const fallbackSourceLine = findFallbackSourceLine(generatedLine, generatedLineIndex, sourceLines);
258
264
  const segments: SourceMapSegment[] = [
@@ -315,12 +321,12 @@ function collectSourceMapSegments(
315
321
  const sourceLocation =
316
322
  bindPropAttribute === undefined
317
323
  ? undefined
318
- : (findSourceLocation(sourceLines, `${bindPropAttribute}={${dynamicExpression}}`) ??
319
- findSourceLocation(sourceLines, `${bindPropAttribute}="${dynamicExpression}"`));
324
+ : (findSourceLocation(sourceLines, `${bindPropAttribute}={${dynamicExpression}}`, searchState) ??
325
+ findSourceLocation(sourceLines, `${bindPropAttribute}="${dynamicExpression}"`, searchState));
320
326
  const fallbackSourceLocation =
321
- findSourceLocation(sourceLines, `{${dynamicExpression}}`) ??
327
+ findSourceLocation(sourceLines, `{${dynamicExpression}}`, searchState) ??
322
328
  findJsxExpressionTokenLocation(sourceLines, dynamicExpression) ??
323
- findSourceLocation(sourceLines, dynamicExpression);
329
+ findSourceLocation(sourceLines, dynamicExpression, searchState);
324
330
  const resolvedSourceLocation = sourceLocation ?? fallbackSourceLocation;
325
331
 
326
332
  if (generatedColumn >= 0 && resolvedSourceLocation !== undefined) {
@@ -421,16 +427,42 @@ function findFallbackSourceLine(
421
427
  function findSourceLocation(
422
428
  sourceLines: readonly string[],
423
429
  token: string,
430
+ searchState?: SourceMapSearchState,
424
431
  ): { line: number; column: number } | undefined {
425
- for (const [line, sourceLine] of sourceLines.entries()) {
426
- const column = sourceLine.indexOf(token);
432
+ const source = sourceLines.join("\n");
433
+ const start = searchState?.tokenOffsets.get(token) ?? 0;
434
+ let offset = source.indexOf(token, start);
435
+
436
+ if (offset < 0 && start > 0) {
437
+ offset = source.indexOf(token);
438
+ }
439
+
440
+ if (offset < 0) {
441
+ return undefined;
442
+ }
443
+
444
+ searchState?.tokenOffsets.set(token, offset + token.length);
445
+ return sourceOffsetToLineColumn(sourceLines, offset);
446
+ }
447
+
448
+ function sourceOffsetToLineColumn(
449
+ sourceLines: readonly string[],
450
+ offset: number,
451
+ ): { line: number; column: number } {
452
+ let remaining = offset;
427
453
 
428
- if (column >= 0) {
429
- return { line, column };
454
+ for (const [line, sourceLine] of sourceLines.entries()) {
455
+ if (remaining <= sourceLine.length) {
456
+ return { line, column: remaining };
430
457
  }
458
+
459
+ remaining -= sourceLine.length + 1;
431
460
  }
432
461
 
433
- return undefined;
462
+ return {
463
+ line: Math.max(0, sourceLines.length - 1),
464
+ column: sourceLines.at(-1)?.length ?? 0,
465
+ };
434
466
  }
435
467
 
436
468
  function findJsxExpressionTokenLocation(