Snap/sprout use new pragma parser and compiler defaults

Updates Snap and Sprout to use the new pragma parser, which also means they will 
always use the same default flags as the compiler itself sets. A side benefit of 
this is that you no longer need to rebuild snap/sprout to update their flags, 
since they will take flags from the version of the compiler being executed.
This commit is contained in:
Joe Savona
2023-10-05 10:57:20 -07:00
parent cadcf3de2d
commit ed9ea81e4a
37 changed files with 80 additions and 78 deletions
@@ -2,7 +2,7 @@
## Input
```javascript
// @memoizeJsxElements false
// @memoizeJsxElements:false
function Component(props) {
const [name, setName] = useState(null);
const onChange = function (e) {
@@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react"; // @memoizeJsxElements false
import { unstable_useMemoCache as useMemoCache } from "react"; // @memoizeJsxElements:false
function Component(props) {
const $ = useMemoCache(1);
const [name, setName] = useState(null);
@@ -1,4 +1,4 @@
// @memoizeJsxElements false
// @memoizeJsxElements:false
function Component(props) {
const [name, setName] = useState(null);
const onChange = function (e) {
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateNoSetStateInRender
function Component(props) {
const logEvent = useLogging(props.appId);
const [currentStep, setCurrentStep] = useState(0);
@@ -33,7 +34,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (11:11)
[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (12:12)
```
@@ -1,3 +1,4 @@
// @validateNoSetStateInRender
function Component(props) {
const logEvent = useLogging(props.appId);
const [currentStep, setCurrentStep] = useState(0);
@@ -2,7 +2,7 @@
## Input
```javascript
// @debug
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const value = ref.current;
@@ -1,4 +1,4 @@
// @debug
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const value = ref.current;
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateFrozenLambdas
function component(a, b) {
let y = { b };
let z = { a };
@@ -20,7 +21,7 @@ function component(a, b) {
## Error
```
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (8:8)
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (9:9)
```
@@ -1,3 +1,4 @@
// @validateFrozenLambdas
function component(a, b) {
let y = { b };
let z = { a };
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateFrozenLambdas
function Component(props) {
const x = {};
let fn;
@@ -25,7 +26,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (15:15)
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (16:16)
```
@@ -1,3 +1,4 @@
// @validateFrozenLambdas
function Component(props) {
const x = {};
let fn;
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateFrozenLambdas
function Component(props) {
const x = {};
const onChange = (e) => {
@@ -18,7 +19,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (8:8)
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (9:9)
```
@@ -1,3 +1,4 @@
// @validateFrozenLambdas
function Component(props) {
const x = {};
const onChange = (e) => {
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateFrozenLambdas
function Component(props) {
let x = "";
const onChange = (e) => {
@@ -16,7 +17,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (6:6)
[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (7:7)
```
@@ -1,3 +1,4 @@
// @validateFrozenLambdas
function Component(props) {
let x = "";
const onChange = (e) => {
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const x = foo(ref);
@@ -14,7 +15,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at mutate? $21[6:8]:TObject<BuiltInUseRefId> (3:3)
[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at mutate? $21[6:8]:TObject<BuiltInUseRefId> (4:4)
```
@@ -1,3 +1,4 @@
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const x = foo(ref);
@@ -2,7 +2,7 @@
## Input
```javascript
// @debug
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const renderItem = (item) => {
@@ -1,4 +1,4 @@
// @debug
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const renderItem = (item) => {
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
return <Foo ref={ref.current} />;
@@ -13,7 +14,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $19:TObject<BuiltInRefValue> (3:3)
[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $19:TObject<BuiltInRefValue> (4:4)
```
@@ -1,3 +1,4 @@
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
return <Foo ref={ref.current} />;
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
ref.current = props.value;
@@ -14,9 +15,9 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at store $21[7:9]:TObject<BuiltInUseRefId> (3:3)
[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at store $21[7:9]:TObject<BuiltInUseRefId> (4:4)
[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $24:TObject<BuiltInRefValue> (4:4)
[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $24:TObject<BuiltInRefValue> (5:5)
```
@@ -1,3 +1,4 @@
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
ref.current = props.value;
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateNoSetStateInRender
function Component(props) {
const [x, setX] = useState(0);
const aliased = setX;
@@ -18,9 +19,9 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (5:5)
[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (6:6)
[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (7:7)
```
@@ -1,3 +1,4 @@
// @validateNoSetStateInRender
function Component(props) {
const [x, setX] = useState(0);
const aliased = setX;
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateRefAccessDuringRender
function Foo({ a }) {
const ref = useRef();
// type information is lost here as we don't track types of fields
@@ -21,7 +22,7 @@ function Foo({ a }) {
## Error
```
[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at capture $29:TObject<BuiltInUseRefId> (4:4)
[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at capture $29:TObject<BuiltInUseRefId> (5:5)
```
@@ -1,3 +1,4 @@
// @validateRefAccessDuringRender
function Foo({ a }) {
const ref = useRef();
// type information is lost here as we don't track types of fields
@@ -2,6 +2,7 @@
## Input
```javascript
// @validateNoSetStateInRender
function Component(props) {
let y = 0;
const [x, setX] = useState(0);
@@ -21,7 +22,7 @@ function Component(props) {
## Error
```
[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (6:6)
[ReactForget] InvalidReact: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) (7:7)
```
@@ -1,3 +1,4 @@
// @validateNoSetStateInRender
function Component(props) {
let y = 0;
const [x, setX] = useState(0);
@@ -2,7 +2,6 @@
## Input
```javascript
// @validateRefAccessDuringRender false
function Foo({ a }) {
const ref = useRef();
const val = ref.current;
@@ -16,7 +15,7 @@ function Foo({ a }) {
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender false
import { unstable_useMemoCache as useMemoCache } from "react";
function Foo(t20) {
const $ = useMemoCache(4);
const { a } = t20;
@@ -1,4 +1,3 @@
// @validateRefAccessDuringRender false
function Foo({ a }) {
const ref = useRef();
const val = ref.current;
@@ -2,7 +2,6 @@
## Input
```javascript
// @validateRefAccessDuringRender false
function Foo({ a }) {
const ref = useRef();
const x = { a, val: ref.current };
@@ -15,7 +14,7 @@ function Foo({ a }) {
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender false
import { unstable_useMemoCache as useMemoCache } from "react";
function Foo(t17) {
const $ = useMemoCache(4);
const { a } = t17;
@@ -1,4 +1,3 @@
// @validateRefAccessDuringRender false
function Foo({ a }) {
const ref = useRef();
const x = { a, val: ref.current };
@@ -2,6 +2,7 @@ import assert from "assert";
import type { runReactForgetBabelPlugin as RunReactForgetBabelPlugin } from "babel-plugin-react-forget/src/Babel/RunReactForgetBabelPlugin";
import { CompilationMode } from "babel-plugin-react-forget/src/Entrypoint";
import type { Effect, ValueKind } from "babel-plugin-react-forget/src/HIR";
import type { parseConfigPragma as ParseConfigPragma } from "babel-plugin-react-forget/src/HIR/Environment";
export function parseLanguage(source: string): "flow" | "typescript" {
return source.indexOf("@flow") !== -1 ? "flow" : "typescript";
@@ -11,6 +12,7 @@ export function transformFixtureInput(
input: string,
basename: string,
pluginFn: typeof RunReactForgetBabelPlugin,
parseConfigPragmaFn: typeof ParseConfigPragma,
includeAst: boolean = false
) {
// Extract the first line to quickly check for custom test directives
@@ -19,16 +21,8 @@ export function transformFixtureInput(
let language = parseLanguage(firstLine);
let gating = null;
let instrumentForget = null;
let memoizeJsxElements = true;
let enableAssumeHooksFollowRulesOfReact = false;
let disableAllMemoization = false;
let validateRefAccessDuringRender = true;
let validateNoSetStateInRender = true;
let enableEmitFreeze = null;
let compilationMode: CompilationMode = "all";
let enableForest = false;
let enableMergeConsecutiveScopes = false;
let bailoutOnHoleyArrays = false;
if (firstLine.indexOf("@compilationMode(annotation)") !== -1) {
assert(
@@ -57,37 +51,13 @@ export function transformFixtureInput(
importSpecifierName: "useRenderCounter",
};
}
if (firstLine.includes("@memoizeJsxElements false")) {
memoizeJsxElements = false;
}
if (firstLine.includes("@enableAssumeHooksFollowRulesOfReact true")) {
enableAssumeHooksFollowRulesOfReact = true;
}
if (firstLine.includes("@disableAllMemoization true")) {
disableAllMemoization = true;
}
if (firstLine.includes("@validateRefAccessDuringRender false")) {
validateRefAccessDuringRender = false;
}
if (firstLine.includes("@validateNoSetStateInRender false")) {
validateNoSetStateInRender = false;
}
if (firstLine.includes("@enableEmitFreeze")) {
enableEmitFreeze = {
source: "react-forget-runtime",
importSpecifierName: "makeReadOnly",
};
}
if (firstLine.includes("@enableForest true")) {
enableForest = true;
}
if (firstLine.includes("@bailoutOnHoleyArrays")) {
bailoutOnHoleyArrays = true;
}
if (firstLine.includes("@enableMergeConsecutiveScopes")) {
enableMergeConsecutiveScopes = true;
}
const config = parseConfigPragmaFn(firstLine);
return pluginFn(
input,
@@ -95,6 +65,7 @@ export function transformFixtureInput(
language,
{
environment: {
...config,
customHooks: new Map([
[
"useFreeze",
@@ -124,17 +95,8 @@ export function transformFixtureInput(
},
],
]),
enableAssumeHooksFollowRulesOfReact,
disableAllMemoization,
memoizeJsxElements,
validateRefAccessDuringRender,
validateFrozenLambdas: true,
validateNoSetStateInRender,
enableEmitFreeze,
enableMergeConsecutiveScopes,
assertValidMutableRanges: true,
bailoutOnHoleyArrays,
enableForest,
},
compilationMode,
logger: null,
@@ -13,6 +13,12 @@ export const LOGGER_PATH = path.join(
"Utils",
"logger.js"
);
export const PARSE_CONFIG_PRAGMA_PATH = path.join(
process.cwd(),
"dist",
"HIR",
"Environment.js"
);
export const FIXTURES_PATH = path.join(
process.cwd(),
"src",
@@ -6,6 +6,7 @@
*/
import type { runReactForgetBabelPlugin as RunReactForgetBabelPlugin } from "babel-plugin-react-forget/src/Babel/RunReactForgetBabelPlugin";
import type { parseConfigPragma as ParseConfigPragma } from "babel-plugin-react-forget/src/HIR/Environment";
import {
TestFixture,
transformFixtureInput,
@@ -39,6 +40,7 @@ export type TestResult = {
export async function compile(
compilerPath: string,
loggerPath: string,
parseConfigPragmaPath: string,
fixture: TestFixture,
compilerVersion: number,
implicitDebugMode: boolean,
@@ -78,6 +80,9 @@ export async function compile(
runReactForgetBabelPlugin: typeof RunReactForgetBabelPlugin;
};
const { toggleLogging } = require(loggerPath);
const { parseConfigPragma } = require(parseConfigPragmaPath) as {
parseConfigPragma: typeof ParseConfigPragma;
};
// only try logging if we filtered out all but one fixture,
// since console log order is non-deterministic
@@ -86,7 +91,8 @@ export async function compile(
code = transformFixtureInput(
input,
basename,
runReactForgetBabelPlugin
runReactForgetBabelPlugin,
parseConfigPragma
).code;
} catch (e) {
e.message = e.message.replace(/\u001b[^m]*m/g, "");
+8 -5
View File
@@ -9,12 +9,14 @@ import watcher from "@parcel/watcher";
import chalk from "chalk";
import {
COMPILER_PATH,
LOGGER_PATH,
FIXTURES_PATH,
FILTER_FILENAME,
FILTER_PATH,
readTestFilter,
FIXTURES_PATH,
LOGGER_PATH,
PARSE_CONFIG_PRAGMA_PATH,
TestFilter,
getFixtures,
readTestFilter,
} from "fixture-test-utils";
import fs from "fs";
import invariant from "invariant";
@@ -26,9 +28,8 @@ import * as readline from "readline";
import ts from "typescript";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { TestResult } from "./compiler-worker";
import * as compiler from "./compiler-worker";
import { getFixtures } from "fixture-test-utils";
import { TestResult } from "./compiler-worker";
const WORKER_PATH = require.resolve("./compiler-worker.js");
@@ -126,6 +127,7 @@ async function run(
.compile(
COMPILER_PATH,
LOGGER_PATH,
PARSE_CONFIG_PRAGMA_PATH,
fixture,
compilerVersion,
filter?.debug ?? false,
@@ -142,6 +144,7 @@ async function run(
let output = await compiler.compile(
COMPILER_PATH,
LOGGER_PATH,
PARSE_CONFIG_PRAGMA_PATH,
fixture,
compilerVersion,
filter?.debug ?? false,
+11 -4
View File
@@ -5,24 +5,30 @@
* LICENSE file in the root directory of this source tree.
*/
import { NodePath, PluginItem, transformFromAstSync } from "@babel/core";
import * as parser from "@babel/parser";
import * as t from "@babel/types";
import type { runReactForgetBabelPlugin as RunReactForgetBabelPlugin } from "babel-plugin-react-forget/src/Babel/RunReactForgetBabelPlugin";
import type { parseConfigPragma as ParseConfigPragma } from "babel-plugin-react-forget/src/HIR/Environment";
import {
COMPILER_PATH,
PARSE_CONFIG_PRAGMA_PATH,
parseLanguage,
TestFixture,
transformFixtureInput,
} from "fixture-test-utils";
import { NodePath, PluginItem, transformFromAstSync } from "@babel/core";
import fs from "fs/promises";
import * as parser from "@babel/parser";
import * as t from "@babel/types";
import { doEval, EvaluatorResult } from "./runner-evaluator";
import path from "path";
import { doEval, EvaluatorResult } from "./runner-evaluator";
const { runReactForgetBabelPlugin } = require(COMPILER_PATH) as {
runReactForgetBabelPlugin: typeof RunReactForgetBabelPlugin;
};
const { parseConfigPragma } = require(PARSE_CONFIG_PRAGMA_PATH) as {
parseConfigPragma: typeof ParseConfigPragma;
};
// TODO: save output in .sprout.md files
export type TestResult =
| {
@@ -118,6 +124,7 @@ function transformFixtureForget(
input,
filename,
runReactForgetBabelPlugin,
parseConfigPragma,
true
);