diff --git a/fixtures/compress/atrules/media-not-nested.css b/fixtures/compress/atrules/media-not-nested.css new file mode 100644 index 00000000..1483ae74 --- /dev/null +++ b/fixtures/compress/atrules/media-not-nested.css @@ -0,0 +1,27 @@ +/* issue #476: nested `not` in media conditions must not drop the @media rule */ +@media (max-width: 701px) and (not (any-pointer: fine)) { + .a { color: red; } +} + +@media (not (color)) { + .b { color: blue; } +} + +@media (max-width: 701px) and (not (any-pointer: fine)), (hover: none) { + .c { color: green; } +} + +@media not screen { + .d { color: black; } +} + +@media (max-width: 701px) and (not (any-pointer: fine)) { + .x { color: red; } +} + +@media (max-width: 701px) and (not (any-pointer: fine)) { + .y { color: blue; } +} + +@media (max-width: 701px) and (not (any-pointer: fine)) { +} diff --git a/fixtures/compress/atrules/media-not-nested.min.css b/fixtures/compress/atrules/media-not-nested.min.css new file mode 100644 index 00000000..f5a3a2a3 --- /dev/null +++ b/fixtures/compress/atrules/media-not-nested.min.css @@ -0,0 +1 @@ +@media (max-width: 701px) and (not (any-pointer: fine)){.a{color:red}}@media (not (color)){.b{color:#00f}}@media (max-width: 701px) and (not (any-pointer: fine)), (hover: none){.c{color:green}}@media not screen{.d{color:#000}}@media (max-width: 701px) and (not (any-pointer: fine)){.x{color:red}.y{color:#00f}} diff --git a/lib/clean/Atrule.js b/lib/clean/Atrule.js index 236d122d..578c6490 100644 --- a/lib/clean/Atrule.js +++ b/lib/clean/Atrule.js @@ -1,5 +1,5 @@ import { keyword as resolveKeyword } from 'css-tree'; -import { hasNoChildren } from './utils.js'; +import { hasNoChildren, isEmptyAtrulePrelude } from './utils.js'; export default function cleanAtrule(node, item, list) { if (node.block) { @@ -16,7 +16,7 @@ export default function cleanAtrule(node, item, list) { switch (node.name) { case 'charset': - if (hasNoChildren(node.prelude)) { + if (isEmptyAtrulePrelude(node.prelude)) { list.remove(item); return; } @@ -60,7 +60,7 @@ export default function cleanAtrule(node, item, list) { name === 'supports') { // drop at-rule with no prelude - if (hasNoChildren(node.prelude) || hasNoChildren(node.block)) { + if (isEmptyAtrulePrelude(node.prelude) || hasNoChildren(node.block)) { list.remove(item); } } diff --git a/lib/clean/utils.js b/lib/clean/utils.js index ca1d57c8..4e0c0976 100644 --- a/lib/clean/utils.js +++ b/lib/clean/utils.js @@ -2,6 +2,21 @@ export function hasNoChildren(node) { return !node || !node.children || node.children.isEmpty; } +// css-tree emits a Raw node (no `children` list) when a prelude fails to parse, +// e.g. Media Queries Level 4 nested `not` conditions. A non-empty Raw value is +// still a real prelude and must not be treated as missing (issue #476). +export function isEmptyAtrulePrelude(node) { + if (!node) { + return true; + } + + if (node.type === 'Raw') { + return !node.value; + } + + return hasNoChildren(node); +} + export function isNodeChildrenList(node, list) { return node !== null && node.children === list; }