Skip to content

Instantly share code, notes, and snippets.

@MattStypa
Last active January 14, 2019 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MattStypa/6913f6613de4a8372999cdc5c5918038 to your computer and use it in GitHub Desktop.
Save MattStypa/6913f6613de4a8372999cdc5c5918038 to your computer and use it in GitHub Desktop.
Modules as plugins speed fix.

The fix

I think I know what is causing the issue. I am not sure why though. I can only speculate.

Before I get into the nitty-gritty, here is the dirty fix.

# parseObjectStyles.js

import _ from 'lodash'
import postcss from 'postcss'
import postcssNested from 'postcss-nested'
import postcssJs from 'postcss-js'

export default function parseObjectStyles(styles) {
  if (!Array.isArray(styles)) {
    return parseObjectStyles([styles])
  }

  const nodes = _.flatMap(
    styles,
    style => postcss([postcssNested]).process(style, { parser: postcssJs }).root.nodes
  )

  nodes.forEach(node => node.raws.semicolon = true)

  return nodes;
}

The reason

It appears that when postCSS converts the styles to a string, it tries to maintain the code-style of the source css file. One of these code-styles is the samicolon following the last rule.

When source CSS is parsed, these opinionated code-styles are saved in the raws property of the parsed node.

postcss-js does not generate any of these raws, which makes sense. The problem is that the default stringifier tries to determine if the trailing semicolon should be used by walking over all the nodes and checking if it is used anywhere.

By forcing the stringifier to use the semicolon we save the traversal time.

I did not research why the generator approach did not cause these issues.

The dirty

There are three ways to fix it.

  • Write custom stringifier for plugins that is more opinionated and faster.
  • Write custom parser that conveys the opinion about stringification. (Hacky)
  • Apply the opinion to the nodes after parsing. (Very hacky) [This is what I did]

How I got there

First i ran the process with the profiler

babel-node --prof src/build.js

Then I processed the profile

node --prof-process isolate-0xnnnnnnnnnnnn-v8.log > processed.txt

After checking the log I noticed that a lot of time is spent in the stringifier

   ticks parent  name
   7759   19.4%  Builtin: LoadIC
   5046   65.0%    LazyCompile: *css.walkRules.rule /Users/mattstypa/Code/tailwindcss/lib/lib/substituteClassApplyAtRules.js:12:19
   5020   99.5%      LazyCompile: ~rawSemicolon /Users/mattstypa/Code/tailwindcss/node_modules/postcss/lib/stringifier.js:165:63
   3262   65.0%        LazyCompile: *raw /Users/mattstypa/Code/tailwindcss/node_modules/postcss/lib/stringifier.js:116:45
   2004   61.4%          LazyCompile: *rule /Users/mattstypa/Code/tailwindcss/node_modules/postcss/lib/stringifier.js:59:47
   2004  100.0%            LazyCompile: *block /Users/mattstypa/Code/tailwindcss/node_modules/postcss/lib/stringifier.js:100:49

Then it started the process with the --inspect-brk flag

babel-node --inspect-brk src/build.js

That allowed me to step through the code using Chrome dev tools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment