Skip to content

LSP server fails to parse files that use modern Flow syntax #80

Description

@jonreading81

Summary

The LSP server can't parse files that use modern Flow syntax, so no markers appear on those files. The failure comes from the Babel Flow parser used before babel-plugin-react-compiler is invoked — it lags behind Flow's own parser (hermes-parser) and is missing several years of Flow language additions.

Reproduction

Any file that uses readonly in a Flow type annotation triggers it:

```js
// @flow strict-local
import { useState } from 'react';

type Props = {
ref?: React.RefObject<{ readonly closeMenu: () => void } | null>,
};

const MyComponent = ({ ref }: Props): React.Node => {
const [open, setOpen] = useState(false);
return <div onClick={() => setOpen(!open)} />;
};

export default MyComponent;
```

Observed behaviour

Extension output log:

```
SERVER ERROR: Failed to compile the file. Please check the file content.
Unexpected token, expected ":" (7:37)
SERVER LOG: Process inlay hints for file:///.../MyComponent.jsx
```

No marker on the component. In our codebase this blocks the extension on a large fraction of files, because we use modern Flow syntax throughout.

Other constructs that trip it

  • `readonly` / `writeonly` in Flow object types
  • `component Foo() renders React.Node {}` declarations
  • `hook useFoo() {}` declarations
  • `renders T` type operator
  • `match (x) { … }` pattern matching
  • `` bounded generics
  • `keyof T`, `T[K]`, `x is T` type guards

All valid Flow (parse fine under `hermes-parser`), all rejected by Babel's Flow parser.

Suggested fix

Run SWC's Flow parser as a pre-step, then feed the stripped output to `babel-plugin-react-compiler`. SWC now has full support for modern Flow — including the newer `component`, `hook`, `renders`, and pattern-matching constructs — via:

```js
import { transform } from '@swc/core';

const stripped = await transform(source, {
filename,
jsc: {
parser: {
syntax: 'flow',
jsx: true,
components: true, // component/hook/renders
patternMatching: true, // match statements
},
target: 'esnext',
transform: { react: { runtime: 'preserve' } },
},
module: { type: 'es6' },
});
```

This is the same approach Vite pipelines use (see e.g. Meta's `swc-flow-relay` plugin). It's fast and needs no per-project configuration.

Environment

  • Extension: `blazejkustra.react-compiler-marker` 2.2.0
  • VS Code: 1.116.0
  • macOS
  • `babel-plugin-react-compiler`: 1.0.0
  • Flow: 0.321.0

Happy to open a PR if the SWC approach sounds right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions