Compare commits

...

23 Commits

Author SHA1 Message Date
Joe Savona
6616654253 [compiler] Show a ref name hint when assigning to non-ref in a callback
In #34125 I added a hint where if you assign to the .current property of a frozen object, we suggest naming the variable as `ref` or `-Ref`. However, the tracking for mutations that assign to .current specifically wasn't propagated past function expression boundaries, which meant that the hint only showed up if you mutated the ref in the main body of the component/hook. That's less likely to happen since most folks know not to access refs in render. What's more likely is that you'll (correctly) assign a ref in an effect or callback, but the compiler will throw an error. By showing a hint in this case we can help people understand the naming pattern.
2025-08-27 12:16:05 -07:00
Jan Kassens
d260b0d8b8 Update Flow to 0.265 (#34270)
Looks like this version removed `Object.prototype` although I didn't see
that in the changelog. This is fine for this code here.
2025-08-22 15:22:22 -04:00
Joseph Savona
425ba0ad6d [compiler] Script to produce markdown of lint rule docs (#34260)
The docs site is in a separate repo, but this gives us a semi-automated
way to update the docs about our lint rules. The script generates
markdown files from the rule definitions which we can then manually
copy/paste into the docs site somewhere. In the future we can automate
this fully.
2025-08-22 09:59:28 -07:00
Jan Kassens
6de32a5a07 Update Flow to 0.263 (#34269)
This update was a bit more involved.

- `React$Component` was removed, I replaced it with Flow component
types.
- Flow removed shipping the standard library. This adds the environment
libraries back from `flow-typed` which seemed to have changed slightly
(probably got more precise and less `any`s). Suppresses some new type
errors.
2025-08-22 12:10:13 -04:00
Abdulwahab Omira
698bb4deb7 Add support for ARIA 1.3 attributes (#34264)
Co-authored-by: Abdulwahab Omira <abdulwahabomira@gmail.com>
Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
2025-08-22 16:22:18 +02:00
Sebastian Markbåge
11d7bcf88c [DevTools] Use source maps to infer name asynchronously (#34212) 2025-08-22 00:38:09 +02:00
Sebastian Markbåge
a85ec041d6 [DevTools] Ignore List Stack Traces (#34210)
Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
2025-08-22 00:03:05 +02:00
Joseph Savona
7d29ecbeb2 [compiler] Aggregate error reporting, separate eslint rules (#34176)
NOTE: this is a merged version of @mofeiZ's original PR along with my
edits per offline discussion. The description is updated to reflect the
latest approach.

The key problem we're trying to solve with this PR is to allow
developers more control over the compiler's various validations. The
idea is to have a number of rules targeting a specific category of
issues, such as enforcing immutability of props/state/etc or disallowing
access to refs during render. We don't want to have to run the compiler
again for every single rule, though, so @mofeiZ added an LRU cache that
caches the full compilation output of N most recent files. The first
rule to run on a given file will cause it to get cached, and then
subsequent rules can pull from the cache, with each rule filtering down
to its specific category of errors.

For the categories, I went through and assigned a category roughly 1:1
to existing validations, and then used my judgement on some places that
felt distinct enough to warrant a separate error. Every error in the
compiler now has to supply both a severity (for legacy reasons) and a
category (for ESLint). Each category corresponds 1:1 to a ESLint rule
definition, so that the set of rules is automatically populated based on
the defined categories.

Categories include a flag for whether they should be in the recommended
set or not.

Note that as with the original version of this PR, only
eslint-plugin-react-compiler is changed. We still have to update the
main lint rule.

## Test Plan

* Created a sample project using ESLint v9 and verified that the plugin
can be configured correctly and detects errors
* Edited `fixtures/eslint-v9` and introduced errors, verified that the w
latest config changes in that fixture it correctly detects the errors
* In the sample project, confirmed that the LRU caching is correctly
caching compiler output, ie compiling files just once.

Co-authored-by: Mofei Zhang <feifei0@meta.com>
2025-08-21 14:53:34 -07:00
Sebastian Markbåge
253abc78a1 [Flight] Transfer Debug Info from a synchronous Reference to another Chunk (#34229) 2025-08-21 23:50:20 +02:00
Jan Kassens
d73b6f1110 Update Flow to 0.261 (#34255)
- 0.261 required to pull out a constant to preserve refinement
- 0.259 needed some updated suppressions for hacky stuff
2025-08-21 15:02:49 -04:00
Jan Kassens
d5586e2059 Update Flow to 0.258 (#34254)
Minor new suppressions only.
2025-08-21 14:17:13 -04:00
Jan Kassens
ec5dd0ab3a Update Flow to 0.257 (#34253)
After an easy couple version with #34252, this version is less flexible
(and safer) on inferring exported types mainly.

We require to annotate some exported types to differentiate between
`boolean` and literal `true` types, etc.
2025-08-21 13:30:01 -04:00
Ruslan Lesiutin
8120753665 [DevTools] fix: always send a response to fetch-file request in the extension (#34235)
This fixes the displaying of "rendered by" section if owner stacks
contained any native frames. This regressed after
https://github.com/facebook/react/pull/34185, where we added the
Suspense boundary for the StackTraceView.

This fails because the Promise that is responsible for symbolication of
the source is never getting resolved or rejected.
Previously, we would just throw an Error without sending a corresponding
message to the `main` script, and it would just cache a Promise that is
never resolved, hence the Suspense boundary for "rendered by" section is
never resolved.

In a separate change, I think we need to update StackTraceView component
to display `native` as location, instead of `:0`:
<img width="712" height="118" alt="Screenshot 2025-08-20 at 00 20 42"
src="https://github.com/user-attachments/assets/c79735c9-fdd2-467c-96cd-2bc29d38c4e0"
/>
2025-08-21 18:28:33 +01:00
Jan Kassens
3770ff3853 Update Flow to 0.256 (#34252)
Looks like these versions didn't require changes, so easy fast forward.
2025-08-21 12:33:56 -04:00
Jan Kassens
873f711299 Update Flow to 0.248 (#34248)
This update remove support for `%checks`.

Thanks @SamChou19815 for finding a close replacement that works.
2025-08-21 11:15:34 -04:00
Jan Kassens
5f06c3d22a Update Flow to 0.247 (#34245)
`$Call` was removed.
2025-08-20 22:19:57 -04:00
Jan Kassens
243a56b9a2 Update Flow to 0.246 (#34244)
Catching up Flow versions. Since there's plenty new errors, I'm taking
each version with breaking changes as a new PR.
2025-08-20 21:46:55 -04:00
Jan Kassens
83c7379b96 Add flow suppression for Constant Condition rollout (#34243) 2025-08-20 18:24:01 -04:00
lauren
c2ac8b4f0e [ci] Fix permissions for direct sync branch PRs workflow (#34241)
Because we sync built artifacts into Meta, we can't support edits from
inside www/fbsource to be synced back into OSS as it would cause merge
conflicts for future OSS PRs.

We have a workflow that should automatically catch and close these PRs,
but it looks like this one was missing one permission.
2025-08-20 17:09:38 -04:00
Sebastian "Sebbie" Silbermann
03fda05d2c [DevTools] Fix display of stack frames with anonymous sources (#34237) 2025-08-20 17:31:42 +02:00
Hendrik Liebau
0bc71e67ab [Flight] Add debugChannel option to Edge and Node clients (#34236)
When a debug channel is used between the Flight server and a browser
Flight client, we want to allow the same RSC stream to be used for
server-side rendering. To support this, the Edge and Node Flight clients
also need to accept a `debugChannel` option. Without it, debug
information would be missing (e.g. for SSR error stacks), and in some
cases this could result in `Connection closed` errors.

This PR adds support for the `debugChannel` option in the Edge and Node
clients for ESM, Parcel, Turbopack, and Webpack. Unlike the browser
clients, these clients only support a one-way channel, since the Flight
server’s return protocol is not designed for multiple clients.

The implementation follows the approach used in the browser clients, but
excludes the writable parts.
2025-08-20 16:46:34 +02:00
Sebastian "Sebbie" Silbermann
3e20dc8b9c [DevTools] Fix crash when inspecting Components suspended on data awaited in anonymous functions (#34234) 2025-08-20 09:34:06 +02:00
Sebastian "Sebbie" Silbermann
ae5c2f82b3 [DevTools] Handle reorders when resuspending while fallback contains Suspense (#34225) 2025-08-19 20:22:54 +02:00
224 changed files with 19216 additions and 2329 deletions

View File

@@ -28,3 +28,6 @@ packages/react-devtools-shared/src/hooks/__tests__/__source__/__untransformed__/
packages/react-devtools-shell/dist
packages/react-devtools-timeline/dist
packages/react-devtools-timeline/static
# Imported third-party Flow types
flow-typed/

View File

@@ -547,7 +547,6 @@ module.exports = {
},
globals: {
$Call: 'readonly',
$ElementType: 'readonly',
$Flow$ModuleRef: 'readonly',
$FlowFixMe: 'readonly',
@@ -582,6 +581,7 @@ module.exports = {
IteratorResult: 'readonly',
JSONValue: 'readonly',
JSResourceReference: 'readonly',
mixin$Animatable: 'readonly',
MouseEventHandler: 'readonly',
NavigateEvent: 'readonly',
PropagationPhases: 'readonly',
@@ -620,7 +620,6 @@ module.exports = {
PropertyIndexedKeyframes: 'readonly',
KeyframeAnimationOptions: 'readonly',
GetAnimationsOptions: 'readonly',
Animatable: 'readonly',
ScrollTimeline: 'readonly',
EventListenerOptionsOrUseCapture: 'readonly',
FocusOptions: 'readonly',

View File

@@ -18,6 +18,7 @@ jobs:
permissions:
# Used to create a review and close PRs
pull-requests: write
contents: write
steps:
- name: Close PR
uses: actions/github-script@v7

View File

@@ -8,6 +8,7 @@ module.exports = {
'@babel/plugin-syntax-jsx',
'@babel/plugin-transform-flow-strip-types',
['@babel/plugin-transform-class-properties', {loose: true}],
['@babel/plugin-transform-private-methods', {loose: true}],
'@babel/plugin-transform-classes',
],
presets: [

View File

@@ -19,7 +19,8 @@
"test": "yarn workspaces run test",
"snap": "yarn workspace babel-plugin-react-compiler run snap",
"snap:build": "yarn workspace snap run build",
"npm:publish": "node scripts/release/publish"
"npm:publish": "node scripts/release/publish",
"eslint-docs": "yarn workspace babel-plugin-react-compiler build && node scripts/build-eslint-docs.js"
},
"dependencies": {
"fs-extra": "^4.0.2",

View File

@@ -7,9 +7,10 @@
import * as t from '@babel/types';
import {codeFrameColumns} from '@babel/code-frame';
import type {SourceLocation} from './HIR';
import {type SourceLocation} from './HIR';
import {Err, Ok, Result} from './Utils/Result';
import {assertExhaustive} from './Utils/utils';
import invariant from 'invariant';
export enum ErrorSeverity {
/**
@@ -47,8 +48,9 @@ export enum ErrorSeverity {
}
export type CompilerDiagnosticOptions = {
category: ErrorCategory;
severity: ErrorSeverity;
category: string;
reason: string;
description: string;
details: Array<CompilerDiagnosticDetail>;
suggestions?: Array<CompilerSuggestion> | null | undefined;
@@ -91,9 +93,10 @@ export type CompilerSuggestion =
};
export type CompilerErrorDetailOptions = {
category: ErrorCategory;
severity: ErrorSeverity;
reason: string;
description?: string | null | undefined;
severity: ErrorSeverity;
loc: SourceLocation | null;
suggestions?: Array<CompilerSuggestion> | null | undefined;
};
@@ -119,8 +122,8 @@ export class CompilerDiagnostic {
return new CompilerDiagnostic({...options, details: []});
}
get category(): CompilerDiagnosticOptions['category'] {
return this.options.category;
get reason(): CompilerDiagnosticOptions['reason'] {
return this.options.reason;
}
get description(): CompilerDiagnosticOptions['description'] {
return this.options.description;
@@ -131,6 +134,9 @@ export class CompilerDiagnostic {
get suggestions(): CompilerDiagnosticOptions['suggestions'] {
return this.options.suggestions;
}
get category(): ErrorCategory {
return this.options.category;
}
withDetail(detail: CompilerDiagnosticDetail): CompilerDiagnostic {
this.options.details.push(detail);
@@ -148,7 +154,7 @@ export class CompilerDiagnostic {
printErrorMessage(source: string, options: PrintErrorMessageOptions): string {
const buffer = [
printErrorSummary(this.severity, this.category),
printErrorSummary(this.severity, this.reason),
'\n\n',
this.description,
];
@@ -193,7 +199,7 @@ export class CompilerDiagnostic {
}
toString(): string {
const buffer = [printErrorSummary(this.severity, this.category)];
const buffer = [printErrorSummary(this.severity, this.reason)];
if (this.description != null) {
buffer.push(`. ${this.description}.`);
}
@@ -231,6 +237,9 @@ export class CompilerErrorDetail {
get suggestions(): CompilerErrorDetailOptions['suggestions'] {
return this.options.suggestions;
}
get category(): ErrorCategory {
return this.options.category;
}
primaryLocation(): SourceLocation | null {
return this.loc;
@@ -280,13 +289,14 @@ export class CompilerError extends Error {
static invariant(
condition: unknown,
options: Omit<CompilerErrorDetailOptions, 'severity'>,
options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
): asserts condition {
if (!condition) {
const errors = new CompilerError();
errors.pushErrorDetail(
new CompilerErrorDetail({
...options,
category: ErrorCategory.Invariant,
severity: ErrorSeverity.Invariant,
}),
);
@@ -301,23 +311,28 @@ export class CompilerError extends Error {
}
static throwTodo(
options: Omit<CompilerErrorDetailOptions, 'severity'>,
options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
): never {
const errors = new CompilerError();
errors.pushErrorDetail(
new CompilerErrorDetail({...options, severity: ErrorSeverity.Todo}),
new CompilerErrorDetail({
...options,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
}),
);
throw errors;
}
static throwInvalidJS(
options: Omit<CompilerErrorDetailOptions, 'severity'>,
options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
): never {
const errors = new CompilerError();
errors.pushErrorDetail(
new CompilerErrorDetail({
...options,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
}),
);
throw errors;
@@ -337,13 +352,14 @@ export class CompilerError extends Error {
}
static throwInvalidConfig(
options: Omit<CompilerErrorDetailOptions, 'severity'>,
options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
): never {
const errors = new CompilerError();
errors.pushErrorDetail(
new CompilerErrorDetail({
...options,
severity: ErrorSeverity.InvalidConfig,
category: ErrorCategory.Config,
}),
);
throw errors;
@@ -407,6 +423,7 @@ export class CompilerError extends Error {
push(options: CompilerErrorDetailOptions): CompilerErrorDetail {
const detail = new CompilerErrorDetail({
category: options.category,
reason: options.reason,
description: options.description ?? null,
severity: options.severity,
@@ -507,3 +524,345 @@ function printErrorSummary(severity: ErrorSeverity, message: string): string {
}
return `${severityCategory}: ${message}`;
}
/**
* See getRuleForCategory() for how these map to ESLint rules
*/
export enum ErrorCategory {
// Checking for valid hooks usage (non conditional, non-first class, non reactive, etc)
Hooks = 'Hooks',
// Checking for no capitalized calls (not definitively an error, hence separating)
CapitalizedCalls = 'CapitalizedCalls',
// Checking for static components
StaticComponents = 'StaticComponents',
// Checking for valid usage of manual memoization
UseMemo = 'UseMemo',
// Checks that manual memoization is preserved
PreserveManualMemo = 'PreserveManualMemo',
// Checking for no mutations of props, hook arguments, hook return values
Immutability = 'Immutability',
// Checking for assignments to globals
Globals = 'Globals',
// Checking for valid usage of refs, ie no access during render
Refs = 'Refs',
// Checks for memoized effect deps
EffectDependencies = 'EffectDependencies',
// Checks for no setState in effect bodies
EffectSetState = 'EffectSetState',
EffectDerivationsOfState = 'EffectDerivationsOfState',
// Validates against try/catch in place of error boundaries
ErrorBoundaries = 'ErrorBoundaries',
// Checking for pure functions
Purity = 'Purity',
// Validates against setState in render
RenderSetState = 'RenderSetState',
// Internal invariants
Invariant = 'Invariant',
// Todos
Todo = 'Todo',
// Syntax errors
Syntax = 'Syntax',
// Checks for use of unsupported syntax
UnsupportedSyntax = 'UnsupportedSyntax',
// Config errors
Config = 'Config',
// Gating error
Gating = 'Gating',
// Suppressions
Suppression = 'Suppression',
// Issues with auto deps
AutomaticEffectDependencies = 'AutomaticEffectDependencies',
// Issues with `fire`
Fire = 'Fire',
// fbt-specific issues
FBT = 'FBT',
}
export type LintRule = {
// Stores the category the rule corresponds to, used to filter errors when reporting
category: ErrorCategory;
/**
* The "name" of the rule as it will be used by developers to enable/disable, eg
* "eslint-disable-nest line <name>"
*/
name: string;
/**
* A description of the rule that appears somewhere in ESLint. This does not affect
* how error messages are formatted
*/
description: string;
/**
* If true, this rule will automatically appear in the default, "recommended" ESLint
* rule set. Otherwise it will be part of an `allRules` export that developers can
* use to opt-in to showing output of all possible rules.
*
* NOTE: not all validations are enabled by default! Setting this flag only affects
* whether a given rule is part of the recommended set. The corresponding validation
* also should be enabled by default if you want the error to actually show up!
*/
recommended: boolean;
};
const RULE_NAME_PATTERN = /^[a-z]+(-[a-z]+)*$/;
export function getRuleForCategory(category: ErrorCategory): LintRule {
const rule = getRuleForCategoryImpl(category);
invariant(
RULE_NAME_PATTERN.test(rule.name),
`Invalid rule name, got '${rule.name}' but rules must match ${RULE_NAME_PATTERN.toString()}`,
);
return rule;
}
function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
switch (category) {
case ErrorCategory.AutomaticEffectDependencies: {
return {
category,
name: 'automatic-effect-dependencies',
description:
'Verifies that automatic effect dependencies are compiled if opted-in',
recommended: false,
};
}
case ErrorCategory.CapitalizedCalls: {
return {
category,
name: 'capitalized-calls',
description:
'Validates against calling capitalized functions/methods instead of using JSX',
recommended: false,
};
}
case ErrorCategory.Config: {
return {
category,
name: 'config',
description: 'Validates the compiler configuration options',
recommended: true,
};
}
case ErrorCategory.EffectDependencies: {
return {
category,
name: 'memoized-effect-dependencies',
description: 'Validates that effect dependencies are memoized',
recommended: false,
};
}
case ErrorCategory.EffectDerivationsOfState: {
return {
category,
name: 'no-deriving-state-in-effects',
description:
'Validates against deriving values from state in an effect',
recommended: false,
};
}
case ErrorCategory.EffectSetState: {
return {
category,
name: 'set-state-in-effect',
description:
'Validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance',
recommended: true,
};
}
case ErrorCategory.ErrorBoundaries: {
return {
category,
name: 'error-boundaries',
description:
'Validates usage of error boundaries instead of try/catch for errors in child components',
recommended: true,
};
}
case ErrorCategory.FBT: {
return {
category,
name: 'fbt',
description: 'Validates usage of fbt',
recommended: false,
};
}
case ErrorCategory.Fire: {
return {
category,
name: 'fire',
description: 'Validates usage of `fire`',
recommended: false,
};
}
case ErrorCategory.Gating: {
return {
category,
name: 'gating',
description:
'Validates configuration of [gating mode](https://react.dev/reference/react-compiler/gating)',
recommended: true,
};
}
case ErrorCategory.Globals: {
return {
category,
name: 'globals',
description:
'Validates against assignment/mutation of globals during render, part of ensuring that ' +
'[side effects must render outside of render](https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)',
recommended: true,
};
}
case ErrorCategory.Hooks: {
return {
category,
name: 'hooks',
description: 'Validates the rules of hooks',
/**
* TODO: the "Hooks" rule largely reimplements the "rules-of-hooks" non-compiler rule.
* We need to dedeupe these (moving the remaining bits into the compiler) and then enable
* this rule.
*/
recommended: false,
};
}
case ErrorCategory.Immutability: {
return {
category,
name: 'immutability',
description:
'Validates against mutating props, state, and other values that [are immutable](https://react.dev/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable)',
recommended: true,
};
}
case ErrorCategory.Invariant: {
return {
category,
name: 'invariant',
description: 'Internal invariants',
recommended: false,
};
}
case ErrorCategory.PreserveManualMemo: {
return {
category,
name: 'preserve-manual-memoization',
description:
'Validates that existing manual memoized is preserved by the compiler. ' +
'React Compiler will only compile components and hooks if its inference ' +
'[matches or exceeds the existing manual memoization](https://react.dev/learn/react-compiler/introduction#what-should-i-do-about-usememo-usecallback-and-reactmemo)',
recommended: true,
};
}
case ErrorCategory.Purity: {
return {
category,
name: 'purity',
description:
'Validates that [components/hooks are pure](https://react.dev/reference/rules/components-and-hooks-must-be-pure) by checking that they do not call known-impure functions',
recommended: true,
};
}
case ErrorCategory.Refs: {
return {
category,
name: 'refs',
description:
'Validates correct usage of refs, not reading/writing during render. See the "pitfalls" section in [`useRef()` usage](https://react.dev/reference/react/useRef#usage)',
recommended: true,
};
}
case ErrorCategory.RenderSetState: {
return {
category,
name: 'set-state-in-render',
description:
'Validates against setting state during render, which can trigger additional renders and potential infinite render loops',
recommended: true,
};
}
case ErrorCategory.StaticComponents: {
return {
category,
name: 'static-components',
description:
'Validates that components are static, not recreated every render. Components that are recreated dynamically can reset state and trigger excessive re-rendering',
recommended: true,
};
}
case ErrorCategory.Suppression: {
return {
category,
name: 'rule-suppression',
description: 'Validates against suppression of other rules',
recommended: false,
};
}
case ErrorCategory.Syntax: {
return {
category,
name: 'syntax',
description: 'Validates against invalid syntax',
recommended: false,
};
}
case ErrorCategory.Todo: {
return {
category,
name: 'todo',
description: 'Unimplemented features',
recommended: false,
};
}
case ErrorCategory.UnsupportedSyntax: {
return {
category,
name: 'unsupported-syntax',
description:
'Validates against syntax that we do not plan to support in React Compiler',
recommended: true,
};
}
case ErrorCategory.UseMemo: {
return {
category,
name: 'use-memo',
description:
'Validates usage of the useMemo() hook against common mistakes. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.',
recommended: true,
};
}
default: {
assertExhaustive(category, `Unsupported category ${category}`);
}
}
}
export const LintRules: Array<LintRule> = Object.keys(ErrorCategory).map(
category => getRuleForCategory(category as any),
);

View File

@@ -9,7 +9,7 @@ import {NodePath} from '@babel/core';
import * as t from '@babel/types';
import {Scope as BabelScope} from '@babel/traverse';
import {CompilerError, ErrorSeverity} from '../CompilerError';
import {CompilerError, ErrorCategory, ErrorSeverity} from '../CompilerError';
import {
EnvironmentConfig,
GeneratedSource,
@@ -38,6 +38,7 @@ export function validateRestrictedImports(
ImportDeclaration(importDeclPath) {
if (restrictedImports.has(importDeclPath.node.source.value)) {
error.push({
category: ErrorCategory.Todo,
severity: ErrorSeverity.Todo,
reason: 'Bailing out due to blocklisted import',
description: `Import from module ${importDeclPath.node.source.value}`,
@@ -205,6 +206,7 @@ export class ProgramContext {
}
const error = new CompilerError();
error.push({
category: ErrorCategory.Todo,
severity: ErrorSeverity.Todo,
reason: 'Encountered conflicting global in generated program',
description: `Conflict from local binding ${name}`,

View File

@@ -10,6 +10,7 @@ import * as t from '@babel/types';
import {
CompilerError,
CompilerErrorDetail,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {ExternalFunction, ReactFunctionType} from '../HIR/Environment';
@@ -105,6 +106,7 @@ function findDirectivesDynamicGating(
reason: `Dynamic gating directive is not a valid JavaScript identifier`,
description: `Found '${directive.value.value}'`,
severity: ErrorSeverity.InvalidReact,
category: ErrorCategory.Gating,
loc: directive.loc ?? null,
suggestions: null,
});
@@ -121,6 +123,7 @@ function findDirectivesDynamicGating(
.map(r => r.directive.value.value)
.join(', ')}]`,
severity: ErrorSeverity.InvalidReact,
category: ErrorCategory.Gating,
loc: result[0].directive.loc ?? null,
suggestions: null,
});
@@ -456,6 +459,7 @@ export function compileProgram(
reason:
'Unexpected compiled functions when module scope opt-out is present',
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Invariant,
loc: null,
}),
);
@@ -811,6 +815,7 @@ function shouldSkipCompilation(
description:
"When the 'sources' config options is specified, the React compiler will only compile files with a name",
severity: ErrorSeverity.InvalidConfig,
category: ErrorCategory.Config,
loc: null,
}),
);

View File

@@ -11,6 +11,7 @@ import {
CompilerDiagnostic,
CompilerError,
CompilerSuggestionOperation,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {assertExhaustive} from '../Utils/utils';
@@ -183,9 +184,10 @@ export function suppressionsToCompilerError(
}
error.pushDiagnostic(
CompilerDiagnostic.create({
category: reason,
reason: reason,
description: `React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression \`${suppressionRange.disableComment.value.trim()}\``,
severity: ErrorSeverity.InvalidReact,
category: ErrorCategory.Suppression,
suggestions: [
{
description: suggestion,

View File

@@ -13,7 +13,11 @@ import {getOrInsertWith} from '../Utils/utils';
import {Environment, GeneratedSource} from '../HIR';
import {DEFAULT_EXPORT} from '../HIR/Environment';
import {CompileProgramMetadata} from './Program';
import {CompilerDiagnostic, CompilerDiagnosticOptions} from '../CompilerError';
import {
CompilerDiagnostic,
CompilerDiagnosticOptions,
ErrorCategory,
} from '../CompilerError';
function throwInvalidReact(
options: Omit<CompilerDiagnosticOptions, 'severity'>,
@@ -92,7 +96,8 @@ function assertValidEffectImportReference(
*/
throwInvalidReact(
{
category:
category: ErrorCategory.AutomaticEffectDependencies,
reason:
'Cannot infer dependencies of this effect. This will break your build!',
description:
'To resolve, either pass a dependency array or fix reported compiler bailout diagnostics.' +
@@ -123,8 +128,8 @@ function assertValidFireImportReference(
);
throwInvalidReact(
{
category:
'[Fire] Untransformed reference to compiler-required feature.',
category: ErrorCategory.Fire,
reason: '[Fire] Untransformed reference to compiler-required feature.',
description:
'Either remove this `fire` call or ensure it is successfully transformed by the compiler' +
maybeErrorDiagnostic

View File

@@ -12,6 +12,7 @@ import {
CompilerDiagnostic,
CompilerError,
CompilerSuggestionOperation,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {Err, Ok, Result} from '../Utils/Result';
@@ -108,7 +109,8 @@ export function lower(
builder.errors.pushDiagnostic(
CompilerDiagnostic.create({
severity: ErrorSeverity.Invariant,
category: 'Could not find binding',
category: ErrorCategory.Invariant,
reason: 'Could not find binding',
description: `[BuildHIR] Could not find binding for param \`${param.node.name}\`.`,
}).withDetail({
kind: 'error',
@@ -172,7 +174,8 @@ export function lower(
builder.errors.pushDiagnostic(
CompilerDiagnostic.create({
severity: ErrorSeverity.Todo,
category: `Handle ${param.node.type} parameters`,
category: ErrorCategory.Todo,
reason: `Handle ${param.node.type} parameters`,
description: `[BuildHIR] Add support for ${param.node.type} parameters.`,
}).withDetail({
kind: 'error',
@@ -203,7 +206,8 @@ export function lower(
builder.errors.pushDiagnostic(
CompilerDiagnostic.create({
severity: ErrorSeverity.InvalidJS,
category: `Unexpected function body kind`,
category: ErrorCategory.Syntax,
reason: `Unexpected function body kind`,
description: `Expected function body to be an expression or a block statement, got \`${body.type}\`.`,
}).withDetail({
kind: 'error',
@@ -273,6 +277,7 @@ function lowerStatement(
reason:
'(BuildHIR::lowerStatement) Support ThrowStatement inside of try/catch',
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -460,6 +465,7 @@ function lowerStatement(
} else if (!binding.path.isVariableDeclarator()) {
builder.errors.push({
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
reason: 'Unsupported declaration type for hoisting',
description: `variable "${binding.identifier.name}" declared with ${binding.path.type}`,
suggestions: null,
@@ -469,6 +475,7 @@ function lowerStatement(
} else {
builder.errors.push({
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
reason: 'Handle non-const declarations for hoisting',
description: `variable "${binding.identifier.name}" declared with ${binding.kind}`,
suggestions: null,
@@ -549,6 +556,7 @@ function lowerStatement(
reason:
'(BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement',
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -621,6 +629,7 @@ function lowerStatement(
builder.errors.push({
reason: `(BuildHIR::lowerStatement) Handle empty test in ForStatement`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -772,6 +781,7 @@ function lowerStatement(
builder.errors.push({
reason: `Expected at most one \`default\` branch in a switch statement, this code should have failed to parse`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: case_.node.loc ?? null,
suggestions: null,
});
@@ -844,6 +854,7 @@ function lowerStatement(
builder.errors.push({
reason: `(BuildHIR::lowerStatement) Handle ${nodeKind} kinds in VariableDeclaration`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -872,6 +883,7 @@ function lowerStatement(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Invariant,
loc: id.node.loc ?? null,
suggestions: null,
});
@@ -889,6 +901,7 @@ function lowerStatement(
builder.errors.push({
reason: `Expect \`const\` declaration not to be reassigned`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: id.node.loc ?? null,
suggestions: [
{
@@ -936,6 +949,7 @@ function lowerStatement(
reason: `Expected variable declaration to be an identifier if no initializer was provided`,
description: `Got a \`${id.type}\``,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -1044,6 +1058,7 @@ function lowerStatement(
builder.errors.push({
reason: `(BuildHIR::lowerStatement) Handle for-await loops`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -1276,6 +1291,7 @@ function lowerStatement(
builder.errors.push({
reason: `(BuildHIR::lowerStatement) Handle TryStatement without a catch clause`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -1285,6 +1301,7 @@ function lowerStatement(
builder.errors.push({
reason: `(BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.node.loc ?? null,
suggestions: null,
});
@@ -1378,6 +1395,7 @@ function lowerStatement(
reason: `JavaScript 'with' syntax is not supported`,
description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,
severity: ErrorSeverity.UnsupportedJS,
category: ErrorCategory.UnsupportedSyntax,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
@@ -1398,6 +1416,7 @@ function lowerStatement(
reason: 'Inline `class` declarations are not supported',
description: `Move class declarations outside of components/hooks`,
severity: ErrorSeverity.UnsupportedJS,
category: ErrorCategory.UnsupportedSyntax,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
@@ -1427,6 +1446,7 @@ function lowerStatement(
reason:
'JavaScript `import` and `export` statements may only appear at the top level of a module',
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
@@ -1442,6 +1462,7 @@ function lowerStatement(
reason:
'TypeScript `namespace` statements may only appear at the top level of a module',
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: stmtPath.node.loc ?? null,
suggestions: null,
});
@@ -1520,6 +1541,7 @@ function lowerObjectPropertyKey(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: key.node.loc ?? null,
suggestions: null,
});
@@ -1545,6 +1567,7 @@ function lowerObjectPropertyKey(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: key.node.loc ?? null,
suggestions: null,
});
@@ -1602,6 +1625,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: valuePath.node.loc ?? null,
suggestions: null,
});
@@ -1628,6 +1652,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.node.kind} functions in ObjectExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: propertyPath.node.loc ?? null,
suggestions: null,
});
@@ -1649,6 +1674,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: propertyPath.node.loc ?? null,
suggestions: null,
});
@@ -1682,6 +1708,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: element.node.loc ?? null,
suggestions: null,
});
@@ -1702,6 +1729,7 @@ function lowerExpression(
reason: `Expected an expression as the \`new\` expression receiver (v8 intrinsics are not supported)`,
description: `Got a \`${calleePath.node.type}\``,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: calleePath.node.loc ?? null,
suggestions: null,
});
@@ -1728,6 +1756,7 @@ function lowerExpression(
builder.errors.push({
reason: `Expected Expression, got ${calleePath.type} in CallExpression (v8 intrinsics not supported). This error is likely caused by a bug in React Compiler. Please file an issue`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: calleePath.node.loc ?? null,
suggestions: null,
});
@@ -1762,6 +1791,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Expected Expression, got ${leftPath.type} lval in BinaryExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: leftPath.node.loc ?? null,
suggestions: null,
});
@@ -1774,6 +1804,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Pipe operator not supported`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: leftPath.node.loc ?? null,
suggestions: null,
});
@@ -1803,6 +1834,7 @@ function lowerExpression(
builder.errors.push({
reason: `Expected sequence expression to have at least one expression`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: expr.node.loc ?? null,
suggestions: null,
});
@@ -2015,6 +2047,7 @@ function lowerExpression(
reason: `(BuildHIR::lowerExpression) Unsupported syntax on the left side of an AssignmentExpression`,
description: `Expected an LVal, got: ${left.type}`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: left.node.loc ?? null,
suggestions: null,
});
@@ -2043,6 +2076,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${operator} operators in AssignmentExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: expr.node.loc ?? null,
suggestions: null,
});
@@ -2142,6 +2176,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Expected Identifier or MemberExpression, got ${expr.type} lval in AssignmentExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: expr.node.loc ?? null,
suggestions: null,
});
@@ -2181,6 +2216,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${attribute.type} attributes in JSXElement`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: attribute.node.loc ?? null,
suggestions: null,
});
@@ -2194,6 +2230,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name \`${propName}\``,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: namePath.node.loc ?? null,
suggestions: null,
});
@@ -2224,6 +2261,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${valueExpr.type} attribute values in JSXElement`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: valueExpr.node?.loc ?? null,
suggestions: null,
});
@@ -2234,6 +2272,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${expression.type} expressions in JSXExpressionContainer within JSXElement`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: valueExpr.node.loc ?? null,
suggestions: null,
});
@@ -2291,7 +2330,8 @@ function lowerExpression(
if (locations.length > 1) {
CompilerError.throwDiagnostic({
severity: ErrorSeverity.Todo,
category: 'Support duplicate fbt tags',
category: ErrorCategory.FBT,
reason: 'Support duplicate fbt tags',
description: `Support \`<${tagName}>\` tags with multiple \`<${tagName}:${name}>\` values`,
details: locations.map(loc => {
return {
@@ -2352,6 +2392,7 @@ function lowerExpression(
reason:
'(BuildHIR::lowerExpression) Handle tagged template with interpolations',
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2370,6 +2411,7 @@ function lowerExpression(
reason:
'(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value',
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2392,6 +2434,7 @@ function lowerExpression(
builder.errors.push({
reason: `Unexpected quasi and subexpression lengths in template literal`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2402,6 +2445,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle TSType in TemplateLiteral.`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2444,6 +2488,7 @@ function lowerExpression(
builder.errors.push({
reason: `Only object properties can be deleted`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: expr.node.loc ?? null,
suggestions: [
{
@@ -2459,6 +2504,7 @@ function lowerExpression(
builder.errors.push({
reason: `Throw expressions are not supported`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: expr.node.loc ?? null,
suggestions: [
{
@@ -2580,6 +2626,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2588,6 +2635,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle UpdateExpression to variables captured within lambdas.`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2608,6 +2656,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Found an invalid UpdateExpression without a previously reported error`,
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Invariant,
loc: exprLoc,
suggestions: null,
});
@@ -2617,6 +2666,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Support UpdateExpression where argument is a global`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprLoc,
suggestions: null,
});
@@ -2672,6 +2722,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2681,6 +2732,7 @@ function lowerExpression(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -2978,6 +3030,7 @@ function lowerReorderableExpression(
builder.errors.push({
reason: `(BuildHIR::node.lowerReorderableExpression) Expression type \`${expr.type}\` cannot be safely reordered`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: expr.node.loc ?? null,
suggestions: null,
});
@@ -3174,6 +3227,7 @@ function lowerArguments(
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: argPath.node.loc ?? null,
suggestions: null,
});
@@ -3209,6 +3263,7 @@ function lowerMemberExpression(
builder.errors.push({
reason: `(BuildHIR::lowerMemberExpression) Handle ${propertyNode.type} property`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: propertyNode.node.loc ?? null,
suggestions: null,
});
@@ -3230,6 +3285,7 @@ function lowerMemberExpression(
builder.errors.push({
reason: `(BuildHIR::lowerMemberExpression) Expected Expression, got ${propertyNode.type} property`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: propertyNode.node.loc ?? null,
suggestions: null,
});
@@ -3289,6 +3345,7 @@ function lowerJsxElementName(
reason: `Expected JSXNamespacedName to have no colons in the namespace or name`,
description: `Got \`${namespace}\` : \`${name}\``,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -3303,6 +3360,7 @@ function lowerJsxElementName(
builder.errors.push({
reason: `(BuildHIR::lowerJsxElementName) Handle ${exprPath.type} tags`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -3401,6 +3459,7 @@ function lowerJsxElement(
builder.errors.push({
reason: `(BuildHIR::lowerJsxElement) Unhandled JsxElement, got: ${exprPath.type}`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -3588,6 +3647,7 @@ function lowerIdentifier(
description:
'Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler',
severity: ErrorSeverity.UnsupportedJS,
category: ErrorCategory.UnsupportedSyntax,
loc: exprPath.node.loc ?? null,
suggestions: null,
});
@@ -3644,6 +3704,7 @@ function lowerIdentifierForAssignment(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Invariant,
loc: path.node.loc ?? null,
suggestions: null,
});
@@ -3656,6 +3717,7 @@ function lowerIdentifierForAssignment(
builder.errors.push({
reason: `Cannot reassign a \`const\` variable`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: path.node.loc ?? null,
description:
binding.identifier.name != null
@@ -3713,6 +3775,7 @@ function lowerAssignment(
builder.errors.push({
reason: `Expected \`const\` declaration not to be reassigned`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: lvalue.node.loc ?? null,
suggestions: null,
});
@@ -3727,6 +3790,7 @@ function lowerAssignment(
builder.errors.push({
reason: `Unexpected context variable kind`,
severity: ErrorSeverity.InvalidJS,
category: ErrorCategory.Syntax,
loc: lvalue.node.loc ?? null,
suggestions: null,
});
@@ -3798,6 +3862,7 @@ function lowerAssignment(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in MemberExpression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: property.node.loc ?? null,
suggestions: null,
});
@@ -3810,6 +3875,7 @@ function lowerAssignment(
reason:
'(BuildHIR::lowerAssignment) Expected private name to appear as a non-computed property',
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: property.node.loc ?? null,
suggestions: null,
});
@@ -3875,6 +3941,7 @@ function lowerAssignment(
} else if (identifier.kind === 'Global') {
builder.errors.push({
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
reason:
'Expected reassignment of globals to enable forceTemporaries',
loc: element.node.loc ?? GeneratedSource,
@@ -3914,6 +3981,7 @@ function lowerAssignment(
} else if (identifier.kind === 'Global') {
builder.errors.push({
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
reason:
'Expected reassignment of globals to enable forceTemporaries',
loc: element.node.loc ?? GeneratedSource,
@@ -3987,6 +4055,7 @@ function lowerAssignment(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ObjectPattern`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: argument.node.loc ?? null,
suggestions: null,
});
@@ -4018,6 +4087,7 @@ function lowerAssignment(
} else if (identifier.kind === 'Global') {
builder.errors.push({
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
reason:
'Expected reassignment of globals to enable forceTemporaries',
loc: property.node.loc ?? GeneratedSource,
@@ -4035,6 +4105,7 @@ function lowerAssignment(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in ObjectPattern`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: property.node.loc ?? null,
suggestions: null,
});
@@ -4044,6 +4115,7 @@ function lowerAssignment(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: property.node.loc ?? null,
suggestions: null,
});
@@ -4058,6 +4130,7 @@ function lowerAssignment(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Expected object property value to be an LVal, got: ${element.type}`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: element.node.loc ?? null,
suggestions: null,
});
@@ -4080,6 +4153,7 @@ function lowerAssignment(
} else if (identifier.kind === 'Global') {
builder.errors.push({
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
reason:
'Expected reassignment of globals to enable forceTemporaries',
loc: element.node.loc ?? GeneratedSource,
@@ -4229,6 +4303,7 @@ function lowerAssignment(
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle ${lvaluePath.type} assignments`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: lvaluePath.node.loc ?? null,
suggestions: null,
});

View File

@@ -7,7 +7,7 @@
import {Binding, NodePath} from '@babel/traverse';
import * as t from '@babel/types';
import {CompilerError, ErrorSeverity} from '../CompilerError';
import {CompilerError, ErrorCategory, ErrorSeverity} from '../CompilerError';
import {Environment} from './Environment';
import {
BasicBlock,
@@ -310,7 +310,8 @@ export default class HIRBuilder {
if (node.name === 'fbt') {
CompilerError.throwDiagnostic({
severity: ErrorSeverity.Todo,
category: 'Support local variables named `fbt`',
category: ErrorCategory.FBT,
reason: 'Support local variables named `fbt`',
description:
'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported',
details: [

View File

@@ -983,16 +983,16 @@ export function printAliasingEffect(effect: AliasingEffect): string {
case 'MutateConditionally':
case 'MutateTransitive':
case 'MutateTransitiveConditionally': {
return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}`;
return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}${effect.kind === 'Mutate' && effect.reason?.kind === 'AssignCurrentProperty' ? ' (assign `.current`)' : ''}`;
}
case 'MutateFrozen': {
return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.category)}`;
return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
}
case 'MutateGlobal': {
return `MutateGlobal ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.category)}`;
return `MutateGlobal ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
}
case 'Impure': {
return `Impure ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.category)}`;
return `Impure ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
}
case 'Render': {
return `Render ${printPlaceForAliasEffect(effect.place)}`;

View File

@@ -231,7 +231,7 @@ export function hashEffect(effect: AliasingEffect): string {
effect.kind,
effect.place.identifier.id,
effect.error.severity,
effect.error.category,
effect.error.reason,
effect.error.description,
printSourceLocation(effect.error.primaryLocation() ?? GeneratedSource),
].join(':');

View File

@@ -11,6 +11,7 @@ import {
ErrorSeverity,
SourceLocation,
} from '..';
import {ErrorCategory} from '../CompilerError';
import {
CallExpression,
Effect,
@@ -300,8 +301,9 @@ function extractManualMemoizationArgs(
if (fnPlace == null) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.UseMemo,
severity: ErrorSeverity.InvalidReact,
category: `Expected a callback function to be passed to ${kind}`,
reason: `Expected a callback function to be passed to ${kind}`,
description: `Expected a callback function to be passed to ${kind}`,
suggestions: null,
}).withDetail({
@@ -315,8 +317,9 @@ function extractManualMemoizationArgs(
if (fnPlace.kind === 'Spread' || depsListPlace?.kind === 'Spread') {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.UseMemo,
severity: ErrorSeverity.InvalidReact,
category: `Unexpected spread argument to ${kind}`,
reason: `Unexpected spread argument to ${kind}`,
description: `Unexpected spread argument to ${kind}`,
suggestions: null,
}).withDetail({
@@ -335,8 +338,9 @@ function extractManualMemoizationArgs(
if (maybeDepsList == null) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.UseMemo,
severity: ErrorSeverity.InvalidReact,
category: `Expected the dependency list for ${kind} to be an array literal`,
reason: `Expected the dependency list for ${kind} to be an array literal`,
description: `Expected the dependency list for ${kind} to be an array literal`,
suggestions: null,
}).withDetail({
@@ -353,8 +357,9 @@ function extractManualMemoizationArgs(
if (maybeDep == null) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.UseMemo,
severity: ErrorSeverity.InvalidReact,
category: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
reason: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
suggestions: null,
}).withDetail({
@@ -459,7 +464,8 @@ export function dropManualMemoization(
errors.pushDiagnostic(
CompilerDiagnostic.create({
severity: ErrorSeverity.InvalidReact,
category: 'useMemo() callbacks must return a value',
category: ErrorCategory.UseMemo,
reason: 'useMemo() callbacks must return a value',
description: `This ${
manualMemo.loadInstr.value.kind === 'PropertyLoad'
? 'React.useMemo'
@@ -498,8 +504,9 @@ export function dropManualMemoization(
if (!sidemap.functions.has(fnPlace.identifier.id)) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.UseMemo,
severity: ErrorSeverity.InvalidReact,
category: `Expected the first argument to be an inline function expression`,
reason: `Expected the first argument to be an inline function expression`,
description: `Expected the first argument to be an inline function expression`,
suggestions: [],
}).withDetail({

View File

@@ -69,6 +69,7 @@ import {
hashEffect,
MutationReason,
} from './AliasingEffects';
import {ErrorCategory} from '../CompilerError';
const DEBUG = false;
@@ -452,8 +453,9 @@ function applySignature(
? `\`${effect.value.identifier.name.value}\``
: 'value';
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
severity: ErrorSeverity.InvalidReact,
category: 'This value cannot be modified',
reason: 'This value cannot be modified',
description: `${reason}.`,
}).withDetail({
kind: 'error',
@@ -1036,8 +1038,9 @@ function applyEffect(
effect.value.identifier.declarationId,
);
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot access variable before it is declared',
reason: 'Cannot access variable before it is declared',
description: `${variable ?? 'This variable'} is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.`,
});
if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
@@ -1075,8 +1078,9 @@ function applyEffect(
? `\`${effect.value.identifier.name.value}\``
: 'value';
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
severity: ErrorSeverity.InvalidReact,
category: 'This value cannot be modified',
reason: 'This value cannot be modified',
description: `${reason}.`,
}).withDetail({
kind: 'error',
@@ -2033,8 +2037,9 @@ function computeSignatureForInstruction(
kind: 'MutateGlobal',
place: value.value,
error: CompilerDiagnostic.create({
category: ErrorCategory.Globals,
severity: ErrorSeverity.InvalidReact,
category:
reason:
'Cannot reassign variables declared outside of the component/hook',
description: `Variable ${variable} is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)`,
}).withDetail({
@@ -2132,8 +2137,9 @@ function computeEffectsForLegacySignature(
kind: 'Impure',
place: receiver,
error: CompilerDiagnostic.create({
category: ErrorCategory.Purity,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot call impure function during render',
reason: 'Cannot call impure function during render',
description:
(signature.canonicalName != null
? `\`${signature.canonicalName}\` is an impure function. `

View File

@@ -27,7 +27,7 @@ import {
} from '../HIR/visitors';
import {assertExhaustive, getOrInsertWith} from '../Utils/utils';
import {Err, Ok, Result} from '../Utils/Result';
import {AliasingEffect} from './AliasingEffects';
import {AliasingEffect, MutationReason} from './AliasingEffects';
/**
* This pass builds an abstract model of the heap and interprets the effects of the
@@ -101,6 +101,7 @@ export function inferMutationAliasingRanges(
transitive: boolean;
kind: MutationKind;
place: Place;
reason: MutationReason | null;
}> = [];
const renders: Array<{index: number; place: Place}> = [];
@@ -176,6 +177,7 @@ export function inferMutationAliasingRanges(
effect.kind === 'MutateTransitive'
? MutationKind.Definite
: MutationKind.Conditional,
reason: null,
place: effect.value,
});
} else if (
@@ -190,6 +192,7 @@ export function inferMutationAliasingRanges(
effect.kind === 'Mutate'
? MutationKind.Definite
: MutationKind.Conditional,
reason: effect.kind === 'Mutate' ? (effect.reason ?? null) : null,
place: effect.value,
});
} else if (
@@ -241,6 +244,7 @@ export function inferMutationAliasingRanges(
mutation.transitive,
mutation.kind,
mutation.place.loc,
mutation.reason,
errors,
);
}
@@ -267,6 +271,7 @@ export function inferMutationAliasingRanges(
functionEffects.push({
kind: 'Mutate',
value: {...place, loc: node.local.loc},
reason: node.mutationReason,
});
}
}
@@ -507,6 +512,7 @@ export function inferMutationAliasingRanges(
true,
MutationKind.Conditional,
into.loc,
null,
ignoredErrors,
);
for (const from of tracked) {
@@ -580,6 +586,7 @@ type Node = {
transitive: {kind: MutationKind; loc: SourceLocation} | null;
local: {kind: MutationKind; loc: SourceLocation} | null;
lastMutated: number;
mutationReason: MutationReason | null;
value:
| {kind: 'Object'}
| {kind: 'Phi'}
@@ -599,6 +606,7 @@ class AliasingState {
transitive: null,
local: null,
lastMutated: 0,
mutationReason: null,
value,
});
}
@@ -697,6 +705,7 @@ class AliasingState {
transitive: boolean,
startKind: MutationKind,
loc: SourceLocation,
reason: MutationReason | null,
errors: CompilerError,
): void {
const seen = new Map<Identifier, MutationKind>();
@@ -717,6 +726,7 @@ class AliasingState {
if (node == null) {
continue;
}
node.mutationReason ??= reason;
node.lastMutated = Math.max(node.lastMutated, index);
if (end != null) {
node.id.mutableRange.end = makeInstructionId(

View File

@@ -42,6 +42,7 @@ import {
mapInstructionValueOperands,
mapTerminalOperands,
} from '../HIR/visitors';
import {ErrorCategory} from '../CompilerError';
type InlinedJsxDeclarationMap = Map<
DeclarationId,
@@ -83,6 +84,7 @@ export function inlineJsxTransform(
kind: 'CompileDiagnostic',
fnLoc: null,
detail: {
category: ErrorCategory.Todo,
reason: 'JSX Inlining is not supported on value blocks',
loc: instr.loc,
},

View File

@@ -13,7 +13,7 @@ import {
pruneUnusedLabels,
renameVariables,
} from '.';
import {CompilerError, ErrorSeverity} from '../CompilerError';
import {CompilerError, ErrorCategory, ErrorSeverity} from '../CompilerError';
import {Environment, ExternalFunction} from '../HIR';
import {
ArrayPattern,
@@ -2185,6 +2185,7 @@ function codegenInstructionValue(
(declarator.id as t.Identifier).name
}'`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: declarator.loc ?? null,
suggestions: null,
});
@@ -2193,6 +2194,7 @@ function codegenInstructionValue(
cx.errors.push({
reason: `(CodegenReactiveFunction::codegenInstructionValue) Handle conversion of ${stmt.type} to expression`,
severity: ErrorSeverity.Todo,
category: ErrorCategory.Todo,
loc: stmt.loc ?? null,
suggestions: null,
});

View File

@@ -42,6 +42,7 @@ import {
import {eachInstructionOperand} from '../HIR/visitors';
import {printSourceLocationLine} from '../HIR/PrintHIR';
import {USE_FIRE_FUNCTION_NAME} from '../HIR/Environment';
import {ErrorCategory} from '../CompilerError';
/*
* TODO(jmbrown):
@@ -133,6 +134,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
loc: value.loc,
description: null,
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Invariant,
reason: '[InsertFire] No LoadGlobal found for useEffect call',
suggestions: null,
});
@@ -179,6 +181,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
description:
'You must use an array literal for an effect dependency array when that effect uses `fire()`',
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Fire,
reason: CANNOT_COMPILE_FIRE,
suggestions: null,
});
@@ -189,6 +192,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
description:
'You must use an array literal for an effect dependency array when that effect uses `fire()`',
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Fire,
reason: CANNOT_COMPILE_FIRE,
suggestions: null,
});
@@ -223,6 +227,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
loc: value.loc,
description: null,
severity: ErrorSeverity.Invariant,
category: ErrorCategory.Invariant,
reason:
'[InsertFire] No loadLocal found for fire call argument',
suggestions: null,
@@ -246,6 +251,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
description:
'`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed',
severity: ErrorSeverity.InvalidReact,
category: ErrorCategory.Fire,
reason: CANNOT_COMPILE_FIRE,
suggestions: null,
});
@@ -264,6 +270,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
loc: value.loc,
description,
severity: ErrorSeverity.InvalidReact,
category: ErrorCategory.Fire,
reason: CANNOT_COMPILE_FIRE,
suggestions: null,
});
@@ -395,6 +402,7 @@ function ensureNoRemainingCalleeCaptures(
this effect or not used with a fire() call at all. ${calleeName} was used with fire() on line \
${printSourceLocationLine(calleeInfo.fireLoc)} in this effect`,
severity: ErrorSeverity.InvalidReact,
category: ErrorCategory.Fire,
reason: CANNOT_COMPILE_FIRE,
suggestions: null,
});
@@ -411,6 +419,7 @@ function ensureNoMoreFireUses(fn: HIRFunction, context: Context): void {
context.pushError({
loc: place.identifier.loc,
description: 'Cannot use `fire` outside of a useEffect function',
category: ErrorCategory.Fire,
severity: ErrorSeverity.Invariant,
reason: CANNOT_COMPILE_FIRE,
suggestions: null,

View File

@@ -90,10 +90,13 @@ export function Ok<T>(val: T): OkImpl<T> {
}
class OkImpl<T> implements Result<T, never> {
constructor(private val: T) {}
#val: T;
constructor(val: T) {
this.#val = val;
}
map<U>(fn: (val: T) => U): Result<U, never> {
return new OkImpl(fn(this.val));
return new OkImpl(fn(this.#val));
}
mapErr<F>(_fn: (val: never) => F): Result<T, F> {
@@ -101,15 +104,15 @@ class OkImpl<T> implements Result<T, never> {
}
mapOr<U>(_fallback: U, fn: (val: T) => U): U {
return fn(this.val);
return fn(this.#val);
}
mapOrElse<U>(_fallback: () => U, fn: (val: T) => U): U {
return fn(this.val);
return fn(this.#val);
}
andThen<U>(fn: (val: T) => Result<U, never>): Result<U, never> {
return fn(this.val);
return fn(this.#val);
}
and<U>(res: Result<U, never>): Result<U, never> {
@@ -133,30 +136,30 @@ class OkImpl<T> implements Result<T, never> {
}
expect(_msg: string): T {
return this.val;
return this.#val;
}
expectErr(msg: string): never {
throw new Error(`${msg}: ${this.val}`);
throw new Error(`${msg}: ${this.#val}`);
}
unwrap(): T {
return this.val;
return this.#val;
}
unwrapOr(_fallback: T): T {
return this.val;
return this.#val;
}
unwrapOrElse(_fallback: (val: never) => T): T {
return this.val;
return this.#val;
}
unwrapErr(): never {
if (this.val instanceof Error) {
throw this.val;
if (this.#val instanceof Error) {
throw this.#val;
}
throw new Error(`Can't unwrap \`Ok\` to \`Err\`: ${this.val}`);
throw new Error(`Can't unwrap \`Ok\` to \`Err\`: ${this.#val}`);
}
}
@@ -165,14 +168,17 @@ export function Err<E>(val: E): ErrImpl<E> {
}
class ErrImpl<E> implements Result<never, E> {
constructor(private val: E) {}
#val: E;
constructor(val: E) {
this.#val = val;
}
map<U>(_fn: (val: never) => U): Result<U, E> {
return this;
}
mapErr<F>(fn: (val: E) => F): Result<never, F> {
return new ErrImpl(fn(this.val));
return new ErrImpl(fn(this.#val));
}
mapOr<U>(fallback: U, _fn: (val: never) => U): U {
@@ -196,7 +202,7 @@ class ErrImpl<E> implements Result<never, E> {
}
orElse<F>(fn: (val: E) => ErrImpl<F>): Result<never, F> {
return fn(this.val);
return fn(this.#val);
}
isOk(): this is OkImpl<never> {
@@ -208,18 +214,18 @@ class ErrImpl<E> implements Result<never, E> {
}
expect(msg: string): never {
throw new Error(`${msg}: ${this.val}`);
throw new Error(`${msg}: ${this.#val}`);
}
expectErr(_msg: string): E {
return this.val;
return this.#val;
}
unwrap(): never {
if (this.val instanceof Error) {
throw this.val;
if (this.#val instanceof Error) {
throw this.#val;
}
throw new Error(`Can't unwrap \`Err\` to \`Ok\`: ${this.val}`);
throw new Error(`Can't unwrap \`Err\` to \`Ok\`: ${this.#val}`);
}
unwrapOr<T>(fallback: T): T {
@@ -227,10 +233,10 @@ class ErrImpl<E> implements Result<never, E> {
}
unwrapOrElse<T>(fallback: (val: E) => T): T {
return fallback(this.val);
return fallback(this.#val);
}
unwrapErr(): E {
return this.val;
return this.#val;
}
}

View File

@@ -9,6 +9,7 @@ import * as t from '@babel/types';
import {
CompilerError,
CompilerErrorDetail,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
@@ -124,6 +125,7 @@ export function validateHooksUsage(
recordError(
place.loc,
new CompilerErrorDetail({
category: ErrorCategory.Hooks,
description: null,
reason,
loc: place.loc,
@@ -140,6 +142,7 @@ export function validateHooksUsage(
recordError(
place.loc,
new CompilerErrorDetail({
category: ErrorCategory.Hooks,
description: null,
reason:
'Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values',
@@ -157,6 +160,7 @@ export function validateHooksUsage(
recordError(
place.loc,
new CompilerErrorDetail({
category: ErrorCategory.Hooks,
description: null,
reason:
'Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks',
@@ -424,7 +428,7 @@ export function validateHooksUsage(
}
for (const [, error] of errorsByPlace) {
errors.push(error);
errors.pushErrorDetail(error);
}
return errors.asResult();
}
@@ -448,6 +452,7 @@ function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
if (hookKind != null) {
errors.pushErrorDetail(
new CompilerErrorDetail({
category: ErrorCategory.Hooks,
severity: ErrorSeverity.InvalidReact,
reason:
'Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)',

View File

@@ -6,6 +6,7 @@
*/
import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
import {ErrorCategory} from '../CompilerError';
import {HIRFunction, IdentifierId, Place} from '../HIR';
import {
eachInstructionLValue,
@@ -36,8 +37,9 @@ export function validateLocalsNotReassignedAfterRender(fn: HIRFunction): void {
: 'variable';
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot reassign variable after render completes',
reason: 'Cannot reassign variable after render completes',
description: `Reassigning ${variable} after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.`,
}).withDetail({
kind: 'error',
@@ -91,8 +93,9 @@ function getContextReassignment(
: 'variable';
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot reassign variable in async function',
reason: 'Cannot reassign variable in async function',
description:
'Reassigning a variable in an async function can cause inconsistent behavior on subsequent renders. Consider using state instead',
}).withDetail({

View File

@@ -6,6 +6,7 @@
*/
import {CompilerError, ErrorSeverity} from '..';
import {ErrorCategory} from '../CompilerError';
import {
Identifier,
Instruction,
@@ -108,6 +109,7 @@ class Visitor extends ReactiveFunctionVisitor<CompilerError> {
isUnmemoized(deps.identifier, this.scopes))
) {
state.push({
category: ErrorCategory.EffectDependencies,
reason:
'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',
description: null,

View File

@@ -6,6 +6,7 @@
*/
import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..';
import {ErrorCategory} from '../CompilerError';
import {HIRFunction, IdentifierId} from '../HIR';
import {DEFAULT_GLOBALS} from '../HIR/Globals';
import {Result} from '../Utils/Result';
@@ -56,6 +57,7 @@ export function validateNoCapitalizedCalls(
const calleeName = capitalLoadGlobals.get(calleeIdentifier);
if (calleeName != null) {
CompilerError.throwInvalidReact({
category: ErrorCategory.CapitalizedCalls,
reason,
description: `${calleeName} may be a component.`,
loc: value.loc,
@@ -79,6 +81,7 @@ export function validateNoCapitalizedCalls(
const propertyName = capitalizedProperties.get(propertyIdentifier);
if (propertyName != null) {
errors.push({
category: ErrorCategory.CapitalizedCalls,
severity: ErrorSeverity.InvalidReact,
reason,
description: `${propertyName} may be a component.`,

View File

@@ -6,6 +6,7 @@
*/
import {CompilerError, ErrorSeverity, SourceLocation} from '..';
import {ErrorCategory} from '../CompilerError';
import {
ArrayExpression,
BlockId,
@@ -219,6 +220,7 @@ function validateEffect(
for (const loc of setStateLocations) {
errors.push({
category: ErrorCategory.EffectDerivationsOfState,
reason:
'Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)',
description: null,

View File

@@ -6,6 +6,7 @@
*/
import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
import {ErrorCategory} from '../CompilerError';
import {
HIRFunction,
IdentifierId,
@@ -64,8 +65,9 @@ export function validateNoFreezingKnownMutableFunctions(
: 'a local variable';
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot modify local variables after render completes',
reason: 'Cannot modify local variables after render completes',
description: `This argument is a function which may reassign or mutate ${variable} after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.`,
})
.withDetail({

View File

@@ -6,6 +6,7 @@
*/
import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
import {ErrorCategory} from '../CompilerError';
import {HIRFunction} from '../HIR';
import {getFunctionCallSignature} from '../Inference/InferMutationAliasingEffects';
import {Result} from '../Utils/Result';
@@ -36,7 +37,8 @@ export function validateNoImpureFunctionsInRender(
if (signature != null && signature.impure === true) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: 'Cannot call impure function during render',
category: ErrorCategory.Purity,
reason: 'Cannot call impure function during render',
description:
(signature.canonicalName != null
? `\`${signature.canonicalName}\` is an impure function. `

View File

@@ -6,6 +6,7 @@
*/
import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
import {ErrorCategory} from '../CompilerError';
import {BlockId, HIRFunction} from '../HIR';
import {Result} from '../Utils/Result';
import {retainWhere} from '../Utils/utils';
@@ -36,8 +37,9 @@ export function validateNoJSXInTryStatement(
case 'JsxFragment': {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.ErrorBoundaries,
severity: ErrorSeverity.InvalidReact,
category: 'Avoid constructing JSX within try/catch',
reason: 'Avoid constructing JSX within try/catch',
description: `React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)`,
}).withDetail({
kind: 'error',

View File

@@ -8,6 +8,7 @@
import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {
@@ -468,8 +469,9 @@ function validateNoRefAccessInRenderImpl(
didError = true;
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Refs,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot access refs during render',
reason: 'Cannot access refs during render',
description: ERROR_DESCRIPTION,
}).withDetail({
kind: 'error',
@@ -731,8 +733,9 @@ function guardCheck(errors: CompilerError, operand: Place, env: Env): void {
if (env.get(operand.identifier.id)?.kind === 'Guard') {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Refs,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot access refs during render',
reason: 'Cannot access refs during render',
description: ERROR_DESCRIPTION,
}).withDetail({
kind: 'error',
@@ -755,8 +758,9 @@ function validateNoRefValueAccess(
) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Refs,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot access refs during render',
reason: 'Cannot access refs during render',
description: ERROR_DESCRIPTION,
}).withDetail({
kind: 'error',
@@ -781,8 +785,9 @@ function validateNoRefPassedToFunction(
) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Refs,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot access refs during render',
reason: 'Cannot access refs during render',
description: ERROR_DESCRIPTION,
}).withDetail({
kind: 'error',
@@ -803,8 +808,9 @@ function validateNoRefUpdate(
if (type?.kind === 'Ref' || type?.kind === 'RefValue') {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Refs,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot access refs during render',
reason: 'Cannot access refs during render',
description: ERROR_DESCRIPTION,
}).withDetail({
kind: 'error',
@@ -824,8 +830,9 @@ function validateNoDirectRefValueAccess(
if (type?.kind === 'RefValue') {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Refs,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot access refs during render',
reason: 'Cannot access refs during render',
description: ERROR_DESCRIPTION,
}).withDetail({
kind: 'error',

View File

@@ -8,6 +8,7 @@
import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {
@@ -96,7 +97,8 @@ export function validateNoSetStateInEffects(
if (setState !== undefined) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category:
category: ErrorCategory.EffectSetState,
reason:
'Calling setState synchronously within an effect can trigger cascading renders',
description:
'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +

View File

@@ -8,6 +8,7 @@
import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
@@ -128,7 +129,8 @@ function validateNoSetStateInRenderImpl(
if (activeManualMemoId !== null) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category:
category: ErrorCategory.RenderSetState,
reason:
'Calling setState from useMemo may trigger an infinite loop',
description:
'Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)',
@@ -143,7 +145,8 @@ function validateNoSetStateInRenderImpl(
} else if (unconditionalBlocks.has(block.id)) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category:
category: ErrorCategory.RenderSetState,
reason:
'Calling setState during render may trigger an infinite loop',
description:
'Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)',
@@ -152,7 +155,7 @@ function validateNoSetStateInRenderImpl(
}).withDetail({
kind: 'error',
loc: callee.loc,
message: 'Found setState() within useMemo()',
message: 'Found setState() in render',
}),
);
}

View File

@@ -8,6 +8,7 @@
import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {
@@ -281,8 +282,9 @@ function validateInferredDep(
}
errorState.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.PreserveManualMemo,
severity: ErrorSeverity.CannotPreserveMemoization,
category:
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. ',
@@ -535,8 +537,9 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
) {
state.errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.PreserveManualMemo,
severity: ErrorSeverity.CannotPreserveMemoization,
category:
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. ',
@@ -583,8 +586,9 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
if (isUnmemoized(identifier, this.scopes)) {
state.errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.PreserveManualMemo,
severity: ErrorSeverity.CannotPreserveMemoization,
category:
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. ',

View File

@@ -8,6 +8,7 @@
import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {HIRFunction, IdentifierId, SourceLocation} from '../HIR';
@@ -65,8 +66,9 @@ export function validateStaticComponents(
if (location != null) {
error.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.StaticComponents,
severity: ErrorSeverity.InvalidReact,
category: 'Cannot create components during render',
reason: 'Cannot create components during render',
description: `Components created during render will reset their state each time they are created. Declare components outside of render. `,
})
.withDetail({

View File

@@ -8,6 +8,7 @@
import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR';
@@ -74,8 +75,9 @@ export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
: firstParam.place.loc;
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.UseMemo,
severity: ErrorSeverity.InvalidReact,
category: 'useMemo() callbacks may not accept parameters',
reason: 'useMemo() callbacks may not accept parameters',
description:
'useMemo() callbacks are called by React to cache calculations across re-renders. They should not take parameters. Instead, directly reference the props, state, or local variables needed for the computation.',
suggestions: null,
@@ -90,8 +92,9 @@ export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
if (body.loweredFunc.func.async || body.loweredFunc.func.generator) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.UseMemo,
severity: ErrorSeverity.InvalidReact,
category:
reason:
'useMemo() callbacks may not be async or generator functions',
description:
'useMemo() callbacks are called once and must synchronously return a value.',

View File

@@ -0,0 +1,37 @@
## Input
```javascript
// Fixture to test that we show a hint to name as `ref` or `-Ref` when attempting
// to assign .current inside an effect
function Component({foo}) {
useEffect(() => {
foo.current = true;
}, [foo]);
}
```
## Error
```
Found 1 error:
Error: This value cannot be modified
Modifying component props or hook arguments is not allowed. Consider using a local variable instead.
error.assign-ref-in-effect-hint.ts:5:4
3 | function Component({foo}) {
4 | useEffect(() => {
> 5 | foo.current = true;
| ^^^ `foo` cannot be modified
6 | }, [foo]);
7 | }
8 |
Hint: If this value is a Ref (value returned by `useRef()`), rename the variable to end in "Ref".
```

View File

@@ -0,0 +1,7 @@
// Fixture to test that we show a hint to name as `ref` or `-Ref` when attempting
// to assign .current inside an effect
function Component({foo}) {
useEffect(() => {
foo.current = true;
}, [foo]);
}

View File

@@ -38,6 +38,8 @@ error.invalid-mutate-context-in-callback.ts:12:4
13 | };
14 | return <div onClick={onClick} />;
15 | }
Hint: If this value is a Ref (value returned by `useRef()`), rename the variable to end in "Ref".
```

View File

@@ -29,7 +29,7 @@ error.invalid-unconditional-set-state-in-render.ts:6:2
4 | const aliased = setX;
5 |
> 6 | setX(1);
| ^^^^ Found setState() within useMemo()
| ^^^^ Found setState() in render
7 | aliased(2);
8 |
9 | return x;
@@ -42,7 +42,7 @@ error.invalid-unconditional-set-state-in-render.ts:7:2
5 |
6 | setX(1);
> 7 | aliased(2);
| ^^^^^^^ Found setState() within useMemo()
| ^^^^^^^ Found setState() in render
8 |
9 | return x;
10 | }

View File

@@ -32,7 +32,7 @@ error.unconditional-set-state-in-render-after-loop-break.ts:11:2
9 | }
10 | }
> 11 | setState(true);
| ^^^^^^^^ Found setState() within useMemo()
| ^^^^^^^^ Found setState() in render
12 | return state;
13 | }
14 |

View File

@@ -27,7 +27,7 @@ error.unconditional-set-state-in-render-after-loop.ts:6:2
4 | for (const _ of props) {
5 | }
> 6 | setState(true);
| ^^^^^^^^ Found setState() within useMemo()
| ^^^^^^^^ Found setState() in render
7 | return state;
8 | }
9 |

View File

@@ -32,7 +32,7 @@ error.unconditional-set-state-in-render-with-loop-throw.ts:11:2
9 | }
10 | }
> 11 | setState(true);
| ^^^^^^^^ Found setState() within useMemo()
| ^^^^^^^^ Found setState() in render
12 | return state;
13 | }
14 |

View File

@@ -30,7 +30,7 @@ error.unconditional-set-state-lambda.ts:8:2
6 | setX(1);
7 | };
> 8 | foo();
| ^^^ Found setState() within useMemo()
| ^^^ Found setState() in render
9 |
10 | return [x];
11 | }

View File

@@ -38,7 +38,7 @@ error.unconditional-set-state-nested-function-expressions.ts:16:2
14 | bar();
15 | };
> 16 | baz();
| ^^^ Found setState() within useMemo()
| ^^^ Found setState() in render
17 |
18 | return [x];
19 | }

View File

@@ -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":{"severity":"CannotPreserveMemoization","category":"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"}]}}}
{"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

View File

@@ -38,7 +38,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":3,"column":0,"index":86},"end":{"line":7,"column":1,"index":190},"filename":"dynamic-gating-invalid-multiple.ts"},"detail":{"options":{"reason":"Multiple dynamic gating directives found","description":"Expected a single directive but found [use memo if(getTrue), use memo if(getFalse)]","severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":4,"column":2,"index":105},"end":{"line":4,"column":25,"index":128},"filename":"dynamic-gating-invalid-multiple.ts"}}}}
{"kind":"CompileError","fnLoc":{"start":{"line":3,"column":0,"index":86},"end":{"line":7,"column":1,"index":190},"filename":"dynamic-gating-invalid-multiple.ts"},"detail":{"options":{"category":"Gating","reason":"Multiple dynamic gating directives found","description":"Expected a single directive but found [use memo if(getTrue), use memo if(getFalse)]","severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":4,"column":2,"index":105},"end":{"line":4,"column":25,"index":128},"filename":"dynamic-gating-invalid-multiple.ts"}}}}
```
### Eval output

View File

@@ -48,7 +48,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":149},"end":{"line":12,"column":1,"index":404},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":365},"end":{"line":10,"column":5,"index":368},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":149},"end":{"line":12,"column":1,"index":404},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"options":{"category":"Immutability","severity":"InvalidReact","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":365},"end":{"line":10,"column":5,"index":368},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":314},"end":{"line":9,"column":49,"index":361},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":336},"end":{"line":9,"column":27,"index":339},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":149},"end":{"line":12,"column":1,"index":404},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"category":"Refs","severity":"InvalidReact","reason":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":237},"end":{"line":8,"column":50,"index":285},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":259},"end":{"line":8,"column":30,"index":265},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":214},"end":{"line":9,"column":5,"index":217},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"category":"Immutability","severity":"InvalidReact","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":214},"end":{"line":9,"column":5,"index":217},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":159},"end":{"line":8,"column":14,"index":210},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":190},"end":{"line":7,"column":16,"index":193},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -54,7 +54,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":195},"end":{"line":14,"column":1,"index":409},"filename":"retry-no-emit.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook.","details":[{"kind":"error","loc":{"start":{"line":12,"column":2,"index":372},"end":{"line":12,"column":6,"index":376},"filename":"retry-no-emit.ts","identifierName":"arr2"},"message":"value cannot be modified"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":195},"end":{"line":14,"column":1,"index":409},"filename":"retry-no-emit.ts"},"detail":{"options":{"category":"Immutability","severity":"InvalidReact","reason":"This value cannot be modified","description":"Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook.","details":[{"kind":"error","loc":{"start":{"line":12,"column":2,"index":372},"end":{"line":12,"column":6,"index":376},"filename":"retry-no-emit.ts","identifierName":"arr2"},"message":"value cannot be modified"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":248},"end":{"line":8,"column":46,"index":292},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":8,"column":31,"index":277},"end":{"line":8,"column":34,"index":280},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":11,"column":2,"index":316},"end":{"line":11,"column":54,"index":368},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":11,"column":25,"index":339},"end":{"line":11,"column":29,"index":343},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":25,"index":339},"end":{"line":11,"column":29,"index":343},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":35,"index":349},"end":{"line":11,"column":42,"index":356},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":195},"end":{"line":14,"column":1,"index":409},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}

View File

@@ -65,7 +65,7 @@ function Component(props) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":11,"column":11,"index":222},"end":{"line":11,"column":32,"index":243},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"ErrorBoundaries","severity":"InvalidReact","reason":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":11,"column":11,"index":222},"end":{"line":11,"column":32,"index":243},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":17,"column":1,"index":298},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -42,7 +42,7 @@ function Component(props) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":5,"column":9,"index":104},"end":{"line":5,"column":16,"index":111},"filename":"invalid-jsx-in-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"ErrorBoundaries","severity":"InvalidReact","reason":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":5,"column":9,"index":104},"end":{"line":5,"column":16,"index":111},"filename":"invalid-jsx-in-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":49},"end":{"line":10,"column":1,"index":160},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -65,7 +65,7 @@ function _temp(s) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"category":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":13,"column":4,"index":265},"end":{"line":13,"column":5,"index":266},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":13,"column":4,"index":265},"end":{"line":13,"column":5,"index":266},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":16,"column":1,"index":293},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -45,7 +45,7 @@ function _temp(s) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"category":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":7,"column":4,"index":180},"end":{"line":7,"column":12,"index":188},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":7,"column":4,"index":180},"end":{"line":7,"column":12,"index":188},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":10,"column":1,"index":225},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -48,7 +48,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":181},"end":{"line":12,"column":1,"index":436},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":397},"end":{"line":10,"column":5,"index":400},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":181},"end":{"line":12,"column":1,"index":436},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"options":{"category":"Immutability","severity":"InvalidReact","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":397},"end":{"line":10,"column":5,"index":400},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":346},"end":{"line":9,"column":49,"index":393},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":368},"end":{"line":9,"column":27,"index":371},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":181},"end":{"line":12,"column":1,"index":436},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"category":"Refs","severity":"InvalidReact","reason":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":269},"end":{"line":8,"column":50,"index":317},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":291},"end":{"line":8,"column":30,"index":297},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":143},"end":{"line":11,"column":1,"index":274},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":246},"end":{"line":9,"column":5,"index":249},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":143},"end":{"line":11,"column":1,"index":274},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"category":"Immutability","severity":"InvalidReact","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect().","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":246},"end":{"line":9,"column":5,"index":249},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":191},"end":{"line":8,"column":14,"index":242},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":222},"end":{"line":7,"column":16,"index":225},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":143},"end":{"line":11,"column":1,"index":274},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -54,7 +54,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":227},"end":{"line":14,"column":1,"index":441},"filename":"retry-no-emit.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook.","details":[{"kind":"error","loc":{"start":{"line":12,"column":2,"index":404},"end":{"line":12,"column":6,"index":408},"filename":"retry-no-emit.ts","identifierName":"arr2"},"message":"value cannot be modified"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":227},"end":{"line":14,"column":1,"index":441},"filename":"retry-no-emit.ts"},"detail":{"options":{"category":"Immutability","severity":"InvalidReact","reason":"This value cannot be modified","description":"Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook.","details":[{"kind":"error","loc":{"start":{"line":12,"column":2,"index":404},"end":{"line":12,"column":6,"index":408},"filename":"retry-no-emit.ts","identifierName":"arr2"},"message":"value cannot be modified"}]}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":280},"end":{"line":8,"column":46,"index":324},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":8,"column":31,"index":309},"end":{"line":8,"column":34,"index":312},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":11,"column":2,"index":348},"end":{"line":11,"column":54,"index":400},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":11,"column":25,"index":371},"end":{"line":11,"column":29,"index":375},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":25,"index":371},"end":{"line":11,"column":29,"index":375},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":35,"index":381},"end":{"line":11,"column":42,"index":388},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":227},"end":{"line":14,"column":1,"index":441},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}

View File

@@ -50,7 +50,7 @@ function Example(props) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":9,"column":10,"index":202},"end":{"line":9,"column":19,"index":211},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":5,"column":16,"index":124},"end":{"line":5,"column":33,"index":141},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","severity":"InvalidReact","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":9,"column":10,"index":202},"end":{"line":9,"column":19,"index":211},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":5,"column":16,"index":124},"end":{"line":5,"column":33,"index":141},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":10,"column":1,"index":217},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"fnName":"Example","memoSlots":3,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -32,7 +32,7 @@ function Example(props) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":120},"end":{"line":4,"column":19,"index":129},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":37,"index":108},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","severity":"InvalidReact","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":120},"end":{"line":4,"column":19,"index":129},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":37,"index":108},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":5,"column":1,"index":135},"filename":"invalid-dynamically-construct-component-in-render.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -37,7 +37,7 @@ function Example(props) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":6,"column":10,"index":130},"end":{"line":6,"column":19,"index":139},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":2,"index":73},"end":{"line":5,"column":3,"index":119},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","severity":"InvalidReact","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":6,"column":10,"index":130},"end":{"line":6,"column":19,"index":139},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":2,"index":73},"end":{"line":5,"column":3,"index":119},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":7,"column":1,"index":145},"filename":"invalid-dynamically-constructed-component-function.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -41,7 +41,7 @@ function Example(props) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":118},"end":{"line":4,"column":19,"index":127},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":35,"index":106},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","severity":"InvalidReact","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":118},"end":{"line":4,"column":19,"index":127},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":35,"index":106},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":5,"column":1,"index":133},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"fnName":"Example","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -32,7 +32,7 @@ function Example(props) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":125},"end":{"line":4,"column":19,"index":134},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":42,"index":113},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","severity":"InvalidReact","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render. ","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":125},"end":{"line":4,"column":19,"index":134},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":42,"index":113},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"The component is created during render here"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":5,"column":1,"index":140},"filename":"invalid-dynamically-constructed-component-new.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -12,9 +12,11 @@ export {
CompilerDiagnostic,
CompilerSuggestionOperation,
ErrorSeverity,
LintRules,
type CompilerErrorDetailOptions,
type CompilerDiagnosticOptions,
type CompilerDiagnosticDetail,
type LintRule,
} from './CompilerError';
export {
compileFn as compile,

View File

@@ -0,0 +1,39 @@
/**
* 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 {
ErrorCategory,
getRuleForCategory,
} from 'babel-plugin-react-compiler/src/CompilerError';
import {normalizeIndent, testRule, makeTestCaseError} from './shared-utils';
import {allRules} from '../src/rules/ReactCompilerRule';
testRule(
'no impure function calls rule',
allRules[getRuleForCategory(ErrorCategory.Purity).name],
{
valid: [],
invalid: [
{
name: 'Known impure function calls are caught',
code: normalizeIndent`
function Component() {
const date = Date.now();
const now = performance.now();
const rand = Math.random();
return <Foo date={date} now={now} rand={rand} />;
}
`,
errors: [
makeTestCaseError('Cannot call impure function during render'),
makeTestCaseError('Cannot call impure function during render'),
makeTestCaseError('Cannot call impure function during render'),
],
},
],
},
);

View File

@@ -0,0 +1,100 @@
/**
* 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 {
ErrorCategory,
getRuleForCategory,
} from 'babel-plugin-react-compiler/src/CompilerError';
import {normalizeIndent, makeTestCaseError, testRule} from './shared-utils';
import {allRules} from '../src/rules/ReactCompilerRule';
testRule(
'rules-of-hooks',
allRules[getRuleForCategory(ErrorCategory.Hooks).name],
{
valid: [
{
name: 'Basic example',
code: normalizeIndent`
function Component() {
useHook();
return <div>Hello world</div>;
}
`,
},
{
name: 'Violation with Flow suppression',
code: `
// Valid since error already suppressed with flow.
function useHook() {
if (cond) {
// $FlowFixMe[react-rule-hook]
useConditionalHook();
}
}
`,
},
{
// OK because invariants are only meant for the compiler team's consumption
name: '[Invariant] Defined after use',
code: normalizeIndent`
function Component(props) {
let y = function () {
m(x);
};
let x = { a };
m(x);
return y;
}
`,
},
{
name: "Classes don't throw",
code: normalizeIndent`
class Foo {
#bar() {}
}
`,
},
],
invalid: [
{
name: 'Simple violation',
code: normalizeIndent`
function useConditional() {
if (cond) {
useConditionalHook();
}
}
`,
errors: [
makeTestCaseError(
'Hooks must always be called in a consistent order',
),
],
},
{
name: 'Multiple diagnostics within the same function are surfaced',
code: normalizeIndent`
function useConditional() {
cond ?? useConditionalHook();
props.cond && useConditionalHook();
return <div>Hello world</div>;
}`,
errors: [
makeTestCaseError(
'Hooks must always be called in a consistent order',
),
makeTestCaseError(
'Hooks must always be called in a consistent order',
),
],
},
],
},
);

View File

@@ -0,0 +1,38 @@
/**
* 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 {
ErrorCategory,
getRuleForCategory,
} from 'babel-plugin-react-compiler/src/CompilerError';
import {normalizeIndent, testRule, makeTestCaseError} from './shared-utils';
import {allRules} from '../src/rules/ReactCompilerRule';
testRule(
'no ambiguous JSX rule',
allRules[getRuleForCategory(ErrorCategory.ErrorBoundaries).name],
{
valid: [],
invalid: [
{
name: 'JSX in try blocks are warned against',
code: normalizeIndent`
function Component(props) {
let el;
try {
el = <Child />;
} catch {
return null;
}
return el;
}
`,
errors: [makeTestCaseError('Avoid constructing JSX within try/catch')],
},
],
},
);

View File

@@ -0,0 +1,71 @@
/**
* 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 {
ErrorCategory,
getRuleForCategory,
} from 'babel-plugin-react-compiler/src/CompilerError';
import {normalizeIndent, makeTestCaseError, testRule} from './shared-utils';
import {allRules} from '../src/rules/ReactCompilerRule';
testRule(
'no-capitalized-calls',
allRules[getRuleForCategory(ErrorCategory.CapitalizedCalls).name],
{
valid: [],
invalid: [
{
name: 'Simple violation',
code: normalizeIndent`
import Child from './Child';
function Component() {
return <>
{Child()}
</>;
}
`,
errors: [
makeTestCaseError(
'Capitalized functions are reserved for components',
),
],
},
{
name: 'Method call violation',
code: normalizeIndent`
import myModule from './MyModule';
function Component() {
return <>
{myModule.Child()}
</>;
}
`,
errors: [
makeTestCaseError(
'Capitalized functions are reserved for components',
),
],
},
{
name: 'Multiple diagnostics within the same function are surfaced',
code: normalizeIndent`
import Child1 from './Child1';
import MyModule from './MyModule';
function Component() {
return <>
{Child1()}
{MyModule.Child2()}
</>;
}`,
errors: [
makeTestCaseError(
'Capitalized functions are reserved for components',
),
],
},
],
},
);

View File

@@ -0,0 +1,34 @@
/**
* 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 {
ErrorCategory,
getRuleForCategory,
} from 'babel-plugin-react-compiler/src/CompilerError';
import {normalizeIndent, testRule, makeTestCaseError} from './shared-utils';
import {allRules} from '../src/rules/ReactCompilerRule';
testRule(
'no ref access in render rule',
allRules[getRuleForCategory(ErrorCategory.Refs).name],
{
valid: [],
invalid: [
{
name: 'validate against simple ref access in render',
code: normalizeIndent`
function Component(props) {
const ref = useRef(null);
const value = ref.current;
return value;
}
`,
errors: [makeTestCaseError('Cannot access refs during render')],
},
],
},
);

View File

@@ -0,0 +1,58 @@
/**
* 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 {NoUnusedDirectivesRule} from '../src/rules/ReactCompilerRule';
import {normalizeIndent, testRule} from './shared-utils';
testRule('no unused directives rule', NoUnusedDirectivesRule, {
valid: [],
invalid: [
{
name: "Unused 'use no forget' directive is reported when no errors are present on components",
code: normalizeIndent`
function Component() {
'use no forget';
return <div>Hello world</div>
}
`,
errors: [
{
message: "Unused 'use no forget' directive",
suggestions: [
{
output:
// yuck
'\nfunction Component() {\n \n return <div>Hello world</div>\n}\n',
},
],
},
],
},
{
name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks",
code: normalizeIndent`
function notacomponent() {
'use no forget';
return 1 + 1;
}
`,
errors: [
{
message: "Unused 'use no forget' directive",
suggestions: [
{
output:
// yuck
'\nfunction notacomponent() {\n \n return 1 + 1;\n}\n',
},
],
},
],
},
],
});

View File

@@ -0,0 +1,158 @@
/**
* 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 {
ErrorCategory,
getRuleForCategory,
} from 'babel-plugin-react-compiler/src/CompilerError';
import {
normalizeIndent,
testRule,
makeTestCaseError,
TestRecommendedRules,
} from './shared-utils';
import {allRules} from '../src/rules/ReactCompilerRule';
testRule('plugin-recommended', TestRecommendedRules, {
valid: [
{
name: 'Basic example with component syntax',
code: normalizeIndent`
export default component HelloWorld(
text: string = 'Hello!',
onClick: () => void,
) {
return <div onClick={onClick}>{text}</div>;
}
`,
},
{
// OK because invariants are only meant for the compiler team's consumption
name: '[Invariant] Defined after use',
code: normalizeIndent`
function Component(props) {
let y = function () {
m(x);
};
let x = { a };
m(x);
return y;
}
`,
},
{
name: "Classes don't throw",
code: normalizeIndent`
class Foo {
#bar() {}
}
`,
},
],
invalid: [
{
// TODO: actually return multiple diagnostics in this case
name: 'Multiple diagnostic kinds from the same function are surfaced',
code: normalizeIndent`
import Child from './Child';
function Component() {
const result = cond ?? useConditionalHook();
return <>
{Child(result)}
</>;
}
`,
errors: [
makeTestCaseError('Hooks must always be called in a consistent order'),
],
},
{
name: 'Multiple diagnostics within the same file are surfaced',
code: normalizeIndent`
function useConditional1() {
'use memo';
return cond ?? useConditionalHook();
}
function useConditional2(props) {
'use memo';
return props.cond && useConditionalHook();
}`,
errors: [
makeTestCaseError('Hooks must always be called in a consistent order'),
makeTestCaseError('Hooks must always be called in a consistent order'),
],
},
{
name: "'use no forget' does not disable eslint rule",
code: normalizeIndent`
let count = 0;
function Component() {
'use no forget';
return cond ?? useConditionalHook();
}
`,
errors: [
makeTestCaseError('Hooks must always be called in a consistent order'),
],
},
{
name: 'Multiple non-fatal useMemo diagnostics are surfaced',
code: normalizeIndent`
import {useMemo, useState} from 'react';
function Component({item, cond}) {
const [prevItem, setPrevItem] = useState(item);
const [state, setState] = useState(0);
useMemo(() => {
if (cond) {
setPrevItem(item);
setState(0);
}
}, [cond, item, init]);
return <Child x={state} />;
}`,
errors: [makeTestCaseError('useMemo() callbacks must return a value')],
},
{
name: 'Pipeline errors are reported',
code: normalizeIndent`
import useMyEffect from 'useMyEffect';
import {AUTODEPS} from 'react';
function Component({a}) {
'use no memo';
useMyEffect(() => console.log(a.b), AUTODEPS);
return <div>Hello world</div>;
}
`,
options: [
{
environment: {
inferEffectDependencies: [
{
function: {
source: 'useMyEffect',
importSpecifierName: 'default',
},
autodepsIndex: 1,
},
],
},
},
],
errors: [
{
message: /Cannot infer dependencies of this effect/,
},
],
},
],
});

View File

@@ -1,287 +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 {ErrorSeverity} from 'babel-plugin-react-compiler/src';
import {RuleTester as ESLintTester} from 'eslint';
import ReactCompilerRule from '../src/rules/ReactCompilerRule';
/**
* A string template tag that removes padding from the left side of multi-line strings
* @param {Array} strings array of code strings (only one expected)
*/
function normalizeIndent(strings: TemplateStringsArray): string {
const codeLines = strings[0].split('\n');
const leftPadding = codeLines[1].match(/\s+/)![0];
return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
}
type CompilerTestCases = {
valid: ESLintTester.ValidTestCase[];
invalid: ESLintTester.InvalidTestCase[];
};
const tests: CompilerTestCases = {
valid: [
{
name: 'Basic example',
code: normalizeIndent`
function foo(x, y) {
if (x) {
return foo(false, y);
}
return [y * 10];
}
`,
},
{
name: 'Violation with Flow suppression',
code: `
// Valid since error already suppressed with flow.
function useHookWithHook() {
if (cond) {
// $FlowFixMe[react-rule-hook]
useConditionalHook();
}
}
`,
},
{
name: 'Basic example with component syntax',
code: normalizeIndent`
export default component HelloWorld(
text: string = 'Hello!',
onClick: () => void,
) {
return <div onClick={onClick}>{text}</div>;
}
`,
},
{
name: 'Unsupported syntax',
code: normalizeIndent`
function foo(x) {
var y = 1;
return y * x;
}
`,
},
{
// OK because invariants are only meant for the compiler team's consumption
name: '[Invariant] Defined after use',
code: normalizeIndent`
function Component(props) {
let y = function () {
m(x);
};
let x = { a };
m(x);
return y;
}
`,
},
{
name: "Classes don't throw",
code: normalizeIndent`
class Foo {
#bar() {}
}
`,
},
],
invalid: [
{
name: 'Reportable levels can be configured',
options: [{reportableLevels: new Set([ErrorSeverity.Todo])}],
code: normalizeIndent`
function Foo(x) {
var y = 1;
return <div>{y * x}</div>;
}`,
errors: [
{
message: /Handle var kinds in VariableDeclaration/,
},
],
},
{
name: '[InvalidReact] ESlint suppression',
// Indentation is intentionally weird so it doesn't add extra whitespace
code: normalizeIndent`
function Component(props) {
// eslint-disable-next-line react-hooks/rules-of-hooks
return <div>{props.foo}</div>;
}`,
errors: [
{
message: /React Compiler has skipped optimizing this component/,
suggestions: [
{
output: normalizeIndent`
function Component(props) {
return <div>{props.foo}</div>;
}`,
},
],
},
{
message:
"Definition for rule 'react-hooks/rules-of-hooks' was not found.",
},
],
},
{
name: 'Multiple diagnostics are surfaced',
options: [
{
reportableLevels: new Set([
ErrorSeverity.Todo,
ErrorSeverity.InvalidReact,
]),
},
],
code: normalizeIndent`
function Foo(x) {
var y = 1;
return <div>{y * x}</div>;
}
function Bar(props) {
props.a.b = 2;
return <div>{props.c}</div>
}`,
errors: [
{
message: /Handle var kinds in VariableDeclaration/,
},
{
message: /Modifying component props or hook arguments is not allowed/,
},
],
},
{
name: 'Test experimental/unstable report all bailouts mode',
options: [
{
reportableLevels: new Set([ErrorSeverity.InvalidReact]),
__unstable_donotuse_reportAllBailouts: true,
},
],
code: normalizeIndent`
function Foo(x) {
var y = 1;
return <div>{y * x}</div>;
}`,
errors: [
{
message: /Handle var kinds in VariableDeclaration/,
},
],
},
{
name: "'use no forget' does not disable eslint rule",
code: normalizeIndent`
let count = 0;
function Component() {
'use no forget';
count = count + 1;
return <div>Hello world {count}</div>
}
`,
errors: [
{
message:
/Cannot reassign variables declared outside of the component\/hook/,
},
],
},
{
name: "Unused 'use no forget' directive is reported when no errors are present on components",
code: normalizeIndent`
function Component() {
'use no forget';
return <div>Hello world</div>
}
`,
errors: [
{
message: "Unused 'use no forget' directive",
suggestions: [
{
output:
// yuck
'\nfunction Component() {\n \n return <div>Hello world</div>\n}\n',
},
],
},
],
},
{
name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks",
code: normalizeIndent`
function notacomponent() {
'use no forget';
return 1 + 1;
}
`,
errors: [
{
message: "Unused 'use no forget' directive",
suggestions: [
{
output:
// yuck
'\nfunction notacomponent() {\n \n return 1 + 1;\n}\n',
},
],
},
],
},
{
name: 'Pipeline errors are reported',
code: normalizeIndent`
import useMyEffect from 'useMyEffect';
import {AUTODEPS} from 'react';
function Component({a}) {
'use no memo';
useMyEffect(() => console.log(a.b), AUTODEPS);
return <div>Hello world</div>;
}
`,
options: [
{
environment: {
inferEffectDependencies: [
{
function: {
source: 'useMyEffect',
importSpecifierName: 'default',
},
autodepsIndex: 1,
},
],
},
},
],
errors: [
{
message: /Cannot infer dependencies of this effect/,
},
],
},
],
};
const eslintTester = new ESLintTester({
parser: require.resolve('hermes-eslint'),
parserOptions: {
ecmaVersion: 2015,
sourceType: 'module',
enableExperimentalComponentSyntax: true,
},
});
eslintTester.run('react-compiler', ReactCompilerRule, tests);

View File

@@ -6,22 +6,11 @@
*/
import {RuleTester} from 'eslint';
import ReactCompilerRule from '../src/rules/ReactCompilerRule';
/**
* A string template tag that removes padding from the left side of multi-line strings
* @param {Array} strings array of code strings (only one expected)
*/
function normalizeIndent(strings: TemplateStringsArray): string {
const codeLines = strings[0].split('\n');
const leftPadding = codeLines[1].match(/\s+/)[0];
return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
}
type CompilerTestCases = {
valid: RuleTester.ValidTestCase[];
invalid: RuleTester.InvalidTestCase[];
};
import {
CompilerTestCases,
normalizeIndent,
TestRecommendedRules,
} from './shared-utils';
const tests: CompilerTestCases = {
valid: [
@@ -70,6 +59,7 @@ const tests: CompilerTestCases = {
};
const eslintTester = new RuleTester({
// @ts-ignore[2353] - outdated types
parser: require.resolve('@typescript-eslint/parser'),
});
eslintTester.run('react-compiler', ReactCompilerRule, tests);
eslintTester.run('react-compiler', TestRecommendedRules, tests);

View File

@@ -0,0 +1,76 @@
import {RuleTester as ESLintTester, Rule} from 'eslint';
import {type ErrorCategory} from 'babel-plugin-react-compiler/src/CompilerError';
import escape from 'regexp.escape';
import {configs} from '../src/index';
import {allRules} from '../src/rules/ReactCompilerRule';
/**
* A string template tag that removes padding from the left side of multi-line strings
* @param {Array} strings array of code strings (only one expected)
*/
export function normalizeIndent(strings: TemplateStringsArray): string {
const codeLines = strings[0].split('\n');
const leftPadding = codeLines[1].match(/\s+/)![0];
return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
}
export type CompilerTestCases = {
valid: ESLintTester.ValidTestCase[];
invalid: ESLintTester.InvalidTestCase[];
};
export function makeTestCaseError(reason: string): ESLintTester.TestCaseError {
return {
message: new RegExp(escape(reason)),
};
}
export function testRule(
name: string,
rule: Rule.RuleModule,
tests: {
valid: ESLintTester.ValidTestCase[];
invalid: ESLintTester.InvalidTestCase[];
},
): void {
const eslintTester = new ESLintTester({
// @ts-ignore[2353] - outdated types
parser: require.resolve('hermes-eslint'),
parserOptions: {
ecmaVersion: 2015,
sourceType: 'module',
enableExperimentalComponentSyntax: true,
},
});
eslintTester.run(name, rule, tests);
}
/**
* Aggregates all recommended rules from the plugin.
*/
export const TestRecommendedRules: Rule.RuleModule = {
meta: {
type: 'problem',
docs: {
description: 'Disallow capitalized function calls',
category: 'Possible Errors',
recommended: true,
},
// validation is done at runtime with zod
schema: [{type: 'object', additionalProperties: true}],
},
create(context) {
for (const rule of Object.values(
configs.recommended.plugins['react-compiler'].rules,
)) {
const listener = rule.create(context);
if (Object.entries(listener).length !== 0) {
throw new Error('TODO: handle rules that return listeners to eslint');
}
}
return {};
},
};
test('no test', () => {});

View File

@@ -24,11 +24,13 @@
"@babel/preset-typescript": "^7.18.6",
"@babel/types": "^7.26.0",
"@types/eslint": "^8.56.12",
"@types/jest": "^30.0.0",
"@types/node": "^20.2.5",
"babel-jest": "^29.0.3",
"eslint": "8.57.0",
"hermes-eslint": "^0.25.1",
"jest": "^29.5.0"
"jest": "^29.5.0",
"regexp.escape": "^2.0.1"
},
"engines": {
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0"

View File

@@ -5,29 +5,26 @@
* LICENSE file in the root directory of this source tree.
*/
import ReactCompilerRule from './rules/ReactCompilerRule';
import {allRules, recommendedRules} from './rules/ReactCompilerRule';
const meta = {
name: 'eslint-plugin-react-compiler',
};
const rules = {
'react-compiler': ReactCompilerRule,
};
const configs = {
recommended: {
plugins: {
'react-compiler': {
rules: {
'react-compiler': ReactCompilerRule,
},
rules: allRules,
},
},
rules: {
'react-compiler/react-compiler': 'error' as const,
},
rules: Object.fromEntries(
Object.keys(recommendedRules).map(ruleName => [
'react-compiler/' + ruleName,
'error',
]),
) as Record<string, 'error' | 'warn'>,
},
};
export {configs, rules, meta};
export {configs, allRules as rules, meta};

View File

@@ -5,43 +5,23 @@
* LICENSE file in the root directory of this source tree.
*/
import {transformFromAstSync} from '@babel/core';
// @ts-expect-error: no types available
import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods';
import type {SourceLocation as BabelSourceLocation} from '@babel/types';
import BabelPluginReactCompiler, {
CompilerDiagnostic,
import {
CompilerDiagnosticOptions,
CompilerErrorDetail,
CompilerErrorDetailOptions,
CompilerSuggestionOperation,
ErrorSeverity,
parsePluginOptions,
validateEnvironmentConfig,
OPT_OUT_DIRECTIVES,
type PluginOptions,
} from 'babel-plugin-react-compiler/src';
import {Logger, LoggerEvent} from 'babel-plugin-react-compiler/src/Entrypoint';
import type {Rule} from 'eslint';
import {Statement} from 'estree';
import * as HermesParser from 'hermes-parser';
import runReactCompiler, {RunCacheEntry} from '../shared/RunReactCompiler';
import {
LintRules,
type LintRule,
} from 'babel-plugin-react-compiler/src/CompilerError';
function assertExhaustive(_: never, errorMsg: string): never {
throw new Error(errorMsg);
}
const DEFAULT_REPORTABLE_LEVELS = new Set([
ErrorSeverity.InvalidReact,
ErrorSeverity.InvalidJS,
]);
let reportableLevels = DEFAULT_REPORTABLE_LEVELS;
function isReportableDiagnostic(
detail: CompilerErrorDetail | CompilerDiagnostic,
): boolean {
return reportableLevels.has(detail.severity);
}
function makeSuggestions(
detail: CompilerErrorDetailOptions | CompilerDiagnosticOptions,
): Array<Rule.SuggestionReportDescriptor> {
@@ -95,28 +75,95 @@ function makeSuggestions(
return suggest;
}
const COMPILER_OPTIONS: Partial<PluginOptions> = {
noEmit: true,
panicThreshold: 'none',
// Don't emit errors on Flow suppressions--Flow already gave a signal
flowSuppressions: false,
environment: validateEnvironmentConfig({
validateRefAccessDuringRender: true,
validateNoSetStateInRender: true,
validateNoSetStateInEffects: true,
validateNoJSXInTryStatements: true,
validateNoImpureFunctionsInRender: true,
validateStaticComponents: true,
validateNoFreezingKnownMutableFunctions: true,
validateNoVoidUseMemo: true,
}),
};
function getReactCompilerResult(context: Rule.RuleContext): RunCacheEntry {
// Compat with older versions of eslint
const sourceCode = context.sourceCode ?? context.getSourceCode();
const filename = context.filename ?? context.getFilename();
const userOpts = context.options[0] ?? {};
const rule: Rule.RuleModule = {
const results = runReactCompiler({
sourceCode,
filename,
userOpts,
});
return results;
}
function hasFlowSuppression(
program: RunCacheEntry,
nodeLoc: BabelSourceLocation,
suppressions: Array<string>,
): boolean {
for (const commentNode of program.flowSuppressions) {
if (
suppressions.includes(commentNode.code) &&
commentNode.line === nodeLoc.start.line - 1
) {
return true;
}
}
return false;
}
function makeRule(rule: LintRule): Rule.RuleModule {
const create = (context: Rule.RuleContext): Rule.RuleListener => {
const result = getReactCompilerResult(context);
for (const event of result.events) {
if (event.kind === 'CompileError') {
const detail = event.detail;
if (detail.category === rule.category) {
const loc = detail.primaryLocation();
if (loc == null || typeof loc === 'symbol') {
continue;
}
if (
hasFlowSuppression(result, loc, [
'react-rule-hook',
'react-rule-unsafe-ref',
])
) {
// If Flow already caught this error, we don't need to report it again.
continue;
}
/*
* TODO: if multiple rules report the same linter category,
* we should deduplicate them with a "reported" set
*/
context.report({
message: detail.printErrorMessage(result.sourceCode, {
eslint: true,
}),
loc,
suggest: makeSuggestions(detail.options),
});
}
}
}
return {};
};
return {
meta: {
type: 'problem',
docs: {
description: rule.description,
recommended: rule.recommended,
},
fixable: 'code',
hasSuggestions: true,
// validation is done at runtime with zod
schema: [{type: 'object', additionalProperties: true}],
},
create,
};
}
export const NoUnusedDirectivesRule: Rule.RuleModule = {
meta: {
type: 'problem',
type: 'suggestion',
docs: {
description: 'Surfaces diagnostics from React Forget',
recommended: true,
},
fixable: 'code',
@@ -124,234 +171,47 @@ const rule: Rule.RuleModule = {
// validation is done at runtime with zod
schema: [{type: 'object', additionalProperties: true}],
},
create(context: Rule.RuleContext) {
// Compat with older versions of eslint
const sourceCode = context.sourceCode ?? context.getSourceCode();
const filename = context.filename ?? context.getFilename();
const userOpts = context.options[0] ?? {};
if (
userOpts.reportableLevels != null &&
userOpts.reportableLevels instanceof Set
) {
reportableLevels = userOpts.reportableLevels;
} else {
reportableLevels = DEFAULT_REPORTABLE_LEVELS;
}
/**
* Experimental setting to report all compilation bailouts on the compilation
* unit (e.g. function or hook) instead of the offensive line.
* Intended to be used when a codebase is 100% reliant on the compiler for
* memoization (i.e. deleted all manual memo) and needs compilation success
* signals for perf debugging.
*/
let __unstable_donotuse_reportAllBailouts: boolean = false;
if (
userOpts.__unstable_donotuse_reportAllBailouts != null &&
typeof userOpts.__unstable_donotuse_reportAllBailouts === 'boolean'
) {
__unstable_donotuse_reportAllBailouts =
userOpts.__unstable_donotuse_reportAllBailouts;
}
create(context: Rule.RuleContext): Rule.RuleListener {
const results = getReactCompilerResult(context);
let shouldReportUnusedOptOutDirective = true;
const options: PluginOptions = parsePluginOptions({
...COMPILER_OPTIONS,
...userOpts,
environment: {
...COMPILER_OPTIONS.environment,
...userOpts.environment,
},
});
const userLogger: Logger | null = options.logger;
options.logger = {
logEvent: (eventFilename, event): void => {
userLogger?.logEvent(eventFilename, event);
if (event.kind === 'CompileError') {
shouldReportUnusedOptOutDirective = false;
const detail = event.detail;
const suggest = makeSuggestions(detail.options);
if (__unstable_donotuse_reportAllBailouts && event.fnLoc != null) {
const loc = detail.primaryLocation();
const locStr =
loc != null && typeof loc !== 'symbol'
? ` (@:${loc.start.line}:${loc.start.column})`
: '';
/**
* Report bailouts with a smaller span (just the first line).
* Compiler bailout lints only serve to flag that a react function
* has not been optimized by the compiler for codebases which depend
* on compiler memo heavily for perf. These lints are also often not
* actionable.
*/
let endLoc;
if (event.fnLoc.end.line === event.fnLoc.start.line) {
endLoc = event.fnLoc.end;
} else {
endLoc = {
line: event.fnLoc.start.line,
// Babel loc line numbers are 1-indexed
column:
sourceCode.text.split(/\r?\n|\r|\n/g)[
event.fnLoc.start.line - 1
]?.length ?? 0,
};
}
const firstLineLoc = {
start: event.fnLoc.start,
end: endLoc,
};
context.report({
message: `${detail.printErrorMessage(sourceCode.text, {eslint: true})} ${locStr}`,
loc: firstLineLoc,
suggest,
});
}
const loc = detail.primaryLocation();
if (
!isReportableDiagnostic(detail) ||
loc == null ||
typeof loc === 'symbol'
) {
return;
}
if (
hasFlowSuppression(loc, 'react-rule-hook') ||
hasFlowSuppression(loc, 'react-rule-unsafe-ref')
) {
// If Flow already caught this error, we don't need to report it again.
return;
}
if (loc != null) {
context.report({
message: detail.printErrorMessage(sourceCode.text, {
eslint: true,
}),
loc,
suggest,
});
}
}
},
};
try {
options.environment = validateEnvironmentConfig(
options.environment ?? {},
);
} catch (err: unknown) {
options.logger?.logEvent('', err as LoggerEvent);
}
function hasFlowSuppression(
nodeLoc: BabelSourceLocation,
suppression: string,
): boolean {
const comments = sourceCode.getAllComments();
const flowSuppressionRegex = new RegExp(
'\\$FlowFixMe\\[' + suppression + '\\]',
);
for (const commentNode of comments) {
if (
flowSuppressionRegex.test(commentNode.value) &&
commentNode.loc!.end.line === nodeLoc.start.line - 1
) {
return true;
}
}
return false;
}
let babelAST;
if (filename.endsWith('.tsx') || filename.endsWith('.ts')) {
try {
const {parse: babelParse} = require('@babel/parser');
babelAST = babelParse(sourceCode.text, {
filename,
sourceType: 'unambiguous',
plugins: ['typescript', 'jsx'],
});
} catch {
/* empty */
}
} else {
try {
babelAST = HermesParser.parse(sourceCode.text, {
babel: true,
enableExperimentalComponentSyntax: true,
sourceFilename: filename,
sourceType: 'module',
});
} catch {
/* empty */
}
}
if (babelAST != null) {
try {
transformFromAstSync(babelAST, sourceCode.text, {
filename,
highlightCode: false,
retainLines: true,
plugins: [
[PluginProposalPrivateMethods, {loose: true}],
[BabelPluginReactCompiler, options],
],
sourceType: 'module',
configFile: false,
babelrc: false,
});
} catch (err) {
/* errors handled by injected logger */
}
}
function reportUnusedOptOutDirective(stmt: Statement) {
if (
stmt.type === 'ExpressionStatement' &&
stmt.expression.type === 'Literal' &&
typeof stmt.expression.value === 'string' &&
OPT_OUT_DIRECTIVES.has(stmt.expression.value) &&
stmt.loc != null
) {
context.report({
message: `Unused '${stmt.expression.value}' directive`,
loc: stmt.loc,
suggest: [
{
desc: 'Remove the directive',
fix(fixer) {
return fixer.remove(stmt);
},
for (const directive of results.unusedOptOutDirectives) {
context.report({
message: `Unused '${directive.directive}' directive`,
loc: directive.loc,
suggest: [
{
desc: 'Remove the directive',
fix(fixer): Rule.Fix {
return fixer.removeRange(directive.range);
},
],
});
}
}
if (shouldReportUnusedOptOutDirective) {
return {
FunctionDeclaration(fnDecl) {
for (const stmt of fnDecl.body.body) {
reportUnusedOptOutDirective(stmt);
}
},
ArrowFunctionExpression(fnExpr) {
if (fnExpr.body.type === 'BlockStatement') {
for (const stmt of fnExpr.body.body) {
reportUnusedOptOutDirective(stmt);
}
}
},
FunctionExpression(fnExpr) {
for (const stmt of fnExpr.body.body) {
reportUnusedOptOutDirective(stmt);
}
},
};
} else {
return {};
},
],
});
}
return {};
},
};
export default rule;
type RulesObject = {[name: string]: Rule.RuleModule};
export const allRules: RulesObject = LintRules.reduce(
(acc, rule) => {
acc[rule.name] = makeRule(rule);
return acc;
},
{
'no-unused-directives': NoUnusedDirectivesRule,
} as RulesObject,
);
export const recommendedRules: RulesObject = LintRules.filter(
rule => rule.recommended,
).reduce(
(acc, rule) => {
acc[rule.name] = makeRule(rule);
return acc;
},
{
'no-unused-directives': NoUnusedDirectivesRule,
} as RulesObject,
);

View File

@@ -0,0 +1,287 @@
/**
* 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 {transformFromAstSync, traverse} from '@babel/core';
import {parse as babelParse} from '@babel/parser';
import {Directive, File} from '@babel/types';
// @ts-expect-error: no types available
import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods';
import BabelPluginReactCompiler, {
parsePluginOptions,
validateEnvironmentConfig,
OPT_OUT_DIRECTIVES,
type PluginOptions,
} from 'babel-plugin-react-compiler/src';
import {Logger, LoggerEvent} from 'babel-plugin-react-compiler/src/Entrypoint';
import type {SourceCode} from 'eslint';
import {SourceLocation} from 'estree';
// @ts-expect-error: no types available
import * as HermesParser from 'hermes-parser';
import {isDeepStrictEqual} from 'util';
import type {ParseResult} from '@babel/parser';
const COMPILER_OPTIONS: Partial<PluginOptions> = {
noEmit: true,
panicThreshold: 'none',
// Don't emit errors on Flow suppressions--Flow already gave a signal
flowSuppressions: false,
environment: validateEnvironmentConfig({
validateRefAccessDuringRender: true,
validateNoSetStateInRender: true,
validateNoSetStateInEffects: true,
validateNoJSXInTryStatements: true,
validateNoImpureFunctionsInRender: true,
validateStaticComponents: true,
validateNoFreezingKnownMutableFunctions: true,
validateNoVoidUseMemo: true,
// TODO: remove, this should be in the type system
validateNoCapitalizedCalls: [],
validateHooksUsage: true,
validateNoDerivedComputationsInEffects: true,
}),
};
export type UnusedOptOutDirective = {
loc: SourceLocation;
range: [number, number];
directive: string;
};
export type RunCacheEntry = {
sourceCode: string;
filename: string;
userOpts: PluginOptions;
flowSuppressions: Array<{line: number; code: string}>;
unusedOptOutDirectives: Array<UnusedOptOutDirective>;
events: Array<LoggerEvent>;
};
type RunParams = {
sourceCode: SourceCode;
filename: string;
userOpts: PluginOptions;
};
const FLOW_SUPPRESSION_REGEX = /\$FlowFixMe\[([^\]]*)\]/g;
function getFlowSuppressions(
sourceCode: SourceCode,
): Array<{line: number; code: string}> {
const comments = sourceCode.getAllComments();
const results: Array<{line: number; code: string}> = [];
for (const commentNode of comments) {
const matches = commentNode.value.matchAll(FLOW_SUPPRESSION_REGEX);
for (const match of matches) {
if (match.index != null && commentNode.loc != null) {
const code = match[1];
results.push({
line: commentNode.loc!.end.line,
code,
});
}
}
}
return results;
}
function filterUnusedOptOutDirectives(
directives: ReadonlyArray<Directive>,
): Array<UnusedOptOutDirective> {
const results: Array<UnusedOptOutDirective> = [];
for (const directive of directives) {
if (
OPT_OUT_DIRECTIVES.has(directive.value.value) &&
directive.loc != null
) {
results.push({
loc: directive.loc,
directive: directive.value.value,
range: [directive.start!, directive.end!],
});
}
}
return results;
}
function runReactCompilerImpl({
sourceCode,
filename,
userOpts,
}: RunParams): RunCacheEntry {
// Compat with older versions of eslint
const options: PluginOptions = parsePluginOptions({
...COMPILER_OPTIONS,
...userOpts,
environment: {
...COMPILER_OPTIONS.environment,
...userOpts.environment,
},
});
const results: RunCacheEntry = {
sourceCode: sourceCode.text,
filename,
userOpts,
flowSuppressions: [],
unusedOptOutDirectives: [],
events: [],
};
const userLogger: Logger | null = options.logger;
options.logger = {
logEvent: (eventFilename, event): void => {
userLogger?.logEvent(eventFilename, event);
results.events.push(event);
},
};
try {
options.environment = validateEnvironmentConfig(options.environment ?? {});
} catch (err: unknown) {
options.logger?.logEvent(filename, err as LoggerEvent);
}
let babelAST: ParseResult<File> | null = null;
if (filename.endsWith('.tsx') || filename.endsWith('.ts')) {
try {
babelAST = babelParse(sourceCode.text, {
sourceFilename: filename,
sourceType: 'unambiguous',
plugins: ['typescript', 'jsx'],
});
} catch {
/* empty */
}
} else {
try {
babelAST = HermesParser.parse(sourceCode.text, {
babel: true,
enableExperimentalComponentSyntax: true,
sourceFilename: filename,
sourceType: 'module',
});
} catch {
/* empty */
}
}
if (babelAST != null) {
results.flowSuppressions = getFlowSuppressions(sourceCode);
try {
transformFromAstSync(babelAST, sourceCode.text, {
filename,
highlightCode: false,
retainLines: true,
plugins: [
[PluginProposalPrivateMethods, {loose: true}],
[BabelPluginReactCompiler, options],
],
sourceType: 'module',
configFile: false,
babelrc: false,
});
if (results.events.filter(e => e.kind === 'CompileError').length === 0) {
traverse(babelAST, {
FunctionDeclaration(path) {
path.node;
results.unusedOptOutDirectives.push(
...filterUnusedOptOutDirectives(path.node.body.directives),
);
},
ArrowFunctionExpression(path) {
if (path.node.body.type === 'BlockStatement') {
results.unusedOptOutDirectives.push(
...filterUnusedOptOutDirectives(path.node.body.directives),
);
}
},
FunctionExpression(path) {
results.unusedOptOutDirectives.push(
...filterUnusedOptOutDirectives(path.node.body.directives),
);
},
});
}
} catch (err) {
/* errors handled by injected logger */
}
}
return results;
}
const SENTINEL = Symbol();
// Array backed LRU cache -- should be small < 10 elements
class LRUCache<K, T> {
// newest at headIdx, then headIdx + 1, ..., tailIdx
#values: Array<[K, T | Error] | [typeof SENTINEL, void]>;
#headIdx: number = 0;
constructor(size: number) {
this.#values = new Array(size).fill(SENTINEL);
}
// gets a value and sets it as "recently used"
get(key: K): T | null {
let idx = this.#values.findIndex(entry => entry[0] === key);
// If found, move to front
if (idx === this.#headIdx) {
return this.#values[this.#headIdx][1] as T;
} else if (idx < 0) {
return null;
}
const entry: [K, T] = this.#values[idx] as [K, T];
const len = this.#values.length;
for (let i = 0; i < Math.min(idx, len - 1); i++) {
this.#values[(this.#headIdx + i + 1) % len] =
this.#values[(this.#headIdx + i) % len];
}
this.#values[this.#headIdx] = entry;
return entry[1];
}
push(key: K, value: T): void {
this.#headIdx =
(this.#headIdx - 1 + this.#values.length) % this.#values.length;
this.#values[this.#headIdx] = [key, value];
}
}
const cache = new LRUCache<string, RunCacheEntry>(10);
export default function runReactCompiler({
sourceCode,
filename,
userOpts,
}: RunParams): RunCacheEntry {
const entry = cache.get(filename);
if (
entry != null &&
entry.sourceCode === sourceCode.text &&
isDeepStrictEqual(entry.userOpts, userOpts)
) {
return entry;
} else if (entry != null) {
if (process.env['DEBUG']) {
console.log(
`Cache hit for ${filename}, but source code or options changed, recomputing`,
);
}
}
const runEntry = runReactCompilerImpl({
sourceCode,
filename,
userOpts,
});
// If we have a cache entry, we can update it
if (entry != null) {
Object.assign(entry, runEntry);
} else {
cache.push(filename, runEntry);
}
return {...runEntry};
}

View File

@@ -338,7 +338,16 @@ export async function transformFixtureInput(
if (logs.length !== 0) {
formattedLogs = logs
.map(({event}) => {
return JSON.stringify(event);
return JSON.stringify(event, (key, value) => {
if (
key === 'detail' &&
value != null &&
typeof value.serialize === 'function'
) {
return value.serialize();
}
return value;
});
})
.join('\n');
}

View File

@@ -0,0 +1,32 @@
const ReactCompiler = require('../packages/babel-plugin-react-compiler/dist');
const combinedRules = [
{
name: 'rules-of-hooks',
recommended: true,
description:
'Validates that components and hooks follow the [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks)',
},
{
name: 'exhaustive-deps',
recommended: true,
description:
'Validates that hooks which accept dependency arrays (`useMemo()`, `useCallback()`, `useEffect()`, etc) ' +
'list all referenced variables in their dependency array. Referencing a value without including it in the ' +
'dependency array can lead to stale UI or callbacks.',
},
...ReactCompiler.LintRules,
];
const printed = combinedRules
.filter(rule => rule.recommended)
.map(rule => {
return `
## \`react-hooks/${rule.name}\`
${rule.description}
`.trim();
})
.join('\n\n');
console.log(printed);

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,9 @@
import type {Linter} from 'eslint';
import * as reactHooks from 'eslint-plugin-react-hooks';
import {defineConfig} from 'eslint/config';
import reactHooks from 'eslint-plugin-react-hooks';
export default [
console.log(reactHooks.configs['recommended-latest']);
export default defineConfig([
{
languageOptions: {
ecmaVersion: 'latest',
@@ -12,11 +14,12 @@ export default [
},
},
},
},
reactHooks.configs['recommended'],
{
plugins: {
'react-hooks': reactHooks,
},
extends: ['react-hooks/recommended-latest'],
rules: {
'react-hooks/exhaustive-deps': 'error',
},
},
] satisfies Linter.Config[];
]);

View File

@@ -2,7 +2,7 @@
"private": true,
"name": "eslint-v9",
"dependencies": {
"eslint": "^9.18.0",
"eslint": "^9.33.0",
"eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks",
"jiti": "^2.4.2"
},

View File

@@ -221,26 +221,31 @@
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
"@eslint/config-array@^0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa"
integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==
"@eslint/config-array@^0.21.0":
version "0.21.0"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636"
integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==
dependencies:
"@eslint/object-schema" "^2.1.6"
debug "^4.3.1"
minimatch "^3.1.2"
"@eslint/core@^0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e"
integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==
"@eslint/config-helpers@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.1.tgz#d316e47905bd0a1a931fa50e669b9af4104d1617"
integrity sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==
"@eslint/core@^0.15.2":
version "0.15.2"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.2.tgz#59386327d7862cc3603ebc7c78159d2dcc4a868f"
integrity sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==
dependencies:
"@types/json-schema" "^7.0.15"
"@eslint/eslintrc@^3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.0.tgz#96a558f45842989cca7ea1ecd785ad5491193846"
integrity sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==
"@eslint/eslintrc@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964"
integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
@@ -252,22 +257,22 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@9.21.0":
version "9.21.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.21.0.tgz#4303ef4e07226d87c395b8fad5278763e9c15c08"
integrity sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==
"@eslint/js@9.33.0":
version "9.33.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.33.0.tgz#475c92fdddab59b8b8cab960e3de2564a44bf368"
integrity sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==
"@eslint/object-schema@^2.1.6":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f"
integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
"@eslint/plugin-kit@^0.2.7":
version "0.2.7"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz#9901d52c136fb8f375906a73dcc382646c3b6a27"
integrity sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==
"@eslint/plugin-kit@^0.3.5":
version "0.3.5"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz#fd8764f0ee79c8ddab4da65460c641cefee017c5"
integrity sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==
dependencies:
"@eslint/core" "^0.12.0"
"@eslint/core" "^0.15.2"
levn "^0.4.1"
"@humanfs/core@^0.19.1":
@@ -350,6 +355,11 @@ acorn@^8.14.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
acorn@^8.15.0:
version "8.15.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -475,10 +485,10 @@ escape-string-regexp@^4.0.0:
version "0.0.0"
uid ""
eslint-scope@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442"
integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==
eslint-scope@^8.4.0:
version "8.4.0"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82"
integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==
dependencies:
esrecurse "^4.3.0"
estraverse "^5.2.0"
@@ -493,18 +503,24 @@ eslint-visitor-keys@^4.2.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
eslint@^9.18.0:
version "9.21.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.21.0.tgz#b1c9c16f5153ff219791f627b94ab8f11f811591"
integrity sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==
eslint-visitor-keys@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1"
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
eslint@^9.33.0:
version "9.33.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.33.0.tgz#cc186b3d9eb0e914539953d6a178a5b413997b73"
integrity sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.12.1"
"@eslint/config-array" "^0.19.2"
"@eslint/core" "^0.12.0"
"@eslint/eslintrc" "^3.3.0"
"@eslint/js" "9.21.0"
"@eslint/plugin-kit" "^0.2.7"
"@eslint/config-array" "^0.21.0"
"@eslint/config-helpers" "^0.3.1"
"@eslint/core" "^0.15.2"
"@eslint/eslintrc" "^3.3.1"
"@eslint/js" "9.33.0"
"@eslint/plugin-kit" "^0.3.5"
"@humanfs/node" "^0.16.6"
"@humanwhocodes/module-importer" "^1.0.1"
"@humanwhocodes/retry" "^0.4.2"
@@ -515,9 +531,9 @@ eslint@^9.18.0:
cross-spawn "^7.0.6"
debug "^4.3.2"
escape-string-regexp "^4.0.0"
eslint-scope "^8.2.0"
eslint-visitor-keys "^4.2.0"
espree "^10.3.0"
eslint-scope "^8.4.0"
eslint-visitor-keys "^4.2.1"
espree "^10.4.0"
esquery "^1.5.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
@@ -533,7 +549,7 @@ eslint@^9.18.0:
natural-compare "^1.4.0"
optionator "^0.9.3"
espree@^10.0.1, espree@^10.3.0:
espree@^10.0.1:
version "10.3.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a"
integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==
@@ -542,6 +558,15 @@ espree@^10.0.1, espree@^10.3.0:
acorn-jsx "^5.3.2"
eslint-visitor-keys "^4.2.0"
espree@^10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837"
integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==
dependencies:
acorn "^8.15.0"
acorn-jsx "^5.3.2"
eslint-visitor-keys "^4.2.1"
esquery@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"

View File

@@ -15,6 +15,7 @@ const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const DevToolsIgnorePlugin = require('devtools-ignore-webpack-plugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const modules = require('./modules');
@@ -685,6 +686,15 @@ module.exports = function (webpackEnv) {
},
}),
// Fork Start
new DevToolsIgnorePlugin({
shouldIgnorePath: function (path) {
return (
path.includes('/node_modules/') ||
path.includes('/webpack/') ||
path.endsWith('/src/index.js')
);
},
}),
new ReactFlightWebpackPlugin({
isServer: false,
clientReferences: {

View File

@@ -29,6 +29,7 @@
"concurrently": "^7.3.0",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.2.0",
"devtools-ignore-webpack-plugin": "^0.2.0",
"dotenv": "^10.0.0",
"dotenv-expand": "^5.1.0",
"file-loader": "^6.2.0",

View File

@@ -58,7 +58,8 @@ function filterStackFrame(sourceURL, functionName) {
sourceURL !== '' &&
!sourceURL.startsWith('node:') &&
!sourceURL.includes('node_modules') &&
!sourceURL.endsWith('library.js')
!sourceURL.endsWith('library.js') &&
!sourceURL.includes('/server/region.js')
);
}

View File

@@ -4614,6 +4614,11 @@ detect-port-alt@^1.1.6:
address "^1.0.1"
debug "^2.6.0"
devtools-ignore-webpack-plugin@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/devtools-ignore-webpack-plugin/-/devtools-ignore-webpack-plugin-0.2.0.tgz#a7b3d1bd0f593c7fee5cbb7534b07860e5e2447c"
integrity sha512-4P+1Y1VhSt1MRBRF6my8N1bs9nNMOFfIFSBHI6u18W73iCHWXNHTSWYeMoQQ72PIIHrP1q4koKpYg1Em3jb9Rw==
didyoumean@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
@@ -8650,16 +8655,7 @@ string-length@^5.0.1:
char-regex "^2.0.0"
strip-ansi "^7.0.1"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -8730,14 +8726,7 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -9452,16 +9441,7 @@ wordwrap@~1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==

20
flow-typed.config.json Normal file
View File

@@ -0,0 +1,20 @@
{
"env": [
"bom",
"cssom",
"dom",
"geometry",
"html",
"node",
"serviceworkers",
"streams",
"web-animations"
],
"ignore": [
"create-react-class",
"jest",
"regenerator-runtime",
"webpack",
"ws"
]
}

2719
flow-typed/environments/bom.js vendored Normal file

File diff suppressed because it is too large Load Diff

414
flow-typed/environments/cssom.js vendored Normal file
View File

@@ -0,0 +1,414 @@
// flow-typed signature: ad7b684aa8897ecb82bcc3e009b9fc30
// flow-typed version: 3e51657e95/cssom/flow_>=v0.261.x
declare class StyleSheet {
disabled: boolean;
+href: string;
+media: MediaList;
+ownerNode: Node;
+parentStyleSheet: ?StyleSheet;
+title: string;
+type: string;
}
declare class StyleSheetList {
@@iterator(): Iterator<StyleSheet>;
length: number;
[index: number]: StyleSheet;
}
declare class MediaList {
@@iterator(): Iterator<string>;
mediaText: string;
length: number;
item(index: number): ?string;
deleteMedium(oldMedium: string): void;
appendMedium(newMedium: string): void;
[index: number]: string;
}
declare class CSSStyleSheet extends StyleSheet {
+cssRules: CSSRuleList;
+ownerRule: ?CSSRule;
deleteRule(index: number): void;
insertRule(rule: string, index: number): number;
replace(text: string): Promise<CSSStyleSheet>;
replaceSync(text: string): void;
}
declare class CSSGroupingRule extends CSSRule {
+cssRules: CSSRuleList;
deleteRule(index: number): void;
insertRule(rule: string, index: number): number;
}
declare class CSSConditionRule extends CSSGroupingRule {
conditionText: string;
}
declare class CSSMediaRule extends CSSConditionRule {
+media: MediaList;
}
declare class CSSStyleRule extends CSSRule {
selectorText: string;
+style: CSSStyleDeclaration;
}
declare class CSSSupportsRule extends CSSConditionRule {}
declare class CSSRule {
cssText: string;
+parentRule: ?CSSRule;
+parentStyleSheet: ?CSSStyleSheet;
+type: number;
static STYLE_RULE: number;
static MEDIA_RULE: number;
static FONT_FACE_RULE: number;
static PAGE_RULE: number;
static IMPORT_RULE: number;
static CHARSET_RULE: number;
static UNKNOWN_RULE: number;
static KEYFRAMES_RULE: number;
static KEYFRAME_RULE: number;
static NAMESPACE_RULE: number;
static COUNTER_STYLE_RULE: number;
static SUPPORTS_RULE: number;
static DOCUMENT_RULE: number;
static FONT_FEATURE_VALUES_RULE: number;
static VIEWPORT_RULE: number;
static REGION_STYLE_RULE: number;
}
declare class CSSKeyframeRule extends CSSRule {
keyText: string;
+style: CSSStyleDeclaration;
}
declare class CSSKeyframesRule extends CSSRule {
name: string;
+cssRules: CSSRuleList;
appendRule(rule: string): void;
deleteRule(select: string): void;
findRule(select: string): CSSKeyframeRule | null;
}
declare class CSSRuleList {
@@iterator(): Iterator<CSSRule>;
length: number;
item(index: number): ?CSSRule;
[index: number]: CSSRule;
}
declare class CSSStyleDeclaration {
@@iterator(): Iterator<string>;
/* DOM CSS Properties */
alignContent: string;
alignItems: string;
alignSelf: string;
all: string;
animation: string;
animationDelay: string;
animationDirection: string;
animationDuration: string;
animationFillMode: string;
animationIterationCount: string;
animationName: string;
animationPlayState: string;
animationTimingFunction: string;
backdropFilter: string;
webkitBackdropFilter: string;
backfaceVisibility: string;
background: string;
backgroundAttachment: string;
backgroundBlendMode: string;
backgroundClip: string;
backgroundColor: string;
backgroundImage: string;
backgroundOrigin: string;
backgroundPosition: string;
backgroundPositionX: string;
backgroundPositionY: string;
backgroundRepeat: string;
backgroundSize: string;
blockSize: string;
border: string;
borderBlockEnd: string;
borderBlockEndColor: string;
borderBlockEndStyle: string;
borderBlockEndWidth: string;
borderBlockStart: string;
borderBlockStartColor: string;
borderBlockStartStyle: string;
borderBlockStartWidth: string;
borderBottom: string;
borderBottomColor: string;
borderBottomLeftRadius: string;
borderBottomRightRadius: string;
borderBottomStyle: string;
borderBottomWidth: string;
borderCollapse: string;
borderColor: string;
borderImage: string;
borderImageOutset: string;
borderImageRepeat: string;
borderImageSlice: string;
borderImageSource: string;
borderImageWidth: string;
borderInlineEnd: string;
borderInlineEndColor: string;
borderInlineEndStyle: string;
borderInlineEndWidth: string;
borderInlineStart: string;
borderInlineStartColor: string;
borderInlineStartStyle: string;
borderInlineStartWidth: string;
borderLeft: string;
borderLeftColor: string;
borderLeftStyle: string;
borderLeftWidth: string;
borderRadius: string;
borderRight: string;
borderRightColor: string;
borderRightStyle: string;
borderRightWidth: string;
borderSpacing: string;
borderStyle: string;
borderTop: string;
borderTopColor: string;
borderTopLeftRadius: string;
borderTopRightRadius: string;
borderTopStyle: string;
borderTopWidth: string;
borderWidth: string;
bottom: string;
boxDecorationBreak: string;
boxShadow: string;
boxSizing: string;
breakAfter: string;
breakBefore: string;
breakInside: string;
captionSide: string;
clear: string;
clip: string;
clipPath: string;
color: string;
columns: string;
columnCount: string;
columnFill: string;
columnGap: string;
columnRule: string;
columnRuleColor: string;
columnRuleStyle: string;
columnRuleWidth: string;
columnSpan: string;
columnWidth: string;
contain: string;
content: string;
counterIncrement: string;
counterReset: string;
cursor: string;
direction: string;
display: string;
emptyCells: string;
filter: string;
flex: string;
flexBasis: string;
flexDirection: string;
flexFlow: string;
flexGrow: string;
flexShrink: string;
flexWrap: string;
float: string;
font: string;
fontFamily: string;
fontFeatureSettings: string;
fontKerning: string;
fontLanguageOverride: string;
fontSize: string;
fontSizeAdjust: string;
fontStretch: string;
fontStyle: string;
fontSynthesis: string;
fontVariant: string;
fontVariantAlternates: string;
fontVariantCaps: string;
fontVariantEastAsian: string;
fontVariantLigatures: string;
fontVariantNumeric: string;
fontVariantPosition: string;
fontWeight: string;
grad: string;
grid: string;
gridArea: string;
gridAutoColumns: string;
gridAutoFlow: string;
gridAutoPosition: string;
gridAutoRows: string;
gridColumn: string;
gridColumnStart: string;
gridColumnEnd: string;
gridRow: string;
gridRowStart: string;
gridRowEnd: string;
gridTemplate: string;
gridTemplateAreas: string;
gridTemplateRows: string;
gridTemplateColumns: string;
height: string;
hyphens: string;
imageRendering: string;
imageResolution: string;
imageOrientation: string;
imeMode: string;
inherit: string;
initial: string;
inlineSize: string;
isolation: string;
justifyContent: string;
left: string;
letterSpacing: string;
lineBreak: string;
lineHeight: string;
listStyle: string;
listStyleImage: string;
listStylePosition: string;
listStyleType: string;
margin: string;
marginBlockEnd: string;
marginBlockStart: string;
marginBottom: string;
marginInlineEnd: string;
marginInlineStart: string;
marginLeft: string;
marginRight: string;
marginTop: string;
marks: string;
mask: string;
maskType: string;
maxBlockSize: string;
maxHeight: string;
maxInlineSize: string;
maxWidth: string;
minBlockSize: string;
minHeight: string;
minInlineSize: string;
minWidth: string;
mixBlendMode: string;
mozTransform: string;
mozTransformOrigin: string;
mozTransitionDelay: string;
mozTransitionDuration: string;
mozTransitionProperty: string;
mozTransitionTimingFunction: string;
objectFit: string;
objectPosition: string;
offsetBlockEnd: string;
offsetBlockStart: string;
offsetInlineEnd: string;
offsetInlineStart: string;
opacity: string;
order: string;
orphans: string;
outline: string;
outlineColor: string;
outlineOffset: string;
outlineStyle: string;
outlineWidth: string;
overflow: string;
overflowWrap: string;
overflowX: string;
overflowY: string;
padding: string;
paddingBlockEnd: string;
paddingBlockStart: string;
paddingBottom: string;
paddingInlineEnd: string;
paddingInlineStart: string;
paddingLeft: string;
paddingRight: string;
paddingTop: string;
pageBreakAfter: string;
pageBreakBefore: string;
pageBreakInside: string;
perspective: string;
perspectiveOrigin: string;
pointerEvents: string;
position: string;
quotes: string;
rad: string;
resize: string;
right: string;
rubyAlign: string;
rubyMerge: string;
rubyPosition: string;
scrollBehavior: string;
scrollSnapCoordinate: string;
scrollSnapDestination: string;
scrollSnapPointsX: string;
scrollSnapPointsY: string;
scrollSnapType: string;
shapeImageThreshold: string;
shapeMargin: string;
shapeOutside: string;
tableLayout: string;
tabSize: string;
textAlign: string;
textAlignLast: string;
textCombineUpright: string;
textDecoration: string;
textDecorationColor: string;
textDecorationLine: string;
textDecorationStyle: string;
textIndent: string;
textOrientation: string;
textOverflow: string;
textRendering: string;
textShadow: string;
textTransform: string;
textUnderlinePosition: string;
top: string;
touchAction: string;
transform: string;
transformOrigin: string;
transformStyle: string;
transition: string;
transitionDelay: string;
transitionDuration: string;
transitionProperty: string;
transitionTimingFunction: string;
turn: string;
unicodeBidi: string;
unicodeRange: string;
userSelect: string;
verticalAlign: string;
visibility: string;
webkitOverflowScrolling: string;
webkitTransform: string;
webkitTransformOrigin: string;
webkitTransitionDelay: string;
webkitTransitionDuration: string;
webkitTransitionProperty: string;
webkitTransitionTimingFunction: string;
whiteSpace: string;
widows: string;
width: string;
willChange: string;
wordBreak: string;
wordSpacing: string;
wordWrap: string;
writingMode: string;
zIndex: string;
cssFloat: string;
cssText: string;
getPropertyPriority(property: string): string;
getPropertyValue(property: string): string;
item(index: number): string;
[index: number]: string;
length: number;
parentRule: CSSRule;
removeProperty(property: string): string;
setProperty(property: string, value: ?string, priority: ?string): void;
setPropertyPriority(property: string, priority: string): void;
}

3676
flow-typed/environments/dom.js vendored Normal file

File diff suppressed because it is too large Load Diff

270
flow-typed/environments/geometry.js vendored Normal file
View File

@@ -0,0 +1,270 @@
// flow-typed signature: c29a716c1825927cdfc3ad29fe929754
// flow-typed version: 52ab99c6db/geometry/flow_>=v0.261.x
// https://www.w3.org/TR/geometry-1/
type DOMMatrix2DInit =
| {|
a: number,
b: number,
c: number,
d: number,
e: number,
f: number,
|}
| {|
m11: number,
m12: number,
m21: number,
m22: number,
m41: number,
m42: number,
|};
type DOMMatrixInit =
| {|
...DOMMatrix2DInit,
is2D: true,
|}
| {|
...DOMMatrix2DInit,
is2D: false,
m13: number,
m14: number,
m23: number,
m24: number,
m31: number,
m32: number,
m33: number,
m34: number,
m43: number,
m44: number,
|};
type DOMPointInit = {|
w: number,
x: number,
y: number,
z: number,
|};
type DOMQuadInit = {|
p1: DOMPointInit,
p2: DOMPointInit,
p3: DOMPointInit,
p4: DOMPointInit,
|};
type DOMRectInit = {|
height: number,
width: number,
x: number,
y: number,
|};
declare class DOMMatrix extends DOMMatrixReadOnly {
a: number;
b: number;
c: number;
d: number;
e: number;
f: number;
m11: number;
m12: number;
m13: number;
m14: number;
m21: number;
m22: number;
m23: number;
m24: number;
m31: number;
m32: number;
m33: number;
m34: number;
m41: number;
m42: number;
m43: number;
m44: number;
static fromFloat32Array(array32: Float32Array): DOMMatrix;
static fromFloat64Array(array64: Float64Array): DOMMatrix;
static fromMatrix(other?: DOMMatrixInit): DOMMatrix;
constructor(init?: string | Array<number>): void;
invertSelf(): DOMMatrix;
multiplySelf(other?: DOMMatrixInit): DOMMatrix;
preMultiplySelf(other?: DOMMatrixInit): DOMMatrix;
rotateAxisAngleSelf(
x?: number,
y?: number,
z?: number,
angle?: number
): DOMMatrix;
rotateFromVectorSelf(x?: number, y?: number): DOMMatrix;
rotateSelf(rotX?: number, rotY?: number, rotZ?: number): DOMMatrix;
scale3dSelf(
scale?: number,
originX?: number,
originY?: number,
originZ?: number
): DOMMatrix;
scaleSelf(
scaleX?: number,
scaleY?: number,
scaleZ?: number,
originX?: number,
originY?: number,
originZ?: number
): DOMMatrix;
setMatrixValue(transformList: string): DOMMatrix;
skewXSelf(sx?: number): DOMMatrix;
skewYSelf(sy?: number): DOMMatrix;
translateSelf(tx?: number, ty?: number, tz?: number): DOMMatrix;
}
declare class DOMMatrixReadOnly {
+a: number;
+b: number;
+c: number;
+d: number;
+e: number;
+f: number;
+is2D: boolean;
+isIdentity: boolean;
+m11: number;
+m12: number;
+m13: number;
+m14: number;
+m21: number;
+m22: number;
+m23: number;
+m24: number;
+m31: number;
+m32: number;
+m33: number;
+m34: number;
+m41: number;
+m42: number;
+m43: number;
+m44: number;
static fromFloat32Array(array32: Float32Array): DOMMatrixReadOnly;
static fromFloat64Array(array64: Float64Array): DOMMatrixReadOnly;
static fromMatrix(other?: DOMMatrixInit): DOMMatrixReadOnly;
constructor(init?: string | Array<number>): void;
flipX(): DOMMatrix;
flipY(): DOMMatrix;
inverse(): DOMMatrix;
multiply(other?: DOMMatrixInit): DOMMatrix;
rotate(rotX?: number, rotY?: number, rotZ?: number): DOMMatrix;
rotateAxisAngle(
x?: number,
y?: number,
z?: number,
angle?: number
): DOMMatrix;
rotateFromVector(x?: number, y?: number): DOMMatrix;
scale(
scaleX?: number,
scaleY?: number,
scaleZ?: number,
originX?: number,
originY?: number,
originZ?: number
): DOMMatrix;
scale3d(
scale?: number,
originX?: number,
originY?: number,
originZ?: number
): DOMMatrix;
scaleNonUniform(scaleX?: number, scaleY?: number): DOMMatrix;
skewX(sx?: number): DOMMatrix;
skewY(sy?: number): DOMMatrix;
toFloat32Array(): Float32Array;
toFloat64Array(): Float64Array;
toJSON(): Object;
transformPoint(point?: DOMPointInit): DOMPoint;
translate(tx?: number, ty?: number, tz?: number): DOMMatrix;
toString(): string;
}
declare class DOMPoint extends DOMPointReadOnly {
w: number;
x: number;
y: number;
z: number;
static fromPoint(other?: DOMPointInit): DOMPoint;
constructor(x?: number, y?: number, z?: number, w?: number): void;
}
declare class DOMPointReadOnly {
+w: number;
+x: number;
+y: number;
+z: number;
static fromPoint(other?: DOMPointInit): DOMPointReadOnly;
constructor(x?: number, y?: number, z?: number, w?: number): void;
matrixTransform(matrix?: DOMMatrixInit): DOMPoint;
toJSON(): Object;
}
declare class DOMQuad {
+p1: DOMPoint;
+p2: DOMPoint;
+p3: DOMPoint;
+p4: DOMPoint;
static fromQuad(other?: DOMQuadInit): DOMQuad;
static fromRect(other?: DOMRectInit): DOMQuad;
constructor(
p1?: DOMPointInit,
p2?: DOMPointInit,
p3?: DOMPointInit,
p4?: DOMPointInit
): void;
getBounds(): DOMRect;
toJSON(): Object;
}
declare class DOMRect extends DOMRectReadOnly {
height: number;
width: number;
x: number;
y: number;
constructor(x?: number, y?: number, width?: number, height?: number): void;
static fromRect(other?: DOMRectInit): DOMRect;
}
declare class DOMRectList {
+length: number;
@@iterator(): Iterator<DOMRect>;
item(index: number): DOMRect;
[index: number]: DOMRect;
}
declare class DOMRectReadOnly {
+bottom: number;
+height: number;
+left: number;
+right: number;
+top: number;
+width: number;
+x: number;
+y: number;
constructor(x?: number, y?: number, width?: number, height?: number): void;
static fromRect(other?: DOMRectInit): DOMRectReadOnly;
toJSON(): Object;
}

1710
flow-typed/environments/html.js vendored Normal file

File diff suppressed because it is too large Load Diff

4286
flow-typed/environments/node.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,248 @@
// flow-typed signature: f6bda44505d6258bae702a65ee2878f2
// flow-typed version: 840509ea9d/serviceworkers/flow_>=v0.261.x
type FrameType = 'auxiliary' | 'top-level' | 'nested' | 'none';
type VisibilityState = 'hidden' | 'visible' | 'prerender' | 'unloaded';
declare class WindowClient extends Client {
visibilityState: VisibilityState;
focused: boolean;
focus(): Promise<WindowClient>;
navigate(url: string): Promise<WindowClient>;
}
declare class Client {
id: string;
reserved: boolean;
url: string;
frameType: FrameType;
postMessage(message: any, transfer?: Iterator<any> | Array<any>): void;
}
declare class ExtendableEvent extends Event {
waitUntil(f: Promise<mixed>): void;
}
type NotificationEvent$Init = {
...Event$Init,
notification: Notification,
action?: string,
...
};
declare class NotificationEvent extends ExtendableEvent {
constructor(type: string, eventInitDict?: NotificationEvent$Init): void;
+notification: Notification;
+action: string;
}
type ForeignFetchOptions = {
scopes: Iterator<string>,
origins: Iterator<string>,
...
};
declare class InstallEvent extends ExtendableEvent {
registerForeignFetch(options: ForeignFetchOptions): void;
}
declare class FetchEvent extends ExtendableEvent {
request: Request;
clientId: string;
isReload: boolean;
respondWith(response: Response | Promise<Response>): void;
preloadResponse: Promise<?Response>;
}
type ClientType = 'window' | 'worker' | 'sharedworker' | 'all';
type ClientQueryOptions = {
includeUncontrolled?: boolean,
includeReserved?: boolean,
type?: ClientType,
...
};
declare class Clients {
get(id: string): Promise<?Client>;
matchAll(options?: ClientQueryOptions): Promise<Array<Client>>;
openWindow(url: string): Promise<?WindowClient>;
claim(): Promise<void>;
}
type ServiceWorkerState =
| 'installing'
| 'installed'
| 'activating'
| 'activated'
| 'redundant';
declare class ServiceWorker extends EventTarget {
scriptURL: string;
state: ServiceWorkerState;
postMessage(message: any, transfer?: Iterator<any>): void;
onstatechange?: EventHandler;
}
declare class NavigationPreloadState {
enabled: boolean;
headerValue: string;
}
declare class NavigationPreloadManager {
enable: Promise<void>;
disable: Promise<void>;
setHeaderValue(value: string): Promise<void>;
getState: Promise<NavigationPreloadState>;
}
type PushSubscriptionOptions = {
userVisibleOnly?: boolean,
applicationServerKey?: string | ArrayBuffer | $ArrayBufferView,
...
};
declare class PushSubscriptionJSON {
endpoint: string;
expirationTime: number | null;
keys: {[string]: string, ...};
}
declare class PushSubscription {
+endpoint: string;
+expirationTime: number | null;
+options: PushSubscriptionOptions;
getKey(name: string): ArrayBuffer | null;
toJSON(): PushSubscriptionJSON;
unsubscribe(): Promise<boolean>;
}
declare class PushManager {
+supportedContentEncodings: Array<string>;
subscribe(options?: PushSubscriptionOptions): Promise<PushSubscription>;
getSubscription(): Promise<PushSubscription | null>;
permissionState(
options?: PushSubscriptionOptions
): Promise<'granted' | 'denied' | 'prompt'>;
}
type ServiceWorkerUpdateViaCache = 'imports' | 'all' | 'none';
type GetNotificationOptions = {
tag?: string,
...
};
declare class ServiceWorkerRegistration extends EventTarget {
+installing: ?ServiceWorker;
+waiting: ?ServiceWorker;
+active: ?ServiceWorker;
+navigationPreload: NavigationPreloadManager;
+scope: string;
+updateViaCache: ServiceWorkerUpdateViaCache;
+pushManager: PushManager;
getNotifications?: (
filter?: GetNotificationOptions
) => Promise<$ReadOnlyArray<Notification>>;
showNotification?: (
title: string,
options?: NotificationOptions
) => Promise<void>;
update(): Promise<void>;
unregister(): Promise<boolean>;
onupdatefound?: EventHandler;
}
type WorkerType = 'classic' | 'module';
type RegistrationOptions = {
scope?: string,
type?: WorkerType,
updateViaCache?: ServiceWorkerUpdateViaCache,
...
};
declare class ServiceWorkerContainer extends EventTarget {
+controller: ?ServiceWorker;
+ready: Promise<ServiceWorkerRegistration>;
getRegistration(
clientURL?: string
): Promise<ServiceWorkerRegistration | void>;
getRegistrations(): Promise<Iterator<ServiceWorkerRegistration>>;
register(
scriptURL: string | TrustedScriptURL,
options?: RegistrationOptions
): Promise<ServiceWorkerRegistration>;
startMessages(): void;
oncontrollerchange?: EventHandler;
onmessage?: EventHandler;
onmessageerror?: EventHandler;
}
/**
* This feature has been removed from the Web standards.
*/
declare class ServiceWorkerMessageEvent extends Event {
data: any;
lastEventId: string;
origin: string;
ports: Array<MessagePort>;
source: ?(ServiceWorker | MessagePort);
}
declare class ExtendableMessageEvent extends ExtendableEvent {
data: any;
lastEventId: string;
origin: string;
ports: Array<MessagePort>;
source: ?(ServiceWorker | MessagePort);
}
type CacheQueryOptions = {
ignoreSearch?: boolean,
ignoreMethod?: boolean,
ignoreVary?: boolean,
cacheName?: string,
...
};
declare class Cache {
match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>;
matchAll(
request: RequestInfo,
options?: CacheQueryOptions
): Promise<Array<Response>>;
add(request: RequestInfo): Promise<void>;
addAll(requests: Array<RequestInfo>): Promise<void>;
put(request: RequestInfo, response: Response): Promise<void>;
delete(request: RequestInfo, options?: CacheQueryOptions): Promise<boolean>;
keys(
request?: RequestInfo,
options?: CacheQueryOptions
): Promise<Array<Request>>;
}
declare class CacheStorage {
match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>;
has(cacheName: string): Promise<true>;
open(cacheName: string): Promise<Cache>;
delete(cacheName: string): Promise<boolean>;
keys(): Promise<Array<string>>;
}
// Service worker global scope
// https://www.w3.org/TR/service-workers/#service-worker-global-scope
declare var clients: Clients;
declare var caches: CacheStorage;
declare var registration: ServiceWorkerRegistration;
declare function skipWaiting(): Promise<void>;
declare var onactivate: ?EventHandler;
declare var oninstall: ?EventHandler;
declare var onfetch: ?EventHandler;
declare var onforeignfetch: ?EventHandler;
declare var onmessage: ?EventHandler;

136
flow-typed/environments/streams.js vendored Normal file
View File

@@ -0,0 +1,136 @@
// flow-typed signature: e6e6768618776352dd676f63502aea4d
// flow-typed version: 40e7dfcbd5/streams/flow_>=v0.261.x
type TextEncodeOptions = {options?: boolean, ...};
declare class ReadableStreamController {
constructor(
stream: ReadableStream,
underlyingSource: UnderlyingSource,
size: number,
highWaterMark: number
): void;
desiredSize: number;
close(): void;
enqueue(chunk: any): void;
error(error: Error): void;
}
declare class ReadableStreamBYOBRequest {
constructor(controller: ReadableStreamController, view: $TypedArray): void;
view: $TypedArray;
respond(bytesWritten: number): ?any;
respondWithNewView(view: $TypedArray): ?any;
}
declare class ReadableByteStreamController extends ReadableStreamController {
constructor(
stream: ReadableStream,
underlyingSource: UnderlyingSource,
highWaterMark: number
): void;
byobRequest: ReadableStreamBYOBRequest;
}
declare class ReadableStreamReader {
constructor(stream: ReadableStream): void;
closed: boolean;
cancel(reason: string): void;
read(): Promise<{
value: ?any,
done: boolean,
...
}>;
releaseLock(): void;
}
declare interface UnderlyingSource {
autoAllocateChunkSize?: number;
type?: string;
start?: (controller: ReadableStreamController) => ?Promise<void>;
pull?: (controller: ReadableStreamController) => ?Promise<void>;
cancel?: (reason: string) => ?Promise<void>;
}
declare class TransformStream {
readable: ReadableStream;
writable: WritableStream;
}
interface PipeThroughTransformStream {
readable: ReadableStream;
writable: WritableStream;
}
type PipeToOptions = {
preventClose?: boolean,
preventAbort?: boolean,
preventCancel?: boolean,
...
};
type QueuingStrategy = {
highWaterMark: number,
size(chunk: ?any): number,
...
};
declare class ReadableStream {
constructor(
underlyingSource: ?UnderlyingSource,
queuingStrategy: ?QueuingStrategy
): void;
locked: boolean;
cancel(reason: string): void;
getReader(): ReadableStreamReader;
pipeThrough(transform: PipeThroughTransformStream, options: ?any): void;
pipeTo(dest: WritableStream, options: ?PipeToOptions): Promise<void>;
tee(): [ReadableStream, ReadableStream];
}
declare interface WritableStreamController {
error(error: Error): void;
}
declare interface UnderlyingSink {
autoAllocateChunkSize?: number;
type?: string;
abort?: (reason: string) => ?Promise<void>;
close?: (controller: WritableStreamController) => ?Promise<void>;
start?: (controller: WritableStreamController) => ?Promise<void>;
write?: (chunk: any, controller: WritableStreamController) => ?Promise<void>;
}
declare interface WritableStreamWriter {
closed: Promise<any>;
desiredSize?: number;
ready: Promise<any>;
abort(reason: string): ?Promise<any>;
close(): Promise<any>;
releaseLock(): void;
write(chunk: any): Promise<any>;
}
declare class WritableStream {
constructor(
underlyingSink: ?UnderlyingSink,
queuingStrategy: QueuingStrategy
): void;
locked: boolean;
abort(reason: string): void;
getWriter(): WritableStreamWriter;
}

View File

@@ -0,0 +1,193 @@
// flow-typed signature: 4631a74b6a0e6a1b4de2ba8c7bb141d6
// flow-typed version: 3e51657e95/web-animations/flow_>=v0.261.x
// https://www.w3.org/TR/web-animations-1/
type AnimationPlayState = 'idle' | 'running' | 'paused' | 'finished';
type AnimationReplaceState = 'active' | 'removed' | 'persisted';
type CompositeOperation = 'replace' | 'add' | 'accumulate';
type CompositeOperationOrAuto = 'replace' | 'add' | 'accumulate' | 'auto';
type FillMode = 'none' | 'forwards' | 'backwards' | 'both' | 'auto';
// This is actually web-animations-2
type IterationCompositeOperation = 'replace' | 'accumulate';
type PlaybackDirection =
| 'normal'
| 'reverse'
| 'alternate'
| 'alternate-reverse';
type AnimationPlaybackEvent$Init = Event$Init & {
currentTime?: number | null,
timelineTime?: number | null,
...
};
type BaseComputedKeyframe = {|
composite: CompositeOperationOrAuto,
computedOffset: number,
easing: string,
offset: number | null,
|};
type BaseKeyframe = {|
composite: CompositeOperationOrAuto,
easing: string,
offset: number | null,
|};
type BasePropertyIndexedKeyframe = {|
composite: CompositeOperationOrAuto | Array<CompositeOperationOrAuto>,
easing: string | Array<string>,
offset: number | null | Array<number | null>,
|};
type ComputedEffectTiming = {|
...EffectTiming,
currentIteration: number | null,
progress: number | null,
|};
type ComputedKeyframe = {
composite: CompositeOperationOrAuto,
computedOffset: number,
easing: string,
offset: number | null,
[property: string]: string | number | null | void,
...
};
type DocumentTimelineOptions = {|
originTime: number,
|};
type EffectTiming = {|
direction: PlaybackDirection,
easing: string,
fill: FillMode,
iterations: number,
iterationStart: number,
|};
type GetAnimationsOptions = {|
pseudoElement: string | null,
subtree: boolean,
|};
type KeyframeAnimationOptions = {|
...KeyframeEffectOptions,
id: string,
timeline: AnimationTimeline | null,
|};
type KeyframeEffectOptions = {|
...EffectTiming,
composite: CompositeOperation,
pseudoElement: string | null,
|};
type Keyframe = {
composite?: CompositeOperationOrAuto,
easing?: string,
offset?: number | null,
[property: string]: string | number | null | void,
...
};
type OptionalEffectTiming = Partial<EffectTiming>;
type PropertyIndexedKeyframes = {
composite?: CompositeOperationOrAuto | CompositeOperationOrAuto[],
easing?: string | string[],
offset?: number | (number | null)[],
[property: string]:
| string
| string[]
| number
| null
| (number | null)[]
| void,
...
};
declare class Animation extends EventTarget {
constructor(
effect?: AnimationEffect | null,
timeline?: AnimationTimeline | null
): void;
id: string;
effect: AnimationEffect | null;
timeline: AnimationTimeline | null;
startTime: number | null;
currentTime: number | null;
playbackRate: number;
+playState: AnimationPlayState;
+replaceState: AnimationReplaceState;
+pending: boolean;
+ready: Promise<Animation>;
+finished: Promise<Animation>;
onfinish: ?(ev: AnimationPlaybackEvent) => mixed;
oncancel: ?(ev: AnimationPlaybackEvent) => mixed;
onremove: ?(ev: AnimationPlaybackEvent) => mixed;
cancel(): void;
finish(): void;
play(): void;
pause(): void;
updatePlaybackRate(playbackRate: number): void;
reverse(): void;
persist(): void;
commitStyles(): void;
}
declare class AnimationEffect {
getTiming(): EffectTiming;
getComputedTiming(): ComputedEffectTiming;
updateTiming(timing?: OptionalEffectTiming): void;
}
declare class AnimationPlaybackEvent extends Event {
constructor(
type: string,
animationEventInitDict?: AnimationPlaybackEvent$Init
): void;
+currentTime: number | null;
+timelineTime: number | null;
}
declare class AnimationTimeline {
+currentTime: number | null;
}
declare class DocumentTimeline extends AnimationTimeline {
constructor(options?: DocumentTimelineOptions): void;
}
declare class KeyframeEffect extends AnimationEffect {
constructor(
target: Element | null,
keyframes: Keyframe[] | PropertyIndexedKeyframes | null,
options?: number | KeyframeEffectOptions
): void;
constructor(source: KeyframeEffect): void;
target: Element | null;
composite: CompositeOperation;
// This is actually web-animations-2
iterationComposite: IterationCompositeOperation;
getKeyframes(): ComputedKeyframe[];
setKeyframes(keyframes: Keyframe[] | PropertyIndexedKeyframes | null): void;
}
declare class mixin$Animatable {
animate(
keyframes: Keyframe[] | PropertyIndexedKeyframes | null,
options?: number | KeyframeAnimationOptions
): Animation;
getAnimations(options?: GetAnimationsOptions): Array<Animation>;
}

View File

@@ -0,0 +1,60 @@
// flow-typed signature: 132e48034ef4756600e1d98681a166b5
// flow-typed version: c6154227d1/error-stack-parser_v2.x.x/flow_>=v0.104.x
declare module 'error-stack-parser' {
declare interface StackFrame {
constructor(object: StackFrame): StackFrame;
isConstructor?: boolean;
getIsConstructor(): boolean;
setIsConstructor(): void;
isEval?: boolean;
getIsEval(): boolean;
setIsEval(): void;
isNative?: boolean;
getIsNative(): boolean;
setIsNative(): void;
isTopLevel?: boolean;
getIsTopLevel(): boolean;
setIsTopLevel(): void;
columnNumber?: number;
getColumnNumber(): number;
setColumnNumber(): void;
lineNumber?: number;
getLineNumber(): number;
setLineNumber(): void;
fileName?: string;
getFileName(): string;
setFileName(): void;
functionName?: string;
getFunctionName(): string;
setFunctionName(): void;
source?: string;
getSource(): string;
setSource(): void;
args?: any[];
getArgs(): any[];
setArgs(): void;
evalOrigin?: StackFrame;
getEvalOrigin(): StackFrame;
setEvalOrigin(): void;
toString(): string;
}
declare class ErrorStackParser {
parse(error: Error): Array<StackFrame>;
}
declare module.exports: ErrorStackParser;
}

27
flow-typed/npm/minimist_v1.x.x.js vendored Normal file
View File

@@ -0,0 +1,27 @@
// flow-typed signature: d48da8db828529253fc20b80747846ea
// flow-typed version: c6154227d1/minimist_v1.x.x/flow_>=v0.104.x
declare module 'minimist' {
declare type minimistOptions = {
string?: string | Array<string>,
boolean?: boolean | string | Array<string>,
alias?: {[arg: string]: string | Array<string>, ...},
default?: {[arg: string]: any, ...},
stopEarly?: boolean,
// TODO: Strings as keys don't work...
// '--'? boolean,
unknown?: (param: string) => boolean,
...
};
declare type minimistOutput = {
[flag: string]: string | boolean,
_: Array<string>,
...
};
declare module.exports: (
argv: Array<string>,
opts?: minimistOptions
) => minimistOutput;
}

Some files were not shown because too many files have changed in this diff Show More