@samitouri / QOS-React-2 / commits / f5af92d2c4

feat(compiler): Support MetaProperty (#29752)

## Summary See #29737 ## How did you test this change? As the feature requires module support and the test runner does currently not support running tests as modules, I could only test it via playground.

Niklas Mollenhauer committed Jun 9, 2024 at 01:18 UTC f5af92d2c47d1e1f455faf912b1d3221d1038c37
17 files changed +202 -2
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+22
@@ -2427,6 +2427,28 @@ function lowerExpression(
2427 let expr = exprPath as NodePath<t.TSNonNullExpression>;
2428 return lowerExpression(builder, expr.get("expression"));
2429 }
2430 + case "MetaProperty": {
2431 + let expr = exprPath as NodePath<t.MetaProperty>;
2432 + if (
2433 + expr.node.meta.name === "import" &&
2434 + expr.node.property.name === "meta"
2435 + ) {
2436 + return {
2437 + kind: "MetaProperty",
2438 + meta: expr.node.meta.name,
2439 + property: expr.node.property.name,
2440 + loc: expr.node.loc ?? GeneratedSource,
2441 + };
2442 + }
2443 +
2444 + builder.errors.push({
2445 + reason: `(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta`,
2446 + severity: ErrorSeverity.Todo,
2447 + loc: exprPath.node.loc ?? null,
2448 + suggestions: null,
2449 + });
2450 + return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
2451 + }
2452 default: {
2453 builder.errors.push({
2454 reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
+6
@@ -898,6 +898,12 @@ export type InstructionValue =
898 flags: string;
899 loc: SourceLocation;
900 }
901 + | {
902 + kind: "MetaProperty";
903 + meta: string;
904 + property: string;
905 + loc: SourceLocation;
906 + }
907
908 // store `object.property = value`
909 | {
compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
+4
@@ -636,6 +636,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
636 value = `RegExp /${instrValue.pattern}/${instrValue.flags}`;
637 break;
638 }
639 + case "MetaProperty": {
640 + value = `MetaProperty ${instrValue.meta}.${instrValue.property}`;
641 + break;
642 + }
643 case "Await": {
644 value = `Await ${printPlace(instrValue.value)}`;
645 break;
compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts
+2
@@ -242,6 +242,7 @@ export function* eachInstructionValueOperand(
242 }
243 case "Debugger":
244 case "RegExpLiteral":
245 + case "MetaProperty":
246 case "LoadGlobal":
247 case "UnsupportedNode":
248 case "Primitive":
@@ -566,6 +567,7 @@ export function mapInstructionValueOperands(
567 }
568 case "Debugger":
569 case "RegExpLiteral":
570 + case "MetaProperty":
571 case "LoadGlobal":
572 case "UnsupportedNode":
573 case "Primitive":
compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts
+12
@@ -1216,6 +1216,18 @@ function inferBlock(
1216 };
1217 break;
1218 }
1219 + case "MetaProperty": {
1220 + if (instrValue.meta !== "import" || instrValue.property !== "meta") {
1221 + continue;
1222 + }
1223 +
1224 + valueKind = {
1225 + kind: ValueKind.Global,
1226 + reason: new Set([ValueReason.Global]),
1227 + context: new Set(),
1228 + };
1229 + break;
1230 + }
1231 case "LoadGlobal":
1232 valueKind = {
1233 kind: ValueKind.Global,
compiler/packages/babel-plugin-react-compiler/src/Optimization/DeadCodeElimination.ts
+1
@@ -350,6 +350,7 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
350 return false;
351 }
352 case "RegExpLiteral":
353 + case "MetaProperty":
354 case "LoadGlobal":
355 case "ArrayExpression":
356 case "BinaryExpression":
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+7
@@ -2021,6 +2021,13 @@ function codegenInstructionValue(
2021 value = t.regExpLiteral(instrValue.pattern, instrValue.flags);
2022 break;
2023 }
2024 + case "MetaProperty": {
2025 + value = t.metaProperty(
2026 + t.identifier(instrValue.meta),
2027 + t.identifier(instrValue.property)
2028 + );
2029 + break;
2030 + }
2031 case "Await": {
2032 value = t.awaitExpression(codegenPlaceToExpression(cx, instrValue.value));
2033 break;
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts
+1
@@ -199,6 +199,7 @@ function mayAllocate(env: Environment, instruction: Instruction): boolean {
199 case "DeclareContext":
200 case "StoreLocal":
201 case "LoadGlobal":
202 + case "MetaProperty":
203 case "TypeCastExpression":
204 case "LoadLocal":
205 case "LoadContext":
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonEscapingScopes.ts
+1
@@ -459,6 +459,7 @@ function computeMemoizationInputs(
459 case "ComputedDelete":
460 case "PropertyDelete":
461 case "LoadGlobal":
462 + case "MetaProperty":
463 case "TemplateLiteral":
464 case "Primitive":
465 case "JSXText":
compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts
+1
@@ -338,6 +338,7 @@ function* generateInstructionTypes(
338 case "DeclareLocal":
339 case "NewExpression":
340 case "RegExpLiteral":
341 + case "MetaProperty":
342 case "PropertyStore":
343 case "ComputedStore":
344 case "ComputedLoad":
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-new-target-meta-property.expect.md new
+27
@@ -0,0 +1,27 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { Stringify } from "shared-runtime";
6 +
7 +function foo() {
8 + const nt = new.target;
9 + return <Stringify value={nt} />;
10 +}
11 +
12 +```
13 +
14 +
15 +## Error
16 +
17 +```
18 + 2 |
19 + 3 | function foo() {
20 +> 4 | const nt = new.target;
21 + | ^^^^^^^^^^ Todo: (BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta (4:4)
22 + 5 | return <Stringify value={nt} />;
23 + 6 | }
24 + 7 |
25 +```
26 +
27 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-new-target-meta-property.js new
+6
@@ -0,0 +1,6 @@
1 +import { Stringify } from "shared-runtime";
2 +
3 +function foo() {
4 + const nt = new.target;
5 + return <Stringify value={nt} />;
6 +}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/meta-property.expect.md new
+67
@@ -0,0 +1,67 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function a() {
6 + return import.meta.url;
7 +}
8 +
9 +function b() {
10 + let a = 0;
11 + if (import.meta.url) {
12 + a = 1;
13 + }
14 + return a;
15 +}
16 +
17 +function c() {
18 + let a = 0;
19 + if (import.meta.foo) {
20 + a = 1;
21 + }
22 + return a;
23 +}
24 +
25 +function d() {
26 + let a = 0;
27 + if (import.meta) {
28 + a = 1;
29 + }
30 + return a;
31 +}
32 +
33 +```
34 +
35 +## Code
36 +
37 +```javascript
38 +function a() {
39 + return import.meta.url;
40 +}
41 +
42 +function b() {
43 + let a = 0;
44 + if (import.meta.url) {
45 + a = 1;
46 + }
47 + return a;
48 +}
49 +
50 +function c() {
51 + let a = 0;
52 + if (import.meta.foo) {
53 + a = 1;
54 + }
55 + return a;
56 +}
57 +
58 +function d() {
59 + let a = 0;
60 + if (import.meta) {
61 + a = 1;
62 + }
63 + return a;
64 +}
65 +
66 +```
67 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/meta-property.mjs new
+27
@@ -0,0 +1,27 @@
1 +function a() {
2 + return import.meta.url;
3 +}
4 +
5 +function b() {
6 + let a = 0;
7 + if (import.meta.url) {
8 + a = 1;
9 + }
10 + return a;
11 +}
12 +
13 +function c() {
14 + let a = 0;
15 + if (import.meta.foo) {
16 + a = 1;
17 + }
18 + return a;
19 +}
20 +
21 +function d() {
22 + let a = 0;
23 + if (import.meta) {
24 + a = 1;
25 + }
26 + return a;
27 +}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/tsconfig.json
+5 -1
@@ -23,8 +23,12 @@
23 },
24 "include": [
25 "./compiler/**/*.js",
26 - "./compiler/**/*.jsx",
26 + "./compiler/**/*.mjs",
27 + "./compiler/**/*.cjs",
28 "./compiler/**/*.ts",
29 + "./compiler/**/*.mts",
30 + "./compiler/**/*.cts",
31 + "./compiler/**/*.jsx",
32 "./compiler/**/*.tsx"
33 ]
34 }
compiler/packages/snap/src/SproutTodoFilter.ts
+3
@@ -499,6 +499,9 @@ const skipFilter = new Set([
499 "useState-unpruned-dependency",
500 "useState-and-other-hook-unpruned-dependency",
501 "change-detect-reassign",
502 +
503 + // needs to be executed as a module
504 + "meta-property",
505 ]);
506
507 export default skipFilter;
compiler/packages/snap/src/fixture-utils.ts
+10 -1
@@ -10,7 +10,16 @@ import * as glob from "glob";
10 import path from "path";
11 import { FILTER_PATH, FIXTURES_PATH, SNAPSHOT_EXTENSION } from "./constants";
12
13 -const INPUT_EXTENSIONS = [".js", ".ts", ".jsx", ".tsx"];
13 +const INPUT_EXTENSIONS = [
14 + ".js",
15 + ".cjs",
16 + ".mjs",
17 + ".ts",
18 + ".cts",
19 + ".mts",
20 + ".jsx",
21 + ".tsx",
22 +];
23
24 export type TestFilter = {
25 debug: boolean;