Skip to content

Commit 156a4af

Browse files
authored
Merge pull request #62 from styled-components/token-stream-perf
Improve tokenstream performance, implement prettier
2 parents 7beef8f + 2352b49 commit 156a4af

16 files changed

+1397
-875
lines changed

.eslintrc.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
module.exports = {
2-
extends: 'airbnb-base',
3-
};
2+
extends: ['airbnb-base', 'prettier'],
3+
plugins: ['prettier'],
4+
rules: {
5+
'prettier/prettier': 2,
6+
},
7+
}

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"trailingComma": "es5"
5+
}

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
language: node_js
32
dist: trusty
43
node_js:
@@ -8,6 +7,7 @@ script:
87
- node --version
98
- yarn --version
109
- yarn run test
10+
- yarn run lint
1111
notifications:
1212
email:
1313
on_failure: change

package.json

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
{
22
"name": "css-to-react-native",
3-
"version": "2.1.0",
3+
"version": "2.1.1",
44
"description": "Convert CSS text to a React Native stylesheet object",
55
"main": "dist/index.js",
66
"scripts": {
77
"build": "babel src --ignore test.js --out-dir dist",
88
"test": "jest",
99
"test:watch": "jest --watch",
10-
"prepublish": "npm run build"
10+
"lint": "eslint src",
11+
"prepublish": "npm run build",
12+
"precommit": "lint-staged",
13+
"lint-staged": "lint-staged"
1114
},
1215
"files": [
1316
"dist",
@@ -35,12 +38,22 @@
3538
"babel-preset-es2015": "^6.18.0",
3639
"eslint": "^3.9.1",
3740
"eslint-config-airbnb-base": "^10.0.1",
41+
"eslint-config-prettier": "^2.9.0",
3842
"eslint-plugin-import": "^2.2.0",
39-
"jest": "^17.0.0"
43+
"eslint-plugin-prettier": "^2.6.0",
44+
"jest": "^17.0.0",
45+
"lint-staged": "^6.1.0",
46+
"prettier": "^1.10.2"
4047
},
4148
"dependencies": {
4249
"css-color-keywords": "^1.0.0",
4350
"fbjs": "^0.8.5",
4451
"postcss-value-parser": "^3.3.0"
52+
},
53+
"lint-staged": {
54+
"*.js": [
55+
"eslint --fix",
56+
"git add"
57+
]
4558
}
4659
}

src/TokenStream.js

Lines changed: 42 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,74 @@
1-
const SYMBOL_BASE_MATCH = 'SYMBOL_BASE_MATCH';
2-
const SYMBOL_MATCH = 'SYMBOL_MATCH';
1+
const SYMBOL_MATCH = 'SYMBOL_MATCH'
32

43
module.exports = class TokenStream {
54
constructor(nodes, parent) {
6-
this.nodes = nodes;
7-
this.parent = parent;
8-
this.lastFunction = null;
9-
this.lastValue = null;
10-
}
11-
12-
get node() {
13-
return this.nodes[0];
5+
this.index = 0
6+
this.nodes = nodes
7+
this.functionName = parent != null ? parent.value : null
8+
this.lastValue = null
9+
this.rewindIndex = -1
1410
}
1511

1612
hasTokens() {
17-
return this.nodes.length > 0;
18-
}
19-
20-
lookAhead() {
21-
return new TokenStream(this.nodes.slice(1), this.parent);
13+
return this.index <= this.nodes.length - 1
2214
}
2315

24-
[SYMBOL_BASE_MATCH](...tokenDescriptors) {
25-
const node = this.node;
16+
[SYMBOL_MATCH](...tokenDescriptors) {
17+
if (!this.hasTokens()) return null
2618

27-
if (!node) return null;
19+
const node = this.nodes[this.index]
2820

2921
for (let i = 0; i < tokenDescriptors.length; i += 1) {
30-
const tokenDescriptor = tokenDescriptors[i];
31-
const value = tokenDescriptor(node);
32-
if (value !== null) return value;
22+
const tokenDescriptor = tokenDescriptors[i]
23+
const value = tokenDescriptor(node)
24+
if (value !== null) {
25+
this.index += 1
26+
this.lastValue = value
27+
return value
28+
}
3329
}
3430

35-
return null;
36-
}
37-
38-
[SYMBOL_MATCH](...tokenDescriptors) {
39-
const value = this[SYMBOL_BASE_MATCH](...tokenDescriptors);
40-
if (value === null) return null;
41-
this.nodes = this.nodes.slice(1);
42-
this.lastFunction = null;
43-
this.lastValue = value;
44-
return value;
45-
}
46-
47-
test(...tokenDescriptors) {
48-
return this[SYMBOL_BASE_MATCH](...tokenDescriptors) !== null;
31+
return null
4932
}
5033

5134
matches(...tokenDescriptors) {
52-
return this[SYMBOL_MATCH](...tokenDescriptors) !== null;
35+
return this[SYMBOL_MATCH](...tokenDescriptors) !== null
5336
}
5437

5538
expect(...tokenDescriptors) {
56-
const value = this[SYMBOL_MATCH](...tokenDescriptors);
57-
if (value !== null) return value;
58-
return this.throw();
39+
const value = this[SYMBOL_MATCH](...tokenDescriptors)
40+
return value !== null ? value : this.throw()
5941
}
6042

6143
matchesFunction() {
62-
const node = this.node;
63-
if (node.type !== 'function') return null;
64-
const value = new TokenStream(node.nodes, node);
65-
this.nodes = this.nodes.slice(1);
66-
this.lastFunction = value;
67-
this.lastValue = null;
68-
return value;
44+
const node = this.nodes[this.index]
45+
if (node.type !== 'function') return null
46+
const value = new TokenStream(node.nodes, node)
47+
this.index += 1
48+
this.lastValue = null
49+
return value
6950
}
7051

7152
expectFunction() {
72-
const value = this.matchesFunction();
73-
if (value !== null) return value;
74-
return this.throw();
53+
const value = this.matchesFunction()
54+
return value !== null ? value : this.throw()
7555
}
7656

7757
expectEmpty() {
78-
if (this.hasTokens()) this.throw();
58+
if (this.hasTokens()) this.throw()
7959
}
8060

8161
throw() {
82-
throw new Error(`Unexpected token type: ${this.node.type}`);
62+
throw new Error(`Unexpected token type: ${this.nodes[this.index].type}`)
63+
}
64+
65+
saveRewindPoint() {
66+
this.rewindIndex = this.index
67+
}
68+
69+
rewind() {
70+
if (this.rewindIndex === -1) throw new Error('Internal error')
71+
this.index = this.rewindIndex
72+
this.lastValue = null
8373
}
84-
};
74+
}

src/index.js

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,73 @@
11
/* eslint-disable no-param-reassign */
2-
const parse = require('postcss-value-parser');
3-
const camelizeStyleName = require('fbjs/lib/camelizeStyleName');
4-
const transforms = require('./transforms');
5-
const TokenStream = require('./TokenStream');
2+
const parse = require('postcss-value-parser')
3+
const camelizeStyleName = require('fbjs/lib/camelizeStyleName')
4+
const transforms = require('./transforms')
5+
const TokenStream = require('./TokenStream')
66

77
// Note if this is wrong, you'll need to change tokenTypes.js too
8-
const numberOrLengthRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)(?:px)?$/i;
9-
const boolRe = /^true|false$/i;
10-
const nullRe = /^null$/i;
11-
const undefinedRe = /^undefined$/i;
8+
const numberOrLengthRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)(?:px)?$/i
9+
const boolRe = /^true|false$/i
10+
const nullRe = /^null$/i
11+
const undefinedRe = /^undefined$/i
1212

1313
// Undocumented export
14-
export const transformRawValue = (input) => {
15-
const value = input.trim();
14+
export const transformRawValue = input => {
15+
const value = input.trim()
1616

17-
const numberMatch = value.match(numberOrLengthRe);
18-
if (numberMatch !== null) return Number(numberMatch[1]);
17+
const numberMatch = value.match(numberOrLengthRe)
18+
if (numberMatch !== null) return Number(numberMatch[1])
1919

20-
const boolMatch = input.match(boolRe);
21-
if (boolMatch !== null) return boolMatch[0].toLowerCase() === 'true';
20+
const boolMatch = input.match(boolRe)
21+
if (boolMatch !== null) return boolMatch[0].toLowerCase() === 'true'
2222

23-
const nullMatch = input.match(nullRe);
24-
if (nullMatch !== null) return null;
23+
const nullMatch = input.match(nullRe)
24+
if (nullMatch !== null) return null
2525

26-
const undefinedMatch = input.match(undefinedRe);
27-
if (undefinedMatch !== null) return undefined;
26+
const undefinedMatch = input.match(undefinedRe)
27+
if (undefinedMatch !== null) return undefined
2828

29-
return value;
30-
};
29+
return value
30+
}
3131

3232
const baseTransformShorthandValue = (propName, inputValue) => {
33-
const ast = parse(inputValue.trim());
34-
const tokenStream = new TokenStream(ast.nodes);
35-
return transforms[propName](tokenStream);
36-
};
33+
const ast = parse(inputValue.trim())
34+
const tokenStream = new TokenStream(ast.nodes)
35+
return transforms[propName](tokenStream)
36+
}
3737

38-
const transformShorthandValue = (process.env.NODE_ENV === 'production')
39-
? baseTransformShorthandValue
40-
: (propName, inputValue) => {
41-
try {
42-
return baseTransformShorthandValue(propName, inputValue);
43-
} catch (e) {
44-
throw new Error(`Failed to parse declaration "${propName}: ${inputValue}"`);
45-
}
46-
};
38+
const transformShorthandValue =
39+
process.env.NODE_ENV === 'production'
40+
? baseTransformShorthandValue
41+
: (propName, inputValue) => {
42+
try {
43+
return baseTransformShorthandValue(propName, inputValue)
44+
} catch (e) {
45+
throw new Error(
46+
`Failed to parse declaration "${propName}: ${inputValue}"`
47+
)
48+
}
49+
}
4750

4851
export const getStylesForProperty = (propName, inputValue, allowShorthand) => {
49-
const isRawValue = (allowShorthand === false) || !(propName in transforms);
52+
const isRawValue = allowShorthand === false || !(propName in transforms)
5053
const propValue = isRawValue
5154
? transformRawValue(inputValue)
52-
: transformShorthandValue(propName, inputValue.trim());
55+
: transformShorthandValue(propName, inputValue.trim())
5356

54-
return (propValue && propValue.$merge)
57+
return propValue && propValue.$merge
5558
? propValue.$merge
56-
: { [propName]: propValue };
57-
};
59+
: { [propName]: propValue }
60+
}
5861

59-
export const getPropertyName = camelizeStyleName;
62+
export const getPropertyName = camelizeStyleName
6063

61-
export default (rules, shorthandBlacklist = []) => rules.reduce((accum, rule) => {
62-
const propertyName = getPropertyName(rule[0]);
63-
const value = rule[1];
64-
const allowShorthand = shorthandBlacklist.indexOf(propertyName) === -1;
65-
return Object.assign(accum, getStylesForProperty(propertyName, value, allowShorthand));
66-
}, {});
64+
export default (rules, shorthandBlacklist = []) =>
65+
rules.reduce((accum, rule) => {
66+
const propertyName = getPropertyName(rule[0])
67+
const value = rule[1]
68+
const allowShorthand = shorthandBlacklist.indexOf(propertyName) === -1
69+
return Object.assign(
70+
accum,
71+
getStylesForProperty(propertyName, value, allowShorthand)
72+
)
73+
}, {})

0 commit comments

Comments
 (0)