Sentenzo
Sentenzo

Reputation: 39

'Cannot find module' when using dynamic import

I've deleted CRA and configured webpack/babel on my own. Now I have problems with dynamic imports.

error

This works fine:

import("./" + "CloudIcon" + ".svg")
  .then(file => {
  console.log(file);
})

This doesn't work:

const name = 'CloudIcon';

import("./" + name + ".svg")
  .then(file => {
  console.log(file);
})

I've tried to export files with different types. It didn't work. Have tried to use Webpack Magic Comments, but didn't help either.

I suppose, that something is wrong with my webpack/babel settings, but what?

babel.config.js:

const plugins = [
  "@babel/syntax-dynamic-import",
  ["@babel/plugin-transform-runtime"],
  "@babel/transform-async-to-generator",
  "@babel/plugin-proposal-class-properties"
];


if (process.env.NODE_ENV === 'development') {
  plugins.push('react-refresh/babel'); 
}

module.exports = {
  presets: [[
    "@babel/preset-env", {
      "debug": false,
      "modules": false,
      "useBuiltIns": false
    }],
    ['@babel/preset-react', {throwIfNamespace: false}],
    '@babel/preset-typescript'
  ],
  plugins,
};

webpack.config.js:

require('dotenv').config();
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const webpack = require('webpack');

const reactAppVars = (() => {
  const obj = {};
  for (let key in process.env) {
    if (key.startsWith('REACT_APP_')) obj[key] = process.env[key];
  }
  return obj;
})();

const target = process.env['NODE_ENV'] === 'production' ? 'browserslist' : 'web';

const plugins = [
  new webpack.EnvironmentPlugin({'NODE_ENV': process.env['NODE_ENV'], 'PUBLIC_URL': '', ...reactAppVars}),
  new HtmlWebpackPlugin({template: path.resolve(__dirname, '../public/index.html')}),
  new MiniCssExtractPlugin({filename: '[name].[contenthash].css'}),
  new webpack.ProvidePlugin({process: 'process/browser'}),
  new webpack.ProvidePlugin({"React": "react"}),
];

if (process.env['SERVE']) plugins.push(new ReactRefreshWebpackPlugin());

const proxy = {
//Proxy settings
}

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "../build"),
    assetModuleFilename: '[path][name].[ext]'
  },
  plugins,
  devtool: 'source-map',
  devServer: {
    static: {
      directory: path.resolve(__dirname, "../public"),
    },
    proxy,
    port: 9999,
    hot: true,
  },

  module: {
    rules: [
      { test: /\.(html)$/, use: ['html-loader'] },
      {
        test: /\.(s[ac]|c)ss$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.less$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                javascriptEnabled: true
              }
            }
          }
        ]
      },
      {
        test: /\.(png|jpe?g|gif|webp|ico)$/i,
        type: process.env['NODE_ENV'] === 'production' ? 'asset' : 'asset/resource'
      },
      {
        test: /\.svg$/i,
        issuer: /\.[jt]sx?$/,
        use: ['@svgr/webpack', {
          loader: 'file-loader',
          options: {
            name: '[path][name].[ext]'
          }
        }],
      },
      {
        test: /\.(woff2?|eot|ttf|otf)$/i,
        type: process.env['NODE_ENV'] === 'production' ? 'asset' : 'asset/resource'
      },
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
          }
        }
      },
      {
        test: /\.([cm]?ts|tsx)$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              "@babel/preset-env",
              "@babel/preset-react",
              "@babel/preset-typescript",
            ]
          }
        }
      },
      {
        test: /\.md$/,
        loader: "raw-loader"
      },
    ],
  },
  resolve: {
    'roots': [path.resolve('./src')],
    'extensions': ['', '.js', '.jsx', '.ts', '.tsx'],
    extensionAlias: {
      ".js": [".js", ".ts"],
      ".cjs": [".cjs", ".cts"],
      ".mjs": [".mjs", ".mts"]
    },
    fallback: {
      'process/browser': require.resolve('process/browser')
    }
  },
  mode: process.env['NODE_ENV'],
  target
}

Upvotes: 2

Views: 8928

Answers (3)

CyberT33N
CyberT33N

Reputation: 124

import



Dynamic expressions in import()

# Will not work
await import(path)

# Will work
await import('./anyHardcoded/path.js')
await import(/* webpackIgnore: true */ path);
  • The import() must contain at least some information about where the module is located. Bundling can be limited to a specific directory or set of files so that when you are using a dynamic expression - every module that could potentially be requested on an import() call is included. For example, import(./locale/${language}.json) will cause every .json file in the ./locale directory to be bundled into the new chunk. At run time, when the variable language has been computed, any file like english.json or german.json will be available for consumption.



Magic Comments

// Single target
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  /* webpackFetchPriority: "high" */
  'module'
);

// Multiple possible targets
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);



webpackIgnore

- import(/* webpackIgnore: true */ path);

Upvotes: 0

Sentenzo
Sentenzo

Reputation: 39

The problem was the extensions array in webpack

'extensions': ['', '.js', '.jsx', '.ts', '.tsx']

I changed it to this

'extensions': ['.js', '.jsx', '.ts', '.tsx', '...']

And now everything works fine.

Upvotes: 0

Evgeni Dikerman
Evgeni Dikerman

Reputation: 516

What is basically happen that when you have defined import path, webpack will include that code in already existing chunk. When you have a dynamic import (which translated to Regex) it will take all files that correspond to this Regex and place them in a different chunk.

It hard to determine from the details if you serve this additional chunks and the browser can reach them. Network and server logs is a good start.

https://webpack.js.org/guides/code-splitting/#prefetchingpreloading-modules

Upvotes: 4

Related Questions