rails / webpacker

Use Webpack to manage app-like JavaScript modules in Rails
MIT License
5.31k stars 1.47k forks source link

Typescript + React + react-hot-loader #1959

Closed Razinsky closed 4 years ago

Razinsky commented 5 years ago

Hi, I'm desperately trying to find an example of Webpacker integration with Typescript, React and the most recent version of react-hot-loader. They suggests to use the babel-loader to transpile Typescript. Have someone succeed in doing that recently? A configuration example would be much appreciated.

Thanks!

rcchen commented 5 years ago

Hey, I've had success with this setup (leans on Babel for TS transpilation rather than ts-loader)

Install the following:

yarn add @babel/preset-react @babel/preset-typescript typescript --dev

In .babelrc

{
    "presets": ["@babel/react", "@babel/typescript"]
}

(alternatively, this can also be configured in babel.config.js in your root folder).

In config/webpack/environment.js

const { environment } = require('@rails/webpacker')

const babelLoader = environment.loaders.get("babel");
babelLoader.test = /\.(js|jsx|ts|tsx|mjs)?(\.erb)?$/;

module.exports = environment

And then in my root component:

import * as React from "react";
import { hot } from "react-hot-loader/root";

class ColdApp extends React.PureComponent {
    public render() {
        return (
            <div>Hello world from the hot app.</div>
        );
    }
}

export const App = hot(ColdApp);

Give that a shot and let me know if I'm missing anything.

dipakchandranp commented 4 years ago

@rcchen After setting babel loader test as following

const { environment } = require("@rails/webpacker");
const erb = require("./loaders/erb");

environment.loaders.append("erb", erb);

const babelLoader = environment.loaders.get("babel");
babelLoader.test = /\.(js|jsx|ts|tsx|mjs)?(\.erb)?$/;

module.exports = environment;

getting the following error while running a webpack-dev-server:

ERROR in ./engines/platform/fundraiser/app/javascript/modules/admin_campaign_intro/index.js 10:2
Module parse failed: Unexpected token (10:2)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| 
| ReactDOM.render(
>   <Provider store={store}>
|     <AdminCampaignIntroContainer />
|   </Provider>,
jakeNiemiec commented 4 years ago

You may need an appropriate loader to handle this file type

Looks like babel isn't setup to handle JSX, have you run https://github.com/rails/webpacker#react? If so, can you post your babel.config.js?

ralampay commented 4 years ago

I'm having similar issues. In my application.js, I have a line that imports a custom js component in react from a different directory (since I'm migrating from a Rails 5 project). The import is a simple:

import MainUI from "../../../react/dashboard/MainUI";

My package.json:

{
  "name": "koins",
  "private": true,
  "dependencies": {
    "@babel/core": "^7.9.6",
    "@babel/preset-env": "^7.9.6",
    "@babel/preset-react": "^7.9.4",
    "@babel/preset-typescript": "^7.9.0",
    "@coreui/coreui": "^3.2.0",
    "@fortawesome/fontawesome-free": "^5.13.0",
    "@popperjs/core": "^2.4.0",
    "@rails/activestorage": "^6.0.3-1",
    "@rails/ujs": "^6.0.3-1",
    "@rails/webpacker": "5.1.1",
    "@types/react": "^16.9.35",
    "@types/react-dom": "^16.9.8",
    "babel-loader": "^8.0.4",
    "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
    "bootstrap": "^4.3.1",
    "chart.js": "^2.9.3",
    "core-js": "^3.6.5",
    "css-loader": "^1.0.0",
    "datatables.net": "^1.10.19",
    "datatables.net-bs4": "^1.10.19",
    "datatables.net-fixedheader": "^3.1.5",
    "datatables.net-fixedheader-bs4": "^3.1.5",
    "jquery": "^3.4.1",
    "jquery-mousewheel": "^3.1.13",
    "moment": "^2.22.2",
    "mustache": "^3.0.0",
    "node-notifier": "^5.3.0",
    "node-sass": "^4.14.1",
    "perfect-scrollbar": "^1.5.0",
    "popper.js": "^1.16.1",
    "prop-types": "^15.7.2",
    "react": "^16.13.1",
    "react-datepicker": "^1.6.0",
    "react-dom": "^16.13.1",
    "react-modal": "^3.6.1",
    "react-select": "^2.1.0",
    "react-table": "^6.8.6",
    "react-toggle": "^4.0.2",
    "react_ujs": "^2.6.1",
    "recharts": "^2.0.0-beta.1",
    "regenerator-runtime": "^0.13.5",
    "sass-loader": "^8.0.2",
    "select2": "^4.0.13",
    "signature_pad": "^3.0.0-beta.3",
    "style-loader": "^1.2.1",
    "terser-webpack-plugin": "^2.3.5",
    "typescript": "^3.9.3",
    "victory": "^31.0.1",
    "yarn": "^1.22.0"
  },
  "devDependencies": {
    "webpack-build-notifier": "^0.1.30",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.11.0",
    "webpack-notifier": "^1.7.0"
  },
  "engines": {
    "node": "12.13.0"
  },
  "scripts": {
    "build": "webpack -p; cp config/settings/production.yml.dist config/settings/production.yml"
  }
}

My babel.config.js:

module.exports = function(api) {
  var validEnv = ['development', 'test', 'production']
  var currentEnv = api.env()
  var isDevelopmentEnv = api.env('development')
  var isProductionEnv = api.env('production')
  var isTestEnv = api.env('test')

  if (!validEnv.includes(currentEnv)) {
    throw new Error(
      'Please specify a valid `NODE_ENV` or ' +
        '`BABEL_ENV` environment variables. Valid values are "development", ' +
        '"test", and "production". Instead, received: ' +
        JSON.stringify(currentEnv) +
        '.'
    )   
  }

  return {
    presets: [
      isTestEnv && [
        '@babel/preset-env',
        {
          targets: {
            node: 'current'
          }
        }
      ],  
      (isProductionEnv || isDevelopmentEnv) && [
        '@babel/preset-env',
        {
          forceAllTransforms: true,
          useBuiltIns: 'entry',
          corejs: 3,
          modules: false,
          exclude: ['transform-typeof-symbol']
        }
      ],  
      [   
        "@babel/preset-react",
        {
          development: isDevelopmentEnv || isTestEnv
        }
      ]   
    ].filter(Boolean),
    plugins: [
      'babel-plugin-macros',
      '@babel/plugin-syntax-dynamic-import',
      isTestEnv && 'babel-plugin-dynamic-import-node',
      '@babel/plugin-transform-destructuring',
      [   
        '@babel/plugin-proposal-class-properties',
        {
          loose: true
        }
      ],  
      [   
        '@babel/plugin-proposal-object-rest-spread',
        {
          useBuiltIns: true
        }
      ],  
      [   
        '@babel/plugin-transform-runtime',
        {
          helpers: false,
          regenerator: true,
          corejs: false
        }
      ],  
      [   
        '@babel/plugin-transform-regenerator',
        {
          async: false
        }
      ]   
    ].filter(Boolean)

To test, I try to run ./bin/webpack to reproduce the error:

ERROR in ./react/dashboard/MainUI.js 34:6
Module parse failed: Unexpected token (34:6)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| 
|     dashboards.push(
>       <DashboardManagement
|         key={"dashboard-Management"}
|       />
 @ ./app/javascript/packs/application.js 225:0-53
ralampay commented 4 years ago

Ok I think I solved my own problem. Now it understands the js stuff inside the react directory. Solution was to include the react directory in resolved_paths array in webpack.yml.