@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
@@ -34,6 +34,44 @@ export function collectOxcPlainComponentNames(program: unknown): string[] {
34
34
  });
35
35
  }
36
36
 
37
+ export function collectOxcLocalJsxReturnFunctionNames(program: unknown): Set<string> {
38
+ const names = new Set<string>();
39
+
40
+ for (const statement of readArray(readObject(program).body)) {
41
+ const object = readObject(statement);
42
+
43
+ if (object.type === "FunctionDeclaration" && hasOxcFunctionLikeJsxReturn(object)) {
44
+ const id = readObject(object.id);
45
+ if (typeof id.name === "string") {
46
+ names.add(id.name);
47
+ }
48
+ continue;
49
+ }
50
+
51
+ if (object.type !== "VariableDeclaration") {
52
+ continue;
53
+ }
54
+
55
+ for (const declarator of readArray(object.declarations)) {
56
+ const declaratorObject = readObject(declarator);
57
+ const id = readObject(declaratorObject.id);
58
+ const initializer = unwrapOxcComponentFunctionLikeInitializer(
59
+ readObject(declaratorObject.init),
60
+ );
61
+
62
+ if (
63
+ typeof id.name === "string" &&
64
+ initializer !== undefined &&
65
+ hasOxcFunctionLikeJsxReturn(initializer)
66
+ ) {
67
+ names.add(id.name);
68
+ }
69
+ }
70
+ }
71
+
72
+ return names;
73
+ }
74
+
37
75
  export function collectOxcExportedComponents(program: unknown): string[] {
38
76
  const body = readArray(readObject(program).body);
39
77
  const components: string[] = [];
@@ -133,8 +171,15 @@ export function isOxcExportedJsxComponent(statement: unknown): boolean {
133
171
  );
134
172
  }
135
173
 
