Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create React App: Transpile JSX of external package in node_modules

I'm trying to use the react-scratchblocks package on my react project.

I've created my project using the create-app-react command.

After importing the package I got the following error:

Failed to compile.

./node_modules/react-scratchblocks/src/Scratchblocks.js
SyntaxError: /Users/jorge/Documents/React/elimu-analyzer-frontend/node_modules/react-scratchblocks/src/Scratchblocks.js: Unexpected token (45:6)

  43 |     const scripts = this.parseScripts(this.props.code);
  44 |     return (
> 45 |       <div className={this.classNames()}>
     |       ^
  46 |         {scripts.map((script,i) => (
  47 |           <Scratchblock key={i} className="script" script={script}/>
  48 |         ))}

I know that the jsx it's not been recognized, but what should I do to make this package work? Remember: i've used the create-rect-app to create my React project.

Thanks.

UPDATE 1:

module.exports = function () {

return {
    overrides: [{
        test: ["./node_modules/react-scratchblocks"],
        presets: ["@babel/preset-react"]
    }],
};
}

UPDATE 2: Component that where I import the react-scratchblocks.

import React, { useState } from 'react';
import { withRouter } from 'react-router-dom';
import './styles.css';
import Fire from '../../config/Fire';
import Realtime from '../Realtime';
import Scratchblocks from 'react-scratchblocks'

function Content(props) {

const [menuOption, setMenuOption] = useState(1);

async function logout() {
    await Fire.logout();
    props.history.push('/');
    console.log('oi');
}

if (menuOption === 0) {
    return (
        <div class='content'>
            <Realtime />
        </div>
    );
}
else if (menuOption === 1) {
    return (
        <div class="content">
            <button onClick={logout}> OUTRA OPÇÃO </button>
        </div>
    );
}
}

export default withRouter(Content);
like image 707
Jorge Nachtigall Avatar asked Sep 02 '19 19:09

Jorge Nachtigall


2 Answers

Create React App (CRA) only transpiles standard JavaScript syntax inside node_modules.

This does not include JSX compilation. Package react-scratchblocks errors due to untranspiled JSX:

SyntaxError: .../Scratchblocks.js: Unexpected token (45:6)

Statement from maintainers (link):

We only compile valid JavaScript syntax in node_modules. JSX is not valid JavaScript. You are using JSX there.

The reason we've taken this stance is because compiling non-standard syntax tightly couples libraries to build tools.

It's also hard to draw a line once you allow experimental things. Most people will want to use not just JSX, but also experimental transforms like class properties. Or decorators. Now we have to argue with every library maintainer about which transforms we want to support, and which we don't.

Hence package authors would have needed to transpile JSX them selves before distribution.

To transpile JSX manually1 you can apply the Babel React preset to react-scratchblocks inside node_modules:

babel node_modules/react-scratchblocks \
  -d node_modules/react-scratchblocks/dist \
  --presets=@babel/preset-react

The build step might be outsourced into its own config file (transpile.js):

module.exports = {
  ...
  overrides: [
    {
      test: ["./node_modules/react-scratchblocks"],
      presets: ["@babel/preset-react"]
    }
  ]
};
babel node_modules/react-scratchblocks \
  -d node_modules/react-scratchblocks/dist \
  --config-file ./transpile.js

Then adjust main entry inside node_modules/react-scratchblocks/package.json to point to the previously transpiled version in dist:

"main": "dist/index.js",

patch-package can further automate the process of fixing broken packages.

Remember this is only a workaround - the duty is on package developers to distribute an npm package with standard JavaScript features.

1 A different alternative would be to adjust Webpack config (only possible with ejected CRA).

like image 187
ford04 Avatar answered Oct 06 '22 08:10

ford04


I personally suggest you to use craco (see @craco/craco)

Craco is a powerful tool that allows you to edit built-in create-react-app configuration without forcing you to eject the project.

How to install it

  • run npm install @craco/craco --save-dev
  • run npm install craco-babel-loader --save-dev
  • create craco.config.js in the root folder of the project
  • update the scripts in package.json:
    • react-scripts start -> craco start
    • react-scripts build -> craco build
    • ...
  • this is the content of the configuration craco file
const path = require('path')
const fs = require('fs')
const cracoBabelLoader = require('craco-babel-loader')

// manage relative paths to packages
const appDirectory = fs.realpathSync(process.cwd())
const resolvePackage = relativePath => path.resolve(appDirectory, relativePath)

module.exports = {
  plugins: [
    {
      plugin: cracoBabelLoader,
      options: {
        includes: [
          resolvePackage('node_modules/package-to-transpile'),
          resolvePackage('node_modules/another-package-to-transpile'),
        ],
      },
    },
  ],
}

You can change a lot of other configurations, and I suggest you to give a look at craco npm package page

Credtis: https://stackoverflow.com/a/58603207/4277948

like image 33
70ny Avatar answered Oct 06 '22 09:10

70ny