Skip to content

Commit

Permalink
feat: supported supports() and layer() and fix multiple @media
Browse files Browse the repository at this point in the history
…merging in `@import` at-rule (#1377)
  • Loading branch information
alexander-akait committed Sep 17, 2021
1 parent cf3a3a7 commit bce2c17
Show file tree
Hide file tree
Showing 27 changed files with 1,251 additions and 1,874 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Expand Up @@ -12,6 +12,7 @@ module.exports = {
"no-param-reassign": "off",
"no-continue": "off",
"no-underscore-dangle": "off",
"no-undefined": "off",
},
},
],
Expand Down
6 changes: 2 additions & 4 deletions src/index.js
Expand Up @@ -71,7 +71,7 @@ export default async function loader(content, map, meta) {
context: this.context,
rootContext: this.rootContext,
resourcePath: this.resourcePath,
filter: getFilter(options.import.filter, this.resourcePath),
filter: options.import.filter,
resolver,
urlHandler: (url) =>
stringifyRequest(
Expand Down Expand Up @@ -205,13 +205,11 @@ export default async function loader(content, map, meta) {

if (options.sourceMap) {
imports.unshift({
type: "api_sourcemap_import",
importName: "___CSS_LOADER_API_SOURCEMAP_IMPORT___",
url: stringifyRequest(this, require.resolve("./runtime/noSourceMaps")),
url: stringifyRequest(this, require.resolve("./runtime/sourceMaps")),
});
} else {
imports.unshift({
type: "api_sourcemap_import",
importName: "___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___",
url: stringifyRequest(this, require.resolve("./runtime/noSourceMaps")),
});
Expand Down
84 changes: 72 additions & 12 deletions src/plugins/postcss-import-parser.js
Expand Up @@ -118,15 +118,54 @@ function parseNode(atRule, key) {
throw error;
}

const mediaNodes = paramsNodes.slice(1);
const additionalNodes = paramsNodes.slice(1);

let supports;
let layer;
let media;

if (mediaNodes.length > 0) {
media = valueParser.stringify(mediaNodes).trim().toLowerCase();
if (additionalNodes.length > 0) {
let nodes = [];

for (const node of additionalNodes) {
nodes.push(node);

const isLayerFunction =
node.type === "function" && node.value.toLowerCase() === "layer";
const isLayerWord =
node.type === "word" && node.value.toLowerCase() === "layer";

if (isLayerFunction || isLayerWord) {
if (isLayerFunction) {
nodes.splice(nodes.length - 1, 1, ...node.nodes);
} else {
nodes.splice(nodes.length - 1, 1, {
type: "string",
value: "",
unclosed: false,
});
}

layer = valueParser.stringify(nodes).trim().toLowerCase();
nodes = [];
} else if (
node.type === "function" &&
node.value.toLowerCase() === "supports"
) {
nodes.splice(nodes.length - 1, 1, ...node.nodes);

supports = valueParser.stringify(nodes).trim().toLowerCase();
nodes = [];
}
}

if (nodes.length > 0) {
media = valueParser.stringify(nodes).trim().toLowerCase();
}
}

// eslint-disable-next-line consistent-return
return { atRule, prefix, url, media, isRequestable };
return { atRule, prefix, url, layer, supports, media, isRequestable };
}

const plugin = (options = {}) => {
Expand Down Expand Up @@ -160,11 +199,24 @@ const plugin = (options = {}) => {

const resolvedAtRules = await Promise.all(
parsedAtRules.map(async (parsedAtRule) => {
const { atRule, isRequestable, prefix, url, media } =
parsedAtRule;
const {
atRule,
isRequestable,
prefix,
url,
layer,
supports,
media,
} = parsedAtRule;

if (options.filter) {
const needKeep = await options.filter(url, media);
const needKeep = await options.filter(
url,
media,
options.resourcePath,
supports,
layer
);

if (!needKeep) {
return;
Expand Down Expand Up @@ -192,13 +244,20 @@ const plugin = (options = {}) => {
atRule.remove();

// eslint-disable-next-line consistent-return
return { url: resolvedUrl, media, prefix, isRequestable };
return {
url: resolvedUrl,
layer,
supports,
media,
prefix,
isRequestable,
};
}

atRule.remove();

// eslint-disable-next-line consistent-return
return { url, media, prefix, isRequestable };
return { url, layer, supports, media, prefix, isRequestable };
})
);

Expand All @@ -212,10 +271,11 @@ const plugin = (options = {}) => {
continue;
}

const { url, isRequestable, media } = resolvedAtRule;
const { url, isRequestable, layer, supports, media } =
resolvedAtRule;

if (!isRequestable) {
options.api.push({ url, media, index });
options.api.push({ url, layer, supports, media, index });

// eslint-disable-next-line no-continue
continue;
Expand All @@ -237,7 +297,7 @@ const plugin = (options = {}) => {
});
}

options.api.push({ importName, media, index });
options.api.push({ importName, layer, supports, media, index });
}
},
};
Expand Down
59 changes: 52 additions & 7 deletions src/runtime/api.js
Expand Up @@ -8,20 +8,44 @@ module.exports = (cssWithMappingToString) => {
// return the list of modules as css string
list.toString = function toString() {
return this.map((item) => {
const content = cssWithMappingToString(item);
let content = "";

const needLayer = typeof item[5] !== "undefined";

if (item[4]) {
content += `@supports (${item[4]}) {`;
}

if (item[2]) {
content += `@media ${item[2]} {`;
}

if (needLayer) {
content += `@layer${item[5].length > 0 ? ` ${item[5]}` : ""} {`;
}

content += cssWithMappingToString(item);

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

if (item[2]) {
return `@media ${item[2]} {${content}}`;
content += "}";
}

if (item[4]) {
content += "}";
}

return content;
}).join("");
};

// import a list of modules into the list
list.i = function i(modules, mediaQuery, dedupe) {
list.i = function i(modules, media, dedupe, supports, layer) {
if (typeof modules === "string") {
modules = [[null, modules, ""]];
modules = [[null, modules, undefined]];
}

const alreadyImportedModules = {};
Expand All @@ -43,11 +67,32 @@ module.exports = (cssWithMappingToString) => {
continue;
}

if (mediaQuery) {
if (typeof layer !== "undefined") {
if (typeof item[5] === "undefined") {
item[5] = layer;
} else {
item[1] = `@layer${item[5].length > 0 ? ` ${item[5]}` : ""} {${
item[1]
}}`;
item[5] = layer;
}
}

if (media) {
if (!item[2]) {
item[2] = mediaQuery;
item[2] = media;
} else {
item[1] = `@media ${item[2]} {${item[1]}}`;
item[2] = media;
}
}

if (supports) {
if (!item[4]) {
item[4] = `${supports}`;
} else {
item[2] = `${mediaQuery} and ${item[2]}`;
item[1] = `@supports (${item[4]}) {${item[1]}}`;
item[4] = supports;
}
}

Expand Down
60 changes: 51 additions & 9 deletions src/utils.js
Expand Up @@ -921,6 +921,34 @@ function normalizeSourceMapForRuntime(map, loaderContext) {
return JSON.stringify(resultMap);
}

function printParams(media, dedupe, supports, layer) {
let result = "";

if (typeof layer !== "undefined") {
result = `, ${JSON.stringify(layer)}`;
}

if (typeof supports !== "undefined") {
result = `, ${JSON.stringify(supports)}${result}`;
} else if (result.length > 0) {
result = `, undefined${result}`;
}

if (dedupe) {
result = `, true${result}`;
} else if (result.length > 0) {
result = `, false${result}`;
}

if (media) {
result = `${JSON.stringify(media)}${result}`;
} else if (result.length > 0) {
result = `""${result}`;
}

return result;
}

function getModuleCode(result, api, replacements, options, loaderContext) {
if (options.modules.exportOnlyLocals === true) {
return "";
Expand All @@ -939,15 +967,22 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
});\n`;

for (const item of api) {
const { url, media, dedupe } = item;

beforeCode += url
? `___CSS_LOADER_EXPORT___.push([module.id, ${JSON.stringify(
`@import url(${url});`
)}${media ? `, ${JSON.stringify(media)}` : ""}]);\n`
: `___CSS_LOADER_EXPORT___.i(${item.importName}${
media ? `, ${JSON.stringify(media)}` : dedupe ? ', ""' : ""
}${dedupe ? ", true" : ""});\n`;
const { url, layer, supports, media, dedupe } = item;

if (url) {
// eslint-disable-next-line no-undefined
const printedParam = printParams(media, undefined, supports, layer);

beforeCode += `___CSS_LOADER_EXPORT___.push([module.id, ${JSON.stringify(
`@import url(${url});`
)}${printedParam.length > 0 ? `, ${printedParam}` : ""}]);\n`;
} else {
const printedParam = printParams(media, dedupe, supports, layer);

beforeCode += `___CSS_LOADER_EXPORT___.i(${item.importName}${
printedParam.length > 0 ? `, ${printedParam}` : ""
});\n`;
}
}

for (const item of replacements) {
Expand Down Expand Up @@ -980,6 +1015,13 @@ function getModuleCode(result, api, replacements, options, loaderContext) {
}
}

// Indexes description:
// 0 - module id
// 1 - CSS code
// 2 - media
// 3 - source map
// 4 - supports
// 5 - layer
return `${beforeCode}// Module\n___CSS_LOADER_EXPORT___.push([module.id, ${code}, ""${sourceMapValue}]);\n`;
}

Expand Down

0 comments on commit bce2c17

Please sign in to comment.