React Loadable JSON mapping file with components being referenced from multiple locations

2k Views Asked by At

I'm using React Loadable to code split my components, but the problem is that certain components are referenced from multiple locations. For example, I have this component CarsModule, which is referenced in multiple areas like this:

const CarsModule = Loadable({
  loader: () => import(/* webpackChunkName: "CarsModule" */ '../components/CarsModule'),
  loading() {
    return <div>Loading...</div>
  }
});

const CarsModule = Loadable({
  loader: () => import(/* webpackChunkName: "CarsModule" */ '../../../../components/CarsModule'),
  loading() {
    return <div>Loading...</div>
  }
});

const CarsModule = Loadable({
  loader: () => import(/* webpackChunkName: "CarsModule" */ '../../components/CarsModule'),
  loading() {
    return <div>Loading...</div>
  }
});

const CarsModule = Loadable({
  loader: () => import(/* webpackChunkName: "CarsModule" */ '../../../../components/CarsModule'),
  loading() {
    return <div>Loading...</div>
  }
});

What ends up happening in the dist/react-loadable.json file is that CarsModule looks like this:

"../../../../components/CarsModule": [
  {
    "id": 822,
    "name": "./dist/components/CarsModule/index.js",
    "file": "0-78a53ff810acc75b6fde.js",
    "publicPath": "http://localhost:3000/dist/0-78a53ff810acc75b6fde.js"
  }
],

When one of the pages goes to request it during server side rendering that imports it like this '../components/CarsModule', it gets undefined for the module (since the bundle's key is this "../../../../components/CarsModule"). I used the webpackChunkName to try and name those keys in the react-loadable.json file but no luck :(

I have chunk hashing going on inside my webpack prod file which looks like this:

module.exports = {
  devtool: 'source-map',
  context: path.resolve(__dirname, '..'),
  entry: {
    main: [
      'babel-polyfill',
      './dist/app/defaultClient.js'
    ],
  },
  output: {
    path: assetsPath,
    filename: '[name]-[chunkhash].js',
    chunkFilename: '[name]-[chunkhash].js',
    publicPath: STATIC_HOST ? `${STATIC_HOST}/dist/` : `http://localhost:3000/dist/`,
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: 'babel-loader',
      },
      {
        test: /\.scss$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            {
              loader: 'css-loader',
              options: {
                modules: true,
                importLoaders: 2,
                localIdentName: '[folder]-[local]',
                minimize: true,
              },
            },
            {
              loader: 'postcss-loader',
              options: {
                plugins: () => [require('autoprefixer')()],
              },
            },
            {
              loader: 'sass-loader',
              options: {
                outputStyle: 'expanded',
                sourceMap: false,
              },
            },
          ],
        }),
      },
      {
        test: /\.woff2?(\?v=\d+\.\d+\.\d+)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
            mimetype: 'application/font-woff',
          },
        },
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
            mimetype: 'application/octet-stream',
          },
        },
      },
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: 'file-loader',
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
            mimetype: 'image/svg+xml',
          },
        },
      },
      {
        test: webpackIsomorphicToolsPlugin.regular_expression('images'),
        use: {
          loader: 'url-loader',
          options: {
            limit: 10240,
          },
        },
      },
    ],
  },
  resolve: {
    modules: ['node_modules', 'src'],
    extensions: ['.json', '.js', '.jsx'],
  },
  plugins: [
    new CleanPlugin([assetsPath], { root: projectRootPath }),

    // css files from the extract-text-plugin loader
    new ExtractTextPlugin({
      filename: '[name]-[chunkhash].css',
      allChunks: true,
    }),

    new webpack.DefinePlugin({
      'process.env': exportedClientAppConfigProperties,
      __CLIENT__: true,
      __DEVTOOLS__: false,
    }),

    // ignore dev config
    new webpack.IgnorePlugin(/\.\/dev/, /\/config$/),

    // optimizations
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false,
      },
    }),

    new ReactLoadablePlugin({ filename: `./dist/react-loadable.json` }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),

    webpackIsomorphicToolsPlugin,
  ],
};

However, there is no CarsModule.js file that is created. How can I solve this issue with multiple components importing the same module from different locations if react-loadable.json is going to use the import location as a key?

If I remove these other files that are doing different imports from my project, the react-loadable.json file will be generated referencing the CarsModule like so:

"../components/CarsModule": [
  {
    "id": 822,
    "name": "./dist/components/CarsModule/index.js",
    "file": "0-78a53ff810acc75b6fde.js",
    "publicPath": "http://localhost:3000/dist/0-78a53ff810acc75b6fde.js"
  }
],

However, this isn't a very plausible solution because other components still need to reference that CarsModule. Any ideas what I'm doing wrong? Thanks in advance!

Here are my versions of react-loadable & webpack:

"react-loadable": "^5.4.0"
"webpack": "^3.5.6"

Here is my .babelrc file:

{
  "presets": [
    ["env", {
      "targets": {
        "node": "current",
        "browsers": ["last 2 versions", "safari >= 7"]
      }
    }],
    "stage-0",
    "react"
  ],
  "plugins": [
    "dynamic-import-webpack",
    "react-loadable/babel",
    "syntax-async-functions",
    "syntax-dynamic-import",
    "transform-decorators-legacy",
    ["module-resolver", {
      "root": ["./src/"],
    }],
    ["transform-runtime", {
      "helpers": false,
      "polyfill": true
    }]
  ]
}
1

There are 1 best solutions below

0
On

Does adding a module resolver alias to your .babelrc help?

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    [
      "module-resolver",
      {
        "root": [
          "."
        ],
        "alias": {
          "components": "./components",
        }
      }
    ],
    "react-loadable/babel"
  ]
}

That way each loader can use the same path string.

const CarsModule = Loadable({
  loader: () => import(/* webpackChunkName: "CarsModule" */ 'components/CarsModule'),
  loading() {
    return <div>Loading...</div>
  }
});