Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: Cannot find module with dynamic import #6680

Closed
leohubert opened this issue Mar 5, 2018 · 38 comments
Closed

Error: Cannot find module with dynamic import #6680

leohubert opened this issue Mar 5, 2018 · 38 comments

Comments

@leohubert
Copy link

leohubert commented Mar 5, 2018

Do you want to request a feature or report a bug?

bug

What is the current behavior?

When I try to load dynamic module with import('/path/to/my/module.js').then() it work, but when i try to import module with import(rootPath + '/' + myModuleName + '/index.js').then() it doesn't work.

I got:

 Error: Cannot find module '/Users/hubert_i/Emodyz/launcher-ezgames.eu/modules/test/index.js'.
      at /Users/hubert_i/Emodyz/launcher-ezgames.eu/dist/electron/main.js:10475:9
      at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

If the current behavior is a bug, please provide the steps to reproduce.

What is the expected behavior?

Dynamic import with variable

Please mention other relevant information such as the browser version, Node.js version, webpack version, and Operating System.

webpack => 3.10.0
npm => 5.6.0
yarn => 1.5.1
Node.js => v9.6.1
Operating System => macOs High Sierra

@leohubert leohubert changed the title Dynamic import broken with variab concatenation Dynamic import broken with variable concatenation Mar 5, 2018
@leohubert leohubert changed the title Dynamic import broken with variable concatenation Error: Cannot find module with dynamic import Mar 5, 2018
@ooflorent
Copy link
Member

What is the version of webpack used here?

@leohubert
Copy link
Author

leohubert commented Mar 6, 2018

It's written in the description 😄
webpack 3.10.0

@leohubert
Copy link
Author

It's incomprehensible 😢

If i write this it work

import( '/Users/hubert_i/Emodyz/launcher-ezgames.eu/modules/test/index.js')
  .then((m) => {
    console.log(m.name)
  })
  .catch(err => {
    console.error('Error during loading module: ' + err)
    callback()
  })

but when i write this don't work

const test = '/Users/hubert_i/Emodyz/launcher-ezgames.eu/modules/test/index.js'
import(test)
  .then((m) => {
    console.log(m.name)
  })
  .catch(err => {
    console.error('Error during loading module: ' + err)
    callback()
  })

@ooflorent
Copy link
Member

ooflorent commented Mar 6, 2018

Webpack performs a static analyse at build time. It doesn't try to infer variables which that import(test) could be anything, hence the failure. It is also the case for import(path+"a.js").

If you need truly dynamic imports, you have to restrict it to a known path:

import("./locales/" + locale + ".js")

In the above example, webpack will create a chunk for each JS file in locale/.

@leohubert
Copy link
Author

I would like to load module located in %appdata% in windows or Application Support on macOS how i can do that ?

@akash1210
Copy link

@MrDarkSkil I needed to do something similar. Please explain how you solved it. I want to read some configs present in application.conf in individual boxes.

@mattb290
Copy link

Would this also work for loading a remote module (over http(s)). I currently am trying to load modules located on a remove server (owned by me). When I import them locally it works fine:

loadJs = ( url, name ) => {
  return new Promise( resolve => {
    import(`/test/${name}`)
    .then((module) => {
      resolve(module.default)
    })
  })
}

However when I try to import over http it fails (no request reaches the backend at all):

import(`http://localhost:3001/api/uicomponents/one/${name}`)

Error: Unhandled Rejection (Error): Cannot find module 'http://localhost:3001/api/uicomponents/one/[name].js'.

@jeremy-coleman
Copy link

Also noteworthy, The behavior is different for named and default exports , which isnt only limited to webpack. You can export as both using ‘export { mymodule as default, mymodule}’

@ghost
Copy link

ghost commented Jul 16, 2018

what @mattb290 said would be a useful addition now that modules are natively supported in browsers with <script type="module"> and people are going to release/load them all over the internet

any progress?

@x5engine
Copy link

This isn't dynamic import if we can load dynamically files depending on our variables otherwise why would I even use this I could just import all the files at the top!!

Seriously @ooflorent ?

@ooflorent
Copy link
Member

@meteorplus you can import files dynamically. You only need a static prefix.

why would I even use this I could just import all the files at the top!!

Because it will reduce the main bundle size.

