css parsing: performance tips & tricks

136
CSS Parsing performance tips & tricks Roman Dvornov Avito Moscow, September 2016

Upload: roman-dvornov

Post on 07-Jan-2017

220 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: CSS parsing: performance tips & tricks

CSS Parsing performance tips & tricks

Roman Dvornov Avito

Moscow, September 2016

Page 2: CSS parsing: performance tips & tricks

Frontend lead in Avito

Specializes in SPA

Maintainer of:basis.js, CSSO, component-inspector, csstree and others

Page 3: CSS parsing: performance tips & tricks

CSS parsing (russian)

3

tinyurl.com/csstree-intro

This talk is the continuation of

Page 4: CSS parsing: performance tips & tricks

CSSTree

Page 5: CSS parsing: performance tips & tricks

CSSTree – fastest detailed CSS parser

5

Page 6: CSS parsing: performance tips & tricks

How this project was born

Page 7: CSS parsing: performance tips & tricks

About a year ago I started to maintain CSSO

(a CSS minifier)

7

github.com/css/csso

Page 8: CSS parsing: performance tips & tricks

CSSO was based on Gonzales (a CSS parser)

8

github.com/css/gonzales

Page 9: CSS parsing: performance tips & tricks

What's wrong with Gonzales• Development stopped in 2013

• Unhandy and buggy AST format

• Parsing mistakes

• Excessively complex code base

• Slow, high memory consumption, pressure for GC

9

Page 10: CSS parsing: performance tips & tricks

But I didn’t want to spend my time developing the

parser…

10

Page 11: CSS parsing: performance tips & tricks

Alternatives?

Page 12: CSS parsing: performance tips & tricks

You can find a lot of CSS parsers

12

Page 13: CSS parsing: performance tips & tricks

Common problems• Not developing currently

• Outdated (don't support latest CSS features)

• Buggy

• Unhandy AST

• Slow13

Page 14: CSS parsing: performance tips & tricks

PostCSS parser is a good choice if you need one now

14

postcss.org

Page 15: CSS parsing: performance tips & tricks

PostCSS pros• Сonstantly developing

• Parses CSS well, even non-standard syntax + tolerant mode

• Saves formatting info

• Handy API to work with AST

• Fast15

Page 16: CSS parsing: performance tips & tricks

General con: selectors and values are not parsed

(are represented as strings)

16

Page 17: CSS parsing: performance tips & tricks

That forces developers to• Use non-robust or non-effective approaches

• Invent their own parsers

• Use additional parsers: postcss-selector-parser postcss-value-parser

17

Page 18: CSS parsing: performance tips & tricks

Switching to PostCSS meant writing our own selector and value parsers,

what is pretty much the same as writing an entirely new parser

18

Page 19: CSS parsing: performance tips & tricks

However, as a result of a continuous refactoring within a few months

the CSSO parser was completely rewrote (which was not planned)

19

Page 20: CSS parsing: performance tips & tricks

And was extracted to a separate project

github.com/csstree/csstree

20

Page 21: CSS parsing: performance tips & tricks

Performance

Page 22: CSS parsing: performance tips & tricks

CSSO – performance boost story (russian)

22

tinyurl.com/csso-speedup

My previous talk about parser performance

Page 23: CSS parsing: performance tips & tricks

After my talk on HolyJS conference the parser's

performance was improved one more time :)

23

* Thanks Vyacheslav @mraleph Egorov for inspiration

Page 24: CSS parsing: performance tips & tricks

24

CSSTree: 24 msMensch: 31 msCSSOM: 36 msPostCSS: 38 msRework: 81 msPostCSS Full: 100 msGonzales: 175 msStylecow: 176 msGonzales PE: 214 msParserLib: 414 ms

bootstrap.css v3.3.7 (146Kb)

github.com/postcss/benchmark

Non-detailed AST

Detailed AST

PostCSS Full = + postcss-selector-parser

+ postcss-value-parser

Page 25: CSS parsing: performance tips & tricks

Epic fail as I realised later I extracted

the wrong version of the parser

25

😱github.com/csstree/csstree/commit/57568c758195153e337f6154874c3bc42dd04450

