Why is webpack not generating chunks from my dynamic imports?
Asked Answered
H

3

9

I had ended up doing some refactoring around routes to allow for code splitting but following the react/webpack instructions I've still got just the 2 entry bundles being generated.

Index.tsx

import React from "react"
import { render } from "react-dom"
import { Provider } from "react-redux"
import { store } from "services/configureStore"
import { ConnectedApp } from "src/App"
import { ConnectedFeatureToggleProvider } from "./components/AppWrapper/FeatureToggleProvider"

const renderApp = () => {
  render(
    <Provider store={store}>
      <ConnectedFeatureToggleProvider>
        <ConnectedApp />
      </ConnectedFeatureToggleProvider>
    </Provider>,
    document.querySelector("#app"),
  )
}

// run app when FIT Core functions are ready
window.onFitCoreReady = () => {
  renderApp()
}

App.tsx

import React, { useEffect, Suspense } from "react"
import { hot } from "react-hot-loader/root"
import { connect } from "react-redux"
import { Switch, Redirect, Route, Router } from "react-router-dom"
// import { ConnectedEconomyManager } from "modules/economyManager/EconomyManager"
import { ConnectedPlayerAccounts } from "modules/playerAccountDataManager/PlayerAccounts"
import { HealthDashboard } from "modules/healthDashboard/HealthDashboard"
import { PackSimulator } from "modules/packSimulator/PackSimulator"

const mapStateToProps = (state: GlobalState) => ({
  ...
})

const mapDispatchToProps = (dispatch: Dispatch) => ({
  ...
})

type Props = {
}

const ConnectedEconomyManager = React.lazy(() => import("modules/economyManager/EconomyManager"))

export const App = ({
}: Props) => {
  return (
      <Router history={history}>
          <Suspense fallback={<span>LOADING LOADING LOADING</span>}>
            <Switch>
              <Redirect
                exact
                from="/marketplace/"
                to={{
                  pathname: "/marketplace/economy",
                  search: window.location.search,
                }}
              />
              <Route path="/marketplace/economy" component={ConnectedEconomyManager} />
              <Route path="/marketplace/playerAccounts" component={ConnectedPlayerAccounts} />
              <Route path="/marketplace/health" component={HealthDashboard} />
              <Route path="/marketplace/packSimulator" component={PackSimulator} />
            </Switch>
          </Suspense>
      </Router>
  )
}

export const ConnectedApp = hot(connect(mapStateToProps, mapDispatchToProps)(App))

webpack/local.js

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const merge = require('webpack-merge');
const _ = require('lodash');
const common = require('./common');
const path = require('path');

const open = process.env.npm_package_config_WEBPACK_OPEN_WINDOW === 'true';
const host = process.env.npm_package_config_WEBPACK_LOCAL_HOST;
const port = process.env.npm_package_config_WEBPACK_PORT;
const ROOT_DIR = path.resolve(__dirname, '../');
const APP_DIR = path.resolve(ROOT_DIR, 'src');

