110

I have a large amount of javascript files split into 4 subdirectory in my app. In grunt I grab all of them and compile them into one file. These files do not have a module.exports function.

I want to use webpack and split it into 4 parts. I don't want to manually go in and require all my files.

I would like to create a plugin that on compilation walks the directory trees, then grabs all the .js file names and paths, then requires all files in the sub directories and adds it to the output.

I want all the files in each directories compiled into a module that I could then require from my entry point file, or include in the assets that http://webpack.github.io/docs/plugins.html mentions.

When adding a new file I just want to drop it in the correct directory and know it will be included.

Is there a way to do this with webpack or a plugin that someone has written to do this?

Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
ChickenFur
  • 2,360
  • 3
  • 20
  • 26

6 Answers6

134

This is what I did to achieve this:

function requireAll(r) { r.keys().forEach(r); }
requireAll(require.context('./modules/', true, /\.js$/));
splintor
  • 9,924
  • 6
  • 74
  • 89
  • Is there any way to specify what loader to use? My use case is that I want to use the `image-size-loader` for all images in order to create placeholders with correct aspect ratios. – bobbaluba Nov 24 '15 at 12:08
  • 17
    could you provide you working sample webpack.config.js – Rizwan Patel Dec 09 '15 at 08:38
  • 2
    @bobbaluba, you can specify the loader in the config file: `loaders: [ { test: /\.json$/, loader: 'json' } ]`. `{ test: /\.json$/, loader: 'json' }` adds the json loader for .json files, as long as you have installed the loader. – Jake May 21 '16 at 18:49
  • 1
    Keep in mind that if you try to be DRY here by writing a function that calls `require.context(...)` for you (e.g. if you're trying to require everything in several folders), webpack will throw a warning. The arguments to `require.context` have to be compile-time constants. – Isaac Lyman Jul 12 '17 at 17:54
  • 5
    @Joel https://www.npmjs.com/package/require-context was created in 2017 and my answer is from 2015, so it is hard to say it is taken straight from there. Also note that `require-context` is a wrapper around `webpack`, and its sample is taken from `webpack`'s documentation at https://webpack.js.org/guides/dependency-management/#context-module-api - which was added in 2016 (in https://github.com/webpack/webpack.js.org/commit/ee8a1989c20943fd559230adc94b94206467ea08), also after I wrote my answer... Maybe webpack's writers were influenced by my answer. Note that they expanded it to have cache. – splintor Feb 16 '19 at 23:12
  • I am getting error require.context is not a function. – Vish May 19 '20 at 07:00
42

In my app file I ended up putting the require

require.context(
  "./common", // context folder
  true, // include subdirectories
  /.*/ // RegExp
)("./" + expr + "")

courtesy of this post: https://github.com/webpack/webpack/issues/118

It is now adding all my files. I have a loader for html and css and it seems to work great.

Mosho
  • 7,099
  • 3
  • 34
  • 51
ChickenFur
  • 2,360
  • 3
  • 20
  • 26
  • 28
    What is `expr` in this example? – twiz Aug 24 '15 at 00:47
  • 2
    `expr` is a path to a single file within the context folder. so if you need to do this not just for requiring all the html files, this is useful. https://webpack.github.io/docs/context.html#context-module-api – mkoryak Sep 30 '15 at 22:23
  • 22
    it's still unclear to me what the `expr` based argument is doing here even after looking at the webpack docs. what does "do this not just for requiring all html files" mean? thank you – mikkelrd Oct 16 '15 at 06:29
  • 1
    It's the same as var req=require.context("./common", true, /.*/); req("./" + expr + "") – VyvIT Nov 05 '15 at 14:38
  • 3
    I got **Uncaught ReferenceError: expr is not defined**. Any hint about that? – CSchulz Sep 06 '16 at 07:01
  • 1
    @CSchulz it might be related to this: https://webpack.github.io/docs/context.html#parser-evaluation. My guess is that `exp` is a placeholder for a simple expression that webpack understands. But I might be wrong ... – AJ Meyghani Sep 09 '16 at 21:40
  • 2
    Still no answer about what `expr` means here – wizulus Sep 17 '18 at 18:38
  • `require.context` returns a function that works just like require, and takes a string indicating what to require. So in this case, `expr` is simply standing in for the name of the file you want to load from the context. – Jason Kohles May 26 '21 at 18:05
12

How about a map of all the files in a folder?

// { 
//   './image1.png':  'data:image/png;base64,iVBORw0KGgoAAAANS',
//   './image2.png':  'data:image/png;base64,iVBP7aCASUUASf892',
// }

Do this:

const allFiles = (ctx => {
    let keys = ctx.keys();
    let values = keys.map(ctx);
    return keys.reduce((o, k, i) => { o[k] = values[i]; return o; }, {});
})(require.context('./path/to/folder', true, /.*/));
Steven de Salas
  • 20,944
  • 9
  • 74
  • 82
3

Example of how to get a map of all images in the current folder.

const IMAGES_REGEX = /\.(png|gif|ico|jpg|jpeg)$/;

function mapFiles(context) {
  const keys = context.keys();
  const values = keys.map(context);
  return keys.reduce((accumulator, key, index) => ({
    ...accumulator,
    [key]: values[index],
  }), {});
}

const allImages = mapFiles(require.context('./', true, IMAGES_REGEX));
Alex K
  • 14,893
  • 4
  • 31
  • 32
2

All merits to @splintor (thanks).

But here it is my own derived version.

Benefits:

  • What modules export is gathered under a {module_name: exports_obj} object.
    • module_name is build from its file name.
    • ...without extension and replacing slashes by underscores (in case of subdirectory scanning).
  • Added comments to ease customization.
    • I.e. you may want to not include files in subdirectories if, say, they are there to be manually required for root level modules.

EDIT: If, like me, you're sure your modules won't return anything else than (at least at root level) a regular javascript object, you can also "mount" them replicating their original directory structure (see Code (Deep Version) section at the end).

Code (Original Version):

function requireAll(r) {
    return Object.fromEntries(
        r.keys().map(function(mpath, ...args) {
            const result =  r(mpath, ...args);
            const name = mpath
                .replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
                .replace(/\//g, '_') // Relace '/'s by '_'s
            ;
            return [name, result];
        })
    );
};
const allModules = requireAll(require.context(
    // Any kind of variables cannot be used here
    '@models'  // (Webpack based) path
    , true     // Use subdirectories
    , /\.js$/  // File name pattern
));

Example:

Sample output for eventual console.log(allModules);:

{
  main: { title: 'Webpack Express Playground' },
  views_home: {
    greeting: 'Welcome to Something!!',
    title: 'Webpack Express Playground'
  }
}

Directory tree:

models
├── main.js
└── views
    └── home.js

Code (Deep Version):

function jsonSet(target, path, value) {
    let current = target;
    path = [...path]; // Detach
    const item = path.pop();
    path.forEach(function(key) {
        (current[key] || (current[key] = {}));
        current = current[key];
    });
    current[item] = value;
    return target;
};
function requireAll(r) {
    const gather = {};
    r.keys().forEach(function(mpath, ...args) {
        const result =  r(mpath, ...args);
        const path = mpath
            .replace(/(?:^[.\/]*\/|\.[^.]+$)/g, '') // Trim
            .split('/')
        ;
        jsonSet(gather, path, result);
    });
    return gather;
};
const models = requireAll(require.context(
    // Any kind of variables cannot be used here
    '@models'  // (Webpack based) path
    , true     // Use subdirectories
    , /\.js$/  // File name pattern
));

Example:

Result of previous example using this version:

{
  main: { title: 'Webpack Express Playground' },
  views: {
    home: {
      greeting: 'Welcome to Something!!',
      title: 'Webpack Express Playground'
    }
  }
}
bitifet
  • 3,514
  • 15
  • 37
0

this works for me :

function requireAll(r) { r.keys().forEach(r); } 

requireAll(require.context('./js/', true, /\.js$/));

NOTE: this can require .js files in subdirs of ./js/ recursively.

Phani Kumar M
  • 4,564
  • 1
  • 14
  • 26
jack long
  • 11
  • 3