fix: respect inherit for CSS modules

This commit is contained in:
alexander.akait 2023-05-01 17:46:47 +03:00
parent 8ac68ebba2
commit e5e86206f8
27 changed files with 1165 additions and 161 deletions

View File

@ -17,3 +17,6 @@ charset = utf-8-bom
[*.md]
trim_trailing_whitespace = false
[*.snap]
trim_trailing_whitespace = false

View File

@ -14,7 +14,7 @@ const makeSerializable = require("./util/makeSerializable");
/** @typedef {import("./serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("./serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
/** @typedef {NormalModuleCreateData & { cssLayer: string|undefined|null, supports: string|undefined|null, media: string|undefined|null }} CSSModuleCreateData */
/** @typedef {NormalModuleCreateData & { cssLayer: string|undefined|null, supports: string|undefined|null, media: string|undefined|null, inheritance: Array<[string|undefined, string|undefined, string|undefined]>|null }} CSSModuleCreateData */
class CssModule extends NormalModule {
/**
@ -27,6 +27,7 @@ class CssModule extends NormalModule {
this.cssLayer = options.cssLayer;
this.supports = options.supports;
this.media = options.media;
this.inheritance = options.inheritance;
}
/**
@ -47,6 +48,17 @@ class CssModule extends NormalModule {
identifier += `|${this.media}`;
}
if (this.inheritance) {
const inheritance = this.inheritance.map(
(item, index) =>
`inheritance_${index}|${item[0] || ""}|${item[1] || ""}|${
item[2] || ""
}`
);
identifier += `|${inheritance.join("|")}`;
}
return identifier;
}
@ -57,11 +69,21 @@ class CssModule extends NormalModule {
readableIdentifier(requestShortener) {
const readableIdentifier = super.readableIdentifier(requestShortener);
return `css ${readableIdentifier}${
this.cssLayer ? ` (layer ${this.cssLayer || ""})` : ""
}${this.supports ? ` (supports ${this.supports || ""})` : ""}${
this.media ? ` (media ${this.media || ""})` : ""
}`;
let identifier = `css ${readableIdentifier}`;
if (this.cssLayer) {
identifier += ` (layer: ${this.cssLayer})`;
}
if (this.supports) {
identifier += ` (supports: ${this.supports})`;
}
if (this.media) {
identifier += ` (media: ${this.media})`;
}
return identifier;
}
/**
@ -77,6 +99,7 @@ class CssModule extends NormalModule {
this.cssLayer = m.cssLayer;
this.supports = m.supports;
this.media = m.media;
this.inheritance = m.inheritance;
}
/**
@ -87,6 +110,7 @@ class CssModule extends NormalModule {
write(this.cssLayer);
write(this.supports);
write(this.media);
write(this.inheritance);
super.serialize(context);
}
@ -114,7 +138,8 @@ class CssModule extends NormalModule {
resolveOptions: null,
cssLayer: null,
supports: null,
media: null
media: null,
inheritance: null
});
obj.deserialize(context);
return obj;
@ -128,6 +153,7 @@ class CssModule extends NormalModule {
this.cssLayer = read();
this.supports = read();
this.media = read();
this.inheritance = read();
super.deserialize(context);
}
}

View File

@ -181,12 +181,36 @@ class CssModulesPlugin {
// When CSS is imported from CSS there is only one dependency
const dependency = resolveData.dependencies[0];
return new CssModule({
...createData,
cssLayer: dependency.layer,
supports: dependency.supports,
media: dependency.media
});
if (dependency instanceof CssImportDependency) {
const parent =
/** @type {CssModule} */
(compilation.moduleGraph.getParentModule(dependency));
if (parent instanceof CssModule) {
let inheritance = [
[parent.cssLayer, parent.supports, parent.media]
];
if (parent.inheritance) {
inheritance.push(...parent.inheritance);
}
return new CssModule({
...createData,
cssLayer: dependency.layer,
supports: dependency.supports,
media: dependency.media,
inheritance
});
}
return new CssModule({
...createData,
cssLayer: dependency.layer,
supports: dependency.supports,
media: dependency.media
});
}
}
return new CssModule(createData);
@ -450,29 +474,41 @@ class CssModulesPlugin {
codeGenResult.sources.get("css") ||
codeGenResult.sources.get("css-import");
if (module.media) {
moduleSource = new ConcatSource(
`@media ${module.media} {\n`,
new PrefixSource("\t", moduleSource),
"}"
);
let inheritance = [[module.cssLayer, module.supports, module.media]];
if (module.inheritance) {
inheritance.push(...module.inheritance);
}
if (module.supports) {
moduleSource = new ConcatSource(
`@supports (${module.supports}) {\n`,
new PrefixSource("\t", moduleSource),
"}"
);
}
for (let i = 0; i < inheritance.length - 1; i++) {
const layer = inheritance[i][0];
const supports = inheritance[i][1];
const media = inheritance[i][2];
// Layer can be anonymous
if (module.cssLayer !== undefined && module.cssLayer !== null) {
moduleSource = new ConcatSource(
`@layer${module.cssLayer ? ` (${module.cssLayer})` : ""} {\n`,
new PrefixSource("\t", moduleSource),
"}"
);
if (media) {
moduleSource = new ConcatSource(
`@media ${media} {\n`,
new PrefixSource("\t", moduleSource),
"}\n"
);
}
if (supports) {
moduleSource = new ConcatSource(
`@supports (${supports}) {\n`,
new PrefixSource("\t", moduleSource),
"}\n"
);
}
// Layer can be anonymous
if (layer !== undefined && layer !== null) {
moduleSource = new ConcatSource(
`@layer${layer ? ` ${layer}` : ""} {\n`,
new PrefixSource("\t", moduleSource),
"}\n"
);
}
}
if (moduleSource) {

View File

@ -197,18 +197,6 @@ class CssParser extends Parser {
const isTopLevelLocal = () =>
modeData === "local" ||
(this.defaultMode === "local" && modeData === undefined);
const eatWhiteLine = (input, pos) => {
for (;;) {
const cc = input.charCodeAt(pos);
if (cc === 32 || cc === 9) {
pos++;
continue;
}
if (cc === 10) pos++;
break;
}
return pos;
};
const eatUntil = chars => {
const charCodes = Array.from({ length: chars.length }, (_, i) =>
chars.charCodeAt(i)
@ -304,7 +292,7 @@ class CssParser extends Parser {
}
pos++;
if (pos === input.length) return pos;
pos = eatWhiteLine(input, pos);
pos = walkCssTokens.eatWhiteLine(input, pos);
return pos;
};
const eatPropertyName = eatUntil(":{};");
@ -521,6 +509,7 @@ class CssParser extends Parser {
);
}
const semicolonPos = end;
end = walkCssTokens.eatWhiteLine(input, end + 1);
const { line: sl, column: sc } = locConverter.get(
modeData.atRuleStart
);

View File

@ -97,18 +97,30 @@ const consumeSpace = (input, pos, callbacks) => {
return pos;
};
/**
* @param {number} cc char code
* @returns {boolean} true, if cc is a newline
*/
const _isNewline = cc => {
return (
cc === CC_LINE_FEED || cc === CC_CARRIAGE_RETURN || cc === CC_FORM_FEED
);
};
/**
* @param {number} cc char code
* @returns {boolean} true, if cc is a space (U+0009 CHARACTER TABULATION or U+0020 SPACE)
*/
const _isSpace = cc => {
return cc === CC_TAB || cc === CC_SPACE;
};
/**
* @param {number} cc char code
* @returns {boolean} true, if cc is a whitespace
*/
const _isWhiteSpace = cc => {
return (
cc === CC_LINE_FEED ||
cc === CC_CARRIAGE_RETURN ||
cc === CC_FORM_FEED ||
cc === CC_TAB ||
cc === CC_SPACE
);
return _isNewline(cc) || _isSpace(cc);
};
/**
@ -739,3 +751,25 @@ module.exports.eatWhitespaceAndComments = (input, pos) => {
return pos;
};
/**
* @param {string} input input
* @param {number} pos position
* @returns {number} position after whitespace
*/
module.exports.eatWhiteLine = (input, pos) => {
for (;;) {
const cc = input.charCodeAt(pos);
if (_isSpace(cc)) {
pos++;
continue;
}
if (_isNewLine(cc)) pos++;
// For `\r\n`
if (cc === CC_CARRIAGE_RETURN && input.charCodeAt(pos + 1) === CC_LINE_FEED)
pos++;
break;
}
return pos;
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
.class {
deep-deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./all-deep-deep-nested.css" layer(baz) supports(display: table) screen and (min-width: 600px);
.class {
deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./all-deep-nested.css" layer(bar) supports(display: grid) screen and (min-width: 500px);
.class {
nested: 1;
}

View File

@ -0,0 +1,3 @@
.class {
deep-deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./anonymous-deep-deep-nested.css" layer;
.class {
deep-nested: 1;
}

View File

@ -0,0 +1,6 @@
@import "./anonymous-deep-nested.css" layer;
@import "./layer-deep-nested.css" layer(base);
.class {
deep-nested: 1;
}

View File

@ -0,0 +1,6 @@
@import url("./style8.css") supports(display: flex);
@import url("./style8.css") supports(display: flex);
.class {
duplicate-nested: true;
}

View File

@ -0,0 +1,3 @@
.class {
deep-deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./layer-deep-deep-nested.css" layer(baz);
.class {
deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./layer-deep-nested.css" layer(bar);
.class {
nested: 1;
}

View File

@ -0,0 +1,3 @@
.class {
deep-deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./media-deep-deep-nested.css" screen and (orientation: portrait);
.class {
deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./media-deep-nested.css" screen and (max-width: 500px);
.class {
nested: 1;
}

View File

@ -0,0 +1,3 @@
.class {
deep-deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./mixed-deep-deep-nested.css" layer(bar);
.class {
deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./mixed-deep-nested.css" supports(display: flex);
.class {
nested: 1;
}

View File

@ -193,6 +193,17 @@ url(style6.css?foo=14)
@import url("./style10.css");
@import "./media-nested.css" screen and (min-width: 400px);
@import "./supports-nested.css" supports(display: flex);
@import "./layer-nested.css" layer(foo);
@import "./all-nested.css" layer(foo) supports(display: flex) screen and (min-width: 400px);
@import "./mixed-nested.css" screen and (min-width: 400px);
@import "./anonymous-nested.css" layer;
@import "./media-deep-deep-nested.css" screen and (orientation: portrait);
@import "./duplicate-nested.css" screen and (orientation: portrait);
@import "./anonymous-nested.css" supports(display: flex) screen and (orientation: portrait);
@import "./all-nested.css" layer(super.foo) supports(display: flex) screen and (min-width: 400px);
body {
background: red;
}

View File

@ -0,0 +1,3 @@
.class {
deep-deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./supports-deep-deep-nested.css" supports(display: table);
.class {
deep-nested: 1;
}

View File

@ -0,0 +1,5 @@
@import "./supports-deep-nested.css" supports(display: grid);
.class {
nested: 1;
}