136
- export function isOxcJsxComponentStatement(statement: unknown): boolean {
137
- return isOxcExportedJsxComponent(statement) || readOxcPlainComponent(statement) !== undefined;
174
+ export function isOxcJsxComponentStatement(
175
+ statement: unknown,
176
+ localJsxReturnFunctionNames: ReadonlySet<string> = new Set(),
177
+ ): boolean {
178
+ return (
179
+ isOxcExportedJsxComponent(statement) ||
180
+ isOxcExportedFunctionReturningLocalJsxHelper(statement, localJsxReturnFunctionNames) ||
181
+ readOxcPlainComponent(statement) !== undefined
182
+ );
138
183
  }
139
184
 
140
185
  export function isOxcExportedFunctionLike(statement: unknown): boolean {
@@ -159,6 +204,7 @@ export function isOxcExportedFunctionLike(statement: unknown): boolean {
159
204
  export function isOxcUnsupportedExportedFunction(
160
205
  statement: unknown,
161
206
  options?: AnalyzeModuleOptions,
207
+ localJsxReturnFunctionNames: ReadonlySet<string> = new Set(),
162
208
  ): boolean {
163
209
  if (options?.compatReactNodeReturn === true) {
164
210
  return false;
@@ -178,10 +224,29 @@ export function isOxcUnsupportedExportedFunction(
178
224
  typeof id.name === "string" &&
179
225
  /^[A-Z]/.test(id.name) &&
180
226
  !hasComponentReturn(declaration.body) &&
227
+ !hasLocalJsxHelperCallReturn(declaration.body, localJsxReturnFunctionNames) &&
181
228
  !hasOnlyNullReturns(declaration.body)
182
229
  );
183
230
  }
184
231
 
232
+ function isOxcExportedFunctionReturningLocalJsxHelper(
233
+ statement: unknown,
234
+ localJsxReturnFunctionNames: ReadonlySet<string>,
235
+ ): boolean {
236
+ const object = readObject(statement);
237
+
238
+ if (object.type !== "ExportNamedDeclaration") {
239
+ return false;
240
+ }
241
+
242
+ const declaration = readObject(object.declaration);
243
+
244
+ return (
245
+ declaration.type === "FunctionDeclaration" &&
246
+ hasLocalJsxHelperCallReturn(declaration.body, localJsxReturnFunctionNames)
247
+ );
248
+ }
249
+
185
250
  export function readOxcVariableComponentDeclaration(
186
251
  declaration: Record<string, unknown>,
187
252
  ): { name: string; initializer: Record<string, unknown> } | undefined {
@@ -305,6 +370,45 @@ export function hasComponentCallReturn(body: unknown): boolean {
305
370
  });
306
371
  }
307
372
 
373
+ export function hasLocalJsxHelperCallReturn(
374
+ body: unknown,
375
+ localJsxReturnFunctionNames: ReadonlySet<string>,
376
+ ): boolean {
377
+ if (localJsxReturnFunctionNames.size === 0) {
378
+ return false;
379
+ }
380
+
381
+ return readArray(readObject(body).body).some((statement) => {
382
+ const object = readObject(statement);
383
+
384
+ if (object.type === "ReturnStatement") {
385
+ return isOxcLocalJsxHelperCallExpression(
386
+ unwrapOxcParentheses(readObject(object.argument)),
387
+ localJsxReturnFunctionNames,
388
+ );
389
+ }
390
+
391
+ return hasNestedLocalJsxHelperCallReturn(object, localJsxReturnFunctionNames);
392
+ });
393
+ }
394
+
395
+ export function isOxcLocalJsxHelperCallExpression(
396
+ expression: Record<string, unknown>,
397
+ localJsxReturnFunctionNames: ReadonlySet<string>,
398
+ ): boolean {
399
+ if (expression.type !== "CallExpression") {
400
+ return false;
401
+ }
402
+
403
+ const callee = unwrapOxcParentheses(readObject(expression.callee));
404
+
405
+ return (
406
+ callee.type === "Identifier" &&
407
+ typeof callee.name === "string" &&
408
+ localJsxReturnFunctionNames.has(callee.name)
409
+ );
410
+ }
411
+
308
412
  function hasNestedJsxReturn(statement: Record<string, unknown>): boolean {
309
413
  if (statement.type === "SwitchStatement") {
310
414
  return readArray(statement.cases).some((switchCase) =>
@@ -359,6 +463,45 @@ function hasNestedComponentCallReturn(statement: Record<string, unknown>): boole
359
463
  return false;
360
464
  }
361
465
 
466
+ function hasNestedLocalJsxHelperCallReturn(
467
+ statement: Record<string, unknown>,
468
+ localJsxReturnFunctionNames: ReadonlySet<string>,
469
+ ): boolean {
470
+ if (statement.type === "SwitchStatement") {
471
+ return readArray(statement.cases).some((switchCase) =>
472
+ readArray(readObject(switchCase).consequent).some((child) => {
473
+ const object = readObject(child);
474
+ return (
475
+ object.type === "ReturnStatement" &&
476
+ isOxcLocalJsxHelperCallExpression(
477
+ unwrapOxcParentheses(readObject(object.argument)),
478
+ localJsxReturnFunctionNames,
479
+ )
480
+ );
481
+ }),
482
+ );
483
+ }
484
+
485
+ if (statement.type === "IfStatement") {
486
+ return (
487
+ hasLocalJsxHelperCallReturn(
488
+ { body: [statement.consequent] },
489
+ localJsxReturnFunctionNames,
490
+ ) ||
491
+ hasLocalJsxHelperCallReturn(
492
+ { body: [statement.alternate] },
493
+ localJsxReturnFunctionNames,
494
+ )
495
+ );
496
+ }
497
+
498
+ if (statement.type === "BlockStatement") {
499
+ return hasLocalJsxHelperCallReturn(statement, localJsxReturnFunctionNames);
500
+ }
501
+
502
+ return false;
503
+ }
504
+
362
505
  function collectReturnArguments(statement: Record<string, unknown>): Record<string, unknown>[] {
363
506
  if (statement.type === "ReturnStatement") {
364
507
  return [readObject(statement.argument)];
@@ -16,6 +16,7 @@ import type { Diagnostic } from "./types.js";
16
16
  export type AnalyzeOxcJsxNodeCallback = (
17
17
  node: Record<string, unknown>,
18
18
  bodyStatementJsx?: OxcBodyStatementJsxMode,
19
+ shadowNames?: readonly string[],
19
20
  ) => JsxNodeIr;
20
21
 
21
22
  export function analyzeOxcComponentProp(
@@ -159,7 +160,7 @@ export function analyzeOxcArrowJsxRenderer(
159
160
  if (body.type === "JSXElement" || body.type === "JSXFragment") {
160
161
  return {
161
162
  valueName,
162
- children: [analyzeJsxNode(body, bodyStatementJsx)],
163
+ children: [analyzeJsxNode(body, bodyStatementJsx, [valueName])],
163
164
  };
164
165
  }
165
166
 
@@ -1,13 +1,20 @@
1
1
  import type { ClientReferenceIr, JsxNodeIr } from "./ir.js";
2
2
  import { readArray, readObject } from "./oxc-node-utils.js";
3
3
 
4
- const routerEntryCompatRuntimeExports = new Set(["Link"]);
5
- const routerLinkCompatRuntimeExports = new Set(["Link"]);
4
+ const routerEntryCompatRuntimeExports = new Set<string>();
5
+ const routerLinkCompatRuntimeExports = new Set<string>();
6
+ const unknownCompatReference: ClientReferenceIr = { moduleId: "", exportName: "default" };
6
7
 
7
8
  interface ClientReferenceAliasState {
9
+ allowAmbiguousAliases: boolean;
8
10
  references: Map<string, ClientReferenceIr>;
9
11
  stringConstants: Map<string, string>;
10
12
  objectMembers: Map<string, Map<string, ClientReferenceIr>>;
13
+ objectMemberKeys: Map<string, Set<string>>;
14
+ dynamicObjectMembers: Map<string, ClientReferenceIr[]>;
15
+ dynamicObjectNestedMembers: Map<string, Map<string, ClientReferenceIr[]>>;
16
+ globModuleMaps: Set<string>;
17
+ objectEntryValueBindings: Map<string, string>;
11
18
  }
12
19
 
13
20
  export function collectOxcClientBoundaryImportComponents(
@@ -117,6 +124,7 @@ export function collectOxcCompatRuntimeImportComponents(
117
124
  }
118
125
  }
119
126
 
127
+ collectOxcClientReferenceAliases(program, names, { allowAmbiguousAliases: true });
120
128
  return names;
121
129
  }
122
130
 
@@ -321,11 +329,18 @@ function readOxcClassComponentName(
321
329
  function collectOxcClientReferenceAliases(
322
330
  node: unknown,
323
331
  references: Map<string, ClientReferenceIr>,
332
+ options: { allowAmbiguousAliases?: boolean } = {},
324
333
  ): void {
325
334
  const state: ClientReferenceAliasState = {
335
+ allowAmbiguousAliases: options.allowAmbiguousAliases === true,
326
336
  references,
327
337
  stringConstants: new Map(),
328
338
  objectMembers: new Map(),
339
+ objectMemberKeys: new Map(),
340
+ dynamicObjectMembers: new Map(),
341
+ dynamicObjectNestedMembers: new Map(),
342
+ globModuleMaps: new Set(),
343
+ objectEntryValueBindings: new Map(),
329
344
  };
330
345
 
331
346
  collectOxcClientReferenceAliasesFromNode(node, state);
@@ -371,6 +386,11 @@ function collectOxcClientReferenceAliasesFromNode(
371
386
  collectOxcObjectAssignClientReferenceAliases(object, state);
372
387
  }
373
388
 
389
+ if (object.type === "ForOfStatement") {
390
+ collectOxcForOfClientReferenceAliases(object, state);
391
+ return;
392
+ }
393
+
374
394
  for (const [key, value] of Object.entries(object)) {
375
395
  if (key === "type" || key === "start" || key === "end" || key === "loc") {
376
396
  continue;
@@ -404,6 +424,10 @@ function collectOxcVariableClientReferenceAlias(
404
424
  }
405
425
  }
406
426
 
427
+ if (isImportMetaGlobEagerCall(init)) {
428
+ state.globModuleMaps.add(aliasName);
429
+ }
430
+
407
431
  collectOxcObjectLiteralClientReferenceAliases(aliasName, init, state);
408
432
 
409
433
  const reference = expressionClientReference(init, state);
@@ -428,7 +452,16 @@ function collectOxcObjectLiteralClientReferenceAliases(
428
452
  }
429
453
 
430
454
  const keyName = propertyName(readOptionalObject(property.key), property.computed === true, state);
431
- const reference = expressionClientReference(readOptionalObject(property.value), state);
455
+ const value = readOptionalObject(property.value);
456
+ if (keyName !== undefined) {
457
+ addObjectMemberKey(state, objectName, keyName);
458
+ }
459
+
460
+ if (keyName !== undefined && value?.type === "ObjectExpression") {
461
+ collectOxcObjectLiteralClientReferenceAliases(`${objectName}.${keyName}`, value, state);
462
+ }
463
+
464
+ const reference = expressionClientReference(value, state);
432
465
  if (keyName !== undefined && reference !== undefined) {
433
466
  setObjectMemberReference(state, objectName, keyName, reference);
434
467
  }
@@ -448,11 +481,50 @@ function collectOxcAssignmentClientReferenceAlias(
448
481
  return;
449
482
  }
450
483
 
451
- const objectName = expressionObjectName(readOptionalObject(left.object));
484
+ const objectName = expressionObjectName(readOptionalObject(left.object), state);
452
485
  const memberName = propertyName(readOptionalObject(left.property), left.computed === true, state);
453
486
  const reference = expressionClientReference(readOptionalObject(node.right), state);
454
- if (objectName !== undefined && memberName !== undefined && reference !== undefined) {
455
- setObjectMemberReference(state, objectName, memberName, reference);
487
+ if (objectName !== undefined && memberName !== undefined) {
488
+ addObjectMemberKey(state, objectName, memberName);
489
+ if (reference !== undefined) {
490
+ setObjectMemberReference(state, objectName, memberName, reference);
491
+ }
492
+ }
493
+
494
+ if (objectName !== undefined && memberName === undefined) {
495
+ if (reference !== undefined) {
496
+ addDynamicObjectMemberReference(state, objectName, reference);
497
+ }
498
+
499
+ collectDynamicObjectNestedMemberReferences(objectName, readOptionalObject(node.right), state);
500
+ }
501
+ }
502
+
503
+ function collectOxcForOfClientReferenceAliases(
504
+ node: Record<string, unknown>,
505
+ state: ClientReferenceAliasState,
506
+ ): void {
507
+ const sourceName = objectEntriesSourceName(readOptionalObject(node.right), state);
508
+ const valueName = forOfObjectEntriesValueBindingName(readOptionalObject(node.left));
509
+
510
+ if (sourceName === undefined || valueName === undefined) {
511
+ for (const [key, value] of Object.entries(node)) {
512
+ if (key === "type" || key === "start" || key === "end" || key === "loc") {
513
+ continue;
514
+ }
515
+
516
+ collectOxcClientReferenceAliasesFromNode(value, state);
517
+ }
518
+ return;
519
+ }
520
+
521
+ const previous = state.objectEntryValueBindings.get(valueName);
522
+ state.objectEntryValueBindings.set(valueName, sourceName);
523
+ collectOxcClientReferenceAliasesFromNode(node.body, state);
524
+ if (previous === undefined) {
525
+ state.objectEntryValueBindings.delete(valueName);
526
+ } else {
527
+ state.objectEntryValueBindings.set(valueName, previous);
456
528
  }
457
529
  }
458
530
 
@@ -465,7 +537,7 @@ function collectOxcObjectAssignClientReferenceAliases(
465
537
  }
466
538
 
467
539
  const args = readArray(node.arguments).map(readOptionalObject);
468
- const target = expressionObjectName(args[0]);
540
+ const target = expressionObjectName(args[0], state);
469
541
  if (target === undefined) {
470
542
  return;
471
543
  }
@@ -488,7 +560,7 @@ function expressionClientReference(
488
560
  }
489
561
 
490
562
  if (node.type === "MemberExpression") {
491
- const objectName = expressionObjectName(readOptionalObject(node.object));
563
+ const objectName = expressionObjectName(readOptionalObject(node.object), state);
492
564
  const memberName = propertyName(readOptionalObject(node.property), node.computed === true, state);
493
565
  const objectReference =
494
566
  objectName === undefined ? undefined : state.references.get(objectName);
@@ -498,10 +570,39 @@ function expressionClientReference(
498
570
  if (objectMember !== undefined) {
499
571
  return objectMember;
500
572
  }
573
+
574
+ const dynamicObjectReference = selectClientReference(
575
+ dynamicObjectMemberReferences(state, objectName),
576
+ state.allowAmbiguousAliases,
577
+ );
578
+ if (dynamicObjectReference !== undefined) {
579
+ return dynamicObjectReference;
580
+ }
581
+
582
+ const objectEntryReference = objectEntryMemberReference(objectName, memberName, state);
583
+ if (objectEntryReference !== undefined) {
584
+ return objectEntryReference;
585
+ }
501
586
  }
502
587
 
503
588
  if (objectName !== undefined && memberName === undefined) {
504
- return uniqueClientReference(Array.from(state.objectMembers.get(objectName)?.values() ?? []));
589
+ return selectClientReference(
590
+ [...objectMemberReferences(state, objectName), ...dynamicObjectMemberReferences(state, objectName)],
591
+ state.allowAmbiguousAliases,
592
+ );
593
+ }
594
+
595
+ if (objectName === undefined && memberName !== undefined) {
596
+ const references = [
597
+ ...expressionObjectNameCandidates(readOptionalObject(node.object), state)
598
+ .map((candidate) => state.objectMembers.get(candidate)?.get(memberName)),
599
+ ...expressionDynamicObjectNestedMemberReferences(
600
+ readOptionalObject(node.object),
601
+ memberName,
602
+ state,
603
+ ),
604
+ ];
605
+ return selectClientReference(references, state.allowAmbiguousAliases);
505
606
  }
506
607
 
507
608
  if (objectReference?.exportName === "*" && memberName !== undefined) {
@@ -532,8 +633,74 @@ function expressionClientReference(
532
633
  return undefined;
533
634
  }
534
635
 
535
- function expressionObjectName(node: Record<string, unknown> | undefined): string | undefined {
536
- return node?.type === "Identifier" && typeof node.name === "string" ? node.name : undefined;
636
+ function objectEntryMemberReference(
637
+ objectName: string,
638
+ memberName: string,
639
+ state: ClientReferenceAliasState,
640
+ ): ClientReferenceIr | undefined {
641
+ const sourceName = state.objectEntryValueBindings.get(objectName);
642
+ if (sourceName === undefined) {
643
+ return undefined;
644
+ }
645
+
646
+ if (state.allowAmbiguousAliases && state.globModuleMaps.has(sourceName) && memberName === "default") {
647
+ return unknownCompatReference;
648
+ }
649
+
650
+ const references = Array.from(state.objectMemberKeys.get(sourceName) ?? [])
651
+ .map((key) => state.objectMembers.get(`${sourceName}.${key}`)?.get(memberName));
652
+ return selectClientReference(references, state.allowAmbiguousAliases);
653
+ }
654
+
655
+ function expressionObjectNameCandidates(
656
+ node: Record<string, unknown> | undefined,
657
+ state: ClientReferenceAliasState,
658
+ ): string[] {
659
+ const exactName = expressionObjectName(node, state);
660
+ if (exactName !== undefined) {
661
+ return [exactName];
662
+ }
663
+
664
+ if (node?.type !== "MemberExpression") {
665
+ return [];
666
+ }
667
+
668
+ const objectName = expressionObjectName(readOptionalObject(node.object), state);
669
+ const memberName = propertyName(readOptionalObject(node.property), node.computed === true, state);
670
+
671
+ if (objectName !== undefined && memberName === undefined) {
672
+ return Array.from(state.objectMemberKeys.get(objectName) ?? []).map(
673
+ (key) => `${objectName}.${key}`,
674
+ );
675
+ }
676
+
677
+ if (memberName === undefined) {
678
+ return [];
679
+ }
680
+
681
+ return expressionObjectNameCandidates(readOptionalObject(node.object), state).map(
682
+ (candidate) => `${candidate}.${memberName}`,
683
+ );
684
+ }
685
+
686
+ function expressionObjectName(
687
+ node: Record<string, unknown> | undefined,
688
+ state: ClientReferenceAliasState,
689
+ ): string | undefined {
690
+ if (node?.type === "Identifier" && typeof node.name === "string") {
691
+ return node.name;
692
+ }
693
+
694
+ if (node?.type === "MemberExpression") {
695
+ const objectName = expressionObjectName(readOptionalObject(node.object), state);
696
+ const memberName = propertyName(readOptionalObject(node.property), node.computed === true, state);
697
+
698
+ return objectName !== undefined && memberName !== undefined
699
+ ? `${objectName}.${memberName}`
700
+ : undefined;
701
+ }
702
+
703
+ return undefined;
537
704
  }
538
705
 
539
706
  function propertyName(
@@ -596,6 +763,63 @@ function setObjectMemberReference(
596
763
  const members = state.objectMembers.get(objectName) ?? new Map<string, ClientReferenceIr>();
597
764
  members.set(memberName, reference);
598
765
  state.objectMembers.set(objectName, members);
766
+ addObjectMemberKey(state, objectName, memberName);
767
+ }
768
+
769
+ function addDynamicObjectMemberReference(
770
+ state: ClientReferenceAliasState,
771
+ objectName: string,
772
+ reference: ClientReferenceIr,
773
+ ): void {
774
+ const references = state.dynamicObjectMembers.get(objectName) ?? [];
775
+ references.push(reference);
776
+ state.dynamicObjectMembers.set(objectName, references);
777
+ }
778
+
779
+ function collectDynamicObjectNestedMemberReferences(
780
+ objectName: string,
781
+ node: Record<string, unknown> | undefined,
782
+ state: ClientReferenceAliasState,
783
+ ): void {
784
+ if (node?.type !== "ObjectExpression") {
785
+ return;
786
+ }
787
+
788
+ for (const propertyValue of readArray(node.properties)) {
789
+ const property = readOptionalObject(propertyValue);
790
+ if (property?.type !== "Property") {
791
+ continue;
792
+ }
793
+
794
+ const keyName = propertyName(readOptionalObject(property.key), property.computed === true, state);
795
+ const reference = expressionClientReference(readOptionalObject(property.value), state);
796
+ if (keyName !== undefined && reference !== undefined) {
797
+ addDynamicObjectNestedMemberReference(state, objectName, keyName, reference);
798
+ }
799
+ }
800
+ }
801
+
802
+ function addDynamicObjectNestedMemberReference(
803
+ state: ClientReferenceAliasState,
804
+ objectName: string,
805
+ memberName: string,
806
+ reference: ClientReferenceIr,
807
+ ): void {
808
+ const members = state.dynamicObjectNestedMembers.get(objectName) ?? new Map<string, ClientReferenceIr[]>();
809
+ const references = members.get(memberName) ?? [];
810
+ references.push(reference);
811
+ members.set(memberName, references);
812
+ state.dynamicObjectNestedMembers.set(objectName, members);
813
+ }
814
+
815
+ function addObjectMemberKey(
816
+ state: ClientReferenceAliasState,
817
+ objectName: string,
818
+ memberName: string,
819
+ ): void {
820
+ const keys = state.objectMemberKeys.get(objectName) ?? new Set<string>();
821
+ keys.add(memberName);
822
+ state.objectMemberKeys.set(objectName, keys);
599
823
  }
600
824
 
601
825
  function isObjectAssignCall(node: Record<string, unknown>): boolean {
@@ -610,6 +834,56 @@ function isObjectAssignCall(node: Record<string, unknown>): boolean {
610
834
  property?.type === "Identifier" && property.name === "assign";
611
835
  }
612
836
 
837
+ function isObjectEntriesCall(node: Record<string, unknown> | undefined): boolean {
838
+ const callee = readOptionalObject(node?.callee);
839
+ if (callee?.type !== "MemberExpression" || callee.computed === true) {
840
+ return false;
841
+ }
842
+
843
+ const object = readOptionalObject(callee.object);
844
+ const property = readOptionalObject(callee.property);
845
+ return object?.type === "Identifier" && object.name === "Object" &&
846
+ property?.type === "Identifier" && property.name === "entries";
847
+ }
848
+
849
+ function isImportMetaGlobEagerCall(node: Record<string, unknown> | undefined): boolean {
850
+ const callee = readOptionalObject(node?.callee);
851
+ if (node?.type !== "CallExpression" || callee?.type !== "MemberExpression") {
852
+ return false;
853
+ }
854
+
855
+ const property = readOptionalObject(callee.property);
856
+ if (callee.computed === true || property?.type !== "Identifier" || property.name !== "glob") {
857
+ return false;
858
+ }
859
+
860
+ const metaProperty = readOptionalObject(callee.object);
861
+ const meta = readOptionalObject(metaProperty?.meta);
862
+ const metaName = meta?.type === "Identifier" ? meta.name : undefined;
863
+ const propertyName = readOptionalObject(metaProperty?.property);
864
+ const importMetaPropertyName = propertyName?.type === "Identifier" ? propertyName.name : undefined;
865
+ if (metaProperty?.type !== "MetaProperty" || metaName !== "import" || importMetaPropertyName !== "meta") {
866
+ return false;
867
+ }
868
+
869
+ const options = readOptionalObject(readArray(node.arguments)[1]);
870
+ if (options?.type !== "ObjectExpression") {
871
+ return false;
872
+ }
873
+
874
+ return readArray(options.properties).some((propertyValue) => {
875
+ const property = readOptionalObject(propertyValue);
876
+ const key = readOptionalObject(property?.key);
877
+ const value = readOptionalObject(property?.value);
878
+ return property?.type === "Property" &&
879
+ property.computed !== true &&
880
+ key?.type === "Identifier" &&
881
+ key.name === "eager" &&
882
+ value?.type === "Literal" &&
883
+ value.value === true;
884
+ });
885
+ }
886
+
613
887
  function uniqueClientReference(
614
888
  values: readonly (ClientReferenceIr | undefined)[],
615
889
  ): ClientReferenceIr | undefined {
@@ -631,6 +905,87 @@ function uniqueClientReference(
631
905
  : undefined;
632
906
  }
633
907
 
908
+ function selectClientReference(
909
+ values: readonly (ClientReferenceIr | undefined)[],
910
+ allowAmbiguousAliases: boolean,
911
+ ): ClientReferenceIr | undefined {
912
+ if (!allowAmbiguousAliases) {
913
+ return uniqueClientReference(values);
914
+ }
915
+
916
+ if (values.length === 0 || values.some((value) => value === undefined)) {
917
+ return undefined;
918
+ }
919
+
920
+ return values[0];
921
+ }
922
+
923
+ function objectMemberReferences(
924
+ state: ClientReferenceAliasState,
925
+ objectName: string,
926
+ ): Array<ClientReferenceIr | undefined> {
927
+ const keys = Array.from(state.objectMemberKeys.get(objectName) ?? []);
928
+ return keys.map((key) => state.objectMembers.get(objectName)?.get(key));
929
+ }
930
+
931
+ function dynamicObjectMemberReferences(
932
+ state: ClientReferenceAliasState,
933
+ objectName: string,
934
+ ): Array<ClientReferenceIr | undefined> {
935
+ return state.dynamicObjectMembers.get(objectName) ?? [];
936
+ }
937
+
938
+ function expressionDynamicObjectNestedMemberReferences(
939
+ node: Record<string, unknown> | undefined,
940
+ memberName: string,
941
+ state: ClientReferenceAliasState,
942
+ ): Array<ClientReferenceIr | undefined> {
943
+ if (node?.type !== "MemberExpression") {
944
+ return [];
945
+ }
946
+
947
+ const objectName = expressionObjectName(readOptionalObject(node.object), state);
948
+ const property = propertyName(readOptionalObject(node.property), node.computed === true, state);
949
+ if (objectName === undefined || property !== undefined) {
950
+ return [];
951
+ }
952
+
953
+ return state.dynamicObjectNestedMembers.get(objectName)?.get(memberName) ?? [];
954
+ }
955
+
956
+ function objectEntriesSourceName(
957
+ node: Record<string, unknown> | undefined,
958
+ state: ClientReferenceAliasState,
959
+ ): string | undefined {
960
+ if (!isObjectEntriesCall(node)) {
961
+ return undefined;
962
+ }
963
+
964
+ const args = readArray(node?.arguments).map(readOptionalObject);
965
+ return expressionObjectName(args[0], state);
966
+ }
967
+
968
+ function forOfObjectEntriesValueBindingName(
969
+ node: Record<string, unknown> | undefined,
970
+ ): string | undefined {
971
+ const declaration =
972
+ node?.type === "VariableDeclaration"
973
+ ? readOptionalObject(readArray(node.declarations)[0])
974
+ : undefined;
975
+ const pattern = declaration?.type === "VariableDeclarator"
976
+ ? readOptionalObject(declaration.id)
977
+ : node;
978
+
979
+ if (pattern?.type !== "ArrayPattern") {
980
+ return undefined;
981
+ }
982
+
983
+ const valueElement = readOptionalObject(readArray(pattern.elements)[1]);
984
+ return valueElement?.type === "Identifier" && typeof valueElement.name === "string"
985
+ ? valueElement.name
986
+ : undefined;
987
+ }
988
+
634
989
  function uniqueString(values: readonly (string | undefined)[]): string | undefined {
635
990
  const unique = new Set(values.filter((value): value is string => value !== undefined));
636
991
  return unique.size === 1 ? Array.from(unique)[0] : undefined;