The interaction between synchronous and asynchronous plugins leads to unexpected and incorrect CSS transformations. It seems like PostCSS may not be fully awaiting the completion of synchronous operations before passing the AST to the next plugin if that subsequent plugin is asynchronous.
In my specific case, I'm using autoprefixer (synchronous) followed by postcss-rename (which becomes asynchronous due to its use of postcss-selector-parser). However, it seems like postcss-rename doesn't affect nodes created by autoprefixer.
This can be reproduced with the following example:
const postcss = require("postcss"); // 8.5.3
const autoprefixer = require("autoprefixer"); // 10.4.21
const rename = require("postcss-rename"); // 0.6.0
const input = `
.foo-bar-baz::placeholder{
color:red;
}
`;
(async () => {
const result = await postcss([
autoprefixer({ overrideBrowserslist: "Chrome >= 56" }),
rename({ strategy: "minimal" }),
]).process(input, { from: undefined });
console.log(result.css);
})();
Want:
.a::-webkit-input-placeholder{
color:red;
}
.a::placeholder{
color:red;
}
Got:
.foo-bar-baz::-webkit-input-placeholder{
color:red;
}
.a::placeholder{
color:red;
}
I acknowledge the immediate issue is the specific interaction between autoprefixer creating nodes and postcss-rename (in its asynchronous form) not subsequently processing these new nodes. However, main point I want to highlight is that, from a plugin user's or even a plugin author's perspective, the internal synchronous or asynchronous nature plugins in the chain shouldn't determine the correctness of the final output.
Ideally PostCSS should guarantee that each plugin, whether synchronous or asynchronous, operates on the fully transformed output of its predecessors regardless of their internal implementation details.
EDIT: Just to be clear, inverting the order of the plugins works, because then renaming happens before Autoprefixer takes place. I also tested a synchronous variant of postcss-rename, which I still need to create a PR for, and it worked as expected. This makes it clearer to me that the issue is with asynchronous plugins per se.
The interaction between synchronous and asynchronous plugins leads to unexpected and incorrect CSS transformations. It seems like PostCSS may not be fully awaiting the completion of synchronous operations before passing the AST to the next plugin if that subsequent plugin is asynchronous.
In my specific case, I'm using
autoprefixer(synchronous) followed bypostcss-rename(which becomes asynchronous due to its use ofpostcss-selector-parser). However, it seems likepostcss-renamedoesn't affect nodes created byautoprefixer.This can be reproduced with the following example:
Want:
Got:
I acknowledge the immediate issue is the specific interaction between
autoprefixercreating nodes andpostcss-rename(in its asynchronous form) not subsequently processing these new nodes. However, main point I want to highlight is that, from a plugin user's or even a plugin author's perspective, the internal synchronous or asynchronous nature plugins in the chain shouldn't determine the correctness of the final output.Ideally PostCSS should guarantee that each plugin, whether synchronous or asynchronous, operates on the fully transformed output of its predecessors regardless of their internal implementation details.
EDIT: Just to be clear, inverting the order of the plugins works, because then renaming happens before Autoprefixer takes place. I also tested a synchronous variant of
postcss-rename, which I still need to create a PR for, and it worked as expected. This makes it clearer to me that the issue is with asynchronous plugins per se.