diff --git a/plugins/css-blank-pseudo/.tape.mjs b/plugins/css-blank-pseudo/.tape.mjs index 71d0cc9d2..9fc5a4127 100644 --- a/plugins/css-blank-pseudo/.tape.mjs +++ b/plugins/css-blank-pseudo/.tape.mjs @@ -30,6 +30,10 @@ postcssTape(plugin)({ disablePolyfillReadyClass: true } }, + 'invalid-selector': { + message: 'warns on invalid selectors', + warnings: 1 + }, 'examples/example': { message: 'minimal example', }, diff --git a/plugins/css-blank-pseudo/CHANGELOG.md b/plugins/css-blank-pseudo/CHANGELOG.md index 13911e3a3..da7b9c551 100644 --- a/plugins/css-blank-pseudo/CHANGELOG.md +++ b/plugins/css-blank-pseudo/CHANGELOG.md @@ -3,6 +3,7 @@ ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 4.1.1 (August, 23, 2022) diff --git a/plugins/css-blank-pseudo/src/index.ts b/plugins/css-blank-pseudo/src/index.ts index 7b371b5fe..53f4ab8bc 100644 --- a/plugins/css-blank-pseudo/src/index.ts +++ b/plugins/css-blank-pseudo/src/index.ts @@ -48,9 +48,9 @@ const creator: PluginCreator = (opts?: pluginOptions) => { try { selectorAST = parser().astSync(selector); - } catch (_) { - rule.warn(result, `Failed to parse selector : ${selector}`); - return selector; + } catch (err) { + rule.warn(result, `Failed to parse selector : "${selector}" with message: "${err.message}"`); + return [selector]; } if (typeof selectorAST === 'undefined') { diff --git a/plugins/css-blank-pseudo/test/invalid-selector.css b/plugins/css-blank-pseudo/test/invalid-selector.css new file mode 100644 index 000000000..52deff7dd --- /dev/null +++ b/plugins/css-blank-pseudo/test/invalid-selector.css @@ -0,0 +1 @@ +foo : bar:blank {} diff --git a/plugins/css-blank-pseudo/test/invalid-selector.expect.css b/plugins/css-blank-pseudo/test/invalid-selector.expect.css new file mode 100644 index 000000000..52deff7dd --- /dev/null +++ b/plugins/css-blank-pseudo/test/invalid-selector.expect.css @@ -0,0 +1 @@ +foo : bar:blank {} diff --git a/plugins/css-has-pseudo/CHANGELOG.md b/plugins/css-has-pseudo/CHANGELOG.md index c48e14f75..bf180faf8 100644 --- a/plugins/css-has-pseudo/CHANGELOG.md +++ b/plugins/css-has-pseudo/CHANGELOG.md @@ -3,6 +3,7 @@ ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 4.0.1 (August 23, 2022) diff --git a/plugins/css-has-pseudo/src/index.ts b/plugins/css-has-pseudo/src/index.ts index 63b0a2a2e..40d784765 100644 --- a/plugins/css-has-pseudo/src/index.ts +++ b/plugins/css-has-pseudo/src/index.ts @@ -30,8 +30,8 @@ const creator: PluginCreator<{ preserve?: boolean, specificityMatchingName?: str let selectorAST; try { selectorAST = parser().astSync(selector); - } catch (_) { - rule.warn(result, `Failed to parse selector : ${selector}`); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${selector}" with message: "${err.message}"`); return selector; } diff --git a/plugins/postcss-attribute-case-insensitive/.tape.mjs b/plugins/postcss-attribute-case-insensitive/.tape.mjs index fb65246f5..a81bc8968 100644 --- a/plugins/postcss-attribute-case-insensitive/.tape.mjs +++ b/plugins/postcss-attribute-case-insensitive/.tape.mjs @@ -5,6 +5,10 @@ postcssTape(plugin)({ basic: { message: "supports basic usage", }, + 'invalid-selector': { + message: 'warns on invalid selectors', + warnings: 1 + }, 'examples/example': { message: 'minimal example', }, diff --git a/plugins/postcss-attribute-case-insensitive/CHANGELOG.md b/plugins/postcss-attribute-case-insensitive/CHANGELOG.md index 1b00cc631..d05779630 100644 --- a/plugins/postcss-attribute-case-insensitive/CHANGELOG.md +++ b/plugins/postcss-attribute-case-insensitive/CHANGELOG.md @@ -3,6 +3,7 @@ ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 5.0.2 (July 8, 2022) diff --git a/plugins/postcss-attribute-case-insensitive/src/index.ts b/plugins/postcss-attribute-case-insensitive/src/index.ts index eaad1913d..710fec155 100644 --- a/plugins/postcss-attribute-case-insensitive/src/index.ts +++ b/plugins/postcss-attribute-case-insensitive/src/index.ts @@ -1,5 +1,6 @@ import type { PluginCreator } from 'postcss'; -import selectorParser, { Container } from 'postcss-selector-parser'; +import selectorParser from 'postcss-selector-parser'; +import type { Container } from 'postcss-selector-parser'; function nodeIsInsensitiveAttribute(node) { return node.type === 'attribute' && node.insensitive; @@ -88,9 +89,18 @@ function transform(selectors) { const creator: PluginCreator = () => { return { postcssPlugin: 'postcss-attribute-case-insensitive', - Rule(rule) { + Rule(rule, { result }) { if (rule.selector.includes('i]')) { - const modifiedSelector = selectorParser(transform).processSync(rule.selector); + + let modifiedSelector = rule.selector; + + try { + modifiedSelector = selectorParser(transform).processSync(rule.selector); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${rule.selector}" with message: "${err.message}"`); + return; + } + if (modifiedSelector === rule.selector) { return; } diff --git a/plugins/postcss-attribute-case-insensitive/test/invalid-selector.css b/plugins/postcss-attribute-case-insensitive/test/invalid-selector.css new file mode 100644 index 000000000..2a6484498 --- /dev/null +++ b/plugins/postcss-attribute-case-insensitive/test/invalid-selector.css @@ -0,0 +1,3 @@ +[foo="a b" i] : bar { + order: 1; +} diff --git a/plugins/postcss-attribute-case-insensitive/test/invalid-selector.expect.css b/plugins/postcss-attribute-case-insensitive/test/invalid-selector.expect.css new file mode 100644 index 000000000..2a6484498 --- /dev/null +++ b/plugins/postcss-attribute-case-insensitive/test/invalid-selector.expect.css @@ -0,0 +1,3 @@ +[foo="a b" i] : bar { + order: 1; +} diff --git a/plugins/postcss-cascade-layers/CHANGELOG.md b/plugins/postcss-cascade-layers/CHANGELOG.md index 22fca2de0..0aac8ee92 100644 --- a/plugins/postcss-cascade-layers/CHANGELOG.md +++ b/plugins/postcss-cascade-layers/CHANGELOG.md @@ -4,6 +4,7 @@ - Run `postcss-cascade-layers` early compared to other PostCSS plugins (breaking) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 1.1.1 (September 17, 2022) diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts index 46112b217..4e0afd369 100644 --- a/plugins/postcss-cascade-layers/src/index.ts +++ b/plugins/postcss-cascade-layers/src/index.ts @@ -69,8 +69,12 @@ const creator: PluginCreator = (opts?: pluginOptions) => { let highestASpecificity = 0; root.walkRules((rule) => { rule.selectors.forEach((selector) => { - const specificity = selectorSpecificity(selectorParser().astSync(selector)); - highestASpecificity = Math.max(highestASpecificity, specificity.a + 1); + try { + const specificity = selectorSpecificity(selectorParser().astSync(selector)); + highestASpecificity = Math.max(highestASpecificity, specificity.a + 1); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${selector}" with message: "${err.message}"`); + } }); }); @@ -93,7 +97,13 @@ const creator: PluginCreator = (opts?: pluginOptions) => { } rule.selectors = rule.selectors.map((selector) => { - return adjustSelectorSpecificity(selector, model.layerCount * highestASpecificity); + try { + return adjustSelectorSpecificity(selector, model.layerCount * highestASpecificity); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${selector}" with message: "${err.message}"`); + } + + return selector; }); }); diff --git a/plugins/postcss-custom-selectors/.tape.mjs b/plugins/postcss-custom-selectors/.tape.mjs index 2c5c971f5..8637673a2 100644 --- a/plugins/postcss-custom-selectors/.tape.mjs +++ b/plugins/postcss-custom-selectors/.tape.mjs @@ -14,6 +14,10 @@ postcssTape(plugin)({ 'conditionals': { message: 'handles conditional rules' }, + 'invalid-selector': { + message: 'warns on invalid selectors', + warnings: 2 + }, 'examples/example': { message: 'minimal example', }, diff --git a/plugins/postcss-custom-selectors/CHANGELOG.md b/plugins/postcss-custom-selectors/CHANGELOG.md index 8f0e94010..15f1b83ca 100644 --- a/plugins/postcss-custom-selectors/CHANGELOG.md +++ b/plugins/postcss-custom-selectors/CHANGELOG.md @@ -7,6 +7,7 @@ - Removed: `exportTo` feature (breaking). - Fixed: follow the specification and use `:is()` in transformed selectors (breaking). - Added: Support for `@scope` and `@container` as parent rules of `@custom-selector`. +- Fixed: Do not throw when a selector is invalid, show a warning instead. ```diff @custom-selector :--heading h1, h2, h3; diff --git a/plugins/postcss-custom-selectors/src/custom-selectors-from-root.ts b/plugins/postcss-custom-selectors/src/custom-selectors-from-root.ts index 1e0161f05..8d1445cb9 100644 --- a/plugins/postcss-custom-selectors/src/custom-selectors-from-root.ts +++ b/plugins/postcss-custom-selectors/src/custom-selectors-from-root.ts @@ -1,10 +1,10 @@ -import type { ChildNode, Container, Document, Root as PostCSSRoot } from 'postcss'; +import type { ChildNode, Container, Document, Result, Root as PostCSSRoot } from 'postcss'; import type { Root as SelectorRoot } from 'postcss-selector-parser'; import parser from 'postcss-selector-parser'; import { isProcessableCustomSelectorRule } from './is-processable-custom-selector-rule'; // return custom selectors from the css root, conditionally removing them -export default function getCustomSelectors(root: PostCSSRoot, opts: { preserve?: boolean }): Map { +export default function getCustomSelectors(root: PostCSSRoot, result: Result, opts: { preserve?: boolean }): Map { // initialize custom selectors const customSelectors = new Map(); @@ -13,24 +13,28 @@ export default function getCustomSelectors(root: PostCSSRoot, opts: { preserve?: return; } - const source = atRule.params.trim(); + try { + const source = atRule.params.trim(); - const selectorAST = parser().astSync(source); - const nameNode = selectorAST?.nodes?.[0]?.nodes?.[0]; - if (!nameNode || nameNode.type !== 'pseudo' || !nameNode.value.startsWith(':--')) { - return; - } + const selectorAST = parser().astSync(source); + const nameNode = selectorAST?.nodes?.[0]?.nodes?.[0]; + if (!nameNode || nameNode.type !== 'pseudo' || !nameNode.value.startsWith(':--')) { + return; + } - const name = nameNode.toString(); + const name = nameNode.toString(); - // re-parsing is important to obtain the correct AST shape - customSelectors.set(name, parser().astSync(source.slice(name.length).trim())); + // re-parsing is important to obtain the correct AST shape + customSelectors.set(name, parser().astSync(source.slice(name.length).trim())); - if (!opts.preserve) { - const parent = atRule.parent; - atRule.remove(); + if (!opts.preserve) { + const parent = atRule.parent; + atRule.remove(); - removeEmptyAncestorBlocks(parent); + removeEmptyAncestorBlocks(parent); + } + } catch (err) { + atRule.warn(result, `Failed to parse custom selector : "${atRule.params}" with message: "${err.message}"`); } }); diff --git a/plugins/postcss-custom-selectors/src/index.ts b/plugins/postcss-custom-selectors/src/index.ts index f450f7c1f..4fa31774f 100644 --- a/plugins/postcss-custom-selectors/src/index.ts +++ b/plugins/postcss-custom-selectors/src/index.ts @@ -25,15 +25,15 @@ const creator: PluginCreator = (opts?: PluginOptions) => { let customSelectors = new Map(); return { - Once: (root) => { - customSelectors = getCustomSelectors(root, { preserve: preserve }); + Once: (root, { result }) => { + customSelectors = getCustomSelectors(root, result, { preserve: preserve }); }, - Rule: (rule) => { + Rule: (rule, { result }) => { if (!rule.selector.includes(':--')) { return; } - transformRule(rule, customSelectors, { preserve: preserve }); + transformRule(rule, result, customSelectors, { preserve: preserve }); }, }; }, diff --git a/plugins/postcss-custom-selectors/src/transform-rule.ts b/plugins/postcss-custom-selectors/src/transform-rule.ts index d3b37f4d7..5c4d3860e 100644 --- a/plugins/postcss-custom-selectors/src/transform-rule.ts +++ b/plugins/postcss-custom-selectors/src/transform-rule.ts @@ -1,28 +1,36 @@ +import { Result, Rule } from 'postcss'; import type { Root, Selector } from 'postcss-selector-parser'; import parser from 'postcss-selector-parser'; // transform custom pseudo selectors with custom selectors -export default (rule, customSelectors: Map, opts: { preserve?: boolean }) => { - const selector = parser(selectors => { - selectors.walkPseudos((pseudo) => { - if (!customSelectors.has(pseudo.value)) { - return; - } - - const isWrapper = parser.pseudo({ - value: ':is', - nodes: [], - }); +export default (rule: Rule, result: Result, customSelectors: Map, opts: { preserve?: boolean }) => { + let selector = rule.selector; - const base = customSelectors.get(pseudo.value); - base.each((node) => { - isWrapper.append(node.clone({}) as Selector); - }); + try { + selector = parser(selectors => { + selectors.walkPseudos((pseudo) => { + if (!customSelectors.has(pseudo.value)) { + return; + } + + const isWrapper = parser.pseudo({ + value: ':is', + nodes: [], + }); + + const base = customSelectors.get(pseudo.value); + base.each((node) => { + isWrapper.append(node.clone({}) as Selector); + }); - pseudo.replaceWith(isWrapper); + pseudo.replaceWith(isWrapper); - }); - }).processSync(rule.selector); + }); + }).processSync(rule.selector); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${selector}" with message: "${err.message}"`); + return; + } if (selector === rule.selector) { return; diff --git a/plugins/postcss-custom-selectors/test/invalid-selector.css b/plugins/postcss-custom-selectors/test/invalid-selector.css new file mode 100644 index 000000000..98aa334ce --- /dev/null +++ b/plugins/postcss-custom-selectors/test/invalid-selector.css @@ -0,0 +1,11 @@ +@custom-selector :--foo foo : bar; + +:--foo { + order: 1; +} + +@custom-selector :--bar bar; + +:--bar : foo { + order: 2; +} diff --git a/plugins/postcss-custom-selectors/test/invalid-selector.expect.css b/plugins/postcss-custom-selectors/test/invalid-selector.expect.css new file mode 100644 index 000000000..4bcb7e61b --- /dev/null +++ b/plugins/postcss-custom-selectors/test/invalid-selector.expect.css @@ -0,0 +1,9 @@ +@custom-selector :--foo foo : bar; + +:--foo { + order: 1; +} + +:--bar : foo { + order: 2; +} diff --git a/plugins/postcss-extract/CHANGELOG.md b/plugins/postcss-extract/CHANGELOG.md index c1e49ae56..51fdcc944 100644 --- a/plugins/postcss-extract/CHANGELOG.md +++ b/plugins/postcss-extract/CHANGELOG.md @@ -1,5 +1,10 @@ # Changes to PostCSS Extract +### Unreleased (major) + +- Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. + ### 1.0.1 (October 4, 2022) - Fixing rule detection diff --git a/plugins/postcss-extract/src/select-nodes.ts b/plugins/postcss-extract/src/select-nodes.ts index 84cfd7999..3b6e93607 100644 --- a/plugins/postcss-extract/src/select-nodes.ts +++ b/plugins/postcss-extract/src/select-nodes.ts @@ -1,5 +1,5 @@ import type { Node, Container } from 'postcss'; -import selectorParser from 'postcss-selector-parser'; +import type selectorParser from 'postcss-selector-parser'; import type { NodeList } from './node-list'; import { notPseudo } from './pseudos/not'; import { adjacentSiblingCombinator } from './selector-engine/combinators/adjacent-sibling'; diff --git a/plugins/postcss-focus-visible/CHANGELOG.md b/plugins/postcss-focus-visible/CHANGELOG.md index 962c2389f..3b2b35a1f 100644 --- a/plugins/postcss-focus-visible/CHANGELOG.md +++ b/plugins/postcss-focus-visible/CHANGELOG.md @@ -3,6 +3,7 @@ ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 7.1.0 (July 30, 2022) diff --git a/plugins/postcss-focus-visible/src/index.ts b/plugins/postcss-focus-visible/src/index.ts index 2912ccf8c..06823a60f 100644 --- a/plugins/postcss-focus-visible/src/index.ts +++ b/plugins/postcss-focus-visible/src/index.ts @@ -36,8 +36,8 @@ const creator: PluginCreator = (opts?: pluginOptions) => { try { selectorAST = parser().astSync(selector); - } catch (_) { - rule.warn(result, `Failed to parse selector : ${selector}`); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${selector}" with message: "${err.message}"`); return selector; } diff --git a/plugins/postcss-focus-within/CHANGELOG.md b/plugins/postcss-focus-within/CHANGELOG.md index 31ad6571e..9692bcd4c 100644 --- a/plugins/postcss-focus-within/CHANGELOG.md +++ b/plugins/postcss-focus-within/CHANGELOG.md @@ -3,6 +3,7 @@ ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 6.1.1 (August 23, 2022) diff --git a/plugins/postcss-focus-within/src/index.ts b/plugins/postcss-focus-within/src/index.ts index 66285a34e..c9cc5152e 100644 --- a/plugins/postcss-focus-within/src/index.ts +++ b/plugins/postcss-focus-within/src/index.ts @@ -48,8 +48,8 @@ const creator: PluginCreator = (opts?: pluginOptions) => { try { selectorAST = parser().astSync(selector); - } catch (_) { - rule.warn(result, `Failed to parse selector : ${selector}`); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${selector}" with message: "${err.message}"`); return selector; } diff --git a/plugins/postcss-nesting/.tape.mjs b/plugins/postcss-nesting/.tape.mjs index 14438e720..2faeb7efd 100644 --- a/plugins/postcss-nesting/.tape.mjs +++ b/plugins/postcss-nesting/.tape.mjs @@ -82,6 +82,10 @@ postcssTape(plugin)({ noIsPseudoSelector: true, }, }, + 'invalid-selector': { + message: 'warns on invalid selectors', + warnings: 4 + }, 'media': { message: 'supports nested @media', }, diff --git a/plugins/postcss-nesting/CHANGELOG.md b/plugins/postcss-nesting/CHANGELOG.md index a35a4659d..f81faca2a 100644 --- a/plugins/postcss-nesting/CHANGELOG.md +++ b/plugins/postcss-nesting/CHANGELOG.md @@ -1,8 +1,11 @@ # Changes to PostCSS Nesting +⚠️ We will most likely not finish this release and switch over to the plugin being worked on in the experimental track. + ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 10.2.0 (September 14, 2022) diff --git a/plugins/postcss-nesting/src/index.ts b/plugins/postcss-nesting/src/index.ts index fdbf828cc..36316b56e 100644 --- a/plugins/postcss-nesting/src/index.ts +++ b/plugins/postcss-nesting/src/index.ts @@ -15,8 +15,8 @@ const creator: PluginCreator = (opts?: pluginOptions) => { return { postcssPlugin: 'postcss-nesting', - Rule(rule) { - walk(rule, options); + Rule(rule, { result }) { + walk(rule, result, options); }, }; }; diff --git a/plugins/postcss-nesting/src/lib/atrule-within-rule.ts b/plugins/postcss-nesting/src/lib/atrule-within-rule.ts index ec80c4bdd..cba8f9a1c 100644 --- a/plugins/postcss-nesting/src/lib/atrule-within-rule.ts +++ b/plugins/postcss-nesting/src/lib/atrule-within-rule.ts @@ -3,9 +3,9 @@ import { options } from './options.js'; import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; import validAtrules from './valid-atrules.js'; import { walkFunc } from './walk-func.js'; -import type { AtRule, Rule } from 'postcss'; +import type { AtRule, Result, Rule } from 'postcss'; -export default function atruleWithinRule(node: AtRule, parent: Rule, walk: walkFunc, opts: options) { +export default function atruleWithinRule(node: AtRule, parent: Rule, result: Result, walk: walkFunc, opts: options) { // move previous siblings and the node to before the parent shiftNodesBeforeParent(node, parent); @@ -19,7 +19,7 @@ export default function atruleWithinRule(node: AtRule, parent: Rule, walk: walkF cleanupParent(parent); // walk the children of the new rule - walk(rule, opts); + walk(rule, result, opts); } export function isAtruleWithinRule(node: AtRule) { diff --git a/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts b/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts index 6a004e2fa..20e6ec4e1 100644 --- a/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts +++ b/plugins/postcss-nesting/src/lib/nest-rule-within-rule.ts @@ -2,11 +2,11 @@ import { comma } from './list.js'; import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; import cleanupParent from './cleanup-parent.js'; import mergeSelectors from './merge-selectors/merge-selectors.js'; -import type { AtRule, Rule } from 'postcss'; +import type { AtRule, Result, Rule } from 'postcss'; import { walkFunc } from './walk-func.js'; import { options } from './options.js'; -export default function transformNestRuleWithinRule(node: AtRule, parent: Rule, walk: walkFunc, opts: options) { +export default function transformNestRuleWithinRule(node: AtRule, parent: Rule, result: Result, walk: walkFunc, opts: options) { // move previous siblings and the node to before the parent shiftNodesBeforeParent(node, parent); @@ -14,17 +14,22 @@ export default function transformNestRuleWithinRule(node: AtRule, parent: Rule, const rule = parent.clone().removeAll().append(node.nodes); rule.raws.semicolon = true; /* nested rules end with "}" and do not have this flag set */ + // update the selectors of the node to be merged with the parent + try { + rule.selectors = mergeSelectors(parent.selectors, comma(node.params), opts); + } catch (err) { + node.warn(result, `Failed to parse selectors : "${parent.selector}" / "${node.params}" with message: "${err.message}"`); + return; + } + // replace the node with the new rule node.replaceWith(rule); - // update the selectors of the node to be merged with the parent - rule.selectors = mergeSelectors(parent.selectors, comma(node.params), opts); - // conditionally cleanup an empty parent rule cleanupParent(parent); // walk the children of the new rule - walk(rule, opts); + walk(rule, result, opts); } export function isValidNestRuleWithinRule(node: AtRule) { diff --git a/plugins/postcss-nesting/src/lib/rule-within-rule.ts b/plugins/postcss-nesting/src/lib/rule-within-rule.ts index 96a97e0ef..879dd448d 100644 --- a/plugins/postcss-nesting/src/lib/rule-within-rule.ts +++ b/plugins/postcss-nesting/src/lib/rule-within-rule.ts @@ -1,15 +1,20 @@ import shiftNodesBeforeParent from './shift-nodes-before-parent.js'; import cleanupParent from './cleanup-parent.js'; import mergeSelectors from './merge-selectors/merge-selectors.js'; -import type { Rule } from 'postcss'; +import type { Result, Rule } from 'postcss'; import { options } from './options.js'; -export default function transformRuleWithinRule(node: Rule, parent: Rule, opts: options) { +export default function transformRuleWithinRule(node: Rule, parent: Rule, result: Result, opts: options) { // move previous siblings and the node to before the parent shiftNodesBeforeParent(node, parent); // update the selectors of the node to be merged with the parent - node.selectors = mergeSelectors(parent.selectors, node.selectors, opts); + try { + node.selectors = mergeSelectors(parent.selectors, node.selectors, opts); + } catch (err) { + node.warn(result, `Failed to parse selectors : "${parent.selector}" / "${node.selector}" with message: "${err.message}"`); + return; + } // merge similar rules back together const areSameRule = (node.type === 'rule' && parent.type === 'rule' && node.selector === parent.selector); diff --git a/plugins/postcss-nesting/src/lib/walk-func.ts b/plugins/postcss-nesting/src/lib/walk-func.ts index a801218d9..65214ced9 100644 --- a/plugins/postcss-nesting/src/lib/walk-func.ts +++ b/plugins/postcss-nesting/src/lib/walk-func.ts @@ -1,3 +1,3 @@ -import type { Container } from 'postcss'; +import type { Container, Result } from 'postcss'; -export type walkFunc = (node: Container, opts: { noIsPseudoSelector: boolean }) => void; +export type walkFunc = (node: Container, result: Result, opts: { noIsPseudoSelector: boolean }) => void; diff --git a/plugins/postcss-nesting/src/lib/walk.ts b/plugins/postcss-nesting/src/lib/walk.ts index 54b4c4b56..d4d3c58c1 100644 --- a/plugins/postcss-nesting/src/lib/walk.ts +++ b/plugins/postcss-nesting/src/lib/walk.ts @@ -2,11 +2,11 @@ import transformRuleWithinRule, { isValidRuleWithinRule } from './rule-within-ru import transformNestRuleWithinRule, { isValidNestRuleWithinRule } from './nest-rule-within-rule.js'; import transformAtruleWithinRule, { isAtruleWithinRule } from './atrule-within-rule.js'; import transformAtruleWithinAtrule, { isAtruleWithinAtrule } from './atrule-within-atrule.js'; -import type { Container } from 'postcss'; +import type { Container, Result } from 'postcss'; import { options } from './options.js'; import { isAtRule, isNestRule, isRule } from './is-type-of-rule.js'; -export default function walk(node: Container, opts: options) { +export default function walk(node: Container, result: Result, opts: options) { node.each((child) => { const parent = child.parent; @@ -15,19 +15,19 @@ export default function walk(node: Container, opts: options) { isRule(parent) && isValidRuleWithinRule(child) ) { - transformRuleWithinRule(child, parent, opts); + transformRuleWithinRule(child, parent, result, opts); } else if ( isNestRule(child) && isRule(parent) && isValidNestRuleWithinRule(child) ) { - transformNestRuleWithinRule(child, parent, walk, opts); + transformNestRuleWithinRule(child, parent, result, walk, opts); } else if ( isAtRule(child) && isRule(parent) && isAtruleWithinRule(child) ) { - transformAtruleWithinRule(child, parent, walk, opts); + transformAtruleWithinRule(child, parent, result, walk, opts); } else if ( isAtRule(child) && isAtRule(parent) && @@ -37,7 +37,7 @@ export default function walk(node: Container, opts: options) { } if ('nodes' in child && child.nodes.length) { - walk(child, opts); + walk(child, result, opts); } }); } diff --git a/plugins/postcss-nesting/test/invalid-selector.css b/plugins/postcss-nesting/test/invalid-selector.css new file mode 100644 index 000000000..1cac2bc97 --- /dev/null +++ b/plugins/postcss-nesting/test/invalid-selector.css @@ -0,0 +1,23 @@ +.foo : bar { + &.child { + order: 1; + } +} + +.foo : bar { + @nest &.child { + order: 2; + } +} + +.foo { + &.child : bar { + order: 3; + } +} + +.foo { + @nest &.child : bar { + order: 4; + } +} diff --git a/plugins/postcss-nesting/test/invalid-selector.expect.css b/plugins/postcss-nesting/test/invalid-selector.expect.css new file mode 100644 index 000000000..78f279b4c --- /dev/null +++ b/plugins/postcss-nesting/test/invalid-selector.expect.css @@ -0,0 +1,24 @@ + + &.child { + order: 1; + }.foo : bar { +} + +@nest &.child { + } + +.foo : bar { +} + +&.child : bar { + order: 3; + } + +.foo { +} + +@nest &.child : bar { + } + +.foo { +} diff --git a/plugins/postcss-pseudo-class-any-link/CHANGELOG.md b/plugins/postcss-pseudo-class-any-link/CHANGELOG.md index 09fa06ca5..fd4d654de 100644 --- a/plugins/postcss-pseudo-class-any-link/CHANGELOG.md +++ b/plugins/postcss-pseudo-class-any-link/CHANGELOG.md @@ -3,6 +3,7 @@ ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 7.1.6 (July 8, 2022) diff --git a/plugins/postcss-pseudo-class-any-link/src/replace-any-link.js b/plugins/postcss-pseudo-class-any-link/src/replace-any-link.js index 2471ee428..c92d76ebd 100644 --- a/plugins/postcss-pseudo-class-any-link/src/replace-any-link.js +++ b/plugins/postcss-pseudo-class-any-link/src/replace-any-link.js @@ -20,8 +20,8 @@ export function replaceAnyLink(rule, result, preserve, areaHrefNeedsFixing) { untouchedSelectors.push(selector); } } - } catch (_) { - rule.warn(result, `Failed to parse selector : ${rule.selector}`); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${rule.selector}" with message: "${err.message}"`); return; } diff --git a/plugins/postcss-scope-pseudo-class/CHANGELOG.md b/plugins/postcss-scope-pseudo-class/CHANGELOG.md index f9cd7dbae..9425b8614 100644 --- a/plugins/postcss-scope-pseudo-class/CHANGELOG.md +++ b/plugins/postcss-scope-pseudo-class/CHANGELOG.md @@ -1,5 +1,10 @@ # Changes to PostCSS Scope Pseudo Class +### Unreleased (major) + +- Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. + ### 1.0.0 (November 4, 2022) - Initial version diff --git a/plugins/postcss-scope-pseudo-class/src/index.ts b/plugins/postcss-scope-pseudo-class/src/index.ts index dde981c83..c60e271c5 100644 --- a/plugins/postcss-scope-pseudo-class/src/index.ts +++ b/plugins/postcss-scope-pseudo-class/src/index.ts @@ -51,7 +51,7 @@ const creator: PluginCreator = (opts?: pluginOptions) => { modifiedSelector = selectorAST.toString(); } catch (err) { - rule.warn(result, `Unable to parse selector: "${rule.selector}"`); + rule.warn(result, `Failed to parse selector : "${rule.selector}" with message: "${err.message}"`); } if (modifiedSelector === rule.selector) { diff --git a/plugins/postcss-selector-not/CHANGELOG.md b/plugins/postcss-selector-not/CHANGELOG.md index 4bb06310b..1732fec5c 100644 --- a/plugins/postcss-selector-not/CHANGELOG.md +++ b/plugins/postcss-selector-not/CHANGELOG.md @@ -3,6 +3,7 @@ ### Unreleased (major) - Updated: Support for Node v14+ (major). +- Fix: Do not throw when a selector is invalid, show a warning instead. ### 6.0.1 (July 8, 2022) diff --git a/plugins/postcss-selector-not/src/index.ts b/plugins/postcss-selector-not/src/index.ts index 0f050c4be..dcaab7286 100644 --- a/plugins/postcss-selector-not/src/index.ts +++ b/plugins/postcss-selector-not/src/index.ts @@ -61,11 +61,8 @@ const creator: PluginCreator = () => { if (modifiedSelector !== rule.selector) { rule.replaceWith(rule.clone({selector: modifiedSelector})); } - } catch (_) { - rule.warn( - result, - `Failed to parse selector "${rule.selector}"`, - ); + } catch (err) { + rule.warn(result, `Failed to parse selector : "${rule.selector}" with message: "${err.message}"`); } }, };