Ordering and conflicting order warning

This commit is contained in:
Tobias Koppers 2021-12-17 18:10:00 +01:00
parent 16784692d3
commit 277a8bb19e
13 changed files with 221 additions and 20 deletions

View File

@ -15,9 +15,7 @@ const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifier
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
const CssUrlDependency = require("../dependencies/CssUrlDependency");
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
const {
compareModulesByPostOrderIndexOrIdentifier
} = require("../util/comparators");
const { compareModulesByIdentifier } = require("../util/comparators");
const createSchemaValidation = require("../util/create-schema-validation");
const createHash = require("../util/createHash");
const memoize = require("../util/memoize");
@ -192,7 +190,8 @@ class CssModulesPlugin {
chunk,
chunkGraph,
codeGenerationResults,
uniqueName: compilation.outputOptions.uniqueName
uniqueName: compilation.outputOptions.uniqueName,
compilation
}),
filenameTemplate: CssModulesPlugin.getChunkFilenameTemplate(
chunk,
@ -236,24 +235,140 @@ class CssModulesPlugin {
);
}
getOrderedChunkCssModules(chunk, chunkGraph) {
const cssImports = chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css-import",
// TODO improve order function
compareModulesByPostOrderIndexOrIdentifier(chunkGraph.moduleGraph)
);
const cssContent = chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css",
// TODO improve order function
compareModulesByPostOrderIndexOrIdentifier(chunkGraph.moduleGraph)
);
return [...(cssImports || []), ...(cssContent || [])];
getModulesInOrder(chunk, modules, compilation) {
if (!modules) return [];
const modulesList = [...modules];
// Get ordered list of modules per chunk group
// Lists are in reverse order to allow to use Array.pop()
const modulesByChunkGroup = Array.from(chunk.groupsIterable, chunkGroup => {
const sortedModules = modulesList
.map(module => {
return {
module,
index: chunkGroup.getModulePostOrderIndex(module)
};
})
.filter(item => item.index !== undefined)
.sort((a, b) => b.index - a.index)
.map(item => item.module);
return { list: sortedModules, set: new Set(sortedModules) };
});
if (modulesByChunkGroup.length === 1)
return modulesByChunkGroup[0].list.reverse();
const compareModuleLists = ({ list: a }, { list: b }) => {
if (a.length === 0) {
return b.length === 0 ? 0 : 1;
} else {
if (b.length === 0) return -1;
return compareModulesByIdentifier(a[a.length - 1], b[b.length - 1]);
}
};
modulesByChunkGroup.sort(compareModuleLists);
const finalModules = [];
for (;;) {
const failedModules = new Set();
const list = modulesByChunkGroup[0].list;
if (list.length === 0) {
// done, everything empty
break;
}
let selectedModule = list[list.length - 1];
let hasFailed = undefined;
outer: for (;;) {
for (const { list, set } of modulesByChunkGroup) {
if (list.length === 0) continue;
const lastModule = list[list.length - 1];
if (lastModule === selectedModule) continue;
if (!set.has(selectedModule)) continue;
failedModules.add(selectedModule);
if (failedModules.has(lastModule)) {
// There is a conflict, try other alternatives
hasFailed = lastModule;
continue;
}
selectedModule = lastModule;
hasFailed = false;
continue outer; // restart
}
break;
}
if (hasFailed) {
// There is a not resolve-able conflict with the selectedModule
if (compilation) {
// TODO print better warning
compilation.warnings.push(
new Error(
`chunk ${
chunk.name || chunk.id
}\nConflicting order between ${hasFailed.readableIdentifier(
compilation.requestShortener
)} and ${selectedModule.readableIdentifier(
compilation.requestShortener
)}`
)
);
}
selectedModule = hasFailed;
}
// Insert the selected module into the final modules list
finalModules.push(selectedModule);
// Remove the selected module from all lists
for (const { list, set } of modulesByChunkGroup) {
const lastModule = list[list.length - 1];
if (lastModule === selectedModule) list.pop();
else if (hasFailed && set.has(selectedModule)) {
const idx = list.indexOf(selectedModule);
if (idx >= 0) list.splice(idx, 1);
}
}
modulesByChunkGroup.sort(compareModuleLists);
}
return finalModules;
}
renderChunk({ uniqueName, chunk, chunkGraph, codeGenerationResults }) {
const modules = this.getOrderedChunkCssModules(chunk, chunkGraph);
getOrderedChunkCssModules(chunk, chunkGraph, compilation) {
return [
...this.getModulesInOrder(
chunk,
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css-import",
compareModulesByIdentifier
),
compilation
),
...this.getModulesInOrder(
chunk,
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css",
compareModulesByIdentifier
),
compilation
)
];
}
renderChunk({
uniqueName,
chunk,
chunkGraph,
codeGenerationResults,
compilation
}) {
const modules = this.getOrderedChunkCssModules(
chunk,
chunkGraph,
compilation
);
const source = new ConcatSource();
const metaData = [];
for (const module of modules) {

View File

@ -0,0 +1,3 @@
body {
color: a;
}

View File

@ -0,0 +1,3 @@
body {
color: b;
}

View File

@ -0,0 +1,3 @@
body {
color: c;
}

View File

@ -0,0 +1,3 @@
body {
color: d;
}

View File

@ -0,0 +1,3 @@
body {
color: e;
}

View File

@ -0,0 +1,23 @@
import fs from "fs";
import path from "path";
it("should lead to conflicting order warning", done => {
__non_webpack_require__("./lazy4_js.bundle0.js");
Promise.all([
import("./lazy1.css"),
import("./lazy2.css"),
import("./lazy3.css"),
import("./lazy4.js")
]).then(() => {
try {
const matches = fs
.readFileSync(path.join(__dirname, "css.bundle0.css"), "utf-8")
.match(/color: ([a-z0-9])/g)
.map(match => match[7]);
expect(matches).toEqual("bcdea123".split(""));
done();
} catch (e) {
done(e);
}
}, done);
});

View File

@ -0,0 +1,7 @@
@import "b.css";
@import "c.css";
@import "a.css";
body {
color: 1;
}

View File

@ -0,0 +1,7 @@
@import "c.css";
@import "b.css";
@import "a.css";
body {
color: 2;
}

View File

@ -0,0 +1,6 @@
@import "d.css";
@import "a.css";
body {
color: 3;
}

View File

@ -0,0 +1,2 @@
import "./e.css";
import "./a.css";

View File

@ -0,0 +1 @@
module.exports = [[/Conflicting order between \.\/b\.css and \.\/c\.css/]];

View File

@ -0,0 +1,25 @@
/** @type {import("../../../../").Configuration} */
module.exports = {
target: "web",
mode: "development",
experiments: {
css: true
},
optimization: {
splitChunks: {
cacheGroups: {
css: {
type: "css",
enforce: true,
name: "css"
}
}
}
},
externalsPresets: {
node: true
},
node: {
__dirname: false
}
};