fix: respect inherit for CSS modules
This commit is contained in:
parent
8ac68ebba2
commit
e5e86206f8
|
@ -17,3 +17,6 @@ charset = utf-8-bom
|
|||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.snap]
|
||||
trim_trailing_whitespace = false
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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
|
@ -0,0 +1,3 @@
|
|||
.class {
|
||||
deep-deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./all-deep-deep-nested.css" layer(baz) supports(display: table) screen and (min-width: 600px);
|
||||
|
||||
.class {
|
||||
deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./all-deep-nested.css" layer(bar) supports(display: grid) screen and (min-width: 500px);
|
||||
|
||||
.class {
|
||||
nested: 1;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.class {
|
||||
deep-deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./anonymous-deep-deep-nested.css" layer;
|
||||
|
||||
.class {
|
||||
deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@import "./anonymous-deep-nested.css" layer;
|
||||
@import "./layer-deep-nested.css" layer(base);
|
||||
|
||||
.class {
|
||||
deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@import url("./style8.css") supports(display: flex);
|
||||
@import url("./style8.css") supports(display: flex);
|
||||
|
||||
.class {
|
||||
duplicate-nested: true;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.class {
|
||||
deep-deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./layer-deep-deep-nested.css" layer(baz);
|
||||
|
||||
.class {
|
||||
deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./layer-deep-nested.css" layer(bar);
|
||||
|
||||
.class {
|
||||
nested: 1;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.class {
|
||||
deep-deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./media-deep-deep-nested.css" screen and (orientation: portrait);
|
||||
|
||||
.class {
|
||||
deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./media-deep-nested.css" screen and (max-width: 500px);
|
||||
|
||||
.class {
|
||||
nested: 1;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.class {
|
||||
deep-deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./mixed-deep-deep-nested.css" layer(bar);
|
||||
|
||||
.class {
|
||||
deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./mixed-deep-nested.css" supports(display: flex);
|
||||
|
||||
.class {
|
||||
nested: 1;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.class {
|
||||
deep-deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./supports-deep-deep-nested.css" supports(display: table);
|
||||
|
||||
.class {
|
||||
deep-nested: 1;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import "./supports-deep-nested.css" supports(display: grid);
|
||||
|
||||
.class {
|
||||
nested: 1;
|
||||
}
|
Loading…
Reference in New Issue