Last active
January 10, 2025 14:06
-
-
Save stevoland/a6cb19ed10c33e420b08b8e5c69b2336 to your computer and use it in GitHub Desktop.
Revisions
-
stevoland revised this gist
Oct 10, 2024 . 2 changed files with 17 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -34,18 +34,24 @@ const BabelPluginReacthookFormNoMemo = declare(({ types: t }) => ({ t.blockStatement([t.returnStatement(function_.node.body)]), ) } if (!t.isBlockStatement(function_.node.body)) { return } const directives = function_.node.body.directives ?? [] const hasManualOptOut = directives.some( (directive) => directive.value.value === 'use no memo', ) if (hasManualOptOut) { return } function_.node.body.directives = [ ...directives, t.directive(t.directiveLiteral('use no memo')), ] }) } }, This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -32,7 +32,8 @@ test('named import', () => { ` expect(transform(code)).toBe(`import { useForm } from 'react-hook-form'; const Component = () => { "use no memo"; const form = useForm(); return null; };`) @@ -50,7 +51,8 @@ test('local alias', () => { expect(transform(code)) .toBe(`import { useForm as useFormLocal } from 'react-hook-form'; const Component = () => { "use no memo"; const form = useFormLocal(); return null; };`) @@ -63,7 +65,8 @@ test('function without block statement', () => { ` expect(transform(code)).toBe(`import { useForm } from 'react-hook-form'; const useThing = () => { "use no memo"; return useForm(); };`) }) -
stevoland renamed this gist
Oct 10, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
stevoland created this gist
Oct 10, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,23 @@ [react-hook-form](https://github.com/react-hook-form/react-hook-form) (as of v7.53) may behave incorrectly when user code is compiled with [react-compiler](https://react.dev/learn/react-compiler). This babel plugin can be applied before the compiler to opt-out all functions which reference `useForm` by inserting the `"use no memo"` directive. Only supports using the named export: ```ts // worky import { useForm } from 'react-hook-form const Component = () => { useForm() return null } // no worky import rhf from 'react-hook-form const Component = () => { rhf.useForm() return null } ``` Requires `@babel/helper-plugin-utils` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,16 @@ module.exports = { presets: [ '@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript', ], plugins: [ './react-hook-form-no-memo', [ 'babel-plugin-react-compiler', { target: '18', }, ], ], }; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,55 @@ const { declare } = require('@babel/helper-plugin-utils') const BabelPluginReacthookFormNoMemo = declare(({ types: t }) => ({ visitor: { Program(programPath, pass) { if (pass.file.opts.filename?.includes('node_modules')) { return } const { bindings } = programPath.scope for (const key in bindings) { const binding = bindings[key] if ( !t.isImportSpecifier(binding.path.node) || !t.isIdentifier(binding.path.node.imported) || !t.isImportDeclaration(binding.path.parentPath?.node) || binding.path.parentPath?.node.source.value !== 'react-hook-form' || binding.path.node.imported.name !== 'useForm' ) { continue } binding.referencePaths.forEach((refPath) => { const function_ = refPath.getFunctionParent() if (!function_) { return } if (!t.isBlockStatement(function_.node.body)) { function_ .get('body') .replaceWith( t.blockStatement([t.returnStatement(function_.node.body)]), ) } const directives = function_.node.body?.directives const hasManualOptOut = directives?.some( (directive) => directive.value.value === 'use no memo', ) if (hasManualOptOut) { return } function_ .get('body') .unshiftContainer('body', t.stringLiteral('use no memo')) }) } }, }, })) module.exports = BabelPluginReacthookFormNoMemo This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,97 @@ import { transformSync } from '@babel/core' import ReactCompiler from 'babel-plugin-react-compiler' import ReactHookFormNoMemo from './react-hook-form-no-memo' const transform = (code) => { const output = transformSync(code, { babelrc: false, configFile: false, filename: 'test.js', plugins: [ ReactHookFormNoMemo, [ ReactCompiler, { target: '18', }, ], ], }) return output.code } test('named import', () => { const code = `import { useForm } from 'react-hook-form' const Component = () => { const form = useForm() return null }; ` expect(transform(code)).toBe(`import { useForm } from 'react-hook-form'; const Component = () => { "use no memo" const form = useForm(); return null; };`) }) test('local alias', () => { const code = `import { useForm as useFormLocal } from 'react-hook-form' const Component = () => { const form = useFormLocal() return null }; ` expect(transform(code)) .toBe(`import { useForm as useFormLocal } from 'react-hook-form'; const Component = () => { "use no memo" const form = useFormLocal(); return null; };`) }) test('function without block statement', () => { const code = `import { useForm } from 'react-hook-form' const useThing = () => useForm() ` expect(transform(code)).toBe(`import { useForm } from 'react-hook-form'; const useThing = () => { "use no memo" return useForm(); };`) }) test('function with manual opt-out', () => { const code = `import { useForm } from 'react-hook-form' const useThing = () => { "use no memo" return useForm() } ` expect(transform(code)).toBe(`import { useForm } from 'react-hook-form'; const useThing = () => { "use no memo"; return useForm(); };`) }) test('function compiled', () => { const code = `import { useForm } from 'something-else' const Component = () => { useForm() return null } ` expect(transform(code)).not.toMatch(/use no memo/) })