27

I am trying to port a library from grunt/requirejs to webpack and stumbled upon a problem, that might be a game-breaker for this endeavor.

The library I try to port has a function, that loads and evaluates multiple modules - based on their filenames that we get from a config file - into our app. The code looks like this (coffee):

loadModules = (arrayOfFilePaths) ->
  new Promise (resolve) ->
    require arrayOfFilePaths, (ms...) ->
      for module in ms
        module ModuleAPI
      resolve()

The require here needs to be called on runtime and behave like it did with requireJS. Webpack seems to only care about what happens in the "build-process".

Is this something that webpack fundamentally doesn't care about? If so, can I still use requireJS with it? What is a good solution to load assets dynamically during runtime?

edit: loadModule can load modules, that are not present on the build-time of this library. They will be provided by the app, that implements my library.

sra
  • 6,338
  • 3
  • 20
  • 17
  • I've similar issue with Cordova, which is using non standard AMD loader, but my app is built using webpack. My app depends on some Cordova plugins, which are loaded in runtime and not present in compile time. I tried many solutions, but none of them appeals to me. I would like to see native webpack module resolution of "runtime" dependencies. In principle implementation should be easy. Some factory function, that will be called (and return some object/func defined in runtime), when module is required by some other module. – mauron85 Nov 19 '17 at 09:04
  • Added feature request to webpack. https://github.com/webpack/webpack/issues/5984 – mauron85 Nov 19 '17 at 10:22

3 Answers3

16

So I found that my requirement to have some files loaded on runtime, that are only available on "app-compile-time" and not on "library-compile-time" is not easily possible with webpack.

I will change the mechanism, so that my library doesn't require the files anymore, but needs to be passed the required modules. Somewhat tells me, this is gonna be the better API anyways.

edit to clarify:

Basically, instead of:

// in my library
load = (path_to_file) ->
  (require path_to_file).do_something()

// in my app (using the 'compiled' libary)
cool_library.load("file_that_exists_in_my_app")

I do this:

// in my library
load = (module) ->
  module.do_something()

// in my app (using the 'compiled' libary)
module = require("file_that_exists_in_my_app")
cool_library.load(module)

The first code worked in require.js but not in webpack.

In hindsight i feel its pretty wrong to have a 3rd-party-library load files at runtime anyway.

Stefan_EOX
  • 1,279
  • 1
  • 16
  • 35
sra
  • 6,338
  • 3
  • 20
  • 17
  • 7
    Why is this answer downvoted? I need to do exactly the same thing as the author of this question, and haven't found a way to do it either, context only works when needed files are present at build time. – Dmitri Pisarev Jan 24 '16 at 10:37
  • I edited the solution for more clarity. Hope this helps – sra Sep 19 '16 at 15:41
  • This solution doesn't solve the issue!!!!, how can I make this code to run - in run time: "require(pathToFile + fileName);" when pathToFile or/and fileName are only evaluated at run time????? – Gil Epshtain Nov 28 '16 at 15:06
  • 2
    Gil, please reconsider your use of exclamation marks ;) `require(pathToFile + fileName)` gets the file for you, if it is available at the moment you run `webpack`. If it is not available at compile-time, but it will be later - for example when your library is loaded into a project, where these files exist - then you ened to reconsider your "require" strategy. Please read the answer again. Maybe you have a different problem? – sra Nov 29 '16 at 12:50
11

There is concept named context (http://webpack.github.io/docs/context.html), it allows to make dynamic requires.

Also there is a possibility to define code split points: http://webpack.github.io/docs/code-splitting.html

function loadInContext(filename) { 
    return new Promise(function(resolve){
        require(['./'+filename], resolve);
    })
}

function loadModules(namesInContext){
    return Promise.all(namesInContext.map(loadInContext));
}

And use it like following:

loadModules(arrayOfFiles).then(function(){
    modules.forEach(function(module){
        module(moduleAPI);
    })
});

But likely it is not what you need - you will have a lot of chunks instead of one bundle with all required modules, and likely it would not be optimal..

It is better to define module requires in you config file, and include it to your build:

// modulesConfig.js
module.exports = [
   require(...),
   ....
]

// run.js
require('modulesConfig').forEach(function(module){
    module(moduleAPI);
})
Wilt
  • 41,477
  • 12
  • 152
  • 203
Bogdan Savluk
  • 6,274
  • 1
  • 30
  • 36
  • 7
    Thanks for your answer. I cannot use context, because it seems that those dynamically loaded modules need to be present at build-time. This is not the case in my library. They will only be available once the app itself gets built, not my library. – sra Jun 02 '15 at 05:27
2

You can also try using a library such as this: https://github.com/Venryx/webpack-runtime-require

Disclaimer: I'm its developer. I wrote it because I was also frustrated with the inability to freely access module contents at runtime. (in my case, for testing from the console)

Venryx
  • 15,624
  • 10
  • 70
  • 96
  • thanks for your contribution @venryx! is there something similar for rollup? – eternalStudent Apr 14 '22 at 16:13
  • @eternalStudent Not that I know of. It's likely to be possible, but I haven't used rollup much, so don't know what the method for extracting the module-contents would be. – Venryx Apr 15 '22 at 01:38