Skip to content

wrap calls to selector parser in try/catch #698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions plugins/css-blank-pseudo/.tape.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ postcssTape(plugin)({
disablePolyfillReadyClass: true
}
},
'invalid-selector': {
message: 'warns on invalid selectors',
warnings: 1
},
'examples/example': {
message: 'minimal example',
},
Expand Down
1 change: 1 addition & 0 deletions plugins/css-blank-pseudo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
6 changes: 3 additions & 3 deletions plugins/css-blank-pseudo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ const creator: PluginCreator<pluginOptions> = (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') {
Expand Down
1 change: 1 addition & 0 deletions plugins/css-blank-pseudo/test/invalid-selector.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo : bar:blank {}
1 change: 1 addition & 0 deletions plugins/css-blank-pseudo/test/invalid-selector.expect.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo : bar:blank {}
1 change: 1 addition & 0 deletions plugins/css-has-pseudo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions plugins/css-has-pseudo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions plugins/postcss-attribute-case-insensitive/.tape.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
1 change: 1 addition & 0 deletions plugins/postcss-attribute-case-insensitive/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
16 changes: 13 additions & 3 deletions plugins/postcss-attribute-case-insensitive/src/index.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -88,9 +89,18 @@ function transform(selectors) {
const creator: PluginCreator<never> = () => {
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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[foo="a b" i] : bar {
order: 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[foo="a b" i] : bar {
order: 1;
}
1 change: 1 addition & 0 deletions plugins/postcss-cascade-layers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
16 changes: 13 additions & 3 deletions plugins/postcss-cascade-layers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ const creator: PluginCreator<pluginOptions> = (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}"`);
}
});
});

Expand All @@ -93,7 +97,13 @@ const creator: PluginCreator<pluginOptions> = (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;
});
});

Expand Down
4 changes: 4 additions & 0 deletions plugins/postcss-custom-selectors/.tape.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
1 change: 1 addition & 0 deletions plugins/postcss-custom-selectors/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
34 changes: 19 additions & 15 deletions plugins/postcss-custom-selectors/src/custom-selectors-from-root.ts
Original file line number Diff line number Diff line change
@@ -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<string, SelectorRoot> {
export default function getCustomSelectors(root: PostCSSRoot, result: Result, opts: { preserve?: boolean }): Map<string, SelectorRoot> {
// initialize custom selectors
const customSelectors = new Map<string, SelectorRoot>();

Expand All @@ -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}"`);
}
});

Expand Down
8 changes: 4 additions & 4 deletions plugins/postcss-custom-selectors/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ const creator: PluginCreator<PluginOptions> = (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 });
},
};
},
Expand Down
44 changes: 26 additions & 18 deletions plugins/postcss-custom-selectors/src/transform-rule.ts
Original file line number Diff line number Diff line change
@@ -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<string, Root>, 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<string, Root>, 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;
Expand Down
11 changes: 11 additions & 0 deletions plugins/postcss-custom-selectors/test/invalid-selector.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@custom-selector :--foo foo : bar;

:--foo {
order: 1;
}

@custom-selector :--bar bar;

:--bar : foo {
order: 2;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@custom-selector :--foo foo : bar;

:--foo {
order: 1;
}

:--bar : foo {
order: 2;
}
5 changes: 5 additions & 0 deletions plugins/postcss-extract/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion plugins/postcss-extract/src/select-nodes.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
1 change: 1 addition & 0 deletions plugins/postcss-focus-visible/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions plugins/postcss-focus-visible/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ const creator: PluginCreator<pluginOptions> = (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;
}

Expand Down
1 change: 1 addition & 0 deletions plugins/postcss-focus-within/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions plugins/postcss-focus-within/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ const creator: PluginCreator<pluginOptions> = (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;
}

Expand Down
4 changes: 4 additions & 0 deletions plugins/postcss-nesting/.tape.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ postcssTape(plugin)({
noIsPseudoSelector: true,
},
},
'invalid-selector': {
message: 'warns on invalid selectors',
warnings: 4
},
'media': {
message: 'supports nested @media',
},
Expand Down
3 changes: 3 additions & 0 deletions plugins/postcss-nesting/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
Loading