Process the same module multiple times with separate rules/loaders

Do you want to request a feature or report a bug?
Feature

What is the current behavior?
If multiple rules match a module, I believe either the loaders are applied serially or only loaders from the first-matched rule processes the file.

What is the expected behavior?
I’d like to be able to process the same module multiple times, possibly via separate rules. This doesn’t necessarily need to be asynchronous.

There was a loader that helped with this once, but it no longer appears relevant in webpack 2/3: https://github.com/webpack-contrib/multi-loader; for loaders like ExtractTextPlugin, no file is output.

One implementation of this might be allowing us to configure a rule to only apply for certain entries; e.g.

config = {
  entry: { main: 'index.js', vendor: 'vendor.js' },
  module: { rules: [ { test: (file, entry) => file === 'test.css' && entry === 'main', use: [ ... ] } ] }
} 

Or perhaps we can just specify a rule to apply in “parallel”, for any overlapping rules.

If this is a feature request, what is motivation or use case for changing the behavior?
Lots of use cases for this; one might be to process a stylesheet in different ways, and saving them as separate files via ExtractTextPlugin.

Author: Fantashit

4 thoughts on “Process the same module multiple times with separate rules/loaders

  1. I believe multi-loader simply takes loaders as strings and adds additional requires to modules.export (and doesn’t support loaders specified as objects). So if you have:

    import 'file.scss';

    And there’s a rule with multi-loader setup, it would transform this into:

    require('!!first-loader!file.scss');
    require('!!second-loader!file.scss');

    This works for many loaders, but it seems to fail for something like ExtractTextPlugin because, I think, the ExtractTextPlugin instance is lost when having to use something like combine-loaders to feed it into multi-loader. This can and probably should be addressed by multi-loader.

    But what I’m requesting here is a bit more general. Essentially:

    1. Have webpack support more than 1 matching rule, where each rule processes the module independently
    2. Make a rule’s test more powerful by adding some additional information about the module, like the entry from which it’s sourced

    This might not be pertinent to vanilla modules, but it’s useful for importing resources/assets. So, a more complete example might be:

    config = {
      entry: {
        main: path.resolve('src/index.js'),
        vendor: path.resolve('src/vendor.js')
      }
      module: {
        rules: [
        ...
          { test: (filename, entry) => filename.match(/\.png$/) !== null && entry == 'main',
            loader: 'file-loader', ... },
          { test: (filename, entry) => filename.match(/\.png$/) !== null && entry == 'vendor',
            loader: 'url-loader', ... },
        ...
          { test: /\/.png$/,
            loader: 'file-loader', { outputPath: 'cdn' } },
        ]
      }
    }

    In this case, png includes from index.js are copied out as files, whereas those in vendor.js are inlined. This example also shows the same module matching multiple rules, such that png imports will also always create an additional file in ./cdn. And either the first matching rule would provide what’s returned on the original import or an order/preference could be specified by the rule itself.

  2. We are using similar setup with multi-loader and ExtractTextPlugin on webpack 1 for building different themes with sass by processing it multiple times with different variables.

    Unfortunately as delsvr pointed out this setup is no longer working with webpack 2/3 and without this we can’t move to webpack 2/3.

    It would be great if this proposal would be a part of some future release.

  3. I would like to see processing of file types with different rules to achieve this:

    (All third party CSS obtained NPM installs in node_modules/**/*.css ) – loaded and extracted into the final build css without any alteration of the style names found in those CSS files. All of the CSS file in project and not included as NPM from node_modules folders to be named according to CSS_MODULES naming and extracted with a CSS_MODULE name along the lines of name__[class] ___[hash] .

     {
        test: /\.css$/,
        include: /node_modules/,
        loaders: ['style-loader', 'css-loader'],
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            {
              loader: 'css-loader',
              query: {
                modules: true,
                localIdentName: cssModuleIdentTemplate,
                sourceMap: true
              },
              {
                loader: 'postcss-loader',
                options: {
                  config: {
                    ctx: {
                      cssFilePaths: patches
                    },
                  }
                }
              }
            }
          ]
        })
      }
    

    But right now the rule for importing file from node_modules is just ignored and those files are not added to the CSS extracted (which only has local files)

    even though they’re imported explicitly in my main index.js project file.

    import 'reset-css/reset.css'; /* never appears in output */
    import 'normalize.css/normalize.css'; /* never appears in output */
    import 'css/var.css';   /* First file contents in the output css */
    import 'src/index.js';  /* bundles app and module CSS into output */
    

    the generated CSS file created by ExtractTextValues starts with the contents of var.css

    :root {
      --g1: #3E4D68;
      --g2: #96999F;
      --g3: #B8BBC2;
      --g4: #DEDEDE;
      --g5: #EDEDED;
    
      --background: #EDEDED;
      --text: #2D2D2D;
      --error: #C54E4E;
      --warning: #FFB800;
      --information: #9766AD;
      --success: #4BAA64;
    }
    
  4. I’ve sorted a way in order to do this, full explanation is on this gist.

    tl;dr;

    Duplicating imports (as multi-loader does), inlining loader configuration with ident and adding (in a hackish way) loader configuration to the RuleSet’s references dictionary.

Comments are closed.