Skip to content

Commit 7e36e15

Browse files
alanzabihiai
authored andcommitted
Cache node.raws locally in Stringifier hot methods
Cache node.raws references locally in hot methods (atrule, block, body, comment, decl) to avoid repeated property chain resolution. In decl(), inline the common rawValue() path when node.raws.value exists. In raw(), cache root.rawCache in a local variable. In body(), short-circuit raw(child, 'before') when child.raws.before is already defined. In block() and comment(), inline the raws check to skip raw() call overhead when the value is already present.
1 parent 8ec62b1 commit 7e36e15

1 file changed

Lines changed: 44 additions & 20 deletions

File tree

lib/stringifier.js

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,20 @@ class Stringifier {
3737
}
3838

3939
atrule(node, semicolon) {
40+
let raws = node.raws
4041
let name = '@' + node.name
4142
let params = node.params ? this.rawValue(node, 'params') : ''
4243

43-
if (typeof node.raws.afterName !== 'undefined') {
44-
name += node.raws.afterName
44+
if (typeof raws.afterName !== 'undefined') {
45+
name += raws.afterName
4546
} else if (params) {
4647
name += ' '
4748
}
4849

4950
if (node.nodes) {
5051
this.block(node, name + params)
5152
} else {
52-
let end = (node.raws.between || '') + (semicolon ? ';' : '')
53+
let end = (raws.between || '') + (semicolon ? ';' : '')
5354
this.builder(escapeHTMLInCSS(name + params + end), node)
5455
}
5556
}
@@ -84,50 +85,73 @@ class Stringifier {
8485
}
8586

8687
block(node, start) {
87-
let between = this.raw(node, 'between', 'beforeOpen')
88+
let raws = node.raws
89+
let between = typeof raws.between !== 'undefined'
90+
? raws.between
91+
: this.raw(node, 'between', 'beforeOpen')
8892
this.builder(escapeHTMLInCSS(start + between) + '{', node, 'start')
8993

9094
let after
9195
if (node.nodes && node.nodes.length) {
9296
this.body(node)
93-
after = this.raw(node, 'after')
97+
after = typeof raws.after !== 'undefined'
98+
? raws.after
99+
: this.raw(node, 'after')
94100
} else {
95-
after = this.raw(node, 'after', 'emptyBody')
101+
after = typeof raws.after !== 'undefined'
102+
? raws.after
103+
: this.raw(node, 'after', 'emptyBody')
96104
}
97105

98106
if (after) this.builder(escapeHTMLInCSS(after))
99107
this.builder('}', node, 'end')
100108
}
101109

102110
body(node) {
103-
let last = node.nodes.length - 1
111+
let nodes = node.nodes
112+
let last = nodes.length - 1
104113
while (last > 0) {
105-
if (node.nodes[last].type !== 'comment') break
114+
if (nodes[last].type !== 'comment') break
106115
last -= 1
107116
}
108117

109118
let semicolon = this.raw(node, 'semicolon')
110119
let isDocument = node.type === 'document'
111-
for (let i = 0; i < node.nodes.length; i++) {
112-
let child = node.nodes[i]
113-
let before = this.raw(child, 'before')
120+
for (let i = 0; i < nodes.length; i++) {
121+
let child = nodes[i]
122+
let before = child.raws.before
123+
if (typeof before === 'undefined') {
124+
before = this.raw(child, 'before')
125+
}
114126
if (before) this.builder(isDocument ? before : escapeHTMLInCSS(before))
115127
this.stringify(child, last !== i || semicolon)
116128
}
117129
}
118130

119131
comment(node) {
120-
let left = this.raw(node, 'left', 'commentLeft')
121-
let right = this.raw(node, 'right', 'commentRight')
132+
let raws = node.raws
133+
let left = typeof raws.left !== 'undefined'
134+
? raws.left
135+
: this.raw(node, 'left', 'commentLeft')
136+
let right = typeof raws.right !== 'undefined'
137+
? raws.right
138+
: this.raw(node, 'right', 'commentRight')
122139
this.builder(escapeHTMLInCSS('/*' + left + node.text + right + '*/'), node)
123140
}
124141

125142
decl(node, semicolon) {
126-
let between = this.raw(node, 'between', 'colon')
127-
let string = node.prop + between + this.rawValue(node, 'value')
143+
let raws = node.raws
144+
let between = typeof raws.between !== 'undefined'
145+
? raws.between
146+
: this.raw(node, 'between', 'colon')
147+
148+
let rawVal = raws.value
149+
let value = rawVal && rawVal.value === node.value ? rawVal.raw : node.value
150+
151+
let string = node.prop + between + value
128152

129153
if (node.important) {
130-
string += node.raws.important || ' !important'
154+
string += raws.important || ' !important'
131155
}
132156

133157
if (semicolon) string += ';'
@@ -167,9 +191,9 @@ class Stringifier {
167191

168192
// Detect style by other nodes
169193
let root = node.root()
170-
if (!root.rawCache) root.rawCache = {}
171-
if (typeof root.rawCache[detect] !== 'undefined') {
172-
return root.rawCache[detect]
194+
let cache = root.rawCache || (root.rawCache = {})
195+
if (typeof cache[detect] !== 'undefined') {
196+
return cache[detect]
173197
}
174198

175199
if (detect === 'before' || detect === 'after') {
@@ -188,7 +212,7 @@ class Stringifier {
188212

189213
if (typeof value === 'undefined') value = DEFAULT_RAW[detect]
190214

191-
root.rawCache[detect] = value
215+
cache[detect] = value
192216
return value
193217
}
194218

0 commit comments

Comments
 (0)