mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Update base for Update on "compiler: super early exploration of instruction reordering"
See comments in InstructionReordering.ts. This needs substantial iteration before landing in some form, just putting up to share for discussion. [ghstack-poisoned]
This commit is contained in:
@@ -0,0 +1 @@
|
||||
c998bb1ed4b3285398c9c7797135d3f060243c6a
|
||||
@@ -24,6 +24,25 @@ jobs:
|
||||
run: echo "matrix=$(find packages -mindepth 1 -maxdepth 1 -type d | sed 's!packages/!!g' | tr '\n' ',' | sed s/.$// | jq -Rsc '. / "," - [""]')" >> $GITHUB_OUTPUT
|
||||
|
||||
# Hardcoded to improve parallelism for babel-plugin-react-compiler
|
||||
prettier:
|
||||
name: Run prettier
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
cache-dependency-path: compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn prettier:ci
|
||||
|
||||
# Hardcoded to improve parallelism
|
||||
lint:
|
||||
name: Lint babel-plugin-react-compiler
|
||||
runs-on: ubuntu-latest
|
||||
@@ -40,10 +59,9 @@ jobs:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('compiler/**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn workspace babel-plugin-react-compiler prettier:ci
|
||||
- run: yarn workspace babel-plugin-react-compiler lint
|
||||
|
||||
# Hardcoded to improve parallelism for babel-plugin-react-compiler
|
||||
# Hardcoded to improve parallelism
|
||||
jest:
|
||||
name: Jest babel-plugin-react-compiler
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -82,11 +82,12 @@ module.exports = {
|
||||
],
|
||||
"@typescript-eslint/array-type": ["error", { default: "generic" }],
|
||||
"@typescript-eslint/triple-slash-reference": "off",
|
||||
"@typescript-eslint/no-var-requires": "off"
|
||||
},
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
root: true,
|
||||
ignorePatterns: ["src/__tests__/**/*", "src/**/*.d.ts", "dist/**/*"],
|
||||
ignorePatterns: ["**/__tests__/**/*", "**/*.d.ts", "**/dist/**/*"],
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
@@ -1,2 +0,0 @@
|
||||
741ae6e3aa10735722a442a3c8be77af7c951204
|
||||
59cba458af27d936df8feabf641f545f431529ad
|
||||
@@ -1,12 +1,21 @@
|
||||
.fixtures/
|
||||
bench/
|
||||
**/dist
|
||||
**/__tests__/fixtures/**/*.expect.md
|
||||
**/__tests__/fixtures/**/*.flow.js
|
||||
**/.next
|
||||
test262/
|
||||
*.md
|
||||
|
||||
crates
|
||||
apps/playground/public
|
||||
|
||||
**/LICENSE
|
||||
.*
|
||||
*.md*
|
||||
*.json
|
||||
*.css
|
||||
*.webmanifest
|
||||
packages/js-fuzzer
|
||||
*.map
|
||||
*.sh
|
||||
*.txt
|
||||
*.ico
|
||||
*.svg
|
||||
*.lock
|
||||
*.toml
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
const config = {
|
||||
requirePragma: false,
|
||||
parser: "babel-ts",
|
||||
semi: true,
|
||||
singleQuote: false,
|
||||
trailingComma: "es5"
|
||||
}
|
||||
|
||||
module.exports = config;
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"requirePragma": false,
|
||||
"parser": "babel-ts"
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Sync from <https://github.com/reactjs/reactjs.org/blob/main/beta/colors.js>.
|
||||
*/
|
||||
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
} from "./Output";
|
||||
|
||||
function parseFunctions(
|
||||
source: string,
|
||||
source: string
|
||||
): Array<
|
||||
NodePath<
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
@@ -148,7 +148,7 @@ function isHookName(s: string): boolean {
|
||||
}
|
||||
|
||||
function getReactFunctionType(
|
||||
id: NodePath<t.Identifier | null | undefined>,
|
||||
id: NodePath<t.Identifier | null | undefined>
|
||||
): ReactFunctionType {
|
||||
if (id && id.node && id.isIdentifier()) {
|
||||
if (isHookName(id.node.name)) {
|
||||
@@ -189,7 +189,7 @@ function compile(source: string): CompilerOutput {
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: fn.node.loc ?? null,
|
||||
suggestions: null,
|
||||
}),
|
||||
})
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -205,7 +205,7 @@ function compile(source: string): CompilerOutput {
|
||||
"_c",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
const fnName = fn.node.id?.name ?? null;
|
||||
switch (result.kind) {
|
||||
@@ -274,7 +274,7 @@ function compile(source: string): CompilerOutput {
|
||||
reason: `Unexpected failure when transforming input! ${err}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ export default function Editor() {
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const compilerOutput = useMemo(
|
||||
() => compile(deferredStore.source),
|
||||
[deferredStore.source],
|
||||
[deferredStore.source]
|
||||
);
|
||||
|
||||
useMountEffect(() => {
|
||||
@@ -305,7 +305,7 @@ export default function Editor() {
|
||||
...createMessage(
|
||||
"Bad URL - fell back to the default Playground.",
|
||||
MessageLevel.Info,
|
||||
MessageSource.Playground,
|
||||
MessageSource.Playground
|
||||
),
|
||||
});
|
||||
mountStore = defaultStore;
|
||||
@@ -319,9 +319,7 @@ export default function Editor() {
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex basis top-14">
|
||||
<div
|
||||
className={clsx("relative sm:basis-1/4")}
|
||||
>
|
||||
<div className={clsx("relative sm:basis-1/4")}>
|
||||
<Input
|
||||
errors={
|
||||
compilerOutput.kind === "err" ? compilerOutput.error.details : []
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function Input({ errors }: Props) {
|
||||
allowSyntheticDefaultImports: true,
|
||||
};
|
||||
monaco.languages.typescript.javascriptDefaults.setCompilerOptions(
|
||||
tscOptions,
|
||||
tscOptions
|
||||
);
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
...tscOptions,
|
||||
|
||||
@@ -106,7 +106,7 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
||||
output={text}
|
||||
diff={lastPassOutput ?? null}
|
||||
showInfoPanel={true}
|
||||
></TextTabContent>,
|
||||
></TextTabContent>
|
||||
);
|
||||
lastPassOutput = text;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
||||
output={code}
|
||||
diff={null}
|
||||
showInfoPanel={false}
|
||||
></TextTabContent>,
|
||||
></TextTabContent>
|
||||
);
|
||||
if (sourceMapUrl) {
|
||||
reorderedTabs.set(
|
||||
@@ -133,7 +133,7 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
||||
className="w-full h-monaco_small sm:h-monaco"
|
||||
title="Generated Code"
|
||||
/>
|
||||
</>,
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -145,16 +145,16 @@ async function tabify(source: string, compilerOutput: CompilerOutput) {
|
||||
|
||||
async function codegen(
|
||||
ast: t.Program,
|
||||
source: string,
|
||||
source: string
|
||||
): Promise<{ code: any; sourceMapUrl: string | null }> {
|
||||
const generated = generate(
|
||||
ast,
|
||||
{ sourceMaps: true, sourceFileName: "input.js" },
|
||||
source,
|
||||
source
|
||||
);
|
||||
const sourceMapUrl = getSourceMapUrl(
|
||||
generated.code,
|
||||
JSON.stringify(generated.map),
|
||||
JSON.stringify(generated.map)
|
||||
);
|
||||
const codegenOutput = await prettier.format(generated.code, {
|
||||
semi: true,
|
||||
@@ -172,14 +172,14 @@ function getSourceMapUrl(code: string, map: string): string | null {
|
||||
code = utf16ToUTF8(code);
|
||||
map = utf16ToUTF8(map);
|
||||
return `https://evanw.github.io/source-map-visualization/#${btoa(
|
||||
`${code.length}\0${code}${map.length}\0${map}`,
|
||||
`${code.length}\0${code}${map.length}\0${map}`
|
||||
)}`;
|
||||
}
|
||||
|
||||
function Output({ store, compilerOutput }: Props) {
|
||||
const [tabsOpen, setTabsOpen] = useState<Set<string>>(() => new Set(['JS']));
|
||||
const [tabsOpen, setTabsOpen] = useState<Set<string>>(() => new Set(["JS"]));
|
||||
const [tabs, setTabs] = useState<Map<string, React.ReactNode>>(
|
||||
() => new Map(),
|
||||
() => new Map()
|
||||
);
|
||||
useEffect(() => {
|
||||
tabify(store.source, compilerOutput).then((tabs) => {
|
||||
|
||||
@@ -13,4 +13,4 @@ const Editor = dynamic(() => import("./EditorImpl"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default Editor;
|
||||
export default Editor;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
import type { EditorProps } from "@monaco-editor/react";
|
||||
|
||||
export const monacoOptions: Partial<EditorProps["options"]> = {
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function Header() {
|
||||
<Logo
|
||||
className={clsx(
|
||||
"w-8 h-8 text-link",
|
||||
process.env.NODE_ENV === "development" && "text-yellow-600",
|
||||
process.env.NODE_ENV === "development" && "text-yellow-600"
|
||||
)}
|
||||
/>
|
||||
<p className="hidden select-none sm:block">React Compiler Playground</p>
|
||||
|
||||
@@ -21,5 +21,5 @@ export const IconGitHub = memo<JSX.IntrinsicElements["svg"]>(
|
||||
<path d="M10 0a10 10 0 0 0-3.16 19.49c.5.1.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.46-1.16-1.11-1.47-1.11-1.47-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.9 1.52 2.34 1.08 2.91.83.1-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.1.39-1.99 1.03-2.69a3.6 3.6 0 0 1 .1-2.64s.84-.27 2.75 1.02a9.58 9.58 0 0 1 5 0c1.91-1.3 2.75-1.02 2.75-1.02.55 1.37.2 2.4.1 2.64.64.7 1.03 1.6 1.03 2.69 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.85l-.01 2.75c0 .26.18.58.69.48A10 10 0 0 0 10 0"></path>
|
||||
</svg>
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
// https://github.com/reactjs/reactjs.org/blob/main/beta/src/components/Logo.tsx
|
||||
|
||||
export default function Logo(props: JSX.IntrinsicElements["svg"]) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
import type { Dispatch, ReactNode } from "react";
|
||||
import { useReducer } from "react";
|
||||
import createContext from "../lib/createContext";
|
||||
|
||||
@@ -78,7 +78,7 @@ function TabbedWindowItem({
|
||||
title="Minimize tab"
|
||||
aria-label="Minimize tab"
|
||||
onClick={toggleTabs}
|
||||
className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${hasChanged ? 'font-bold' : 'font-light'} text-secondary hover:text-link`}
|
||||
className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${hasChanged ? "font-bold" : "font-light"} text-secondary hover:text-link`}
|
||||
>
|
||||
- {name}
|
||||
</h2>
|
||||
@@ -91,7 +91,7 @@ function TabbedWindowItem({
|
||||
aria-label={`Expand compiler tab: ${name}`}
|
||||
style={{ transform: "rotate(90deg) translate(-50%)" }}
|
||||
onClick={toggleTabs}
|
||||
className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${hasChanged ? 'font-bold' : 'font-light'} text-secondary hover:text-link`}
|
||||
className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${hasChanged ? "font-bold" : "font-light"} text-secondary hover:text-link`}
|
||||
>
|
||||
{name}
|
||||
</button>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
export { default as Editor } from "./Editor";
|
||||
export { default as Header } from "./Header";
|
||||
export { StoreProvider } from "./StoreContext";
|
||||
|
||||
@@ -5,5 +5,4 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
export { default as useMountEffect } from "./useMountEffect";
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
import type { EffectCallback } from "react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
import React from "react";
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@ import { MarkerSeverity, type editor } from "monaco-editor";
|
||||
|
||||
function mapReactCompilerSeverityToMonaco(
|
||||
level: ErrorSeverity,
|
||||
monaco: Monaco,
|
||||
monaco: Monaco
|
||||
): MarkerSeverity {
|
||||
switch (level) {
|
||||
case ErrorSeverity.Todo:
|
||||
@@ -26,7 +26,7 @@ function mapReactCompilerSeverityToMonaco(
|
||||
|
||||
function mapReactCompilerDiagnosticToMonacoMarker(
|
||||
detail: CompilerErrorDetail,
|
||||
monaco: Monaco,
|
||||
monaco: Monaco
|
||||
): editor.IMarkerData | null {
|
||||
if (detail.loc == null || typeof detail.loc === "symbol") {
|
||||
return null;
|
||||
@@ -70,7 +70,7 @@ export function renderReactCompilerMarkers({
|
||||
marker.startLineNumber,
|
||||
marker.startColumn,
|
||||
marker.endLineNumber,
|
||||
marker.endColumn,
|
||||
marker.endColumn
|
||||
),
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
@@ -83,7 +83,7 @@ export function renderReactCompilerMarkers({
|
||||
monaco.editor.setModelMarkers(model, "owner", []);
|
||||
decorations = model.deltaDecorations(
|
||||
model.getAllDecorations().map((d) => d.id),
|
||||
[],
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,5 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
export * from "./messages";
|
||||
export * from "./store";
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
export enum MessageSource {
|
||||
Babel,
|
||||
Forget,
|
||||
|
||||
@@ -23,7 +23,7 @@ const nextConfig = {
|
||||
new MonacoWebpackPlugin({
|
||||
languages: ["typescript", "javascript"],
|
||||
filename: "static/[name].worker.js",
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const nextConfig = {
|
||||
...config.resolve.alias,
|
||||
"react-compiler-runtime": path.resolve(
|
||||
__dirname,
|
||||
"../../packages/react-compiler-runtime",
|
||||
"../../packages/react-compiler-runtime"
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
"build": "yarn workspaces run build",
|
||||
"dev": "concurrently --kill-others -n compiler,runtime,playground \"yarn workspace babel-plugin-react-compiler run build --watch\" \"yarn workspace react-compiler-runtime run build --watch\" \"wait-on packages/babel-plugin-react-compiler/dist/index.js && yarn workspace playground run dev\"",
|
||||
"test": "yarn workspaces run test",
|
||||
"prettier:write": "prettier --write . --log-level=warn",
|
||||
"prettier:ci": "prettier --check . --log-level=warn",
|
||||
"snap": "yarn workspace babel-plugin-react-compiler run snap",
|
||||
"snap:build": "yarn workspace snap run build",
|
||||
"postinstall": "perl -p -i -e 's/react\\.element/react.transitional.element/' packages/snap/node_modules/fbt/lib/FbtReactUtil.js && perl -p -i -e 's/didWarnAboutUsingAct = false;/didWarnAboutUsingAct = true;/' packages/babel-plugin-react-compiler/node_modules/react-dom/cjs/react-dom-test-utils.development.js",
|
||||
@@ -40,6 +42,7 @@
|
||||
"concurrently": "^7.4.0",
|
||||
"folder-hash": "^4.0.4",
|
||||
"ora": "5.4.1",
|
||||
"prettier": "^3.2.5",
|
||||
"prompt-promise": "^1.0.3",
|
||||
"rollup": "^4.13.2",
|
||||
"rollup-plugin-banner2": "^1.2.3",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
**/dist
|
||||
**/__tests__/fixtures/**/*.expect.md
|
||||
**/__tests__/fixtures/**/*.flow.js
|
||||
*.md
|
||||
*.json
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"requirePragma": false,
|
||||
"parser": "babel-ts"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "babel-plugin-react-compiler",
|
||||
"version": "0.0.0-experimental-592953e-20240517",
|
||||
"version": "0.0.0-experimental-487cb0e-20240529",
|
||||
"description": "Babel plugin for React Compiler.",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
@@ -15,9 +15,6 @@
|
||||
"snap:build": "yarn workspace snap run build",
|
||||
"snap:ci": "yarn snap:build && yarn snap",
|
||||
"ts:analyze-trace": "scripts/ts-analyze-trace.sh",
|
||||
"prettier": "node ./scripts/prettier.js write-changed",
|
||||
"prettier:all": "node ./scripts/prettier.js write",
|
||||
"prettier:ci": "prettier --check .",
|
||||
"lint": "yarn eslint src"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -53,7 +50,6 @@
|
||||
"glob": "^7.1.6",
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
"prettier": "2.8.8",
|
||||
"react": "19.0.0-beta-b498834eab-20240506",
|
||||
"react-dom": "19.0.0-beta-b498834eab-20240506",
|
||||
"rimraf": "^3.0.2",
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Based on similar script in React
|
||||
* https://github.com/facebook/react/blob/main/scripts/prettier/index.js
|
||||
*/
|
||||
|
||||
const chalk = require("chalk");
|
||||
const glob = require("glob");
|
||||
const prettier = require("prettier");
|
||||
const fs = require("fs");
|
||||
const listChangedFiles = require("./shared/list-changed-files");
|
||||
const prettierConfigPath = require.resolve("../.prettierrc");
|
||||
|
||||
const mode = process.argv[2] || "check";
|
||||
const shouldWrite = mode === "write" || mode === "write-changed";
|
||||
const onlyChanged = mode === "check-changed" || mode === "write-changed";
|
||||
|
||||
const changedFiles = onlyChanged ? listChangedFiles() : null;
|
||||
let didWarn = false;
|
||||
let didError = false;
|
||||
|
||||
const files = glob
|
||||
.sync("**/*.{js,ts,tsx,jsx}", {
|
||||
ignore: ["**/node_modules/**", "**/__tests__/fixtures/**/*.flow.js"],
|
||||
})
|
||||
.filter((f) => !onlyChanged || changedFiles.has(f));
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
files.forEach((file) => {
|
||||
const options = prettier.resolveConfig.sync(file, {
|
||||
config: prettierConfigPath,
|
||||
});
|
||||
try {
|
||||
const input = fs.readFileSync(file, "utf8");
|
||||
if (shouldWrite) {
|
||||
const output = prettier.format(input, options);
|
||||
if (output !== input) {
|
||||
fs.writeFileSync(file, output, "utf8");
|
||||
}
|
||||
} else {
|
||||
if (!prettier.check(input, options)) {
|
||||
if (!didWarn) {
|
||||
console.log(
|
||||
"\n" +
|
||||
chalk.red(
|
||||
` This project uses prettier to format all JavaScript code.\n`
|
||||
) +
|
||||
chalk.dim(` Please run `) +
|
||||
chalk.reset("yarn prettier:all") +
|
||||
chalk.dim(
|
||||
` and add changes to files listed below to your commit:`
|
||||
) +
|
||||
`\n\n`
|
||||
);
|
||||
didWarn = true;
|
||||
}
|
||||
console.log(file);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
didError = true;
|
||||
console.log("\n\n" + error.message);
|
||||
console.log(file);
|
||||
}
|
||||
});
|
||||
|
||||
if (didWarn || didError) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const execFileSync = require("child_process").execFileSync;
|
||||
|
||||
const exec = (command, args) => {
|
||||
console.log("> " + [command].concat(args).join(" "));
|
||||
const options = {
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
stdio: "pipe",
|
||||
encoding: "utf-8",
|
||||
};
|
||||
return execFileSync(command, args, options);
|
||||
};
|
||||
|
||||
const execGitCmd = (args) => exec("git", args).trim().toString().split("\n");
|
||||
|
||||
const listChangedFiles = () => {
|
||||
const mergeBase = execGitCmd(["merge-base", "HEAD", "main"]);
|
||||
return new Set([
|
||||
...execGitCmd([
|
||||
"diff",
|
||||
"--name-only",
|
||||
"--relative",
|
||||
"--diff-filter=ACMRTUB",
|
||||
mergeBase,
|
||||
]),
|
||||
...execGitCmd(["ls-files", "--others", "--exclude-standard"]),
|
||||
]);
|
||||
};
|
||||
|
||||
module.exports = listChangedFiles;
|
||||
@@ -36,6 +36,8 @@ export function runBabelPluginReactCompiler(
|
||||
"babel-plugin-fbt-runtime",
|
||||
],
|
||||
sourceType: "module",
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
});
|
||||
invariant(
|
||||
result?.code != null,
|
||||
|
||||
@@ -1520,6 +1520,15 @@ function lowerExpression(
|
||||
place,
|
||||
});
|
||||
} else if (propertyPath.isObjectMethod()) {
|
||||
if (propertyPath.node.kind !== "method") {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.node.kind} functions in ObjectExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: propertyPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const method = lowerObjectMethod(builder, propertyPath);
|
||||
const place = lowerValueToTemporary(builder, method);
|
||||
const loweredKey = lowerObjectPropertyKey(builder, propertyPath);
|
||||
@@ -3336,6 +3345,20 @@ function lowerIdentifierForAssignment(
|
||||
});
|
||||
return null;
|
||||
}
|
||||
} else if (
|
||||
binding.bindingKind === "const" &&
|
||||
kind === InstructionKind.Reassign
|
||||
) {
|
||||
builder.errors.push({
|
||||
reason: `Cannot reassign a \`const\` variable`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: path.node.loc ?? null,
|
||||
description:
|
||||
binding.identifier.name != null
|
||||
? `\`${binding.identifier.name.value}\` is declared as const`
|
||||
: null,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
const place: Place = {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { BindingKind } from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import { CompilerError, CompilerErrorDetailOptions } from "../CompilerError";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
@@ -77,7 +78,7 @@ export type ReactiveInstructionStatement = {
|
||||
};
|
||||
|
||||
export type ReactiveTerminalStatement<
|
||||
Tterminal extends ReactiveTerminal = ReactiveTerminal
|
||||
Tterminal extends ReactiveTerminal = ReactiveTerminal,
|
||||
> = {
|
||||
kind: "terminal";
|
||||
terminal: Tterminal;
|
||||
@@ -1105,7 +1106,7 @@ export type MutableRange = {
|
||||
|
||||
export type VariableBinding =
|
||||
// let, const, etc declared within the current component/hook
|
||||
| { kind: "Identifier"; identifier: Identifier }
|
||||
| { kind: "Identifier"; identifier: Identifier; bindingKind: BindingKind }
|
||||
// bindings declard outside the current component/hook
|
||||
| NonLocalBinding;
|
||||
|
||||
|
||||
@@ -280,7 +280,11 @@ export default class HIRBuilder {
|
||||
if (resolvedBinding.name && resolvedBinding.name.value !== originalName) {
|
||||
babelBinding.scope.rename(originalName, resolvedBinding.name.value);
|
||||
}
|
||||
return { kind: "Identifier", identifier: resolvedBinding };
|
||||
return {
|
||||
kind: "Identifier",
|
||||
identifier: resolvedBinding,
|
||||
bindingKind: babelBinding.kind,
|
||||
};
|
||||
}
|
||||
|
||||
isContextIdentifier(path: NodePath<t.Identifier | t.JSXIdentifier>): boolean {
|
||||
|
||||
@@ -431,6 +431,9 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
||||
["*", { kind: "Object", shapeId: BuiltInMixedReadonlyId }],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, BuiltInJsxId, []);
|
||||
addObject(BUILTIN_SHAPES, BuiltInFunctionId, []);
|
||||
|
||||
export const DefaultMutatingHook = addHook(
|
||||
BUILTIN_SHAPES,
|
||||
{
|
||||
|
||||
@@ -876,7 +876,7 @@ export function mapTerminalSuccessors(
|
||||
|
||||
export function terminalHasFallthrough<
|
||||
T extends Terminal,
|
||||
U extends T & { fallthrough: BlockId }
|
||||
U extends T & { fallthrough: BlockId },
|
||||
>(terminal: T): terminal is U {
|
||||
switch (terminal.kind) {
|
||||
case "maybe-throw":
|
||||
|
||||
+47
-4
@@ -1103,13 +1103,56 @@ function inferBlock(
|
||||
break;
|
||||
}
|
||||
case "JsxExpression": {
|
||||
valueKind = {
|
||||
if (instrValue.tag.kind === "Identifier") {
|
||||
state.referenceAndRecordEffects(
|
||||
instrValue.tag,
|
||||
Effect.Freeze,
|
||||
ValueReason.JsxCaptured,
|
||||
functionEffects
|
||||
);
|
||||
}
|
||||
if (instrValue.children !== null) {
|
||||
for (const child of instrValue.children) {
|
||||
state.referenceAndRecordEffects(
|
||||
child,
|
||||
Effect.Freeze,
|
||||
ValueReason.JsxCaptured,
|
||||
functionEffects
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const attr of instrValue.props) {
|
||||
if (attr.kind === "JsxSpreadAttribute") {
|
||||
state.referenceAndRecordEffects(
|
||||
attr.argument,
|
||||
Effect.Freeze,
|
||||
ValueReason.JsxCaptured,
|
||||
functionEffects
|
||||
);
|
||||
} else {
|
||||
const propEffects: Array<FunctionEffect> = [];
|
||||
state.referenceAndRecordEffects(
|
||||
attr.place,
|
||||
Effect.Freeze,
|
||||
ValueReason.JsxCaptured,
|
||||
propEffects
|
||||
);
|
||||
functionEffects.push(
|
||||
...propEffects.filter(
|
||||
(propEffect) => propEffect.kind !== "GlobalMutation"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
state.initialize(instrValue, {
|
||||
kind: ValueKind.Frozen,
|
||||
reason: new Set([ValueReason.Other]),
|
||||
context: new Set(),
|
||||
};
|
||||
effect = { kind: Effect.Freeze, reason: ValueReason.JsxCaptured };
|
||||
break;
|
||||
});
|
||||
state.define(instr.lvalue, instrValue);
|
||||
instr.lvalue.effect = Effect.ConditionallyMutate;
|
||||
continue;
|
||||
}
|
||||
case "JsxFragment": {
|
||||
valueKind = {
|
||||
|
||||
@@ -311,6 +311,25 @@ function evaluateInstruction(
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case "UnaryExpression": {
|
||||
switch (value.operator) {
|
||||
case "!": {
|
||||
const operand = read(constants, value.value);
|
||||
if (operand !== null && operand.kind === "Primitive") {
|
||||
const result: Primitive = {
|
||||
kind: "Primitive",
|
||||
value: !operand.value,
|
||||
loc: value.loc,
|
||||
};
|
||||
instr.value = result;
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case "BinaryExpression": {
|
||||
const lhsValue = read(constants, value.left);
|
||||
const rhsValue = read(constants, value.right);
|
||||
@@ -327,6 +346,8 @@ function evaluateInstruction(
|
||||
case "+": {
|
||||
if (typeof lhs === "number" && typeof rhs === "number") {
|
||||
result = { kind: "Primitive", value: lhs + rhs, loc: value.loc };
|
||||
} else if (typeof lhs === "string" && typeof rhs === "string") {
|
||||
result = { kind: "Primitive", value: lhs + rhs, loc: value.loc };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+11
-8
@@ -81,18 +81,21 @@ export function codegenFunction(
|
||||
);
|
||||
|
||||
/**
|
||||
* Hot-module reloading reuses component instances at runtime even as the source of the component changes.
|
||||
* Fast Refresh reuses component instances at runtime even as the source of the component changes.
|
||||
* The generated code needs to prevent values from one version of the code being reused after a code cange.
|
||||
* If HMR detection is enabled and we know the source code of the component, assign a cache slot to track
|
||||
* the source hash, and later, emit code to check for source changes and reset the cache on source changes.
|
||||
*/
|
||||
let hotModuleReloadState: { cacheIndex: number; hash: string } | null = null;
|
||||
let fastRefreshState: {
|
||||
cacheIndex: number;
|
||||
hash: string;
|
||||
} | null = null;
|
||||
if (
|
||||
fn.env.config.enableResetCacheOnSourceFileChanges &&
|
||||
fn.env.code !== null
|
||||
) {
|
||||
const hash = createHmac("sha256", fn.env.code).digest("hex");
|
||||
hotModuleReloadState = {
|
||||
fastRefreshState = {
|
||||
cacheIndex: cx.nextCacheIndex,
|
||||
hash,
|
||||
};
|
||||
@@ -131,7 +134,7 @@ export function codegenFunction(
|
||||
),
|
||||
])
|
||||
);
|
||||
if (hotModuleReloadState !== null) {
|
||||
if (fastRefreshState !== null) {
|
||||
// HMR detection is enabled, emit code to reset the memo cache on source changes
|
||||
const index = cx.synthesizeName("$i");
|
||||
preface.push(
|
||||
@@ -140,10 +143,10 @@ export function codegenFunction(
|
||||
"!==",
|
||||
t.memberExpression(
|
||||
t.identifier(cx.synthesizeName("$")),
|
||||
t.numericLiteral(hotModuleReloadState.cacheIndex),
|
||||
t.numericLiteral(fastRefreshState.cacheIndex),
|
||||
true
|
||||
),
|
||||
t.stringLiteral(hotModuleReloadState.hash)
|
||||
t.stringLiteral(fastRefreshState.hash)
|
||||
),
|
||||
t.blockStatement([
|
||||
t.forStatement(
|
||||
@@ -185,10 +188,10 @@ export function codegenFunction(
|
||||
"=",
|
||||
t.memberExpression(
|
||||
t.identifier(cx.synthesizeName("$")),
|
||||
t.numericLiteral(hotModuleReloadState.cacheIndex),
|
||||
t.numericLiteral(fastRefreshState.cacheIndex),
|
||||
true
|
||||
),
|
||||
t.stringLiteral(hotModuleReloadState.hash)
|
||||
t.stringLiteral(fastRefreshState.hash)
|
||||
)
|
||||
),
|
||||
])
|
||||
|
||||
@@ -257,7 +257,7 @@ export type Transformed<T> =
|
||||
| { kind: "replace-many"; value: Array<T> };
|
||||
|
||||
export class ReactiveFunctionTransform<
|
||||
TState = void
|
||||
TState = void,
|
||||
> extends ReactiveFunctionVisitor<TState> {
|
||||
override traverseBlock(block: ReactiveBlock, state: TState): void {
|
||||
let nextBlock: ReactiveBlock | null = null;
|
||||
|
||||
+49
-10
@@ -10,6 +10,7 @@ import {
|
||||
HIRFunction,
|
||||
IdentifierId,
|
||||
Place,
|
||||
SourceLocation,
|
||||
isRefValueType,
|
||||
isUseRefType,
|
||||
} from "../HIR";
|
||||
@@ -117,7 +118,12 @@ function validateNoRefAccessInRenderImpl(
|
||||
case "MethodCall": {
|
||||
if (!isEffectHook(instr.value.property.identifier)) {
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
validateNoRefAccess(errors, refAccessingFunctions, operand);
|
||||
validateNoRefAccess(
|
||||
errors,
|
||||
refAccessingFunctions,
|
||||
operand,
|
||||
operand.loc
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -138,7 +144,12 @@ function validateNoRefAccessInRenderImpl(
|
||||
});
|
||||
}
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
validateNoRefAccess(errors, refAccessingFunctions, operand);
|
||||
validateNoRefAccess(
|
||||
errors,
|
||||
refAccessingFunctions,
|
||||
operand,
|
||||
operand.loc
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -146,7 +157,30 @@ function validateNoRefAccessInRenderImpl(
|
||||
case "ObjectExpression":
|
||||
case "ArrayExpression": {
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
validateNoRefAccess(errors, refAccessingFunctions, operand);
|
||||
validateNoRefAccess(
|
||||
errors,
|
||||
refAccessingFunctions,
|
||||
operand,
|
||||
operand.loc
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "PropertyDelete":
|
||||
case "PropertyStore":
|
||||
case "ComputedDelete":
|
||||
case "ComputedStore": {
|
||||
validateNoRefAccess(
|
||||
errors,
|
||||
refAccessingFunctions,
|
||||
instr.value.object,
|
||||
instr.loc
|
||||
);
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
if (operand === instr.value.object) {
|
||||
continue;
|
||||
}
|
||||
validateNoRefValueAccess(errors, refAccessingFunctions, operand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -172,12 +206,12 @@ function validateNoRefAccessInRenderImpl(
|
||||
|
||||
function validateNoRefValueAccess(
|
||||
errors: CompilerError,
|
||||
unconditionalSetStateFunctions: Set<IdentifierId>,
|
||||
refAccessingFunctions: Set<IdentifierId>,
|
||||
operand: Place
|
||||
): void {
|
||||
if (
|
||||
isRefValueType(operand.identifier) ||
|
||||
unconditionalSetStateFunctions.has(operand.identifier.id)
|
||||
refAccessingFunctions.has(operand.identifier.id)
|
||||
) {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
@@ -192,20 +226,25 @@ function validateNoRefValueAccess(
|
||||
|
||||
function validateNoRefAccess(
|
||||
errors: CompilerError,
|
||||
unconditionalSetStateFunctions: Set<IdentifierId>,
|
||||
operand: Place
|
||||
refAccessingFunctions: Set<IdentifierId>,
|
||||
operand: Place,
|
||||
loc: SourceLocation
|
||||
): void {
|
||||
if (
|
||||
isRefValueType(operand.identifier) ||
|
||||
isUseRefType(operand.identifier) ||
|
||||
unconditionalSetStateFunctions.has(operand.identifier.id)
|
||||
refAccessingFunctions.has(operand.identifier.id)
|
||||
) {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
"Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)",
|
||||
loc: operand.loc,
|
||||
description: `Cannot access ref value at ${printPlace(operand)}`,
|
||||
loc: loc,
|
||||
description:
|
||||
operand.identifier.name !== null &&
|
||||
operand.identifier.name.kind === "named"
|
||||
? `Cannot access ref value \`${operand.identifier.name.value}\``
|
||||
: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
+1
-2
@@ -24,8 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* This is a weird case as data has type `BuiltInMixedReadonly`.
|
||||
* The only scoped value we currently infer in this program is the
|
||||
* PropertyLoad `data?.toString`.
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { useMemo } from "react";
|
||||
|
||||
const someGlobal = { value: 0 };
|
||||
|
||||
function Component({ value }) {
|
||||
const onClick = () => {
|
||||
someGlobal.value = value;
|
||||
};
|
||||
return useMemo(() => {
|
||||
return <div onClick={onClick}>{someGlobal.value}</div>;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 0 }],
|
||||
sequentialRenders: [
|
||||
{ value: 1 },
|
||||
{ value: 1 },
|
||||
{ value: 42 },
|
||||
{ value: 42 },
|
||||
{ value: 0 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const someGlobal = { value: 0 };
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(4);
|
||||
const { value } = t0;
|
||||
let t1;
|
||||
if ($[0] !== value) {
|
||||
t1 = () => {
|
||||
someGlobal.value = value;
|
||||
};
|
||||
$[0] = value;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const onClick = t1;
|
||||
let t2;
|
||||
let t3;
|
||||
if ($[2] !== onClick) {
|
||||
t3 = <div onClick={onClick}>{someGlobal.value}</div>;
|
||||
$[2] = onClick;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
}
|
||||
t2 = t3;
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 0 }],
|
||||
sequentialRenders: [
|
||||
{ value: 1 },
|
||||
{ value: 1 },
|
||||
{ value: 42 },
|
||||
{ value: 42 },
|
||||
{ value: 0 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>0</div>
|
||||
<div>0</div>
|
||||
<div>0</div>
|
||||
<div>0</div>
|
||||
<div>0</div>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
const someGlobal = { value: 0 };
|
||||
|
||||
function Component({ value }) {
|
||||
const onClick = () => {
|
||||
someGlobal.value = value;
|
||||
};
|
||||
return useMemo(() => {
|
||||
return <div onClick={onClick}>{someGlobal.value}</div>;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 0 }],
|
||||
sequentialRenders: [
|
||||
{ value: 1 },
|
||||
{ value: 1 },
|
||||
{ value: 42 },
|
||||
{ value: 42 },
|
||||
{ value: 0 },
|
||||
],
|
||||
};
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const onClick = () => {
|
||||
// Cannot assign to globals
|
||||
someUnknownGlobal = true;
|
||||
moduleLocal = true;
|
||||
};
|
||||
// It's possible that this could be an event handler / effect function,
|
||||
// but we don't know that and optimistically assume it will only be
|
||||
// called by an event handler or effect, where it is allowed to modify globals
|
||||
return <div onClick={onClick} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const onClick = () => {
|
||||
someUnknownGlobal = true;
|
||||
moduleLocal = true;
|
||||
};
|
||||
|
||||
t0 = <div onClick={onClick} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div></div>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
function Component() {
|
||||
const onClick = () => {
|
||||
// Cannot assign to globals
|
||||
someUnknownGlobal = true;
|
||||
moduleLocal = true;
|
||||
};
|
||||
// It's possible that this could be an event handler / effect function,
|
||||
// but we don't know that and optimistically assume it will only be
|
||||
// called by an event handler or effect, where it is allowed to modify globals
|
||||
return <div onClick={onClick} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* We currently hoist the accessed properties of function expressions,
|
||||
* regardless of control flow. This is simply because we wrote support for
|
||||
* function expressions before doing a lot of work in PropagateScopeDeps
|
||||
* to handle conditionally accessed dependencies.
|
||||
*
|
||||
* Current evaluator error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <div>{"shouldInvokeFns":true,"callback":{"kind":"Function","result":null}}</div>
|
||||
* Forget:
|
||||
* (kind: exception) Cannot read properties of null (reading 'prop')
|
||||
*/
|
||||
function Component({ obj, isObjNull }) {
|
||||
const callback = () => {
|
||||
if (!isObjNull) {
|
||||
return obj.prop;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return <Stringify shouldInvokeFns={true} callback={callback} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ obj: null, isObjNull: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* We currently hoist the accessed properties of function expressions,
|
||||
* regardless of control flow. This is simply because we wrote support for
|
||||
* function expressions before doing a lot of work in PropagateScopeDeps
|
||||
* to handle conditionally accessed dependencies.
|
||||
*
|
||||
* Current evaluator error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <div>{"shouldInvokeFns":true,"callback":{"kind":"Function","result":null}}</div>
|
||||
* Forget:
|
||||
* (kind: exception) Cannot read properties of null (reading 'prop')
|
||||
*/
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { obj, isObjNull } = t0;
|
||||
let t1;
|
||||
if ($[0] !== isObjNull || $[1] !== obj.prop) {
|
||||
t1 = () => {
|
||||
if (!isObjNull) {
|
||||
return obj.prop;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
$[0] = isObjNull;
|
||||
$[1] = obj.prop;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const callback = t1;
|
||||
let t2;
|
||||
if ($[3] !== callback) {
|
||||
t2 = <Stringify shouldInvokeFns={true} callback={callback} />;
|
||||
$[3] = callback;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ obj: null, isObjNull: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* We currently hoist the accessed properties of function expressions,
|
||||
* regardless of control flow. This is simply because we wrote support for
|
||||
* function expressions before doing a lot of work in PropagateScopeDeps
|
||||
* to handle conditionally accessed dependencies.
|
||||
*
|
||||
* Current evaluator error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <div>{"shouldInvokeFns":true,"callback":{"kind":"Function","result":null}}</div>
|
||||
* Forget:
|
||||
* (kind: exception) Cannot read properties of null (reading 'prop')
|
||||
*/
|
||||
function Component({ obj, isObjNull }) {
|
||||
const callback = () => {
|
||||
if (!isObjNull) {
|
||||
return obj.prop;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return <Stringify shouldInvokeFns={true} callback={callback} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ obj: null, isObjNull: true }],
|
||||
};
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import invariant from "invariant";
|
||||
import {
|
||||
makeObject_Primitives,
|
||||
mutate,
|
||||
sum,
|
||||
useIdentity,
|
||||
} from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Exposes fundamental issue with pruning 'non-reactive' dependencies + flattening
|
||||
* those scopes. Here, `z`'s original memo block is removed due to the inner hook call.
|
||||
* However, we also infer that `z` is non-reactive and does not need to be a memo
|
||||
* dependency.
|
||||
*
|
||||
* Current evaluator error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* Forget:
|
||||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [[ (exception in render) Invariant Violation: oh no! ]]
|
||||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
*/
|
||||
|
||||
function MyApp({ count }) {
|
||||
const z = makeObject_Primitives();
|
||||
const x = useIdentity(2);
|
||||
const y = sum(x, count);
|
||||
mutate(z);
|
||||
const thing = [y, z];
|
||||
if (thing[1] !== z) {
|
||||
invariant(false, "oh no!");
|
||||
}
|
||||
return thing;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: MyApp,
|
||||
params: [{ count: 2 }],
|
||||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import invariant from "invariant";
|
||||
import {
|
||||
makeObject_Primitives,
|
||||
mutate,
|
||||
sum,
|
||||
useIdentity,
|
||||
} from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Exposes fundamental issue with pruning 'non-reactive' dependencies + flattening
|
||||
* those scopes. Here, `z`'s original memo block is removed due to the inner hook call.
|
||||
* However, we also infer that `z` is non-reactive and does not need to be a memo
|
||||
* dependency.
|
||||
*
|
||||
* Current evaluator error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* Forget:
|
||||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [[ (exception in render) Invariant Violation: oh no! ]]
|
||||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
*/
|
||||
|
||||
function MyApp(t0) {
|
||||
const $ = _c(5);
|
||||
const { count } = t0;
|
||||
const z = makeObject_Primitives();
|
||||
const x = useIdentity(2);
|
||||
let t1;
|
||||
if ($[0] !== x || $[1] !== count) {
|
||||
t1 = sum(x, count);
|
||||
$[0] = x;
|
||||
$[1] = count;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const y = t1;
|
||||
mutate(z);
|
||||
let t2;
|
||||
if ($[3] !== y) {
|
||||
t2 = [y, z];
|
||||
$[3] = y;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const thing = t2;
|
||||
if (thing[1] !== z) {
|
||||
invariant(false, "oh no!");
|
||||
}
|
||||
return thing;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: MyApp,
|
||||
params: [{ count: 2 }],
|
||||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import invariant from "invariant";
|
||||
import {
|
||||
makeObject_Primitives,
|
||||
mutate,
|
||||
sum,
|
||||
useIdentity,
|
||||
} from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Exposes fundamental issue with pruning 'non-reactive' dependencies + flattening
|
||||
* those scopes. Here, `z`'s original memo block is removed due to the inner hook call.
|
||||
* However, we also infer that `z` is non-reactive and does not need to be a memo
|
||||
* dependency.
|
||||
*
|
||||
* Current evaluator error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* Forget:
|
||||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
* [[ (exception in render) Invariant Violation: oh no! ]]
|
||||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}]
|
||||
*/
|
||||
|
||||
function MyApp({ count }) {
|
||||
const z = makeObject_Primitives();
|
||||
const x = useIdentity(2);
|
||||
const y = sum(x, count);
|
||||
mutate(z);
|
||||
const thing = [y, z];
|
||||
if (thing[1] !== z) {
|
||||
invariant(false, "oh no!");
|
||||
}
|
||||
return thing;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: MyApp,
|
||||
params: [{ count: 2 }],
|
||||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }],
|
||||
};
|
||||
+1
-1
@@ -76,7 +76,7 @@ function getNativeLogFunction(level) {
|
||||
INSPECTOR_LEVELS[logLevel],
|
||||
str,
|
||||
[].slice.call(arguments),
|
||||
INSPECTOR_FRAMES_TO_SKIP
|
||||
INSPECTOR_FRAMES_TO_SKIP,
|
||||
);
|
||||
}
|
||||
if (groupStack.length) {
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ function Component() {
|
||||
x = { f: () => console.log("original") };
|
||||
|
||||
(console.log("A"), x)[(console.log("B"), "f")](
|
||||
(changeF(x), console.log("arg"), 1)
|
||||
(changeF(x), console.log("arg"), 1),
|
||||
);
|
||||
$[1] = x;
|
||||
} else {
|
||||
|
||||
+1
-2
@@ -29,8 +29,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* props.b *does* influence `a`
|
||||
*/
|
||||
function Component(props) {
|
||||
|
||||
+1
-2
@@ -66,8 +66,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* props.b does *not* influence `a`
|
||||
*/
|
||||
function ComponentA(props) {
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function foo() {
|
||||
const a = "a" + "b";
|
||||
const c = "c";
|
||||
return a + c;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
isComponent: false,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function foo() {
|
||||
return "abc";
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
isComponent: false,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "abc"
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
function foo() {
|
||||
const a = "a" + "b";
|
||||
const c = "c";
|
||||
return a + c;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
isComponent: false,
|
||||
};
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function foo() {
|
||||
let _b;
|
||||
const b = true;
|
||||
if (!b) {
|
||||
_b = "bar";
|
||||
} else {
|
||||
_b = "baz";
|
||||
}
|
||||
|
||||
return (
|
||||
<Stringify
|
||||
value={{
|
||||
_b,
|
||||
b0: !true,
|
||||
n0: !0,
|
||||
n1: !1,
|
||||
n2: !2,
|
||||
n3: !-1,
|
||||
s0: !"",
|
||||
s1: !"a",
|
||||
s2: !"ab",
|
||||
u: !undefined,
|
||||
n: !null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
isComponent: false,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function foo() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = (
|
||||
<Stringify
|
||||
value={{
|
||||
_b: "baz",
|
||||
b0: false,
|
||||
n0: true,
|
||||
n1: false,
|
||||
n2: false,
|
||||
n3: !-1,
|
||||
s0: true,
|
||||
s1: false,
|
||||
s2: false,
|
||||
u: !undefined,
|
||||
n: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
isComponent: false,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":{"_b":"baz","b0":false,"n0":true,"n1":false,"n2":false,"n3":false,"s0":true,"s1":false,"s2":false,"u":true,"n":true}}</div>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function foo() {
|
||||
let _b;
|
||||
const b = true;
|
||||
if (!b) {
|
||||
_b = "bar";
|
||||
} else {
|
||||
_b = "baz";
|
||||
}
|
||||
|
||||
return (
|
||||
<Stringify
|
||||
value={{
|
||||
_b,
|
||||
b0: !true,
|
||||
n0: !0,
|
||||
n1: !1,
|
||||
n2: !2,
|
||||
n3: !-1,
|
||||
s0: !"",
|
||||
s1: !"a",
|
||||
s2: !"ab",
|
||||
u: !undefined,
|
||||
n: !null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
isComponent: false,
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const Foo = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
return <Foo />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function Component() {
|
||||
2 | const Foo = () => {
|
||||
> 3 | someGlobal = true;
|
||||
| ^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3)
|
||||
4 | };
|
||||
5 | return <Foo />;
|
||||
6 | }
|
||||
```
|
||||
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
function Component() {
|
||||
const Foo = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
return <Foo />;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const foo = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
// Children are generally access/called during render, so
|
||||
// modifying a global in a children function is almost
|
||||
// certainly a mistake.
|
||||
return <Foo>{foo}</Foo>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function Component() {
|
||||
2 | const foo = () => {
|
||||
> 3 | someGlobal = true;
|
||||
| ^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3)
|
||||
4 | };
|
||||
5 | // Children are generally access/called during render, so
|
||||
6 | // modifying a global in a children function is almost
|
||||
```
|
||||
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
function Component() {
|
||||
const foo = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
// Children are generally access/called during render, so
|
||||
// modifying a global in a children function is almost
|
||||
// certainly a mistake.
|
||||
return <Foo>{foo}</Foo>;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const foo = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
return <div {...foo} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function Component() {
|
||||
2 | const foo = () => {
|
||||
> 3 | someGlobal = true;
|
||||
| ^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3)
|
||||
4 | };
|
||||
5 | return <div {...foo} />;
|
||||
6 | }
|
||||
```
|
||||
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
function Component() {
|
||||
const foo = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
return <div {...foo} />;
|
||||
}
|
||||
+1
-1
@@ -22,7 +22,7 @@ function Component(props) {
|
||||
7 | return <Foo item={item} current={current} />;
|
||||
8 | };
|
||||
> 9 | return <Items>{props.items.map((item) => renderItem(item))}</Items>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at mutate? $64[13:15]:TObject<BuiltInFunction> (9:9)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (9:9)
|
||||
10 | }
|
||||
11 |
|
||||
```
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ function Component(props) {
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef(null);
|
||||
> 4 | const x = foo(ref);
|
||||
| ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at mutate? $21[6:8]:TObject<BuiltInUseRefId> (4:4)
|
||||
| ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4)
|
||||
5 | return x.current;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const x = 0;
|
||||
x = 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function Component() {
|
||||
2 | const x = 0;
|
||||
> 3 | x = 1;
|
||||
| ^ InvalidJS: Cannot reassign a `const` variable. `x` is declared as const (3:3)
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
function Component() {
|
||||
const x = 0;
|
||||
x = 1;
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ function Component(props) {
|
||||
6 | return <Foo item={item} current={current} />;
|
||||
7 | };
|
||||
> 8 | return <Items>{props.items.map((item) => renderItem(item))}</Items>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at mutate? $60[14:16]:TObject<BuiltInFunction> (8:8)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (8:8)
|
||||
9 | }
|
||||
10 |
|
||||
```
|
||||
|
||||
+6
-3
@@ -15,10 +15,13 @@ function Component(props) {
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef(null);
|
||||
4 | ref.current = props.value;
|
||||
> 5 | return ref.current;
|
||||
| ^^^^^^^^^^^ 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)
|
||||
> 4 | ref.current = props.value;
|
||||
| ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4)
|
||||
|
||||
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)
|
||||
5 | return ref.current;
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateRefAccessDuringRender
|
||||
function Component(props) {
|
||||
const ref = useRef({ inner: null });
|
||||
ref.current.inner = props.value;
|
||||
return ref.current.inner;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef({ inner: null });
|
||||
> 4 | ref.current.inner = props.value;
|
||||
| ^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4)
|
||||
|
||||
InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $30:TObject<BuiltInRefValue> (5:5)
|
||||
5 | return ref.current.inner;
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// @validateRefAccessDuringRender
|
||||
function Component(props) {
|
||||
const ref = useRef({ inner: null });
|
||||
ref.current.inner = props.value;
|
||||
return ref.current.inner;
|
||||
}
|
||||
+1
-1
@@ -25,7 +25,7 @@ function Foo({ a }) {
|
||||
3 | const ref = useRef();
|
||||
4 | // type information is lost here as we don't track types of fields
|
||||
> 5 | const val = { ref };
|
||||
| ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at capture $29:TObject<BuiltInUseRefId> (5:5)
|
||||
| ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (5:5)
|
||||
6 | // without type info, we don't know that val.ref.current is a ref value so we
|
||||
7 | // *would* end up depending on val.ref.current
|
||||
8 | // however, this is an instance of accessing a ref during render and is disallowed
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateRefAccessDuringRender
|
||||
function useHook({ value }) {
|
||||
const ref = useRef(null);
|
||||
// Writing to a ref in render is against the rules:
|
||||
ref.current = value;
|
||||
// returning a ref is allowed, so this alone doesn't trigger an error:
|
||||
return ref;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
3 | const ref = useRef(null);
|
||||
4 | // Writing to a ref in render is against the rules:
|
||||
> 5 | ref.current = value;
|
||||
| ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (5:5)
|
||||
6 | // returning a ref is allowed, so this alone doesn't trigger an error:
|
||||
7 | return ref;
|
||||
8 | }
|
||||
```
|
||||
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// @validateRefAccessDuringRender
|
||||
function useHook({ value }) {
|
||||
const ref = useRef(null);
|
||||
// Writing to a ref in render is against the rules:
|
||||
ref.current = value;
|
||||
// returning a ref is allowed, so this alone doesn't trigger an error:
|
||||
return ref;
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const foo = () => {
|
||||
// Cannot assign to globals
|
||||
someUnknownGlobal = true;
|
||||
moduleLocal = true;
|
||||
};
|
||||
// It's possible that this could be an event handler / effect function,
|
||||
// but we don't know that and conservatively assume it's a render helper
|
||||
// where it's disallowed to modify globals
|
||||
return <Foo foo={foo} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | const foo = () => {
|
||||
3 | // Cannot assign to globals
|
||||
> 4 | someUnknownGlobal = true;
|
||||
| ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (4:4)
|
||||
5 | moduleLocal = true;
|
||||
6 | };
|
||||
7 | // It's possible that this could be an event handler / effect function,
|
||||
```
|
||||
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
function Component() {
|
||||
const foo = () => {
|
||||
// Cannot assign to globals
|
||||
someUnknownGlobal = true;
|
||||
moduleLocal = true;
|
||||
};
|
||||
// It's possible that this could be an event handler / effect function,
|
||||
// but we don't know that and conservatively assume it's a render helper
|
||||
// where it's disallowed to modify globals
|
||||
return <Foo foo={foo} />;
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component({ value }) {
|
||||
const object = {
|
||||
get value() {
|
||||
return value;
|
||||
},
|
||||
};
|
||||
return <div>{object.value}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [{ value: 0 }],
|
||||
sequentialRenders: [{ value: 1 }, { value: 2 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function Component({ value }) {
|
||||
2 | const object = {
|
||||
> 3 | get value() {
|
||||
| ^^^^^^^^^^^^^
|
||||
> 4 | return value;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
> 5 | },
|
||||
| ^^^^^^ Todo: (BuildHIR::lowerExpression) Handle get functions in ObjectExpression (3:5)
|
||||
6 | };
|
||||
7 | return <div>{object.value}</div>;
|
||||
8 | }
|
||||
```
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
function Component({ value }) {
|
||||
const object = {
|
||||
get value() {
|
||||
return value;
|
||||
},
|
||||
};
|
||||
return <div>{object.value}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [{ value: 0 }],
|
||||
sequentialRenders: [{ value: 1 }, { value: 2 }],
|
||||
};
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let value;
|
||||
const object = {
|
||||
set value(v) {
|
||||
value = v;
|
||||
},
|
||||
};
|
||||
object.value = props.value;
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [{ value: 0 }],
|
||||
sequentialRenders: [{ value: 1 }, { value: 2 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | let value;
|
||||
3 | const object = {
|
||||
> 4 | set value(v) {
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 5 | value = v;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
> 6 | },
|
||||
| ^^^^^^ Todo: (BuildHIR::lowerExpression) Handle set functions in ObjectExpression (4:6)
|
||||
7 | };
|
||||
8 | object.value = props.value;
|
||||
9 | return <div>{value}</div>;
|
||||
```
|
||||
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
function Component(props) {
|
||||
let value;
|
||||
const object = {
|
||||
set value(v) {
|
||||
value = v;
|
||||
},
|
||||
};
|
||||
object.value = props.value;
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [{ value: 0 }],
|
||||
sequentialRenders: [{ value: 1 }, { value: 2 }],
|
||||
};
|
||||
+1
-1
@@ -23,7 +23,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
1 | // @validateRefAccessDuringRender:true
|
||||
2 | function Foo(props, ref) {
|
||||
> 3 | console.log(ref.current);
|
||||
| ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $16:TObject<BuiltInRefValue> (3:3)
|
||||
| ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (3:3)
|
||||
4 | return <div>{props.bar}</div>;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @compilationMode(infer)
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
|
||||
let pretendConst = 0;
|
||||
|
||||
function unsafeResetConst() {
|
||||
pretendConst = 0;
|
||||
}
|
||||
|
||||
function unsafeUpdateConst() {
|
||||
pretendConst += 1;
|
||||
}
|
||||
|
||||
function Component() {
|
||||
useState(() => {
|
||||
// unsafe: reset the constant when first rendering the instance
|
||||
unsafeResetConst();
|
||||
});
|
||||
// UNSAFE! changing a module variable that is read by a component is normally
|
||||
// unsafe, but in this case we're simulating a fast refresh between each render
|
||||
unsafeUpdateConst();
|
||||
|
||||
// In production mode (no @enableResetCacheOnSourceFileChanges) memo caches are not
|
||||
// reset unless the deps change
|
||||
const value = useMemo(() => [{ pretendConst }], []);
|
||||
|
||||
return <ValidateMemoization inputs={[]} output={value} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @compilationMode(infer)
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
|
||||
let pretendConst = 0;
|
||||
|
||||
function unsafeResetConst() {
|
||||
pretendConst = 0;
|
||||
}
|
||||
|
||||
function unsafeUpdateConst() {
|
||||
pretendConst += 1;
|
||||
}
|
||||
|
||||
function Component() {
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
unsafeResetConst();
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
useState(t0);
|
||||
|
||||
unsafeUpdateConst();
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = [{ pretendConst }];
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
t1 = t2;
|
||||
const value = t1;
|
||||
let t3;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <ValidateMemoization inputs={[]} output={value} />;
|
||||
$[2] = t3;
|
||||
} else {
|
||||
t3 = $[2];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"inputs":[],"output":[{"pretendConst":1}]}</div>
|
||||
<div>{"inputs":[],"output":[{"pretendConst":1}]}</div>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// @compilationMode(infer)
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
|
||||
let pretendConst = 0;
|
||||
|
||||
function unsafeResetConst() {
|
||||
pretendConst = 0;
|
||||
}
|
||||
|
||||
function unsafeUpdateConst() {
|
||||
pretendConst += 1;
|
||||
}
|
||||
|
||||
function Component() {
|
||||
useState(() => {
|
||||
// unsafe: reset the constant when first rendering the instance
|
||||
unsafeResetConst();
|
||||
});
|
||||
// UNSAFE! changing a module variable that is read by a component is normally
|
||||
// unsafe, but in this case we're simulating a fast refresh between each render
|
||||
unsafeUpdateConst();
|
||||
|
||||
// In production mode (no @enableResetCacheOnSourceFileChanges) memo caches are not
|
||||
// reset unless the deps change
|
||||
const value = useMemo(() => [{ pretendConst }], []);
|
||||
|
||||
return <ValidateMemoization inputs={[]} output={value} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @compilationMode(infer) @enableResetCacheOnSourceFileChanges
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
|
||||
let pretendConst = 0;
|
||||
|
||||
function unsafeResetConst() {
|
||||
pretendConst = 0;
|
||||
}
|
||||
|
||||
function unsafeUpdateConst() {
|
||||
pretendConst += 1;
|
||||
}
|
||||
|
||||
function Component() {
|
||||
useState(() => {
|
||||
// unsafe: reset the constant when first rendering the instance
|
||||
unsafeResetConst();
|
||||
});
|
||||
// UNSAFE! changing a module variable that is read by a component is normally
|
||||
// unsafe, but in this case we're simulating a fast refresh between each render
|
||||
unsafeUpdateConst();
|
||||
|
||||
// TODO: In fast refresh mode (@enableResetCacheOnSourceFileChanges) Forget should
|
||||
// reset on changes to globals that impact the component/hook, effectively memoizing
|
||||
// as if value was reactive. However, we don't want to actually treat globals as
|
||||
// reactive (though that would be trivial) since it could change compilation too much
|
||||
// btw dev and prod. Instead, we should reset the cache via a secondary mechanism.
|
||||
const value = useMemo(() => [{ pretendConst }], [pretendConst]);
|
||||
|
||||
return <ValidateMemoization inputs={[pretendConst]} output={value} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @compilationMode(infer) @enableResetCacheOnSourceFileChanges
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
|
||||
let pretendConst = 0;
|
||||
|
||||
function unsafeResetConst() {
|
||||
pretendConst = 0;
|
||||
}
|
||||
|
||||
function unsafeUpdateConst() {
|
||||
pretendConst += 1;
|
||||
}
|
||||
|
||||
function Component() {
|
||||
const $ = _c(4);
|
||||
if (
|
||||
$[0] !== "4bf230b116dd95f382060ad17350e116395e41ed757e51fd074ea0b4ed281272"
|
||||
) {
|
||||
for (let $i = 0; $i < 4; $i += 1) {
|
||||
$[$i] = Symbol.for("react.memo_cache_sentinel");
|
||||
}
|
||||
$[0] = "4bf230b116dd95f382060ad17350e116395e41ed757e51fd074ea0b4ed281272";
|
||||
}
|
||||
let t0;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
unsafeResetConst();
|
||||
};
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
useState(t0);
|
||||
|
||||
unsafeUpdateConst();
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = [{ pretendConst }];
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
t1 = t2;
|
||||
const value = t1;
|
||||
let t3;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <ValidateMemoization inputs={[pretendConst]} output={value} />;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// @compilationMode(infer) @enableResetCacheOnSourceFileChanges
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
|
||||
let pretendConst = 0;
|
||||
|
||||
function unsafeResetConst() {
|
||||
pretendConst = 0;
|
||||
}
|
||||
|
||||
function unsafeUpdateConst() {
|
||||
pretendConst += 1;
|
||||
}
|
||||
|
||||
function Component() {
|
||||
useState(() => {
|
||||
// unsafe: reset the constant when first rendering the instance
|
||||
unsafeResetConst();
|
||||
});
|
||||
// UNSAFE! changing a module variable that is read by a component is normally
|
||||
// unsafe, but in this case we're simulating a fast refresh between each render
|
||||
unsafeUpdateConst();
|
||||
|
||||
// TODO: In fast refresh mode (@enableResetCacheOnSourceFileChanges) Forget should
|
||||
// reset on changes to globals that impact the component/hook, effectively memoizing
|
||||
// as if value was reactive. However, we don't want to actually treat globals as
|
||||
// reactive (though that would be trivial) since it could change compilation too much
|
||||
// btw dev and prod. Instead, we should reset the cache via a secondary mechanism.
|
||||
const value = useMemo(() => [{ pretendConst }], [pretendConst]);
|
||||
|
||||
return <ValidateMemoization inputs={[pretendConst]} output={value} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
+1
-1
@@ -40,7 +40,7 @@ function Component(props) {
|
||||
title={fbs._(
|
||||
"Hello {user name}",
|
||||
[fbs._param("user name", props.name)],
|
||||
{ hk: "2zEDKF" }
|
||||
{ hk: "2zEDKF" },
|
||||
)}
|
||||
>
|
||||
Hover me
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ function Component(props) {
|
||||
t0 = fbt._(
|
||||
"Hello, {(key) name}!",
|
||||
[fbt._param("(key) name", identity(props.name))],
|
||||
{ hk: "2sOsn5" }
|
||||
{ hk: "2sOsn5" },
|
||||
);
|
||||
$[0] = props.name;
|
||||
$[1] = t0;
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ function Component(props) {
|
||||
t0 = fbt._(
|
||||
"{(key) count} items",
|
||||
[fbt._param("(key) count", props.count)],
|
||||
{ hk: "3yW91j" }
|
||||
{ hk: "3yW91j" },
|
||||
);
|
||||
$[0] = props.count;
|
||||
$[1] = t0;
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ function Component(t0) {
|
||||
t1 = fbt._(
|
||||
"Before text{paramName}After text",
|
||||
[fbt._param("paramName", value)],
|
||||
{ hk: "aKEGX" }
|
||||
{ hk: "aKEGX" },
|
||||
);
|
||||
$[0] = value;
|
||||
$[1] = t1;
|
||||
|
||||
+2
-2
@@ -56,10 +56,10 @@ function Component(props) {
|
||||
fbt._param(
|
||||
"option",
|
||||
|
||||
props.option
|
||||
props.option,
|
||||
),
|
||||
],
|
||||
{ hk: "3Bg20a" }
|
||||
{ hk: "3Bg20a" },
|
||||
)}
|
||||
!
|
||||
</span>
|
||||
|
||||
+2
-2
@@ -56,10 +56,10 @@ function Component(props) {
|
||||
fbt._param(
|
||||
"option",
|
||||
|
||||
props.option
|
||||
props.option,
|
||||
),
|
||||
],
|
||||
{ hk: "3Bg20a" }
|
||||
{ hk: "3Bg20a" },
|
||||
)}
|
||||
!
|
||||
</span>
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ function Component(props) {
|
||||
t0 = fbt._(
|
||||
"Hello {user name}",
|
||||
[fbt._param("user name", capitalize(props.name))],
|
||||
{ hk: "2zEDKF" }
|
||||
{ hk: "2zEDKF" },
|
||||
);
|
||||
$[0] = props.name;
|
||||
$[1] = t0;
|
||||
|
||||
+2
-2
@@ -36,10 +36,10 @@ function Foo(props) {
|
||||
fbt._param(
|
||||
"value",
|
||||
|
||||
props.value
|
||||
props.value,
|
||||
),
|
||||
],
|
||||
{ hk: "Ri5kJ" }
|
||||
{ hk: "Ri5kJ" },
|
||||
);
|
||||
$[0] = props.value;
|
||||
$[1] = t0;
|
||||
|
||||
+2
-2
@@ -39,10 +39,10 @@ function Component(t0) {
|
||||
fbt._param(
|
||||
"paramName",
|
||||
|
||||
value
|
||||
value,
|
||||
),
|
||||
],
|
||||
{ hk: "3z5SVE" }
|
||||
{ hk: "3z5SVE" },
|
||||
);
|
||||
$[0] = value;
|
||||
$[1] = t1;
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ function Component(t0) {
|
||||
t1 = fbt._(
|
||||
"Before text {paramName} after text",
|
||||
[fbt._param("paramName", value)],
|
||||
{ hk: "26pxNm" }
|
||||
{ hk: "26pxNm" },
|
||||
);
|
||||
$[0] = value;
|
||||
$[1] = t1;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user