Latest Sage 10 no longer respecting JSX syntax

I just created a new Sage 10 project using composer create-project roots/sage your-theme-name dev-main. I then installed React using yarn add -D react, and edited resources/scripts/editor.js so that I could try registering a new block variation, which involves inserting an SVG using JSX syntax:

import React from 'react';

...

  wp.blocks.registerBlockVariation(
    'core/columns', {
      name: 'four-columns',
      title: 'Four columns; equal split',
      icon: <svg …>…</svg>,   // Shortened here for brevity
      scope: ['block'],
      innerBlocks: [
        ['core/column'],
        ['core/column'],
        ['core/column'],
        ['core/column'],
      ],
    }
  );

When I try to build the project, there is a parsing error and the project can’t be compiled:

Parsing error: This experimental syntax requires enabling one of the following
parser plugin(s): "jsx", "flow", "typescript".

I see that in this latest iteration of Sage 10, the .eslintrc.js file has been significantly shortened, and inherits basically all its settings from @roots/sage/eslint-config. I’m not sure if eslint is what’s outputting this error, or babel, or something else.

I have another Sage 10 project that I built a few months ago (Laravel Mix only, no Bud), and there’s no problem using JSX in that project.

I feel like perhaps I just don’t know what steps are required to get the project to recognize JSX, although I’m also surprised it doesn’t just work out of the box, considering editor.js is meant to be loaded specifically in the context of WordPress’s React-based block editor.

to be fair to us, mix doesn’t offer first-party support for eslint at all, and even the community plugin doesn’t offer common presets as a nice-to-have. Thanks for bearing with us.

@roots/sage is supposed to inherit the eslint rules that allow for jsx from @roots/bud-preset-wordpress, and I’m not sure why it isn’t. but, you can add the rules from @roots/bud-preset-wordpress directly:

module.exports = {
  root: true,
  extends: [
    require.resolve('@roots/bud-preset-wordpress/eslint-config'),
    require.resolve('@roots/sage/eslint-config'),
  ],
};

It’s set up that way to make it more manageable for us to maintain multiple eslint configurations for different use cases. @roots/sage is essentially this stack:

  • @roots/sage/eslint-config
    • @roots/bud-preset-wordpress/eslint-config
      • @roots/bud-react/eslint-config
      • @roots/bud-preset-recommend/eslint-config
        • @roots/bud-babel/eslint-config
        • @roots/bud-eslint/eslint-config

It might be a minor annoyance right now but it’s of obvious benefit to have sage’s eslint config be composed from individual parts rather than defined as a big block.

sidenote: you might consider using the webpack externals set up for your project instead of the global wp:

import {
  registerBlockVariation,
} from '@wordpress/blocks';

registerBlockVariation('core/columns', {
  name: 'four-columns',
  title: 'Four columns; equal split',
  icon: <svg></svg>,
  scope: ['block'],
  innerBlocks: [
    ['core/column'],
    ['core/column'],
    ['core/column'],
    ['core/column'],
  ],
});

It will be transpiled to the wp window var, so it doesn’t add anything to your bundle size. And with that in place you can install @wordpress/blocks in your project and get better feedback when authoring your script.

2 Likes

Hey @kellymears, thanks for your quick reply.

I tried adding require.resolve('@roots/bud-preset-wordpress/eslint-config') to the list of extends in .eslintrc.js, and it made no difference. Both VS Code and bud build/bud dev think the line containing the JSX syntax has that parsing error.

Did adding that line to .eslintrc.js get it working for you, though? I’m wondering what I must still be missing… are there any other Node packages I’m supposed to install?

Since this was a bit of a blocker on my project, I found a workaround, which might be helpful for anyone else trying to use JSX in their Sage 10 project: I installed html-react-parser, which converts a string of HTML markup to a React component, and it did the trick.

