Skip to content

Plugin best practices

Romain Menke edited this page Jul 9, 2022 · 5 revisions

A set of guidelines and tips to create plugins that work well for as much users as possible.

These are not in any particular order.

Selector and Value parsers

It is tempting to use string find/replace or regular expression operations to modify CSS in a plugin. These are easy ways that work well for simple cases.

However they fail when bits have escape sequences (\) or maybe they are part of a string.

/* looking for selector `:fancy-selector` */

:fancy-selector {}

\:fancy-selector {} /* escaping changes ":" */

[data-example=":fancy-selector"] {} /* strings in attribute selectors */
/* looking for value `fancy-value` */

.example {
  order: fancy-value; /* keyword value */
}

.example {
  content: "fancy-value"; /* string value */
}

Avoid regular expressions

A lot of people can do amazing things with regular expressions. We have however found that these create barriers and form maintenance issue.

If the regular expression is simple it will most likely also have a simple string operation that is more expressive.

// you need to know about the `i` flag
/foo/i.test("fOO")

vs.

// you can read the code and understand everything without prior knowledge
"fOO".toLowerCase() === "foo"

If the regular expression is complex it will not be maintainable. The chances that someone in the future needs to make a change and does not fully understand your regular expression is non-zero. We prefer to use more expressive API when these are available.

Fast aborts before further parsing

Often it is possible to have a simple and fast check to verify that a PostCSS definitely does not need to be processed. If you only want to act on :has(...) selectors you can look for :has(.

If this is not found you do not need to pass the selector to postcss-selector-parser. The early return pattern also helps to keep your code clean and readable.

var lowerCaseSelector = rule.selector.toLowerCase();
if ( ! lowerCaseSelector.includes(':has(') ) {
	return;
}
Clone this wiki locally