Skip to content

Option to turn on deduplication for @import #1233

Closed
@jasongrout

Description

@jasongrout
  • Operating System: macOS Catalina
  • Node Version: 14
  • NPM Version: 6.14
  • webpack Version: 5.4
  • css-loader Version: 4.3.0

Feature Proposal

Since 3.3.1, CSS @import modules are not deduplicated (removed in 1fb5134, for example). Deduplication was reenabled later, but only for CSS modules (see #1040 (comment) and #1044, which tried to reenable this for @import).

We'd like an option to turn back on deduplication for @import. For example:

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          import: {
            deduplication: true
          },
        },
      },
    ],
  },
};

We recognize in general this affects the order of css on the page, so it probably should default to false. However, providing this option can drastically improve applications that use lots of @import statements to include well-structured css with lots of duplicates.

Feature Use Case

In our case (JupyterLab), we have over 100 packages bundled together with webpack, many of which have css files. Each package uses @import to include the css of its dependencies in its main css file, but no package puts css on the page (i.e., no package imports its css into its javascript files). The top-level application is responsible for putting all the css on the page, and it accomplishes this with a single css file that @imports its direct dependency's css files. In essence, we have a dependency graph of css files which the top-level application is responsible for getting onto the page. For the application as a whole, we have many cases where a particular dependency css file is @imported dozens of times through this dependency graph of css files. This worked fine up through css-loader 3.3.1, since deduplication guaranteed that any given module's css file was only actually included once on the page, in a topographically correct order.

However, css-loader over version 3.3.1 does not do this deduplication, so we end up with thousands of DOM style tags, most of which are duplicates of others (for example, one dependency's CSS is on the page dozens of times). Our application slows to a crawl, and the startup time is at least 10x longer. More specifically, with css-loader 4.3.0, we end up with 23,151 style DOM elements on the page (without deduplication), but when I manually change the css-loader code to reenable deduplication for @import, this shrinks to 175 style DOM elements (and application load times are normal again). As it is, we won't be able to upgrade past css-loader 3.3.1 without either a major restructuring of our css (possibly losing the benefits of our approach) or abandoning or forking css-loader.

Essentially, we would love an option to effect this change:

diff --git a/src/plugins/postcss-import-parser.js b/src/plugins/postcss-import-parser.js
index b0698a6..8e73944 100644
--- a/src/plugins/postcss-import-parser.js
+++ b/src/plugins/postcss-import-parser.js
@@ -178,13 +178,13 @@ const plugin = (options = {}) => {
                 });
               }
 
-              options.api.push({ importName, media, index });
+              options.api.push({ importName, media, index, dedup: options.deduplicate });
 
               // eslint-disable-next-line no-continue
               continue;
             }
 
-            options.api.push({ url, media, index });
+            options.api.push({ url, media, index, dedup: options.deduplicate });
           }
         },
       };

If you are okay with merging such an option, I can also work on an implementation, if that helps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions