I have some JSON that is created at build time in my Webpack app - is there any way I can "inject" it with a path during the build? I know I could just write it out to a file and include it that way, but I'm hoping I can do something cleaner than that.
Edit 2020/10/27: webpack-virtual-modules is a project that can do this and has compatibility with webpack 5
Edit 2018/04/09: val-loader is another way to achieve injecting code and values at build-time, but it requires loading that code from a separate file which may not be able to access the JSON data in the OP's setup if that only exists in memory.
I was looking for a way to do this as well. I ended up digging into the internals of webpack and wrote a plugin that seems to work.
It's clean in that you don't have to write a file to disk, but the internals of it are a little messy because I had to get into how webpack's CachedInputFileSystem works.
It didn't seem to be possible to do this using a loader. Webpack needs to resolve a file location on disk and read the contents of that before it will move on to the loader phase.
By placing a plugin function on the 'resolve' phase of compiler.resolvers.normal
, it is possible to access the filesystem in use by webpack and then add the virtual filename and contents into the caches of that filesystem.
After doing that, everything else works as normal in webpack and your virtual file/module will pass through the other loaders and plugins you have configured.
See https://github.com/rmarscher/virtual-module-webpack-plugin for the code that I wrote. It has been published to npm: https://www.npmjs.com/package/virtual-module-webpack-plugin
Here is the code for the resolve part of the plugin. Note that this example was for webpack 1 and 2, but the plugin has been updated to work with more recent versions:
compiler.resolvers.normal.plugin('resolve', function resolverPlugin(request, cb) {
// populate the file system cache with the virtual module
const fs = this.fileSystem;
// webpack 1.x compatibility
if (typeof request === 'string') {
request = cb;
cb = null;
}
if (!modulePath) {
modulePath = this.join(compiler.context, moduleName);
}
VirtualModulePlugin.populateFilesystem({ fs, modulePath, contents, ctime });
if (cb) {
cb();
}
});
The populateFilesystem
static method adds the contents to the fs._readFileStorage.data
and creates a mock result to fs.stat()
in the fs._statStorage.data
cache. For creating the mock fs.Stats
object, I borrowed some code from the mock-fs
package.
So far I have tested it with the latest webpack 1.x and 2.x as well as webpack-dev-server. I've used the extract-text-webpack-plugin
, json-loader
, raw-loader
and css-loader
. All seem to work as expected.
Here is a webpack config that uses the plugin:
'use strict';
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const VirtualModulePlugin = require('virtual-module-webpack-plugin');
module.exports = function webpackConfig() {
const runtimeJsonContents = JSON.stringify({
greeting: 'Hello!',
});
const runtimeStyleContents = `
body { background: #000; color: #ccc; }
.greeting { font: 600 40px/50px fantasy; text-align: center; }
`;
const config = {
context: __dirname,
devtool: 'source-map',
entry: {
index: './src/index',
},
output: {
filename: '[name].js',
path: 'dist',
publicPath: '/',
devtoolModuleFilenameTemplate: '../[resource-path]',
},
module: {
loaders: [
{
test: /\.json$/,
loaders: ['json-loader'],
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallbackLoader: 'style-loader',
loader: 'css-loader?sourceMap',
}),
},
],
},
plugins: [
new VirtualModulePlugin({
moduleName: 'src/mysettings.json',
contents: runtimeJsonContents,
}),
new VirtualModulePlugin({
moduleName: 'src/css/generated.css',
contents: runtimeStyleContents,
}),
new ExtractTextPlugin({
filename: '[name].css',
allChunks: true,
}),
],
resolve: {
modules: [
path.join(__dirname, 'src'),
'node_modules',
],
},
};
return config;
};
See https://github.com/rmarscher/virtual-module-webpack-plugin/tree/master/examples for complete examples with different versions of webpack.
I should also note that this code requires NodeJS 4.x or higher.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With