Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2bc92a8be |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"packages": ["packages/react", "packages/react-dom", "packages/react-server-dom-webpack", "packages/scheduler"],
|
||||
"buildCommand": "download-build-in-codesandbox-ci",
|
||||
"node": "20",
|
||||
"node": "18",
|
||||
"publishDirectory": {
|
||||
"react": "build/oss-experimental/react",
|
||||
"react-dom": "build/oss-experimental/react-dom",
|
||||
|
||||
@@ -6,51 +6,52 @@
|
||||
*/
|
||||
|
||||
import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
|
||||
import {parseConfigPragmaAsString} from 'babel-plugin-react-compiler';
|
||||
import type {editor} from 'monaco-editor';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import {useState} from 'react';
|
||||
import parserBabel from 'prettier/plugins/babel';
|
||||
import * as prettierPluginEstree from 'prettier/plugins/estree';
|
||||
import * as prettier from 'prettier/standalone';
|
||||
import {useState, useEffect} from 'react';
|
||||
import {Resizable} from 're-resizable';
|
||||
import {useStore, useStoreDispatch} from '../StoreContext';
|
||||
import {useStore} from '../StoreContext';
|
||||
import {monacoOptions} from './monacoOptions';
|
||||
import {
|
||||
generateOverridePragmaFromConfig,
|
||||
updateSourceWithOverridePragma,
|
||||
} from '../../lib/configUtils';
|
||||
|
||||
loader.config({monaco});
|
||||
|
||||
export default function ConfigEditor(): JSX.Element {
|
||||
const [, setMonaco] = useState<Monaco | null>(null);
|
||||
const store = useStore();
|
||||
const dispatchStore = useStoreDispatch();
|
||||
|
||||
const handleChange: (value: string | undefined) => void = async value => {
|
||||
if (value === undefined) return;
|
||||
// Parse string-based override config from pragma comment and format it
|
||||
const [configJavaScript, setConfigJavaScript] = useState('');
|
||||
|
||||
try {
|
||||
const newPragma = await generateOverridePragmaFromConfig(value);
|
||||
const updatedSource = updateSourceWithOverridePragma(
|
||||
store.source,
|
||||
newPragma,
|
||||
);
|
||||
useEffect(() => {
|
||||
const pragma = store.source.substring(0, store.source.indexOf('\n'));
|
||||
const configString = `(${parseConfigPragmaAsString(pragma)})`;
|
||||
|
||||
// Update the store with both the new config and updated source
|
||||
dispatchStore({
|
||||
type: 'updateFile',
|
||||
payload: {
|
||||
source: updatedSource,
|
||||
config: value,
|
||||
},
|
||||
prettier
|
||||
.format(configString, {
|
||||
semi: true,
|
||||
parser: 'babel-ts',
|
||||
plugins: [parserBabel, prettierPluginEstree],
|
||||
})
|
||||
.then(formatted => {
|
||||
setConfigJavaScript(formatted);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error formatting config:', error);
|
||||
setConfigJavaScript('({})'); // Return empty object if not valid for now
|
||||
//TODO: Add validation and error handling for config
|
||||
});
|
||||
} catch (_) {
|
||||
dispatchStore({
|
||||
type: 'updateFile',
|
||||
payload: {
|
||||
source: store.source,
|
||||
config: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log('Config:', configString);
|
||||
}, [store.source]);
|
||||
|
||||
const handleChange: (value: string | undefined) => void = value => {
|
||||
if (!value) return;
|
||||
|
||||
// TODO: Implement sync logic to update pragma comments in the source
|
||||
console.log('Config changed:', value);
|
||||
};
|
||||
|
||||
const handleMount: (
|
||||
@@ -80,11 +81,12 @@ export default function ConfigEditor(): JSX.Element {
|
||||
<MonacoEditor
|
||||
path={'config.js'}
|
||||
language={'javascript'}
|
||||
value={store.config}
|
||||
value={configJavaScript}
|
||||
onMount={handleMount}
|
||||
onChange={handleChange}
|
||||
options={{
|
||||
...monacoOptions,
|
||||
readOnly: true,
|
||||
lineNumbers: 'off',
|
||||
folding: false,
|
||||
renderLineHighlight: 'none',
|
||||
|
||||
@@ -48,7 +48,6 @@ import {
|
||||
import {transformFromAstSync} from '@babel/core';
|
||||
import {LoggerEvent} from 'babel-plugin-react-compiler/dist/Entrypoint';
|
||||
import {useSearchParams} from 'next/navigation';
|
||||
import {parseAndFormatConfig} from '../../lib/configUtils';
|
||||
|
||||
function parseInput(
|
||||
input: string,
|
||||
@@ -316,17 +315,9 @@ export default function Editor(): JSX.Element {
|
||||
});
|
||||
mountStore = defaultStore;
|
||||
}
|
||||
|
||||
parseAndFormatConfig(mountStore.source).then(config => {
|
||||
dispatchStore({
|
||||
type: 'setStore',
|
||||
payload: {
|
||||
store: {
|
||||
...mountStore,
|
||||
config,
|
||||
},
|
||||
},
|
||||
});
|
||||
dispatchStore({
|
||||
type: 'setStore',
|
||||
payload: {store: mountStore},
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import {useStore, useStoreDispatch} from '../StoreContext';
|
||||
import {monacoOptions} from './monacoOptions';
|
||||
// @ts-expect-error TODO: Make TS recognize .d.ts files, in addition to loading them with webpack.
|
||||
import React$Types from '../../node_modules/@types/react/index.d.ts';
|
||||
import {parseAndFormatConfig} from '../../lib/configUtils.ts';
|
||||
|
||||
loader.config({monaco});
|
||||
|
||||
@@ -80,17 +79,13 @@ export default function Input({errors, language}: Props): JSX.Element {
|
||||
});
|
||||
}, [monaco, language]);
|
||||
|
||||
const handleChange: (value: string | undefined) => void = async value => {
|
||||
const handleChange: (value: string | undefined) => void = value => {
|
||||
if (!value) return;
|
||||
|
||||
// Parse and format the config
|
||||
const config = await parseAndFormatConfig(value);
|
||||
|
||||
dispatchStore({
|
||||
type: 'updateFile',
|
||||
payload: {
|
||||
source: value,
|
||||
config,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -56,7 +56,6 @@ type ReducerAction =
|
||||
type: 'updateFile';
|
||||
payload: {
|
||||
source: string;
|
||||
config?: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -67,11 +66,10 @@ function storeReducer(store: Store, action: ReducerAction): Store {
|
||||
return newStore;
|
||||
}
|
||||
case 'updateFile': {
|
||||
const {source, config} = action.payload;
|
||||
const {source} = action.payload;
|
||||
const newStore = {
|
||||
...store,
|
||||
source,
|
||||
config,
|
||||
};
|
||||
return newStore;
|
||||
}
|
||||
|
||||
@@ -1,87 +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.
|
||||
*/
|
||||
|
||||
import parserBabel from 'prettier/plugins/babel';
|
||||
import prettierPluginEstree from 'prettier/plugins/estree';
|
||||
import * as prettier from 'prettier/standalone';
|
||||
import {parseConfigPragmaAsString} from '../../../packages/babel-plugin-react-compiler/src/Utils/TestUtils';
|
||||
|
||||
/**
|
||||
* Parse config from pragma and format it with prettier
|
||||
*/
|
||||
export async function parseAndFormatConfig(source: string): Promise<string> {
|
||||
const pragma = source.substring(0, source.indexOf('\n'));
|
||||
let configString = parseConfigPragmaAsString(pragma);
|
||||
if (configString !== '') {
|
||||
configString = `(${configString})`;
|
||||
}
|
||||
|
||||
try {
|
||||
const formatted = await prettier.format(configString, {
|
||||
semi: true,
|
||||
parser: 'babel-ts',
|
||||
plugins: [parserBabel, prettierPluginEstree],
|
||||
});
|
||||
return formatted;
|
||||
} catch (error) {
|
||||
console.error('Error formatting config:', error);
|
||||
return ''; // Return empty string if not valid for now
|
||||
}
|
||||
}
|
||||
|
||||
function extractCurlyBracesContent(input: string): string {
|
||||
const startIndex = input.indexOf('{');
|
||||
const endIndex = input.lastIndexOf('}');
|
||||
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
|
||||
throw new Error('No outer curly braces found in input');
|
||||
}
|
||||
return input.slice(startIndex, endIndex + 1);
|
||||
}
|
||||
|
||||
function cleanContent(content: string): string {
|
||||
return content
|
||||
.replace(/[\r\n]+/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a the override pragma comment from a formatted config object string
|
||||
*/
|
||||
export async function generateOverridePragmaFromConfig(
|
||||
formattedConfigString: string,
|
||||
): Promise<string> {
|
||||
const content = extractCurlyBracesContent(formattedConfigString);
|
||||
const cleanConfig = cleanContent(content);
|
||||
|
||||
// Format the config to ensure it's valid
|
||||
await prettier.format(`(${cleanConfig})`, {
|
||||
semi: false,
|
||||
parser: 'babel-ts',
|
||||
plugins: [parserBabel, prettierPluginEstree],
|
||||
});
|
||||
|
||||
return `// @OVERRIDE:${cleanConfig}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the override pragma comment in source code.
|
||||
*/
|
||||
export function updateSourceWithOverridePragma(
|
||||
source: string,
|
||||
newPragma: string,
|
||||
): string {
|
||||
const firstLineEnd = source.indexOf('\n');
|
||||
const firstLine = source.substring(0, firstLineEnd);
|
||||
|
||||
const pragmaRegex = /^\/\/\s*@/;
|
||||
if (firstLineEnd !== -1 && pragmaRegex.test(firstLine.trim())) {
|
||||
return newPragma + source.substring(firstLineEnd);
|
||||
} else {
|
||||
return newPragma + '\n' + source;
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,8 @@ export default function MyApp() {
|
||||
|
||||
export const defaultStore: Store = {
|
||||
source: index,
|
||||
config: '',
|
||||
};
|
||||
|
||||
export const emptyStore: Store = {
|
||||
source: '',
|
||||
config: '',
|
||||
};
|
||||
|
||||
@@ -17,7 +17,6 @@ import {defaultStore} from '../defaultStore';
|
||||
*/
|
||||
export interface Store {
|
||||
source: string;
|
||||
config?: string;
|
||||
}
|
||||
export function encodeStore(store: Store): string {
|
||||
return compressToEncodedURIComponent(JSON.stringify(store));
|
||||
@@ -66,14 +65,5 @@ export function initStoreFromUrlOrLocalStorage(): Store {
|
||||
const raw = decodeStore(encodedSource);
|
||||
|
||||
invariant(isValidStore(raw), 'Invalid Store');
|
||||
|
||||
// Add config property if missing for backwards compatibility
|
||||
if (!('config' in raw)) {
|
||||
return {
|
||||
...raw,
|
||||
config: '',
|
||||
};
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
1
compiler/apps/playground/next-env.d.ts
vendored
1
compiler/apps/playground/next-env.d.ts
vendored
@@ -1,6 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -34,30 +34,26 @@
|
||||
"invariant": "^2.2.4",
|
||||
"lz-string": "^1.5.0",
|
||||
"monaco-editor": "^0.52.0",
|
||||
"next": "15.5.2",
|
||||
"next": "^15.2.0-canary.64",
|
||||
"notistack": "^3.0.0-alpha.7",
|
||||
"prettier": "^3.3.3",
|
||||
"pretty-format": "^29.3.1",
|
||||
"re-resizable": "^6.9.16",
|
||||
"react": "19.1.1",
|
||||
"react-dom": "19.1.1"
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.9",
|
||||
"@types/react": "19.1.12",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"clsx": "^1.2.1",
|
||||
"concurrently": "^7.4.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-next": "15.5.2",
|
||||
"eslint-config-next": "^15.0.1",
|
||||
"monaco-editor-webpack-plugin": "^7.1.0",
|
||||
"postcss": "^8.4.31",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"wait-on": "^7.2.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "19.1.12",
|
||||
"@types/react-dom": "19.1.9"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,14 +36,6 @@ export enum ErrorSeverity {
|
||||
* memoization.
|
||||
*/
|
||||
CannotPreserveMemoization = 'CannotPreserveMemoization',
|
||||
/**
|
||||
* An API that is known to be incompatible with the compiler. Generally as a result of
|
||||
* the library using "interior mutability", ie having a value whose referential identity
|
||||
* stays the same but which provides access to values that can change. For example a
|
||||
* function that doesn't change but returns different results, or an object that doesn't
|
||||
* change identity but whose properties change.
|
||||
*/
|
||||
IncompatibleLibrary = 'IncompatibleLibrary',
|
||||
/**
|
||||
* Unhandled syntax that we don't support yet.
|
||||
*/
|
||||
@@ -466,8 +458,7 @@ export class CompilerError extends Error {
|
||||
case ErrorSeverity.InvalidJS:
|
||||
case ErrorSeverity.InvalidReact:
|
||||
case ErrorSeverity.InvalidConfig:
|
||||
case ErrorSeverity.UnsupportedJS:
|
||||
case ErrorSeverity.IncompatibleLibrary: {
|
||||
case ErrorSeverity.UnsupportedJS: {
|
||||
return true;
|
||||
}
|
||||
case ErrorSeverity.CannotPreserveMemoization:
|
||||
@@ -515,9 +506,8 @@ function printErrorSummary(severity: ErrorSeverity, message: string): string {
|
||||
severityCategory = 'Error';
|
||||
break;
|
||||
}
|
||||
case ErrorSeverity.IncompatibleLibrary:
|
||||
case ErrorSeverity.CannotPreserveMemoization: {
|
||||
severityCategory = 'Compilation Skipped';
|
||||
severityCategory = 'Memoization';
|
||||
break;
|
||||
}
|
||||
case ErrorSeverity.Invariant: {
|
||||
@@ -557,9 +547,6 @@ export enum ErrorCategory {
|
||||
// Checks that manual memoization is preserved
|
||||
PreserveManualMemo = 'PreserveManualMemo',
|
||||
|
||||
// Checks for known incompatible libraries
|
||||
IncompatibleLibrary = 'IncompatibleLibrary',
|
||||
|
||||
// Checking for no mutations of props, hook arguments, hook return values
|
||||
Immutability = 'Immutability',
|
||||
|
||||
@@ -883,15 +870,6 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
case ErrorCategory.IncompatibleLibrary: {
|
||||
return {
|
||||
category,
|
||||
name: 'incompatible-library',
|
||||
description:
|
||||
'Validates against usage of libraries which are incompatible with memoization (manual or automatic)',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(category, `Unsupported category ${category}`);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,12 @@ export type PluginOptions = {
|
||||
*/
|
||||
eslintSuppressionRules: Array<string> | null | undefined;
|
||||
|
||||
/**
|
||||
* Whether to report "suppression" errors for Flow suppressions. If false, suppression errors
|
||||
* are only emitted for ESLint suppressions
|
||||
*/
|
||||
flowSuppressions: boolean;
|
||||
|
||||
/*
|
||||
* Ignore 'use no forget' annotations. Helpful during testing but should not be used in production.
|
||||
*/
|
||||
|
||||
@@ -87,12 +87,18 @@ export function findProgramSuppressions(
|
||||
let enableComment: t.Comment | null = null;
|
||||
let source: SuppressionSource | null = null;
|
||||
|
||||
const rulePattern = `(${ruleNames.join('|')})`;
|
||||
const disableNextLinePattern = new RegExp(
|
||||
`eslint-disable-next-line ${rulePattern}`,
|
||||
);
|
||||
const disablePattern = new RegExp(`eslint-disable ${rulePattern}`);
|
||||
const enablePattern = new RegExp(`eslint-enable ${rulePattern}`);
|
||||
let disableNextLinePattern: RegExp | null = null;
|
||||
let disablePattern: RegExp | null = null;
|
||||
let enablePattern: RegExp | null = null;
|
||||
if (ruleNames.length !== 0) {
|
||||
const rulePattern = `(${ruleNames.join('|')})`;
|
||||
disableNextLinePattern = new RegExp(
|
||||
`eslint-disable-next-line ${rulePattern}`,
|
||||
);
|
||||
disablePattern = new RegExp(`eslint-disable ${rulePattern}`);
|
||||
enablePattern = new RegExp(`eslint-enable ${rulePattern}`);
|
||||
}
|
||||
|
||||
const flowSuppressionPattern = new RegExp(
|
||||
'\\$(FlowFixMe\\w*|FlowExpectedError|FlowIssue)\\[react\\-rule',
|
||||
);
|
||||
@@ -108,6 +114,7 @@ export function findProgramSuppressions(
|
||||
* CommentLine within the block.
|
||||
*/
|
||||
disableComment == null &&
|
||||
disableNextLinePattern != null &&
|
||||
disableNextLinePattern.test(comment.value)
|
||||
) {
|
||||
disableComment = comment;
|
||||
@@ -125,12 +132,16 @@ export function findProgramSuppressions(
|
||||
source = 'Flow';
|
||||
}
|
||||
|
||||
if (disablePattern.test(comment.value)) {
|
||||
if (disablePattern != null && disablePattern.test(comment.value)) {
|
||||
disableComment = comment;
|
||||
source = 'Eslint';
|
||||
}
|
||||
|
||||
if (enablePattern.test(comment.value) && source === 'Eslint') {
|
||||
if (
|
||||
enablePattern != null &&
|
||||
enablePattern.test(comment.value) &&
|
||||
source === 'Eslint'
|
||||
) {
|
||||
enableComment = comment;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,91 +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.
|
||||
*/
|
||||
|
||||
import {Effect, ValueKind} from '..';
|
||||
import {TypeConfig} from './TypeSchema';
|
||||
|
||||
/**
|
||||
* Libraries developed before we officially documented the [Rules of React](https://react.dev/reference/rules)
|
||||
* implement APIs which cannot be memoized safely, either via manual or automatic memoization.
|
||||
*
|
||||
* Any non-hook API that is designed to be called during render (not events/effects) should be safe to memoize:
|
||||
*
|
||||
* ```js
|
||||
* function Component() {
|
||||
* const {someFunction} = useLibrary();
|
||||
* // it should always be safe to memoize functions like this
|
||||
* const result = useMemo(() => someFunction(), [someFunction]);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* However, some APIs implement "interior mutability" — mutating values rather than copying into a new value
|
||||
* and setting state with the new value. Such functions (`someFunction()` in the example) could return different
|
||||
* values even though the function itself is the same object. This breaks memoization, since React relies on
|
||||
* the outer object (or function) changing if part of its value has changed.
|
||||
*
|
||||
* Given that we didn't have the Rules of React precisely documented prior to the introduction of React compiler,
|
||||
* it's understandable that some libraries accidentally shipped APIs that break this rule. However, developers
|
||||
* can easily run into pitfalls with these APIs. They may manually memoize them, which can break their app. Or
|
||||
* they may try using React Compiler, and think that the compiler has broken their code.
|
||||
*
|
||||
* To help ensure that developers can successfully use the compiler with existing code, this file teaches the
|
||||
* compiler about specific APIs that are known to be incompatible with memoization. We've tried to be as precise
|
||||
* as possible.
|
||||
*
|
||||
* The React team is open to collaborating with library authors to help develop compatible versions of these APIs,
|
||||
* and we have already reached out to the teams who own any API listed here to ensure they are aware of the issue.
|
||||
*/
|
||||
export function defaultModuleTypeProvider(
|
||||
moduleName: string,
|
||||
): TypeConfig | null {
|
||||
switch (moduleName) {
|
||||
case 'react-hook-form': {
|
||||
return {
|
||||
kind: 'object',
|
||||
properties: {
|
||||
useForm: {
|
||||
kind: 'hook',
|
||||
returnType: {
|
||||
kind: 'object',
|
||||
properties: {
|
||||
// Only the `watch()` function returned by react-hook-form's `useForm()` API is incompatible
|
||||
watch: {
|
||||
kind: 'function',
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
calleeEffect: Effect.Read,
|
||||
returnType: {kind: 'type', name: 'Any'},
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
knownIncompatible: `React Hook Form's \`useForm()\` API returns a \`watch()\` function which cannot be memoized safely.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case '@tanstack/react-table': {
|
||||
return {
|
||||
kind: 'object',
|
||||
properties: {
|
||||
/*
|
||||
* Many of the properties of `useReactTable()`'s return value are incompatible, so we mark the entire hook
|
||||
* as incompatible
|
||||
*/
|
||||
useReactTable: {
|
||||
kind: 'hook',
|
||||
positionalParams: [],
|
||||
restParam: Effect.Read,
|
||||
returnType: {kind: 'type', name: 'Any'},
|
||||
knownIncompatible: `TanStack Table's \`useReactTable()\` API returns functions that cannot be memoized safely`,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -50,7 +50,6 @@ import {
|
||||
import {Scope as BabelScope, NodePath} from '@babel/traverse';
|
||||
import {TypeSchema} from './TypeSchema';
|
||||
import {FlowTypeEnv} from '../Flood/Types';
|
||||
import {defaultModuleTypeProvider} from './DefaultModuleTypeProvider';
|
||||
|
||||
export const ReactElementSymbolSchema = z.object({
|
||||
elementSymbol: z.union([
|
||||
@@ -861,16 +860,10 @@ export class Environment {
|
||||
#resolveModuleType(moduleName: string, loc: SourceLocation): Global | null {
|
||||
let moduleType = this.#moduleTypes.get(moduleName);
|
||||
if (moduleType === undefined) {
|
||||
/*
|
||||
* NOTE: Zod doesn't work when specifying a function as a default, so we have to
|
||||
* fallback to the default value here
|
||||
*/
|
||||
const moduleTypeProvider =
|
||||
this.config.moduleTypeProvider ?? defaultModuleTypeProvider;
|
||||
if (moduleTypeProvider == null) {
|
||||
if (this.config.moduleTypeProvider == null) {
|
||||
return null;
|
||||
}
|
||||
const unparsedModuleConfig = moduleTypeProvider(moduleName);
|
||||
const unparsedModuleConfig = this.config.moduleTypeProvider(moduleName);
|
||||
if (unparsedModuleConfig != null) {
|
||||
const parsedModuleConfig = TypeSchema.safeParse(unparsedModuleConfig);
|
||||
if (!parsedModuleConfig.success) {
|
||||
|
||||
@@ -1001,7 +1001,6 @@ export function installTypeConfig(
|
||||
mutableOnlyIfOperandsAreMutable:
|
||||
typeConfig.mutableOnlyIfOperandsAreMutable === true,
|
||||
aliasing: typeConfig.aliasing,
|
||||
knownIncompatible: typeConfig.knownIncompatible ?? null,
|
||||
});
|
||||
}
|
||||
case 'hook': {
|
||||
@@ -1020,7 +1019,6 @@ export function installTypeConfig(
|
||||
returnValueKind: typeConfig.returnValueKind ?? ValueKind.Frozen,
|
||||
noAlias: typeConfig.noAlias === true,
|
||||
aliasing: typeConfig.aliasing,
|
||||
knownIncompatible: typeConfig.knownIncompatible ?? null,
|
||||
});
|
||||
}
|
||||
case 'object': {
|
||||
|
||||
@@ -332,7 +332,6 @@ export type FunctionSignature = {
|
||||
mutableOnlyIfOperandsAreMutable?: boolean;
|
||||
|
||||
impure?: boolean;
|
||||
knownIncompatible?: string | null | undefined;
|
||||
|
||||
canonicalName?: string;
|
||||
|
||||
|
||||
@@ -251,7 +251,6 @@ export type FunctionTypeConfig = {
|
||||
impure?: boolean | null | undefined;
|
||||
canonicalName?: string | null | undefined;
|
||||
aliasing?: AliasingSignatureConfig | null | undefined;
|
||||
knownIncompatible?: string | null | undefined;
|
||||
};
|
||||
export const FunctionTypeSchema: z.ZodType<FunctionTypeConfig> = z.object({
|
||||
kind: z.literal('function'),
|
||||
@@ -265,7 +264,6 @@ export const FunctionTypeSchema: z.ZodType<FunctionTypeConfig> = z.object({
|
||||
impure: z.boolean().nullable().optional(),
|
||||
canonicalName: z.string().nullable().optional(),
|
||||
aliasing: AliasingSignatureSchema.nullable().optional(),
|
||||
knownIncompatible: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export type HookTypeConfig = {
|
||||
@@ -276,7 +274,6 @@ export type HookTypeConfig = {
|
||||
returnValueKind?: ValueKind | null | undefined;
|
||||
noAlias?: boolean | null | undefined;
|
||||
aliasing?: AliasingSignatureConfig | null | undefined;
|
||||
knownIncompatible?: string | null | undefined;
|
||||
};
|
||||
export const HookTypeSchema: z.ZodType<HookTypeConfig> = z.object({
|
||||
kind: z.literal('hook'),
|
||||
@@ -286,7 +283,6 @@ export const HookTypeSchema: z.ZodType<HookTypeConfig> = z.object({
|
||||
returnValueKind: ValueKindSchema.nullable().optional(),
|
||||
noAlias: z.boolean().nullable().optional(),
|
||||
aliasing: AliasingSignatureSchema.nullable().optional(),
|
||||
knownIncompatible: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export type BuiltInTypeConfig =
|
||||
|
||||
@@ -2089,7 +2089,7 @@ function computeSignatureForInstruction(
|
||||
effects.push({
|
||||
kind: 'Freeze',
|
||||
value: operand,
|
||||
reason: ValueReason.HookCaptured,
|
||||
reason: ValueReason.Other,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2170,27 +2170,6 @@ function computeEffectsForLegacySignature(
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (signature.knownIncompatible != null && state.env.isInferredMemoEnabled) {
|
||||
const errors = new CompilerError();
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.IncompatibleLibrary,
|
||||
severity: ErrorSeverity.IncompatibleLibrary,
|
||||
reason: 'Use of incompatible library',
|
||||
description: [
|
||||
'This API returns functions which cannot be memoized without leading to stale UI. ' +
|
||||
'To prevent this, by default React Compiler will skip memoizing this component/hook. ' +
|
||||
'However, you may see issues if values from this API are passed to other components/hooks that are ' +
|
||||
'memoized.',
|
||||
].join(''),
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: receiver.loc,
|
||||
message: signature.knownIncompatible,
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
const stores: Array<Place> = [];
|
||||
const captures: Array<Place> = [];
|
||||
function visit(place: Place, effect: Effect): void {
|
||||
|
||||
@@ -175,41 +175,6 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
|
||||
if (node != null) {
|
||||
valueBlockNodes.set(fallthrough, node);
|
||||
}
|
||||
} else if (terminal.kind === 'goto') {
|
||||
/**
|
||||
* If we encounter a goto that is not to the natural fallthrough of the current
|
||||
* block (not the topmost fallthrough on the stack), then this is a goto to a
|
||||
* label. Any scopes that extend beyond the goto must be extended to include
|
||||
* the labeled range, so that the break statement doesn't accidentally jump
|
||||
* out of the scope. We do this by extending the start and end of the scope's
|
||||
* range to the label and its fallthrough respectively.
|
||||
*/
|
||||
const start = activeBlockFallthroughRanges.find(
|
||||
range => range.fallthrough === terminal.block,
|
||||
);
|
||||
if (start != null && start !== activeBlockFallthroughRanges.at(-1)) {
|
||||
const fallthroughBlock = fn.body.blocks.get(start.fallthrough)!;
|
||||
const firstId =
|
||||
fallthroughBlock.instructions[0]?.id ?? fallthroughBlock.terminal.id;
|
||||
for (const scope of activeScopes) {
|
||||
/**
|
||||
* activeScopes is only filtered at block start points, so some of the
|
||||
* scopes may not actually be active anymore, ie we've past their end
|
||||
* instruction. Only extend ranges for scopes that are actually active.
|
||||
*
|
||||
* TODO: consider pruning activeScopes per instruction
|
||||
*/
|
||||
if (scope.range.end <= terminal.id) {
|
||||
continue;
|
||||
}
|
||||
scope.range.start = makeInstructionId(
|
||||
Math.min(start.range.start, scope.range.start),
|
||||
);
|
||||
scope.range.end = makeInstructionId(
|
||||
Math.max(firstId, scope.range.end),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -945,8 +945,7 @@ function codegenTerminal(
|
||||
if (terminal.targetKind === 'implicit') {
|
||||
return null;
|
||||
}
|
||||
return createBreakStatement(
|
||||
terminal.loc,
|
||||
return t.breakStatement(
|
||||
terminal.targetKind === 'labeled'
|
||||
? t.identifier(codegenLabel(terminal.target))
|
||||
: null,
|
||||
@@ -956,16 +955,14 @@ function codegenTerminal(
|
||||
if (terminal.targetKind === 'implicit') {
|
||||
return null;
|
||||
}
|
||||
return createContinueStatement(
|
||||
terminal.loc,
|
||||
return t.continueStatement(
|
||||
terminal.targetKind === 'labeled'
|
||||
? t.identifier(codegenLabel(terminal.target))
|
||||
: null,
|
||||
);
|
||||
}
|
||||
case 'for': {
|
||||
return createForStatement(
|
||||
terminal.loc,
|
||||
return t.forStatement(
|
||||
codegenForInit(cx, terminal.init),
|
||||
codegenInstructionValueToExpression(cx, terminal.test),
|
||||
terminal.update !== null
|
||||
@@ -1050,8 +1047,7 @@ function codegenTerminal(
|
||||
`Unhandled lvalue kind: ${iterableItem.value.lvalue.kind}`,
|
||||
);
|
||||
}
|
||||
return createForInStatement(
|
||||
terminal.loc,
|
||||
return t.forInStatement(
|
||||
/*
|
||||
* Special handling here since we only want the VariableDeclarators without any inits
|
||||
* This needs to be updated when we handle non-trivial ForOf inits
|
||||
@@ -1144,8 +1140,7 @@ function codegenTerminal(
|
||||
`Unhandled lvalue kind: ${iterableItem.value.lvalue.kind}`,
|
||||
);
|
||||
}
|
||||
return createForOfStatement(
|
||||
terminal.loc,
|
||||
return t.forOfStatement(
|
||||
/*
|
||||
* Special handling here since we only want the VariableDeclarators without any inits
|
||||
* This needs to be updated when we handle non-trivial ForOf inits
|
||||
@@ -1167,7 +1162,7 @@ function codegenTerminal(
|
||||
alternate = block;
|
||||
}
|
||||
}
|
||||
return createIfStatement(terminal.loc, test, consequent, alternate);
|
||||
return t.ifStatement(test, consequent, alternate);
|
||||
}
|
||||
case 'return': {
|
||||
const value = codegenPlaceToExpression(cx, terminal.value);
|
||||
@@ -1178,8 +1173,7 @@ function codegenTerminal(
|
||||
return t.returnStatement(value);
|
||||
}
|
||||
case 'switch': {
|
||||
return createSwitchStatement(
|
||||
terminal.loc,
|
||||
return t.switchStatement(
|
||||
codegenPlaceToExpression(cx, terminal.test),
|
||||
terminal.cases.map(case_ => {
|
||||
const test =
|
||||
@@ -1192,26 +1186,15 @@ function codegenTerminal(
|
||||
);
|
||||
}
|
||||
case 'throw': {
|
||||
return createThrowStatement(
|
||||
terminal.loc,
|
||||
codegenPlaceToExpression(cx, terminal.value),
|
||||
);
|
||||
return t.throwStatement(codegenPlaceToExpression(cx, terminal.value));
|
||||
}
|
||||
case 'do-while': {
|
||||
const test = codegenInstructionValueToExpression(cx, terminal.test);
|
||||
return createDoWhileStatement(
|
||||
terminal.loc,
|
||||
test,
|
||||
codegenBlock(cx, terminal.loop),
|
||||
);
|
||||
return t.doWhileStatement(test, codegenBlock(cx, terminal.loop));
|
||||
}
|
||||
case 'while': {
|
||||
const test = codegenInstructionValueToExpression(cx, terminal.test);
|
||||
return createWhileStatement(
|
||||
terminal.loc,
|
||||
test,
|
||||
codegenBlock(cx, terminal.loop),
|
||||
);
|
||||
return t.whileStatement(test, codegenBlock(cx, terminal.loop));
|
||||
}
|
||||
case 'label': {
|
||||
return codegenBlock(cx, terminal.block);
|
||||
@@ -1222,8 +1205,7 @@ function codegenTerminal(
|
||||
catchParam = convertIdentifier(terminal.handlerBinding.identifier);
|
||||
cx.temp.set(terminal.handlerBinding.identifier.declarationId, null);
|
||||
}
|
||||
return createTryStatement(
|
||||
terminal.loc,
|
||||
return t.tryStatement(
|
||||
codegenBlock(cx, terminal.block),
|
||||
t.catchClause(catchParam, codegenBlock(cx, terminal.handler)),
|
||||
);
|
||||
@@ -1561,13 +1543,7 @@ const createExpressionStatement = withLoc(t.expressionStatement);
|
||||
const _createLabelledStatement = withLoc(t.labeledStatement);
|
||||
const createVariableDeclaration = withLoc(t.variableDeclaration);
|
||||
const createFunctionDeclaration = withLoc(t.functionDeclaration);
|
||||
const createWhileStatement = withLoc(t.whileStatement);
|
||||
const createDoWhileStatement = withLoc(t.doWhileStatement);
|
||||
const createSwitchStatement = withLoc(t.switchStatement);
|
||||
const createIfStatement = withLoc(t.ifStatement);
|
||||
const createForStatement = withLoc(t.forStatement);
|
||||
const createForOfStatement = withLoc(t.forOfStatement);
|
||||
const createForInStatement = withLoc(t.forInStatement);
|
||||
const _createWhileStatement = withLoc(t.whileStatement);
|
||||
const createTaggedTemplateExpression = withLoc(t.taggedTemplateExpression);
|
||||
const createLogicalExpression = withLoc(t.logicalExpression);
|
||||
const createSequenceExpression = withLoc(t.sequenceExpression);
|
||||
@@ -1582,10 +1558,6 @@ const createJsxText = withLoc(t.jsxText);
|
||||
const createJsxClosingElement = withLoc(t.jsxClosingElement);
|
||||
const createJsxOpeningElement = withLoc(t.jsxOpeningElement);
|
||||
const createStringLiteral = withLoc(t.stringLiteral);
|
||||
const createThrowStatement = withLoc(t.throwStatement);
|
||||
const createTryStatement = withLoc(t.tryStatement);
|
||||
const createBreakStatement = withLoc(t.breakStatement);
|
||||
const createContinueStatement = withLoc(t.continueStatement);
|
||||
|
||||
function createHookGuard(
|
||||
guard: ExternalFunction,
|
||||
@@ -2342,9 +2314,6 @@ function codegenInstructionValue(
|
||||
);
|
||||
}
|
||||
}
|
||||
if (instrValue.loc != null && instrValue.loc != GeneratedSource) {
|
||||
value.loc = instrValue.loc;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -411,9 +411,7 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<
|
||||
this.state = state;
|
||||
this.options = {
|
||||
memoizeJsxElements: !this.env.config.enableForest,
|
||||
forceMemoizePrimitives:
|
||||
this.env.config.enableForest ||
|
||||
this.env.config.enablePreserveExistingMemoizationGuarantees,
|
||||
forceMemoizePrimitives: this.env.config.enableForest,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -536,23 +534,9 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<
|
||||
case 'JSXText':
|
||||
case 'BinaryExpression':
|
||||
case 'UnaryExpression': {
|
||||
if (options.forceMemoizePrimitives) {
|
||||
/**
|
||||
* Because these instructions produce primitives we usually don't consider
|
||||
* them as escape points: they are known to copy, not return references.
|
||||
* However if we're forcing memoization of primitives then we mark these
|
||||
* instructions as needing memoization and walk their rvalues to ensure
|
||||
* any scopes transitively reachable from the rvalues are considered for
|
||||
* memoization. Note: we may still prune primitive-producing scopes if
|
||||
* they don't ultimately escape at all.
|
||||
*/
|
||||
const level = MemoizationLevel.Conditional;
|
||||
return {
|
||||
lvalues: lvalue !== null ? [{place: lvalue, level}] : [],
|
||||
rvalues: [...eachReactiveValueOperand(value)],
|
||||
};
|
||||
}
|
||||
const level = MemoizationLevel.Never;
|
||||
const level = options.forceMemoizePrimitives
|
||||
? MemoizationLevel.Memoized
|
||||
: MemoizationLevel.Never;
|
||||
return {
|
||||
// All of these instructions return a primitive value and never need to be memoized
|
||||
lvalues: lvalue !== null ? [{place: lvalue, level}] : [],
|
||||
@@ -701,7 +685,9 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<
|
||||
}
|
||||
case 'ComputedLoad':
|
||||
case 'PropertyLoad': {
|
||||
const level = MemoizationLevel.Conditional;
|
||||
const level = options.forceMemoizePrimitives
|
||||
? MemoizationLevel.Memoized
|
||||
: MemoizationLevel.Conditional;
|
||||
return {
|
||||
// Indirection for the inner value, memoized if the value is
|
||||
lvalues: lvalue !== null ? [{place: lvalue, level}] : [],
|
||||
|
||||
@@ -255,16 +255,11 @@ function parseConfigStringAsJS(
|
||||
|
||||
console.log('OVERRIDE:', parsedConfig);
|
||||
|
||||
const environment = parseConfigPragmaEnvironmentForTest(
|
||||
'',
|
||||
defaults.environment ?? {},
|
||||
);
|
||||
|
||||
const options: Record<keyof PluginOptions, unknown> = {
|
||||
...defaultOptions,
|
||||
panicThreshold: 'all_errors',
|
||||
compilationMode: defaults.compilationMode,
|
||||
environment,
|
||||
environment: defaults.environment ?? defaultOptions.environment,
|
||||
};
|
||||
|
||||
// Apply parsed config, merging environment if it exists
|
||||
@@ -274,9 +269,22 @@ function parseConfigStringAsJS(
|
||||
...parsedConfig.environment,
|
||||
};
|
||||
|
||||
// Apply complex defaults for environment flags that are set to true
|
||||
const environmentConfig: Partial<Record<keyof EnvironmentConfig, unknown>> =
|
||||
{};
|
||||
for (const [key, value] of Object.entries(mergedEnvironment)) {
|
||||
if (hasOwnProperty(EnvironmentConfigSchema.shape, key)) {
|
||||
if (value === true && key in testComplexConfigDefaults) {
|
||||
environmentConfig[key] = testComplexConfigDefaults[key];
|
||||
} else {
|
||||
environmentConfig[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate environment config
|
||||
const validatedEnvironment =
|
||||
EnvironmentConfigSchema.safeParse(mergedEnvironment);
|
||||
EnvironmentConfigSchema.safeParse(environmentConfig);
|
||||
if (!validatedEnvironment.success) {
|
||||
CompilerError.invariant(false, {
|
||||
reason: 'Invalid environment configuration in config pragma',
|
||||
@@ -286,6 +294,10 @@ function parseConfigStringAsJS(
|
||||
});
|
||||
}
|
||||
|
||||
if (validatedEnvironment.data.enableResetCacheOnSourceFileChanges == null) {
|
||||
validatedEnvironment.data.enableResetCacheOnSourceFileChanges = false;
|
||||
}
|
||||
|
||||
options.environment = validatedEnvironment.data;
|
||||
}
|
||||
|
||||
@@ -296,7 +308,9 @@ function parseConfigStringAsJS(
|
||||
}
|
||||
|
||||
if (hasOwnProperty(defaultOptions, key)) {
|
||||
if (key === 'target' && value === 'donotuse_meta_internal') {
|
||||
if (value === true && key in testComplexPluginOptionDefaults) {
|
||||
options[key] = testComplexPluginOptionDefaults[key];
|
||||
} else if (key === 'target' && value === 'donotuse_meta_internal') {
|
||||
options[key] = {
|
||||
kind: value,
|
||||
runtimeModule: 'react',
|
||||
|
||||
@@ -284,7 +284,8 @@ function validateInferredDep(
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.PreserveManualMemo,
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
reason: 'Existing memoization could not be preserved',
|
||||
reason:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description: [
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. ',
|
||||
'The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. ',
|
||||
@@ -538,7 +539,8 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.PreserveManualMemo,
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
reason: 'Existing memoization could not be preserved',
|
||||
reason:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description: [
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. ',
|
||||
'This dependency may be mutated later, which could cause the value to change unexpectedly.',
|
||||
@@ -586,7 +588,8 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
CompilerDiagnostic.create({
|
||||
category: ErrorCategory.PreserveManualMemo,
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
reason: 'Existing memoization could not be preserved',
|
||||
reason:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description: [
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. ',
|
||||
DEBUG
|
||||
|
||||
@@ -129,7 +129,6 @@ function useFoo(t0) {
|
||||
t1 = null;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
if (cond2) {
|
||||
mutate(s);
|
||||
}
|
||||
|
||||
@@ -46,16 +46,14 @@ function useFoo(t0) {
|
||||
t1 = $[0];
|
||||
}
|
||||
let items = t1;
|
||||
if ($[1] !== cond) {
|
||||
bb0: {
|
||||
if (cond) {
|
||||
items = [];
|
||||
} else {
|
||||
break bb0;
|
||||
}
|
||||
|
||||
items.push(2);
|
||||
bb0: if ($[1] !== cond) {
|
||||
if (cond) {
|
||||
items = [];
|
||||
} else {
|
||||
break bb0;
|
||||
}
|
||||
|
||||
items.push(2);
|
||||
$[1] = cond;
|
||||
$[2] = items;
|
||||
} else {
|
||||
|
||||
@@ -43,7 +43,6 @@ function useFoo(t0) {
|
||||
if ($[0] !== cond || $[1] !== value) {
|
||||
bb0: {
|
||||
items = [];
|
||||
|
||||
if (cond) {
|
||||
break bb0;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ function Foo() {
|
||||
if (cond) {
|
||||
thing = makeObject_Primitives();
|
||||
}
|
||||
|
||||
if (CONST_TRUE) {
|
||||
mutate(thing);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import { c as _c } from "react/compiler-runtime";
|
||||
function useBar(props) {
|
||||
const $ = _c(1);
|
||||
let z;
|
||||
|
||||
if (props.a) {
|
||||
if (props.b) {
|
||||
let t0;
|
||||
|
||||
@@ -67,7 +67,6 @@ function getNativeLogFunction(level) {
|
||||
) {
|
||||
logLevel = LOG_LEVELS.warn;
|
||||
}
|
||||
|
||||
if (global.__inspectorLog) {
|
||||
global.__inspectorLog(
|
||||
INSPECTOR_LEVELS[logLevel],
|
||||
@@ -76,7 +75,6 @@ function getNativeLogFunction(level) {
|
||||
INSPECTOR_FRAMES_TO_SKIP,
|
||||
);
|
||||
}
|
||||
|
||||
if (groupStack.length) {
|
||||
str = groupFormat("", str);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,9 @@ function useKeyCommand() {
|
||||
};
|
||||
|
||||
const moveLeft = { handler: handleKey("left") };
|
||||
|
||||
const moveRight = { handler: handleKey("right") };
|
||||
|
||||
t0 = [moveLeft, moveRight];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
|
||||
@@ -33,6 +33,7 @@ function Component() {
|
||||
let y;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
y = x = {};
|
||||
|
||||
const foo = () => {
|
||||
x = makeArray();
|
||||
};
|
||||
|
||||
@@ -44,7 +44,6 @@ function ComponentA(props) {
|
||||
if (b) {
|
||||
a.push(props.p0);
|
||||
}
|
||||
|
||||
if (props.p1) {
|
||||
b.push(props.p2);
|
||||
}
|
||||
@@ -69,7 +68,6 @@ function ComponentB(props) {
|
||||
if (mayMutate(b)) {
|
||||
a.push(props.p0);
|
||||
}
|
||||
|
||||
if (props.p1) {
|
||||
b.push(props.p2);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ function Component(props) {
|
||||
const foo = () => {
|
||||
setX(1);
|
||||
};
|
||||
|
||||
if (props.cond) {
|
||||
setX(2);
|
||||
foo();
|
||||
|
||||
@@ -36,6 +36,7 @@ function Foo() {
|
||||
return identity(1);
|
||||
},
|
||||
};
|
||||
|
||||
t0 = x.foo();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
|
||||
@@ -79,6 +79,7 @@ function foo() {
|
||||
value={[
|
||||
true,
|
||||
true,
|
||||
|
||||
"a\nb",
|
||||
"\n",
|
||||
"a1b",
|
||||
|
||||
@@ -48,6 +48,7 @@ function foo() {
|
||||
true,
|
||||
-Infinity,
|
||||
-NaN,
|
||||
|
||||
-1 * NaN,
|
||||
-1 * Infinity,
|
||||
-1 * -Infinity,
|
||||
|
||||
@@ -28,7 +28,6 @@ function Component(props) {
|
||||
const a = [];
|
||||
const b = {};
|
||||
new Foo(a, b);
|
||||
|
||||
new Foo(b);
|
||||
t0 = <div a={a} b={b} />;
|
||||
$[0] = t0;
|
||||
|
||||
@@ -43,6 +43,7 @@ function Component(t0) {
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
invoke(obj.method, cond);
|
||||
$[0] = cond;
|
||||
$[1] = x;
|
||||
|
||||
@@ -27,7 +27,6 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
function Component(props) {
|
||||
debugger;
|
||||
|
||||
if (props.cond) {
|
||||
debugger;
|
||||
} else {
|
||||
|
||||
@@ -26,7 +26,6 @@ function Component(props) {
|
||||
let x;
|
||||
if ($[0] !== props.a || $[1] !== props.b) {
|
||||
x = { a: props.a, b: props.b };
|
||||
|
||||
delete x["b"];
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
|
||||
@@ -46,7 +46,6 @@ function foo(a, b) {
|
||||
if (x.length) {
|
||||
y.push(x);
|
||||
}
|
||||
|
||||
if (b) {
|
||||
y.push(b);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ function foo(x, y, z) {
|
||||
} else {
|
||||
items2 = $[2];
|
||||
}
|
||||
|
||||
if (y) {
|
||||
items.push(x);
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const result = useMemo(
|
||||
() => makeObject(props.value).value + 1,
|
||||
[props.value]
|
||||
);
|
||||
console.log(result);
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return {value};
|
||||
}
|
||||
|
||||
export const TODO_FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
sequentialRenders: [
|
||||
{value: 42},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { makeObject_Primitives, ValidateMemoization } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const result = makeObject(props.value).value + 1;
|
||||
|
||||
console.log(result);
|
||||
return "ok";
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return { value };
|
||||
}
|
||||
|
||||
export const TODO_FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 42 }],
|
||||
sequentialRenders: [
|
||||
{ value: 42 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
{ value: 3.14 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,32 +0,0 @@
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const result = useMemo(
|
||||
() => makeObject(props.value).value + 1,
|
||||
[props.value]
|
||||
);
|
||||
console.log(result);
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return {value};
|
||||
}
|
||||
|
||||
export const TODO_FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
sequentialRenders: [
|
||||
{value: 42},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
],
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const result = makeObject(props.value).value + 1;
|
||||
console.log(result);
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return {value};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
sequentialRenders: [
|
||||
{value: 42},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { makeObject_Primitives, ValidateMemoization } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const result = makeObject(props.value).value + 1;
|
||||
console.log(result);
|
||||
return "ok";
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return { value };
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 42 }],
|
||||
sequentialRenders: [
|
||||
{ value: 42 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
{ value: 3.14 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "ok"
|
||||
"ok"
|
||||
"ok"
|
||||
"ok"
|
||||
"ok"
|
||||
"ok"
|
||||
"ok"
|
||||
"ok"
|
||||
logs: [42,43,42,43,3.14,4.140000000000001,3.14,4.140000000000001,42,43,3.14,4.140000000000001,42,43,3.14,4.140000000000001]
|
||||
@@ -1,29 +0,0 @@
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const result = makeObject(props.value).value + 1;
|
||||
console.log(result);
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return {value};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
sequentialRenders: [
|
||||
{value: 42},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
],
|
||||
};
|
||||
@@ -57,7 +57,6 @@ function Component(t0) {
|
||||
let y;
|
||||
if ($[0] !== a || $[1] !== b || $[2] !== c) {
|
||||
x = [];
|
||||
|
||||
if (a) {
|
||||
let t1;
|
||||
if ($[5] !== b) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @eslintSuppressionRules:[]
|
||||
|
||||
// The suppression here shouldn't cause compilation to get skipped
|
||||
// Previously we had a bug where an empty list of suppressions would
|
||||
// create a regexp that matched any suppression
|
||||
function Component(props) {
|
||||
'use forget';
|
||||
// eslint-disable-next-line foo/not-react-related
|
||||
return <div>{props.text}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{text: 'Hello'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @eslintSuppressionRules:[]
|
||||
|
||||
// The suppression here shouldn't cause compilation to get skipped
|
||||
// Previously we had a bug where an empty list of suppressions would
|
||||
// create a regexp that matched any suppression
|
||||
function Component(props) {
|
||||
"use forget";
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.text) {
|
||||
t0 = <div>{props.text}</div>;
|
||||
$[0] = props.text;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ text: "Hello" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>Hello</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
// @eslintSuppressionRules:[]
|
||||
|
||||
// The suppression here shouldn't cause compilation to get skipped
|
||||
// Previously we had a bug where an empty list of suppressions would
|
||||
// create a regexp that matched any suppression
|
||||
function Component(props) {
|
||||
'use forget';
|
||||
// eslint-disable-next-line foo/not-react-related
|
||||
return <div>{props.text}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{text: 'Hello'}],
|
||||
};
|
||||
@@ -26,7 +26,7 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `props.items`, but the source dependencies were [props?.items, props.cond]. Inferred different dependency than source.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `props.items`, but the source dependencies were [props?.items, props.cond]. Inferred different dependency than source.
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {knownIncompatible} from 'ReactCompilerKnownIncompatibleTest';
|
||||
|
||||
function Component() {
|
||||
const data = knownIncompatible();
|
||||
return <div>Error</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Use of incompatible library
|
||||
|
||||
This API returns functions which cannot be memoized without leading to stale UI. To prevent this, by default React Compiler will skip memoizing this component/hook. However, you may see issues if values from this API are passed to other components/hooks that are memoized.
|
||||
|
||||
error.invalid-known-incompatible-function.ts:4:15
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | const data = knownIncompatible();
|
||||
| ^^^^^^^^^^^^^^^^^ useKnownIncompatible is known to be incompatible
|
||||
5 | return <div>Error</div>;
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import {knownIncompatible} from 'ReactCompilerKnownIncompatibleTest';
|
||||
|
||||
function Component() {
|
||||
const data = knownIncompatible();
|
||||
return <div>Error</div>;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useKnownIncompatibleIndirect} from 'ReactCompilerKnownIncompatibleTest';
|
||||
|
||||
function Component() {
|
||||
const {incompatible} = useKnownIncompatibleIndirect();
|
||||
return <div>{incompatible()}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Use of incompatible library
|
||||
|
||||
This API returns functions which cannot be memoized without leading to stale UI. To prevent this, by default React Compiler will skip memoizing this component/hook. However, you may see issues if values from this API are passed to other components/hooks that are memoized.
|
||||
|
||||
error.invalid-known-incompatible-hook-return-property.ts:5:15
|
||||
3 | function Component() {
|
||||
4 | const {incompatible} = useKnownIncompatibleIndirect();
|
||||
> 5 | return <div>{incompatible()}</div>;
|
||||
| ^^^^^^^^^^^^ useKnownIncompatibleIndirect returns an incompatible() function that is known incompatible
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import {useKnownIncompatibleIndirect} from 'ReactCompilerKnownIncompatibleTest';
|
||||
|
||||
function Component() {
|
||||
const {incompatible} = useKnownIncompatibleIndirect();
|
||||
return <div>{incompatible()}</div>;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useKnownIncompatible} from 'ReactCompilerKnownIncompatibleTest';
|
||||
|
||||
function Component() {
|
||||
const data = useKnownIncompatible();
|
||||
return <div>Error</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Use of incompatible library
|
||||
|
||||
This API returns functions which cannot be memoized without leading to stale UI. To prevent this, by default React Compiler will skip memoizing this component/hook. However, you may see issues if values from this API are passed to other components/hooks that are memoized.
|
||||
|
||||
error.invalid-known-incompatible-hook.ts:4:15
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | const data = useKnownIncompatible();
|
||||
| ^^^^^^^^^^^^^^^^^^^^ useKnownIncompatible is known to be incompatible
|
||||
5 | return <div>Error</div>;
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import {useKnownIncompatible} from 'ReactCompilerKnownIncompatibleTest';
|
||||
|
||||
function Component() {
|
||||
const data = useKnownIncompatible();
|
||||
return <div>Error</div>;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `props.items.edges.nodes`, but the source dependencies were [props.items?.edges?.nodes]. Inferred different dependency than source.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
Memoization: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
|
||||
error.invalid-useEffect-dep-not-memoized-bc-range-overlaps-hook.ts:9:2
|
||||
7 |
|
||||
|
||||
@@ -22,7 +22,7 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
Memoization: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
|
||||
error.invalid-useEffect-dep-not-memoized.ts:6:2
|
||||
4 | function Component(props) {
|
||||
|
||||
@@ -22,7 +22,7 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
Memoization: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
|
||||
error.invalid-useInsertionEffect-dep-not-memoized.ts:6:2
|
||||
4 | function Component(props) {
|
||||
|
||||
@@ -22,7 +22,7 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
Memoization: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
|
||||
error.invalid-useLayoutEffect-dep-not-memoized.ts:6:2
|
||||
4 | function Component(props) {
|
||||
|
||||
@@ -33,7 +33,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `Ref.current`, but the source dependencies were []. Inferred dependency not present in source.
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `notaref.current`, but the source dependencies were []. Inferred dependency not present in source.
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ component Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ component Component(id) {
|
||||
```
|
||||
Found 3 errors:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
@@ -76,7 +76,7 @@ React Compiler has skipped optimizing this component because the existing manual
|
||||
18 | const setCurrentIndex = useCallback(
|
||||
19 | (index: number) => {
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly.
|
||||
|
||||
@@ -88,7 +88,7 @@ React Compiler has skipped optimizing this component because the existing manual
|
||||
30 |
|
||||
31 | if (prevId !== id) {
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Compilation Skipped: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
Memoization: React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior
|
||||
|
||||
error.validate-memoized-effect-deps-invalidated-dep-value.ts:11:2
|
||||
9 | const y = [x];
|
||||
|
||||
@@ -27,7 +27,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly.
|
||||
|
||||
@@ -40,7 +40,7 @@ error.validate-object-entries-mutation.ts:6:57
|
||||
8 | value.updated = true;
|
||||
9 | });
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly.
|
||||
|
||||
@@ -40,7 +40,7 @@ error.validate-object-values-mutation.ts:6:55
|
||||
8 | value.updated = true;
|
||||
9 | });
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ function foo(a, b, c) {
|
||||
} else {
|
||||
x = $[3];
|
||||
}
|
||||
|
||||
if (x.length) {
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ function Component(props) {
|
||||
let items;
|
||||
if ($[0] !== props.items) {
|
||||
items = [];
|
||||
|
||||
for (let i = 0, length = props.items.length; i < length; i++) {
|
||||
items.push(props.items[i]);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ function Component(props) {
|
||||
for (const x of items) {
|
||||
lastItem = x;
|
||||
}
|
||||
|
||||
if (lastItem != null) {
|
||||
lastItem.a = lastItem.a + 1;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ function Component(props) {
|
||||
for (const x of items) {
|
||||
lastItem = x;
|
||||
}
|
||||
|
||||
if (lastItem != null) {
|
||||
lastItem.a = lastItem.a + 1;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ function Component(props) {
|
||||
t1 = $[1];
|
||||
}
|
||||
const onChange = t1;
|
||||
|
||||
if (props.cond) {
|
||||
}
|
||||
let t2;
|
||||
|
||||
@@ -69,7 +69,6 @@ const Foo = isForgetEnabled_Fixtures()
|
||||
if (DEV && shouldInstrument)
|
||||
useRenderCounter("Foo", "/codegen-instrument-forget-gating-test.ts");
|
||||
const $ = _c(3);
|
||||
|
||||
if (props.bar < 0) {
|
||||
return props.children;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":206},"end":{"line":16,"column":1,"index":433},"filename":"dynamic-gating-bailout-nopanic.ts"},"detail":{"options":{"category":"PreserveManualMemo","severity":"CannotPreserveMemoization","reason":"Existing memoization could not be preserved","description":"React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `value`, but the source dependencies were []. Inferred dependency not present in source.","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":9,"column":31,"index":288},"end":{"line":9,"column":52,"index":309},"filename":"dynamic-gating-bailout-nopanic.ts"},"message":"Could not preserve existing manual memoization"}]}}}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":206},"end":{"line":16,"column":1,"index":433},"filename":"dynamic-gating-bailout-nopanic.ts"},"detail":{"options":{"category":"PreserveManualMemo","severity":"CannotPreserveMemoization","reason":"Compilation skipped because existing memoization could not be preserved","description":"React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `value`, but the source dependencies were []. Inferred dependency not present in source.","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":9,"column":31,"index":288},"end":{"line":9,"column":52,"index":309},"filename":"dynamic-gating-bailout-nopanic.ts"},"message":"Could not preserve existing manual memoization"}]}}}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
|
||||
@@ -63,7 +63,6 @@ const Foo = isForgetEnabled_Fixtures()
|
||||
? function Foo(props) {
|
||||
"use forget";
|
||||
const $ = _c(3);
|
||||
|
||||
if (props.bar < 0) {
|
||||
return props.children;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ function hoisting() {
|
||||
return bar();
|
||||
},
|
||||
};
|
||||
|
||||
const bar = _temp;
|
||||
|
||||
t0 = x.foo();
|
||||
|
||||
@@ -30,7 +30,6 @@ function Component(props) {
|
||||
let items;
|
||||
if ($[0] !== props.a || $[1] !== props.cond) {
|
||||
let t0;
|
||||
|
||||
if (props.cond) {
|
||||
t0 = [];
|
||||
} else {
|
||||
|
||||
@@ -390,7 +390,6 @@ function ConditionalJsx(t0) {
|
||||
t1 = $[0];
|
||||
}
|
||||
let content = t1;
|
||||
|
||||
if (shouldWrap) {
|
||||
const t2 = content;
|
||||
let t3;
|
||||
|
||||
@@ -27,7 +27,6 @@ import * as SharedRuntime from "shared-runtime";
|
||||
function useFoo(t0) {
|
||||
const $ = _c(1);
|
||||
const { cond } = t0;
|
||||
|
||||
if (cond) {
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const result = useMemo(() => {
|
||||
return makeObject(props.value).value + 1;
|
||||
}, [props.value]);
|
||||
return <ValidateMemoization inputs={[props.value]} output={result} />;
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return {value};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
sequentialRenders: [
|
||||
{value: 42},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { makeObject_Primitives, ValidateMemoization } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(7);
|
||||
let t0;
|
||||
if ($[0] !== props.value) {
|
||||
t0 = makeObject(props.value);
|
||||
$[0] = props.value;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const result = t0.value + 1;
|
||||
let t1;
|
||||
if ($[2] !== props.value) {
|
||||
t1 = [props.value];
|
||||
$[2] = props.value;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
let t2;
|
||||
if ($[4] !== result || $[5] !== t1) {
|
||||
t2 = <ValidateMemoization inputs={t1} output={result} />;
|
||||
$[4] = result;
|
||||
$[5] = t1;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return { value };
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 42 }],
|
||||
sequentialRenders: [
|
||||
{ value: 42 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
{ value: 3.14 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
{ value: 42 },
|
||||
{ value: 3.14 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"inputs":[42],"output":43}</div>
|
||||
<div>{"inputs":[42],"output":43}</div>
|
||||
<div>{"inputs":[3.14],"output":4.140000000000001}</div>
|
||||
<div>{"inputs":[3.14],"output":4.140000000000001}</div>
|
||||
<div>{"inputs":[42],"output":43}</div>
|
||||
<div>{"inputs":[3.14],"output":4.140000000000001}</div>
|
||||
<div>{"inputs":[42],"output":43}</div>
|
||||
<div>{"inputs":[3.14],"output":4.140000000000001}</div>
|
||||
logs: [42,3.14,42,3.14,42,3.14]
|
||||
@@ -1,30 +0,0 @@
|
||||
// @compilationMode:"infer" @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const result = useMemo(() => {
|
||||
return makeObject(props.value).value + 1;
|
||||
}, [props.value]);
|
||||
return <ValidateMemoization inputs={[props.value]} output={result} />;
|
||||
}
|
||||
|
||||
function makeObject(value) {
|
||||
console.log(value);
|
||||
return {value};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
sequentialRenders: [
|
||||
{value: 42},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
{value: 42},
|
||||
{value: 3.14},
|
||||
],
|
||||
};
|
||||
@@ -30,7 +30,6 @@ const { getNumber } = require("shared-runtime");
|
||||
function Component(props) {
|
||||
const $ = _c(1);
|
||||
let x;
|
||||
|
||||
if (props.cond) {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @compilationMode:"infer"
|
||||
import {makeArray} from 'shared-runtime';
|
||||
|
||||
function Component() {
|
||||
@@ -31,7 +30,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @compilationMode:"infer"
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { makeArray } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @compilationMode:"infer"
|
||||
import {makeArray} from 'shared-runtime';
|
||||
|
||||
function Component() {
|
||||
|
||||
@@ -75,6 +75,7 @@ function Component() {
|
||||
if ($[0] !== state) {
|
||||
t0 = [
|
||||
React,
|
||||
|
||||
state,
|
||||
CONST,
|
||||
NON_REASSIGNED_LET,
|
||||
|
||||
@@ -94,16 +94,12 @@ function testFunction(props) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (a) {
|
||||
}
|
||||
|
||||
if (b) {
|
||||
}
|
||||
|
||||
if (c) {
|
||||
}
|
||||
|
||||
if (d) {
|
||||
}
|
||||
|
||||
|
||||
@@ -78,19 +78,14 @@ function Component(props) {
|
||||
x = {};
|
||||
x.b = b;
|
||||
const y = mutate(x, d);
|
||||
|
||||
if (a) {
|
||||
}
|
||||
|
||||
if (b) {
|
||||
}
|
||||
|
||||
if (c) {
|
||||
}
|
||||
|
||||
if (d) {
|
||||
}
|
||||
|
||||
if (y) {
|
||||
}
|
||||
|
||||
|
||||
@@ -51,16 +51,12 @@ function Component(props) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (a) {
|
||||
}
|
||||
|
||||
if (b) {
|
||||
}
|
||||
|
||||
if (c) {
|
||||
}
|
||||
|
||||
if (d) {
|
||||
}
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ import {
|
||||
} from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(3);
|
||||
const { data } = t0;
|
||||
let obj;
|
||||
let myDiv = null;
|
||||
if ($[0] !== data.cond || $[1] !== data.cond1) {
|
||||
bb0: if (data.cond) {
|
||||
bb0: if (data.cond) {
|
||||
if ($[0] !== data.cond1) {
|
||||
obj = makeObject_Primitives();
|
||||
if (data.cond1) {
|
||||
myDiv = <Stringify value={mutateAndReturn(obj)} />;
|
||||
@@ -62,14 +62,13 @@ function useFoo(t0) {
|
||||
}
|
||||
|
||||
mutate(obj);
|
||||
$[0] = data.cond1;
|
||||
$[1] = obj;
|
||||
$[2] = myDiv;
|
||||
} else {
|
||||
obj = $[1];
|
||||
myDiv = $[2];
|
||||
}
|
||||
$[0] = data.cond;
|
||||
$[1] = data.cond1;
|
||||
$[2] = obj;
|
||||
$[3] = myDiv;
|
||||
} else {
|
||||
obj = $[2];
|
||||
myDiv = $[3];
|
||||
}
|
||||
return myDiv;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,8 @@ function Component(t0) {
|
||||
let z;
|
||||
if ($[0] !== prop1 || $[1] !== prop2) {
|
||||
let x = [{ value: prop1 }];
|
||||
|
||||
while (x.length < 2) {
|
||||
arrayPush(x, { value: prop2 });
|
||||
|
||||
if (x[0].value === prop1) {
|
||||
x = [{ value: prop2 }];
|
||||
const y = x;
|
||||
|
||||
@@ -31,7 +31,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly.
|
||||
|
||||
@@ -44,7 +44,7 @@ error.invalid-useCallback-captures-reassigned-context.ts:11:37
|
||||
13 | x = makeArray();
|
||||
14 |
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ function Component(props) {
|
||||
let items;
|
||||
if ($[0] !== props.a || $[1] !== props.cond) {
|
||||
let t0;
|
||||
|
||||
if (props.cond) {
|
||||
t0 = [];
|
||||
} else {
|
||||
|
||||
@@ -33,6 +33,7 @@ function Component(props) {
|
||||
if ($[0] !== props.value) {
|
||||
const key = {};
|
||||
context = { [key]: identity([props.value]) };
|
||||
|
||||
mutate(key);
|
||||
$[0] = props.value;
|
||||
$[1] = context;
|
||||
|
||||
@@ -44,6 +44,7 @@ function Component(props) {
|
||||
t2 = $[3];
|
||||
}
|
||||
context = t2;
|
||||
|
||||
mutate(key);
|
||||
$[0] = props.value;
|
||||
$[1] = context;
|
||||
|
||||
@@ -32,7 +32,6 @@ import fbt from "fbt";
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
const sections = Object.keys(items);
|
||||
|
||||
for (let i = 0; i < sections.length; i = i + 3, i) {
|
||||
chunks.push(sections.slice(i, i + 3).map(_temp));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user