Skip to content

Commit e5e8620

Browse files
fix: respect inherit for CSS modules
1 parent 8ac68eb commit e5e8620

27 files changed

+1165
-161
lines changed

.editorconfig

+3
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ charset = utf-8-bom
1717

1818
[*.md]
1919
trim_trailing_whitespace = false
20+
21+
[*.snap]
22+
trim_trailing_whitespace = false

lib/CssModule.js

+33-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const makeSerializable = require("./util/makeSerializable");
1414
/** @typedef {import("./serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
1515
/** @typedef {import("./serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
1616

17-
/** @typedef {NormalModuleCreateData & { cssLayer: string|undefined|null, supports: string|undefined|null, media: string|undefined|null }} CSSModuleCreateData */
17+
/** @typedef {NormalModuleCreateData & { cssLayer: string|undefined|null, supports: string|undefined|null, media: string|undefined|null, inheritance: Array<[string|undefined, string|undefined, string|undefined]>|null }} CSSModuleCreateData */
1818

1919
class CssModule extends NormalModule {
2020
/**
@@ -27,6 +27,7 @@ class CssModule extends NormalModule {
2727
this.cssLayer = options.cssLayer;
2828
this.supports = options.supports;
2929
this.media = options.media;
30+
this.inheritance = options.inheritance;
3031
}
3132

3233
/**
@@ -47,6 +48,17 @@ class CssModule extends NormalModule {
4748
identifier += `|${this.media}`;
4849
}
4950

51+
if (this.inheritance) {
52+
const inheritance = this.inheritance.map(
53+
(item, index) =>
54+
`inheritance_${index}|${item[0] || ""}|${item[1] || ""}|${
55+
item[2] || ""
56+
}`
57+
);
58+
59+
identifier += `|${inheritance.join("|")}`;
60+
}
61+
5062
return identifier;
5163
}
5264

@@ -57,11 +69,21 @@ class CssModule extends NormalModule {
5769
readableIdentifier(requestShortener) {
5870
const readableIdentifier = super.readableIdentifier(requestShortener);
5971

60-
return `css ${readableIdentifier}${
61-
this.cssLayer ? ` (layer ${this.cssLayer || ""})` : ""
62-
}${this.supports ? ` (supports ${this.supports || ""})` : ""}${
63-
this.media ? ` (media ${this.media || ""})` : ""
64-
}`;
72+
let identifier = `css ${readableIdentifier}`;
73+
74+
if (this.cssLayer) {
75+
identifier += ` (layer: ${this.cssLayer})`;
76+
}
77+
78+
if (this.supports) {
79+
identifier += ` (supports: ${this.supports})`;
80+
}
81+
82+
if (this.media) {
83+
identifier += ` (media: ${this.media})`;
84+
}
85+
86+
return identifier;
6587
}
6688

6789
/**
@@ -77,6 +99,7 @@ class CssModule extends NormalModule {
7799
this.cssLayer = m.cssLayer;
78100
this.supports = m.supports;
79101
this.media = m.media;
102+
this.inheritance = m.inheritance;
80103
}
81104

82105
/**
@@ -87,6 +110,7 @@ class CssModule extends NormalModule {
87110
write(this.cssLayer);
88111
write(this.supports);
89112
write(this.media);
113+
write(this.inheritance);
90114
super.serialize(context);
91115
}
92116

@@ -114,7 +138,8 @@ class CssModule extends NormalModule {
114138
resolveOptions: null,
115139
cssLayer: null,
116140
supports: null,
117-
media: null
141+
media: null,
142+
inheritance: null
118143
});
119144
obj.deserialize(context);
120145
return obj;
@@ -128,6 +153,7 @@ class CssModule extends NormalModule {
128153
this.cssLayer = read();
129154
this.supports = read();
130155
this.media = read();
156+
this.inheritance = read();
131157
super.deserialize(context);
132158
}
133159
}

lib/css/CssModulesPlugin.js

+62-26
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,36 @@ class CssModulesPlugin {
181181
// When CSS is imported from CSS there is only one dependency
182182
const dependency = resolveData.dependencies[0];
183183

184-
return new CssModule({
185-
...createData,
186-
cssLayer: dependency.layer,
187-
supports: dependency.supports,
188-
media: dependency.media
189-
});
184+
if (dependency instanceof CssImportDependency) {
185+
const parent =
186+
/** @type {CssModule} */
187+
(compilation.moduleGraph.getParentModule(dependency));
188+
189+
if (parent instanceof CssModule) {
190+
let inheritance = [
191+
[parent.cssLayer, parent.supports, parent.media]
192+
];
193+
194+
if (parent.inheritance) {
195+
inheritance.push(...parent.inheritance);
196+
}
197+
198+
return new CssModule({
199+
...createData,
200+
cssLayer: dependency.layer,
201+
supports: dependency.supports,
202+
media: dependency.media,
203+
inheritance
204+
});
205+
}
206+
207+
return new CssModule({
208+
...createData,
209+
cssLayer: dependency.layer,
210+
supports: dependency.supports,
211+
media: dependency.media
212+
});
213+
}
190214
}
191215

