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

1

7

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
    }]
  ]
}
Mord answered 6/7, 2018 at 18:14 Comment(0)
C
0

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>
  }
});
Compressibility answered 26/9, 2018 at 0:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.