@muradm
Copy link

muradm commented Nov 1, 2018

@ooflorent, what if I don't want to reduce bundle size, but load another webpack packed bundle with all dependencies (even with duplicate dependencies) from absolute path?

@thw0rted
Copy link

Just in case this helps somebody else who finds this issue, I got the behavior I wanted by adding a rule to my Webpack config to load the files I wanted with file-loader:

{
  test: /\.config\.js$/,
  exclude: /node_modules/,
  loader: "file-loader",
  options: { name: "./conf/[name]-[hash:base36:4].[ext]", },
}

then getting a reference to the output path and dynamically importing it with native import():

const path = require("../conf/" + ENVIRONMENT_NAME + ".config.js");
import(/* webpackIgnore: true */CONFIG_PATH).then(mod => config.init(mod));

This means that all the files matching ../conf/*.config.js get copied to ./conf/ in the build, and a map is established between their original path and the public path. Now, I can use the inline "ignore" comment to cause Webpack to actually emit a native import call, and the argument will be the public path to the file in question. Works great! (...on browsers that support import())

@jslegers
Copy link

I have the following code :

function parse (fileName, callback) {
  var path = "./relative/path/to/" + fileName + ".json";
  var cb = process.bind(this, fileName, callback);
  if (typeof webpackJsonp !== "undefined") { // <-- Test if run as Webpack bundle
    // Do dynamic import
    import(path).then(cb);
  } else {
    // Do Ajax call
    request(Config.toUrlFromRoot(path)).then(cb);
  }
}

The import works as expected in my Electron demo (which uses Webpack 3.12).

It breaks in my Angular demo (which uses Webpack 4.28).

My Angular demo has the following Webpack config :

module.exports = {
  node: { fs: 'empty' }
};

It produces the following error at runtime :

Unhandled Promise rejection: Cannot find module './resources/symbology/labelTemplates/labelTemplates.json' ; Zone: <root> ; Task: Promise.then ; Value: Error: Cannot find module './resources/symbology/labelTemplates/labelTemplates.json'
    at symbology lazy namespace object:5
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
    at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:150)
    at zone.js:889
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)
    at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195)
    at drainMicroTaskQueue (zone.js:601) Error: Cannot find module './resources/symbology/labelTemplates/labelTemplates.json'
...

Is there anything I'm doing wrong?

How can I fix this?

@adjenks
Copy link

adjenks commented May 15, 2019

I don't think the current import method is ideal. I think it should load modules from arbitrary urls like is done in SystemJS.import(), and I think that you should simply build all of the other files you want built separately instead of having it infer where they are based on paths.

wscheng pushed a commit to wscheng/gchordlyrics-vue-pwa that referenced this issue Oct 24, 2019
* get names of files in directory

use reuire.context to get

* dynamic import the file content

IMORTANT: the first parameter in import method should be a constant prefix!
Ref:
webpack/webpack#6680 (comment)

====

Tried but failed method:

      // 1.fs failed, in webpack fs cannot be use
      // const fs = require("fs");
      // console.warn("content=>", fileContent);
      // fs.readFile(filePath, "utf8", (err, data) => {
      //   if (err) {
      //     console.warn("err=>", err);
      //   } else console.warn("data=>", data);
      // });

      // 2.fileReader failed
      // var reader = new FileReader();
      // reader.readAsText(filePath);

      // 3.require raw-loader failed, the require expression not figured out yet
      // var test = require("raw-loader!./static/" + name);
      // console.log("content=>", test);

// import * failed!
// import * as txtFiles from "../../public/lyrics";
@Genluo
Copy link

Genluo commented Nov 14, 2019

If you develop a node project and use webpack to build your node project, when you meet the Error: Cannot find module with dynamic import , you can use NodeNative module to replace webpack build create __webpack_require__ function,

webpack.config.js

{
 target: 'node',
    node: {
      __dirname: false,
      __filename: false,
    }
}

eg :

import module from 'module';
const test = '/Users/hubert_i/Emodyz/launcher-ezgames.eu/modules/test/index.js'
module.createRequire(__dirname)(test)
  .then((m) => {
    console.log(m.name)
  })
  .catch(err => {
    console.error('Error during loading module: ' + err)
    callback()
  })

@arajay
Copy link

arajay commented Feb 6, 2020

I solved this problem by simply removing the opening slash from the dynamic url. this might work for you.

const test = 'Users/hubert_i/Emodyz/launcher-ezgames.eu/modules/test/index.js'
import(`/${test}`)

@Fransebas
Copy link

Fransebas commented Apr 15, 2020

The @thw0rted solution was inconsistent because it depends on your webpack configuration but I found this very clean solution:

https://stackoverflow.com/questions/37339866/using-node-require-with-electron-and-webpack/37349344#37349344

What they do is simply call global.require(/absolute/path) instead of require(/absolute/path).

Just make sure that your electron configuration is not sandboxed and node is enabled.

@thw0rted
Copy link

Dynamic import() is nothing to do with Node require. My solution is for the browser environment.

@turbopasi
Copy link

I would like to load module located in %appdata% in windows or Application Support on macOS how i can do that ?

@MrDarkSkil did you found a solution to that ?

@leohubert
Copy link
Author

leohubert commented Jun 16, 2020

I would like to load module located in %appdata% in windows or Application Support on macOS how i can do that ?

@MrDarkSkil did you found a solution to that ?

@turbopasi

Yep, many alternatives.
Use: https://www.npmjs.com/package/vm2 or https://www.npmjs.com/package/live-plugin-manager

Or:

/**
  * Load custom module with simple require and absolute path
  * 
  * @param {string} path
  */
  private requireDynamically(path: string) {
    path = path.split('\\').join('/');
    return eval(`require('${path}');`);
  }

