diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 90651818c7..4a6fdeacf5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -196,10 +196,18 @@ function runWithEnvironment( if (env.enableValidations) { if (env.config.validateHooksUsage) { - validateHooksUsage(hir).unwrap(); + if (env.outputMode === 'lint') { + env.logErrors(validateHooksUsage(hir)); + } else { + validateHooksUsage(hir).unwrap(); + } } if (env.config.validateNoCapitalizedCalls) { - validateNoCapitalizedCalls(hir).unwrap(); + if (env.outputMode === 'lint') { + env.logErrors(validateNoCapitalizedCalls(hir)); + } else { + validateNoCapitalizedCalls(hir).unwrap(); + } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 80caee2caf..79269d25a7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -696,7 +696,7 @@ export class Environment { return this.#scope; } - logErrors(errors: Result): void { + logErrors(errors: Result): void { if (errors.isOk() || this.logger == null) { return; } diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/PluginTest-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/PluginTest-test.ts index e9fe8e001b..eeec23048d 100644 --- a/compiler/packages/eslint-plugin-react-compiler/__tests__/PluginTest-test.ts +++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/PluginTest-test.ts @@ -57,7 +57,6 @@ testRule('plugin-recommended', TestRecommendedRules, { ], invalid: [ { - // TODO: actually return multiple diagnostics in this case name: 'Multiple diagnostic kinds from the same function are surfaced', code: normalizeIndent` import Child from './Child'; @@ -70,6 +69,7 @@ testRule('plugin-recommended', TestRecommendedRules, { `, errors: [ makeTestCaseError('Hooks must always be called in a consistent order'), + makeTestCaseError('Capitalized functions are reserved for components'), ], }, { diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/Repro35394-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/Repro35394-test.ts new file mode 100644 index 0000000000..6d6d9d909e --- /dev/null +++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/Repro35394-test.ts @@ -0,0 +1,63 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {normalizeIndent, TestRecommendedRules, testRule} from './shared-utils'; + +testRule('repro-35394', TestRecommendedRules, { + valid: [], + invalid: [ + { + name: '#35394 repro - multiple violations with useState in nested component', + code: normalizeIndent` + import {useState, useEffect} from 'react'; + export default function App() { + const [foo, setFoo] = useState(false); + + useEffect(() => { + setFoo(!foo); + }, []); + + const ViolateStaticComponents = () => { + const [counter, setCounter] = useState(0); + return

Hello, World!

; + }; + + return ( + <> + + + ); + } + `, + errors: 3, + }, + { + name: 'Without nested useState - should show 2 errors', + code: normalizeIndent` + import {useState, useEffect} from 'react'; + export default function App() { + const [foo, setFoo] = useState(false); + + useEffect(() => { + setFoo(!foo); + }, []); + + const ViolateStaticComponents = () => { + return

Hello, World!

; + }; + + return ( + <> + + + ); + } + `, + errors: 2, + }, + ], +});