Skip to content

Commit 561ee9b

Browse files
feat: added CssModulesPlugin.getCompilationHooks(compilation).orderModules hook
1 parent dba0399 commit 561ee9b

20 files changed

Lines changed: 249 additions & 10 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": minor
3+
---
4+
5+
Add `CssModulesPlugin.getCompilationHooks(compilation).orderModules` hook. The hook is called once per CSS source type (CSS imports, CSS modules) with the chunk's modules pre-sorted by full module name; taps may return an ordered `Module[]` to override webpack's default import-order topological sort, or return `undefined` to keep the default. This lets a plugin enforce a deterministic CSS module order (e.g. by file path) and side-step the "Conflicting order between css ..." warning.

lib/css/CssModulesPlugin.js

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
"use strict";
77

8-
const { SyncHook, SyncWaterfallHook } = require("tapable");
8+
const { SyncBailHook, SyncHook, SyncWaterfallHook } = require("tapable");
99
const {
1010
CachedSource,
1111
ConcatSource,
@@ -93,6 +93,7 @@ const CssParser = require("./CssParser");
9393
* @typedef {object} CompilationHooks
9494
* @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModulePackage
9595
* @property {SyncHook<[Chunk, Hash, ChunkHashContext]>} chunkHash
96+
* @property {SyncBailHook<[Chunk, Module[], Compilation], Module[] | undefined | void>} orderModules called for each CSS source type (CSS_IMPORT_TYPE, CSS_TYPE) with the chunk's modules pre-sorted by full module name; return an ordered `Module[]` to override the default import-order topological sort, or return `undefined` to keep the default
9697
*/
9798

9899
/**
@@ -160,7 +161,8 @@ class CssModulesPlugin {
160161
"module",
161162
"renderContext"
162163
]),
163-
chunkHash: new SyncHook(["chunk", "hash", "context"])
164+
chunkHash: new SyncHook(["chunk", "hash", "context"]),
165+
orderModules: new SyncBailHook(["chunk", "modules", "compilation"])
164166
};
165167
compilationHooksMap.set(compilation, hooks);
166168
}
@@ -869,24 +871,33 @@ class CssModulesPlugin {
869871
/** @type {string | undefined} */
870872
let charset;
871873

874+
const hooks = CssModulesPlugin.getCompilationHooks(compilation);
875+
876+
/**
877+
* @param {Iterable<Module> | undefined} iter modules pre-sorted by full module name
878+
* @returns {Module[]} ordered modules
879+
*/
880+
const orderModules = (iter) => {
881+
const modules = iter ? [...iter] : [];
882+
const result = hooks.orderModules.call(chunk, modules, compilation);
883+
if (result !== undefined) return result;
884+
return this.getModulesInOrder(chunk, modules, compilation);
885+
};
886+
872887
return /** @type {CssModule[]} */ ([
873-
...this.getModulesInOrder(
874-
chunk,
888+
...orderModules(
875889
chunkGraph.getOrderedChunkModulesIterableBySourceType(
876890
chunk,
877891
CSS_IMPORT_TYPE,
878892
compareModulesByFullName(compilation.compiler)
879-
),
880-
compilation
893+
)
881894
),
882-
...this.getModulesInOrder(
883-
chunk,
895+
...orderModules(
884896
chunkGraph.getOrderedChunkModulesIterableBySourceType(
885897
chunk,
886898
CSS_TYPE,
887899
compareModulesByFullName(compilation.compiler)
888-
),
889-
compilation
900+
)
890901
).map((module) => {
891902
if (
892903
typeof (/** @type {BuildInfo} */ (module.buildInfo).charset) !==
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
color: a;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
color: b;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
color: c;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
color: d;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
color: e;
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { expectCssOrder } from "./helpers";
2+
3+
it("orderModules: returning undefined falls back to the default import-order sort", done => {
4+
expectCssOrder("fallback", 2, "bcdea123", done);
5+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import fs from "fs";
2+
import path from "path";
3+
4+
export function expectCssOrder(variant, idx, expected, done) {
5+
__non_webpack_require__(`./${variant}-lazy4_js.bundle${idx}.js`);
6+
Promise.all([
7+
import("./lazy1.css"),
8+
import("./lazy2.css"),
9+
import("./lazy3.css"),
10+
import("./lazy4.js")
11+
]).then(() => {
12+
try {
13+
const matches = fs
14+
.readFileSync(
15+
path.join(__dirname, `${variant}-css.bundle${idx}.css`),
16+
"utf-8"
17+
)
18+
.match(/color: ([a-z0-9])/g)
19+
.map(match => match[7]);
20+
expect(matches).toEqual(expected.split(""));
21+
done();
22+
} catch (e) {
23+
done(e);
24+
}
25+
}, done);
26+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@import "b.css";
2+
@import "c.css";
3+
@import "a.css";
4+
5+
body {
6+
color: 1;
7+
}

0 commit comments

Comments
 (0)