Skip to content

Commit

Permalink
feat: added support for @supports, @layer and @media from `@imp…
Browse files Browse the repository at this point in the history
…ort` at-rules
  • Loading branch information
alexander-akait authored Sep 20, 2021
1 parent 74fa1cf commit b9a600c
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 27 deletions.
6 changes: 5 additions & 1 deletion src/runtime/injectStylesIntoStyleTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ function modulesToDom(list, options) {
css: item[1],
media: item[2],
sourceMap: item[3],
supports: item[4],
layer: item[5],
};

if (index !== -1) {
Expand Down Expand Up @@ -59,7 +61,9 @@ function addStyle(obj, options) {
if (
newObj.css === obj.css &&
newObj.media === obj.media &&
newObj.sourceMap === obj.sourceMap
newObj.sourceMap === obj.sourceMap &&
newObj.supports === obj.supports &&
newObj.layer === obj.layer
) {
return;
}
Expand Down
40 changes: 35 additions & 5 deletions src/runtime/singletonStyleDomAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,41 @@ const replaceText = (function replaceText() {

/* istanbul ignore next */
function apply(style, index, remove, obj) {
const css = remove
? ""
: obj.media
? `@media ${obj.media} {${obj.css}}`
: obj.css;
let css;

if (remove) {
css = "";
} else {
css = "";

if (obj.supports) {
css += `@supports (${obj.supports}) {`;
}

if (obj.media) {
css += `@media ${obj.media} {`;
}

const needLayer = typeof obj.layer !== "undefined";

if (needLayer) {
css += `@layer${obj.layer.length > 0 ? ` ${obj.layer}` : ""} {`;
}

css += obj.css;

if (needLayer) {
css += "}";
}

if (obj.media) {
css += "}";
}

if (obj.supports) {
css += "}";
}
}

// For old IE
/* istanbul ignore if */
Expand Down
36 changes: 29 additions & 7 deletions src/runtime/styleDomAPI.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
/* istanbul ignore next */
function apply(style, options, obj) {
let css = obj.css;
const media = obj.media;
const sourceMap = obj.sourceMap;
let css = "";

if (obj.supports) {
css += `@supports (${obj.supports}) {`;
}

if (obj.media) {
css += `@media ${obj.media} {`;
}

const needLayer = typeof obj.layer !== "undefined";

if (media) {
style.setAttribute("media", media);
} else {
style.removeAttribute("media");
if (needLayer) {
css += `@layer${obj.layer.length > 0 ? ` ${obj.layer}` : ""} {`;
}

css += obj.css;

if (needLayer) {
css += "}";
}

if (obj.media) {
css += "}";
}

if (obj.supports) {
css += "}";
}

const sourceMap = obj.sourceMap;

if (sourceMap && typeof btoa !== "undefined") {
css += `\n/*# sourceMappingURL=data:application/json;base64,${btoa(
unescape(encodeURIComponent(JSON.stringify(sourceMap)))
Expand Down
2 changes: 2 additions & 0 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
runInJsDom,
} from "./helpers/index";

jest.setTimeout(10000);

describe("loader", () => {
const injectTypes = [
"styleTag",
Expand Down
8 changes: 8 additions & 0 deletions test/manual/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ <h2>Modules</h2>
<h2>Toggle</h2>
</section>

<section>
<h2>Media and Supports</h2>
<div class="bottom">BLUE</div>
<div class="middle">GREEN</div>
<div class="top">RED</div>
<div class="media-and-supports">RED</div>
</section>

<section>
<h2>Custom element</h2>
<custom-square w="100" h="100"></custom-square>
Expand Down
7 changes: 7 additions & 0 deletions test/manual/src/bottom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.bottom {
color: blue;
}

.media-and-supports {
color: blue;
}
1 change: 1 addition & 0 deletions test/manual/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import api2, {
namedExportLazyBlue,
namedExportLazyBackground,
} from "./style.named-export.lazy.module.css";
import "./top.css";

console.log("___LOCALS___");
console.log(component);
Expand Down
10 changes: 10 additions & 0 deletions test/manual/src/middle.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import url("./bottom.css") supports(display: grid) screen and
(max-width: 2400px);

.middle {
color: green;
}

.media-and-supports {
color: green;
}
5 changes: 0 additions & 5 deletions test/manual/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,3 @@
.blue {
color: blue;
}

.background {
height: 1200px;
background: url("./logo.png") center no-repeat;
}
10 changes: 10 additions & 0 deletions test/manual/src/top.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import url("./middle.css") supports(display: flex) screen and
(min-width: 400px);

.top {
color: red;
}

.media-and-supports {
color: red;
}
1 change: 1 addition & 0 deletions test/manual/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ module.exports = {
},
devServer: {
hot: true,
liveReload: false,
static: __dirname,
},
};
24 changes: 15 additions & 9 deletions test/runtime/__snapshots__/injectStylesIntoStyleTag.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ exports[`addStyle should work with "insert" option 1`] = `"<head><title>Title</t
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>"`;
exports[`addStyle should work with empty layer 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #2 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #2 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
Expand All @@ -56,7 +58,9 @@ exports[`addStyle should work with empty modules list 1`] = `"<head><title>Title
exports[`addStyle should work with empty modules list 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
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>"`;
exports[`addStyle should work with layer 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
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>"`;
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>"`;
Expand All @@ -67,6 +71,8 @@ exports[`addStyle should work with source maps 1`] = `
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTUuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS01LmNzcyIsInNvdXJjZXNDb250ZW50IjpbIi5mb28geyBjb2xvcjogcmVkIH0iXX0= */</style></head><body><h1>Hello world</h1></body>"
`;
exports[`addStyle should work with supports 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with updates #2 1`] = `"<head><title>Title</title><style>.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with updates #2 2`] = `"<head><title>Title</title><style>.foo { color: blue }</style></head><body><h1>Hello world</h1></body>"`;
Expand All @@ -79,9 +85,9 @@ exports[`addStyle should work with updates #4 1`] = `"<head><title>Title</title>
exports[`addStyle should work with updates #4 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
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>"`;
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>"`;
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>"`;
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>"`;
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>"`;
Expand All @@ -102,14 +108,14 @@ exports[`addStyle should work with updates #7 1`] = `"<head><title>Title</title>
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>"`;
exports[`addStyle should work with updates #8 1`] = `
"<head><title>Title</title><style media=\\"screen and (min-width: 320px)\\">.foo { color: red }
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDMyMHB4KSB7IC5mb28geyBjb2xvcjogcmVkIH0gfSJdfQ== */</style><style media=\\"screen and (max-width: 240px)\\">.bar { color: yellow }
"<head><title>Title</title><style>@media screen and (min-width: 320px) {.foo { color: red }}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDMyMHB4KSB7IC5mb28geyBjb2xvcjogcmVkIH0gfSJdfQ== */</style><style>@media screen and (max-width: 240px) {.bar { color: yellow }}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTIuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0yLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDI0MHB4KSB7IC5iYXIgeyBjb2xvcjogeWVsbG93IH0gfSJdfQ== */</style></head><body><h1>Hello world</h1></body>"
`;
exports[`addStyle should work with updates #8 2`] = `
"<head><title>Title</title><style media=\\"screen and (min-width: 640px)\\">.foo { color: black }
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJCQkJCLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtaW4td2lkdGg6IDY0MHB4KSB7IC5mb28geyBjb2xvcjogYmxhY2sgfSB9Il19 */</style><style media=\\"screen and (max-width: 1240px)\\">.bar { color: black }
"<head><title>Title</title><style>@media screen and (min-width: 640px) {.foo { color: black }}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTEuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJCQkJCLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0xLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtaW4td2lkdGg6IDY0MHB4KSB7IC5mb28geyBjb2xvcjogYmxhY2sgfSB9Il19 */</style><style>@media screen and (max-width: 1240px) {.bar { color: black }}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLTI2LTIuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJCQkJCLGNBQXFCLGVBQWUsRUFBRSIsImZpbGUiOiJzdHlsZS0yNi0yLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDEyNDBweCkgeyAuYmFyIHsgY29sb3I6IGJsYWNrIH0gfSJdfQ== */</style></head><body><h1>Hello world</h1></body>"
`;
Expand All @@ -127,9 +133,9 @@ exports[`addStyle should work with updates #11 1`] = `"<head><title>Title</title
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>"`;
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>"`;
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>"`;
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>"`;
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>"`;
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>"`;
Expand Down
38 changes: 38 additions & 0 deletions test/runtime/injectStylesIntoStyleTag.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,44 @@ describe("addStyle", () => {
expect(document.documentElement.innerHTML).toMatchSnapshot();
});

it("should work with supports", () => {
injectStylesIntoStyleTag(
[
[
"./style-4.css",
".foo { color: red }",
"",
// eslint-disable-next-line no-undefined
undefined,
"display: flex",
],
],
defaultOptions
);

expect(document.documentElement.innerHTML).toMatchSnapshot();
});

it("should work with layer", () => {
injectStylesIntoStyleTag(
// eslint-disable-next-line no-undefined
[["./style-4.css", ".foo { color: red }", "", undefined, "", "default"]],
defaultOptions
);

expect(document.documentElement.innerHTML).toMatchSnapshot();
});

it("should work with empty layer", () => {
injectStylesIntoStyleTag(
// eslint-disable-next-line no-undefined
[["./style-4.css", ".foo { color: red }", "", undefined, "", ""]],
defaultOptions
);

expect(document.documentElement.innerHTML).toMatchSnapshot();
});

it("should work with source maps", () => {
injectStylesIntoStyleTag(
[
Expand Down

0 comments on commit b9a600c

Please sign in to comment.