@Heather-Li
Copy link

Do you want to request a feature or report a bug?

bug

What is the current behavior?

When I try to load dynamic module with import('/path/to/my/module.js').then() it work, but when i try to import module with import(rootPath + '/' + myModuleName + '/index.js').then() it doesn't work.

I got:

 Error: Cannot find module '/Users/hubert_i/Emodyz/launcher-ezgames.eu/modules/test/index.js'.
      at /Users/hubert_i/Emodyz/launcher-ezgames.eu/dist/electron/main.js:10475:9
      at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

If the current behavior is a bug, please provide the steps to reproduce.

What is the expected behavior?

Dynamic import with variable

Please mention other relevant information such as the browser version, Node.js version, webpack version, and Operating System.

webpack => 3.10.0
npm => 5.6.0
yarn => 1.5.1
Node.js => v9.6.1
Operating System => macOs High Sierra

you can try async-await,I have the same problem solved

export const loadArray = async () => {
  const locale = getLocale().replace('_', '-')
  return await require(`./array/${locale}.js`)
}

@Heather-Li
Copy link

I would like to load module located in %appdata% in windows or Application Support on macOS how i can do that ?

@MrDarkSkil did you found a solution to that ?

you can try async-await,I have the same problem solved

export const loadArray = async () => {
  const locale = getLocale().replace('_', '-')
  return await require(`./array/${locale}.js`)
}

@thw0rted
Copy link

thw0rted commented Jul 9, 2020

Your solution does not use dynamic import to load from the server at runtime. Look at the code that Webpack generates and you'll see that the code is being resolved at compile-time using a context that it generates based on how your path string is constructed.

@rubenberna
Copy link

Hi,

If I import each npm package individually with React.Lazy, it's not a problem:

const GHub = lazy(() => import('react-switch-app-one'))
const Count = lazy(() => import('@ruben.bernardes.dev/counter'))

but if I iterate the paths to create lazy components, webpack cannot find the module:

const packagesPaths = ['react-switch-app-one', '@ruben.bernardes.dev/counter']
const importComponent = path =>
  lazy(() => import(path).catch((e) => console.log(e)));

const loadedAppPaths = packagesPaths.map(app => importComponent(app))
Sidebar.js:25 Error: Cannot find module 'react-switch-app-one'
    at sidebar lazy groupOptions: {} namespace object:5

@adjenks
Copy link

adjenks commented Nov 19, 2020

@rubenberna That's because webpack uses a simple logic for building its package bundles. It can't predict what will happen when the code executes. It pre-packs everything it sees within the import() call. If you call import('src/'+path) it will literally package everything in the "src" directory even if "path" is only ever 3 of those files, because the heuristic that webpack uses is so simple. It's best to just do them individually.

