Skip to content

Commit

Permalink
Finished performance optimizations by being less restrict on parseCon…
Browse files Browse the repository at this point in the history
…fig() with a simpler regex
  • Loading branch information
renatoi committed May 6, 2015
1 parent e6b4d1e commit 0cb9ac6
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 49 deletions.
23 changes: 17 additions & 6 deletions src/atomizer.js
Expand Up @@ -64,11 +64,11 @@ Atomizer.prototype.addRules = function(rules/*:AtomizerRules*/)/*:void*/ {
Atomizer.prototype.getSyntax = function (isSimple)/*:string*/ {

if (isSimple && !this.syntaxSimple) {
this.syntaxSimple = new Grammar(this.rulesMap, this.helpersMap).getSyntax(true);
this.syntaxSimple = new Grammar(this.rules).getSyntax(true);
}
if (!isSimple && !this.syntax) {
// All Grammar and syntax parsing should be in the Grammar class
this.syntax = new Grammar(this.rulesMap, this.helpersMap).getSyntax();
this.syntax = new Grammar(this.rules).getSyntax();
}

return isSimple ? this.syntaxSimple : this.syntax;
Expand Down Expand Up @@ -147,11 +147,22 @@ Atomizer.prototype.parseConfig = function (config/*:AtomizerConfig*/, options/*:
var rgb;
var values;

if (!match || !match.prop) {
return '';
if (!match || (!match.atomicSelector && !match.selector)) {
// no match, no op
return;
}

ruleIndex = this.rulesMap[match.prop] || this.helpersMap[match.prop] || this.helpersMap[match.helperProp];
// check where this rule belongs to
if (this.rulesMap.hasOwnProperty(match.atomicSelector)) {
ruleIndex = this.rulesMap[match.atomicSelector];
} else if (this.helpersMap.hasOwnProperty(match.atomicSelector)) {
ruleIndex = this.helpersMap[match.atomicSelector];
} else if (this.helpersMap.hasOwnProperty(match.selector)) {
ruleIndex = this.helpersMap[match.selector];
} else {
// not a valid class, no op
return;
}

// get the rule that this class name belongs to.
// this is why we created the dictionary
Expand Down Expand Up @@ -238,7 +249,7 @@ Atomizer.prototype.parseConfig = function (config/*:AtomizerConfig*/, options/*:
}
// now check if named value was passed in the config
else {
propAndValue = [match.prop, '(', matchVal.named, ')'].join('');
propAndValue = [match.atomicSelector, '(', matchVal.named, ')'].join('');

// no custom, warn it
if (!config.custom) {
Expand Down
15 changes: 15 additions & 0 deletions src/helpers.js
Expand Up @@ -26,6 +26,7 @@ module.exports = [
"type": "helper",
"name": "Border",
"matcher": "Bd",
"noParams": true,
"styles": {
"border-width": "1px",
"border-style": "solid"
Expand All @@ -36,6 +37,7 @@ module.exports = [
"type": "helper",
"name": "Border X 1px solid",
"matcher": "BdX",
"noParams": true,
"styles": {
"border-top-width": 0,
"border-right-width": "1px",
Expand All @@ -49,6 +51,7 @@ module.exports = [
"type": "helper",
"name": "Border Y 1px solid",
"matcher": "BdY",
"noParams": true,
"styles": {
"border-top-width": "1px",
"border-right-width": 0,
Expand All @@ -63,6 +66,7 @@ module.exports = [
"type": "helper",
"name": "Border Top 1px solid",
"matcher": "BdT",
"noParams": true,
"styles": {
"border-top-width": "1px",
"border-right-width": 0,
Expand All @@ -76,6 +80,7 @@ module.exports = [
"type": "helper",
"name": "Border End 1px solid",
"matcher": "BdEnd",
"noParams": true,
"styles": {
"border-top-width": 0,
"border-__END__-width": "1px",
Expand All @@ -89,6 +94,7 @@ module.exports = [
"type": "helper",
"name": "Border Bottom 1px solid",
"matcher": "BdB",
"noParams": true,
"styles": {
"border-top-width": 0,
"border-right-width": 0,
Expand All @@ -102,6 +108,7 @@ module.exports = [
"type": "helper",
"name": "Border Start 1px solid",
"matcher": "BdStart",
"noParams": true,
"styles": {
"border-top-width": 0,
"border-__END__-width": 0,
Expand All @@ -121,6 +128,7 @@ module.exports = [
"type": "helper",
"name": "BfcHack",
"matcher": "BfcHack",
"noParams": true,
"styles": {
"display": "table-cell",
"width": "1600px", /* 1 */
Expand All @@ -137,6 +145,7 @@ module.exports = [
"type": "helper",
"name": "Clearfix",
"matcher": "Cf",
"noParams": true,
"styles": {
"zoom": 1
},
Expand All @@ -160,6 +169,7 @@ module.exports = [
"type": "helper",
"name": "Ellipsis",
"matcher": "Ell",
"noParams": true,
"styles": {
"max-width": "100%",
"white-space": "nowrap",
Expand Down Expand Up @@ -189,6 +199,7 @@ module.exports = [
"type": "helper",
"name": "Hidden",
"matcher": "Hidden",
"noParams": true,
"styles": {
"position": "absolute !important",
"clip": "rect(1px 1px 1px 1px)",
Expand All @@ -210,6 +221,7 @@ module.exports = [
"type": "helper",
"name": "IbBox",
"matcher": "IbBox",
"noParams": true,
"styles": {
"display": "inline-block",
"*display": "inline",
Expand Down Expand Up @@ -273,6 +285,7 @@ module.exports = [
"type": "helper",
"name": "Row",
"matcher": "Row",
"noParams": true,
"styles": {
"clear": "both",
"display": "inline-block",
Expand All @@ -294,6 +307,7 @@ module.exports = [
"type": "helper",
"name": "StretchedBox",
"matcher": "StretchedBox",
"noParams": true,
"styles": {
"position": "absolute",
"top": 0,
Expand All @@ -312,6 +326,7 @@ module.exports = [
"type": "helper",
"name": "Zoom",
"matcher": "Zoom",
"noParams": true,
"styles": {
"zoom": "1"
}
Expand Down
73 changes: 30 additions & 43 deletions src/lib/grammar.js
Expand Up @@ -51,6 +51,8 @@ var GRAMMAR = {
'BOUNDARY' : '(?:^|\\s|"|\'|\{)',
'PARENT' : '[a-zA-Z][-_a-zA-Z0-9]+?',
'PARENT_SEP' : '[>_+]',
// all characters allowed to be a prop
'PROP' : '[A-Za-z]+',
// all character allowed to be in values
'VALUES' : '[-_,.#$/%0-9a-zA-Z]+',
'FRACTION' : '(?<numerator>[0-9]+)\\/(?<denominator>[1-9](?:[0-9]+)?)',
Expand Down Expand Up @@ -131,33 +133,37 @@ var VALUE_SYNTAXE = XRegExp([
* this is important so "B" doesn't match "Bgc"
* e.g. Use (Bgc|B) instead of (B|Bgc)
*/
function getSortedKeys(map) {
return Object.keys(map).sort(function (a, b) {
function getSortedKeys(arr) {
return arr.length > 1 ? arr.sort(function (a, b) {
return a > b ? -1 : 1;
}).join('|');
}).join('|') : arr.toString();
}

function buildRegex(map, isParamRequired) {
var keys = getSortedKeys(map);

return keys.length && [
// matcher
'(?<prop>',
keys,
')',
'(?:\\(',
'(?<atomicValues>',
GRAMMAR.VALUES,
')',
'\\))',
isParamRequired ? '?' : ''
].join('');
function buildRegex(matchersParams, matchersNoParams) {
matchersParams = matchersParams ? '(?<atomicSelector>' + matchersParams + ')\\((?<atomicValues>' + GRAMMAR.VALUES + ')\\)' : '';
matchersNoParams = matchersNoParams ? '(?<selector>' + matchersNoParams + ')' : '';
return '(?:' + [matchersParams, matchersNoParams].join('|') + ')';
}

function Grammar(rulesMap, helpersMap) {
this.mainSyntax = [];
this.addSyntaxRegex(buildRegex(rulesMap));
this.addSyntaxRegex(buildRegex(helpersMap, false));
function Grammar(rules) {
var matchersParams = [];
var matchersNoParams = [];
var matchersParamsStr;
var matchersNoParamsStr;

rules.forEach(function (rule) {
if (rule.noParams) {
matchersNoParams.push(rule.matcher);
} else {
matchersParams.push(rule.matcher);
}
});

matchersParamsStr = getSortedKeys(matchersParams);
matchersNoParamsStr = getSortedKeys(matchersNoParams);

this.simpleSyntax = buildRegex(GRAMMAR.PROP, matchersNoParamsStr);
this.complexSyntax = buildRegex(matchersParamsStr, matchersNoParamsStr);
}

/**
Expand All @@ -171,26 +177,6 @@ Grammar.matchValue = function matchValue(value) {
return XRegExp.exec(value, VALUE_SYNTAXE);
};

Grammar.prototype.addSyntaxRegex = function addRegex(regex)/*:string*/ {
regex && this.mainSyntax.push(regex);
};

Grammar.prototype.getMainSyntax = function getMainSyntax(isSimple)/*:string*/ {
// simple regex makes the search faster
// we don't care if the prop is valid on a simple case
// we just care that the syntax is correct and we capture each group
if (isSimple) {
return [
'(?:',
'(?<prop>[A-Za-z]+)',
'\\((?<atomicValues>', GRAMMAR.VALUES, ')\\)',
')',
].join('');
} else {
return this.mainSyntax.length > 1 ? '(?:' + this.mainSyntax.join('|') + ')' : this.mainSyntax[0];
}
};

Grammar.prototype.getSyntax = function getSyntax(isSimple)/*:string*/ {
var syntax = [
// word boundary
Expand All @@ -199,7 +185,8 @@ Grammar.prototype.getSyntax = function getSyntax(isSimple)/*:string*/ {
'(?<parentSelector>',
isSimple ? GRAMMAR.PARENT_SELECTOR_SIMPLE : GRAMMAR.PARENT_SELECTOR,
')?',
this.getMainSyntax(isSimple),
// the main syntax
isSimple ? this.simpleSyntax : this.complexSyntax,
'(?<important>',
GRAMMAR.IMPORTANT,
')?',
Expand Down
16 changes: 16 additions & 0 deletions tests/atomizer.js
Expand Up @@ -130,6 +130,16 @@ describe('Atomizer()', function () {
});
expect(result).to.deep.equal(expected);
});
it('returns empty object if invalid class names have been passed', function () {
var atomizer = new Atomizer();
var expected = {};
var result = atomizer.parseConfig({
classNames: [
'RandomInvalidClass'
]
});
expect(result).to.deep.equal(expected);
});
});
describe('getConfig()', function () {
it ('returns a valid config object when given classes and no config', function () {
Expand Down Expand Up @@ -387,6 +397,7 @@ describe('Atomizer()', function () {
type: 'helper',
name: 'Bar',
matcher: 'Bar',
noParams: true,
styles: {
'bar': 'foo'
}
Expand All @@ -396,6 +407,7 @@ describe('Atomizer()', function () {
type: 'helper',
name: 'Baz',
matcher: 'Baz',
noParams: true,
styles: {
'baz': 'foo'
}
Expand Down Expand Up @@ -518,6 +530,7 @@ describe('Atomizer()', function () {
type: 'helper',
name: 'Foo',
matcher: 'Foo',
noParams: true,
styles: {
foo: 'bar'
}
Expand Down Expand Up @@ -587,6 +600,7 @@ describe('Atomizer()', function () {
type: 'pattern',
name: 'color',
matcher: 'C',
noParams: true,
styles: {
'color': '$0'
}
Expand All @@ -595,6 +609,7 @@ describe('Atomizer()', function () {
type: 'pattern',
name: 'display',
matcher: 'D',
noParams: true,
styles: {
'display': '$0'
},
Expand All @@ -606,6 +621,7 @@ describe('Atomizer()', function () {
type: 'helper',
name: 'foo',
matcher: 'Foo',
noParams: true,
styles: {
'font-weight': 'bold'
}
Expand Down

0 comments on commit 0cb9ac6

Please sign in to comment.