grubersjoe/reflow

Support Optional Chaining and Nullish Coalescing

Opened this issue ยท 4 comments

jnv commented

Hi! I would like to use Reflow on our project which uses optional chaining syntax and I run into this error when using reflow:

    ๐Ÿ”ฅ Error: [file].js could not be transpiled. Skipping.
    ๐Ÿ”ฅ Error: [file].js: Support for the experimental syntax 'optionalChaining' isn't currently enabled (50:29):
...
Add @babel/plugin-proposal-optional-chaining (https://git.io/vb4Sk) to the 'plugins' section of your Babel config to enable transformation.

Now, the plugin for optional chaining is setup in our project's babel.config.js file, but I have noticed that Reflow ignores any Babel configuration from the project:

const defaultOptions: TransformOptions = {
babelrc: false,
comments: false,
configFile: false,
plugins: [[reflowPlugin, overwrites.pluginOptions]],
};

Do you have some recommendation how to handle this? I could use reflow as a Babel plugin, but sadly that option is undocumented. Perhaps the Reflow CLI could accept some extra arguments for customization of the default plugins set (e.g. explicitly pass a path to babelrc or add arguments for plugins)?

jnv commented

I have managed to fix this by adding (hardcoding) relevant options to babel-parser configuration in my fork: jnv@aa43fca

I have also added tests and it seems to work without breaking existing fixtures (I have a problem with TypeScript's ts(2589) error, but that seems to be irrelevant to these changes) โ€“ see the diff of my fork. Would you be interested in PR?

jnv commented

Okay, I was a bit too optimistic. Looks like it is failing on formatting with error Expression expected.; this is likely due to older version of Prettier used in prettier-reflow, since stage 3 syntax features should be supported since Prettier 1.19 per prettier/prettier#6595

So perhaps I should open a PR there first.

Thank's for the feedback! I agree that the syntax plugins used inside of this plugin should somehow be configurable. The idea to add an option, where the path to a babelrc file can be passed to Reflow to add other syntax plugins sounds good to me ๐Ÿ‘.

For now I've updated the Prettier fork and added support for optional chaining and nullish coalescence by hand, but I will look into a more flexible approach in the next days. Please check out v0.4.0.

@grubersjoe since you never know what babel version and plugins someone is using, it's more reliable to just load @babel/core from the project directory and use it to parse the file:

import fs from 'fs-extra'
import path from 'path'
import _resolve from 'resolve'
import { promisify } from 'util'
const resolve = promisify(_resolve)

async function transform(file: string) {
  const basedir = path.dirname(file)
  const babel = require(await resolve('@babel/core', { basedir }))

  const code = await fs.readFile(file, 'utf8')
  const ast = await babel.parseAsync(code, {
    filename: file
    cwd: basedir,
  })

  // transform the ast...
}

This also gets the applicable babelrc file in a parent directory, which might not work correctly if you have to pass a single babelrc path to Reflow. It's not worth reimplementing any of the logic in @babel/core.

I've made a lot of CLIs and VSCode extensions that do codemods this way. It's basically either this approach or use flow-parser (when code has to be compatible with flow) and use something else to traverse the flow AST.

I could make a PR to do this eventually!