module.exports = env => {
  if (!env) {
    // Prevent references to 'undefined'
    env = {};
  }
  return merge.smart(common, {
    mode: 'development',
    devServer: {
      disableHostCheck: true,
      port: '443',
      historyApiFallback: true,
      open: open ? 'Google Chrome' : open, // auto-open in browser
      openPage: 'marketplace/economy?project=' + projectName,
    },
    devtool: 'eval-source-map',
    module: {
      rules: [
        _.merge(
          _.find(common.module.rules, rule => rule.use && rule.use.loader === 'babel-loader'),
          { use: { options: { plugins: ['@babel/plugin-syntax-dynamic-import', 'babel-plugin-styled-components', '@babel/plugin-proposal-class-properties'] } } }
        ),
        {
          test: /\.css$/,
          use: ['style-loader', 'css-loader'],
        },
        { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
        { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
      ],
    },
    plugins: [
      // copies the index.html file to the build directory:
      new HtmlWebpackPlugin({ 
        template: `${APP_DIR}/index.html`,
        templateParameters: {
          ...define
        }
      }),
    ],
  });
}

webpack/common.js

const path = require('path');
const webpack = require('webpack');

const ROOT_DIR = path.resolve(__dirname, '../');
const BUILD_DIR = path.resolve(ROOT_DIR, 'dist');
const APP_DIR = path.resolve(ROOT_DIR, 'src');

module.exports = {
  entry: {
    main: [
      `${APP_DIR}/index.tsx`, // main entry point to the application
    ],
    semantic: path.resolve(ROOT_DIR, 'semantic-theme', 'semantic.less'),
  },
  module: {
    rules: [
      {
        test: /\.[j|t]sx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [['@babel/preset-env', { useBuiltIns: 'entry', corejs: '3.0.0' }], '@babel/preset-react'],
            overrides: [
              {
                test: /\.tsx?$/,
                presets: [['@babel/preset-env', { useBuiltIns: 'entry', corejs: '3.0.0' }], '@babel/preset-react', '@babel/preset-typescript'],
              },
            ],
          },
        },
        include: APP_DIR,
      },names
      {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        use: 'url-loader?limit=10000&mimetype=application/font-woff',
      },
      {
        test: /\.(ttf|otf|eot|svg|png|jpe?g|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        use: 'file-loader',
      },
    ],
  },
  output: {
    path: `${BUILD_DIR}`,
    filename: '[name].[hash].js',
    chunkFilename: '[name].[hash].js',
    publicPath: '/',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    modules: [
      ROOT_DIR,
      APP_DIR,
      'node_modules',
    ],
    alias: {
      // tell semantic-ui-less to use our theme config
      '../../theme.config$': path.resolve(ROOT_DIR, 'semantic-theme', 'theme.config'),
      'react-dom': '@hot-loader/react-dom',
    },
  },
  stats: { colors: true },
};

tsconfig.json

{
  "compilerOptions": {
      "plugins": [
        {
          "name": "typescript-styled-plugin"
        }
      ],
      "noEmit": true,
      "strict": true,
      "sourceMap": true,
      "noImplicitAny": false,
      "noUnusedLocals": true,
      "module": "esnext",
      "target": "esnext",
      "lib": [
        "esnext",
        "dom"
      ],
      "moduleResolution": "node",
      "jsx": "preserve",
      "allowSyntheticDefaultImports": true,
      "resolveJsonModule": true,
      "baseUrl": ".",
      "paths": {
        "components/*": ["src/components/*"],
        "modules/*": ["src/modules/*"],
        "services/*": ["src/services/*"],
      },
      "types": [
        "react",
        "jest",
      ]
  },
  "include": [
    "./src/**/*",
    "./@types/**/*",
  ],
}

I expected a new chunk to be generated for the EconomyManager lazy import but the build only generates a main.[hash].js and semantic.[hash].js. Where am I going wrong here?

I checked and EconomyManager exports are not being referenced anywhere else in the application as I thought that might be it.

Hardboard answered 29/11, 2019 at 22:25 Comment(0)
L
8

@babel/preset-env might be transpiling your dynamic imports to deferred requires, this will prevent Webpack from knowing where to code split.

We will need to exclude the plugin @babel/plugin-proposal-dynamic-import so your import() statements are preserved. Try adding the exclude field to your @babel/preset-env options in your Webpack config.

presets: [['@babel/preset-env', { useBuiltIns: 'entry', corejs: '3.0.0', exclude: ['proposal-dynamic-import'] }]

This is described in similar GitHub Issues:

Lowis answered 24/8, 2020 at 13:3 Comment(0)
E
0

If you set the 'moduleResolution' property to 'NodeNext' in tsconfig, it creates.

  "compilerOptions": {
    "moduleResolution": "NodeNext",
    "module": "commonjs",
  }
End answered 2/1, 2023 at 10:22 Comment(0)
I
0

I ran into this problem, and after making tsconfig.json changes described in other answers and at https://github.com/webpack/webpack/issues/5703, I still wasn't getting separate chunks. For me the problem was that I was using SplitChunksPlugin to separate out common chunks, which was getting the dynamically imported modules. The solution to having both dynamically imported chunks and common chunks is to use the chunks: 'initial' option for common chunks:

https://webpack.js.org/plugins/split-chunks-plugin/#splitchunkschunks

Inter answered 25/4 at 17:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.