From 41c23e87c52b958f7c93b0f94940abc50a2ead8d Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 31 Aug 2023 23:16:27 +0100 Subject: [PATCH] Disable compilation of "Components" inside class methods Minimal repro extracted from our internal codebase. Our inference mode sees that this arrow function is component-like and attempts to compile it, which then fails because the function accesses `this` which we bailout on. --- .../src/Entrypoint/Program.ts | 14 +++++++ ...ass-component-with-render-helper.expect.md | 41 +++++++++++++++++++ .../class-component-with-render-helper.js | 14 +++++++ .../packages/sprout/src/SproutTodoFilter.ts | 1 + 4 files changed, 70 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.js diff --git a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts index 52c2b2ad31..cbcf52fe31 100644 --- a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts @@ -270,6 +270,20 @@ export function compileProgram( // Main traversal to compile with Forget program.traverse( { + ClassDeclaration(node: NodePath) { + // Don't visit functions defined inside classes, because they + // can reference `this` which is unsafe for compilation + node.skip(); + return; + }, + + ClassExpression(node: NodePath) { + // Don't visit functions defined inside classes, because they + // can reference `this` which is unsafe for compilation + node.skip(); + return; + }, + FunctionDeclaration( fn: NodePath, pass: CompilerPass diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.expect.md new file mode 100644 index 0000000000..11abaa66ee --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +// @compilationMode(infer) +class Component { + _renderMessage = () => { + const Message = () => { + const message = this.state.message; + return
{message}
; + }; + return ; + }; + + render() { + return this._renderMessage(); + } +} + +``` + +## Code + +```javascript +// @compilationMode(infer) +class Component { + _renderMessage = () => { + const Message = () => { + const message = this.state.message; + return
{message}
; + }; + return ; + }; + + render() { + return this._renderMessage(); + } +} + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.js new file mode 100644 index 0000000000..90cba5b265 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/class-component-with-render-helper.js @@ -0,0 +1,14 @@ +// @compilationMode(infer) +class Component { + _renderMessage = () => { + const Message = () => { + const message = this.state.message; + return
{message}
; + }; + return ; + }; + + render() { + return this._renderMessage(); + } +} diff --git a/compiler/packages/sprout/src/SproutTodoFilter.ts b/compiler/packages/sprout/src/SproutTodoFilter.ts index 9618e38042..985392e954 100644 --- a/compiler/packages/sprout/src/SproutTodoFilter.ts +++ b/compiler/packages/sprout/src/SproutTodoFilter.ts @@ -450,6 +450,7 @@ const skipFilter = new Set([ "infer-function-expression-component", "infer-function-expression-React-memo-gating", "infer-skip-components-without-hooks-or-jsx", + "class-component-with-render-helper", ]); export default skipFilter;