Skip to content

Plugins should be run sequentially regardless of asynchrony #2047

Description

@Goodwine

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions