@samitouri / QOS-React / commits / 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>

Joseph Savona committed Aug 21, 2025 at 14:53 UTC 7d29ecbeb24327fdcd889fe184311bbeb0f04c30
87 files changed +3298 -1531
babel.config-ts.js
+1
@@ -8,6 +8,7 @@ module.exports = {
8 '@babel/plugin-syntax-jsx',
9 '@babel/plugin-transform-flow-strip-types',
10 ['@babel/plugin-transform-class-properties', {loose: true}],
11 + ['@babel/plugin-transform-private-methods', {loose: true}],
12 '@babel/plugin-transform-classes',
13 ],
14 presets: [
compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts
+351 -11
@@ -47,8 +47,9 @@ export enum ErrorSeverity {
47 }
48
49 export type CompilerDiagnosticOptions = {
50 + category: ErrorCategory;
51 severity: ErrorSeverity;
51 - category: string;
52 + reason: string;
53 description: string;
54 details: Array<CompilerDiagnosticDetail>;
55 suggestions?: Array<CompilerSuggestion> | null | undefined;
@@ -91,9 +92,10 @@ export type CompilerSuggestion =
92 };
93
94 export type CompilerErrorDetailOptions = {
95 + category: ErrorCategory;
96 + severity: ErrorSeverity;
97 reason: string;
98 description?: string | null | undefined;
96 - severity: ErrorSeverity;
99 loc: SourceLocation | null;
100 suggestions?: Array<CompilerSuggestion> | null | undefined;
101 };
@@ -119,8 +121,8 @@ export class CompilerDiagnostic {
121 return new CompilerDiagnostic({...options, details: []});
122 }
123
122 - get category(): CompilerDiagnosticOptions['category'] {
123 - return this.options.category;
124 + get reason(): CompilerDiagnosticOptions['reason'] {
125 + return this.options.reason;
126 }
127 get description(): CompilerDiagnosticOptions['description'] {
128 return this.options.description;
@@ -131,6 +133,9 @@ export class CompilerDiagnostic {
133 get suggestions(): CompilerDiagnosticOptions['suggestions'] {
134 return this.options.suggestions;
135 }
136 + get category(): ErrorCategory {
137 + return this.options.category;
138 + }
139
140 withDetail(detail: CompilerDiagnosticDetail): CompilerDiagnostic {
141 this.options.details.push(detail);
@@ -148,7 +153,7 @@ export class CompilerDiagnostic {
153
154 printErrorMessage(source: string, options: PrintErrorMessageOptions): string {
155 const buffer = [
151 - printErrorSummary(this.severity, this.category),
156 + printErrorSummary(this.severity, this.reason),
157 '\n\n',
158 this.description,
159 ];
@@ -193,7 +198,7 @@ export class CompilerDiagnostic {
198 }
199
200 toString(): string {
196 - const buffer = [printErrorSummary(this.severity, this.category)];
201 + const buffer = [printErrorSummary(this.severity, this.reason)];
202 if (this.description != null) {
203 buffer.push(`. ${this.description}.`);
204 }
@@ -231,6 +236,9 @@ export class CompilerErrorDetail {
236 get suggestions(): CompilerErrorDetailOptions['suggestions'] {
237 return this.options.suggestions;
238 }
239 + get category(): ErrorCategory {
240 + return this.options.category;
241 + }
242
243 primaryLocation(): SourceLocation | null {
244 return this.loc;
@@ -280,13 +288,14 @@ export class CompilerError extends Error {
288
289 static invariant(
290 condition: unknown,
283 - options: Omit<CompilerErrorDetailOptions, 'severity'>,
291 + options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
292 ): asserts condition {
293 if (!condition) {
294 const errors = new CompilerError();
295 errors.pushErrorDetail(
296 new CompilerErrorDetail({
297 ...options,
298 + category: ErrorCategory.Invariant,
299 severity: ErrorSeverity.Invariant,
300 }),
301 );
@@ -301,23 +310,28 @@ export class CompilerError extends Error {
310 }
311
312 static throwTodo(
304 - options: Omit<CompilerErrorDetailOptions, 'severity'>,
313 + options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
314 ): never {
315 const errors = new CompilerError();
316 errors.pushErrorDetail(
308 - new CompilerErrorDetail({...options, severity: ErrorSeverity.Todo}),
317 + new CompilerErrorDetail({
318 + ...options,
319 + severity: ErrorSeverity.Todo,
320 + category: ErrorCategory.Todo,
321 + }),
322 );
323 throw errors;
324 }
325
326 static throwInvalidJS(
314 - options: Omit<CompilerErrorDetailOptions, 'severity'>,
327 + options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
328 ): never {
329 const errors = new CompilerError();
330 errors.pushErrorDetail(
331 new CompilerErrorDetail({
332 ...options,
333 severity: ErrorSeverity.InvalidJS,
334 + category: ErrorCategory.Syntax,
335 }),
336 );
337 throw errors;
@@ -337,13 +351,14 @@ export class CompilerError extends Error {
351 }
352
353 static throwInvalidConfig(
340 - options: Omit<CompilerErrorDetailOptions, 'severity'>,
354 + options: Omit<CompilerErrorDetailOptions, 'severity' | 'category'>,
355 ): never {
356 const errors = new CompilerError();
357 errors.pushErrorDetail(
358 new CompilerErrorDetail({
359 ...options,
360 severity: ErrorSeverity.InvalidConfig,
361 + category: ErrorCategory.Config,
362 }),
363 );
364 throw errors;
@@ -407,6 +422,7 @@ export class CompilerError extends Error {
422
423 push(options: CompilerErrorDetailOptions): CompilerErrorDetail {
424 const detail = new CompilerErrorDetail({
425 + category: options.category,
426 reason: options.reason,
427 description: options.description ?? null,
428 severity: options.severity,
@@ -507,3 +523,327 @@ function printErrorSummary(severity: ErrorSeverity, message: string): string {
523 }
524 return `${severityCategory}: ${message}`;
525 }
526 +
527 +/**
528 + * See getRuleForCategory() for how these map to ESLint rules
529 + */
530 +export enum ErrorCategory {
531 + // Checking for valid hooks usage (non conditional, non-first class, non reactive, etc)
532 + Hooks = 'Hooks',
533 +
534 + // Checking for no capitalized calls (not definitively an error, hence separating)
535 + CapitalizedCalls = 'CapitalizedCalls',
536 +
537 + // Checking for static components
538 + StaticComponents = 'StaticComponents',
539 +
540 + // Checking for valid usage of manual memoization
541 + UseMemo = 'UseMemo',
542 +
543 + // Checks that manual memoization is preserved
544 + PreserveManualMemo = 'PreserveManualMemo',
545 +
546 + // Checking for no mutations of props, hook arguments, hook return values
547 + Immutability = 'Immutability',
548 +
549 + // Checking for assignments to globals
550 + Globals = 'Globals',
551 +
552 + // Checking for valid usage of refs, ie no access during render
553 + Refs = 'Refs',
554 +
555 + // Checks for memoized effect deps
556 + EffectDependencies = 'EffectDependencies',
557 +
558 + // Checks for no setState in effect bodies
559 + EffectSetState = 'EffectSetState',
560 +
561 + EffectDerivationsOfState = 'EffectDerivationsOfState',
562 +
563 + // Validates against try/catch in place of error boundaries
564 + ErrorBoundaries = 'ErrorBoundaries',
565 +
566 + // Checking for pure functions
567 + Purity = 'Purity',
568 +
569 + // Validates against setState in render
570 + RenderSetState = 'RenderSetState',
571 +
572 + // Internal invariants
573 + Invariant = 'Invariant',
574 +
575 + // Todos
576 + Todo = 'Todo',
577 +
578 + // Syntax errors
579 + Syntax = 'Syntax',
580 +
581 + // Checks for use of unsupported syntax
582 + UnsupportedSyntax = 'UnsupportedSyntax',
583 +
584 + // Config errors
585 + Config = 'Config',
586 +
587 + // Gating error
588 + Gating = 'Gating',
589 +
590 + // Suppressions
591 + Suppression = 'Suppression',
592 +
593 + // Issues with auto deps
594 + AutomaticEffectDependencies = 'AutomaticEffectDependencies',
595 +
596 + // Issues with `fire`
597 + Fire = 'Fire',
598 +
599 + // fbt-specific issues
600 + FBT = 'FBT',
601 +}
602 +
603 +export type LintRule = {
604 + // Stores the category the rule corresponds to, used to filter errors when reporting
605 + category: ErrorCategory;
606 +
607 + /**
608 + * The "name" of the rule as it will be used by developers to enable/disable, eg
609 + * "eslint-disable-nest line <name>"
610 + */
611 + name: string;
612 +
613 + /**
614 + * A description of the rule that appears somewhere in ESLint. This does not affect
615 + * how error messages are formatted
616 + */
617 + description: string;
618 +
619 + /**
620 + * If true, this rule will automatically appear in the default, "recommended" ESLint
621 + * rule set. Otherwise it will be part of an `allRules` export that developers can
622 + * use to opt-in to showing output of all possible rules.
623 + *
624 + * NOTE: not all validations are enabled by default! Setting this flag only affects
625 + * whether a given rule is part of the recommended set. The corresponding validation
626 + * also should be enabled by default if you want the error to actually show up!
627 + */
628 + recommended: boolean;
629 +};
630 +
631 +export function getRuleForCategory(category: ErrorCategory): LintRule {
632 + switch (category) {
633 + case ErrorCategory.AutomaticEffectDependencies: {
634 + return {
635 + category,
636 + name: 'automatic-effect-dependencies',
637 + description:
638 + 'Verifies that automatic effect dependencies are compiled if opted-in',
639 + recommended: true,
640 + };
641 + }
642 + case ErrorCategory.CapitalizedCalls: {
643 + return {
644 + category,
645 + name: 'capitalized-calls',
646 + description:
647 + 'Validates against calling capitalized functions/methods instead of using JSX',
648 + recommended: false,
649 + };
650 + }
651 + case ErrorCategory.Config: {
652 + return {
653 + category,
654 + name: 'config',
655 + description: 'Validates the configuration',
656 + recommended: true,
657 + };
658 + }
659 + case ErrorCategory.EffectDependencies: {
660 + return {
661 + category,
662 + name: 'memoized-effect-dependencies',
663 + description: 'Validates that effect dependencies are memoized',
664 + recommended: false,
665 + };
666 + }
667 + case ErrorCategory.EffectDerivationsOfState: {
668 + return {
669 + category,
670 + name: 'no-deriving-state-in-effects',
671 + description:
672 + 'Validates against deriving values from state in an effect',
673 + recommended: false,
674 + };
675 + }
676 + case ErrorCategory.EffectSetState: {
677 + return {
678 + category,
679 + name: 'set-state-in-effect',
680 + description:
681 + 'Validates against calling setState synchronously in an effect',
682 + recommended: true,
683 + };
684 + }
685 + case ErrorCategory.ErrorBoundaries: {
686 + return {
687 + category,
688 + name: 'error-boundaries',
689 + description:
690 + 'Validates usage of error boundaries instead of try/catch for errors in JSX',
691 + recommended: true,
692 + };
693 + }
694 + case ErrorCategory.FBT: {
695 + return {
696 + category,
697 + name: 'fbt',
698 + description: 'Validates usage of fbt',
699 + recommended: false,
700 + };
701 + }
702 + case ErrorCategory.Fire: {
703 + return {
704 + category,
705 + name: 'fire',
706 + description: 'Validates usage of `fire`',
707 + recommended: false,
708 + };
709 + }
710 + case ErrorCategory.Gating: {
711 + return {
712 + category,
713 + name: 'gating',
714 + description: 'Validates configuration of gating mode',
715 + recommended: true,
716 + };
717 + }
718 + case ErrorCategory.Globals: {
719 + return {
720 + category,
721 + name: 'globals',
722 + description:
723 + 'Validates against assignment/mutation of globals during render',
724 + recommended: true,
725 + };
726 + }
727 + case ErrorCategory.Hooks: {
728 + return {
729 + category,
730 + name: 'hooks',
731 + description: 'Validates the rules of hooks',
732 + /**
733 + * TODO: the "Hooks" rule largely reimplements the "rules-of-hooks" non-compiler rule.
734 + * We need to dedeupe these (moving the remaining bits into the compiler) and then enable
735 + * this rule.
736 + */
737 + recommended: false,
738 + };
739 + }
740 + case ErrorCategory.Immutability: {
741 + return {
742 + category,
743 + name: 'immutability',
744 + description:
745 + 'Validates that immutable values (props, state, etc) are not mutated',
746 + recommended: true,
747 + };
748 + }
749 + case ErrorCategory.Invariant: {
750 + return {
751 + category,
752 + name: 'invariant',
753 + description: 'Internal invariants',
754 + recommended: false,
755 + };
756 + }
757 + case ErrorCategory.PreserveManualMemo: {
758 + return {
759 + category,
760 + name: 'preserve-manual-memoization',
761 + description:
762 + 'Validates that existing manual memoized is preserved by the compiler',
763 + recommended: true,
764 + };
765 + }
766 + case ErrorCategory.Purity: {
767 + return {
768 + category,
769 + name: 'purity',
770 + description:
771 + 'Validates that the component/hook is pure, and does not call known-impure functions',
772 + recommended: true,
773 + };
774 + }
775 + case ErrorCategory.Refs: {
776 + return {
777 + category,
778 + name: 'refs',
779 + description:
780 + 'Validates correct usage of refs, not reading/writing during render',
781 + recommended: true,
782 + };
783 + }
784 + case ErrorCategory.RenderSetState: {
785 + return {
786 + category,
787 + name: 'set-state-in-render',
788 + description: 'Validates against setting state during render',
789 + recommended: true,
790 + };
791 + }
792 + case ErrorCategory.StaticComponents: {
793 + return {
794 + category,
795 + name: 'static-components',
796 + description:
797 + 'Validates that components are static, not recreated every render',
798 + recommended: true,
799 + };
800 + }
801 + case ErrorCategory.Suppression: {
802 + return {
803 + category,
804 + name: 'rule-suppression',
805 + description: 'Validates against suppression of other rules',
806 + recommended: false,
807 + };
808 + }
809 + case ErrorCategory.Syntax: {
810 + return {
811 + category,
812 + name: 'syntax',
813 + description: 'Validates against invalid syntax',
814 + recommended: false,
815 + };
816 + }
817 + case ErrorCategory.Todo: {
818 + return {
819 + category,
820 + name: 'todo',
821 + description: 'Unimplemented features',
822 + recommended: false,
823 + };
824 + }
825 + case ErrorCategory.UnsupportedSyntax: {
826 + return {
827 + category,
828 + name: 'unsupported-syntax',
829 + description: 'Validates against syntax that we do not plan to support',
830 + recommended: true,
831 + };
832 + }
833 + case ErrorCategory.UseMemo: {
834 + return {
835 + category,
836 + name: 'use-memo',
837 + description: 'Validates usage of the useMemo() hook',
838 + recommended: true,
839 + };
840 + }
841 + default: {
842 + assertExhaustive(category, `Unsupported category ${category}`);
843 + }
844 + }
845 +}
846 +
847 +export const LintRules: Array<LintRule> = Object.keys(ErrorCategory).map(
848 + category => getRuleForCategory(category as any),
849 +);
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts
+3 -1
@@ -9,7 +9,7 @@ import {NodePath} from '@babel/core';
9 import * as t from '@babel/types';
10 import {Scope as BabelScope} from '@babel/traverse';
11
12 -import {CompilerError, ErrorSeverity} from '../CompilerError';
12 +import {CompilerError, ErrorCategory, ErrorSeverity} from '../CompilerError';
13 import {
14 EnvironmentConfig,
15 GeneratedSource,
@@ -38,6 +38,7 @@ export function validateRestrictedImports(
38 ImportDeclaration(importDeclPath) {
39 if (restrictedImports.has(importDeclPath.node.source.value)) {
40 error.push({
41 + category: ErrorCategory.Todo,
42 severity: ErrorSeverity.Todo,
43 reason: 'Bailing out due to blocklisted import',
44 description: `Import from module ${importDeclPath.node.source.value}`,
@@ -205,6 +206,7 @@ export class ProgramContext {
206 }
207 const error = new CompilerError();
208 error.push({
209 + category: ErrorCategory.Todo,
210 severity: ErrorSeverity.Todo,
211 reason: 'Encountered conflicting global in generated program',
212 description: `Conflict from local binding ${name}`,
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
+5
@@ -10,6 +10,7 @@ import * as t from '@babel/types';
10 import {
11 CompilerError,
12 CompilerErrorDetail,
13 + ErrorCategory,
14 ErrorSeverity,
15 } from '../CompilerError';
16 import {ExternalFunction, ReactFunctionType} from '../HIR/Environment';
@@ -105,6 +106,7 @@ function findDirectivesDynamicGating(
106 reason: `Dynamic gating directive is not a valid JavaScript identifier`,
107 description: `Found '${directive.value.value}'`,
108 severity: ErrorSeverity.InvalidReact,
109 + category: ErrorCategory.Gating,
110 loc: directive.loc ?? null,
111 suggestions: null,
112 });
@@ -121,6 +123,7 @@ function findDirectivesDynamicGating(
123 .map(r => r.directive.value.value)
124 .join(', ')}]`,
125 severity: ErrorSeverity.InvalidReact,
126 + category: ErrorCategory.Gating,
127 loc: result[0].directive.loc ?? null,
128 suggestions: null,
129 });
@@ -456,6 +459,7 @@ export function compileProgram(
459 reason:
460 'Unexpected compiled functions when module scope opt-out is present',
461 severity: ErrorSeverity.Invariant,
462 + category: ErrorCategory.Invariant,
463 loc: null,
464 }),
465 );
@@ -811,6 +815,7 @@ function shouldSkipCompilation(
815 description:
816 "When the 'sources' config options is specified, the React compiler will only compile files with a name",
817 severity: ErrorSeverity.InvalidConfig,
818 + category: ErrorCategory.Config,
819 loc: null,
820 }),
821 );
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Suppression.ts
+3 -1
@@ -11,6 +11,7 @@ import {
11 CompilerDiagnostic,
12 CompilerError,
13 CompilerSuggestionOperation,
14 + ErrorCategory,
15 ErrorSeverity,
16 } from '../CompilerError';
17 import {assertExhaustive} from '../Utils/utils';
@@ -183,9 +184,10 @@ export function suppressionsToCompilerError(
184 }
185 error.pushDiagnostic(
186 CompilerDiagnostic.create({
186 - category: reason,
187 + reason: reason,
188 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()}\``,
189 severity: ErrorSeverity.InvalidReact,
190 + category: ErrorCategory.Suppression,
191 suggestions: [
192 {
193 description: suggestion,
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts
+9 -4
@@ -13,7 +13,11 @@ import {getOrInsertWith} from '../Utils/utils';
13 import {Environment, GeneratedSource} from '../HIR';
14 import {DEFAULT_EXPORT} from '../HIR/Environment';
15 import {CompileProgramMetadata} from './Program';
16 -import {CompilerDiagnostic, CompilerDiagnosticOptions} from '../CompilerError';
16 +import {
17 + CompilerDiagnostic,
18 + CompilerDiagnosticOptions,
19 + ErrorCategory,
20 +} from '../CompilerError';
21
22 function throwInvalidReact(
23 options: Omit<CompilerDiagnosticOptions, 'severity'>,
@@ -92,7 +96,8 @@ function assertValidEffectImportReference(
96 */
97 throwInvalidReact(
98 {
95 - category:
99 + category: ErrorCategory.AutomaticEffectDependencies,
100 + reason:
101 'Cannot infer dependencies of this effect. This will break your build!',
102 description:
103 'To resolve, either pass a dependency array or fix reported compiler bailout diagnostics.' +
@@ -123,8 +128,8 @@ function assertValidFireImportReference(
128 );
129 throwInvalidReact(
130 {
126 - category:
127 - '[Fire] Untransformed reference to compiler-required feature.',
131 + category: ErrorCategory.Fire,
132 + reason: '[Fire] Untransformed reference to compiler-required feature.',
133 description:
134 'Either remove this `fire` call or ensure it is successfully transformed by the compiler' +
135 maybeErrorDiagnostic
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+79 -4
@@ -12,6 +12,7 @@ import {
12 CompilerDiagnostic,
13 CompilerError,
14 CompilerSuggestionOperation,
15 + ErrorCategory,
16 ErrorSeverity,
17 } from '../CompilerError';
18 import {Err, Ok, Result} from '../Utils/Result';
@@ -108,7 +109,8 @@ export function lower(
109 builder.errors.pushDiagnostic(
110 CompilerDiagnostic.create({
111 severity: ErrorSeverity.Invariant,
111 - category: 'Could not find binding',
112 + category: ErrorCategory.Invariant,
113 + reason: 'Could not find binding',
114 description: `[BuildHIR] Could not find binding for param \`${param.node.name}\`.`,
115 }).withDetail({
116 kind: 'error',
@@ -172,7 +174,8 @@ export function lower(
174 builder.errors.pushDiagnostic(
175 CompilerDiagnostic.create({
176 severity: ErrorSeverity.Todo,
175 - category: `Handle ${param.node.type} parameters`,
177 + category: ErrorCategory.Todo,
178 + reason: `Handle ${param.node.type} parameters`,
179 description: `[BuildHIR] Add support for ${param.node.type} parameters.`,
180 }).withDetail({
181 kind: 'error',
@@ -203,7 +206,8 @@ export function lower(
206 builder.errors.pushDiagnostic(
207 CompilerDiagnostic.create({
208 severity: ErrorSeverity.InvalidJS,
206 - category: `Unexpected function body kind`,
209 + category: ErrorCategory.Syntax,
210 + reason: `Unexpected function body kind`,
211 description: `Expected function body to be an expression or a block statement, got \`${body.type}\`.`,
212 }).withDetail({
213 kind: 'error',
@@ -273,6 +277,7 @@ function lowerStatement(
277 reason:
278 '(BuildHIR::lowerStatement) Support ThrowStatement inside of try/catch',
279 severity: ErrorSeverity.Todo,
280 + category: ErrorCategory.Todo,
281 loc: stmt.node.loc ?? null,
282 suggestions: null,
283 });
@@ -460,6 +465,7 @@ function lowerStatement(
465 } else if (!binding.path.isVariableDeclarator()) {
466 builder.errors.push({
467 severity: ErrorSeverity.Todo,
468 + category: ErrorCategory.Todo,
469 reason: 'Unsupported declaration type for hoisting',
470 description: `variable "${binding.identifier.name}" declared with ${binding.path.type}`,
471 suggestions: null,
@@ -469,6 +475,7 @@ function lowerStatement(
475 } else {
476 builder.errors.push({
477 severity: ErrorSeverity.Todo,
478 + category: ErrorCategory.Todo,
479 reason: 'Handle non-const declarations for hoisting',
480 description: `variable "${binding.identifier.name}" declared with ${binding.kind}`,
481 suggestions: null,
@@ -549,6 +556,7 @@ function lowerStatement(
556 reason:
557 '(BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement',
558 severity: ErrorSeverity.Todo,
559 + category: ErrorCategory.Todo,
560 loc: stmt.node.loc ?? null,
561 suggestions: null,
562 });
@@ -621,6 +629,7 @@ function lowerStatement(
629 builder.errors.push({
630 reason: `(BuildHIR::lowerStatement) Handle empty test in ForStatement`,
631 severity: ErrorSeverity.Todo,
632 + category: ErrorCategory.Todo,
633 loc: stmt.node.loc ?? null,
634 suggestions: null,
635 });
@@ -772,6 +781,7 @@ function lowerStatement(
781 builder.errors.push({
782 reason: `Expected at most one \`default\` branch in a switch statement, this code should have failed to parse`,
783 severity: ErrorSeverity.InvalidJS,
784 + category: ErrorCategory.Syntax,
785 loc: case_.node.loc ?? null,
786 suggestions: null,
787 });
@@ -844,6 +854,7 @@ function lowerStatement(
854 builder.errors.push({
855 reason: `(BuildHIR::lowerStatement) Handle ${nodeKind} kinds in VariableDeclaration`,
856 severity: ErrorSeverity.Todo,
857 + category: ErrorCategory.Todo,
858 loc: stmt.node.loc ?? null,
859 suggestions: null,
860 });
@@ -872,6 +883,7 @@ function lowerStatement(
883 builder.errors.push({
884 reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
885 severity: ErrorSeverity.Invariant,
886 + category: ErrorCategory.Invariant,
887 loc: id.node.loc ?? null,
888 suggestions: null,
889 });
@@ -889,6 +901,7 @@ function lowerStatement(
901 builder.errors.push({
902 reason: `Expect \`const\` declaration not to be reassigned`,
903 severity: ErrorSeverity.InvalidJS,
904 + category: ErrorCategory.Syntax,
905 loc: id.node.loc ?? null,
906 suggestions: [
907 {
@@ -936,6 +949,7 @@ function lowerStatement(
949 reason: `Expected variable declaration to be an identifier if no initializer was provided`,
950 description: `Got a \`${id.type}\``,
951 severity: ErrorSeverity.InvalidJS,
952 + category: ErrorCategory.Syntax,
953 loc: stmt.node.loc ?? null,
954 suggestions: null,
955 });
@@ -1044,6 +1058,7 @@ function lowerStatement(
1058 builder.errors.push({
1059 reason: `(BuildHIR::lowerStatement) Handle for-await loops`,
1060 severity: ErrorSeverity.Todo,
1061 + category: ErrorCategory.Todo,
1062 loc: stmt.node.loc ?? null,
1063 suggestions: null,
1064 });
@@ -1276,6 +1291,7 @@ function lowerStatement(
1291 builder.errors.push({
1292 reason: `(BuildHIR::lowerStatement) Handle TryStatement without a catch clause`,
1293 severity: ErrorSeverity.Todo,
1294 + category: ErrorCategory.Todo,
1295 loc: stmt.node.loc ?? null,
1296 suggestions: null,
1297 });
@@ -1285,6 +1301,7 @@ function lowerStatement(
1301 builder.errors.push({
1302 reason: `(BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause`,
1303 severity: ErrorSeverity.Todo,
1304 + category: ErrorCategory.Todo,
1305 loc: stmt.node.loc ?? null,
1306 suggestions: null,
1307 });
@@ -1378,6 +1395,7 @@ function lowerStatement(
1395 reason: `JavaScript 'with' syntax is not supported`,
1396 description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,
1397 severity: ErrorSeverity.UnsupportedJS,
1398 + category: ErrorCategory.UnsupportedSyntax,
1399 loc: stmtPath.node.loc ?? null,
1400 suggestions: null,
1401 });
@@ -1398,6 +1416,7 @@ function lowerStatement(
1416 reason: 'Inline `class` declarations are not supported',
1417 description: `Move class declarations outside of components/hooks`,
1418 severity: ErrorSeverity.UnsupportedJS,
1419 + category: ErrorCategory.UnsupportedSyntax,
1420 loc: stmtPath.node.loc ?? null,
1421 suggestions: null,
1422 });
@@ -1427,6 +1446,7 @@ function lowerStatement(
1446 reason:
1447 'JavaScript `import` and `export` statements may only appear at the top level of a module',
1448 severity: ErrorSeverity.InvalidJS,
1449 + category: ErrorCategory.Syntax,
1450 loc: stmtPath.node.loc ?? null,
1451 suggestions: null,
1452 });
@@ -1442,6 +1462,7 @@ function lowerStatement(
1462 reason:
1463 'TypeScript `namespace` statements may only appear at the top level of a module',
1464 severity: ErrorSeverity.InvalidJS,
1465 + category: ErrorCategory.Syntax,
1466 loc: stmtPath.node.loc ?? null,
1467 suggestions: null,
1468 });
@@ -1520,6 +1541,7 @@ function lowerObjectPropertyKey(
1541 builder.errors.push({
1542 reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
1543 severity: ErrorSeverity.Todo,
1544 + category: ErrorCategory.Todo,
1545 loc: key.node.loc ?? null,
1546 suggestions: null,
1547 });
@@ -1545,6 +1567,7 @@ function lowerObjectPropertyKey(
1567 builder.errors.push({
1568 reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
1569 severity: ErrorSeverity.Todo,
1570 + category: ErrorCategory.Todo,
1571 loc: key.node.loc ?? null,
1572 suggestions: null,
1573 });
@@ -1602,6 +1625,7 @@ function lowerExpression(
1625 builder.errors.push({
1626 reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,
1627 severity: ErrorSeverity.Todo,
1628 + category: ErrorCategory.Todo,
1629 loc: valuePath.node.loc ?? null,
1630 suggestions: null,
1631 });
@@ -1628,6 +1652,7 @@ function lowerExpression(
1652 builder.errors.push({
1653 reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.node.kind} functions in ObjectExpression`,
1654 severity: ErrorSeverity.Todo,
1655 + category: ErrorCategory.Todo,
1656 loc: propertyPath.node.loc ?? null,
1657 suggestions: null,
1658 });
@@ -1649,6 +1674,7 @@ function lowerExpression(
1674 builder.errors.push({
1675 reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,
1676 severity: ErrorSeverity.Todo,
1677 + category: ErrorCategory.Todo,
1678 loc: propertyPath.node.loc ?? null,
1679 suggestions: null,
1680 });
@@ -1682,6 +1708,7 @@ function lowerExpression(
1708 builder.errors.push({
1709 reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
1710 severity: ErrorSeverity.Todo,
1711 + category: ErrorCategory.Todo,
1712 loc: element.node.loc ?? null,
1713 suggestions: null,
1714 });
@@ -1702,6 +1729,7 @@ function lowerExpression(
1729 reason: `Expected an expression as the \`new\` expression receiver (v8 intrinsics are not supported)`,
1730 description: `Got a \`${calleePath.node.type}\``,
1731 severity: ErrorSeverity.InvalidJS,
1732 + category: ErrorCategory.Syntax,
1733 loc: calleePath.node.loc ?? null,
1734 suggestions: null,
1735 });
@@ -1728,6 +1756,7 @@ function lowerExpression(
1756 builder.errors.push({
1757 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`,
1758 severity: ErrorSeverity.Todo,
1759 + category: ErrorCategory.Todo,
1760 loc: calleePath.node.loc ?? null,
1761 suggestions: null,
1762 });
@@ -1762,6 +1791,7 @@ function lowerExpression(
1791 builder.errors.push({
1792 reason: `(BuildHIR::lowerExpression) Expected Expression, got ${leftPath.type} lval in BinaryExpression`,
1793 severity: ErrorSeverity.Todo,
1794 + category: ErrorCategory.Todo,
1795 loc: leftPath.node.loc ?? null,
1796 suggestions: null,
1797 });
@@ -1774,6 +1804,7 @@ function lowerExpression(
1804 builder.errors.push({
1805 reason: `(BuildHIR::lowerExpression) Pipe operator not supported`,
1806 severity: ErrorSeverity.Todo,
1807 + category: ErrorCategory.Todo,
1808 loc: leftPath.node.loc ?? null,
1809 suggestions: null,
1810 });
@@ -1803,6 +1834,7 @@ function lowerExpression(
1834 builder.errors.push({
1835 reason: `Expected sequence expression to have at least one expression`,
1836 severity: ErrorSeverity.InvalidJS,
1837 + category: ErrorCategory.Syntax,
1838 loc: expr.node.loc ?? null,
1839 suggestions: null,
1840 });
@@ -2015,6 +2047,7 @@ function lowerExpression(
2047 reason: `(BuildHIR::lowerExpression) Unsupported syntax on the left side of an AssignmentExpression`,
2048 description: `Expected an LVal, got: ${left.type}`,
2049 severity: ErrorSeverity.Todo,
2050 + category: ErrorCategory.Todo,
2051 loc: left.node.loc ?? null,
2052 suggestions: null,
2053 });
@@ -2043,6 +2076,7 @@ function lowerExpression(
2076 builder.errors.push({
2077 reason: `(BuildHIR::lowerExpression) Handle ${operator} operators in AssignmentExpression`,
2078 severity: ErrorSeverity.Todo,
2079 + category: ErrorCategory.Todo,
2080 loc: expr.node.loc ?? null,
2081 suggestions: null,
2082 });
@@ -2142,6 +2176,7 @@ function lowerExpression(
2176 builder.errors.push({
2177 reason: `(BuildHIR::lowerExpression) Expected Identifier or MemberExpression, got ${expr.type} lval in AssignmentExpression`,
2178 severity: ErrorSeverity.Todo,
2179 + category: ErrorCategory.Todo,
2180 loc: expr.node.loc ?? null,
2181 suggestions: null,
2182 });
@@ -2181,6 +2216,7 @@ function lowerExpression(
2216 builder.errors.push({
2217 reason: `(BuildHIR::lowerExpression) Handle ${attribute.type} attributes in JSXElement`,
2218 severity: ErrorSeverity.Todo,
2219 + category: ErrorCategory.Todo,
2220 loc: attribute.node.loc ?? null,
2221 suggestions: null,
2222 });
@@ -2194,6 +2230,7 @@ function lowerExpression(
2230 builder.errors.push({
2231 reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name \`${propName}\``,
2232 severity: ErrorSeverity.Todo,
2233 + category: ErrorCategory.Todo,
2234 loc: namePath.node.loc ?? null,
2235 suggestions: null,
2236 });
@@ -2224,6 +2261,7 @@ function lowerExpression(
2261 builder.errors.push({
2262 reason: `(BuildHIR::lowerExpression) Handle ${valueExpr.type} attribute values in JSXElement`,
2263 severity: ErrorSeverity.Todo,
2264 + category: ErrorCategory.Todo,
2265 loc: valueExpr.node?.loc ?? null,
2266 suggestions: null,
2267 });
@@ -2234,6 +2272,7 @@ function lowerExpression(
2272 builder.errors.push({
2273 reason: `(BuildHIR::lowerExpression) Handle ${expression.type} expressions in JSXExpressionContainer within JSXElement`,
2274 severity: ErrorSeverity.Todo,
2275 + category: ErrorCategory.Todo,
2276 loc: valueExpr.node.loc ?? null,
2277 suggestions: null,
2278 });
@@ -2291,7 +2330,8 @@ function lowerExpression(
2330 if (locations.length > 1) {
2331 CompilerError.throwDiagnostic({
2332 severity: ErrorSeverity.Todo,
2294 - category: 'Support duplicate fbt tags',
2333 + category: ErrorCategory.FBT,
2334 + reason: 'Support duplicate fbt tags',
2335 description: `Support \`<${tagName}>\` tags with multiple \`<${tagName}:${name}>\` values`,
2336 details: locations.map(loc => {
2337 return {
@@ -2352,6 +2392,7 @@ function lowerExpression(
2392 reason:
2393 '(BuildHIR::lowerExpression) Handle tagged template with interpolations',
2394 severity: ErrorSeverity.Todo,
2395 + category: ErrorCategory.Todo,
2396 loc: exprPath.node.loc ?? null,
2397 suggestions: null,
2398 });
@@ -2370,6 +2411,7 @@ function lowerExpression(
2411 reason:
2412 '(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value',
2413 severity: ErrorSeverity.Todo,
2414 + category: ErrorCategory.Todo,
2415 loc: exprPath.node.loc ?? null,
2416 suggestions: null,
2417 });
@@ -2392,6 +2434,7 @@ function lowerExpression(
2434 builder.errors.push({
2435 reason: `Unexpected quasi and subexpression lengths in template literal`,
2436 severity: ErrorSeverity.InvalidJS,
2437 + category: ErrorCategory.Syntax,
2438 loc: exprPath.node.loc ?? null,
2439 suggestions: null,
2440 });
@@ -2402,6 +2445,7 @@ function lowerExpression(
2445 builder.errors.push({
2446 reason: `(BuildHIR::lowerAssignment) Handle TSType in TemplateLiteral.`,
2447 severity: ErrorSeverity.Todo,
2448 + category: ErrorCategory.Todo,
2449 loc: exprPath.node.loc ?? null,
2450 suggestions: null,
2451 });
@@ -2444,6 +2488,7 @@ function lowerExpression(
2488 builder.errors.push({
2489 reason: `Only object properties can be deleted`,
2490 severity: ErrorSeverity.InvalidJS,
2491 + category: ErrorCategory.Syntax,
2492 loc: expr.node.loc ?? null,
2493 suggestions: [
2494 {
@@ -2459,6 +2504,7 @@ function lowerExpression(
2504 builder.errors.push({
2505 reason: `Throw expressions are not supported`,
2506 severity: ErrorSeverity.InvalidJS,
2507 + category: ErrorCategory.Syntax,
2508 loc: expr.node.loc ?? null,
2509 suggestions: [
2510 {
@@ -2580,6 +2626,7 @@ function lowerExpression(
2626 builder.errors.push({
2627 reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`,
2628 severity: ErrorSeverity.Todo,
2629 + category: ErrorCategory.Todo,
2630 loc: exprPath.node.loc ?? null,
2631 suggestions: null,
2632 });
@@ -2588,6 +2635,7 @@ function lowerExpression(
2635 builder.errors.push({
2636 reason: `(BuildHIR::lowerExpression) Handle UpdateExpression to variables captured within lambdas.`,
2637 severity: ErrorSeverity.Todo,
2638 + category: ErrorCategory.Todo,
2639 loc: exprPath.node.loc ?? null,
2640 suggestions: null,
2641 });
@@ -2608,6 +2656,7 @@ function lowerExpression(
2656 builder.errors.push({
2657 reason: `(BuildHIR::lowerExpression) Found an invalid UpdateExpression without a previously reported error`,
2658 severity: ErrorSeverity.Invariant,
2659 + category: ErrorCategory.Invariant,
2660 loc: exprLoc,
2661 suggestions: null,
2662 });
@@ -2617,6 +2666,7 @@ function lowerExpression(
2666 builder.errors.push({
2667 reason: `(BuildHIR::lowerExpression) Support UpdateExpression where argument is a global`,
2668 severity: ErrorSeverity.Todo,
2669 + category: ErrorCategory.Todo,
2670 loc: exprLoc,
2671 suggestions: null,
2672 });
@@ -2672,6 +2722,7 @@ function lowerExpression(
2722 builder.errors.push({
2723 reason: `(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta`,
2724 severity: ErrorSeverity.Todo,
2725 + category: ErrorCategory.Todo,
2726 loc: exprPath.node.loc ?? null,
2727 suggestions: null,
2728 });
@@ -2681,6 +2732,7 @@ function lowerExpression(
2732 builder.errors.push({
2733 reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
2734 severity: ErrorSeverity.Todo,
2735 + category: ErrorCategory.Todo,
2736 loc: exprPath.node.loc ?? null,
2737 suggestions: null,
2738 });
@@ -2978,6 +3030,7 @@ function lowerReorderableExpression(
3030 builder.errors.push({
3031 reason: `(BuildHIR::node.lowerReorderableExpression) Expression type \`${expr.type}\` cannot be safely reordered`,
3032 severity: ErrorSeverity.Todo,
3033 + category: ErrorCategory.Todo,
3034 loc: expr.node.loc ?? null,
3035 suggestions: null,
3036 });
@@ -3174,6 +3227,7 @@ function lowerArguments(
3227 builder.errors.push({
3228 reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
3229 severity: ErrorSeverity.Todo,
3230 + category: ErrorCategory.Todo,
3231 loc: argPath.node.loc ?? null,
3232 suggestions: null,
3233 });
@@ -3209,6 +3263,7 @@ function lowerMemberExpression(
3263 builder.errors.push({
3264 reason: `(BuildHIR::lowerMemberExpression) Handle ${propertyNode.type} property`,
3265 severity: ErrorSeverity.Todo,
3266 + category: ErrorCategory.Todo,
3267 loc: propertyNode.node.loc ?? null,
3268 suggestions: null,
3269 });
@@ -3230,6 +3285,7 @@ function lowerMemberExpression(
3285 builder.errors.push({
3286 reason: `(BuildHIR::lowerMemberExpression) Expected Expression, got ${propertyNode.type} property`,
3287 severity: ErrorSeverity.Todo,
3288 + category: ErrorCategory.Todo,
3289 loc: propertyNode.node.loc ?? null,
3290 suggestions: null,
3291 });
@@ -3289,6 +3345,7 @@ function lowerJsxElementName(
3345 reason: `Expected JSXNamespacedName to have no colons in the namespace or name`,
3346 description: `Got \`${namespace}\` : \`${name}\``,
3347 severity: ErrorSeverity.InvalidJS,
3348 + category: ErrorCategory.Syntax,
3349 loc: exprPath.node.loc ?? null,
3350 suggestions: null,
3351 });
@@ -3303,6 +3360,7 @@ function lowerJsxElementName(
3360 builder.errors.push({
3361 reason: `(BuildHIR::lowerJsxElementName) Handle ${exprPath.type} tags`,
3362 severity: ErrorSeverity.Todo,
3363 + category: ErrorCategory.Todo,
3364 loc: exprPath.node.loc ?? null,
3365 suggestions: null,
3366 });
@@ -3401,6 +3459,7 @@ function lowerJsxElement(
3459 builder.errors.push({
3460 reason: `(BuildHIR::lowerJsxElement) Unhandled JsxElement, got: ${exprPath.type}`,
3461 severity: ErrorSeverity.Todo,
3462 + category: ErrorCategory.Todo,
3463 loc: exprPath.node.loc ?? null,
3464 suggestions: null,
3465 });
@@ -3588,6 +3647,7 @@ function lowerIdentifier(
3647 description:
3648 'Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler',
3649 severity: ErrorSeverity.UnsupportedJS,
3650 + category: ErrorCategory.UnsupportedSyntax,
3651 loc: exprPath.node.loc ?? null,
3652 suggestions: null,
3653 });
@@ -3644,6 +3704,7 @@ function lowerIdentifierForAssignment(
3704 builder.errors.push({
3705 reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
3706 severity: ErrorSeverity.Invariant,
3707 + category: ErrorCategory.Invariant,
3708 loc: path.node.loc ?? null,
3709 suggestions: null,
3710 });
@@ -3656,6 +3717,7 @@ function lowerIdentifierForAssignment(
3717 builder.errors.push({
3718 reason: `Cannot reassign a \`const\` variable`,
3719 severity: ErrorSeverity.InvalidJS,
3720 + category: ErrorCategory.Syntax,
3721 loc: path.node.loc ?? null,
3722 description:
3723 binding.identifier.name != null
@@ -3713,6 +3775,7 @@ function lowerAssignment(
3775 builder.errors.push({
3776 reason: `Expected \`const\` declaration not to be reassigned`,
3777 severity: ErrorSeverity.InvalidJS,
3778 + category: ErrorCategory.Syntax,
3779 loc: lvalue.node.loc ?? null,
3780 suggestions: null,
3781 });
@@ -3727,6 +3790,7 @@ function lowerAssignment(
3790 builder.errors.push({
3791 reason: `Unexpected context variable kind`,
3792 severity: ErrorSeverity.InvalidJS,
3793 + category: ErrorCategory.Syntax,
3794 loc: lvalue.node.loc ?? null,
3795 suggestions: null,
3796 });
@@ -3798,6 +3862,7 @@ function lowerAssignment(
3862 builder.errors.push({
3863 reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in MemberExpression`,
3864 severity: ErrorSeverity.Todo,
3865 + category: ErrorCategory.Todo,
3866 loc: property.node.loc ?? null,
3867 suggestions: null,
3868 });
@@ -3810,6 +3875,7 @@ function lowerAssignment(
3875 reason:
3876 '(BuildHIR::lowerAssignment) Expected private name to appear as a non-computed property',
3877 severity: ErrorSeverity.Todo,
3878 + category: ErrorCategory.Todo,
3879 loc: property.node.loc ?? null,
3880 suggestions: null,
3881 });
@@ -3875,6 +3941,7 @@ function lowerAssignment(
3941 } else if (identifier.kind === 'Global') {
3942 builder.errors.push({
3943 severity: ErrorSeverity.Todo,
3944 + category: ErrorCategory.Todo,
3945 reason:
3946 'Expected reassignment of globals to enable forceTemporaries',
3947 loc: element.node.loc ?? GeneratedSource,
@@ -3914,6 +3981,7 @@ function lowerAssignment(
3981 } else if (identifier.kind === 'Global') {
3982 builder.errors.push({
3983 severity: ErrorSeverity.Todo,
3984 + category: ErrorCategory.Todo,
3985 reason:
3986 'Expected reassignment of globals to enable forceTemporaries',
3987 loc: element.node.loc ?? GeneratedSource,
@@ -3987,6 +4055,7 @@ function lowerAssignment(
4055 builder.errors.push({
4056 reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ObjectPattern`,
4057 severity: ErrorSeverity.Todo,
4058 + category: ErrorCategory.Todo,
4059 loc: argument.node.loc ?? null,
4060 suggestions: null,
4061 });
@@ -4018,6 +4087,7 @@ function lowerAssignment(
4087 } else if (identifier.kind === 'Global') {
4088 builder.errors.push({
4089 severity: ErrorSeverity.Todo,
4090 + category: ErrorCategory.Todo,
4091 reason:
4092 'Expected reassignment of globals to enable forceTemporaries',
4093 loc: property.node.loc ?? GeneratedSource,
@@ -4035,6 +4105,7 @@ function lowerAssignment(
4105 builder.errors.push({
4106 reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in ObjectPattern`,
4107 severity: ErrorSeverity.Todo,
4108 + category: ErrorCategory.Todo,
4109 loc: property.node.loc ?? null,
4110 suggestions: null,
4111 });
@@ -4044,6 +4115,7 @@ function lowerAssignment(
4115 builder.errors.push({
4116 reason: `(BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern`,
4117 severity: ErrorSeverity.Todo,
4118 + category: ErrorCategory.Todo,
4119 loc: property.node.loc ?? null,
4120 suggestions: null,
4121 });
@@ -4058,6 +4130,7 @@ function lowerAssignment(
4130 builder.errors.push({
4131 reason: `(BuildHIR::lowerAssignment) Expected object property value to be an LVal, got: ${element.type}`,
4132 severity: ErrorSeverity.Todo,
4133 + category: ErrorCategory.Todo,
4134 loc: element.node.loc ?? null,
4135 suggestions: null,
4136 });
@@ -4080,6 +4153,7 @@ function lowerAssignment(
4153 } else if (identifier.kind === 'Global') {
4154 builder.errors.push({
4155 severity: ErrorSeverity.Todo,
4156 + category: ErrorCategory.Todo,
4157 reason:
4158 'Expected reassignment of globals to enable forceTemporaries',
4159 loc: element.node.loc ?? GeneratedSource,
@@ -4229,6 +4303,7 @@ function lowerAssignment(
4303 builder.errors.push({
4304 reason: `(BuildHIR::lowerAssignment) Handle ${lvaluePath.type} assignments`,
4305 severity: ErrorSeverity.Todo,
4306 + category: ErrorCategory.Todo,
4307 loc: lvaluePath.node.loc ?? null,
4308 suggestions: null,
4309 });
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+3 -2
@@ -7,7 +7,7 @@
7
8 import {Binding, NodePath} from '@babel/traverse';
9 import * as t from '@babel/types';
10 -import {CompilerError, ErrorSeverity} from '../CompilerError';
10 +import {CompilerError, ErrorCategory, ErrorSeverity} from '../CompilerError';
11 import {Environment} from './Environment';
12 import {
13 BasicBlock,
@@ -310,7 +310,8 @@ export default class HIRBuilder {
310 if (node.name === 'fbt') {
311 CompilerError.throwDiagnostic({
312 severity: ErrorSeverity.Todo,
313 - category: 'Support local variables named `fbt`',
313 + category: ErrorCategory.FBT,
314 + reason: 'Support local variables named `fbt`',
315 description:
316 'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported',
317 details: [
compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
+3 -3
@@ -986,13 +986,13 @@ export function printAliasingEffect(effect: AliasingEffect): string {
986 return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}`;
987 }
988 case 'MutateFrozen': {
989 - return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.category)}`;
989 + return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
990 }
991 case 'MutateGlobal': {
992 - return `MutateGlobal ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.category)}`;
992 + return `MutateGlobal ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
993 }
994 case 'Impure': {
995 - return `Impure ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.category)}`;
995 + return `Impure ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
996 }
997 case 'Render': {
998 return `Render ${printPlaceForAliasEffect(effect.place)}`;
compiler/packages/babel-plugin-react-compiler/src/Inference/AliasingEffects.ts
+1 -1
@@ -231,7 +231,7 @@ export function hashEffect(effect: AliasingEffect): string {
231 effect.kind,
232 effect.place.identifier.id,
233 effect.error.severity,
234 - effect.error.category,
234 + effect.error.reason,
235 effect.error.description,
236 printSourceLocation(effect.error.primaryLocation() ?? GeneratedSource),
237 ].join(':');
compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts
+13 -6
@@ -11,6 +11,7 @@ import {
11 ErrorSeverity,
12 SourceLocation,
13 } from '..';
14 +import {ErrorCategory} from '../CompilerError';
15 import {
16 CallExpression,
17 Effect,
@@ -300,8 +301,9 @@ function extractManualMemoizationArgs(
301 if (fnPlace == null) {
302 errors.pushDiagnostic(
303 CompilerDiagnostic.create({
304 + category: ErrorCategory.UseMemo,
305 severity: ErrorSeverity.InvalidReact,
304 - category: `Expected a callback function to be passed to ${kind}`,
306 + reason: `Expected a callback function to be passed to ${kind}`,
307 description: `Expected a callback function to be passed to ${kind}`,
308 suggestions: null,
309 }).withDetail({
@@ -315,8 +317,9 @@ function extractManualMemoizationArgs(
317 if (fnPlace.kind === 'Spread' || depsListPlace?.kind === 'Spread') {
318 errors.pushDiagnostic(
319 CompilerDiagnostic.create({
320 + category: ErrorCategory.UseMemo,
321 severity: ErrorSeverity.InvalidReact,
319 - category: `Unexpected spread argument to ${kind}`,
322 + reason: `Unexpected spread argument to ${kind}`,
323 description: `Unexpected spread argument to ${kind}`,
324 suggestions: null,
325 }).withDetail({
@@ -335,8 +338,9 @@ function extractManualMemoizationArgs(
338 if (maybeDepsList == null) {
339 errors.pushDiagnostic(
340 CompilerDiagnostic.create({
341 + category: ErrorCategory.UseMemo,
342 severity: ErrorSeverity.InvalidReact,
339 - category: `Expected the dependency list for ${kind} to be an array literal`,
343 + reason: `Expected the dependency list for ${kind} to be an array literal`,
344 description: `Expected the dependency list for ${kind} to be an array literal`,
345 suggestions: null,
346 }).withDetail({
@@ -353,8 +357,9 @@ function extractManualMemoizationArgs(
357 if (maybeDep == null) {
358 errors.pushDiagnostic(
359 CompilerDiagnostic.create({
360 + category: ErrorCategory.UseMemo,
361 severity: ErrorSeverity.InvalidReact,
357 - category: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
362 + reason: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
363 description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
364 suggestions: null,
365 }).withDetail({
@@ -459,7 +464,8 @@ export function dropManualMemoization(
464 errors.pushDiagnostic(
465 CompilerDiagnostic.create({
466 severity: ErrorSeverity.InvalidReact,
462 - category: 'useMemo() callbacks must return a value',
467 + category: ErrorCategory.UseMemo,
468 + reason: 'useMemo() callbacks must return a value',
469 description: `This ${
470 manualMemo.loadInstr.value.kind === 'PropertyLoad'
471 ? 'React.useMemo'
@@ -498,8 +504,9 @@ export function dropManualMemoization(
504 if (!sidemap.functions.has(fnPlace.identifier.id)) {
505 errors.pushDiagnostic(
506 CompilerDiagnostic.create({
507 + category: ErrorCategory.UseMemo,
508 severity: ErrorSeverity.InvalidReact,
502 - category: `Expected the first argument to be an inline function expression`,
509 + reason: `Expected the first argument to be an inline function expression`,
510 description: `Expected the first argument to be an inline function expression`,
511 suggestions: [],
512 }).withDetail({
compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
+11 -5
@@ -69,6 +69,7 @@ import {
69 hashEffect,
70 MutationReason,
71 } from './AliasingEffects';
72 +import {ErrorCategory} from '../CompilerError';
73
74 const DEBUG = false;
75
@@ -452,8 +453,9 @@ function applySignature(
453 ? `\`${effect.value.identifier.name.value}\``
454 : 'value';
455 const diagnostic = CompilerDiagnostic.create({
456 + category: ErrorCategory.Immutability,
457 severity: ErrorSeverity.InvalidReact,
456 - category: 'This value cannot be modified',
458 + reason: 'This value cannot be modified',
459 description: `${reason}.`,
460 }).withDetail({
461 kind: 'error',
@@ -1036,8 +1038,9 @@ function applyEffect(
1038 effect.value.identifier.declarationId,
1039 );
1040 const diagnostic = CompilerDiagnostic.create({
1041 + category: ErrorCategory.Immutability,
1042 severity: ErrorSeverity.InvalidReact,
1040 - category: 'Cannot access variable before it is declared',
1043 + reason: 'Cannot access variable before it is declared',
1044 description: `${variable ?? 'This variable'} is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.`,
1045 });
1046 if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
@@ -1075,8 +1078,9 @@ function applyEffect(
1078 ? `\`${effect.value.identifier.name.value}\``
1079 : 'value';
1080 const diagnostic = CompilerDiagnostic.create({
1081 + category: ErrorCategory.Immutability,
1082 severity: ErrorSeverity.InvalidReact,
1079 - category: 'This value cannot be modified',
1083 + reason: 'This value cannot be modified',
1084 description: `${reason}.`,
1085 }).withDetail({
1086 kind: 'error',
@@ -2033,8 +2037,9 @@ function computeSignatureForInstruction(
2037 kind: 'MutateGlobal',
2038 place: value.value,
2039 error: CompilerDiagnostic.create({
2040 + category: ErrorCategory.Globals,
2041 severity: ErrorSeverity.InvalidReact,
2037 - category:
2042 + reason:
2043 'Cannot reassign variables declared outside of the component/hook',
2044 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)`,
2045 }).withDetail({
@@ -2132,8 +2137,9 @@ function computeEffectsForLegacySignature(
2137 kind: 'Impure',
2138 place: receiver,
2139 error: CompilerDiagnostic.create({
2140 + category: ErrorCategory.Purity,
2141 severity: ErrorSeverity.InvalidReact,
2136 - category: 'Cannot call impure function during render',
2142 + reason: 'Cannot call impure function during render',
2143 description:
2144 (signature.canonicalName != null
2145 ? `\`${signature.canonicalName}\` is an impure function. `
compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts
+2
@@ -42,6 +42,7 @@ import {
42 mapInstructionValueOperands,
43 mapTerminalOperands,
44 } from '../HIR/visitors';
45 +import {ErrorCategory} from '../CompilerError';
46
47 type InlinedJsxDeclarationMap = Map<
48 DeclarationId,
@@ -83,6 +84,7 @@ export function inlineJsxTransform(
84 kind: 'CompileDiagnostic',
85 fnLoc: null,
86 detail: {
87 + category: ErrorCategory.Todo,
88 reason: 'JSX Inlining is not supported on value blocks',
89 loc: instr.loc,
90 },
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+3 -1
@@ -13,7 +13,7 @@ import {
13 pruneUnusedLabels,
14 renameVariables,
15 } from '.';
16 -import {CompilerError, ErrorSeverity} from '../CompilerError';
16 +import {CompilerError, ErrorCategory, ErrorSeverity} from '../CompilerError';
17 import {Environment, ExternalFunction} from '../HIR';
18 import {
19 ArrayPattern,
@@ -2185,6 +2185,7 @@ function codegenInstructionValue(
2185 (declarator.id as t.Identifier).name
2186 }'`,
2187 severity: ErrorSeverity.Todo,
2188 + category: ErrorCategory.Todo,
2189 loc: declarator.loc ?? null,
2190 suggestions: null,
2191 });
@@ -2193,6 +2194,7 @@ function codegenInstructionValue(
2194 cx.errors.push({
2195 reason: `(CodegenReactiveFunction::codegenInstructionValue) Handle conversion of ${stmt.type} to expression`,
2196 severity: ErrorSeverity.Todo,
2197 + category: ErrorCategory.Todo,
2198 loc: stmt.loc ?? null,
2199 suggestions: null,
2200 });
compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts
+9
@@ -42,6 +42,7 @@ import {
42 import {eachInstructionOperand} from '../HIR/visitors';
43 import {printSourceLocationLine} from '../HIR/PrintHIR';
44 import {USE_FIRE_FUNCTION_NAME} from '../HIR/Environment';
45 +import {ErrorCategory} from '../CompilerError';
46
47 /*
48 * TODO(jmbrown):
@@ -133,6 +134,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
134 loc: value.loc,
135 description: null,
136 severity: ErrorSeverity.Invariant,
137 + category: ErrorCategory.Invariant,
138 reason: '[InsertFire] No LoadGlobal found for useEffect call',
139 suggestions: null,
140 });
@@ -179,6 +181,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
181 description:
182 'You must use an array literal for an effect dependency array when that effect uses `fire()`',
183 severity: ErrorSeverity.Invariant,
184 + category: ErrorCategory.Fire,
185 reason: CANNOT_COMPILE_FIRE,
186 suggestions: null,
187 });
@@ -189,6 +192,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
192 description:
193 'You must use an array literal for an effect dependency array when that effect uses `fire()`',
194 severity: ErrorSeverity.Invariant,
195 + category: ErrorCategory.Fire,
196 reason: CANNOT_COMPILE_FIRE,
197 suggestions: null,
198 });
@@ -223,6 +227,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
227 loc: value.loc,
228 description: null,
229 severity: ErrorSeverity.Invariant,
230 + category: ErrorCategory.Invariant,
231 reason:
232 '[InsertFire] No loadLocal found for fire call argument',
233 suggestions: null,
@@ -246,6 +251,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
251 description:
252 '`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed',
253 severity: ErrorSeverity.InvalidReact,
254 + category: ErrorCategory.Fire,
255 reason: CANNOT_COMPILE_FIRE,
256 suggestions: null,
257 });
@@ -264,6 +270,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
270 loc: value.loc,
271 description,
272 severity: ErrorSeverity.InvalidReact,
273 + category: ErrorCategory.Fire,
274 reason: CANNOT_COMPILE_FIRE,
275 suggestions: null,
276 });
@@ -395,6 +402,7 @@ function ensureNoRemainingCalleeCaptures(
402 this effect or not used with a fire() call at all. ${calleeName} was used with fire() on line \
403 ${printSourceLocationLine(calleeInfo.fireLoc)} in this effect`,
404 severity: ErrorSeverity.InvalidReact,
405 + category: ErrorCategory.Fire,
406 reason: CANNOT_COMPILE_FIRE,
407 suggestions: null,
408 });
@@ -411,6 +419,7 @@ function ensureNoMoreFireUses(fn: HIRFunction, context: Context): void {
419 context.pushError({
420 loc: place.identifier.loc,
421 description: 'Cannot use `fire` outside of a useEffect function',
422 + category: ErrorCategory.Fire,
423 severity: ErrorSeverity.Invariant,
424 reason: CANNOT_COMPILE_FIRE,
425 suggestions: null,
compiler/packages/babel-plugin-react-compiler/src/Utils/Result.ts
+29 -23
@@ -90,10 +90,13 @@ export function Ok<T>(val: T): OkImpl<T> {
90 }
91
92 class OkImpl<T> implements Result<T, never> {
93 - constructor(private val: T) {}
93 + #val: T;
94 + constructor(val: T) {
95 + this.#val = val;
96 + }
97
98 map<U>(fn: (val: T) => U): Result<U, never> {
96 - return new OkImpl(fn(this.val));
99 + return new OkImpl(fn(this.#val));
100 }
101
102 mapErr<F>(_fn: (val: never) => F): Result<T, F> {
@@ -101,15 +104,15 @@ class OkImpl<T> implements Result<T, never> {
104 }
105
106 mapOr<U>(_fallback: U, fn: (val: T) => U): U {
104 - return fn(this.val);
107 + return fn(this.#val);
108 }
109
110 mapOrElse<U>(_fallback: () => U, fn: (val: T) => U): U {
108 - return fn(this.val);
111 + return fn(this.#val);
112 }
113
114 andThen<U>(fn: (val: T) => Result<U, never>): Result<U, never> {
112 - return fn(this.val);
115 + return fn(this.#val);
116 }
117
118 and<U>(res: Result<U, never>): Result<U, never> {
@@ -133,30 +136,30 @@ class OkImpl<T> implements Result<T, never> {
136 }
137
138 expect(_msg: string): T {
136 - return this.val;
139 + return this.#val;
140 }
141
142 expectErr(msg: string): never {
140 - throw new Error(`${msg}: ${this.val}`);
143 + throw new Error(`${msg}: ${this.#val}`);
144 }
145
146 unwrap(): T {
144 - return this.val;
147 + return this.#val;
148 }
149
150 unwrapOr(_fallback: T): T {
148 - return this.val;
151 + return this.#val;
152 }
153
154 unwrapOrElse(_fallback: (val: never) => T): T {
152 - return this.val;
155 + return this.#val;
156 }
157
158 unwrapErr(): never {
156 - if (this.val instanceof Error) {
157 - throw this.val;
159 + if (this.#val instanceof Error) {
160 + throw this.#val;
161 }
159 - throw new Error(`Can't unwrap \`Ok\` to \`Err\`: ${this.val}`);
162 + throw new Error(`Can't unwrap \`Ok\` to \`Err\`: ${this.#val}`);
163 }
164 }
165
@@ -165,14 +168,17 @@ export function Err<E>(val: E): ErrImpl<E> {
168 }
169
170 class ErrImpl<E> implements Result<never, E> {
168 - constructor(private val: E) {}
171 + #val: E;
172 + constructor(val: E) {
173 + this.#val = val;
174 + }
175
176 map<U>(_fn: (val: never) => U): Result<U, E> {
177 return this;
178 }
179
180 mapErr<F>(fn: (val: E) => F): Result<never, F> {
175 - return new ErrImpl(fn(this.val));
181 + return new ErrImpl(fn(this.#val));
182 }
183
184 mapOr<U>(fallback: U, _fn: (val: never) => U): U {
@@ -196,7 +202,7 @@ class ErrImpl<E> implements Result<never, E> {
202 }
203
204 orElse<F>(fn: (val: E) => ErrImpl<F>): Result<never, F> {
199 - return fn(this.val);
205 + return fn(this.#val);
206 }
207
208 isOk(): this is OkImpl<never> {
@@ -208,18 +214,18 @@ class ErrImpl<E> implements Result<never, E> {
214 }
215
216 expect(msg: string): never {
211 - throw new Error(`${msg}: ${this.val}`);
217 + throw new Error(`${msg}: ${this.#val}`);
218 }
219
220 expectErr(_msg: string): E {
215 - return this.val;
221 + return this.#val;
222 }
223
224 unwrap(): never {
219 - if (this.val instanceof Error) {
220 - throw this.val;
225 + if (this.#val instanceof Error) {
226 + throw this.#val;
227 }
222 - throw new Error(`Can't unwrap \`Err\` to \`Ok\`: ${this.val}`);
228 + throw new Error(`Can't unwrap \`Err\` to \`Ok\`: ${this.#val}`);
229 }
230
231 unwrapOr<T>(fallback: T): T {
@@ -227,10 +233,10 @@ class ErrImpl<E> implements Result<never, E> {
233 }
234
235 unwrapOrElse<T>(fallback: (val: E) => T): T {
230 - return fallback(this.val);
236 + return fallback(this.#val);
237 }
238
239 unwrapErr(): E {
234 - return this.val;
240 + return this.#val;
241 }
242 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts
+6 -1
@@ -9,6 +9,7 @@ import * as t from '@babel/types';
9 import {
10 CompilerError,
11 CompilerErrorDetail,
12 + ErrorCategory,
13 ErrorSeverity,
14 } from '../CompilerError';
15 import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
@@ -124,6 +125,7 @@ export function validateHooksUsage(
125 recordError(
126 place.loc,
127 new CompilerErrorDetail({
128 + category: ErrorCategory.Hooks,
129 description: null,
130 reason,
131 loc: place.loc,
@@ -140,6 +142,7 @@ export function validateHooksUsage(
142 recordError(
143 place.loc,
144 new CompilerErrorDetail({
145 + category: ErrorCategory.Hooks,
146 description: null,
147 reason:
148 '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(
160 recordError(
161 place.loc,
162 new CompilerErrorDetail({
163 + category: ErrorCategory.Hooks,
164 description: null,
165 reason:
166 '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(
428 }
429
430 for (const [, error] of errorsByPlace) {
427 - errors.push(error);
431 + errors.pushErrorDetail(error);
432 }
433 return errors.asResult();
434 }
@@ -448,6 +452,7 @@ function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
452 if (hookKind != null) {
453 errors.pushErrorDetail(
454 new CompilerErrorDetail({
455 + category: ErrorCategory.Hooks,
456 severity: ErrorSeverity.InvalidReact,
457 reason:
458 '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)',
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts
+5 -2
@@ -6,6 +6,7 @@
6 */
7
8 import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
9 +import {ErrorCategory} from '../CompilerError';
10 import {HIRFunction, IdentifierId, Place} from '../HIR';
11 import {
12 eachInstructionLValue,
@@ -36,8 +37,9 @@ export function validateLocalsNotReassignedAfterRender(fn: HIRFunction): void {
37 : 'variable';
38 errors.pushDiagnostic(
39 CompilerDiagnostic.create({
40 + category: ErrorCategory.Immutability,
41 severity: ErrorSeverity.InvalidReact,
40 - category: 'Cannot reassign variable after render completes',
42 + reason: 'Cannot reassign variable after render completes',
43 description: `Reassigning ${variable} after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.`,
44 }).withDetail({
45 kind: 'error',
@@ -91,8 +93,9 @@ function getContextReassignment(
93 : 'variable';
94 errors.pushDiagnostic(
95 CompilerDiagnostic.create({
96 + category: ErrorCategory.Immutability,
97 severity: ErrorSeverity.InvalidReact,
95 - category: 'Cannot reassign variable in async function',
98 + reason: 'Cannot reassign variable in async function',
99 description:
100 'Reassigning a variable in an async function can cause inconsistent behavior on subsequent renders. Consider using state instead',
101 }).withDetail({
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts
+2
@@ -6,6 +6,7 @@
6 */
7
8 import {CompilerError, ErrorSeverity} from '..';
9 +import {ErrorCategory} from '../CompilerError';
10 import {
11 Identifier,
12 Instruction,
@@ -108,6 +109,7 @@ class Visitor extends ReactiveFunctionVisitor<CompilerError> {
109 isUnmemoized(deps.identifier, this.scopes))
110 ) {
111 state.push({
112 + category: ErrorCategory.EffectDependencies,
113 reason:
114 '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',
115 description: null,
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts
+3
@@ -6,6 +6,7 @@
6 */
7
8 import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..';
9 +import {ErrorCategory} from '../CompilerError';
10 import {HIRFunction, IdentifierId} from '../HIR';
11 import {DEFAULT_GLOBALS} from '../HIR/Globals';
12 import {Result} from '../Utils/Result';
@@ -56,6 +57,7 @@ export function validateNoCapitalizedCalls(
57 const calleeName = capitalLoadGlobals.get(calleeIdentifier);
58 if (calleeName != null) {
59 CompilerError.throwInvalidReact({
60 + category: ErrorCategory.CapitalizedCalls,
61 reason,
62 description: `${calleeName} may be a component.`,
63 loc: value.loc,
@@ -79,6 +81,7 @@ export function validateNoCapitalizedCalls(
81 const propertyName = capitalizedProperties.get(propertyIdentifier);
82 if (propertyName != null) {
83 errors.push({
84 + category: ErrorCategory.CapitalizedCalls,
85 severity: ErrorSeverity.InvalidReact,
86 reason,
87 description: `${propertyName} may be a component.`,
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoDerivedComputationsInEffects.ts
+2
@@ -6,6 +6,7 @@
6 */
7
8 import {CompilerError, ErrorSeverity, SourceLocation} from '..';
9 +import {ErrorCategory} from '../CompilerError';
10 import {
11 ArrayExpression,
12 BlockId,
@@ -219,6 +220,7 @@ function validateEffect(
220
221 for (const loc of setStateLocations) {
222 errors.push({
223 + category: ErrorCategory.EffectDerivationsOfState,
224 reason:
225 '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)',
226 description: null,
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts
+3 -1
@@ -6,6 +6,7 @@
6 */
7
8 import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
9 +import {ErrorCategory} from '../CompilerError';
10 import {
11 HIRFunction,
12 IdentifierId,
@@ -64,8 +65,9 @@ export function validateNoFreezingKnownMutableFunctions(
65 : 'a local variable';
66 errors.pushDiagnostic(
67 CompilerDiagnostic.create({
68 + category: ErrorCategory.Immutability,
69 severity: ErrorSeverity.InvalidReact,
68 - category: 'Cannot modify local variables after render completes',
70 + reason: 'Cannot modify local variables after render completes',
71 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.`,
72 })
73 .withDetail({
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts
+3 -1
@@ -6,6 +6,7 @@
6 */
7
8 import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
9 +import {ErrorCategory} from '../CompilerError';
10 import {HIRFunction} from '../HIR';
11 import {getFunctionCallSignature} from '../Inference/InferMutationAliasingEffects';
12 import {Result} from '../Utils/Result';
@@ -36,7 +37,8 @@ export function validateNoImpureFunctionsInRender(
37 if (signature != null && signature.impure === true) {
38 errors.pushDiagnostic(
39 CompilerDiagnostic.create({
39 - category: 'Cannot call impure function during render',
40 + category: ErrorCategory.Purity,
41 + reason: 'Cannot call impure function during render',
42 description:
43 (signature.canonicalName != null
44 ? `\`${signature.canonicalName}\` is an impure function. `
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts
+3 -1
@@ -6,6 +6,7 @@
6 */
7
8 import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
9 +import {ErrorCategory} from '../CompilerError';
10 import {BlockId, HIRFunction} from '../HIR';
11 import {Result} from '../Utils/Result';
12 import {retainWhere} from '../Utils/utils';
@@ -36,8 +37,9 @@ export function validateNoJSXInTryStatement(
37 case 'JsxFragment': {
38 errors.pushDiagnostic(
39 CompilerDiagnostic.create({
40 + category: ErrorCategory.ErrorBoundaries,
41 severity: ErrorSeverity.InvalidReact,
40 - category: 'Avoid constructing JSX within try/catch',
42 + reason: 'Avoid constructing JSX within try/catch',
43 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)`,
44 }).withDetail({
45 kind: 'error',
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts
+13 -6
@@ -8,6 +8,7 @@
8 import {
9 CompilerDiagnostic,
10 CompilerError,
11 + ErrorCategory,
12 ErrorSeverity,
13 } from '../CompilerError';
14 import {
@@ -468,8 +469,9 @@ function validateNoRefAccessInRenderImpl(
469 didError = true;
470 errors.pushDiagnostic(
471 CompilerDiagnostic.create({
472 + category: ErrorCategory.Refs,
473 severity: ErrorSeverity.InvalidReact,
472 - category: 'Cannot access refs during render',
474 + reason: 'Cannot access refs during render',
475 description: ERROR_DESCRIPTION,
476 }).withDetail({
477 kind: 'error',
@@ -731,8 +733,9 @@ function guardCheck(errors: CompilerError, operand: Place, env: Env): void {
733 if (env.get(operand.identifier.id)?.kind === 'Guard') {
734 errors.pushDiagnostic(
735 CompilerDiagnostic.create({
736 + category: ErrorCategory.Refs,
737 severity: ErrorSeverity.InvalidReact,
735 - category: 'Cannot access refs during render',
738 + reason: 'Cannot access refs during render',
739 description: ERROR_DESCRIPTION,
740 }).withDetail({
741 kind: 'error',
@@ -755,8 +758,9 @@ function validateNoRefValueAccess(
758 ) {
759 errors.pushDiagnostic(
760 CompilerDiagnostic.create({
761 + category: ErrorCategory.Refs,
762 severity: ErrorSeverity.InvalidReact,
759 - category: 'Cannot access refs during render',
763 + reason: 'Cannot access refs during render',
764 description: ERROR_DESCRIPTION,
765 }).withDetail({
766 kind: 'error',
@@ -781,8 +785,9 @@ function validateNoRefPassedToFunction(
785 ) {
786 errors.pushDiagnostic(
787 CompilerDiagnostic.create({
788 + category: ErrorCategory.Refs,
789 severity: ErrorSeverity.InvalidReact,
785 - category: 'Cannot access refs during render',
790 + reason: 'Cannot access refs during render',
791 description: ERROR_DESCRIPTION,
792 }).withDetail({
793 kind: 'error',
@@ -803,8 +808,9 @@ function validateNoRefUpdate(
808 if (type?.kind === 'Ref' || type?.kind === 'RefValue') {
809 errors.pushDiagnostic(
810 CompilerDiagnostic.create({
811 + category: ErrorCategory.Refs,
812 severity: ErrorSeverity.InvalidReact,
807 - category: 'Cannot access refs during render',
813 + reason: 'Cannot access refs during render',
814 description: ERROR_DESCRIPTION,
815 }).withDetail({
816 kind: 'error',
@@ -824,8 +830,9 @@ function validateNoDirectRefValueAccess(
830 if (type?.kind === 'RefValue') {
831 errors.pushDiagnostic(
832 CompilerDiagnostic.create({
833 + category: ErrorCategory.Refs,
834 severity: ErrorSeverity.InvalidReact,
828 - category: 'Cannot access refs during render',
835 + reason: 'Cannot access refs during render',
836 description: ERROR_DESCRIPTION,
837 }).withDetail({
838 kind: 'error',
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts
+3 -1
@@ -8,6 +8,7 @@
8 import {
9 CompilerDiagnostic,
10 CompilerError,
11 + ErrorCategory,
12 ErrorSeverity,
13 } from '../CompilerError';
14 import {
@@ -96,7 +97,8 @@ export function validateNoSetStateInEffects(
97 if (setState !== undefined) {
98 errors.pushDiagnostic(
99 CompilerDiagnostic.create({
99 - category:
100 + category: ErrorCategory.EffectSetState,
101 + reason:
102 'Calling setState synchronously within an effect can trigger cascading renders',
103 description:
104 'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts
+6 -3
@@ -8,6 +8,7 @@
8 import {
9 CompilerDiagnostic,
10 CompilerError,
11 + ErrorCategory,
12 ErrorSeverity,
13 } from '../CompilerError';
14 import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
@@ -128,7 +129,8 @@ function validateNoSetStateInRenderImpl(
129 if (activeManualMemoId !== null) {
130 errors.pushDiagnostic(
131 CompilerDiagnostic.create({
131 - category:
132 + category: ErrorCategory.RenderSetState,
133 + reason:
134 'Calling setState from useMemo may trigger an infinite loop',
135 description:
136 '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(
145 } else if (unconditionalBlocks.has(block.id)) {
146 errors.pushDiagnostic(
147 CompilerDiagnostic.create({
146 - category:
148 + category: ErrorCategory.RenderSetState,
149 + reason:
150 'Calling setState during render may trigger an infinite loop',
151 description:
152 '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(
155 }).withDetail({
156 kind: 'error',
157 loc: callee.loc,
155 - message: 'Found setState() within useMemo()',
158 + message: 'Found setState() in render',
159 }),
160 );
161 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts
+7 -3
@@ -8,6 +8,7 @@
8 import {
9 CompilerDiagnostic,
10 CompilerError,
11 + ErrorCategory,
12 ErrorSeverity,
13 } from '../CompilerError';
14 import {
@@ -281,8 +282,9 @@ function validateInferredDep(
282 }
283 errorState.pushDiagnostic(
284 CompilerDiagnostic.create({
285 + category: ErrorCategory.PreserveManualMemo,
286 severity: ErrorSeverity.CannotPreserveMemoization,
285 - category:
287 + reason:
288 'Compilation skipped because existing memoization could not be preserved',
289 description: [
290 '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> {
537 ) {
538 state.errors.pushDiagnostic(
539 CompilerDiagnostic.create({
540 + category: ErrorCategory.PreserveManualMemo,
541 severity: ErrorSeverity.CannotPreserveMemoization,
539 - category:
542 + reason:
543 'Compilation skipped because existing memoization could not be preserved',
544 description: [
545 '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> {
586 if (isUnmemoized(identifier, this.scopes)) {
587 state.errors.pushDiagnostic(
588 CompilerDiagnostic.create({
589 + category: ErrorCategory.PreserveManualMemo,
590 severity: ErrorSeverity.CannotPreserveMemoization,
587 - category:
591 + reason:
592 'Compilation skipped because existing memoization could not be preserved',
593 description: [
594 '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. ',
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts
+3 -1
@@ -8,6 +8,7 @@
8 import {
9 CompilerDiagnostic,
10 CompilerError,
11 + ErrorCategory,
12 ErrorSeverity,
13 } from '../CompilerError';
14 import {HIRFunction, IdentifierId, SourceLocation} from '../HIR';
@@ -65,8 +66,9 @@ export function validateStaticComponents(
66 if (location != null) {
67 error.pushDiagnostic(
68 CompilerDiagnostic.create({
69 + category: ErrorCategory.StaticComponents,
70 severity: ErrorSeverity.InvalidReact,
69 - category: 'Cannot create components during render',
71 + reason: 'Cannot create components during render',
72 description: `Components created during render will reset their state each time they are created. Declare components outside of render. `,
73 })
74 .withDetail({
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts
+5 -2
@@ -8,6 +8,7 @@
8 import {
9 CompilerDiagnostic,
10 CompilerError,
11 + ErrorCategory,
12 ErrorSeverity,
13 } from '../CompilerError';
14 import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR';
@@ -74,8 +75,9 @@ export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
75 : firstParam.place.loc;
76 errors.pushDiagnostic(
77 CompilerDiagnostic.create({
78 + category: ErrorCategory.UseMemo,
79 severity: ErrorSeverity.InvalidReact,
78 - category: 'useMemo() callbacks may not accept parameters',
80 + reason: 'useMemo() callbacks may not accept parameters',
81 description:
82 '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.',
83 suggestions: null,
@@ -90,8 +92,9 @@ export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
92 if (body.loweredFunc.func.async || body.loweredFunc.func.generator) {
93 errors.pushDiagnostic(
94 CompilerDiagnostic.create({
95 + category: ErrorCategory.UseMemo,
96 severity: ErrorSeverity.InvalidReact,
94 - category:
97 + reason:
98 'useMemo() callbacks may not be async or generator functions',
99 description:
100 'useMemo() callbacks are called once and must synchronously return a value.',
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-unconditional-set-state-in-render.expect.md
+2 -2
@@ -29,7 +29,7 @@ error.invalid-unconditional-set-state-in-render.ts:6:2
29 4 | const aliased = setX;
30 5 |
31 > 6 | setX(1);
32 - | ^^^^ Found setState() within useMemo()
32 + | ^^^^ Found setState() in render
33 7 | aliased(2);
34 8 |
35 9 | return x;
@@ -42,7 +42,7 @@ error.invalid-unconditional-set-state-in-render.ts:7:2
42 5 |
43 6 | setX(1);
44 > 7 | aliased(2);
45 - | ^^^^^^^ Found setState() within useMemo()
45 + | ^^^^^^^ Found setState() in render
46 8 |
47 9 | return x;
48 10 | }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop-break.expect.md
+1 -1
@@ -32,7 +32,7 @@ error.unconditional-set-state-in-render-after-loop-break.ts:11:2
32 9 | }
33 10 | }
34 > 11 | setState(true);
35 - | ^^^^^^^^ Found setState() within useMemo()
35 + | ^^^^^^^^ Found setState() in render
36 12 | return state;
37 13 | }
38 14 |
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop.expect.md
+1 -1
@@ -27,7 +27,7 @@ error.unconditional-set-state-in-render-after-loop.ts:6:2
27 4 | for (const _ of props) {
28 5 | }
29 > 6 | setState(true);
30 - | ^^^^^^^^ Found setState() within useMemo()
30 + | ^^^^^^^^ Found setState() in render
31 7 | return state;
32 8 | }
33 9 |
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-with-loop-throw.expect.md
+1 -1
@@ -32,7 +32,7 @@ error.unconditional-set-state-in-render-with-loop-throw.ts:11:2
32 9 | }
33 10 | }
34 > 11 | setState(true);
35 - | ^^^^^^^^ Found setState() within useMemo()
35 + | ^^^^^^^^ Found setState() in render
36 12 | return state;
37 13 | }
38 14 |
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-lambda.expect.md
+1 -1
@@ -30,7 +30,7 @@ error.unconditional-set-state-lambda.ts:8:2
30 6 | setX(1);
31 7 | };
32 > 8 | foo();
33 - | ^^^ Found setState() within useMemo()
33 + | ^^^ Found setState() in render
34 9 |
35 10 | return [x];
36 11 | }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-nested-function-expressions.expect.md
+1 -1
@@ -38,7 +38,7 @@ error.unconditional-set-state-nested-function-expressions.ts:16:2
38 14 | bar();
39 15 | };
40 > 16 | baz();
41 - | ^^^ Found setState() within useMemo()
41 + | ^^^ Found setState() in render
42 17 |
43 18 | return [x];
44 19 | }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-bailout-nopanic.expect.md
+1 -1
@@ -58,7 +58,7 @@ export const FIXTURE_ENTRYPOINT = {
58 ## Logs
59
60 ```
61 -{"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"}]}}}
61 +{"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"}]}}}
62 ```
63
64 ### Eval output
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-invalid-multiple.expect.md
+1 -1
@@ -38,7 +38,7 @@ export const FIXTURE_ENTRYPOINT = {
38 ## Logs
39
40 ```
41 -{"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"}}}}
41 +{"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"}}}}
42 ```
43
44 ### Eval output
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/mutate-after-useeffect-optional-chain.expect.md
+1 -1
@@ -48,7 +48,7 @@ export const FIXTURE_ENTRYPOINT = {
48 ## Logs
49
50 ```
51 -{"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"}]}}}
51 +{"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"}]}}}
52 {"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"}]}
53 {"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}
54 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/mutate-after-useeffect-ref-access.expect.md
+1 -1
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
47 ## Logs
48
49 ```
50 -{"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"}]}}}
50 +{"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"}]}}}
51 {"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"}]}
52 {"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}
53 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/mutate-after-useeffect.expect.md
+1 -1
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
47 ## Logs
48
49 ```
50 -{"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"}]}}}
50 +{"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"}]}}}
51 {"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"}]}
52 {"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}
53 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.expect.md
+1 -1
@@ -54,7 +54,7 @@ export const FIXTURE_ENTRYPOINT = {
54 ## Logs
55
56 ```
57 -{"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"}]}}}
57 +{"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"}]}}}
58 {"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"}]}
59 {"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"}]}
60 {"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}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md
+1 -1
@@ -65,7 +65,7 @@ function Component(props) {
65 ## Logs
66
67 ```
68 -{"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}
68 +{"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}
69 {"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}
70 ```
71
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md
+1 -1
@@ -42,7 +42,7 @@ function Component(props) {
42 ## Logs
43
44 ```
45 -{"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}
45 +{"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}
46 {"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}
47 ```
48
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md
+1 -1
@@ -65,7 +65,7 @@ function _temp(s) {
65 ## Logs
66
67 ```
68 -{"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}
68 +{"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}
69 {"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}
70 ```
71
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md
+1 -1
@@ -45,7 +45,7 @@ function _temp(s) {
45 ## Logs
46
47 ```
48 -{"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}
48 +{"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}
49 {"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}
50 ```
51
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect-optional-chain.expect.md
+1 -1
@@ -48,7 +48,7 @@ export const FIXTURE_ENTRYPOINT = {
48 ## Logs
49
50 ```
51 -{"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"}]}}}
51 +{"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"}]}}}
52 {"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"}]}
53 {"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}
54 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect-ref-access.expect.md
+1 -1
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
47 ## Logs
48
49 ```
50 -{"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"}]}}}
50 +{"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"}]}}}
51 {"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"}]}
52 {"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}
53 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect.expect.md
+1 -1
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
47 ## Logs
48
49 ```
50 -{"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"}]}}}
50 +{"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"}]}}}
51 {"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"}]}
52 {"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}
53 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.expect.md
+1 -1
@@ -54,7 +54,7 @@ export const FIXTURE_ENTRYPOINT = {
54 ## Logs
55
56 ```
57 -{"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"}]}}}
57 +{"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"}]}}}
58 {"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"}]}
59 {"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"}]}
60 {"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}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md
+1 -1
@@ -50,7 +50,7 @@ function Example(props) {
50 ## Logs
51
52 ```
53 -{"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}
53 +{"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}
54 {"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}
55 ```
56
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md
+1 -1
@@ -32,7 +32,7 @@ function Example(props) {
32 ## Logs
33
34 ```
35 -{"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}
35 +{"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}
36 {"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}
37 ```
38
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md
+1 -1
@@ -37,7 +37,7 @@ function Example(props) {
37 ## Logs
38
39 ```
40 -{"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}
40 +{"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}
41 {"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}
42 ```
43
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md
+1 -1
@@ -41,7 +41,7 @@ function Example(props) {
41 ## Logs
42
43 ```
44 -{"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}
44 +{"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}
45 {"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}
46 ```
47
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md
+1 -1
@@ -32,7 +32,7 @@ function Example(props) {
32 ## Logs
33
34 ```
35 -{"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}
35 +{"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}
36 {"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}
37 ```
38
compiler/packages/babel-plugin-react-compiler/src/index.ts
+2
@@ -12,9 +12,11 @@ export {
12 CompilerDiagnostic,
13 CompilerSuggestionOperation,
14 ErrorSeverity,
15 + LintRules,
16 type CompilerErrorDetailOptions,
17 type CompilerDiagnosticOptions,
18 type CompilerDiagnosticDetail,
19 + type LintRule,
20 } from './CompilerError';
21 export {
22 compileFn as compile,
compiler/packages/eslint-plugin-react-compiler/__tests__/ImpureFunctionCallsRule-test.ts new
+39
@@ -0,0 +1,39 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {
9 + ErrorCategory,
10 + getRuleForCategory,
11 +} from 'babel-plugin-react-compiler/src/CompilerError';
12 +import {normalizeIndent, testRule, makeTestCaseError} from './shared-utils';
13 +import {allRules} from '../src/rules/ReactCompilerRule';
14 +
15 +testRule(
16 + 'no impure function calls rule',
17 + allRules[getRuleForCategory(ErrorCategory.Purity).name],
18 + {
19 + valid: [],
20 + invalid: [
21 + {
22 + name: 'Known impure function calls are caught',
23 + code: normalizeIndent`
24 + function Component() {
25 + const date = Date.now();
26 + const now = performance.now();
27 + const rand = Math.random();
28 + return <Foo date={date} now={now} rand={rand} />;
29 + }
30 + `,
31 + errors: [
32 + makeTestCaseError('Cannot call impure function during render'),
33 + makeTestCaseError('Cannot call impure function during render'),
34 + makeTestCaseError('Cannot call impure function during render'),
35 + ],
36 + },
37 + ],
38 + },
39 +);
compiler/packages/eslint-plugin-react-compiler/__tests__/InvalidHooksRule-test.ts new
+100
@@ -0,0 +1,100 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {
9 + ErrorCategory,
10 + getRuleForCategory,
11 +} from 'babel-plugin-react-compiler/src/CompilerError';
12 +import {normalizeIndent, makeTestCaseError, testRule} from './shared-utils';
13 +import {allRules} from '../src/rules/ReactCompilerRule';
14 +
15 +testRule(
16 + 'rules-of-hooks',
17 + allRules[getRuleForCategory(ErrorCategory.Hooks).name],
18 + {
19 + valid: [
20 + {
21 + name: 'Basic example',
22 + code: normalizeIndent`
23 + function Component() {
24 + useHook();
25 + return <div>Hello world</div>;
26 + }
27 + `,
28 + },
29 + {
30 + name: 'Violation with Flow suppression',
31 + code: `
32 + // Valid since error already suppressed with flow.
33 + function useHook() {
34 + if (cond) {
35 + // $FlowFixMe[react-rule-hook]
36 + useConditionalHook();
37 + }
38 + }
39 + `,
40 + },
41 + {
42 + // OK because invariants are only meant for the compiler team's consumption
43 + name: '[Invariant] Defined after use',
44 + code: normalizeIndent`
45 + function Component(props) {
46 + let y = function () {
47 + m(x);
48 + };
49 +
50 + let x = { a };
51 + m(x);
52 + return y;
53 + }
54 + `,
55 + },
56 + {
57 + name: "Classes don't throw",
58 + code: normalizeIndent`
59 + class Foo {
60 + #bar() {}
61 + }
62 + `,
63 + },
64 + ],
65 + invalid: [
66 + {
67 + name: 'Simple violation',
68 + code: normalizeIndent`
69 + function useConditional() {
70 + if (cond) {
71 + useConditionalHook();
72 + }
73 + }
74 + `,
75 + errors: [
76 + makeTestCaseError(
77 + 'Hooks must always be called in a consistent order',
78 + ),
79 + ],
80 + },
81 + {
82 + name: 'Multiple diagnostics within the same function are surfaced',
83 + code: normalizeIndent`
84 + function useConditional() {
85 + cond ?? useConditionalHook();
86 + props.cond && useConditionalHook();
87 + return <div>Hello world</div>;
88 + }`,
89 + errors: [
90 + makeTestCaseError(
91 + 'Hooks must always be called in a consistent order',
92 + ),
93 + makeTestCaseError(
94 + 'Hooks must always be called in a consistent order',
95 + ),
96 + ],
97 + },
98 + ],
99 + },
100 +);
compiler/packages/eslint-plugin-react-compiler/__tests__/NoAmbiguousJsxRule-test.ts new
+38
@@ -0,0 +1,38 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {
9 + ErrorCategory,
10 + getRuleForCategory,
11 +} from 'babel-plugin-react-compiler/src/CompilerError';
12 +import {normalizeIndent, testRule, makeTestCaseError} from './shared-utils';
13 +import {allRules} from '../src/rules/ReactCompilerRule';
14 +
15 +testRule(
16 + 'no ambiguous JSX rule',
17 + allRules[getRuleForCategory(ErrorCategory.ErrorBoundaries).name],
18 + {
19 + valid: [],
20 + invalid: [
21 + {
22 + name: 'JSX in try blocks are warned against',
23 + code: normalizeIndent`
24 + function Component(props) {
25 + let el;
26 + try {
27 + el = <Child />;
28 + } catch {
29 + return null;
30 + }
31 + return el;
32 + }
33 + `,
34 + errors: [makeTestCaseError('Avoid constructing JSX within try/catch')],
35 + },
36 + ],
37 + },
38 +);
compiler/packages/eslint-plugin-react-compiler/__tests__/NoCapitalizedCallsRule-test.ts new
+71
@@ -0,0 +1,71 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +import {
8 + ErrorCategory,
9 + getRuleForCategory,
10 +} from 'babel-plugin-react-compiler/src/CompilerError';
11 +import {normalizeIndent, makeTestCaseError, testRule} from './shared-utils';
12 +import {allRules} from '../src/rules/ReactCompilerRule';
13 +
14 +testRule(
15 + 'no-capitalized-calls',
16 + allRules[getRuleForCategory(ErrorCategory.CapitalizedCalls).name],
17 + {
18 + valid: [],
19 + invalid: [
20 + {
21 + name: 'Simple violation',
22 + code: normalizeIndent`
23 + import Child from './Child';
24 + function Component() {
25 + return <>
26 + {Child()}
27 + </>;
28 + }
29 + `,
30 + errors: [
31 + makeTestCaseError(
32 + 'Capitalized functions are reserved for components',
33 + ),
34 + ],
35 + },
36 + {
37 + name: 'Method call violation',
38 + code: normalizeIndent`
39 + import myModule from './MyModule';
40 + function Component() {
41 + return <>
42 + {myModule.Child()}
43 + </>;
44 + }
45 + `,
46 + errors: [
47 + makeTestCaseError(
48 + 'Capitalized functions are reserved for components',
49 + ),
50 + ],
51 + },
52 + {
53 + name: 'Multiple diagnostics within the same function are surfaced',
54 + code: normalizeIndent`
55 + import Child1 from './Child1';
56 + import MyModule from './MyModule';
57 + function Component() {
58 + return <>
59 + {Child1()}
60 + {MyModule.Child2()}
61 + </>;
62 + }`,
63 + errors: [
64 + makeTestCaseError(
65 + 'Capitalized functions are reserved for components',
66 + ),
67 + ],
68 + },
69 + ],
70 + },
71 +);
compiler/packages/eslint-plugin-react-compiler/__tests__/NoRefAccessInRender-tests.ts new
+34
@@ -0,0 +1,34 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {
9 + ErrorCategory,
10 + getRuleForCategory,
11 +} from 'babel-plugin-react-compiler/src/CompilerError';
12 +import {normalizeIndent, testRule, makeTestCaseError} from './shared-utils';
13 +import {allRules} from '../src/rules/ReactCompilerRule';
14 +
15 +testRule(
16 + 'no ref access in render rule',
17 + allRules[getRuleForCategory(ErrorCategory.Refs).name],
18 + {
19 + valid: [],
20 + invalid: [
21 + {
22 + name: 'validate against simple ref access in render',
23 + code: normalizeIndent`
24 + function Component(props) {
25 + const ref = useRef(null);
26 + const value = ref.current;
27 + return value;
28 + }
29 + `,
30 + errors: [makeTestCaseError('Cannot access refs during render')],
31 + },
32 + ],
33 + },
34 +);
compiler/packages/eslint-plugin-react-compiler/__tests__/NoUnusedDirectivesRule-test.ts new
+58
@@ -0,0 +1,58 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {NoUnusedDirectivesRule} from '../src/rules/ReactCompilerRule';
9 +import {normalizeIndent, testRule} from './shared-utils';
10 +
11 +testRule('no unused directives rule', NoUnusedDirectivesRule, {
12 + valid: [],
13 + invalid: [
14 + {
15 + name: "Unused 'use no forget' directive is reported when no errors are present on components",
16 + code: normalizeIndent`
17 + function Component() {
18 + 'use no forget';
19 + return <div>Hello world</div>
20 + }
21 + `,
22 + errors: [
23 + {
24 + message: "Unused 'use no forget' directive",
25 + suggestions: [
26 + {
27 + output:
28 + // yuck
29 + '\nfunction Component() {\n \n return <div>Hello world</div>\n}\n',
30 + },
31 + ],
32 + },
33 + ],
34 + },
35 +
36 + {
37 + name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks",
38 + code: normalizeIndent`
39 + function notacomponent() {
40 + 'use no forget';
41 + return 1 + 1;
42 + }
43 + `,
44 + errors: [
45 + {
46 + message: "Unused 'use no forget' directive",
47 + suggestions: [
48 + {
49 + output:
50 + // yuck
51 + '\nfunction notacomponent() {\n \n return 1 + 1;\n}\n',
52 + },
53 + ],
54 + },
55 + ],
56 + },
57 + ],
58 +});
compiler/packages/eslint-plugin-react-compiler/__tests__/PluginTest-test.ts new
+158
@@ -0,0 +1,158 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {
9 + ErrorCategory,
10 + getRuleForCategory,
11 +} from 'babel-plugin-react-compiler/src/CompilerError';
12 +import {
13 + normalizeIndent,
14 + testRule,
15 + makeTestCaseError,
16 + TestRecommendedRules,
17 +} from './shared-utils';
18 +import {allRules} from '../src/rules/ReactCompilerRule';
19 +
20 +testRule('plugin-recommended', TestRecommendedRules, {
21 + valid: [
22 + {
23 + name: 'Basic example with component syntax',
24 + code: normalizeIndent`
25 + export default component HelloWorld(
26 + text: string = 'Hello!',
27 + onClick: () => void,
28 + ) {
29 + return <div onClick={onClick}>{text}</div>;
30 + }
31 + `,
32 + },
33 +
34 + {
35 + // OK because invariants are only meant for the compiler team's consumption
36 + name: '[Invariant] Defined after use',
37 + code: normalizeIndent`
38 + function Component(props) {
39 + let y = function () {
40 + m(x);
41 + };
42 +
43 + let x = { a };
44 + m(x);
45 + return y;
46 + }
47 + `,
48 + },
49 + {
50 + name: "Classes don't throw",
51 + code: normalizeIndent`
52 + class Foo {
53 + #bar() {}
54 + }
55 + `,
56 + },
57 + ],
58 + invalid: [
59 + {
60 + // TODO: actually return multiple diagnostics in this case
61 + name: 'Multiple diagnostic kinds from the same function are surfaced',
62 + code: normalizeIndent`
63 + import Child from './Child';
64 + function Component() {
65 + const result = cond ?? useConditionalHook();
66 + return <>
67 + {Child(result)}
68 + </>;
69 + }
70 + `,
71 + errors: [
72 + makeTestCaseError('Hooks must always be called in a consistent order'),
73 + ],
74 + },
75 + {
76 + name: 'Multiple diagnostics within the same file are surfaced',
77 + code: normalizeIndent`
78 + function useConditional1() {
79 + 'use memo';
80 + return cond ?? useConditionalHook();
81 + }
82 + function useConditional2(props) {
83 + 'use memo';
84 + return props.cond && useConditionalHook();
85 + }`,
86 + errors: [
87 + makeTestCaseError('Hooks must always be called in a consistent order'),
88 + makeTestCaseError('Hooks must always be called in a consistent order'),
89 + ],
90 + },
91 + {
92 + name: "'use no forget' does not disable eslint rule",
93 + code: normalizeIndent`
94 + let count = 0;
95 + function Component() {
96 + 'use no forget';
97 + return cond ?? useConditionalHook();
98 +
99 + }
100 + `,
101 + errors: [
102 + makeTestCaseError('Hooks must always be called in a consistent order'),
103 + ],
104 + },
105 + {
106 + name: 'Multiple non-fatal useMemo diagnostics are surfaced',
107 + code: normalizeIndent`
108 + import {useMemo, useState} from 'react';
109 +
110 + function Component({item, cond}) {
111 + const [prevItem, setPrevItem] = useState(item);
112 + const [state, setState] = useState(0);
113 +
114 + useMemo(() => {
115 + if (cond) {
116 + setPrevItem(item);
117 + setState(0);
118 + }
119 + }, [cond, item, init]);
120 +
121 + return <Child x={state} />;
122 + }`,
123 + errors: [makeTestCaseError('useMemo() callbacks must return a value')],
124 + },
125 + {
126 + name: 'Pipeline errors are reported',
127 + code: normalizeIndent`
128 + import useMyEffect from 'useMyEffect';
129 + import {AUTODEPS} from 'react';
130 + function Component({a}) {
131 + 'use no memo';
132 + useMyEffect(() => console.log(a.b), AUTODEPS);
133 + return <div>Hello world</div>;
134 + }
135 + `,
136 + options: [
137 + {
138 + environment: {
139 + inferEffectDependencies: [
140 + {
141 + function: {
142 + source: 'useMyEffect',
143 + importSpecifierName: 'default',
144 + },
145 + autodepsIndex: 1,
146 + },
147 + ],
148 + },
149 + },
150 + ],
151 + errors: [
152 + {
153 + message: /Cannot infer dependencies of this effect/,
154 + },
155 + ],
156 + },
157 + ],
158 +});
compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts deleted
-287
@@ -1,287 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -import {ErrorSeverity} from 'babel-plugin-react-compiler/src';
9 -import {RuleTester as ESLintTester} from 'eslint';
10 -import ReactCompilerRule from '../src/rules/ReactCompilerRule';
11 -
12 -/**
13 - * A string template tag that removes padding from the left side of multi-line strings
14 - * @param {Array} strings array of code strings (only one expected)
15 - */
16 -function normalizeIndent(strings: TemplateStringsArray): string {
17 - const codeLines = strings[0].split('\n');
18 - const leftPadding = codeLines[1].match(/\s+/)![0];
19 - return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
20 -}
21 -
22 -type CompilerTestCases = {
23 - valid: ESLintTester.ValidTestCase[];
24 - invalid: ESLintTester.InvalidTestCase[];
25 -};
26 -
27 -const tests: CompilerTestCases = {
28 - valid: [
29 - {
30 - name: 'Basic example',
31 - code: normalizeIndent`
32 - function foo(x, y) {
33 - if (x) {
34 - return foo(false, y);
35 - }
36 - return [y * 10];
37 - }
38 - `,
39 - },
40 - {
41 - name: 'Violation with Flow suppression',
42 - code: `
43 - // Valid since error already suppressed with flow.
44 - function useHookWithHook() {
45 - if (cond) {
46 - // $FlowFixMe[react-rule-hook]
47 - useConditionalHook();
48 - }
49 - }
50 - `,
51 - },
52 - {
53 - name: 'Basic example with component syntax',
54 - code: normalizeIndent`
55 - export default component HelloWorld(
56 - text: string = 'Hello!',
57 - onClick: () => void,
58 - ) {
59 - return <div onClick={onClick}>{text}</div>;
60 - }
61 - `,
62 - },
63 - {
64 - name: 'Unsupported syntax',
65 - code: normalizeIndent`
66 - function foo(x) {
67 - var y = 1;
68 - return y * x;
69 - }
70 - `,
71 - },
72 - {
73 - // OK because invariants are only meant for the compiler team's consumption
74 - name: '[Invariant] Defined after use',
75 - code: normalizeIndent`
76 - function Component(props) {
77 - let y = function () {
78 - m(x);
79 - };
80 -
81 - let x = { a };
82 - m(x);
83 - return y;
84 - }
85 - `,
86 - },
87 - {
88 - name: "Classes don't throw",
89 - code: normalizeIndent`
90 - class Foo {
91 - #bar() {}
92 - }
93 - `,
94 - },
95 - ],
96 - invalid: [
97 - {
98 - name: 'Reportable levels can be configured',
99 - options: [{reportableLevels: new Set([ErrorSeverity.Todo])}],
100 - code: normalizeIndent`
101 - function Foo(x) {
102 - var y = 1;
103 - return <div>{y * x}</div>;
104 - }`,
105 - errors: [
106 - {
107 - message: /Handle var kinds in VariableDeclaration/,
108 - },
109 - ],
110 - },
111 - {
112 - name: '[InvalidReact] ESlint suppression',
113 - // Indentation is intentionally weird so it doesn't add extra whitespace
114 - code: normalizeIndent`
115 - function Component(props) {
116 - // eslint-disable-next-line react-hooks/rules-of-hooks
117 - return <div>{props.foo}</div>;
118 - }`,
119 - errors: [
120 - {
121 - message: /React Compiler has skipped optimizing this component/,
122 - suggestions: [
123 - {
124 - output: normalizeIndent`
125 - function Component(props) {
126 -
127 - return <div>{props.foo}</div>;
128 - }`,
129 - },
130 - ],
131 - },
132 - {
133 - message:
134 - "Definition for rule 'react-hooks/rules-of-hooks' was not found.",
135 - },
136 - ],
137 - },
138 - {
139 - name: 'Multiple diagnostics are surfaced',
140 - options: [
141 - {
142 - reportableLevels: new Set([
143 - ErrorSeverity.Todo,
144 - ErrorSeverity.InvalidReact,
145 - ]),
146 - },
147 - ],
148 - code: normalizeIndent`
149 - function Foo(x) {
150 - var y = 1;
151 - return <div>{y * x}</div>;
152 - }
153 - function Bar(props) {
154 - props.a.b = 2;
155 - return <div>{props.c}</div>
156 - }`,
157 - errors: [
158 - {
159 - message: /Handle var kinds in VariableDeclaration/,
160 - },
161 - {
162 - message: /Modifying component props or hook arguments is not allowed/,
163 - },
164 - ],
165 - },
166 - {
167 - name: 'Test experimental/unstable report all bailouts mode',
168 - options: [
169 - {
170 - reportableLevels: new Set([ErrorSeverity.InvalidReact]),
171 - __unstable_donotuse_reportAllBailouts: true,
172 - },
173 - ],
174 - code: normalizeIndent`
175 - function Foo(x) {
176 - var y = 1;
177 - return <div>{y * x}</div>;
178 - }`,
179 - errors: [
180 - {
181 - message: /Handle var kinds in VariableDeclaration/,
182 - },
183 - ],
184 - },
185 - {
186 - name: "'use no forget' does not disable eslint rule",
187 - code: normalizeIndent`
188 - let count = 0;
189 - function Component() {
190 - 'use no forget';
191 - count = count + 1;
192 - return <div>Hello world {count}</div>
193 - }
194 - `,
195 - errors: [
196 - {
197 - message:
198 - /Cannot reassign variables declared outside of the component\/hook/,
199 - },
200 - ],
201 - },
202 - {
203 - name: "Unused 'use no forget' directive is reported when no errors are present on components",
204 - code: normalizeIndent`
205 - function Component() {
206 - 'use no forget';
207 - return <div>Hello world</div>
208 - }
209 - `,
210 - errors: [
211 - {
212 - message: "Unused 'use no forget' directive",
213 - suggestions: [
214 - {
215 - output:
216 - // yuck
217 - '\nfunction Component() {\n \n return <div>Hello world</div>\n}\n',
218 - },
219 - ],
220 - },
221 - ],
222 - },
223 - {
224 - name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks",
225 - code: normalizeIndent`
226 - function notacomponent() {
227 - 'use no forget';
228 - return 1 + 1;
229 - }
230 - `,
231 - errors: [
232 - {
233 - message: "Unused 'use no forget' directive",
234 - suggestions: [
235 - {
236 - output:
237 - // yuck
238 - '\nfunction notacomponent() {\n \n return 1 + 1;\n}\n',
239 - },
240 - ],
241 - },
242 - ],
243 - },
244 - {
245 - name: 'Pipeline errors are reported',
246 - code: normalizeIndent`
247 - import useMyEffect from 'useMyEffect';
248 - import {AUTODEPS} from 'react';
249 - function Component({a}) {
250 - 'use no memo';
251 - useMyEffect(() => console.log(a.b), AUTODEPS);
252 - return <div>Hello world</div>;
253 - }
254 - `,
255 - options: [
256 - {
257 - environment: {
258 - inferEffectDependencies: [
259 - {
260 - function: {
261 - source: 'useMyEffect',
262 - importSpecifierName: 'default',
263 - },
264 - autodepsIndex: 1,
265 - },
266 - ],
267 - },
268 - },
269 - ],
270 - errors: [
271 - {
272 - message: /Cannot infer dependencies of this effect/,
273 - },
274 - ],
275 - },
276 - ],
277 -};
278 -
279 -const eslintTester = new ESLintTester({
280 - parser: require.resolve('hermes-eslint'),
281 - parserOptions: {
282 - ecmaVersion: 2015,
283 - sourceType: 'module',
284 - enableExperimentalComponentSyntax: true,
285 - },
286 -});
287 -eslintTester.run('react-compiler', ReactCompilerRule, tests);
compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRuleTypescript-test.ts
+7 -17
@@ -6,22 +6,11 @@
6 */
7
8 import {RuleTester} from 'eslint';
9 -import ReactCompilerRule from '../src/rules/ReactCompilerRule';
10 -
11 -/**
12 - * A string template tag that removes padding from the left side of multi-line strings
13 - * @param {Array} strings array of code strings (only one expected)
14 - */
15 -function normalizeIndent(strings: TemplateStringsArray): string {
16 - const codeLines = strings[0].split('\n');
17 - const leftPadding = codeLines[1].match(/\s+/)[0];
18 - return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
19 -}
20 -
21 -type CompilerTestCases = {
22 - valid: RuleTester.ValidTestCase[];
23 - invalid: RuleTester.InvalidTestCase[];
24 -};
9 +import {
10 + CompilerTestCases,
11 + normalizeIndent,
12 + TestRecommendedRules,
13 +} from './shared-utils';
14
15 const tests: CompilerTestCases = {
16 valid: [
@@ -70,6 +59,7 @@ const tests: CompilerTestCases = {
59 };
60
61 const eslintTester = new RuleTester({
62 + // @ts-ignore[2353] - outdated types
63 parser: require.resolve('@typescript-eslint/parser'),
64 });
75 -eslintTester.run('react-compiler', ReactCompilerRule, tests);
65 +eslintTester.run('react-compiler', TestRecommendedRules, tests);
compiler/packages/eslint-plugin-react-compiler/__tests__/shared-utils.ts new
+76
@@ -0,0 +1,76 @@
1 +import {RuleTester as ESLintTester, Rule} from 'eslint';
2 +import {type ErrorCategory} from 'babel-plugin-react-compiler/src/CompilerError';
3 +import escape from 'regexp.escape';
4 +import {configs} from '../src/index';
5 +import {allRules} from '../src/rules/ReactCompilerRule';
6 +
7 +/**
8 + * A string template tag that removes padding from the left side of multi-line strings
9 + * @param {Array} strings array of code strings (only one expected)
10 + */
11 +export function normalizeIndent(strings: TemplateStringsArray): string {
12 + const codeLines = strings[0].split('\n');
13 + const leftPadding = codeLines[1].match(/\s+/)![0];
14 + return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
15 +}
16 +
17 +export type CompilerTestCases = {
18 + valid: ESLintTester.ValidTestCase[];
19 + invalid: ESLintTester.InvalidTestCase[];
20 +};
21 +
22 +export function makeTestCaseError(reason: string): ESLintTester.TestCaseError {
23 + return {
24 + message: new RegExp(escape(reason)),
25 + };
26 +}
27 +
28 +export function testRule(
29 + name: string,
30 + rule: Rule.RuleModule,
31 + tests: {
32 + valid: ESLintTester.ValidTestCase[];
33 + invalid: ESLintTester.InvalidTestCase[];
34 + },
35 +): void {
36 + const eslintTester = new ESLintTester({
37 + // @ts-ignore[2353] - outdated types
38 + parser: require.resolve('hermes-eslint'),
39 + parserOptions: {
40 + ecmaVersion: 2015,
41 + sourceType: 'module',
42 + enableExperimentalComponentSyntax: true,
43 + },
44 + });
45 +
46 + eslintTester.run(name, rule, tests);
47 +}
48 +
49 +/**
50 + * Aggregates all recommended rules from the plugin.
51 + */
52 +export const TestRecommendedRules: Rule.RuleModule = {
53 + meta: {
54 + type: 'problem',
55 + docs: {
56 + description: 'Disallow capitalized function calls',
57 + category: 'Possible Errors',
58 + recommended: true,
59 + },
60 + // validation is done at runtime with zod
61 + schema: [{type: 'object', additionalProperties: true}],
62 + },
63 + create(context) {
64 + for (const rule of Object.values(
65 + configs.recommended.plugins['react-compiler'].rules,
66 + )) {
67 + const listener = rule.create(context);
68 + if (Object.entries(listener).length !== 0) {
69 + throw new Error('TODO: handle rules that return listeners to eslint');
70 + }
71 + }
72 + return {};
73 + },
74 +};
75 +
76 +test('no test', () => {});
compiler/packages/eslint-plugin-react-compiler/package.json
+3 -1
@@ -24,11 +24,13 @@
24 "@babel/preset-typescript": "^7.18.6",
25 "@babel/types": "^7.26.0",
26 "@types/eslint": "^8.56.12",
27 + "@types/jest": "^30.0.0",
28 "@types/node": "^20.2.5",
29 "babel-jest": "^29.0.3",
30 "eslint": "8.57.0",
31 "hermes-eslint": "^0.25.1",
31 - "jest": "^29.5.0"
32 + "jest": "^29.5.0",
33 + "regexp.escape": "^2.0.1"
34 },
35 "engines": {
36 "node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
compiler/packages/eslint-plugin-react-compiler/src/index.ts
+9 -12
@@ -5,29 +5,26 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import ReactCompilerRule from './rules/ReactCompilerRule';
8 +import {allRules, recommendedRules} from './rules/ReactCompilerRule';
9
10 const meta = {
11 name: 'eslint-plugin-react-compiler',
12 };
13
14 -const rules = {
15 - 'react-compiler': ReactCompilerRule,
16 -};
17 -
14 const configs = {
15 recommended: {
16 plugins: {
17 'react-compiler': {
22 - rules: {
23 - 'react-compiler': ReactCompilerRule,
24 - },
18 + rules: allRules,
19 },
20 },
27 - rules: {
28 - 'react-compiler/react-compiler': 'error' as const,
29 - },
21 + rules: Object.fromEntries(
22 + Object.keys(recommendedRules).map(ruleName => [
23 + 'react-compiler/' + ruleName,
24 + 'error',
25 + ]),
26 + ) as Record<string, 'error' | 'warn'>,
27 },
28 };
29
33 -export {configs, rules, meta};
30 +export {configs, allRules as rules, meta};
compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts
+123 -263
@@ -5,43 +5,23 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {transformFromAstSync} from '@babel/core';
9 -// @ts-expect-error: no types available
10 -import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods';
8 import type {SourceLocation as BabelSourceLocation} from '@babel/types';
12 -import BabelPluginReactCompiler, {
13 - CompilerDiagnostic,
9 +import {
10 CompilerDiagnosticOptions,
15 - CompilerErrorDetail,
11 CompilerErrorDetailOptions,
12 CompilerSuggestionOperation,
18 - ErrorSeverity,
19 - parsePluginOptions,
20 - validateEnvironmentConfig,
21 - OPT_OUT_DIRECTIVES,
22 - type PluginOptions,
13 } from 'babel-plugin-react-compiler/src';
24 -import {Logger, LoggerEvent} from 'babel-plugin-react-compiler/src/Entrypoint';
14 import type {Rule} from 'eslint';
26 -import {Statement} from 'estree';
27 -import * as HermesParser from 'hermes-parser';
15 +import runReactCompiler, {RunCacheEntry} from '../shared/RunReactCompiler';
16 +import {
17 + LintRules,
18 + type LintRule,
19 +} from 'babel-plugin-react-compiler/src/CompilerError';
20
21 function assertExhaustive(_: never, errorMsg: string): never {
22 throw new Error(errorMsg);
23 }
24
33 -const DEFAULT_REPORTABLE_LEVELS = new Set([
34 - ErrorSeverity.InvalidReact,
35 - ErrorSeverity.InvalidJS,
36 -]);
37 -let reportableLevels = DEFAULT_REPORTABLE_LEVELS;
38 -
39 -function isReportableDiagnostic(
40 - detail: CompilerErrorDetail | CompilerDiagnostic,
41 -): boolean {
42 - return reportableLevels.has(detail.severity);
43 -}
44 -
25 function makeSuggestions(
26 detail: CompilerErrorDetailOptions | CompilerDiagnosticOptions,
27 ): Array<Rule.SuggestionReportDescriptor> {
@@ -95,263 +75,143 @@ function makeSuggestions(
75 return suggest;
76 }
77
98 -const COMPILER_OPTIONS: Partial<PluginOptions> = {
99 - noEmit: true,
100 - panicThreshold: 'none',
101 - // Don't emit errors on Flow suppressions--Flow already gave a signal
102 - flowSuppressions: false,
103 - environment: validateEnvironmentConfig({
104 - validateRefAccessDuringRender: true,
105 - validateNoSetStateInRender: true,
106 - validateNoSetStateInEffects: true,
107 - validateNoJSXInTryStatements: true,
108 - validateNoImpureFunctionsInRender: true,
109 - validateStaticComponents: true,
110 - validateNoFreezingKnownMutableFunctions: true,
111 - validateNoVoidUseMemo: true,
112 - }),
113 -};
78 +function getReactCompilerResult(context: Rule.RuleContext): RunCacheEntry {
79 + // Compat with older versions of eslint
80 + const sourceCode = context.sourceCode ?? context.getSourceCode();
81 + const filename = context.filename ?? context.getFilename();
82 + const userOpts = context.options[0] ?? {};
83
115 -const rule: Rule.RuleModule = {
116 - meta: {
117 - type: 'problem',
118 - docs: {
119 - description: 'Surfaces diagnostics from React Forget',
120 - recommended: true,
121 - },
122 - fixable: 'code',
123 - hasSuggestions: true,
124 - // validation is done at runtime with zod
125 - schema: [{type: 'object', additionalProperties: true}],
126 - },
127 - create(context: Rule.RuleContext) {
128 - // Compat with older versions of eslint
129 - const sourceCode = context.sourceCode ?? context.getSourceCode();
130 - const filename = context.filename ?? context.getFilename();
131 - const userOpts = context.options[0] ?? {};
132 - if (
133 - userOpts.reportableLevels != null &&
134 - userOpts.reportableLevels instanceof Set
135 - ) {
136 - reportableLevels = userOpts.reportableLevels;
137 - } else {
138 - reportableLevels = DEFAULT_REPORTABLE_LEVELS;
139 - }
140 - /**
141 - * Experimental setting to report all compilation bailouts on the compilation
142 - * unit (e.g. function or hook) instead of the offensive line.
143 - * Intended to be used when a codebase is 100% reliant on the compiler for
144 - * memoization (i.e. deleted all manual memo) and needs compilation success
145 - * signals for perf debugging.
146 - */
147 - let __unstable_donotuse_reportAllBailouts: boolean = false;
84 + const results = runReactCompiler({
85 + sourceCode,
86 + filename,
87 + userOpts,
88 + });
89 +
90 + return results;
91 +}
92 +
93 +function hasFlowSuppression(
94 + program: RunCacheEntry,
95 + nodeLoc: BabelSourceLocation,
96 + suppressions: Array<string>,
97 +): boolean {
98 + for (const commentNode of program.flowSuppressions) {
99 if (
149 - userOpts.__unstable_donotuse_reportAllBailouts != null &&
150 - typeof userOpts.__unstable_donotuse_reportAllBailouts === 'boolean'
100 + suppressions.includes(commentNode.code) &&
101 + commentNode.line === nodeLoc.start.line - 1
102 ) {
152 - __unstable_donotuse_reportAllBailouts =
153 - userOpts.__unstable_donotuse_reportAllBailouts;
103 + return true;
104 }
105 + }
106 + return false;
107 +}
108
156 - let shouldReportUnusedOptOutDirective = true;
157 - const options: PluginOptions = parsePluginOptions({
158 - ...COMPILER_OPTIONS,
159 - ...userOpts,
160 - environment: {
161 - ...COMPILER_OPTIONS.environment,
162 - ...userOpts.environment,
163 - },
164 - });
165 - const userLogger: Logger | null = options.logger;
166 - options.logger = {
167 - logEvent: (eventFilename, event): void => {
168 - userLogger?.logEvent(eventFilename, event);
169 - if (event.kind === 'CompileError') {
170 - shouldReportUnusedOptOutDirective = false;
171 - const detail = event.detail;
172 - const suggest = makeSuggestions(detail.options);
173 - if (__unstable_donotuse_reportAllBailouts && event.fnLoc != null) {
174 - const loc = detail.primaryLocation();
175 - const locStr =
176 - loc != null && typeof loc !== 'symbol'
177 - ? ` (@:${loc.start.line}:${loc.start.column})`
178 - : '';
179 - /**
180 - * Report bailouts with a smaller span (just the first line).
181 - * Compiler bailout lints only serve to flag that a react function
182 - * has not been optimized by the compiler for codebases which depend
183 - * on compiler memo heavily for perf. These lints are also often not
184 - * actionable.
185 - */
186 - let endLoc;
187 - if (event.fnLoc.end.line === event.fnLoc.start.line) {
188 - endLoc = event.fnLoc.end;
189 - } else {
190 - endLoc = {
191 - line: event.fnLoc.start.line,
192 - // Babel loc line numbers are 1-indexed
193 - column:
194 - sourceCode.text.split(/\r?\n|\r|\n/g)[
195 - event.fnLoc.start.line - 1
196 - ]?.length ?? 0,
197 - };
198 - }
199 - const firstLineLoc = {
200 - start: event.fnLoc.start,
201 - end: endLoc,
202 - };
203 - context.report({
204 - message: `${detail.printErrorMessage(sourceCode.text, {eslint: true})} ${locStr}`,
205 - loc: firstLineLoc,
206 - suggest,
207 - });
208 - }
109 +function makeRule(rule: LintRule): Rule.RuleModule {
110 + const create = (context: Rule.RuleContext): Rule.RuleListener => {
111 + const result = getReactCompilerResult(context);
112
113 + for (const event of result.events) {
114 + if (event.kind === 'CompileError') {
115 + const detail = event.detail;
116 + if (detail.category === rule.category) {
117 const loc = detail.primaryLocation();
211 - if (
212 - !isReportableDiagnostic(detail) ||
213 - loc == null ||
214 - typeof loc === 'symbol'
215 - ) {
216 - return;
118 + if (loc == null || typeof loc === 'symbol') {
119 + continue;
120 }
121 if (
219 - hasFlowSuppression(loc, 'react-rule-hook') ||
220 - hasFlowSuppression(loc, 'react-rule-unsafe-ref')
122 + hasFlowSuppression(result, loc, [
123 + 'react-rule-hook',
124 + 'react-rule-unsafe-ref',
125 + ])
126 ) {
127 // If Flow already caught this error, we don't need to report it again.
223 - return;
128 + continue;
129 }
225 - if (loc != null) {
226 - context.report({
227 - message: detail.printErrorMessage(sourceCode.text, {
228 - eslint: true,
229 - }),
230 - loc,
231 - suggest,
232 - });
233 - }
234 - }
235 - },
236 - };
237 -
238 - try {
239 - options.environment = validateEnvironmentConfig(
240 - options.environment ?? {},
241 - );
242 - } catch (err: unknown) {
243 - options.logger?.logEvent('', err as LoggerEvent);
244 - }
245 -
246 - function hasFlowSuppression(
247 - nodeLoc: BabelSourceLocation,
248 - suppression: string,
249 - ): boolean {
250 - const comments = sourceCode.getAllComments();
251 - const flowSuppressionRegex = new RegExp(
252 - '\\$FlowFixMe\\[' + suppression + '\\]',
253 - );
254 - for (const commentNode of comments) {
255 - if (
256 - flowSuppressionRegex.test(commentNode.value) &&
257 - commentNode.loc!.end.line === nodeLoc.start.line - 1
258 - ) {
259 - return true;
130 + /*
131 + * TODO: if multiple rules report the same linter category,
132 + * we should deduplicate them with a "reported" set
133 + */
134 + context.report({
135 + message: detail.printErrorMessage(result.sourceCode, {
136 + eslint: true,
137 + }),
138 + loc,
139 + suggest: makeSuggestions(detail.options),
140 + });
141 }
142 }
262 - return false;
143 }
144 + return {};
145 + };
146
265 - let babelAST;
266 - if (filename.endsWith('.tsx') || filename.endsWith('.ts')) {
267 - try {
268 - const {parse: babelParse} = require('@babel/parser');
269 - babelAST = babelParse(sourceCode.text, {
270 - filename,
271 - sourceType: 'unambiguous',
272 - plugins: ['typescript', 'jsx'],
273 - });
274 - } catch {
275 - /* empty */
276 - }
277 - } else {
278 - try {
279 - babelAST = HermesParser.parse(sourceCode.text, {
280 - babel: true,
281 - enableExperimentalComponentSyntax: true,
282 - sourceFilename: filename,
283 - sourceType: 'module',
284 - });
285 - } catch {
286 - /* empty */
287 - }
288 - }
147 + return {
148 + meta: {
149 + type: 'problem',
150 + docs: {
151 + description: rule.description,
152 + recommended: rule.recommended,
153 + },
154 + fixable: 'code',
155 + hasSuggestions: true,
156 + // validation is done at runtime with zod
157 + schema: [{type: 'object', additionalProperties: true}],
158 + },
159 + create,
160 + };
161 +}
162
290 - if (babelAST != null) {
291 - try {
292 - transformFromAstSync(babelAST, sourceCode.text, {
293 - filename,
294 - highlightCode: false,
295 - retainLines: true,
296 - plugins: [
297 - [PluginProposalPrivateMethods, {loose: true}],
298 - [BabelPluginReactCompiler, options],
299 - ],
300 - sourceType: 'module',
301 - configFile: false,
302 - babelrc: false,
303 - });
304 - } catch (err) {
305 - /* errors handled by injected logger */
306 - }
307 - }
163 +export const NoUnusedDirectivesRule: Rule.RuleModule = {
164 + meta: {
165 + type: 'suggestion',
166 + docs: {
167 + recommended: true,
168 + },
169 + fixable: 'code',
170 + hasSuggestions: true,
171 + // validation is done at runtime with zod
172 + schema: [{type: 'object', additionalProperties: true}],
173 + },
174 + create(context: Rule.RuleContext): Rule.RuleListener {
175 + const results = getReactCompilerResult(context);
176
309 - function reportUnusedOptOutDirective(stmt: Statement) {
310 - if (
311 - stmt.type === 'ExpressionStatement' &&
312 - stmt.expression.type === 'Literal' &&
313 - typeof stmt.expression.value === 'string' &&
314 - OPT_OUT_DIRECTIVES.has(stmt.expression.value) &&
315 - stmt.loc != null
316 - ) {
317 - context.report({
318 - message: `Unused '${stmt.expression.value}' directive`,
319 - loc: stmt.loc,
320 - suggest: [
321 - {
322 - desc: 'Remove the directive',
323 - fix(fixer) {
324 - return fixer.remove(stmt);
325 - },
177 + for (const directive of results.unusedOptOutDirectives) {
178 + context.report({
179 + message: `Unused '${directive.directive}' directive`,
180 + loc: directive.loc,
181 + suggest: [
182 + {
183 + desc: 'Remove the directive',
184 + fix(fixer): Rule.Fix {
185 + return fixer.removeRange(directive.range);
186 },
327 - ],
328 - });
329 - }
330 - }
331 - if (shouldReportUnusedOptOutDirective) {
332 - return {
333 - FunctionDeclaration(fnDecl) {
334 - for (const stmt of fnDecl.body.body) {
335 - reportUnusedOptOutDirective(stmt);
336 - }
337 - },
338 - ArrowFunctionExpression(fnExpr) {
339 - if (fnExpr.body.type === 'BlockStatement') {
340 - for (const stmt of fnExpr.body.body) {
341 - reportUnusedOptOutDirective(stmt);
342 - }
343 - }
344 - },
345 - FunctionExpression(fnExpr) {
346 - for (const stmt of fnExpr.body.body) {
347 - reportUnusedOptOutDirective(stmt);
348 - }
349 - },
350 - };
351 - } else {
352 - return {};
187 + },
188 + ],
189 + });
190 }
191 + return {};
192 },
193 };
194
357 -export default rule;
195 +type RulesObject = {[name: string]: Rule.RuleModule};
196 +
197 +export const allRules: RulesObject = LintRules.reduce(
198 + (acc, rule) => {
199 + acc[rule.name] = makeRule(rule);
200 + return acc;
201 + },
202 + {
203 + 'no-unused-directives': NoUnusedDirectivesRule,
204 + } as RulesObject,
205 +);
206 +
207 +export const recommendedRules: RulesObject = LintRules.filter(
208 + rule => rule.recommended,
209 +).reduce(
210 + (acc, rule) => {
211 + acc[rule.name] = makeRule(rule);
212 + return acc;
213 + },
214 + {
215 + 'no-unused-directives': NoUnusedDirectivesRule,
216 + } as RulesObject,
217 +);
compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts new
+287
@@ -0,0 +1,287 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {transformFromAstSync, traverse} from '@babel/core';
9 +import {parse as babelParse} from '@babel/parser';
10 +import {Directive, File} from '@babel/types';
11 +// @ts-expect-error: no types available
12 +import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods';
13 +import BabelPluginReactCompiler, {
14 + parsePluginOptions,
15 + validateEnvironmentConfig,
16 + OPT_OUT_DIRECTIVES,
17 + type PluginOptions,
18 +} from 'babel-plugin-react-compiler/src';
19 +import {Logger, LoggerEvent} from 'babel-plugin-react-compiler/src/Entrypoint';
20 +import type {SourceCode} from 'eslint';
21 +import {SourceLocation} from 'estree';
22 +// @ts-expect-error: no types available
23 +import * as HermesParser from 'hermes-parser';
24 +import {isDeepStrictEqual} from 'util';
25 +import type {ParseResult} from '@babel/parser';
26 +
27 +const COMPILER_OPTIONS: Partial<PluginOptions> = {
28 + noEmit: true,
29 + panicThreshold: 'none',
30 + // Don't emit errors on Flow suppressions--Flow already gave a signal
31 + flowSuppressions: false,
32 + environment: validateEnvironmentConfig({
33 + validateRefAccessDuringRender: true,
34 + validateNoSetStateInRender: true,
35 + validateNoSetStateInEffects: true,
36 + validateNoJSXInTryStatements: true,
37 + validateNoImpureFunctionsInRender: true,
38 + validateStaticComponents: true,
39 + validateNoFreezingKnownMutableFunctions: true,
40 + validateNoVoidUseMemo: true,
41 + // TODO: remove, this should be in the type system
42 + validateNoCapitalizedCalls: [],
43 + validateHooksUsage: true,
44 + validateNoDerivedComputationsInEffects: true,
45 + }),
46 +};
47 +
48 +export type UnusedOptOutDirective = {
49 + loc: SourceLocation;
50 + range: [number, number];
51 + directive: string;
52 +};
53 +export type RunCacheEntry = {
54 + sourceCode: string;
55 + filename: string;
56 + userOpts: PluginOptions;
57 + flowSuppressions: Array<{line: number; code: string}>;
58 + unusedOptOutDirectives: Array<UnusedOptOutDirective>;
59 + events: Array<LoggerEvent>;
60 +};
61 +
62 +type RunParams = {
63 + sourceCode: SourceCode;
64 + filename: string;
65 + userOpts: PluginOptions;
66 +};
67 +const FLOW_SUPPRESSION_REGEX = /\$FlowFixMe\[([^\]]*)\]/g;
68 +
69 +function getFlowSuppressions(
70 + sourceCode: SourceCode,
71 +): Array<{line: number; code: string}> {
72 + const comments = sourceCode.getAllComments();
73 + const results: Array<{line: number; code: string}> = [];
74 +
75 + for (const commentNode of comments) {
76 + const matches = commentNode.value.matchAll(FLOW_SUPPRESSION_REGEX);
77 + for (const match of matches) {
78 + if (match.index != null && commentNode.loc != null) {
79 + const code = match[1];
80 + results.push({
81 + line: commentNode.loc!.end.line,
82 + code,
83 + });
84 + }
85 + }
86 + }
87 + return results;
88 +}
89 +
90 +function filterUnusedOptOutDirectives(
91 + directives: ReadonlyArray<Directive>,
92 +): Array<UnusedOptOutDirective> {
93 + const results: Array<UnusedOptOutDirective> = [];
94 + for (const directive of directives) {
95 + if (
96 + OPT_OUT_DIRECTIVES.has(directive.value.value) &&
97 + directive.loc != null
98 + ) {
99 + results.push({
100 + loc: directive.loc,
101 + directive: directive.value.value,
102 + range: [directive.start!, directive.end!],
103 + });
104 + }
105 + }
106 + return results;
107 +}
108 +
109 +function runReactCompilerImpl({
110 + sourceCode,
111 + filename,
112 + userOpts,
113 +}: RunParams): RunCacheEntry {
114 + // Compat with older versions of eslint
115 + const options: PluginOptions = parsePluginOptions({
116 + ...COMPILER_OPTIONS,
117 + ...userOpts,
118 + environment: {
119 + ...COMPILER_OPTIONS.environment,
120 + ...userOpts.environment,
121 + },
122 + });
123 + const results: RunCacheEntry = {
124 + sourceCode: sourceCode.text,
125 + filename,
126 + userOpts,
127 + flowSuppressions: [],
128 + unusedOptOutDirectives: [],
129 + events: [],
130 + };
131 + const userLogger: Logger | null = options.logger;
132 + options.logger = {
133 + logEvent: (eventFilename, event): void => {
134 + userLogger?.logEvent(eventFilename, event);
135 + results.events.push(event);
136 + },
137 + };
138 +
139 + try {
140 + options.environment = validateEnvironmentConfig(options.environment ?? {});
141 + } catch (err: unknown) {
142 + options.logger?.logEvent(filename, err as LoggerEvent);
143 + }
144 +
145 + let babelAST: ParseResult<File> | null = null;
146 + if (filename.endsWith('.tsx') || filename.endsWith('.ts')) {
147 + try {
148 + babelAST = babelParse(sourceCode.text, {
149 + sourceFilename: filename,
150 + sourceType: 'unambiguous',
151 + plugins: ['typescript', 'jsx'],
152 + });
153 + } catch {
154 + /* empty */
155 + }
156 + } else {
157 + try {
158 + babelAST = HermesParser.parse(sourceCode.text, {
159 + babel: true,
160 + enableExperimentalComponentSyntax: true,
161 + sourceFilename: filename,
162 + sourceType: 'module',
163 + });
164 + } catch {
165 + /* empty */
166 + }
167 + }
168 +
169 + if (babelAST != null) {
170 + results.flowSuppressions = getFlowSuppressions(sourceCode);
171 + try {
172 + transformFromAstSync(babelAST, sourceCode.text, {
173 + filename,
174 + highlightCode: false,
175 + retainLines: true,
176 + plugins: [
177 + [PluginProposalPrivateMethods, {loose: true}],
178 + [BabelPluginReactCompiler, options],
179 + ],
180 + sourceType: 'module',
181 + configFile: false,
182 + babelrc: false,
183 + });
184 +
185 + if (results.events.filter(e => e.kind === 'CompileError').length === 0) {
186 + traverse(babelAST, {
187 + FunctionDeclaration(path) {
188 + path.node;
189 + results.unusedOptOutDirectives.push(
190 + ...filterUnusedOptOutDirectives(path.node.body.directives),
191 + );
192 + },
193 + ArrowFunctionExpression(path) {
194 + if (path.node.body.type === 'BlockStatement') {
195 + results.unusedOptOutDirectives.push(
196 + ...filterUnusedOptOutDirectives(path.node.body.directives),
197 + );
198 + }
199 + },
200 + FunctionExpression(path) {
201 + results.unusedOptOutDirectives.push(
202 + ...filterUnusedOptOutDirectives(path.node.body.directives),
203 + );
204 + },
205 + });
206 + }
207 + } catch (err) {
208 + /* errors handled by injected logger */
209 + }
210 + }
211 +
212 + return results;
213 +}
214 +
215 +const SENTINEL = Symbol();
216 +
217 +// Array backed LRU cache -- should be small < 10 elements
218 +class LRUCache<K, T> {
219 + // newest at headIdx, then headIdx + 1, ..., tailIdx
220 + #values: Array<[K, T | Error] | [typeof SENTINEL, void]>;
221 + #headIdx: number = 0;
222 +
223 + constructor(size: number) {
224 + this.#values = new Array(size).fill(SENTINEL);
225 + }
226 +
227 + // gets a value and sets it as "recently used"
228 + get(key: K): T | null {
229 + let idx = this.#values.findIndex(entry => entry[0] === key);
230 + // If found, move to front
231 + if (idx === this.#headIdx) {
232 + return this.#values[this.#headIdx][1] as T;
233 + } else if (idx < 0) {
234 + return null;
235 + }
236 +
237 + const entry: [K, T] = this.#values[idx] as [K, T];
238 +
239 + const len = this.#values.length;
240 + for (let i = 0; i < Math.min(idx, len - 1); i++) {
241 + this.#values[(this.#headIdx + i + 1) % len] =
242 + this.#values[(this.#headIdx + i) % len];
243 + }
244 + this.#values[this.#headIdx] = entry;
245 + return entry[1];
246 + }
247 + push(key: K, value: T): void {
248 + this.#headIdx =
249 + (this.#headIdx - 1 + this.#values.length) % this.#values.length;
250 + this.#values[this.#headIdx] = [key, value];
251 + }
252 +}
253 +const cache = new LRUCache<string, RunCacheEntry>(10);
254 +
255 +export default function runReactCompiler({
256 + sourceCode,
257 + filename,
258 + userOpts,
259 +}: RunParams): RunCacheEntry {
260 + const entry = cache.get(filename);
261 + if (
262 + entry != null &&
263 + entry.sourceCode === sourceCode.text &&
264 + isDeepStrictEqual(entry.userOpts, userOpts)
265 + ) {
266 + return entry;
267 + } else if (entry != null) {
268 + if (process.env['DEBUG']) {
269 + console.log(
270 + `Cache hit for ${filename}, but source code or options changed, recomputing`,
271 + );
272 + }
273 + }
274 +
275 + const runEntry = runReactCompilerImpl({
276 + sourceCode,
277 + filename,
278 + userOpts,
279 + });
280 + // If we have a cache entry, we can update it
281 + if (entry != null) {
282 + Object.assign(entry, runEntry);
283 + } else {
284 + cache.push(filename, runEntry);
285 + }
286 + return {...runEntry};
287 +}
compiler/packages/snap/src/compiler.ts
+10 -1
@@ -338,7 +338,16 @@ export async function transformFixtureInput(
338 if (logs.length !== 0) {
339 formattedLogs = logs
340 .map(({event}) => {
341 - return JSON.stringify(event);
341 + return JSON.stringify(event, (key, value) => {
342 + if (
343 + key === 'detail' &&
344 + value != null &&
345 + typeof value.serialize === 'function'
346 + ) {
347 + return value.serialize();
348 + }
349 + return value;
350 + });
351 })
352 .join('\n');
353 }
compiler/yarn.lock
+873 -28
@@ -542,11 +542,6 @@
542 resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz"
543 integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
544
545 -"@babel/helper-string-parser@^7.27.1":
546 - version "7.27.1"
547 - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
548 - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
549 -
545 "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.25.9":
546 version "7.25.9"
547 resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz"
@@ -1605,7 +1600,7 @@
1600 debug "^4.3.1"
1601 globals "^11.1.0"
1602
1608 -"@babel/types@^7.0.0", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.2", "@babel/types@^7.24.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.4":
1603 +"@babel/types@7.26.3", "@babel/types@^7.0.0", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.2", "@babel/types@^7.24.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.10", "@babel/types@^7.26.3", "@babel/types@^7.27.0", "@babel/types@^7.27.1", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.4":
1604 version "7.26.3"
1605 resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0"
1606 integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==
@@ -1613,14 +1608,6 @@
1608 "@babel/helper-string-parser" "^7.25.9"
1609 "@babel/helper-validator-identifier" "^7.25.9"
1610
1616 -"@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.27.1":
1617 - version "7.27.1"
1618 - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560"
1619 - integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==
1620 - dependencies:
1621 - "@babel/helper-string-parser" "^7.27.1"
1622 - "@babel/helper-validator-identifier" "^7.27.1"
1623 -
1611 "@bcoe/v8-coverage@^0.2.3":
1612 version "0.2.3"
1613 resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
@@ -2148,6 +2135,11 @@
2135 slash "^3.0.0"
2136 strip-ansi "^6.0.0"
2137
2138 +"@jest/diff-sequences@30.0.1":
2139 + version "30.0.1"
2140 + resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be"
2141 + integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==
2142 +
2143 "@jest/environment@^28.1.3":
2144 version "28.1.3"
2145 resolved "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz"
@@ -2178,6 +2170,13 @@
2170 "@types/node" "*"
2171 jest-mock "^29.5.0"
2172
2173 +"@jest/expect-utils@30.0.5":
2174 + version "30.0.5"
2175 + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.0.5.tgz#9d42e4b8bc80367db30abc6c42b2cb14073f66fc"
2176 + integrity sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==
2177 + dependencies:
2178 + "@jest/get-type" "30.0.1"
2179 +
2180 "@jest/expect-utils@^28.1.3":
2181 version "28.1.3"
2182 resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz"
@@ -2274,6 +2273,11 @@
2273 jest-mock "^29.5.0"
2274 jest-util "^29.5.0"
2275
2276 +"@jest/get-type@30.0.1":
2277 + version "30.0.1"
2278 + resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.0.1.tgz#0d32f1bbfba511948ad247ab01b9007724fc9f52"
2279 + integrity sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==
2280 +
2281 "@jest/globals@^28.1.3":
2282 version "28.1.3"
2283 resolved "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz"
@@ -2313,6 +2317,14 @@
2317 "@jest/types" "^29.6.3"
2318 jest-mock "^29.7.0"
2319
2320 +"@jest/pattern@30.0.1":
2321 + version "30.0.1"
2322 + resolved "https://registry.yarnpkg.com/@jest/pattern/-/pattern-30.0.1.tgz#d5304147f49a052900b4b853dedb111d080e199f"
2323 + integrity sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==
2324 + dependencies:
2325 + "@types/node" "*"
2326 + jest-regex-util "30.0.1"
2327 +
2328 "@jest/reporters@^28.1.3":
2329 version "28.1.3"
2330 resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz"
@@ -2435,6 +2447,13 @@
2447 strip-ansi "^6.0.0"
2448 v8-to-istanbul "^9.0.1"
2449
2450 +"@jest/schemas@30.0.5":
2451 + version "30.0.5"
2452 + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.5.tgz#7bdf69fc5a368a5abdb49fd91036c55225846473"
2453 + integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==
2454 + dependencies:
2455 + "@sinclair/typebox" "^0.34.0"
2456 +
2457 "@jest/schemas@^28.1.3":
2458 version "28.1.3"
2459 resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz"
@@ -2663,6 +2682,19 @@
2682 slash "^3.0.0"
2683 write-file-atomic "^4.0.2"
2684
2685 +"@jest/types@30.0.5":
2686 + version "30.0.5"
2687 + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.0.5.tgz#29a33a4c036e3904f1cfd94f6fe77f89d2e1cc05"
2688 + integrity sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==
2689 + dependencies:
2690 + "@jest/pattern" "30.0.1"
2691 + "@jest/schemas" "30.0.5"
2692 + "@types/istanbul-lib-coverage" "^2.0.6"
2693 + "@types/istanbul-reports" "^3.0.4"
2694 + "@types/node" "*"
2695 + "@types/yargs" "^17.0.33"
2696 + chalk "^4.1.2"
2697 +
2698 "@jest/types@^24.9.0":
2699 version "24.9.0"
2700 resolved "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz"
@@ -2965,6 +2997,11 @@
2997 resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz"
2998 integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
2999
3000 +"@sinclair/typebox@^0.34.0":
3001 + version "0.34.38"
3002 + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.38.tgz#2365df7c23406a4d79413a766567bfbca708b49d"
3003 + integrity sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==
3004 +
3005 "@sinonjs/commons@^1.7.0":
3006 version "1.8.3"
3007 resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz"
@@ -3154,6 +3191,11 @@
3191 resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz"
3192 integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==
3193
3194 +"@types/istanbul-lib-coverage@^2.0.6":
3195 + version "2.0.6"
3196 + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
3197 + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==
3198 +
3199 "@types/istanbul-lib-report@*":
3200 version "3.0.0"
3201 resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz"
@@ -3176,6 +3218,13 @@
3218 dependencies:
3219 "@types/istanbul-lib-report" "*"
3220
3221 +"@types/istanbul-reports@^3.0.4":
3222 + version "3.0.4"
3223 + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54"
3224 + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==
3225 + dependencies:
3226 + "@types/istanbul-lib-report" "*"
3227 +
3228 "@types/jest@^28.1.6":
3229 version "28.1.8"
3230 resolved "https://registry.npmjs.org/@types/jest/-/jest-28.1.8.tgz"
@@ -3200,6 +3249,14 @@
3249 expect "^29.0.0"
3250 pretty-format "^29.0.0"
3251
3252 +"@types/jest@^30.0.0":
3253 + version "30.0.0"
3254 + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-30.0.0.tgz#5e85ae568006712e4ad66f25433e9bdac8801f1d"
3255 + integrity sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==
3256 + dependencies:
3257 + expect "^30.0.0"
3258 + pretty-format "^30.0.0"
3259 +
3260 "@types/jsdom@^20.0.0":
3261 version "20.0.0"
3262 resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.0.tgz"
@@ -3282,6 +3339,11 @@
3339 resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz"
3340 integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
3341
3342 +"@types/stack-utils@^2.0.3":
3343 + version "2.0.3"
3344 + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
3345 + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
3346 +
3347 "@types/tough-cookie@*":
3348 version "4.0.2"
3349 resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz"
@@ -3309,6 +3371,13 @@
3371 dependencies:
3372 "@types/yargs-parser" "*"
3373
3374 +"@types/yargs@^17.0.33":
3375 + version "17.0.33"
3376 + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d"
3377 + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==
3378 + dependencies:
3379 + "@types/yargs-parser" "*"
3380 +
3381 "@types/yargs@^17.0.8":
3382 version "17.0.13"
3383 resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz"
@@ -3740,7 +3809,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
3809 dependencies:
3810 color-convert "^2.0.1"
3811
3743 -ansi-styles@^5.0.0:
3812 +ansi-styles@^5.0.0, ansi-styles@^5.2.0:
3813 version "5.2.0"
3814 resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz"
3815 integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
@@ -3803,11 +3872,32 @@ aria-query@^5.0.0:
3872 resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.0.2.tgz"
3873 integrity sha512-eigU3vhqSO+Z8BKDnVLN/ompjhf3pYzecKXz8+whRy+9gZu8n1TCGfwzQUUPnqdHl9ax1Hr9031orZ+UOEYr7Q==
3874
3875 +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2:
3876 + version "1.0.2"
3877 + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b"
3878 + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==
3879 + dependencies:
3880 + call-bound "^1.0.3"
3881 + is-array-buffer "^3.0.5"
3882 +
3883 array-union@^2.1.0:
3884 version "2.1.0"
3885 resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz"
3886 integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
3887
3888 +arraybuffer.prototype.slice@^1.0.4:
3889 + version "1.0.4"
3890 + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c"
3891 + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==
3892 + dependencies:
3893 + array-buffer-byte-length "^1.0.1"
3894 + call-bind "^1.0.8"
3895 + define-properties "^1.2.1"
3896 + es-abstract "^1.23.5"
3897 + es-errors "^1.3.0"
3898 + get-intrinsic "^1.2.6"
3899 + is-array-buffer "^3.0.4"
3900 +
3901 ast-types@^0.13.4:
3902 version "0.13.4"
3903 resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz"
@@ -3815,6 +3905,11 @@ ast-types@^0.13.4:
3905 dependencies:
3906 tslib "^2.0.1"
3907
3908 +async-function@^1.0.0:
3909 + version "1.0.0"
3910 + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b"
3911 + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==
3912 +
3913 async@^3.2.3:
3914 version "3.2.6"
3915 resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz"
@@ -3825,6 +3920,13 @@ asynckit@^0.4.0:
3920 resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
3921 integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
3922
3923 +available-typed-arrays@^1.0.7:
3924 + version "1.0.7"
3925 + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
3926 + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==
3927 + dependencies:
3928 + possible-typed-array-names "^1.0.0"
3929 +
3930 axios@^1.6.1:
3931 version "1.7.4"
3932 resolved "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz"
@@ -4245,7 +4347,7 @@ cac@^6.7.14:
4347 resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz"
4348 integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
4349
4248 -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
4350 +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
4351 version "1.0.2"
4352 resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
4353 integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
@@ -4253,7 +4355,17 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
4355 es-errors "^1.3.0"
4356 function-bind "^1.1.2"
4357
4256 -call-bound@^1.0.2:
4358 +call-bind@^1.0.7, call-bind@^1.0.8:
4359 + version "1.0.8"
4360 + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
4361 + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
4362 + dependencies:
4363 + call-bind-apply-helpers "^1.0.0"
4364 + es-define-property "^1.0.0"
4365 + get-intrinsic "^1.2.4"
4366 + set-function-length "^1.2.2"
4367 +
4368 +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4:
4369 version "1.0.4"
4370 resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz"
4371 integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
@@ -4295,7 +4407,7 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.2:
4407 escape-string-regexp "^1.0.5"
4408 supports-color "^5.3.0"
4409
4298 -chalk@4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0:
4410 +chalk@4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
4411 version "4.1.2"
4412 resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
4413 integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -4377,6 +4489,11 @@ ci-info@^3.2.0:
4489 resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz"
4490 integrity sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==
4491
4492 +ci-info@^4.2.0:
4493 + version "4.3.0"
4494 + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.0.tgz#c39b1013f8fdbd28cd78e62318357d02da160cd7"
4495 + integrity sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==
4496 +
4497 cjs-module-lexer@^1.0.0:
4498 version "1.2.2"
4499 resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
@@ -4722,6 +4839,33 @@ data-urls@^4.0.0:
4839 whatwg-mimetype "^3.0.0"
4840 whatwg-url "^12.0.0"
4841
4842 +data-view-buffer@^1.0.2:
4843 + version "1.0.2"
4844 + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570"
4845 + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==
4846 + dependencies:
4847 + call-bound "^1.0.3"
4848 + es-errors "^1.3.0"
4849 + is-data-view "^1.0.2"
4850 +
4851 +data-view-byte-length@^1.0.2:
4852 + version "1.0.2"
4853 + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735"
4854 + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==
4855 + dependencies:
4856 + call-bound "^1.0.3"
4857 + es-errors "^1.3.0"
4858 + is-data-view "^1.0.2"
4859 +
4860 +data-view-byte-offset@^1.0.1:
4861 + version "1.0.1"
4862 + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191"
4863 + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==
4864 + dependencies:
4865 + call-bound "^1.0.2"
4866 + es-errors "^1.3.0"
4867 + is-data-view "^1.0.1"
4868 +
4869 date-fns@^2.29.1:
4870 version "2.30.0"
4871 resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz"
@@ -4788,6 +4932,24 @@ defaults@^1.0.3:
4932 dependencies:
4933 clone "^1.0.2"
4934
4935 +define-data-property@^1.0.1, define-data-property@^1.1.4:
4936 + version "1.1.4"
4937 + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
4938 + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
4939 + dependencies:
4940 + es-define-property "^1.0.0"
4941 + es-errors "^1.3.0"
4942 + gopd "^1.0.1"
4943 +
4944 +define-properties@^1.2.1:
4945 + version "1.2.1"
4946 + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
4947 + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
4948 + dependencies:
4949 + define-data-property "^1.0.1"
4950 + has-property-descriptors "^1.0.0"
4951 + object-keys "^1.1.1"
4952 +
4953 degenerator@^5.0.0:
4954 version "5.0.1"
4955 resolved "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz"
@@ -4922,7 +5084,7 @@ dreamopt@~0.6.0:
5084 dependencies:
5085 wordwrap ">=0.0.2"
5086
4925 -dunder-proto@^1.0.1:
5087 +dunder-proto@^1.0.0, dunder-proto@^1.0.1:
5088 version "1.0.1"
5089 resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz"
5090 integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
@@ -5033,7 +5195,67 @@ error-ex@^1.3.1:
5195 dependencies:
5196 is-arrayish "^0.2.1"
5197
5036 -es-define-property@^1.0.1:
5198 +es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.9:
5199 + version "1.24.0"
5200 + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328"
5201 + integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==
5202 + dependencies:
5203 + array-buffer-byte-length "^1.0.2"
5204 + arraybuffer.prototype.slice "^1.0.4"
5205 + available-typed-arrays "^1.0.7"
5206 + call-bind "^1.0.8"
5207 + call-bound "^1.0.4"
5208 + data-view-buffer "^1.0.2"
5209 + data-view-byte-length "^1.0.2"
5210 + data-view-byte-offset "^1.0.1"
5211 + es-define-property "^1.0.1"
5212 + es-errors "^1.3.0"
5213 + es-object-atoms "^1.1.1"
5214 + es-set-tostringtag "^2.1.0"
5215 + es-to-primitive "^1.3.0"
5216 + function.prototype.name "^1.1.8"
5217 + get-intrinsic "^1.3.0"
5218 + get-proto "^1.0.1"
5219 + get-symbol-description "^1.1.0"
5220 + globalthis "^1.0.4"
5221 + gopd "^1.2.0"
5222 + has-property-descriptors "^1.0.2"
5223 + has-proto "^1.2.0"
5224 + has-symbols "^1.1.0"
5225 + hasown "^2.0.2"
5226 + internal-slot "^1.1.0"
5227 + is-array-buffer "^3.0.5"
5228 + is-callable "^1.2.7"
5229 + is-data-view "^1.0.2"
5230 + is-negative-zero "^2.0.3"
5231 + is-regex "^1.2.1"
5232 + is-set "^2.0.3"
5233 + is-shared-array-buffer "^1.0.4"
5234 + is-string "^1.1.1"
5235 + is-typed-array "^1.1.15"
5236 + is-weakref "^1.1.1"
5237 + math-intrinsics "^1.1.0"
5238 + object-inspect "^1.13.4"
5239 + object-keys "^1.1.1"
5240 + object.assign "^4.1.7"
5241 + own-keys "^1.0.1"
5242 + regexp.prototype.flags "^1.5.4"
5243 + safe-array-concat "^1.1.3"
5244 + safe-push-apply "^1.0.0"
5245 + safe-regex-test "^1.1.0"
5246 + set-proto "^1.0.0"
5247 + stop-iteration-iterator "^1.1.0"
5248 + string.prototype.trim "^1.2.10"
5249 + string.prototype.trimend "^1.0.9"
5250 + string.prototype.trimstart "^1.0.8"
5251 + typed-array-buffer "^1.0.3"
5252 + typed-array-byte-length "^1.0.3"
5253 + typed-array-byte-offset "^1.0.4"
5254 + typed-array-length "^1.0.7"
5255 + unbox-primitive "^1.1.0"
5256 + which-typed-array "^1.1.19"
5257 +
5258 +es-define-property@^1.0.0, es-define-property@^1.0.1:
5259 version "1.0.1"
5260 resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz"
5261 integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
@@ -5050,6 +5272,25 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
5272 dependencies:
5273 es-errors "^1.3.0"
5274
5275 +es-set-tostringtag@^2.1.0:
5276 + version "2.1.0"
5277 + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
5278 + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
5279 + dependencies:
5280 + es-errors "^1.3.0"
5281 + get-intrinsic "^1.2.6"
5282 + has-tostringtag "^1.0.2"
5283 + hasown "^2.0.2"
5284 +
5285 +es-to-primitive@^1.3.0:
5286 + version "1.3.0"
5287 + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18"
5288 + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==
5289 + dependencies:
5290 + is-callable "^1.2.7"
5291 + is-date-object "^1.0.5"
5292 + is-symbol "^1.0.4"
5293 +
5294 es5-ext@0.8.x:
5295 version "0.8.2"
5296 resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.8.2.tgz"
@@ -5428,6 +5669,18 @@ expect@^29.7.0:
5669 jest-message-util "^29.7.0"
5670 jest-util "^29.7.0"
5671
5672 +expect@^30.0.0:
5673 + version "30.0.5"
5674 + resolved "https://registry.yarnpkg.com/expect/-/expect-30.0.5.tgz#c23bf193c5e422a742bfd2990ad990811de41a5a"
5675 + integrity sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==
5676 + dependencies:
5677 + "@jest/expect-utils" "30.0.5"
5678 + "@jest/get-type" "30.0.1"
5679 + jest-matcher-utils "30.0.5"
5680 + jest-message-util "30.0.5"
5681 + jest-mock "30.0.5"
5682 + jest-util "30.0.5"
5683 +
5684 express-rate-limit@^7.5.0:
5685 version "7.5.0"
5686 resolved "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz"
@@ -5719,6 +5972,13 @@ follow-redirects@^1.15.6:
5972 resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz"
5973 integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
5974
5975 +for-each@^0.3.3, for-each@^0.3.5:
5976 + version "0.3.5"
5977 + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47"
5978 + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==
5979 + dependencies:
5980 + is-callable "^1.2.7"
5981 +
5982 foreground-child@^3.1.0:
5983 version "3.1.1"
5984 resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz"
@@ -5788,6 +6048,23 @@ function-bind@^1.1.2:
6048 resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
6049 integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
6050
6051 +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8:
6052 + version "1.1.8"
6053 + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78"
6054 + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==
6055 + dependencies:
6056 + call-bind "^1.0.8"
6057 + call-bound "^1.0.3"
6058 + define-properties "^1.2.1"
6059 + functions-have-names "^1.2.3"
6060 + hasown "^2.0.2"
6061 + is-callable "^1.2.7"
6062 +
6063 +functions-have-names@^1.2.3:
6064 + version "1.2.3"
6065 + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
6066 + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
6067 +
6068 gensync@^1.0.0-beta.2:
6069 version "1.0.0-beta.2"
6070 resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz"
@@ -5798,7 +6075,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
6075 resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
6076 integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
6077
5801 -get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
6078 +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0:
6079 version "1.3.0"
6080 resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
6081 integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
@@ -5819,7 +6096,7 @@ get-package-type@^0.1.0:
6096 resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz"
6097 integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
6098
5822 -get-proto@^1.0.1:
6099 +get-proto@^1.0.0, get-proto@^1.0.1:
6100 version "1.0.1"
6101 resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz"
6102 integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
@@ -5839,6 +6116,15 @@ get-stream@^6.0.0:
6116 resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz"
6117 integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
6118
6119 +get-symbol-description@^1.1.0:
6120 + version "1.1.0"
6121 + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee"
6122 + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==
6123 + dependencies:
6124 + call-bound "^1.0.3"
6125 + es-errors "^1.3.0"
6126 + get-intrinsic "^1.2.6"
6127 +
6128 get-uri@^6.0.1:
6129 version "6.0.4"
6130 resolved "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz"
@@ -5957,6 +6243,14 @@ globals@^14.0.0:
6243 resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
6244 integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
6245
6246 +globalthis@^1.0.4:
6247 + version "1.0.4"
6248 + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236"
6249 + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==
6250 + dependencies:
6251 + define-properties "^1.2.1"
6252 + gopd "^1.0.1"
6253 +
6254 globby@^11.1.0:
6255 version "11.1.0"
6256 resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz"
@@ -5969,12 +6263,12 @@ globby@^11.1.0:
6263 merge2 "^1.4.1"
6264 slash "^3.0.0"
6265
5972 -gopd@^1.2.0:
6266 +gopd@^1.0.1, gopd@^1.2.0:
6267 version "1.2.0"
6268 resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
6269 integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
6270
5977 -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4:
6271 +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.11, graceful-fs@^4.2.4:
6272 version "4.2.11"
6273 resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
6274 integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -5989,6 +6283,11 @@ graphemer@^1.4.0:
6283 resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
6284 integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
6285
6286 +has-bigints@^1.0.2:
6287 + version "1.1.0"
6288 + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe"
6289 + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==
6290 +
6291 has-flag@^3.0.0:
6292 version "3.0.0"
6293 resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
@@ -5999,11 +6298,32 @@ has-flag@^4.0.0:
6298 resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
6299 integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
6300
6002 -has-symbols@^1.1.0:
6301 +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
6302 + version "1.0.2"
6303 + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
6304 + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
6305 + dependencies:
6306 + es-define-property "^1.0.0"
6307 +
6308 +has-proto@^1.2.0:
6309 + version "1.2.0"
6310 + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5"
6311 + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==
6312 + dependencies:
6313 + dunder-proto "^1.0.0"
6314 +
6315 +has-symbols@^1.0.3, has-symbols@^1.1.0:
6316 version "1.1.0"
6317 resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz"
6318 integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
6319
6320 +has-tostringtag@^1.0.2:
6321 + version "1.0.2"
6322 + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
6323 + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
6324 + dependencies:
6325 + has-symbols "^1.0.3"
6326 +
6327 has@^1.0.3:
6328 version "1.0.3"
6329 resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
@@ -6231,6 +6551,15 @@ ini@^1.3.4:
6551 resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
6552 integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
6553
6554 +internal-slot@^1.1.0:
6555 + version "1.1.0"
6556 + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961"
6557 + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==
6558 + dependencies:
6559 + es-errors "^1.3.0"
6560 + hasown "^2.0.2"
6561 + side-channel "^1.1.0"
6562 +
6563 invariant@^2.2.4:
6564 version "2.2.4"
6565 resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz"
@@ -6251,6 +6580,15 @@ ipaddr.js@1.9.1:
6580 resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
6581 integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
6582
6583 +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5:
6584 + version "3.0.5"
6585 + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280"
6586 + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==
6587 + dependencies:
6588 + call-bind "^1.0.8"
6589 + call-bound "^1.0.3"
6590 + get-intrinsic "^1.2.6"
6591 +
6592 is-arrayish@^0.2.1:
6593 version "0.2.1"
6594 resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz"
@@ -6261,6 +6599,24 @@ is-arrayish@^0.3.1:
6599 resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz"
6600 integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
6601
6602 +is-async-function@^2.0.0:
6603 + version "2.1.1"
6604 + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523"
6605 + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==
6606 + dependencies:
6607 + async-function "^1.0.0"
6608 + call-bound "^1.0.3"
6609 + get-proto "^1.0.1"
6610 + has-tostringtag "^1.0.2"
6611 + safe-regex-test "^1.1.0"
6612 +
6613 +is-bigint@^1.1.0:
6614 + version "1.1.0"
6615 + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672"
6616 + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==
6617 + dependencies:
6618 + has-bigints "^1.0.2"
6619 +
6620 is-binary-path@~2.1.0:
6621 version "2.1.0"
6622 resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
@@ -6268,6 +6624,19 @@ is-binary-path@~2.1.0:
6624 dependencies:
6625 binary-extensions "^2.0.0"
6626
6627 +is-boolean-object@^1.2.1:
6628 + version "1.2.2"
6629 + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e"
6630 + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==
6631 + dependencies:
6632 + call-bound "^1.0.3"
6633 + has-tostringtag "^1.0.2"
6634 +
6635 +is-callable@^1.2.7:
6636 + version "1.2.7"
6637 + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
6638 + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
6639 +
6640 is-core-module@^2.9.0:
6641 version "2.10.0"
6642 resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz"
@@ -6275,11 +6644,35 @@ is-core-module@^2.9.0:
6644 dependencies:
6645 has "^1.0.3"
6646
6647 +is-data-view@^1.0.1, is-data-view@^1.0.2:
6648 + version "1.0.2"
6649 + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e"
6650 + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==
6651 + dependencies:
6652 + call-bound "^1.0.2"
6653 + get-intrinsic "^1.2.6"
6654 + is-typed-array "^1.1.13"
6655 +
6656 +is-date-object@^1.0.5, is-date-object@^1.1.0:
6657 + version "1.1.0"
6658 + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7"
6659 + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==
6660 + dependencies:
6661 + call-bound "^1.0.2"
6662 + has-tostringtag "^1.0.2"
6663 +
6664 is-extglob@^2.1.1:
6665 version "2.1.1"
6666 resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
6667 integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
6668
6669 +is-finalizationregistry@^1.1.0:
6670 + version "1.1.1"
6671 + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90"
6672 + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==
6673 + dependencies:
6674 + call-bound "^1.0.3"
6675 +
6676 is-fullwidth-code-point@^3.0.0:
6677 version "3.0.0"
6678 resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
@@ -6290,6 +6683,16 @@ is-generator-fn@^2.0.0:
6683 resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz"
6684 integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
6685
6686 +is-generator-function@^1.0.10:
6687 + version "1.1.0"
6688 + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca"
6689 + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==
6690 + dependencies:
6691 + call-bound "^1.0.3"
6692 + get-proto "^1.0.0"
6693 + has-tostringtag "^1.0.2"
6694 + safe-regex-test "^1.1.0"
6695 +
6696 is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
6697 version "4.0.3"
6698 resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
@@ -6307,6 +6710,24 @@ is-interactive@^2.0.0:
6710 resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz"
6711 integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==
6712
6713 +is-map@^2.0.3:
6714 + version "2.0.3"
6715 + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
6716 + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
6717 +
6718 +is-negative-zero@^2.0.3:
6719 + version "2.0.3"
6720 + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747"
6721 + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==
6722 +
6723 +is-number-object@^1.1.1:
6724 + version "1.1.1"
6725 + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541"
6726 + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==
6727 + dependencies:
6728 + call-bound "^1.0.3"
6729 + has-tostringtag "^1.0.2"
6730 +
6731 is-number@^7.0.0:
6732 version "7.0.0"
6733 resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
@@ -6339,11 +6760,57 @@ is-promise@^4.0.0:
6760 resolved "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz"
6761 integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==
6762
6763 +is-regex@^1.2.1:
6764 + version "1.2.1"
6765 + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22"
6766 + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==
6767 + dependencies:
6768 + call-bound "^1.0.2"
6769 + gopd "^1.2.0"
6770 + has-tostringtag "^1.0.2"
6771 + hasown "^2.0.2"
6772 +
6773 +is-set@^2.0.3:
6774 + version "2.0.3"
6775 + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d"
6776 + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==
6777 +
6778 +is-shared-array-buffer@^1.0.4:
6779 + version "1.0.4"
6780 + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f"
6781 + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==
6782 + dependencies:
6783 + call-bound "^1.0.3"
6784 +
6785 is-stream@^2.0.0:
6786 version "2.0.1"
6787 resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
6788 integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
6789
6790 +is-string@^1.1.1:
6791 + version "1.1.1"
6792 + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9"
6793 + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==
6794 + dependencies:
6795 + call-bound "^1.0.3"
6796 + has-tostringtag "^1.0.2"
6797 +
6798 +is-symbol@^1.0.4, is-symbol@^1.1.1:
6799 + version "1.1.1"
6800 + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634"
6801 + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==
6802 + dependencies:
6803 + call-bound "^1.0.2"
6804 + has-symbols "^1.1.0"
6805 + safe-regex-test "^1.1.0"
6806 +
6807 +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15:
6808 + version "1.1.15"
6809 + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b"
6810 + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==
6811 + dependencies:
6812 + which-typed-array "^1.1.16"
6813 +
6814 is-unicode-supported@^0.1.0:
6815 version "0.1.0"
6816 resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz"
@@ -6354,11 +6821,36 @@ is-unicode-supported@^1.1.0, is-unicode-supported@^1.3.0:
6821 resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz"
6822 integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==
6823
6824 +is-weakmap@^2.0.2:
6825 + version "2.0.2"
6826 + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd"
6827 + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==
6828 +
6829 +is-weakref@^1.0.2, is-weakref@^1.1.1:
6830 + version "1.1.1"
6831 + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293"
6832 + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==
6833 + dependencies:
6834 + call-bound "^1.0.3"
6835 +
6836 +is-weakset@^2.0.3:
6837 + version "2.0.4"
6838 + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca"
6839 + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==
6840 + dependencies:
6841 + call-bound "^1.0.3"
6842 + get-intrinsic "^1.2.6"
6843 +
6844 is-windows@^1.0.1:
6845 version "1.0.2"
6846 resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz"
6847 integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
6848
6849 +isarray@^2.0.5:
6850 + version "2.0.5"
6851 + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
6852 + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
6853 +
6854 isarray@~1.0.0:
6855 version "1.0.0"
6856 resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
@@ -6797,6 +7289,16 @@ jest-config@^29.7.0:
7289 slash "^3.0.0"
7290 strip-json-comments "^3.1.1"
7291
7292 +jest-diff@30.0.5:
7293 + version "30.0.5"
7294 + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.0.5.tgz#b40f81e0c0d13e5b81c4d62b0d0dfa6a524ee0fd"
7295 + integrity sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==
7296 + dependencies:
7297 + "@jest/diff-sequences" "30.0.1"
7298 + "@jest/get-type" "30.0.1"
7299 + chalk "^4.1.2"
7300 + pretty-format "30.0.5"
7301 +
7302 jest-diff@^28.1.3:
7303 version "28.1.3"
7304 resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz"
@@ -7106,6 +7608,16 @@ jest-leak-detector@^29.7.0:
7608 jest-get-type "^29.6.3"
7609 pretty-format "^29.7.0"
7610
7611 +jest-matcher-utils@30.0.5:
7612 + version "30.0.5"
7613 + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz#dff3334be58faea4a5e1becc228656fbbfc2467d"
7614 + integrity sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==
7615 + dependencies:
7616 + "@jest/get-type" "30.0.1"
7617 + chalk "^4.1.2"
7618 + jest-diff "30.0.5"
7619 + pretty-format "30.0.5"
7620 +
7621 jest-matcher-utils@^28.1.3:
7622 version "28.1.3"
7623 resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz"
@@ -7146,6 +7658,21 @@ jest-matcher-utils@^29.7.0:
7658 jest-get-type "^29.6.3"
7659 pretty-format "^29.7.0"
7660
7661 +jest-message-util@30.0.5:
7662 + version "30.0.5"
7663 + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.0.5.tgz#dd12ffec91dd3fa6a59cbd538a513d8e239e070c"
7664 + integrity sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==
7665 + dependencies:
7666 + "@babel/code-frame" "^7.27.1"
7667 + "@jest/types" "30.0.5"
7668 + "@types/stack-utils" "^2.0.3"
7669 + chalk "^4.1.2"
7670 + graceful-fs "^4.2.11"
7671 + micromatch "^4.0.8"
7672 + pretty-format "30.0.5"
7673 + slash "^3.0.0"
7674 + stack-utils "^2.0.6"
7675 +
7676 jest-message-util@^28.1.3:
7677 version "28.1.3"
7678 resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz"
@@ -7206,6 +7733,15 @@ jest-message-util@^29.7.0:
7733 slash "^3.0.0"
7734 stack-utils "^2.0.3"
7735
7736 +jest-mock@30.0.5:
7737 + version "30.0.5"
7738 + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.0.5.tgz#ef437e89212560dd395198115550085038570bdd"
7739 + integrity sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==
7740 + dependencies:
7741 + "@jest/types" "30.0.5"
7742 + "@types/node" "*"
7743 + jest-util "30.0.5"
7744 +
7745 jest-mock@^28.1.3:
7746 version "28.1.3"
7747 resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz"
@@ -7237,6 +7773,11 @@ jest-pnp-resolver@^1.2.2:
7773 resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz"
7774 integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
7775
7776 +jest-regex-util@30.0.1:
7777 + version "30.0.1"
7778 + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b"
7779 + integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==
7780 +
7781 jest-regex-util@^28.0.2:
7782 version "28.0.2"
7783 resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz"
@@ -7683,6 +8224,18 @@ jest-snapshot@^29.7.0:
8224 pretty-format "^29.7.0"
8225 semver "^7.5.3"
8226
8227 +jest-util@30.0.5:
8228 + version "30.0.5"
8229 + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.0.5.tgz#035d380c660ad5f1748dff71c4105338e05f8669"
8230 + integrity sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==
8231 + dependencies:
8232 + "@jest/types" "30.0.5"
8233 + "@types/node" "*"
8234 + chalk "^4.1.2"
8235 + ci-info "^4.2.0"
8236 + graceful-fs "^4.2.11"
8237 + picomatch "^4.0.2"
8238 +
8239 jest-util@^28.0.0, jest-util@^28.1.3:
8240 version "28.1.3"
8241 resolved "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz"
@@ -8327,7 +8880,7 @@ merge@^2.1.1:
8880 resolved "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz"
8881 integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==
8882
8330 -micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
8883 +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8:
8884 version "4.0.8"
8885 resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz"
8886 integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
@@ -8620,11 +9173,28 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1:
9173 resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
9174 integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
9175
8623 -object-inspect@^1.13.3:
9176 +object-inspect@^1.13.3, object-inspect@^1.13.4:
9177 version "1.13.4"
9178 resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz"
9179 integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
9180
9181 +object-keys@^1.1.1:
9182 + version "1.1.1"
9183 + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
9184 + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
9185 +
9186 +object.assign@^4.1.7:
9187 + version "4.1.7"
9188 + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d"
9189 + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==
9190 + dependencies:
9191 + call-bind "^1.0.8"
9192 + call-bound "^1.0.3"
9193 + define-properties "^1.2.1"
9194 + es-object-atoms "^1.0.0"
9195 + has-symbols "^1.1.0"
9196 + object-keys "^1.1.1"
9197 +
9198 on-finished@^2.4.1:
9199 version "2.4.1"
9200 resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
@@ -8695,6 +9265,15 @@ ora@^7.0.1:
9265 string-width "^6.1.0"
9266 strip-ansi "^7.1.0"
9267
9268 +own-keys@^1.0.1:
9269 + version "1.0.1"
9270 + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358"
9271 + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==
9272 + dependencies:
9273 + get-intrinsic "^1.2.6"
9274 + object-keys "^1.1.1"
9275 + safe-push-apply "^1.0.0"
9276 +
9277 p-limit@^2.0.0, p-limit@^2.2.0:
9278 version "2.3.0"
9279 resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
@@ -8949,6 +9528,11 @@ pkg-dir@^4.2.0:
9528 dependencies:
9529 find-up "^4.0.0"
9530
9531 +possible-typed-array-names@^1.0.0:
9532 + version "1.1.0"
9533 + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae"
9534 + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==
9535 +
9536 postcss-load-config@^6.0.1:
9537 version "6.0.1"
9538 resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz"
@@ -8975,6 +9559,15 @@ prettier@^3.3.3:
9559 resolved "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz"
9560 integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
9561
9562 +pretty-format@30.0.5, pretty-format@^30.0.0:
9563 + version "30.0.5"
9564 + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.0.5.tgz#e001649d472800396c1209684483e18a4d250360"
9565 + integrity sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==
9566 + dependencies:
9567 + "@jest/schemas" "30.0.5"
9568 + ansi-styles "^5.2.0"
9569 + react-is "^18.3.1"
9570 +
9571 pretty-format@^24:
9572 version "24.9.0"
9573 resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz"
@@ -9202,7 +9795,7 @@ react-is@^17.0.1:
9795 resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
9796 integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
9797
9205 -react-is@^18.0.0:
9798 +react-is@^18.0.0, react-is@^18.3.1:
9799 version "18.3.1"
9800 resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz"
9801 integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
@@ -9251,6 +9844,20 @@ readline@^1.3.0:
9844 resolved "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz"
9845 integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==
9846
9847 +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9:
9848 + version "1.0.10"
9849 + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9"
9850 + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==
9851 + dependencies:
9852 + call-bind "^1.0.8"
9853 + define-properties "^1.2.1"
9854 + es-abstract "^1.23.9"
9855 + es-errors "^1.3.0"
9856 + es-object-atoms "^1.0.0"
9857 + get-intrinsic "^1.2.7"
9858 + get-proto "^1.0.1"
9859 + which-builtin-type "^1.2.1"
9860 +
9861 regenerate-unicode-properties@^10.2.0:
9862 version "10.2.0"
9863 resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz"
@@ -9280,6 +9887,30 @@ regenerator-transform@^0.15.2:
9887 dependencies:
9888 "@babel/runtime" "^7.8.4"
9889
9890 +regexp.escape@^2.0.1:
9891 + version "2.0.1"
9892 + resolved "https://registry.yarnpkg.com/regexp.escape/-/regexp.escape-2.0.1.tgz#09e4beef9d202dbd739868f3818223f977cf91da"
9893 + integrity sha512-JItRb4rmyTzmERBkAf6J87LjDPy/RscIwmaJQ3gsFlAzrmZbZU8LwBw5IydFZXW9hqpgbPlGbMhtpqtuAhMgtg==
9894 + dependencies:
9895 + call-bind "^1.0.7"
9896 + define-properties "^1.2.1"
9897 + es-abstract "^1.23.3"
9898 + es-errors "^1.3.0"
9899 + for-each "^0.3.3"
9900 + safe-regex-test "^1.0.3"
9901 +
9902 +regexp.prototype.flags@^1.5.4:
9903 + version "1.5.4"
9904 + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19"
9905 + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==
9906 + dependencies:
9907 + call-bind "^1.0.8"
9908 + define-properties "^1.2.1"
9909 + es-errors "^1.3.0"
9910 + get-proto "^1.0.1"
9911 + gopd "^1.2.0"
9912 + set-function-name "^2.0.2"
9913 +
9914 regexpu-core@^6.2.0:
9915 version "6.2.0"
9916 resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz"
@@ -9457,6 +10088,17 @@ rxjs@^7.0.0, rxjs@^7.8.1:
10088 dependencies:
10089 tslib "^2.1.0"
10090
10091 +safe-array-concat@^1.1.3:
10092 + version "1.1.3"
10093 + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3"
10094 + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==
10095 + dependencies:
10096 + call-bind "^1.0.8"
10097 + call-bound "^1.0.2"
10098 + get-intrinsic "^1.2.6"
10099 + has-symbols "^1.1.0"
10100 + isarray "^2.0.5"
10101 +
10102 safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
10103 version "5.2.1"
10104 resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
@@ -9467,6 +10109,23 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
10109 resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
10110 integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
10111
10112 +safe-push-apply@^1.0.0:
10113 + version "1.0.0"
10114 + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5"
10115 + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==
10116 + dependencies:
10117 + es-errors "^1.3.0"
10118 + isarray "^2.0.5"
10119 +
10120 +safe-regex-test@^1.0.3, safe-regex-test@^1.1.0:
10121 + version "1.1.0"
10122 + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1"
10123 + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==
10124 + dependencies:
10125 + call-bound "^1.0.2"
10126 + es-errors "^1.3.0"
10127 + is-regex "^1.2.1"
10128 +
10129 safe-stable-stringify@^2.3.1:
10130 version "2.5.0"
10131 resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz"
@@ -9576,6 +10235,37 @@ set-blocking@^2.0.0:
10235 resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz"
10236 integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
10237
10238 +set-function-length@^1.2.2:
10239 + version "1.2.2"
10240 + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
10241 + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
10242 + dependencies:
10243 + define-data-property "^1.1.4"
10244 + es-errors "^1.3.0"
10245 + function-bind "^1.1.2"
10246 + get-intrinsic "^1.2.4"
10247 + gopd "^1.0.1"
10248 + has-property-descriptors "^1.0.2"
10249 +
10250 +set-function-name@^2.0.2:
10251 + version "2.0.2"
10252 + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985"
10253 + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==
10254 + dependencies:
10255 + define-data-property "^1.1.4"
10256 + es-errors "^1.3.0"
10257 + functions-have-names "^1.2.3"
10258 + has-property-descriptors "^1.0.2"
10259 +
10260 +set-proto@^1.0.0:
10261 + version "1.0.0"
10262 + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e"
10263 + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==
10264 + dependencies:
10265 + dunder-proto "^1.0.1"
10266 + es-errors "^1.3.0"
10267 + es-object-atoms "^1.0.0"
10268 +
10269 setimmediate@^1.0.5:
10270 version "1.0.5"
10271 resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz"
@@ -9759,6 +10449,13 @@ stack-utils@^2.0.3:
10449 dependencies:
10450 escape-string-regexp "^2.0.0"
10451
10452 +stack-utils@^2.0.6:
10453 + version "2.0.6"
10454 + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
10455 + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
10456 + dependencies:
10457 + escape-string-regexp "^2.0.0"
10458 +
10459 statuses@2.0.1, statuses@^2.0.1:
10460 version "2.0.1"
10461 resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
@@ -9771,6 +10468,14 @@ stdin-discarder@^0.1.0:
10468 dependencies:
10469 bl "^5.0.0"
10470
10471 +stop-iteration-iterator@^1.1.0:
10472 + version "1.1.0"
10473 + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad"
10474 + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==
10475 + dependencies:
10476 + es-errors "^1.3.0"
10477 + internal-slot "^1.1.0"
10478 +
10479 streamx@^2.15.0, streamx@^2.21.0:
10480 version "2.22.0"
10481 resolved "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz"
@@ -9825,6 +10530,38 @@ string-width@^6.1.0:
10530 emoji-regex "^10.2.1"
10531 strip-ansi "^7.0.1"
10532
10533 +string.prototype.trim@^1.2.10:
10534 + version "1.2.10"
10535 + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81"
10536 + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==
10537 + dependencies:
10538 + call-bind "^1.0.8"
10539 + call-bound "^1.0.2"
10540 + define-data-property "^1.1.4"
10541 + define-properties "^1.2.1"
10542 + es-abstract "^1.23.5"
10543 + es-object-atoms "^1.0.0"
10544 + has-property-descriptors "^1.0.2"
10545 +
10546 +string.prototype.trimend@^1.0.9:
10547 + version "1.0.9"
10548 + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942"
10549 + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==
10550 + dependencies:
10551 + call-bind "^1.0.8"
10552 + call-bound "^1.0.2"
10553 + define-properties "^1.2.1"
10554 + es-object-atoms "^1.0.0"
10555 +
10556 +string.prototype.trimstart@^1.0.8:
10557 + version "1.0.8"
10558 + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde"
10559 + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==
10560 + dependencies:
10561 + call-bind "^1.0.7"
10562 + define-properties "^1.2.1"
10563 + es-object-atoms "^1.0.0"
10564 +
10565 string_decoder@^1.1.1:
10566 version "1.3.0"
10567 resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
@@ -10232,6 +10969,51 @@ type-is@^2.0.0, type-is@^2.0.1:
10969 media-typer "^1.1.0"
10970 mime-types "^3.0.0"
10971
10972 +typed-array-buffer@^1.0.3:
10973 + version "1.0.3"
10974 + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536"
10975 + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==
10976 + dependencies:
10977 + call-bound "^1.0.3"
10978 + es-errors "^1.3.0"
10979 + is-typed-array "^1.1.14"
10980 +
10981 +typed-array-byte-length@^1.0.3:
10982 + version "1.0.3"
10983 + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce"
10984 + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==
10985 + dependencies:
10986 + call-bind "^1.0.8"
10987 + for-each "^0.3.3"
10988 + gopd "^1.2.0"
10989 + has-proto "^1.2.0"
10990 + is-typed-array "^1.1.14"
10991 +
10992 +typed-array-byte-offset@^1.0.4:
10993 + version "1.0.4"
10994 + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355"
10995 + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==
10996 + dependencies:
10997 + available-typed-arrays "^1.0.7"
10998 + call-bind "^1.0.8"
10999 + for-each "^0.3.3"
11000 + gopd "^1.2.0"
11001 + has-proto "^1.2.0"
11002 + is-typed-array "^1.1.15"
11003 + reflect.getprototypeof "^1.0.9"
11004 +
11005 +typed-array-length@^1.0.7:
11006 + version "1.0.7"
11007 + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d"
11008 + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==
11009 + dependencies:
11010 + call-bind "^1.0.7"
11011 + for-each "^0.3.3"
11012 + gopd "^1.0.1"
11013 + is-typed-array "^1.1.13"
11014 + possible-typed-array-names "^1.0.0"
11015 + reflect.getprototypeof "^1.0.6"
11016 +
11017 typed-query-selector@^2.12.0:
11018 version "2.12.0"
11019 resolved "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz"
@@ -10251,6 +11033,16 @@ typescript@^5.4.3:
11033 resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz"
11034 integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==
11035
11036 +unbox-primitive@^1.1.0:
11037 + version "1.1.0"
11038 + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2"
11039 + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==
11040 + dependencies:
11041 + call-bound "^1.0.3"
11042 + has-bigints "^1.0.2"
11043 + has-symbols "^1.1.0"
11044 + which-boxed-primitive "^1.1.1"
11045 +
11046 undici-types@~6.19.2:
11047 version "6.19.8"
11048 resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz"
@@ -10460,11 +11252,64 @@ whatwg-url@^7.0.0:
11252 tr46 "^1.0.1"
11253 webidl-conversions "^4.0.2"
11254
11255 +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1:
11256 + version "1.1.1"
11257 + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e"
11258 + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==
11259 + dependencies:
11260 + is-bigint "^1.1.0"
11261 + is-boolean-object "^1.2.1"
11262 + is-number-object "^1.1.1"
11263 + is-string "^1.1.1"
11264 + is-symbol "^1.1.1"
11265 +
11266 +which-builtin-type@^1.2.1:
11267 + version "1.2.1"
11268 + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e"
11269 + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==
11270 + dependencies:
11271 + call-bound "^1.0.2"
11272 + function.prototype.name "^1.1.6"
11273 + has-tostringtag "^1.0.2"
11274 + is-async-function "^2.0.0"
11275 + is-date-object "^1.1.0"
11276 + is-finalizationregistry "^1.1.0"
11277 + is-generator-function "^1.0.10"
11278 + is-regex "^1.2.1"
11279 + is-weakref "^1.0.2"
11280 + isarray "^2.0.5"
11281 + which-boxed-primitive "^1.1.0"
11282 + which-collection "^1.0.2"
11283 + which-typed-array "^1.1.16"
11284 +
11285 +which-collection@^1.0.2:
11286 + version "1.0.2"
11287 + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0"
11288 + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==
11289 + dependencies:
11290 + is-map "^2.0.3"
11291 + is-set "^2.0.3"
11292 + is-weakmap "^2.0.2"
11293 + is-weakset "^2.0.3"
11294 +
11295 which-module@^2.0.0:
11296 version "2.0.0"
11297 resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz"
11298 integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==
11299
11300 +which-typed-array@^1.1.16, which-typed-array@^1.1.19:
11301 + version "1.1.19"
11302 + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956"
11303 + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==
11304 + dependencies:
11305 + available-typed-arrays "^1.0.7"
11306 + call-bind "^1.0.8"
11307 + call-bound "^1.0.4"
11308 + for-each "^0.3.5"
11309 + get-proto "^1.0.1"
11310 + gopd "^1.2.0"
11311 + has-tostringtag "^1.0.2"
11312 +
11313 which@^1.2.10, which@^1.2.14:
11314 version "1.3.1"
11315 resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"
fixtures/eslint-v9/eslint.config.ts
+10 -7
@@ -1,7 +1,9 @@
1 -import type {Linter} from 'eslint';
2 -import * as reactHooks from 'eslint-plugin-react-hooks';
1 +import {defineConfig} from 'eslint/config';
2 +import reactHooks from 'eslint-plugin-react-hooks';
3
4 -export default [
4 +console.log(reactHooks.configs['recommended-latest']);
5 +
6 +export default defineConfig([
7 {
8 languageOptions: {
9 ecmaVersion: 'latest',
@@ -12,11 +14,12 @@ export default [
14 },
15 },
16 },
15 - },
16 - reactHooks.configs['recommended'],
17 - {
17 + plugins: {
18 + 'react-hooks': reactHooks,
19 + },
20 + extends: ['react-hooks/recommended-latest'],
21 rules: {
22 'react-hooks/exhaustive-deps': 'error',
23 },
24 },
22 -] satisfies Linter.Config[];
25 +]);
fixtures/eslint-v9/package.json
+1 -1
@@ -2,7 +2,7 @@
2 "private": true,
3 "name": "eslint-v9",
4 "dependencies": {
5 - "eslint": "^9.18.0",
5 + "eslint": "^9.33.0",
6 "eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks",
7 "jiti": "^2.4.2"
8 },
fixtures/eslint-v9/yarn.lock
+63 -38
@@ -221,26 +221,31 @@
221 resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
222 integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
223
224 -"@eslint/config-array@^0.19.2":
225 - version "0.19.2"
226 - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa"
227 - integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==
224 +"@eslint/config-array@^0.21.0":
225 + version "0.21.0"
226 + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636"
227 + integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==
228 dependencies:
229 "@eslint/object-schema" "^2.1.6"
230 debug "^4.3.1"
231 minimatch "^3.1.2"
232
233 -"@eslint/core@^0.12.0":
234 - version "0.12.0"
235 - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e"
236 - integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==
233 +"@eslint/config-helpers@^0.3.1":
234 + version "0.3.1"
235 + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.1.tgz#d316e47905bd0a1a931fa50e669b9af4104d1617"
236 + integrity sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==
237 +
238 +"@eslint/core@^0.15.2":
239 + version "0.15.2"
240 + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.2.tgz#59386327d7862cc3603ebc7c78159d2dcc4a868f"
241 + integrity sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==
242 dependencies:
243 "@types/json-schema" "^7.0.15"
244
240 -"@eslint/eslintrc@^3.3.0":
241 - version "3.3.0"
242 - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.0.tgz#96a558f45842989cca7ea1ecd785ad5491193846"
243 - integrity sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==
245 +"@eslint/eslintrc@^3.3.1":
246 + version "3.3.1"
247 + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964"
248 + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==
249 dependencies:
250 ajv "^6.12.4"
251 debug "^4.3.2"
@@ -252,22 +257,22 @@
257 minimatch "^3.1.2"
258 strip-json-comments "^3.1.1"
259
255 -"@eslint/js@9.21.0":
256 - version "9.21.0"
257 - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.21.0.tgz#4303ef4e07226d87c395b8fad5278763e9c15c08"
258 - integrity sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==
260 +"@eslint/js@9.33.0":
261 + version "9.33.0"
262 + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.33.0.tgz#475c92fdddab59b8b8cab960e3de2564a44bf368"
263 + integrity sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==
264
265 "@eslint/object-schema@^2.1.6":
266 version "2.1.6"
267 resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f"
268 integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
269
265 -"@eslint/plugin-kit@^0.2.7":
266 - version "0.2.7"
267 - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz#9901d52c136fb8f375906a73dcc382646c3b6a27"
268 - integrity sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==
270 +"@eslint/plugin-kit@^0.3.5":
271 + version "0.3.5"
272 + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz#fd8764f0ee79c8ddab4da65460c641cefee017c5"
273 + integrity sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==
274 dependencies:
270 - "@eslint/core" "^0.12.0"
275 + "@eslint/core" "^0.15.2"
276 levn "^0.4.1"
277
278 "@humanfs/core@^0.19.1":
@@ -350,6 +355,11 @@ acorn@^8.14.0:
355 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
356 integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
357
358 +acorn@^8.15.0:
359 + version "8.15.0"
360 + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
361 + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
362 +
363 ajv@^6.12.4:
364 version "6.12.6"
365 resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -475,10 +485,10 @@ escape-string-regexp@^4.0.0:
485 version "0.0.0"
486 uid ""
487
478 -eslint-scope@^8.2.0:
479 - version "8.2.0"
480 - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442"
481 - integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==
488 +eslint-scope@^8.4.0:
489 + version "8.4.0"
490 + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82"
491 + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==
492 dependencies:
493 esrecurse "^4.3.0"
494 estraverse "^5.2.0"
@@ -493,18 +503,24 @@ eslint-visitor-keys@^4.2.0:
503 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
504 integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
505
496 -eslint@^9.18.0:
497 - version "9.21.0"
498 - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.21.0.tgz#b1c9c16f5153ff219791f627b94ab8f11f811591"
499 - integrity sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==
506 +eslint-visitor-keys@^4.2.1:
507 + version "4.2.1"
508 + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1"
509 + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
510 +
511 +eslint@^9.33.0:
512 + version "9.33.0"
513 + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.33.0.tgz#cc186b3d9eb0e914539953d6a178a5b413997b73"
514 + integrity sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==
515 dependencies:
516 "@eslint-community/eslint-utils" "^4.2.0"
517 "@eslint-community/regexpp" "^4.12.1"
503 - "@eslint/config-array" "^0.19.2"
504 - "@eslint/core" "^0.12.0"
505 - "@eslint/eslintrc" "^3.3.0"
506 - "@eslint/js" "9.21.0"
507 - "@eslint/plugin-kit" "^0.2.7"
518 + "@eslint/config-array" "^0.21.0"
519 + "@eslint/config-helpers" "^0.3.1"
520 + "@eslint/core" "^0.15.2"
521 + "@eslint/eslintrc" "^3.3.1"
522 + "@eslint/js" "9.33.0"
523 + "@eslint/plugin-kit" "^0.3.5"
524 "@humanfs/node" "^0.16.6"
525 "@humanwhocodes/module-importer" "^1.0.1"
526 "@humanwhocodes/retry" "^0.4.2"
@@ -515,9 +531,9 @@ eslint@^9.18.0:
531 cross-spawn "^7.0.6"
532 debug "^4.3.2"
533 escape-string-regexp "^4.0.0"
518 - eslint-scope "^8.2.0"
519 - eslint-visitor-keys "^4.2.0"
520 - espree "^10.3.0"
534 + eslint-scope "^8.4.0"
535 + eslint-visitor-keys "^4.2.1"
536 + espree "^10.4.0"
537 esquery "^1.5.0"
538 esutils "^2.0.2"
539 fast-deep-equal "^3.1.3"
@@ -533,7 +549,7 @@ eslint@^9.18.0:
549 natural-compare "^1.4.0"
550 optionator "^0.9.3"
551
536 -espree@^10.0.1, espree@^10.3.0:
552 +espree@^10.0.1:
553 version "10.3.0"
554 resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a"
555 integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==
@@ -542,6 +558,15 @@ espree@^10.0.1, espree@^10.3.0:
558 acorn-jsx "^5.3.2"
559 eslint-visitor-keys "^4.2.0"
560
561 +espree@^10.4.0:
562 + version "10.4.0"
563 + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837"
564 + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==
565 + dependencies:
566 + acorn "^8.15.0"
567 + acorn-jsx "^5.3.2"
568 + eslint-visitor-keys "^4.2.1"
569 +
570 esquery@^1.5.0:
571 version "1.6.0"
572 resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
package.json
+1
@@ -29,6 +29,7 @@
29 "@babel/plugin-transform-modules-commonjs": "^7.10.4",
30 "@babel/plugin-transform-object-super": "^7.10.4",
31 "@babel/plugin-transform-parameters": "^7.10.5",
32 + "@babel/plugin-transform-private-methods": "^7.10.4",
33 "@babel/plugin-transform-react-jsx": "^7.23.4",
34 "@babel/plugin-transform-react-jsx-development": "^7.22.5",
35 "@babel/plugin-transform-react-jsx-source": "^7.10.5",
packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js
+2 -1
@@ -12,7 +12,8 @@
12 const ESLintTesterV7 = require('eslint-v7').RuleTester;
13 const ESLintTesterV9 = require('eslint-v9').RuleTester;
14 const ReactHooksESLintPlugin = require('eslint-plugin-react-hooks');
15 -const ReactHooksESLintRule = ReactHooksESLintPlugin.rules['exhaustive-deps'];
15 +const ReactHooksESLintRule =
16 + ReactHooksESLintPlugin.default.rules['exhaustive-deps'];
17
18 /**
19 * A string template tag that removes padding from the left side of multi-line strings
packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js
+2 -1
@@ -12,7 +12,8 @@
12 const ESLintTesterV7 = require('eslint-v7').RuleTester;
13 const ESLintTesterV9 = require('eslint-v9').RuleTester;
14 const ReactHooksESLintPlugin = require('eslint-plugin-react-hooks');
15 -const ReactHooksESLintRule = ReactHooksESLintPlugin.rules['rules-of-hooks'];
15 +const ReactHooksESLintRule =
16 + ReactHooksESLintPlugin.default.rules['rules-of-hooks'];
17
18 /**
19 * A string template tag that removes padding from the left side of multi-line strings
packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts deleted
-289
@@ -1,289 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -import {ErrorSeverity} from 'babel-plugin-react-compiler';
9 -import {RuleTester as ESLintTester} from 'eslint';
10 -import ReactCompilerRule from '../src/rules/ReactCompiler';
11 -
12 -const ESLintTesterV8 = require('eslint-v8').RuleTester;
13 -
14 -/**
15 - * A string template tag that removes padding from the left side of multi-line strings
16 - * @param {Array} strings array of code strings (only one expected)
17 - */
18 -function normalizeIndent(strings: TemplateStringsArray): string {
19 - const codeLines = strings[0]?.split('\n') ?? [];
20 - const leftPadding = codeLines[1]?.match(/\s+/)![0] ?? '';
21 - return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
22 -}
23 -
24 -type CompilerTestCases = {
25 - valid: ESLintTester.ValidTestCase[];
26 - invalid: ESLintTester.InvalidTestCase[];
27 -};
28 -
29 -const tests: CompilerTestCases = {
30 - valid: [
31 - {
32 - name: 'Basic example',
33 - code: normalizeIndent`
34 - function foo(x, y) {
35 - if (x) {
36 - return foo(false, y);
37 - }
38 - return [y * 10];
39 - }
40 - `,
41 - },
42 - {
43 - name: 'Violation with Flow suppression',
44 - code: `
45 - // Valid since error already suppressed with flow.
46 - function useHookWithHook() {
47 - if (cond) {
48 - // $FlowFixMe[react-rule-hook]
49 - useConditionalHook();
50 - }
51 - }
52 - `,
53 - },
54 - {
55 - name: 'Basic example with component syntax',
56 - code: normalizeIndent`
57 - export default component HelloWorld(
58 - text: string = 'Hello!',
59 - onClick: () => void,
60 - ) {
61 - return <div onClick={onClick}>{text}</div>;
62 - }
63 - `,
64 - },
65 - {
66 - name: 'Unsupported syntax',
67 - code: normalizeIndent`
68 - function foo(x) {
69 - var y = 1;
70 - return y * x;
71 - }
72 - `,
73 - },
74 - {
75 - // OK because invariants are only meant for the compiler team's consumption
76 - name: '[Invariant] Defined after use',
77 - code: normalizeIndent`
78 - function Component(props) {
79 - let y = function () {
80 - m(x);
81 - };
82 -
83 - let x = { a };
84 - m(x);
85 - return y;
86 - }
87 - `,
88 - },
89 - {
90 - name: "Classes don't throw",
91 - code: normalizeIndent`
92 - class Foo {
93 - #bar() {}
94 - }
95 - `,
96 - },
97 - ],
98 - invalid: [
99 - {
100 - name: 'Reportable levels can be configured',
101 - options: [{reportableLevels: new Set([ErrorSeverity.Todo])}],
102 - code: normalizeIndent`
103 - function Foo(x) {
104 - var y = 1;
105 - return <div>{y * x}</div>;
106 - }`,
107 - errors: [
108 - {
109 - message: /Handle var kinds in VariableDeclaration/,
110 - },
111 - ],
112 - },
113 - {
114 - name: '[InvalidReact] ESlint suppression',
115 - // Indentation is intentionally weird so it doesn't add extra whitespace
116 - code: normalizeIndent`
117 - function Component(props) {
118 - // eslint-disable-next-line react-hooks/rules-of-hooks
119 - return <div>{props.foo}</div>;
120 - }`,
121 - errors: [
122 - {
123 - message: /React Compiler has skipped optimizing this component/,
124 - suggestions: [
125 - {
126 - output: normalizeIndent`
127 - function Component(props) {
128 -
129 - return <div>{props.foo}</div>;
130 - }`,
131 - },
132 - ],
133 - },
134 - {
135 - message:
136 - "Definition for rule 'react-hooks/rules-of-hooks' was not found.",
137 - },
138 - ],
139 - },
140 - {
141 - name: 'Multiple diagnostics are surfaced',
142 - options: [
143 - {
144 - reportableLevels: new Set([
145 - ErrorSeverity.Todo,
146 - ErrorSeverity.InvalidReact,
147 - ]),
148 - },
149 - ],
150 - code: normalizeIndent`
151 - function Foo(x) {
152 - var y = 1;
153 - return <div>{y * x}</div>;
154 - }
155 - function Bar(props) {
156 - props.a.b = 2;
157 - return <div>{props.c}</div>
158 - }`,
159 - errors: [
160 - {
161 - message: /Handle var kinds in VariableDeclaration/,
162 - },
163 - {
164 - message: /Modifying component props or hook arguments is not allowed/,
165 - },
166 - ],
167 - },
168 - {
169 - name: 'Test experimental/unstable report all bailouts mode',
170 - options: [
171 - {
172 - reportableLevels: new Set([ErrorSeverity.InvalidReact]),
173 - __unstable_donotuse_reportAllBailouts: true,
174 - },
175 - ],
176 - code: normalizeIndent`
177 - function Foo(x) {
178 - var y = 1;
179 - return <div>{y * x}</div>;
180 - }`,
181 - errors: [
182 - {
183 - message: /Handle var kinds in VariableDeclaration/,
184 - },
185 - ],
186 - },
187 - {
188 - name: "'use no forget' does not disable eslint rule",
189 - code: normalizeIndent`
190 - let count = 0;
191 - function Component() {
192 - 'use no forget';
193 - count = count + 1;
194 - return <div>Hello world {count}</div>
195 - }
196 - `,
197 - errors: [
198 - {
199 - message:
200 - /Cannot reassign variables declared outside of the component\/hook/,
201 - },
202 - ],
203 - },
204 - {
205 - name: "Unused 'use no forget' directive is reported when no errors are present on components",
206 - code: normalizeIndent`
207 - function Component() {
208 - 'use no forget';
209 - return <div>Hello world</div>
210 - }
211 - `,
212 - errors: [
213 - {
214 - message: "Unused 'use no forget' directive",
215 - suggestions: [
216 - {
217 - output:
218 - // yuck
219 - '\nfunction Component() {\n \n return <div>Hello world</div>\n}\n',
220 - },
221 - ],
222 - },
223 - ],
224 - },
225 - {
226 - name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks",
227 - code: normalizeIndent`
228 - function notacomponent() {
229 - 'use no forget';
230 - return 1 + 1;
231 - }
232 - `,
233 - errors: [
234 - {
235 - message: "Unused 'use no forget' directive",
236 - suggestions: [
237 - {
238 - output:
239 - // yuck
240 - '\nfunction notacomponent() {\n \n return 1 + 1;\n}\n',
241 - },
242 - ],
243 - },
244 - ],
245 - },
246 - {
247 - name: 'Pipeline errors are reported',
248 - code: normalizeIndent`
249 - import useMyEffect from 'useMyEffect';
250 - import {AUTODEPS} from 'react';
251 - function Component({a}) {
252 - 'use no memo';
253 - useMyEffect(() => console.log(a.b), AUTODEPS);
254 - return <div>Hello world</div>;
255 - }
256 - `,
257 - options: [
258 - {
259 - environment: {
260 - inferEffectDependencies: [
261 - {
262 - function: {
263 - source: 'useMyEffect',
264 - importSpecifierName: 'default',
265 - },
266 - autodepsIndex: 1,
267 - },
268 - ],
269 - },
270 - },
271 - ],
272 - errors: [
273 - {
274 - message: /Cannot infer dependencies of this effect/,
275 - },
276 - ],
277 - },
278 - ],
279 -};
280 -
281 -const eslintTester = new ESLintTesterV8({
282 - parser: require.resolve('hermes-eslint'),
283 - parserOptions: {
284 - ecmaVersion: 2015,
285 - sourceType: 'module',
286 - enableExperimentalComponentSyntax: true,
287 - },
288 -});
289 -eslintTester.run('react-compiler', ReactCompilerRule, tests);
packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts
+2 -2
@@ -6,7 +6,7 @@
6 */
7
8 import {RuleTester} from 'eslint';
9 -import ReactCompilerRule from '../src/rules/ReactCompiler';
9 +import {allRules} from '../src/shared/ReactCompiler';
10
11 const ESLintTesterV8 = require('eslint-v8').RuleTester;
12
@@ -74,4 +74,4 @@ const tests: CompilerTestCases = {
74 const eslintTester = new ESLintTesterV8({
75 parser: require.resolve('@typescript-eslint/parser-v5'),
76 });
77 -eslintTester.run('react-compiler', ReactCompilerRule, tests);
77 +eslintTester.run('react-compiler', allRules['immutability'], tests);
packages/eslint-plugin-react-hooks/src/index.ts
+38 -37
@@ -4,63 +4,64 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7 -import type {ESLint, Linter, Rule} from 'eslint';
7 +import type {Linter, Rule} from 'eslint';
8
9 import ExhaustiveDeps from './rules/ExhaustiveDeps';
10 -import ReactCompiler from './rules/ReactCompiler';
10 +import {allRules, recommendedRules} from './shared/ReactCompiler';
11 import RulesOfHooks from './rules/RulesOfHooks';
12
13 // All rules
14 const rules = {
15 'exhaustive-deps': ExhaustiveDeps,
16 - 'react-compiler': ReactCompiler,
16 'rules-of-hooks': RulesOfHooks,
17 + ...allRules,
18 } satisfies Record<string, Rule.RuleModule>;
19
20 // Config rules
21 -const configRules = {
21 +const ruleConfigs = {
22 'react-hooks/rules-of-hooks': 'error',
23 'react-hooks/exhaustive-deps': 'warn',
24 + ...Object.fromEntries(
25 + Object.keys(recommendedRules).map(name => ['react-hooks/' + name, 'error']),
26 + ),
27 } satisfies Linter.RulesRecord;
28
26 -// Flat config
27 -const recommendedConfig = {
28 - name: 'react-hooks/recommended',
29 - plugins: {
30 - get 'react-hooks'(): ESLint.Plugin {
31 - return plugin;
32 - },
29 +const plugin = {
30 + meta: {
31 + name: 'eslint-plugin-react-hooks',
32 },
34 - rules: configRules,
33 + configs: {},
34 + rules,
35 };
36
37 -// Plugin object
38 -const plugin = {
39 - // TODO: Make this more dynamic to populate version from package.json.
40 - // This can be done by injecting at build time, since importing the package.json isn't an option in Meta
41 - meta: {name: 'eslint-plugin-react-hooks'},
42 - rules,
43 - configs: {
44 - /** Legacy recommended config, to be used with rc-based configurations */
45 - 'recommended-legacy': {
46 - plugins: ['react-hooks'],
47 - rules: configRules,
37 +Object.assign(plugin.configs, {
38 + 'recommended-legacy': {
39 + plugins: ['react-hooks'],
40 + rules: ruleConfigs,
41 + },
42 +
43 + 'flat/recommended': [
44 + {
45 + plugins: {
46 + 'react-hooks': plugin,
47 + },
48 + rules: ruleConfigs,
49 },
50 + ],
51
50 - /**
51 - * Recommended config, to be used with flat configs.
52 - */
53 - recommended: recommendedConfig,
52 + 'recommended-latest': [
53 + {
54 + plugins: {
55 + 'react-hooks': plugin,
56 + },
57 + rules: ruleConfigs,
58 + },
59 + ],
60
55 - /** @deprecated please use `recommended`; will be removed in v7 */
56 - 'recommended-latest': recommendedConfig,
61 + recommended: {
62 + plugins: ['react-hooks'],
63 + rules: ruleConfigs,
64 },
58 -} satisfies ESLint.Plugin;
59 -
60 -const configs = plugin.configs;
61 -const meta = plugin.meta;
62 -export {configs, meta, rules};
65 +});
66
64 -// TODO: If the plugin is ever updated to be pure ESM and drops support for rc-based configs, then it should be exporting the plugin as default
65 -// instead of individual named exports.
66 -// export default plugin;
67 +export default plugin;
packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts deleted
-359
@@ -1,359 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -/* eslint-disable no-for-of-loops/no-for-of-loops */
8 -
9 -import {transformFromAstSync} from '@babel/core';
10 -// @ts-expect-error: no types available
11 -import PluginProposalPrivateMethods from '@babel/plugin-transform-private-methods';
12 -import type {SourceLocation as BabelSourceLocation} from '@babel/types';
13 -import BabelPluginReactCompiler, {
14 - type CompilerErrorDetail,
15 - type CompilerErrorDetailOptions,
16 - type CompilerDiagnostic,
17 - type CompilerDiagnosticOptions,
18 - CompilerSuggestionOperation,
19 - ErrorSeverity,
20 - parsePluginOptions,
21 - validateEnvironmentConfig,
22 - OPT_OUT_DIRECTIVES,
23 - type Logger,
24 - type LoggerEvent,
25 - type PluginOptions,
26 -} from 'babel-plugin-react-compiler';
27 -import type {Rule} from 'eslint';
28 -import {Statement} from 'estree';
29 -import * as HermesParser from 'hermes-parser';
30 -
31 -function assertExhaustive(_: never, errorMsg: string): never {
32 - throw new Error(errorMsg);
33 -}
34 -
35 -const DEFAULT_REPORTABLE_LEVELS = new Set([
36 - ErrorSeverity.InvalidReact,
37 - ErrorSeverity.InvalidJS,
38 -]);
39 -let reportableLevels = DEFAULT_REPORTABLE_LEVELS;
40 -
41 -function isReportableDiagnostic(
42 - detail: CompilerErrorDetail | CompilerDiagnostic,
43 -): boolean {
44 - return reportableLevels.has(detail.severity);
45 -}
46 -
47 -function makeSuggestions(
48 - detail: CompilerErrorDetailOptions | CompilerDiagnosticOptions,
49 -): Array<Rule.SuggestionReportDescriptor> {
50 - const suggest: Array<Rule.SuggestionReportDescriptor> = [];
51 - if (Array.isArray(detail.suggestions)) {
52 - for (const suggestion of detail.suggestions) {
53 - switch (suggestion.op) {
54 - case CompilerSuggestionOperation.InsertBefore:
55 - suggest.push({
56 - desc: suggestion.description,
57 - fix(fixer) {
58 - return fixer.insertTextBeforeRange(
59 - suggestion.range,
60 - suggestion.text,
61 - );
62 - },
63 - });
64 - break;
65 - case CompilerSuggestionOperation.InsertAfter:
66 - suggest.push({
67 - desc: suggestion.description,
68 - fix(fixer) {
69 - return fixer.insertTextAfterRange(
70 - suggestion.range,
71 - suggestion.text,
72 - );
73 - },
74 - });
75 - break;
76 - case CompilerSuggestionOperation.Replace:
77 - suggest.push({
78 - desc: suggestion.description,
79 - fix(fixer) {
80 - return fixer.replaceTextRange(suggestion.range, suggestion.text);
81 - },
82 - });
83 - break;
84 - case CompilerSuggestionOperation.Remove:
85 - suggest.push({
86 - desc: suggestion.description,
87 - fix(fixer) {
88 - return fixer.removeRange(suggestion.range);
89 - },
90 - });
91 - break;
92 - default:
93 - assertExhaustive(suggestion, 'Unhandled suggestion operation');
94 - }
95 - }
96 - }
97 - return suggest;
98 -}
99 -
100 -const COMPILER_OPTIONS: Partial<PluginOptions> = {
101 - noEmit: true,
102 - panicThreshold: 'none',
103 - // Don't emit errors on Flow suppressions--Flow already gave a signal
104 - flowSuppressions: false,
105 - environment: validateEnvironmentConfig({
106 - validateRefAccessDuringRender: true,
107 - validateNoSetStateInRender: true,
108 - validateNoSetStateInEffects: true,
109 - validateNoJSXInTryStatements: true,
110 - validateNoImpureFunctionsInRender: true,
111 - validateStaticComponents: true,
112 - validateNoFreezingKnownMutableFunctions: true,
113 - validateNoVoidUseMemo: true,
114 - }),
115 -};
116 -
117 -const rule: Rule.RuleModule = {
118 - meta: {
119 - type: 'problem',
120 - docs: {
121 - description: 'Surfaces diagnostics from React Forget',
122 - recommended: true,
123 - },
124 - fixable: 'code',
125 - hasSuggestions: true,
126 - // validation is done at runtime with zod
127 - schema: [{type: 'object', additionalProperties: true}],
128 - },
129 - create(context: Rule.RuleContext) {
130 - // Compat with older versions of eslint
131 - const sourceCode = context.sourceCode ?? context.getSourceCode();
132 - const filename = context.filename ?? context.getFilename();
133 - const userOpts = context.options[0] ?? {};
134 - if (
135 - userOpts.reportableLevels != null &&
136 - userOpts.reportableLevels instanceof Set
137 - ) {
138 - reportableLevels = userOpts.reportableLevels;
139 - } else {
140 - reportableLevels = DEFAULT_REPORTABLE_LEVELS;
141 - }
142 - /**
143 - * Experimental setting to report all compilation bailouts on the compilation
144 - * unit (e.g. function or hook) instead of the offensive line.
145 - * Intended to be used when a codebase is 100% reliant on the compiler for
146 - * memoization (i.e. deleted all manual memo) and needs compilation success
147 - * signals for perf debugging.
148 - */
149 - let __unstable_donotuse_reportAllBailouts: boolean = false;
150 - if (
151 - userOpts.__unstable_donotuse_reportAllBailouts != null &&
152 - typeof userOpts.__unstable_donotuse_reportAllBailouts === 'boolean'
153 - ) {
154 - __unstable_donotuse_reportAllBailouts =
155 - userOpts.__unstable_donotuse_reportAllBailouts;
156 - }
157 -
158 - let shouldReportUnusedOptOutDirective = true;
159 - const options: PluginOptions = parsePluginOptions({
160 - ...COMPILER_OPTIONS,
161 - ...userOpts,
162 - environment: {
163 - ...COMPILER_OPTIONS.environment,
164 - ...userOpts.environment,
165 - },
166 - });
167 - const userLogger: Logger | null = options.logger;
168 - options.logger = {
169 - logEvent: (eventFilename, event): void => {
170 - userLogger?.logEvent(eventFilename, event);
171 - if (event.kind === 'CompileError') {
172 - shouldReportUnusedOptOutDirective = false;
173 - const detail = event.detail;
174 - const suggest = makeSuggestions(detail.options);
175 - if (__unstable_donotuse_reportAllBailouts && event.fnLoc != null) {
176 - const loc = detail.primaryLocation();
177 - const locStr =
178 - loc != null && typeof loc !== 'symbol'
179 - ? ` (@:${loc.start.line}:${loc.start.column})`
180 - : '';
181 - /**
182 - * Report bailouts with a smaller span (just the first line).
183 - * Compiler bailout lints only serve to flag that a react function
184 - * has not been optimized by the compiler for codebases which depend
185 - * on compiler memo heavily for perf. These lints are also often not
186 - * actionable.
187 - */
188 - let endLoc;
189 - if (event.fnLoc.end.line === event.fnLoc.start.line) {
190 - endLoc = event.fnLoc.end;
191 - } else {
192 - endLoc = {
193 - line: event.fnLoc.start.line,
194 - // Babel loc line numbers are 1-indexed
195 - column:
196 - sourceCode.text.split(/\r?\n|\r|\n/g)[
197 - event.fnLoc.start.line - 1
198 - ]?.length ?? 0,
199 - };
200 - }
201 - const firstLineLoc = {
202 - start: event.fnLoc.start,
203 - end: endLoc,
204 - };
205 - context.report({
206 - message: `${detail.printErrorMessage(sourceCode.text, {eslint: true})} ${locStr}`,
207 - loc: firstLineLoc,
208 - suggest,
209 - });
210 - }
211 -
212 - const loc = detail.primaryLocation();
213 - if (
214 - !isReportableDiagnostic(detail) ||
215 - loc == null ||
216 - typeof loc === 'symbol'
217 - ) {
218 - return;
219 - }
220 - if (
221 - hasFlowSuppression(loc, 'react-rule-hook') ||
222 - hasFlowSuppression(loc, 'react-rule-unsafe-ref')
223 - ) {
224 - // If Flow already caught this error, we don't need to report it again.
225 - return;
226 - }
227 - if (loc != null) {
228 - context.report({
229 - message: detail.printErrorMessage(sourceCode.text, {
230 - eslint: true,
231 - }),
232 - loc,
233 - suggest,
234 - });
235 - }
236 - }
237 - },
238 - };
239 -
240 - try {
241 - options.environment = validateEnvironmentConfig(
242 - options.environment ?? {},
243 - );
244 - } catch (err: unknown) {
245 - options.logger?.logEvent('', err as LoggerEvent);
246 - }
247 -
248 - function hasFlowSuppression(
249 - nodeLoc: BabelSourceLocation,
250 - suppression: string,
251 - ): boolean {
252 - const comments = sourceCode.getAllComments();
253 - const flowSuppressionRegex = new RegExp(
254 - '\\$FlowFixMe\\[' + suppression + '\\]',
255 - );
256 - for (const commentNode of comments) {
257 - if (
258 - flowSuppressionRegex.test(commentNode.value) &&
259 - commentNode.loc!.end.line === nodeLoc.start.line - 1
260 - ) {
261 - return true;
262 - }
263 - }
264 - return false;
265 - }
266 -
267 - let babelAST;
268 - if (filename.endsWith('.tsx') || filename.endsWith('.ts')) {
269 - try {
270 - const {parse: babelParse} = require('@babel/parser');
271 - babelAST = babelParse(sourceCode.text, {
272 - filename,
273 - sourceType: 'unambiguous',
274 - plugins: ['typescript', 'jsx'],
275 - });
276 - } catch {
277 - /* empty */
278 - }
279 - } else {
280 - try {
281 - babelAST = HermesParser.parse(sourceCode.text, {
282 - babel: true,
283 - enableExperimentalComponentSyntax: true,
284 - sourceFilename: filename,
285 - sourceType: 'module',
286 - });
287 - } catch {
288 - /* empty */
289 - }
290 - }
291 -
292 - if (babelAST != null) {
293 - try {
294 - transformFromAstSync(babelAST, sourceCode.text, {
295 - filename,
296 - highlightCode: false,
297 - retainLines: true,
298 - plugins: [
299 - [PluginProposalPrivateMethods, {loose: true}],
300 - [BabelPluginReactCompiler, options],
301 - ],
302 - sourceType: 'module',
303 - configFile: false,
304 - babelrc: false,
305 - });
306 - } catch (err) {
307 - /* errors handled by injected logger */
308 - }
309 - }
310 -
311 - function reportUnusedOptOutDirective(stmt: Statement) {
312 - if (
313 - stmt.type === 'ExpressionStatement' &&
314 - stmt.expression.type === 'Literal' &&
315 - typeof stmt.expression.value === 'string' &&
316 - OPT_OUT_DIRECTIVES.has(stmt.expression.value) &&
317 - stmt.loc != null
318 - ) {
319 - context.report({
320 - message: `Unused '${stmt.expression.value}' directive`,
321 - loc: stmt.loc,
322 - suggest: [
323 - {
324 - desc: 'Remove the directive',
325 - fix(fixer) {
326 - return fixer.remove(stmt);
327 - },
328 - },
329 - ],
330 - });
331 - }
332 - }
333 - if (shouldReportUnusedOptOutDirective) {
334 - return {
335 - FunctionDeclaration(fnDecl) {
336 - for (const stmt of fnDecl.body.body) {
337 - reportUnusedOptOutDirective(stmt);
338 - }
339 - },
340 - ArrowFunctionExpression(fnExpr) {
341 - if (fnExpr.body.type === 'BlockStatement') {
342 - for (const stmt of fnExpr.body.body) {
343 - reportUnusedOptOutDirective(stmt);
344 - }
345 - }
346 - },
347 - FunctionExpression(fnExpr) {
348 - for (const stmt of fnExpr.body.body) {
349 - reportUnusedOptOutDirective(stmt);
350 - }
351 - },
352 - };
353 - } else {
354 - return {};
355 - }
356 - },
357 -};
358 -
359 -export default rule;
packages/eslint-plugin-react-hooks/src/shared/ReactCompiler.ts new
+216
@@ -0,0 +1,216 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +/* eslint-disable no-for-of-loops/no-for-of-loops */
8 +
9 +import type {SourceLocation as BabelSourceLocation} from '@babel/types';
10 +import {
11 + type CompilerDiagnosticOptions,
12 + type CompilerErrorDetailOptions,
13 + CompilerSuggestionOperation,
14 + LintRules,
15 + type LintRule,
16 +} from 'babel-plugin-react-compiler';
17 +import type {Rule} from 'eslint';
18 +import runReactCompiler, {RunCacheEntry} from './RunReactCompiler';
19 +
20 +function assertExhaustive(_: never, errorMsg: string): never {
21 + throw new Error(errorMsg);
22 +}
23 +
24 +function makeSuggestions(
25 + detail: CompilerErrorDetailOptions | CompilerDiagnosticOptions,
26 +): Array<Rule.SuggestionReportDescriptor> {
27 + const suggest: Array<Rule.SuggestionReportDescriptor> = [];
28 + if (Array.isArray(detail.suggestions)) {
29 + for (const suggestion of detail.suggestions) {
30 + switch (suggestion.op) {
31 + case CompilerSuggestionOperation.InsertBefore:
32 + suggest.push({
33 + desc: suggestion.description,
34 + fix(fixer) {
35 + return fixer.insertTextBeforeRange(
36 + suggestion.range,
37 + suggestion.text,
38 + );
39 + },
40 + });
41 + break;
42 + case CompilerSuggestionOperation.InsertAfter:
43 + suggest.push({
44 + desc: suggestion.description,
45 + fix(fixer) {
46 + return fixer.insertTextAfterRange(
47 + suggestion.range,
48 + suggestion.text,
49 + );
50 + },
51 + });
52 + break;
53 + case CompilerSuggestionOperation.Replace:
54 + suggest.push({
55 + desc: suggestion.description,
56 + fix(fixer) {
57 + return fixer.replaceTextRange(suggestion.range, suggestion.text);
58 + },
59 + });
60 + break;
61 + case CompilerSuggestionOperation.Remove:
62 + suggest.push({
63 + desc: suggestion.description,
64 + fix(fixer) {
65 + return fixer.removeRange(suggestion.range);
66 + },
67 + });
68 + break;
69 + default:
70 + assertExhaustive(suggestion, 'Unhandled suggestion operation');
71 + }
72 + }
73 + }
74 + return suggest;
75 +}
76 +
77 +function getReactCompilerResult(context: Rule.RuleContext): RunCacheEntry {
78 + // Compat with older versions of eslint
79 + const sourceCode = context.sourceCode ?? context.getSourceCode();
80 + const filename = context.filename ?? context.getFilename();
81 + const userOpts = context.options[0] ?? {};
82 +
83 + const results = runReactCompiler({
84 + sourceCode,
85 + filename,
86 + userOpts,
87 + });
88 +
89 + return results;
90 +}
91 +
92 +function hasFlowSuppression(
93 + program: RunCacheEntry,
94 + nodeLoc: BabelSourceLocation,
95 + suppressions: Array<string>,
96 +): boolean {
97 + for (const commentNode of program.flowSuppressions) {
98 + if (
99 + suppressions.includes(commentNode.code) &&
100 + commentNode.line === nodeLoc.start.line - 1
101 + ) {
102 + return true;
103 + }
104 + }
105 + return false;
106 +}
107 +
108 +function makeRule(rule: LintRule): Rule.RuleModule {
109 + const create = (context: Rule.RuleContext): Rule.RuleListener => {
110 + const result = getReactCompilerResult(context);
111 +
112 + for (const event of result.events) {
113 + if (event.kind === 'CompileError') {
114 + const detail = event.detail;
115 + if (detail.category === rule.category) {
116 + const loc = detail.primaryLocation();
117 + if (loc == null || typeof loc === 'symbol') {
118 + continue;
119 + }
120 + if (
121 + hasFlowSuppression(result, loc, [
122 + 'react-rule-hook',
123 + 'react-rule-unsafe-ref',
124 + ])
125 + ) {
126 + // If Flow already caught this error, we don't need to report it again.
127 + continue;
128 + }
129 + /*
130 + * TODO: if multiple rules report the same linter category,
131 + * we should deduplicate them with a "reported" set
132 + */
133 + context.report({
134 + message: detail.printErrorMessage(result.sourceCode, {
135 + eslint: true,
136 + }),
137 + loc,
138 + suggest: makeSuggestions(detail.options),
139 + });
140 + }
141 + }
142 + }
143 + return {};
144 + };
145 +
146 + return {
147 + meta: {
148 + type: 'problem',
149 + docs: {
150 + description: rule.description,
151 + recommended: rule.recommended,
152 + },
153 + fixable: 'code',
154 + hasSuggestions: true,
155 + // validation is done at runtime with zod
156 + schema: [{type: 'object', additionalProperties: true}],
157 + },
158 + create,
159 + };
160 +}
161 +
162 +export const NoUnusedDirectivesRule: Rule.RuleModule = {
163 + meta: {
164 + type: 'suggestion',
165 + docs: {
166 + recommended: true,
167 + },
168 + fixable: 'code',
169 + hasSuggestions: true,
170 + // validation is done at runtime with zod
171 + schema: [{type: 'object', additionalProperties: true}],
172 + },
173 + create(context: Rule.RuleContext): Rule.RuleListener {
174 + const results = getReactCompilerResult(context);
175 +
176 + for (const directive of results.unusedOptOutDirectives) {
177 + context.report({
178 + message: `Unused '${directive.directive}' directive`,
179 + loc: directive.loc,
180 + suggest: [
181 + {
182 + desc: 'Remove the directive',
183 + fix(fixer): Rule.Fix {
184 + return fixer.removeRange(directive.range);
185 + },
186 + },
187 + ],
188 + });
189 + }
190 + return {};
191 + },
192 +};
193 +
194 +type RulesObject = {[name: string]: Rule.RuleModule};
195 +
196 +export const allRules: RulesObject = LintRules.reduce(
197 + (acc, rule) => {
198 + acc[rule.name] = makeRule(rule);
199 + return acc;
200 + },
201 + {
202 + 'no-unused-directives': NoUnusedDirectivesRule,
203 + } as RulesObject,
204 +);
205 +
206 +export const recommendedRules: RulesObject = LintRules.filter(
207 + rule => rule.recommended,
208 +).reduce(
209 + (acc, rule) => {
210 + acc[rule.name] = makeRule(rule);
211 + return acc;
212 + },
213 + {
214 + 'no-unused-directives': NoUnusedDirectivesRule,
215 + } as RulesObject,
216 +);
packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts new
+281
@@ -0,0 +1,281 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +/* eslint-disable no-for-of-loops/no-for-of-loops */
8 +
9 +import {transformFromAstSync, traverse} from '@babel/core';
10 +import {parse as babelParse} from '@babel/parser';
11 +import {Directive, File} from '@babel/types';
12 +// @ts-expect-error: no types available
13 +import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods';
14 +import BabelPluginReactCompiler, {
15 + parsePluginOptions,
16 + validateEnvironmentConfig,
17 + OPT_OUT_DIRECTIVES,
18 + type PluginOptions,
19 + Logger,
20 + LoggerEvent,
21 +} from 'babel-plugin-react-compiler';
22 +import type {SourceCode} from 'eslint';
23 +import {SourceLocation} from 'estree';
24 +import * as HermesParser from 'hermes-parser';
25 +import {isDeepStrictEqual} from 'util';
26 +import type {ParseResult} from '@babel/parser';
27 +
28 +const COMPILER_OPTIONS: Partial<PluginOptions> = {
29 + noEmit: true,
30 + panicThreshold: 'none',
31 + // Don't emit errors on Flow suppressions--Flow already gave a signal
32 + flowSuppressions: false,
33 + environment: validateEnvironmentConfig({
34 + validateRefAccessDuringRender: true,
35 + validateNoSetStateInRender: true,
36 + validateNoSetStateInEffects: true,
37 + validateNoJSXInTryStatements: true,
38 + validateNoImpureFunctionsInRender: true,
39 + validateStaticComponents: true,
40 + validateNoFreezingKnownMutableFunctions: true,
41 + validateNoVoidUseMemo: true,
42 + // TODO: remove, this should be in the type system
43 + validateNoCapitalizedCalls: [],
44 + validateHooksUsage: true,
45 + validateNoDerivedComputationsInEffects: true,
46 + }),
47 +};
48 +
49 +export type UnusedOptOutDirective = {
50 + loc: SourceLocation;
51 + range: [number, number];
52 + directive: string;
53 +};
54 +export type RunCacheEntry = {
55 + sourceCode: string;
56 + filename: string;
57 + userOpts: PluginOptions;
58 + flowSuppressions: Array<{line: number; code: string}>;
59 + unusedOptOutDirectives: Array<UnusedOptOutDirective>;
60 + events: Array<LoggerEvent>;
61 +};
62 +
63 +type RunParams = {
64 + sourceCode: SourceCode;
65 + filename: string;
66 + userOpts: PluginOptions;
67 +};
68 +const FLOW_SUPPRESSION_REGEX = /\$FlowFixMe\[([^\]]*)\]/g;
69 +
70 +function getFlowSuppressions(
71 + sourceCode: SourceCode,
72 +): Array<{line: number; code: string}> {
73 + const comments = sourceCode.getAllComments();
74 + const results: Array<{line: number; code: string}> = [];
75 +
76 + for (const commentNode of comments) {
77 + const matches = commentNode.value.matchAll(FLOW_SUPPRESSION_REGEX);
78 + for (const match of matches) {
79 + if (match.index != null && commentNode.loc != null) {
80 + const code = match[1];
81 + results.push({
82 + line: commentNode.loc!.end.line,
83 + code,
84 + });
85 + }
86 + }
87 + }
88 + return results;
89 +}
90 +
91 +function filterUnusedOptOutDirectives(
92 + directives: ReadonlyArray<Directive>,
93 +): Array<UnusedOptOutDirective> {
94 + const results: Array<UnusedOptOutDirective> = [];
95 + for (const directive of directives) {
96 + if (
97 + OPT_OUT_DIRECTIVES.has(directive.value.value) &&
98 + directive.loc != null
99 + ) {
100 + results.push({
101 + loc: directive.loc,
102 + directive: directive.value.value,
103 + range: [directive.start!, directive.end!],
104 + });
105 + }
106 + }
107 + return results;
108 +}
109 +
110 +function runReactCompilerImpl({
111 + sourceCode,
112 + filename,
113 + userOpts,
114 +}: RunParams): RunCacheEntry {
115 + // Compat with older versions of eslint
116 + const options: PluginOptions = parsePluginOptions({
117 + ...COMPILER_OPTIONS,
118 + ...userOpts,
119 + environment: {
120 + ...COMPILER_OPTIONS.environment,
121 + ...userOpts.environment,
122 + },
123 + });
124 + const results: RunCacheEntry = {
125 + sourceCode: sourceCode.text,
126 + filename,
127 + userOpts,
128 + flowSuppressions: [],
129 + unusedOptOutDirectives: [],
130 + events: [],
131 + };
132 + const userLogger: Logger | null = options.logger;
133 + options.logger = {
134 + logEvent: (eventFilename, event): void => {
135 + userLogger?.logEvent(eventFilename, event);
136 + results.events.push(event);
137 + },
138 + };
139 +
140 + try {
141 + options.environment = validateEnvironmentConfig(options.environment ?? {});
142 + } catch (err: unknown) {
143 + options.logger?.logEvent(filename, err as LoggerEvent);
144 + }
145 +
146 + let babelAST: ParseResult<File> | null = null;
147 + if (filename.endsWith('.tsx') || filename.endsWith('.ts')) {
148 + try {
149 + babelAST = babelParse(sourceCode.text, {
150 + sourceFilename: filename,
151 + sourceType: 'unambiguous',
152 + plugins: ['typescript', 'jsx'],
153 + });
154 + } catch {
155 + /* empty */
156 + }
157 + } else {
158 + try {
159 + babelAST = HermesParser.parse(sourceCode.text, {
160 + babel: true,
161 + enableExperimentalComponentSyntax: true,
162 + sourceFilename: filename,
163 + sourceType: 'module',
164 + });
165 + } catch {
166 + /* empty */
167 + }
168 + }
169 +
170 + if (babelAST != null) {
171 + results.flowSuppressions = getFlowSuppressions(sourceCode);
172 + try {
173 + transformFromAstSync(babelAST, sourceCode.text, {
174 + filename,
175 + highlightCode: false,
176 + retainLines: true,
177 + plugins: [
178 + [PluginProposalPrivateMethods, {loose: true}],
179 + [BabelPluginReactCompiler, options],
180 + ],
181 + sourceType: 'module',
182 + configFile: false,
183 + babelrc: false,
184 + });
185 +
186 + if (results.events.filter(e => e.kind === 'CompileError').length === 0) {
187 + traverse(babelAST, {
188 + FunctionDeclaration(path) {
189 + results.unusedOptOutDirectives.push(
190 + ...filterUnusedOptOutDirectives(path.node.body.directives),
191 + );
192 + },
193 + ArrowFunctionExpression(path) {
194 + if (path.node.body.type === 'BlockStatement') {
195 + results.unusedOptOutDirectives.push(
196 + ...filterUnusedOptOutDirectives(path.node.body.directives),
197 + );
198 + }
199 + },
200 + FunctionExpression(path) {
201 + results.unusedOptOutDirectives.push(
202 + ...filterUnusedOptOutDirectives(path.node.body.directives),
203 + );
204 + },
205 + });
206 + }
207 + } catch (err) {
208 + /* errors handled by injected logger */
209 + }
210 + }
211 +
212 + return results;
213 +}
214 +
215 +const SENTINEL = Symbol();
216 +
217 +// Array backed LRU cache -- should be small < 10 elements
218 +class LRUCache<K, T> {
219 + // newest at headIdx, then headIdx + 1, ..., tailIdx
220 + #values: Array<[K, T | Error] | [typeof SENTINEL, void]>;
221 + #headIdx: number = 0;
222 +
223 + constructor(size: number) {
224 + this.#values = new Array(size).fill(SENTINEL);
225 + }
226 +
227 + // gets a value and sets it as "recently used"
228 + get(key: K): T | null {
229 + const idx = this.#values.findIndex(entry => entry[0] === key);
230 + // If found, move to front
231 + if (idx === this.#headIdx) {
232 + return this.#values[this.#headIdx][1] as T;
233 + } else if (idx < 0) {
234 + return null;
235 + }
236 +
237 + const entry: [K, T] = this.#values[idx] as [K, T];
238 +
239 + const len = this.#values.length;
240 + for (let i = 0; i < Math.min(idx, len - 1); i++) {
241 + this.#values[(this.#headIdx + i + 1) % len] =
242 + this.#values[(this.#headIdx + i) % len];
243 + }
244 + this.#values[this.#headIdx] = entry;
245 + return entry[1];
246 + }
247 + push(key: K, value: T): void {
248 + this.#headIdx =
249 + (this.#headIdx - 1 + this.#values.length) % this.#values.length;
250 + this.#values[this.#headIdx] = [key, value];
251 + }
252 +}
253 +const cache = new LRUCache<string, RunCacheEntry>(10);
254 +
255 +export default function runReactCompiler({
256 + sourceCode,
257 + filename,
258 + userOpts,
259 +}: RunParams): RunCacheEntry {
260 + const entry = cache.get(filename);
261 + if (
262 + entry != null &&
263 + entry.sourceCode === sourceCode.text &&
264 + isDeepStrictEqual(entry.userOpts, userOpts)
265 + ) {
266 + return entry;
267 + }
268 +
269 + const runEntry = runReactCompilerImpl({
270 + sourceCode,
271 + filename,
272 + userOpts,
273 + });
274 + // If we have a cache entry, we can update it
275 + if (entry != null) {
276 + Object.assign(entry, runEntry);
277 + } else {
278 + cache.push(filename, runEntry);
279 + }
280 + return {...runEntry};
281 +}
scripts/rollup/build.js
+1
@@ -403,6 +403,7 @@ function getPlugins(
403 // Use Node resolution mechanism.
404 resolve({
405 // skip: externals, // TODO: options.skip was removed in @rollup/plugin-node-resolve 3.0.0
406 + preferBuiltins: bundle.preferBuiltins,
407 }),
408 // Remove license headers from individual modules
409 stripBanner({
scripts/rollup/bundles.js
+21 -2
@@ -1208,7 +1208,14 @@ const bundles = [
1208 global: 'ESLintPluginReactHooks',
1209 minifyWithProdErrorCodes: false,
1210 wrapWithModuleBoundaries: false,
1211 - externals: [],
1211 + preferBuiltins: true,
1212 + externals: [
1213 + '@babel/core',
1214 + '@babel/plugin-proposal-private-methods',
1215 + 'hermes-parser',
1216 + 'zod',
1217 + 'zod-validation-error',
1218 + ],
1219 tsconfig: './packages/eslint-plugin-react-hooks/tsconfig.json',
1220 prebuild: `mkdir -p ./compiler/packages/babel-plugin-react-compiler/dist && echo "module.exports = require('../src/index.ts');" > ./compiler/packages/babel-plugin-react-compiler/dist/index.js`,
1221 },
@@ -1296,9 +1303,21 @@ function getFilename(bundle, bundleType) {
1303 }
1304 }
1305
1306 +let activeBundles = bundles;
1307 +if (process.env.BUNDLES_FILTER != null) {
1308 + activeBundles = activeBundles.filter(
1309 + bundle => bundle.name === process.env.BUNDLES_FILTER
1310 + );
1311 + if (activeBundles.length === 0) {
1312 + throw new Error(
1313 + `No bundles matched for BUNDLES_FILTER=${process.env.BUNDLES_FILTER}`
1314 + );
1315 + }
1316 +}
1317 +
1318 module.exports = {
1319 bundleTypes,
1320 moduleTypes,
1302 - bundles,
1321 + bundles: activeBundles,
1322 getFilename,
1323 };
yarn.lock
+157 -75
@@ -70,6 +70,15 @@
70 js-tokens "^4.0.0"
71 picocolors "^1.0.0"
72
73 +"@babel/code-frame@^7.27.1":
74 + version "7.27.1"
75 + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
76 + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
77 + dependencies:
78 + "@babel/helper-validator-identifier" "^7.27.1"
79 + js-tokens "^4.0.0"
80 + picocolors "^1.1.1"
81 +
82 "@babel/code-frame@^7.8.3":
83 version "7.8.3"
84 resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@@ -197,6 +206,17 @@
206 "@jridgewell/trace-mapping" "^0.3.25"
207 jsesc "^3.0.2"
208
209 +"@babel/generator@^7.28.3":
210 + version "7.28.3"
211 + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e"
212 + integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==
213 + dependencies:
214 + "@babel/parser" "^7.28.3"
215 + "@babel/types" "^7.28.2"
216 + "@jridgewell/gen-mapping" "^0.3.12"
217 + "@jridgewell/trace-mapping" "^0.3.28"
218 + jsesc "^3.0.2"
219 +
220 "@babel/generator@^7.8.3":
221 version "7.8.3"
222 resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03"
@@ -235,6 +255,13 @@
255 dependencies:
256 "@babel/types" "^7.25.9"
257
258 +"@babel/helper-annotate-as-pure@^7.27.3":
259 + version "7.27.3"
260 + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5"
261 + integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==
262 + dependencies:
263 + "@babel/types" "^7.27.3"
264 +
265 "@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4":
266 version "7.10.4"
267 resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3"
@@ -343,6 +370,19 @@
370 "@babel/traverse" "^7.26.9"
371 semver "^6.3.1"
372
373 +"@babel/helper-create-class-features-plugin@^7.27.1":
374 + version "7.28.3"
375 + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz#3e747434ea007910c320c4d39a6b46f20f371d46"
376 + integrity sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==
377 + dependencies:
378 + "@babel/helper-annotate-as-pure" "^7.27.3"
379 + "@babel/helper-member-expression-to-functions" "^7.27.1"
380 + "@babel/helper-optimise-call-expression" "^7.27.1"
381 + "@babel/helper-replace-supers" "^7.27.1"
382 + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1"
383 + "@babel/traverse" "^7.28.3"
384 + semver "^6.3.1"
385 +
386 "@babel/helper-create-regexp-features-plugin@^7.10.4":
387 version "7.10.4"
388 resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8"
@@ -449,6 +489,11 @@
489 dependencies:
490 "@babel/types" "^7.8.3"
491
492 +"@babel/helper-globals@^7.28.0":
493 + version "7.28.0"
494 + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
495 + integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
496 +
497 "@babel/helper-hoist-variables@^7.10.4":
498 version "7.10.4"
499 resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e"
@@ -478,6 +523,14 @@
523 "@babel/traverse" "^7.25.9"
524 "@babel/types" "^7.25.9"
525
526 +"@babel/helper-member-expression-to-functions@^7.27.1":
527 + version "7.27.1"
528 + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44"
529 + integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==
530 + dependencies:
531 + "@babel/traverse" "^7.27.1"
532 + "@babel/types" "^7.27.1"
533 +
534 "@babel/helper-module-imports@^7.10.4":
535 version "7.10.4"
536 resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
@@ -561,6 +614,13 @@
614 dependencies:
615 "@babel/types" "^7.25.9"
616
617 +"@babel/helper-optimise-call-expression@^7.27.1":
618 + version "7.27.1"
619 + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200"
620 + integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==
621 + dependencies:
622 + "@babel/types" "^7.27.1"
623 +
624 "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0":
625 version "7.24.5"
626 resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a"
@@ -581,6 +641,11 @@
641 resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35"
642 integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==
643
644 +"@babel/helper-plugin-utils@^7.27.1":
645 + version "7.27.1"
646 + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c"
647 + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==
648 +
649 "@babel/helper-plugin-utils@^7.8.3":
650 version "7.8.3"
651 resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
@@ -644,6 +709,15 @@
709 "@babel/helper-optimise-call-expression" "^7.25.9"
710 "@babel/traverse" "^7.26.5"
711
712 +"@babel/helper-replace-supers@^7.27.1":
713 + version "7.27.1"
714 + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0"
715 + integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==
716 + dependencies:
717 + "@babel/helper-member-expression-to-functions" "^7.27.1"
718 + "@babel/helper-optimise-call-expression" "^7.27.1"
719 + "@babel/traverse" "^7.27.1"
720 +
721 "@babel/helper-simple-access@^7.10.4":
722 version "7.10.4"
723 resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461"
@@ -681,6 +755,14 @@
755 "@babel/traverse" "^7.25.9"
756 "@babel/types" "^7.25.9"
757
758 +"@babel/helper-skip-transparent-expression-wrappers@^7.27.1":
759 + version "7.27.1"
760 + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56"
761 + integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==
762 + dependencies:
763 + "@babel/traverse" "^7.27.1"
764 + "@babel/types" "^7.27.1"
765 +
766 "@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
767 version "7.11.0"
768 resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
@@ -707,6 +789,11 @@
789 resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
790 integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
791
792 +"@babel/helper-string-parser@^7.27.1":
793 + version "7.27.1"
794 + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
795 + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
796 +
797 "@babel/helper-validator-identifier@^7.14.0", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.24.5":
798 version "7.24.5"
799 resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62"
@@ -722,6 +809,11 @@
809 resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
810 integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
811
812 +"@babel/helper-validator-identifier@^7.27.1":
813 + version "7.27.1"
814 + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
815 + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
816 +
817 "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5":
818 version "7.23.5"
819 resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
@@ -849,6 +941,13 @@
941 dependencies:
942 "@babel/types" "^7.26.10"
943
944 +"@babel/parser@^7.27.2", "@babel/parser@^7.28.3":
945 + version "7.28.3"
946 + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71"
947 + integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==
948 + dependencies:
949 + "@babel/types" "^7.28.2"
950 +
951 "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
952 version "7.25.9"
953 resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe"
@@ -1765,6 +1864,14 @@
1864 dependencies:
1865 "@babel/helper-plugin-utils" "^7.25.9"
1866
1867 +"@babel/plugin-transform-private-methods@^7.10.4":
1868 + version "7.27.1"
1869 + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz#fdacbab1c5ed81ec70dfdbb8b213d65da148b6af"
1870 + integrity sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==
1871 + dependencies:
1872 + "@babel/helper-create-class-features-plugin" "^7.27.1"
1873 + "@babel/helper-plugin-utils" "^7.27.1"
1874 +
1875 "@babel/plugin-transform-private-methods@^7.24.4", "@babel/plugin-transform-private-methods@^7.25.9":
1876 version "7.25.9"
1877 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57"
@@ -2366,6 +2473,15 @@
2473 "@babel/parser" "^7.26.9"
2474 "@babel/types" "^7.26.9"
2475
2476 +"@babel/template@^7.27.2":
2477 + version "7.27.2"
2478 + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
2479 + integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
2480 + dependencies:
2481 + "@babel/code-frame" "^7.27.1"
2482 + "@babel/parser" "^7.27.2"
2483 + "@babel/types" "^7.27.1"
2484 +
2485 "@babel/template@^7.8.3":
2486 version "7.8.3"
2487 resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8"
@@ -2446,6 +2562,19 @@
2562 debug "^4.3.1"
2563 globals "^11.1.0"
2564
2565 +"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3":
2566 + version "7.28.3"
2567 + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434"
2568 + integrity sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==
2569 + dependencies:
2570 + "@babel/code-frame" "^7.27.1"
2571 + "@babel/generator" "^7.28.3"
2572 + "@babel/helper-globals" "^7.28.0"
2573 + "@babel/parser" "^7.28.3"
2574 + "@babel/template" "^7.27.2"
2575 + "@babel/types" "^7.28.2"
2576 + debug "^4.3.1"
2577 +
2578 "@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.12.13", "@babel/types@^7.12.5", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.24.0", "@babel/types@^7.24.5", "@babel/types@^7.25.9", "@babel/types@^7.26.9", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.8.3":
2579 version "7.26.9"
2580 resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce"
@@ -2462,6 +2591,14 @@
2591 "@babel/helper-string-parser" "^7.25.9"
2592 "@babel/helper-validator-identifier" "^7.25.9"
2593
2594 +"@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2":
2595 + version "7.28.2"
2596 + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b"
2597 + integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==
2598 + dependencies:
2599 + "@babel/helper-string-parser" "^7.27.1"
2600 + "@babel/helper-validator-identifier" "^7.27.1"
2601 +
2602 "@bcoe/v8-coverage@^0.2.3":
2603 version "0.2.3"
2604 resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -3047,6 +3184,14 @@
3184 "@jridgewell/sourcemap-codec" "^1.4.10"
3185 "@jridgewell/trace-mapping" "^0.3.9"
3186
3187 +"@jridgewell/gen-mapping@^0.3.12":
3188 + version "0.3.13"
3189 + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
3190 + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
3191 + dependencies:
3192 + "@jridgewell/sourcemap-codec" "^1.5.0"
3193 + "@jridgewell/trace-mapping" "^0.3.24"
3194 +
3195 "@jridgewell/gen-mapping@^0.3.2":
3196 version "0.3.8"
3197 resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
@@ -3119,6 +3264,14 @@
3264 "@jridgewell/resolve-uri" "3.1.0"
3265 "@jridgewell/sourcemap-codec" "1.4.14"
3266
3267 +"@jridgewell/trace-mapping@^0.3.28":
3268 + version "0.3.30"
3269 + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99"
3270 + integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==
3271 + dependencies:
3272 + "@jridgewell/resolve-uri" "^3.1.0"
3273 + "@jridgewell/sourcemap-codec" "^1.4.14"
3274 +
3275 "@leichtgewicht/ip-codec@^2.0.1":
3276 version "2.0.4"
3277 resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
@@ -8100,7 +8253,7 @@ eslint-utils@^2.0.0, eslint-utils@^2.1.0:
8253 dependencies:
8254 eslint-visitor-keys "^1.1.0"
8255
8103 -"eslint-v7@npm:eslint@^7.7.0":
8256 +"eslint-v7@npm:eslint@^7.7.0", eslint@^7.7.0:
8257 version "7.32.0"
8258 resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
8259 integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
@@ -8299,52 +8452,6 @@ eslint@8.57.0:
8452 strip-ansi "^6.0.1"
8453 text-table "^0.2.0"
8454
8302 -eslint@^7.7.0:
8303 - version "7.32.0"
8304 - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
8305 - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
8306 - dependencies:
8307 - "@babel/code-frame" "7.12.11"
8308 - "@eslint/eslintrc" "^0.4.3"
8309 - "@humanwhocodes/config-array" "^0.5.0"
8310 - ajv "^6.10.0"
8311 - chalk "^4.0.0"
8312 - cross-spawn "^7.0.2"
8313 - debug "^4.0.1"
8314 - doctrine "^3.0.0"
8315 - enquirer "^2.3.5"
8316 - escape-string-regexp "^4.0.0"
8317 - eslint-scope "^5.1.1"
8318 - eslint-utils "^2.1.0"
8319 - eslint-visitor-keys "^2.0.0"
8320 - espree "^7.3.1"
8321 - esquery "^1.4.0"
8322 - esutils "^2.0.2"
8323 - fast-deep-equal "^3.1.3"
8324 - file-entry-cache "^6.0.1"
8325 - functional-red-black-tree "^1.0.1"
8326 - glob-parent "^5.1.2"
8327 - globals "^13.6.0"
8328 - ignore "^4.0.6"
8329 - import-fresh "^3.0.0"
8330 - imurmurhash "^0.1.4"
8331 - is-glob "^4.0.0"
8332 - js-yaml "^3.13.1"
8333 - json-stable-stringify-without-jsonify "^1.0.1"
8334 - levn "^0.4.1"
8335 - lodash.merge "^4.6.2"
8336 - minimatch "^3.0.4"
8337 - natural-compare "^1.4.0"
8338 - optionator "^0.9.1"
8339 - progress "^2.0.0"
8340 - regexpp "^3.1.0"
8341 - semver "^7.2.1"
8342 - strip-ansi "^6.0.0"
8343 - strip-json-comments "^3.1.0"
8344 - table "^6.0.9"
8345 - text-table "^0.2.0"
8346 - v8-compile-cache "^2.0.3"
8347 -
8455 espree@10.0.1, espree@^10.0.1:
8456 version "10.0.1"
8457 resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f"
@@ -15960,7 +16067,7 @@ string-natural-compare@^3.0.1:
16067 resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
16068 integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
16069
15963 -"string-width-cjs@npm:string-width@^4.2.0":
16070 +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
16071 version "4.2.3"
16072 resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
16073 integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -15995,15 +16102,6 @@ string-width@^4.0.0:
16102 is-fullwidth-code-point "^3.0.0"
16103 strip-ansi "^6.0.0"
16104
15998 -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
15999 - version "4.2.3"
16000 - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
16001 - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
16002 - dependencies:
16003 - emoji-regex "^8.0.0"
16004 - is-fullwidth-code-point "^3.0.0"
16005 - strip-ansi "^6.0.1"
16006 -
16105 string-width@^5.0.1, string-width@^5.1.2:
16106 version "5.1.2"
16107 resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -16064,7 +16162,7 @@ string_decoder@~1.1.1:
16162 dependencies:
16163 safe-buffer "~5.1.0"
16164
16067 -"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
16165 +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
16166 version "6.0.1"
16167 resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
16168 integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -16092,13 +16190,6 @@ strip-ansi@^5.1.0:
16190 dependencies:
16191 ansi-regex "^4.1.0"
16192
16095 -strip-ansi@^6.0.0, strip-ansi@^6.0.1:
16096 - version "6.0.1"
16097 - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
16098 - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
16099 - dependencies:
16100 - ansi-regex "^5.0.1"
16101 -
16193 strip-ansi@^7.0.1:
16194 version "7.1.0"
16195 resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -17681,7 +17772,7 @@ workerize-loader@^2.0.2:
17772 dependencies:
17773 loader-utils "^2.0.0"
17774
17684 -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
17775 +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
17776 version "7.0.0"
17777 resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
17778 integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -17699,15 +17790,6 @@ wrap-ansi@^6.2.0:
17790 string-width "^4.1.0"
17791 strip-ansi "^6.0.0"
17792
17702 -wrap-ansi@^7.0.0:
17703 - version "7.0.0"
17704 - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
17705 - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
17706 - dependencies:
17707 - ansi-styles "^4.0.0"
17708 - string-width "^4.1.0"
17709 - strip-ansi "^6.0.0"
17710 -
17793 wrap-ansi@^8.1.0:
17794 version "8.1.0"
17795 resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"