Skip to content

Commit b9a600c

Browse files
feat: added support for @supports, @layer and @media from @import at-rules
1 parent 74fa1cf commit b9a600c

13 files changed

+161
-27
lines changed

src/runtime/injectStylesIntoStyleTag.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ function modulesToDom(list, options) {
3030
css: item[1],
3131
media: item[2],
3232
sourceMap: item[3],
33+
supports: item[4],
34+
layer: item[5],
3335
};
3436

3537
if (index !== -1) {
@@ -59,7 +61,9 @@ function addStyle(obj, options) {
5961
if (
6062
newObj.css === obj.css &&
6163
newObj.media === obj.media &&
62-
newObj.sourceMap === obj.sourceMap
64+
newObj.sourceMap === obj.sourceMap &&
65+
newObj.supports === obj.supports &&
66+
newObj.layer === obj.layer
6367
) {
6468
return;
6569
}

src/runtime/singletonStyleDomAPI.js

+35-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,41 @@ const replaceText = (function replaceText() {
1111

1212
/* istanbul ignore next */
1313
function apply(style, index, remove, obj) {
14-
const css = remove
15-
? ""
16-
: obj.media
17-
? `@media ${obj.media} {${obj.css}}`
18-
: obj.css;
14+
let css;
15+
16+
if (remove) {
17+
css = "";
18+
} else {
19+
css = "";
20+
21+
if (obj.supports) {
22+
css += `@supports (${obj.supports}) {`;
23+
}
24+
25+
if (obj.media) {
26+
css += `@media ${obj.media} {`;
27+
}
28+
29+
const needLayer = typeof obj.layer !== "undefined";
30+
31+
if (needLayer) {
32+
css += `@layer${obj.layer.length > 0 ? ` ${obj.layer}` : ""} {`;
33+
}
34+
35+
css += obj.css;
36+
37+
if (needLayer) {
38+
css += "}";
39+
}
40+
41+
if (obj.media) {
42+
css += "}";
43+
}
44+
45+
if (obj.supports) {
46+
css += "}";
47+
}
48+
}
1949

2050
// For old IE
2151
/* istanbul ignore if */

src/runtime/styleDomAPI.js

+29-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
11
/* istanbul ignore next */
22
function apply(style, options, obj) {
3-
let css = obj.css;
4-
const media = obj.media;
5-
const sourceMap = obj.sourceMap;
3+
let css = "";
4+
5+
if (obj.supports) {
6+
css += `@supports (${obj.supports}) {`;
7+
}
8+
9+
if (obj.media) {
10+
css += `@media ${obj.media} {`;
11+
}
12+
13+
const needLayer = typeof obj.layer !== "undefined";
614

7-
if (media) {
8-
style.setAttribute("media", media);
9-
} else {
10-
style.removeAttribute("media");
15+
if (needLayer) {
16+
css += `@layer${obj.layer.length > 0 ? ` ${obj.layer}` : ""} {`;
1117
}
1218

19+
css += obj.css;
20+
21+
if (needLayer) {
22+
css += "}";
23+
}
24+
25+
if (obj.media) {
26+
css += "}";
27+
}
28+
29+
if (obj.supports) {
30+
css += "}";
31+
}
32+
33+
const sourceMap = obj.sourceMap;
34+
1335
if (sourceMap && typeof btoa !== "undefined") {
1436
css += `\n/*# sourceMappingURL=data:application/json;base64,${btoa(
1537
unescape(encodeURIComponent(JSON.stringify(sourceMap)))

test/loader.test.js

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
runInJsDom,
1515
} from "./helpers/index";
1616

17+
jest.setTimeout(10000);
18+
1719
describe("loader", () => {
1820
const injectTypes = [
1921
"styleTag",

test/manual/index.html

+8
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ <h2>Modules</h2>
9191
<h2>Toggle</h2>
9292
</section>
9393

94+
<section>
95+
<h2>Media and Supports</h2>
96+
<div class="bottom">BLUE</div>
97+
<div class="middle">GREEN</div>
98+
<div class="top">RED</div>
99+
<div class="media-and-supports">RED</div>
100+
</section>
101+
94102
<section>
95103
<h2>Custom element</h2>
96104
<custom-square w="100" h="100"></custom-square>

test/manual/src/bottom.css

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.bottom {
2+
color: blue;
3+
}
4+
5+
.media-and-supports {
6+
color: blue;
7+
}

test/manual/src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import api2, {
2929
namedExportLazyBlue,
3030
namedExportLazyBackground,
3131
} from "./style.named-export.lazy.module.css";
32+
import "./top.css";
3233

3334
console.log("___LOCALS___");
3435
console.log(component);

test/manual/src/middle.css

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@import url("./bottom.css") supports(display: grid) screen and
2+
(max-width: 2400px);
3+
4+
.middle {
5+
color: green;
6+
}
7+
8+
.media-and-supports {
9+
color: green;
10+
}

test/manual/src/style.css

-5
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,3 @@
99
.blue {
1010
color: blue;
1111
}
12-
13-
.background {
14-
height: 1200px;
15-
background: url("./logo.png") center no-repeat;
16-
}

test/manual/src/top.css

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@import url("./middle.css") supports(display: flex) screen and
2+
(min-width: 400px);
3+
4+
.top {
5+
color: red;
6+
}
7+
8+
.media-and-supports {
9+
color: red;
10+
}

test/manual/webpack.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ module.exports = {
246246
},
247247
devServer: {
248248
hot: true,
249+
liveReload: false,
249250
static: __dirname,
250251
},
251252
};

test/runtime/__snapshots__/injectStylesIntoStyleTag.test.js.snap

+15-9
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ exports[`addStyle should work with "insert" option 1`] = `"<head><title>Title</t
3838
3939
exports[`addStyle should work with "nonce" attribute and "__webpack_nonce__" variable 1`] = `"<head><title>Title</title><style nonce=\\"87654321\\">.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
4040
41+
exports[`addStyle should work with empty layer 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
42+
4143
exports[`addStyle should work with empty modules list #2 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
4244
4345
exports[`addStyle should work with empty modules list #2 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
@@ -56,7 +58,9 @@ exports[`addStyle should work with empty modules list 1`] = `"<head><title>Title
5658
5759
exports[`addStyle should work with empty modules list 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
5860
59-
exports[`addStyle should work with media 1`] = `"<head><title>Title</title><style media=\\"screen and (min-width:320px)\\">.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
61+
exports[`addStyle should work with layer 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
62+
63+
exports[`addStyle should work with media 1`] = `"<head><title>Title</title><style>@media screen and (min-width:320px) {.foo { color: red }}</style></head><body><h1>Hello world</h1></body>"`;
6064
6165
exports[`addStyle should work with multiple styles 1`] = `"<head><title>Title</title><style>.foo { color: red }</style><style>.bar { color: blue }</style></head><body><h1>Hello world</h1></body>"`;
6266
@@ -67,6 +71,8 @@ exports[`addStyle should work with source maps 1`] = `
6771
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTUuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS01LmNzcyIsInNvdXJjZXNDb250ZW50IjpbIi5mb28geyBjb2xvcjogcmVkIH0iXX0= */</style></head><body><h1>Hello world</h1></body>"
6872
`;
6973
74+
exports[`addStyle should work with supports 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
75+
7076
exports[`addStyle should work with updates #2 1`] = `"<head><title>Title</title><style>.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
7177
7278
exports[`addStyle should work with updates #2 2`] = `"<head><title>Title</title><style>.foo { color: blue }</style></head><body><h1>Hello world</h1></body>"`;
@@ -79,9 +85,9 @@ exports[`addStyle should work with updates #4 1`] = `"<head><title>Title</title>
7985
8086
exports[`addStyle should work with updates #4 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
8187
82-
exports[`addStyle should work with updates #5 1`] = `"<head><title>Title</title><style media=\\"screen and (min-width:320px)\\">.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
88+
exports[`addStyle should work with updates #5 1`] = `"<head><title>Title</title><style>@media screen and (min-width:320px) {.foo { color: red }}</style></head><body><h1>Hello world</h1></body>"`;
8389
84-
exports[`addStyle should work with updates #5 2`] = `"<head><title>Title</title><style media=\\"screen and (min-width:640px)\\">.foo { color: blue }</style></head><body><h1>Hello world</h1></body>"`;
90+
exports[`addStyle should work with updates #5 2`] = `"<head><title>Title</title><style>@media screen and (min-width:640px) {.foo { color: blue }}</style></head><body><h1>Hello world</h1></body>"`;
8591
8692
exports[`addStyle should work with updates #6 1`] = `"<head><title>Title</title><style>.foo { color: red }.bar { color: yellow }</style></head><body><h1>Hello world</h1></body>"`;
8793
@@ -102,14 +108,14 @@ exports[`addStyle should work with updates #7 1`] = `"<head><title>Title</title>
102108
exports[`addStyle should work with updates #7 2`] = `"<head><title>Title</title><style>.foo { color: green }</style><style>.bar { color: black }</style></head><body><h1>Hello world</h1></body>"`;
103109
104110
exports[`addStyle should work with updates #8 1`] = `
105-
"<head><title>Title</title><style media=\\"screen and (min-width: 320px)\\">.foo { color: red }
106-
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDMyMHB4KSB7IC5mb28geyBjb2xvcjogcmVkIH0gfSJdfQ== */</style><style media=\\"screen and (max-width: 240px)\\">.bar { color: yellow }
111+
"<head><title>Title</title><style>@media screen and (min-width: 320px) {.foo { color: red }}
112+
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDMyMHB4KSB7IC5mb28geyBjb2xvcjogcmVkIH0gfSJdfQ== */</style><style>@media screen and (max-width: 240px) {.bar { color: yellow }}
107113
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTIuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0yLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDI0MHB4KSB7IC5iYXIgeyBjb2xvcjogeWVsbG93IH0gfSJdfQ== */</style></head><body><h1>Hello world</h1></body>"
108114
`;
109115
110116
exports[`addStyle should work with updates #8 2`] = `
111-
"<head><title>Title</title><style media=\\"screen and (min-width: 640px)\\">.foo { color: black }
112-
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJCQkJCLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtaW4td2lkdGg6IDY0MHB4KSB7IC5mb28geyBjb2xvcjogYmxhY2sgfSB9Il19 */</style><style media=\\"screen and (max-width: 1240px)\\">.bar { color: black }
117+
"<head><title>Title</title><style>@media screen and (min-width: 640px) {.foo { color: black }}
118+
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJCQkJCLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtaW4td2lkdGg6IDY0MHB4KSB7IC5mb28geyBjb2xvcjogYmxhY2sgfSB9Il19 */</style><style>@media screen and (max-width: 1240px) {.bar { color: black }}
113119
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTIuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJCQkJCLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0yLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDEyNDBweCkgeyAuYmFyIHsgY29sb3I6IGJsYWNrIH0gfSJdfQ== */</style></head><body><h1>Hello world</h1></body>"
114120
`;
115121
@@ -127,9 +133,9 @@ exports[`addStyle should work with updates #11 1`] = `"<head><title>Title</title
127133
128134
exports[`addStyle should work with updates #11 2`] = `"<head><title>Title</title><style>.foo { color: black }</style><style>.bar { color: white }</style><script src=\\"https://example.com/script.js\\" id=\\"id\\"></script></head><body><h1>Hello world</h1></body>"`;
129135
130-
exports[`addStyle should work with updates #12 1`] = `"<head><title>Title</title><style>.order { color: red }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: blue }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: red }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style media=\\"screen and (min-width: 2000px)\\">.order { color: blue }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style></head><body><h1>Hello world</h1></body>"`;
136+
exports[`addStyle should work with updates #12 1`] = `"<head><title>Title</title><style>.order { color: red }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: blue }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: red }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>@media screen and (min-width: 2000px) {.order { color: blue }}</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style></head><body><h1>Hello world</h1></body>"`;
131137
132-
exports[`addStyle should work with updates #12 2`] = `"<head><title>Title</title><style>.order { color: orange }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: blue }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: orange }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style media=\\"screen and (min-width: 2000px)\\">.order { color: blue }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style></head><body><h1>Hello world</h1></body>"`;
138+
exports[`addStyle should work with updates #12 2`] = `"<head><title>Title</title><style>.order { color: orange }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: blue }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>.order { color: orange }</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style><style>@media screen and (min-width: 2000px) {.order { color: blue }}</style><style>.@import url(\\"https://fonts.googleapis.com/css?family=Roboto&display=swap\\");</style></head><body><h1>Hello world</h1></body>"`;
133139
134140
exports[`addStyle should work with updates #12 3`] = `"<head><title>Title</title><style>.foo { color: red }</style><style>.bar { color: blue }</style></head><body><h1>Hello world</h1></body>"`;
135141

test/runtime/injectStylesIntoStyleTag.test.js

+38
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,44 @@ describe("addStyle", () => {
143143
expect(document.documentElement.innerHTML).toMatchSnapshot();
144144
});
145145

146+
it("should work with supports", () => {
147+
injectStylesIntoStyleTag(
148+
[
149+
[
150+
"./style-4.css",
151+
".foo { color: red }",
152+
"",
153+
// eslint-disable-next-line no-undefined
154+
undefined,
155+
"display: flex",
156+
],
157+
],
158+
defaultOptions
159+
);
160+
161+
expect(document.documentElement.innerHTML).toMatchSnapshot();
162+
});
163+
164+
it("should work with layer", () => {
165+
injectStylesIntoStyleTag(
166+
// eslint-disable-next-line no-undefined
167+
[["./style-4.css", ".foo { color: red }", "", undefined, "", "default"]],
168+
defaultOptions
169+
);
170+
171+
expect(document.documentElement.innerHTML).toMatchSnapshot();
172+
});
173+
174+
it("should work with empty layer", () => {
175+
injectStylesIntoStyleTag(
176+
// eslint-disable-next-line no-undefined
177+
[["./style-4.css", ".foo { color: red }", "", undefined, "", ""]],
178+
defaultOptions
179+
);
180+
181+
expect(document.documentElement.innerHTML).toMatchSnapshot();
182+
});
183+
146184
it("should work with source maps", () => {
147185
injectStylesIntoStyleTag(
148186
[

0 commit comments

Comments
 (0)