Skip to content

Instantly share code, notes, and snippets.

@likerRr
Last active May 22, 2018 10:17
Show Gist options
  • Save likerRr/2c964f145b6e1f9e567fecfd6dd4fe0e to your computer and use it in GitHub Desktop.
Save likerRr/2c964f145b6e1f9e567fecfd6dd4fe0e to your computer and use it in GitHub Desktop.
Webpack's `require.context` emulation for nodejs

Webpack's require.context is a powerful feature which helps to extract an array of paths within which the search of modules will be done. e.g.:

const requireFromContext = require.context(__dirname, true, /config\.js$/);
// Now `requireFromContext` contains files inside `__dirname/config` folder and which extension is `.js`

require.context is a special feature of webpack. Native nodejs's require doesn't containt that method and if you want to run unbundled code on nodejs, then you have to emulate require.context.

const fs = require('fs');
const path = require('path');
/**
* This function emulates behaviour of require.context
* The reason why we can't return appropriate function in runtime is because require.context parses during build phase
* Make sure that base is set relative to __dirname where it's called
* @param base
* @param scanSubDirectories
* @param regularExpression
* @returns {Module}
*/
require.context = (base = '.', scanSubDirectories = false, regularExpression = /\.js$/) => {
const files = {};
function readDirectory(directory) {
fs.readdirSync(directory).forEach(file => {
let fullPath = path.resolve(directory, file);
if (fs.statSync(fullPath).isDirectory()) {
if (scanSubDirectories) {
readDirectory(fullPath);
}
return;
}
fullPath = fixWinPathSeparators(fullPath);
if (!regularExpression.test(fullPath)) {
return;
}
files[`./${fixWinPathSeparators(path.relative(base, fullPath))}`] = fullPath;
});
}
readDirectory(path.resolve(__dirname, base));
function Module(file) {
try {
// eslint-disable-next-line global-require
return require(files[file]);
} catch (err) {
throw new Error(`
Can't resolve module ${file} from context ${JSON.stringify(files)}
Check if given context is correct:
1. directory: ${path.resolve(__dirname, base)}
2. subDirectories: ${scanSubDirectories.toString()}
3. regularExpression: ${regularExpression}
Original message: ${err}
`);
}
}
Module.keys = () => Object.keys(files);
return Module;
};
module.exports = require.context;
/**
* Replaces win separators with unix-like
* @param anyPath
*/
function fixWinPathSeparators(anyPath) {
return anyPath.replace(/\\/g, '/');
}
// In case when you need to run application by node and your applications uses require.context webpack's feature, then this emulation will provide exactly the same bahevior
// I recommend to define an environment variable to determine how build is done, e.g. COMPILER
if (process.env.COMPILER === 'babel') {
require.context = require('./nodeRequireContext');
} else {
// Webpack's require.context will be used
}
const requireFromContext = require.context(__dirname, true, /config\.js$/);
// ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment