I'm trying to write a custom Webpack loader that reads a JavaScript file and exports SASS variables, similar to js-to-sass-var-loader but with some added functionality.
import _ from 'lodash';
import path from 'path';
const importRegex = /@import\s+'([^']+\.js)'\s*;/ig;
/*
functions for transforming...
*/
export default function (content) {
let self = this;
// search for "@import '*.js';" and replace that text with
// the transformed output of the specified JavaScript file
return content.replace(importRegex, (match, relativePath) => {
if (match) {
let modulePath = path.join(self.context, relativePath);
self.addDependency(modulePath);
let data = require(modulePath).default;
return transform(data);
}
});
}
This JavaScript file loads a .json config file, does a little processing and spits out an object.
import config from '../../config.json';
console.log('Generating SASS variables.');
let sass = {
// some special values here
};
for (let key of Object.keys(config.style)) {
sass[key] = config.style[key];
}
sass.debug = (process.env.NODE_ENV || 'production').trim() === 'development';
export default sass;
The json file includes comments, so I'm stripping them away with another trivial loader. This simply parses the json file using json5, then stringifies the output and passes it on.
import json5 from 'json5';
export default function (source) {
return JSON.stringify(json5.parse(source));
}
My webpack.config.babel.js includes rules for sass and json files to use these two loaders.
// ... imports ...
const webpackConfig = {
entry: {
app: './client/script.js',
},
output: {
filename: 'script.js',
path: path.resolve(__dirname, 'public')
},
resolveLoader: {
alias: {
'js-to-sass-loader': path.resolve(__dirname, 'webpack-loaders/js-to-sass-loader'),
'remove-json-comments-loader': path.resolve(__dirname, 'webpack-loaders/remove-json-comments-loader')
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.json$/,
exclude: /node_modules/,
loader: 'remove-json-comments-loader',
},
{
test: /\.s[ca]ss$/,
exclude: /node_modules/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
{ loader: 'js-to-sass-loader' },
]
}),
},
// ...
],
},
plugins: [
// ...
new ExtractTextPlugin({ filename: 'style.css' }),
// ...
],
// ...
};
module.exports = webpackConfig;
In a few of my JavaScript files, I can import Config from './path/to/config.json'
with no problem. It correctly uses the remove-json-comments-loader
. But when my js-to-sass-loader
attempts to require sassConfig.js
, Webpack doesn't use the loader for parsing config.json
. It attempts to load it as a regular json file, which causes it to fail due to the comments. I've tried using import config from '!!remove-json-comments-loader!../../config.json';
but webpack says it cannot find the file.
I'm super new to writing webpack loaders, so I'm sure it's something simple. Help? Thanks!
Here's a link to the github repo: https://github.com/dfoverdx/PokeStreamer-Tools/tree/9b4c7315d5dc6b30c5972a0b8678489598311bf0
To reproduce the issue, open up /node/client/sass/sassConfig.js
, and change the first four lines to:
// import json5 from 'json5';
// import fs from 'fs';
// const config = json5.parse(fs.readFileSync('config.json'));
import config from '../../config.json';
That is, comment the first 3 lines and uncomment the 4th.
From /node
run npm run build
to produce the error.
So as discussed earlier, below are my observations
require
native nodejs methodjs-to-sass-loader
requires the sassConfig.js
which then requires the config.json
file directlyconfig.json
is the native file which has comments and hence can't be required as suchSo the key takeout is that you are mixing webpack which is basically about bundling stuff into bundles and then requiring the bundled code. That is why the json work in rest of the code and not in the sassConfig.js
.
A loader ideally shouldn't require
processed json
file itself. The processed json
file are suppose to land in the bundle and not as a part of your loader's internal code. So its better to just use the workaround that you are using to load the config.json
using the json5
module, this make sure your loader is not looking for modified code itself