Page 26: CSS parsing: performance tips & tricks

26

CSSTree: 24 msMensch: 31 msCSSOM: 36 msPostCSS: 38 msRework: 81 msPostCSS Full: 100 msGonzales: 175 msStylecow: 176 msGonzales PE: 214 msParserLib: 414 ms

bootstrap.css v3.3.7 (146Kb)

github.com/postcss/benchmark

Time after parser update

13 ms

Page 27: CSS parsing: performance tips & tricks

Parsers: basic training

Page 28: CSS parsing: performance tips & tricks

Main steps

• Tokenization

• Tree assembling

28

Page 29: CSS parsing: performance tips & tricks

Tokenization

Page 30: CSS parsing: performance tips & tricks

30

• whitespaces – [ \n\r\t\f]+ • keyword – [a-zA-aZ…]+ • number – [0-9]+ • string – "string" or 'string' • comment – /* comment */ • punctuation – [;,.#\{\}\[\]\(\)…]

Split text into tokens

Page 31: CSS parsing: performance tips & tricks

31

.foo { width: 10px;}

[ '.', 'foo', ' ', '{', '\n ', 'width', ':', ' ', '10', 'px', ';', '\n', '}']

Page 32: CSS parsing: performance tips & tricks

We need more info about every token: type and location

32

It is more efficient to compute type and location

on tokenization step

Page 33: CSS parsing: performance tips & tricks

33

.foo { width: 10px;}

[ { type: 'FullStop', value: '.', offset: 0, line: 1, column: 1 }, …]

Page 34: CSS parsing: performance tips & tricks

Tree assembling

Page 35: CSS parsing: performance tips & tricks

35

function getSelector() { var selector = { type: 'Selector', sequence: [] };

// main loop

return selector;}

Creating a node

Page 36: CSS parsing: performance tips & tricks

36

for (;currentToken < tokenCount; currentToken++) { switch (tokens[currentToken]) { case TokenType.Hash: // # selector.sequence.push(getId()); break; case TokenType.FullStop: // . selector.sequence.push(getClass()); break; … }

Main loop

Page 37: CSS parsing: performance tips & tricks

37

{ "type": "StyleSheet", "rules": [{ "type": "Atrule", "name": "import", "expression": { "type": "AtruleExpression", "sequence": [ ... ] }, "block": null }]}

Result

Page 38: CSS parsing: performance tips & tricks

Parser performance boost Part 2: new horizons

Page 39: CSS parsing: performance tips & tricks

39

[ { type: 'FullStop', value: '.', offset: 0, line: 1, column: 1 }, …]

Token's cost: 24 + 5 * 4 + array =

min 50 bytes per token

Our project ~1Mb CSS 254 062 tokens

= min 12.7 Mb

Page 40: CSS parsing: performance tips & tricks

Out of the box: changing approach

Page 41: CSS parsing: performance tips & tricks

Compute all tokens at once and then assembly a tree is much more easy, but needs more memory, therefore is

slower

41

Page 42: CSS parsing: performance tips & tricks

Scanner (lazy tokenizer)

42

Page 43: CSS parsing: performance tips & tricks

43

scanner.token // current token or nullscanner.next() // going to next tokenscanner.lookup(N) // look ahead, returns // Nth token from current token

Key API

Page 44: CSS parsing: performance tips & tricks

44

• lookup(N)fills tokens buffer up to N tokens (if they are not computed yet), returns N-1 token from buffer

• next()shift token from buffer, if any, or compute next token

Page 45: CSS parsing: performance tips & tricks

Computing the same number of tokens, but not simultaneously

and requires less memory

45

Page 46: CSS parsing: performance tips & tricks

Problem: the approach puts pressure on GC

46

Page 47: CSS parsing: performance tips & tricks

Reducing token's cost step by step

Page 48: CSS parsing: performance tips & tricks

48

[ { type: 'FullStop', value: '.', offset: 0, line: 1, column: 1 }, …]

Type as string is easy to understand, but it's for

internal use only and we can replace it by numbers

Page 49: CSS parsing: performance tips & tricks

49

[ { type: FULLSTOP, value: '.', offset: 0, line: 1, column: 1 }, …]

…// '.'.charCodeAt(0)var FULLSTOP = 46;…

Page 50: CSS parsing: performance tips & tricks

50

[ { type: 46, value: '.', offset: 0, line: 1, column: 1 }, …]

Page 51: CSS parsing: performance tips & tricks

51

[ { type: 46, value: '.', offset: 0, line: 1, column: 1 }, …]

We can avoid substring storage in the token – it's very

expensive for punctuation (moreover those substrings

are never used); Many constructions are assembled by several

substrings. One long substring is better than

a concat of several small ones

Page 52: CSS parsing: performance tips & tricks

52

[ { type: 46, value: '.', offset: 0, line: 1, column: 1 }, …]

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Page 53: CSS parsing: performance tips & tricks

53

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Look, Ma! No strings just numbers!

Page 54: CSS parsing: performance tips & tricks

54

Moreover not an Array, but TypedArray

Array of objects

Arraysof numbers

Page 55: CSS parsing: performance tips & tricks

Array vs. TypedArray• Can't have holes

• Faster in theory (less checking)

• Can be stored outside the heap (when big enough)

• Prefilled with zeros

55

Page 56: CSS parsing: performance tips & tricks

56

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array

1 4 4 4 4

17 per token(tokens count) 254 062 x 17 = 4.3Mb

Page 57: CSS parsing: performance tips & tricks

4.3Mb vs. 12.7Mb (min)

57

Page 58: CSS parsing: performance tips & tricks

Houston we have a problem: TypedArray has a fixed length,

but we don't know how many tokens will be found

58

Page 59: CSS parsing: performance tips & tricks

59

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array

1 4 4 4 4

17 per token(symbols count) 983 085 x 17 = 16.7Mb

Page 60: CSS parsing: performance tips & tricks

16.7Mb vs. 12.7Mb (min)

60

Page 61: CSS parsing: performance tips & tricks

16.7Mb vs. 12.7Mb (min)

60

Don't give up, let's look on arrays

more attentively

Page 62: CSS parsing: performance tips & tricks

61

start = [ 0, 5, 6, 7, 9, 11, …, 35 ]

end = [ 5, 6, 7, 9, 11, 12, …, 36 ]

Page 63: CSS parsing: performance tips & tricks

61

start = [ 0, 5, 6, 7, 9, 11, …, 35 ]

end = [ 5, 6, 7, 9, 11, 12, …, 36 ]

Page 64: CSS parsing: performance tips & tricks

62

start = [ 0, 5, 6, 7, 9, 11, …, 35 ]

end = [ 5, 6, 7, 9, 11, 12, …, 36 ]

offset = [ 0, 5, 6, 7, 9, 11, …, 35, 36 ] start = offset[i] end = offset[i + 1]

+

=

Page 65: CSS parsing: performance tips & tricks

63

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array

1 4 4 4 4

13 per token983 085 x 13 = 12.7Mb

Page 66: CSS parsing: performance tips & tricks

64

a { top: 0;}

lines = [ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3]

columns = [ 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1]

lines & columns

Page 67: CSS parsing: performance tips & tricks

64

a { top: 0;}

lines = [ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3]

columns = [ 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1]

lines & columns

Page 68: CSS parsing: performance tips & tricks

65

line = lines[offset];

column = offset - lines.lastIndexOf(line - 1, offset);

lines & columns

Page 69: CSS parsing: performance tips & tricks

65

line = lines[offset];

column = offset - lines.lastIndexOf(line - 1, offset);

lines & columns

It's acceptable only for short lines, that's why we cache the last line

start offset

Page 70: CSS parsing: performance tips & tricks

66

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array

1 4 4 4 4

9 per token983 085 x 9 = 8.8Mb

Page 71: CSS parsing: performance tips & tricks

67

8.8Mb vs. 12.7Mb (min)

Page 72: CSS parsing: performance tips & tricks

Reduce operations with strings

Page 73: CSS parsing: performance tips & tricks

Performance «killers»*• RegExp • String concatenation • toLowerCase/toUpperCase • substr/substring • …

69

* Polluted GC pulls performance down

Page 74: CSS parsing: performance tips & tricks

Performance «killers»*• RegExp • String concatenation • toLowerCase/toUpperCase • substr/substring • …

70

* Polluted GC pulls performance down

We can’t avoid using these things, but we

can get rid of the rest

Page 75: CSS parsing: performance tips & tricks

71

var start = scanner.tokenStart;

scanner.next();

scanner.next();

return source.substr(start, scanner.tokenEnd);

Avoid string concatenations

Page 76: CSS parsing: performance tips & tricks

72

function cmpStr(source, start, end, str) { if (end - start !== str.length) { return false; }

for (var i = start; i < end; i++) { var sourceCode = source.charCodeAt(i); var strCode = str.charCodeAt(i - start);

if (sourceCode !== strCode) { return false; } }

return true;}

String comparison

No substring!

Page 77: CSS parsing: performance tips & tricks

73

function cmpStr(source, start, end, str) { if (end - start !== str.length) { return false; }

for (var i = start; i < end; i++) { var sourceCode = source.charCodeAt(i); var strCode = str.charCodeAt(i - start);

if (sourceCode !== strCode) { return false; } }

return true;}

String comparison

Length fast-check

Page 78: CSS parsing: performance tips & tricks

74

function cmpStr(source, start, end, str) { if (end - start !== str.length) { return false; }

for (var i = start; i < end; i++) { var sourceCode = source.charCodeAt(i); var strCode = str.charCodeAt(i - start);

if (sourceCode !== strCode) { return false; } }

return true;}

String comparison

Compare strings by char codes

Page 79: CSS parsing: performance tips & tricks

Case insensitive comparison of strings*?

75

* Means avoid toLowerCase/toUpperCase

Page 80: CSS parsing: performance tips & tricks

Heuristics• Comparison with the reference strings only (str)

• Reference strings may be in lower case and contain latin letters only (no unicode)

• I read once on Twitter…

76

Page 81: CSS parsing: performance tips & tricks

Setting of the 6th bit to 1 changes upper case latin letter to lower case

(works for latin ASCII letters only)

'A' = 01000001'a' = 01100001

'A'.charCodeAt(0) | 32 === 'a'.charCodeAt(0)

77

Page 82: CSS parsing: performance tips & tricks

78

function cmpStr(source, start, end, str) { … for (var i = start; i < end; i++) { … // source[i].toLowerCase() if (sourceCode >= 65 && sourceCode <= 90) { // 'A' .. 'Z' sourceCode = sourceCode | 32; }

if (sourceCode !== strCode) { return false; } } …}

Case insensitive string comparison

Page 83: CSS parsing: performance tips & tricks

Benefits• Frequent comparison stops on length check

• No substring (no pressure on GC)

• No temporary strings (e.g. result of toLowerCase/toUpperCase)

• String comparison don't pollute CG

79

Page 84: CSS parsing: performance tips & tricks

Results• RegExp • string concatenation • toLowerCase/toUpperCase • substr/substring

80

Page 85: CSS parsing: performance tips & tricks

No arrays in AST

Page 86: CSS parsing: performance tips & tricks

What's wrong with arrays?• As we are growing arrays their memory

fragments are to be relocated frequently (unnecessary memory moving)

• Pressure on GC

• We don't know the size of resulting arrays

82

Page 87: CSS parsing: performance tips & tricks

Solution?

83

Page 88: CSS parsing: performance tips & tricks

Bi-directional list

84

Page 89: CSS parsing: performance tips & tricks

85

Page 90: CSS parsing: performance tips & tricks

85

AST node AST node AST node AST node

Page 91: CSS parsing: performance tips & tricks

Needs a little bit more memory than arrays, but…

86

Page 92: CSS parsing: performance tips & tricks

Pros• No memory relocation

• No GC pollution during AST assembly

• next/prev references for free

• Cheap insertion and deletion

• Better for monomorphic walkers87

Page 93: CSS parsing: performance tips & tricks

Those approaches and others allowed to reduce memory consumption,

pressure on GC and made the parser twice faster than before

88

Page 94: CSS parsing: performance tips & tricks

89

CSSTree: 24 msMensch: 31 msCSSOM: 36 msPostCSS: 38 msRework: 81 msPostCSS Full: 100 msGonzales: 175 msStylecow: 176 msGonzales PE: 214 msParserLib: 414 ms

bootstrap.css v3.3.7 (146Kb)

github.com/postcss/benchmark

It's about this changes

13 ms

Page 95: CSS parsing: performance tips & tricks

But the story goes on 😋

90

Page 96: CSS parsing: performance tips & tricks

Parser performance boost story Part 3: а week after FrontTalks

Page 97: CSS parsing: performance tips & tricks

In general

• Simplify AST structure

• Less memory consumption

• Arrays reusing

• list.map().join() -> loop + string concatenation

• and others…92

Page 98: CSS parsing: performance tips & tricks

Once more time about token costs

Page 99: CSS parsing: performance tips & tricks

94

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array

1 types 4 offsets 4 4 lines 4

9 per token983 085 x 9 = 8.8Mb

Page 100: CSS parsing: performance tips & tricks

lines can be computed on demand

95

Page 101: CSS parsing: performance tips & tricks

96

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array

1 types 4 offsets 4 4 lines 4

5 per token983 085 x 5 = 4.9Mb

Page 102: CSS parsing: performance tips & tricks

Do we really needs all 32 bits for the offset?

Heuristics: no one parses more than 16Mb of CSS

97

Page 103: CSS parsing: performance tips & tricks

98

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

Page 104: CSS parsing: performance tips & tricks

99

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

offsetAndType[i] = type[i] << 24 | offset[i]

+

=

Page 105: CSS parsing: performance tips & tricks

100

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

offsetAndType[i] = type[i] << 24 | offset[i]offsetAndType = [ 16777216, 788529157, … ]

+

=

Page 106: CSS parsing: performance tips & tricks

101

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

offsetAndType[i] = type[i] << 24 | offset[i]offsetAndType = [ 16777216, 788529157, … ]start = offsetAndType[i] & 0xFFFFFF;type = offsetAndType[i] >> 24;

+

=

Page 107: CSS parsing: performance tips & tricks

102

[ { type: 46, start: 0, end: 1, line: 1, column: 1 }, …]

Uint8Array Uint32Array Uint32Array Uint32Array Uint32Array

1 types 4 offsets 4 4 lines 4

4 per token983 085 x 4 = 3.9Mb

Page 108: CSS parsing: performance tips & tricks

3.9-7.8 Mb vs. 12.7 Mb (min)

103

Page 109: CSS parsing: performance tips & tricks

104

class Scanner { ... next() { var next = this.currentToken + 1;

this.currentToken = next; this.tokenStart = this.tokenEnd; this.tokenEnd = this.offsetAndType[next + 1] & 0xFFFFFF; this.tokenType = this.offsetAndType[next] >> 24; }}

Needs 2 reads for 3 values (tokenEnd becomes tokenStart)

Page 110: CSS parsing: performance tips & tricks

105

class Scanner { ... next() { var next = this.currentToken + 1;

this.currentToken = next; this.tokenStart = this.tokenEnd; this.tokenEnd = this.offsetAndType[next + 1] & 0xFFFFFF; this.tokenType = this.offsetAndType[next] >> 24; }}

But 2 reads look redundant, let's fix it…

Page 111: CSS parsing: performance tips & tricks

106

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

offsetAndType[i] = type[i] << 24 | offset[i]start = endend = offsetAndType[i + 1] & 0xFFFFFF;type = offsetAndType[i] >> 24;

Page 112: CSS parsing: performance tips & tricks

106

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

offsetAndType[i] = type[i] << 24 | offset[i]start = endend = offsetAndType[i + 1] & 0xFFFFFF;type = offsetAndType[i] >> 24;

Page 113: CSS parsing: performance tips & tricks

107

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

The first offset is always zero

Page 114: CSS parsing: performance tips & tricks

108

offset = [ 0, 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

Shift offsets to the left

Page 115: CSS parsing: performance tips & tricks

109

offset = [ 5, 6, 7, 9, 11, 11, …, 1234 ]

type = [ 1, 47, 47, 4, 4, 47, 5, …, 3 ]

offsetAndType[i] = type[i] << 24 | offset[i + 1]offsetAndType[i] = type[i] << 24 | offset[i]start = endend = offsetAndType[i] & 0xFFFFFF;type = offsetAndType[i] >> 24;

Page 116: CSS parsing: performance tips & tricks

110

class Scanner { ... next() { var next = this.currentToken + 1;

this.currentToken = next; this.tokenStart = this.tokenEnd; this.tokenEnd = this.offsetAndType[next] & 0xFFFFFF; this.tokenType = this.offsetAndType[next] >> 24; }}

Now we need just one read

Page 117: CSS parsing: performance tips & tricks

111

class Scanner { ... next() { var next = this.currentToken + 1;

this.currentToken = next; this.tokenStart = this.tokenEnd; next = this.offsetAndType[next]; this.tokenEnd = next & 0xFFFFFF; this.tokenType = next >> 24; }}

-50% reads (~250k)

👌

Page 118: CSS parsing: performance tips & tricks

Re-use

Page 119: CSS parsing: performance tips & tricks

The scanner creates arrays every time when it parses

a new string

113

Page 120: CSS parsing: performance tips & tricks

The scanner creates arrays every time when it parses

a new string

113

Page 121: CSS parsing: performance tips & tricks

New strategy• Preallocate 16Kb buffer by default

• Create new buffer only if current is smaller than needed for parsing

• Significantly improves performance especially in cases when parsing a number of small CSS fragments

114

Page 122: CSS parsing: performance tips & tricks

115

CSSTree: 24 msMensch: 31 msCSSOM: 36 msPostCSS: 38 msRework: 81 msPostCSS Full: 100 msGonzales: 175 msStylecow: 176 msGonzales PE: 214 msParserLib: 414 ms

bootstrap.css v3.3.7 (146Kb)

github.com/postcss/benchmark

13 ms 7 ms

Current results

Page 123: CSS parsing: performance tips & tricks

And still not the end… 😋

116

Page 124: CSS parsing: performance tips & tricks

One more thing

Page 125: CSS parsing: performance tips & tricks

CSSTree – is not just about performance

118

Page 126: CSS parsing: performance tips & tricks

New feature*: Parsing and matching of

CSS values syntax

119

* Currently unique across CSS parsers

Page 127: CSS parsing: performance tips & tricks

Example

120

Page 128: CSS parsing: performance tips & tricks

121

csstree.github.io/docs/syntax.html

CSS syntax reference

Page 129: CSS parsing: performance tips & tricks

122

csstree.github.io/docs/validator.html

CSS values validator

Page 130: CSS parsing: performance tips & tricks

123

var csstree = require('css-tree');var syntax = csstree.syntax.defaultSyntax;var ast = csstree.parse('… your css …');

csstree.walkDeclarations(ast, function(node) { if (!syntax.match(node.property.name, node.value)) { console.log(syntax.lastMatchError); }});

Your own validator in 8 lines of code

Page 131: CSS parsing: performance tips & tricks

Some tools and plugins• csstree-validator – npm package + cli command

• stylelint-csstree-validator – plugin for stylelint

• gulp-csstree – plugin for gulp

• SublimeLinter-contrib-csstree – plugin for Sublime Text

• vscode-csstree – plugin for VS Code

• csstree-validator – plugin for Atom

More is coming…124

Page 132: CSS parsing: performance tips & tricks

Conclusion

Page 133: CSS parsing: performance tips & tricks

If you want your JavaScript works as fast as C, make it look like C

126

Page 134: CSS parsing: performance tips & tricks

Previous talks• CSSO – performance boost story (russian)

tinyurl.com/csso-speedup

• CSS parsing (russian)tinyurl.com/csstree-intro

127

Page 135: CSS parsing: performance tips & tricks

github.com/csstree/csstree

128

Your feedback is welcome

Page 136: CSS parsing: performance tips & tricks

Roman Dvornov @rdvornov

github.com/lahmatiy [email protected]

Questions?

github.com/csstree/csstree