@yonahochieng
Copy link

#4175 (comment)

This is the BEST solution I came across

@adjenks
Copy link

adjenks commented Apr 16, 2021

@Yawhnie Be careful 😊: #4175 (comment)

@zealotrahl
Copy link

zealotrahl commented Jun 4, 2021

Webpack performs a static analyse at build time. It doesn't try to infer variables which that import(test) could be anything, hence the failure. It is also the case for import(path+"a.js").

If you need truly dynamic imports, you have to restrict it to a known path:

import("./locales/" + locale + ".js")

In the above example, webpack will create a chunk for each JS file in locale/.

Is there any specific webpack config required for this?
Because this works:
this.component = defineAsyncComponent(() => import('@/views/branches/create.vue'));
This does not work
this.component = defineAsyncComponent(() => import('@/views/' + 'branches/create' + '.vue'));

Reason: Error: Cannot find module '@/views/branches/create.vue'

Update
Solved, in babel config removed plugin ['dynamic-import-node'],

@ChristopherHButler
Copy link

I wanted to use dynamic imports to choose which module to import. I used require as a work around (posting in case this helps someone trying to do the same):

export const keys = (process.env.NODE_ENV === 'development' ? require('./dev') : require('./prod')).default;

@Nefcanto
Copy link

It seems that Next.js needs to address this issue more seriously. This question reports a strange behavior from Next.js.

@raphaelApard
Copy link

Is that possible to import conditionally a node module ?

It's work great with local path like :

const { i18n } = await import(`./i18n/${lang}`)

But when i try to import module like this :

const myModule = await import(`@library-${myVar}`)

But the module is missing from the bundle

Error: Cannot find module '/Users/hubert_i/Emodyz/launcher-ezgames.eu/modules/test/index.js'.

@thw0rted
Copy link

Have you tried a relative (vs absolute) path specifier? import('../../node_modules/@library-' + myVar) should work, according to the posts upthread. Webpack is supposed to have logic that computes a context (in require terms) based on a prefix string like that. I don't know how well it supports multiple directories matching the context, or doing Node-style specifier resolution, i.e. specifying a directory containing a package.json then using that to look up main / module / exports. If the packages in question all have the same entry point (@library-foo/index.js, @library-bar/index.js, etc) then maybe you could construct a path directly to the entry point, with your variable in the middle.

@raphaelApard
Copy link

Thanks!
Using import('../../node_modules/@library-' + myVar) webpack build fails trying to parse to much files (markdown files) from other librairies.
But if i specify import('@library-' + myVar)/dist//dist/esm/index.js it's working.

@adjenks
Copy link

adjenks commented Mar 21, 2023

Guys, if you want really dynamic imports with complete control over how/when/what is imported, use SystemJS: https://github.com/systemjs/systemjs

If you want imports that depend on very simple rules, use what webpack comes with by default.

Unsubscribes to issue

@bert-w
Copy link

bert-w commented Oct 22, 2023

For me a solution that worked is:

// Absolute path:
const somePath = 'file:C:/somefiles/config.js';
// Or relative:
const somePath = '../config.js';

var someUserProvidedConfigFile = import(
    /* webpackIgnore: true */
    somePath
);

By using /* webpackIgnore: true */, the dynamic import statement is still present in your bundle (I am compiling a Node.js module specifically so it will work), and a user can then pass his own input file to be imported, either absolute or relative.

@niteshkhanna1989
Copy link

i am using async await which delays the import statement

export async function registerLazyLoadedStuff(): Promise {
await import('lazyLoadedStuff');
}

and wherever you need the lazyloaded stuff just call the function in ngIf (wrapping it up with rxjs from) this does not statically check the import and not load the lazyloaded stuff at runtime till we do not require it. Hope this helps !!

@minikN
Copy link

minikN commented Jun 19, 2024

For me a solution that worked is:

// Absolute path:
const somePath = 'file:C:/somefiles/config.js';
// Or relative:
const somePath = '../config.js';

var someUserProvidedConfigFile = import(
    /* webpackIgnore: true */
    somePath
);

By using /* webpackIgnore: true */, the dynamic import statement is still present in your bundle (I am compiling a Node.js module specifically so it will work), and a user can then pass his own input file to be imported, either absolute or relative.

Thanks for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests