Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract multiple theme stylesheets with webpack?

I am trying to make a React app themeable. For now themes only consist of different sets of Sass variables which define different header colors, etc.

From my current understanding the ExtractTextPlugin seems to be my best bet as I don't want my styles to be inlined and rather have separate files per theme.

So I created two themes:

src/scss/themes/theme-a.scss
src/scss/themes/theme-b.scss

The themes import the basic layout and common styles and override the relevant variables.

But the only way I managed to make webpack create separate css files for both themes was to create distinct entry points for each theme in my webpack.prod.config:

entry: {
  app: './src/js/init.js',
  theme-a: './src/scss/themes/theme-a.scss',
  theme-b: './src/scss/themes/theme-b.scss'
},

But adding a new entry point for every new theme that gets added feels wrong and I figure there must be a better way?

like image 829
arie Avatar asked Jul 14 '16 20:07

arie


2 Answers

To avoid having to manually add the themes one possible solution is to create a function that reads contents of the themes folder and programatically add an entry for each *.scss file.

Something along the lines of this:

function getThemes(themePath) {
    var themes = {};
    fs.readdirSync(themePath).forEach(function(fileName) {
        var fileNameWithPath = path.join(themePath, fileName);

        var stat = fs.lstatSync(fileNameWithPath);
        if (stat.isDirectory()) return;
        if (!/\.scss$/.test(fileName)) return;

        var nameWithoutExt = path.basename(fileName, '.scss');
        themes[nameWithoutExt] = fileNameWithPath;
    });

    return themes;
}

var themes = getThemes('./src/scss/themes');

var config= {
    entry: _.merge({ app: './src/js/init.js' }, themes),
    // rest of options
};

This will require you to restart your dev-server or re-run you webpack build when adding new theme files though, but at least you won't have to touch your webpack config.

like image 99
Markus-ipse Avatar answered Nov 09 '22 11:11

Markus-ipse


Here's your solution:

npm i --save-dev file-loader

In the loaders section, add this:

{
    test: /themes\/.+\.scss$/,
    loader: "file-loader?name=./compiled-themes/css/[name].css!css!sass"
},

There may be more scss files, if so there must be another section which bundles them as usual, but skips the themes:

{
    test: /\.scss$/,
    exclude: /themes\/.+\.scss$/,
    loader: "css!sass"
},

The file loader writes files by filename, hash and extension so you are able to preserve the name.

Note the name=./compiled-themes/css/[name].css part, where [] vars are substituted.

https://github.com/webpack/file-loader

like image 20
Tudor Ilisoi Avatar answered Nov 09 '22 12:11

Tudor Ilisoi