bazelbuild / rules_postcss

PostCSS rules for Bazel
Apache License 2.0
10 stars 13 forks source link

Investigate workers #45

Open rzhw opened 4 years ago

rzhw commented 4 years ago

In larger projects, postcss_binary and/or postcss_multi_binary may see repeated numbers of calls in a single build invocation.

Currently, the PostCSS build rules generate new source files and new corresponding nodejs_binary targets in order to run PostCSS with a configuration.

We should investigate whether using workers can reduce the impact of this.

nex3 commented 3 years ago

The difficulty with using postcss_binary with workers is that they only run in the invocation phase, so they don't have any say in which targets are declared in the loading phase or which actions in the analysis phase. And since postcss_binary declares a separate runner for each target, workerizing that runner has limited utility (although not zero—I believe a workerized runner can still stay alive in memory across multiple invocations when using iBazel).

For postcss_multi_binary it's more useful, because the same worker can be used to process all actions in a given target since they all share a single nodejs_binary. But even then, nothing can be shared across multiple postcss_multi_binary invocations.

Ideally, we would be able to share a single worker across many different PostCSS invocations, possibly with different configurations. The reason we have to generate a custom runner today is that different postprocessor stacks have different depedencies, but a single stack is likely to be used across multiple targets. We could support this by adding "advanced-level" rules that support declaring a stack of post-processors separately from invoking those post-processors:

postcss_stack(
  name = "my_cool_stack",
  # A map from short names for plugins to `postcss_plugin` targets. The short names
  # allow them to be configured elegantly at the call site, rather than baking the
  # configuration into the runner binary.
  plugins = {
    "autoprefixer": "//path/to/autoprefixer:plugin",
    "rtlcss": "//path/to/rtlcss:plugin",
  ],
)

postcss_stack_binary(
  name = "css",
  stack = ":my_cool_stack",
  # Configuration is passed as CLI arguments to a runner binary that's shared across
  # all uses of the same stack.
  configuration = {
    "autoprefixer": "[{browsers: '> 5%'}]",
  },
)

Each stack would have a single nodejs_binary, which could then be workerized to handle compilation of all associated postcss_stack_binary rules.

rzhw commented 3 years ago

Would making this an "advanced-level" concept via a separate set of rules be worth it, compared to making a stack optional attribute(s)?

I could imagine postcss_binary keeping the same API, but instead of the current "runner" concept creates a once-off postcss_stack in the background. A macro or leaf user of postcss_binary could optionally add stack = ":my_cool_stack" in order to reuse a certain stack and take advantage of a persistent worker for that stack.

nex3 commented 3 years ago

I could imagine postcss_binary keeping the same API, but instead of the current "runner" concept creates a once-off postcss_stack in the background. A macro or leaf user of postcss_binary could optionally add stack = ":my_cool_stack" in order to reuse a certain stack and take advantage of a persistent worker for that stack.

I like this idea a lot.