192216
return new CssModule(createData);
@@ -450,29 +474,41 @@ class CssModulesPlugin {
450474
codeGenResult.sources.get("css") ||
451475
codeGenResult.sources.get("css-import");
452476

453-
if (module.media) {
454-
moduleSource = new ConcatSource(
455-
`@media ${module.media} {\n`,
456-
new PrefixSource("\t", moduleSource),
457-
"}"
458-
);
459-
}
477+
let inheritance = [[module.cssLayer, module.supports, module.media]];
460478

461-
if (module.supports) {
462-
moduleSource = new ConcatSource(
463-
`@supports (${module.supports}) {\n`,
464-
new PrefixSource("\t", moduleSource),
465-
"}"
466-
);
479+
if (module.inheritance) {
480+
inheritance.push(...module.inheritance);
467481
}
468482

469-
// Layer can be anonymous
470-
if (module.cssLayer !== undefined && module.cssLayer !== null) {
471-
moduleSource = new ConcatSource(
472-
`@layer${module.cssLayer ? ` (${module.cssLayer})` : ""} {\n`,
473-
new PrefixSource("\t", moduleSource),
474-
"}"
475-
);
483+
for (let i = 0; i < inheritance.length - 1; i++) {
484+
const layer = inheritance[i][0];
485+
const supports = inheritance[i][1];
486+
const media = inheritance[i][2];
487+
488+
if (media) {
489+
moduleSource = new ConcatSource(
490+
`@media ${media} {\n`,
491+
new PrefixSource("\t", moduleSource),
492+
"}\n"
493+
);
494+
}
495+
496+
if (supports) {
497+
moduleSource = new ConcatSource(
498+
`@supports (${supports}) {\n`,
499+
new PrefixSource("\t", moduleSource),
500+
"}\n"
501+
);
502+
}
503+
504+
// Layer can be anonymous
505+
if (layer !== undefined && layer !== null) {
506+
moduleSource = new ConcatSource(
507+
`@layer${layer ? ` ${layer}` : ""} {\n`,
508+
new PrefixSource("\t", moduleSource),
509+
"}\n"
510+
);
511+
}
476512
}
477513

478514
if (moduleSource) {

lib/css/CssParser.js

+2-13
Original file line numberDiff line numberDiff line change
@@ -197,18 +197,6 @@ class CssParser extends Parser {
197197
const isTopLevelLocal = () =>
198198
modeData === "local" ||
199199
(this.defaultMode === "local" && modeData === undefined);
200-
const eatWhiteLine = (input, pos) => {
201-
for (;;) {
202-
const cc = input.charCodeAt(pos);
203-
if (cc === 32 || cc === 9) {
204-
pos++;
205-
continue;
206-
}
207-
if (cc === 10) pos++;
208-
break;
209-
}
210-
return pos;
211-
};
212200
const eatUntil = chars => {
213201
const charCodes = Array.from({ length: chars.length }, (_, i) =>
214202
chars.charCodeAt(i)
@@ -304,7 +292,7 @@ class CssParser extends Parser {
304292
}
305293
pos++;
306294
if (pos === input.length) return pos;
307-
pos = eatWhiteLine(input, pos);
295+
pos = walkCssTokens.eatWhiteLine(input, pos);
308296
return pos;
309297
};
310298
const eatPropertyName = eatUntil(":{};");
@@ -521,6 +509,7 @@ class CssParser extends Parser {
521509
);
522510
}
523511
const semicolonPos = end;
512+
end = walkCssTokens.eatWhiteLine(input, end + 1);
524513
const { line: sl, column: sc } = locConverter.get(
525514
modeData.atRuleStart
526515
);

lib/css/walkCssTokens.js

+41-7
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,30 @@ const consumeSpace = (input, pos, callbacks) => {
9999

100100
/**
101101
* @param {number} cc char code
102-
* @returns {boolean} true, if cc is a whitespace
102+
* @returns {boolean} true, if cc is a newline
103103
*/
104-
const _isWhiteSpace = cc => {
104+
const _isNewline = cc => {
105105
return (
106-
cc === CC_LINE_FEED ||
107-
cc === CC_CARRIAGE_RETURN ||
108-
cc === CC_FORM_FEED ||
109-
cc === CC_TAB ||
110-
cc === CC_SPACE
106+
cc === CC_LINE_FEED || cc === CC_CARRIAGE_RETURN || cc === CC_FORM_FEED
111107
);
112108
};
113109

110+
/**
111+
* @param {number} cc char code
112+
* @returns {boolean} true, if cc is a space (U+0009 CHARACTER TABULATION or U+0020 SPACE)
113+
*/
114+
const _isSpace = cc => {
115+
return cc === CC_TAB || cc === CC_SPACE;
116+
};
117+
118+
/**
119+
* @param {number} cc char code
120+
* @returns {boolean} true, if cc is a whitespace
121+
*/
122+
const _isWhiteSpace = cc => {
123+
return _isNewline(cc) || _isSpace(cc);
124+
};
125+
114126
/**
115127
* @param {number} cc char code
116128
* @returns {boolean} true, if cc is a start code point of an identifier
@@ -739,3 +751,25 @@ module.exports.eatWhitespaceAndComments = (input, pos) => {
739751

740752
return pos;
741753
};
754+
755+
/**
756+
* @param {string} input input
757+
* @param {number} pos position
758+
* @returns {number} position after whitespace
759+
*/
760+
module.exports.eatWhiteLine = (input, pos) => {
761+
for (;;) {
762+
const cc = input.charCodeAt(pos);
763+
if (_isSpace(cc)) {
764+
pos++;
765+
continue;
766+
}
767+
if (_isNewLine(cc)) pos++;
768+
// For `\r\n`
769+
if (cc === CC_CARRIAGE_RETURN && input.charCodeAt(pos + 1) === CC_LINE_FEED)
770+
pos++;
771+
break;
772+
}
773+
774+
return pos;
775+
};

0 commit comments

Comments
 (0)