Also, thanks for the tip in avoiding using the wp global. I actually habitually do import everything from @wordpress/* normally, I had just copy-pasted that snippet from another site and forgot to fix that up. :slight_smile:

it should “just work”. can you try reversing them?

module.exports = {
  root: true,
  extends: [
    require.resolve('@roots/sage/eslint-config'),
    require.resolve('@roots/bud-preset-wordpress/eslint-config'),
  ],
};

you shouldn’t need to modify src to appease a linter.

worst case add the react specific stuff in manually:

  extends: ['plugin:react/recommended'],
  plugins: ['react-hooks', 'jsx-a11y'],
  settings: {
    react: {
      version: 'detect',
    },
  },
1 Like

The first of your tips – reversing those lines – did the trick, @kellymears, thanks for that!

Unfortunately, that didn’t convince VS Code’s eslint server that JSX is allowed, and I even tried manually adding those lines as you suggested. I don’t quite understand the ins & outs of how VS Code’s instance of eslint runs differently, as normally it’s totally consistent with the build output. I can tell it’s properly reading that .eslintrc.js file, too, as it immediately lints my code differently if I start deleting lines in it… anyway, not a big deal, I can live with a few lint errors in VS Code.

helpful to know that it’s so dependent on ordering. thanks @farmerpaul!

Actually, I take that back, sorry @kellymears!

For some reason, after I made that change, it did build successfully once, but in trying to copy the same fix to another project, it wouldn’t work there. Then I realized it had something to do with the bud cache, so I now ensure I do a bud clean before every bud build. When I clean the cache and build, those suggestions of yours don’t seem to make any difference. It still insists I haven’t installed the jsx plugin.

I’m banging my head against the wall here, trying all different kinds of combinations of .eslintrc.js settings, but I’m honestly shooting in the dark at this point. I have no real idea what each of those different config lines means.

Here’s my .eslintrc.js file as it stands now, which is still causing the same error to be spewed out:

module.exports = {
  root: true,
  extends: [
    require.resolve('@roots/sage/eslint-config'),
    'plugin:react/recommended',
  ],
  plugins: ['react', 'jsx', 'react-hooks', 'jsx-a11y'],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
};

For eslint to even recognize the jsx plugin (which maybe is redundant, I dunno), I had to also manually run yarn add -D eslint-plugin-jsx, since it said that package was missing when I just tried to add jsx to the list of plugins. But yeah, still doesn’t matter. It insists that JSX is experimental and I haven’t properly installed the modules.

I’d be so grateful if someone could try this out, when they have a chance (@kellymears, or whoever has the talent and time), as I’m just spinning my wheels:

  1. composer create-project roots/sage test-theme dev-main
  2. cd test-theme && yarn add -D react
  3. Add something like const someSvg = <svg></svg> to resources/scripts/editor.js.
  4. Run yarn run bud clean && yarn build and see it probably blow up.
  5. Fix .eslintrc.js until step 4 succeeds. :slightly_smiling_face:

Bummed/happy to report I can reproduce this (bummed for myself; happy that you know you’re not losing your grip).

I updated my PR which exports all of the eslint config modules out of a singular @roots/eslint-config package. Hopefully can get that merged quick and have you back to a place where you can just do overrides as designed.

Until then, you have all the modules and everything you need available to you to configure eslint for yourself – there is no need to install anything extra. Here is (roughly) what @roots/eslint-config/sage will export:

module.exports = {
  root: true,
  extends: ['eslint:recommended', 'plugin:react/recommended'],
  plugins: ['import', 'react', 'react-hooks', 'jsx-a11y'],
  env: {
    browser: true,
    es6: true,
    node: true,
    jquery: true,
  },
  globals: {wp: true},
  parserOptions: {
    sourceType: 'module',
    ecmaFeatures: {
      globalReturn: false,
      jsx: true,
    },
    ecmaVersion: 2020,
  },
  rules: {
    'no-console': 0,
    'comma-dangle': [
      'error',
      {
        arrays: 'always-multiline',
        objects: 'always-multiline',
        imports: 'always-multiline',
        exports: 'always-multiline',
        functions: 'ignore',
      },
    ],
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
};
1 Like

Thanks for taking a closer look, @kellymears, and coming up with this solution!

1 Like