Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is webpack not generating chunks from my dynamic imports?

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.

like image 709
Kyle S Avatar asked Nov 29 '19 22:11

Kyle S


1 Answers

@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:

  • https://github.com/babel/babel/issues/11204
  • https://github.com/babel/babel/issues/10194
like image 176
jamsinclair Avatar answered Oct 06 '22 00:10

jamsinclair