如何配置webpack以生成单独的css主题

时间:2017-10-07 20:23:34

标签: webpack sass themes

我正在开发一个反应应用程序,现在已经扩展了几个主题。

这些主题在我的主题文件夹中的.scss个文件中定义:

- themes
- - theme-base.scss
- - theme.dark.scss
- - theme.light.scss

它们包含带有一些变量的基本配色方案,例如:

[theme-dark.scss]
$color-primary: #100534;
$color-primary-accent: #231454;

所有组件只使用这些颜色变量来对其进行主题化。

现在我希望webpack编译并连接我的scss代码,以分离每个主题的静态css文件。

我通过使用extractWebpackTextPlugin和一些文件加载​​器来实现这一点:

module.exports = env => {
  const config = require('./webpack.common')(env);
  const project = env.project;

  const postcss = {
    loader: 'postcss-loader',
    options: postcssConfig(settings.browsers),
  };

  config.watch = true;
  config.devtool = 'eval';

  const themes = ['theme-base'];

  themes.forEach(themeKey => {
    const themeInstance = new ExtractTextPlugin('css/' + themeKey + '.css');
    const themePath = path.resolve(__dirname, '../../react/app/css/_' + themeKey + '.scss');

    config.module.rules.push({
      test: /\.s?css$/,
      exclude: /node_modules/,
      use: themeInstance.extract({
        fallback: 'style-loader',
        use: [
          {
            loader: 'css-loader',
            options: {
              localIdentName: '[hash:base64:5]',
              modules: true,
              importLoaders: 1,
            }
          },
          postcss,
          {
            loader: 'sass-loader',
            options: {
              data: `@import "${themePath}";`,
            }
          }
        ]
      }),
    });

    config.plugins.push(themeInstance);
  });
}

可是: 我不能在我的数组themes中添加多个主题!只要它包含多个项目,css和sass加载器就会在读取scss文件时抛出错误。

Invalid CSS after "...load the styles": expected 1 selector or at-rule, was "var content = requi"

我如何设置webpack来为每个主题编译一个静态css文件?

2 个答案:

答案 0 :(得分:0)

我目前正在处理类似的问题,我试图在没有任何侵入性代码的情况下解决它。然后,我编写一个webpack插件themes-switch来做到这一点。它将根据主题目录提取主题。也许可以帮到您。

示例配置:

new ThemesGeneratorPlugin({
      srcDir: 'src',
      themesDir: 'src/assets/themes',
      outputDir: 'static/css',
      defaultStyleName: 'default.less',
      themesLoader: {
        test: /\.(less|css)$/,
        loaders: [
          { loader: require.resolve('css-loader') },
          { loader: require.resolve('less-loader') }
        ]
      }
    })

现在,我仅使用变量来定义不同的颜色和尺寸,然后webpack会为我生成主题,并且样式格式可以更少,postcss和sass。然后,我可以在我的应用中进行实时切换。该插件将自动清除未使用的文件。

答案 1 :(得分:0)

我遇到了类似的问题,并且意识到发生错误是因为您不能有多个具有相同test: /\.s?css$/条件的Webpack规则。第一个之后的所有内容都会出错。

要解决此问题,请为所有SCSS创建1条规则,然后将其他ExtractTextPlugin实例推送到规则的oneOf数组中。...

/ SASS文件夹

-- app.scss
-- custom/
---- /blue/theme.scss
---- /red/theme.scss
---- /default/theme.scss
---- ...

./src/sass/app.scss?theme条目是默认/第一个主题。

var themeDefault = "default";
module.exports = {
  entry: ['./src/app.js','./src/sass/app.scss?theme'],
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'app.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ],
      },
      {
        test: /\.scss$/,
        oneOf: [ // use oneOf since we'll need diff rules/handlers for each .scss
          {
            resourceQuery: /theme/, 
            use: extractSass.extract({
                use: [
                  {
                    loader: "css-loader"
                  },
                  {
                    loader: "sass-loader",
                    options: {
                      allChunks: true,
                      sourceMap: true,
                      includePaths: [
                        './src/sass/custom/'+themeDefault
                      ]
                    }
                }]
            })
          }
        ],
      },

      ...
    ]
  },
  plugins: [
    extractSass
  ],
  ...
}

遍历/ custom文件夹中的每个主题。

  // iterate themes to add an entry point, rule and plugin instance for each
  fs.readdirSync('./src/sass/custom').filter(f => fs.statSync("./src/sass/custom/"+f).isDirectory()).forEach(themeKey => {
    const themeInstance = new ExtractTextPlugin('css/' + themeKey + '.css');

    module.exports.entry.push('./src/sass/app.scss?'+themeKey);

    // the SCSS rule is at index 1 in the rules array
    module.exports.module.rules[1].oneOf.push( 
    {
      resourceQuery: new RegExp(themeKey),  
      use: themeInstance.extract({
          use: [
            {
              loader: "css-loader"
            },
            {
              loader: "sass-loader",
              options: {
                allChunks: true,
                includePaths: [
                  './src/sass/custom/'+themeKey
                ]
              }
          }]
      })
    });

    module.exports.plugins.push(themeInstance);

  });

然后在app.scss中,从Webpack sass-loader中指定的includePaths加载适当的主题文件夹...

// load theme.scss from included path
@import "theme";
...

因此,每次以上forEach碰到app.scss中的下一个themeKey时,@import "theme"都指向一个不同的子文件夹(蓝色,红色等),从而生成不同的CSS文件每个“主题”。