Hello postcss experts!
I’m updating an old plugin to postCSS 8 API but I meet some issues.
This simple postCSS plugin fall into an infinite loop:
module.exports = (options = {}) => {
return {
postcssPlugin: 'postcss-failing-plugin',
Declaration(decl) {
if (decl.prop.startsWith('--')) {
decl.prop = decl.prop.replace(/^--/, `--prefix-`);
}
},
};
};
module.exports.postcss = true;
The documentation mention this behaviour:
Plugins will re-visit all nodes, which you changed or added. If you will change any children, plugin will re-visit parent as well. Only
Once
andOnceExit
will not be called again. writing a plugin
But nothing to avoid it.
How to edit a value in Declaration
without making an infinite loop?
You may be repeatedly adding a prefix to custom property declarations that are already prefixed, causing the declaration visitor to run infinitely.
You can use a negative lookahead assertion (?!)
to match custom properties that do not begin with a specific custom property prefix, i.e. ^--(?!prefix-)
.
const matcher = /^--(?!prefix-)/
const replacement = '--prefix-'
const ensure = value => value.replace(matcher, replacement)
// these _should not_ receive a new prefix
ensure('foo') // "foo"
ensure('prefix-foo') // "prefix-foo"
ensure('--prefix-foo') // "--prefix-foo"
// these _should_ receive a new prefixed
ensure('--foo') // "--prefix-foo"
ensure('--prefixable-foo') // "--prefix-prefixable-foo"
As applied to your example
module.exports = (options = {}) => {
return {
postcssPlugin: 'postcss-failing-plugin',
Declaration(decl) {
/** Matches a `--` property not beginning with `--prefix-`. */
const match = /^--(?!prefix-)/
if (match.test(decl.prop)) {
decl.prop = decl.prop.replace(match, `--prefix-`);
}
},
};
};
module.exports.postcss = true;