Skip to content

Values parser #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 24, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Allow omitting quotes on font family
  • Loading branch information
jacobp100 committed Jan 23, 2017
commit 83d470bf82a394302ed9513a5ba706f25eb77f46
51 changes: 51 additions & 0 deletions src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,57 @@ it('allows line height as multiple', () => runTest([
lineHeight: 24,
}));

it('transforms font without quotes', () => runTest([
['font', 'bold italic small-caps 16px/18px Helvetica Neue'],
], {
fontFamily: 'Helvetica Neue',
fontSize: 16,
fontWeight: 'bold',
fontStyle: 'italic',
fontVariant: ['small-caps'],
lineHeight: 18,
}));

it('transforms font-family with double quotes', () => runTest([
['font-family', '"Helvetica Neue"'],
], {
fontFamily: 'Helvetica Neue',
}));

it('transforms font-family with single quotes', () => runTest([
['font-family', '\'Helvetica Neue\''],
], {
fontFamily: 'Helvetica Neue',
}));

it('transforms font-family without quotes', () => runTest([
['font-family', 'Helvetica Neue'],
], {
fontFamily: 'Helvetica Neue',
}));

it('transforms font-family with quotes with otherwise invalid values', () => runTest([
['font-family', '"Goudy Bookletter 1911"'],
], {
fontFamily: 'Goudy Bookletter 1911',
}));

it('transforms font-family with quotes with escaped values', () => runTest([
['font-family', '"test\\A test"'],
], {
fontFamily: 'test\ntest',
}));

it('transforms font-family with quotes with escaped quote', () => runTest([
['font-family', '"test\\"test"'],
], {
fontFamily: 'test"test',
}));

it('does not transform invalid unquoted font-family', () => {
expect(() => transformCss([['font-family', 'Goudy Bookletter 1911']])).toThrow();
});

it('allows blacklisting shorthands', () => {
const actualStyles = transformCss([['border-radius', '50']], ['borderRadius']);
expect(actualStyles).toEqual({ borderRadius: 50 });
Expand Down
13 changes: 12 additions & 1 deletion src/tokenTypes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
const { stringify } = require('postcss-value-parser');
const cssColorKeywords = require('css-color-keywords');

const matchString = (node) => {
if (node.type !== 'string') return null;
return node.value
.replace(/\\([0-9a-f]{1,6})(?:\s|$)/gi, (match, charCode) => (
String.fromCharCode(parseInt(charCode, 16))
))
.replace(/\\/g, '');
};

const hexColorRe = /^(#(?:[0-9a-f]{3,4}){1,2})$/i;
const cssFunctionNameRe = /^(rgba?|hsla?|hwb|lab|lch|gray|color)$/;

Expand All @@ -14,6 +23,7 @@ const matchColor = (node) => {
};

const noneRe = /^(none)$/;
const identRe = /(^-?[_a-z][_a-z0-9-]*$)/i;
// Note if these are wrong, you'll need to change index.js too
const numberRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)$/;
const lengthRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)px$/;
Expand Down Expand Up @@ -41,12 +51,13 @@ module.exports.tokens = {
SPACE: noopToken(node => node.type === 'space'),
SLASH: noopToken(node => node.type === 'div' && node.value === '/'),
COMMA: noopToken(node => node.type === 'div' && node.value === ','),
STRING: valueForTypeToken('string'),
WORD: valueForTypeToken('word'),
NONE: regExpToken(noneRe),
NUMBER: regExpToken(numberRe, Number),
LENGTH: regExpToken(lengthRe, Number),
ANGLE: regExpToken(angleRe),
PERCENT: regExpToken(percentRe),
IDENT: regExpToken(identRe),
STRING: matchString,
COLOR: matchColor,
};
17 changes: 4 additions & 13 deletions src/transforms/font.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const parseFontFamily = require('./fontFamily');
const { regExpToken, tokens } = require('../tokenTypes');

const { SPACE, LENGTH, NUMBER, SLASH, WORD, STRING } = tokens;
const { SPACE, LENGTH, NUMBER, SLASH } = tokens;
const NORMAL = regExpToken(/^(normal)$/);
const STYLE = regExpToken(/^(italic)$/);
const WEIGHT = regExpToken(/^([1-9]00|bold)$/);
Expand All @@ -16,7 +17,7 @@ module.exports = (tokenStream) => {
let fontVariant;
// let fontSize;
let lineHeight;
let fontFamily;
// let fontFamily;

let numStyleWeightVariantMatched = 0;
while (numStyleWeightVariantMatched < 3 && tokenStream.hasTokens()) {
Expand Down Expand Up @@ -48,17 +49,7 @@ module.exports = (tokenStream) => {

tokenStream.expect(SPACE);

if (tokenStream.match(STRING)) {
fontFamily = tokenStream.lastValue;
} else {
fontFamily = tokenStream.expect(WORD);
while (tokenStream.hasTokens()) {
const nextWord = tokenStream.expect(WORD);
fontFamily += ` ${nextWord}`;
}
}

tokenStream.expectEmpty();
const fontFamily = parseFontFamily(tokenStream);

if (fontStyle === undefined) fontStyle = defaultFontStyle;
if (fontWeight === undefined) fontWeight = defaultFontWeight;
Expand Down
22 changes: 22 additions & 0 deletions src/transforms/fontFamily.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { tokens } = require('../tokenTypes');

const { SPACE, IDENT, STRING } = tokens;

module.exports = (tokenStream) => {
let fontFamily;

if (tokenStream.match(STRING)) {
fontFamily = tokenStream.lastValue;
} else {
fontFamily = tokenStream.expect(IDENT);
while (tokenStream.hasTokens()) {
tokenStream.expect(SPACE);
const nextIdent = tokenStream.expect(IDENT);
fontFamily += ` ${nextIdent}`;
}
}

tokenStream.expectEmpty();

return fontFamily;
};
8 changes: 5 additions & 3 deletions src/transforms/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const { regExpToken, tokens } = require('../tokenTypes');
const flex = require('./flex');
const font = require('./font');
const fontFamily = require('./fontFamily');
const transform = require('./transform');
const { directionFactory, anyOrderFactory, shadowOffsetFactory } = require('./util');

const { WORD, COLOR } = tokens;
const { IDENT, WORD, COLOR } = tokens;

const background = tokenStream => ({ $merge: { backgroundColor: tokenStream.match(COLOR) } });
const border = anyOrderFactory({
Expand Down Expand Up @@ -40,8 +41,8 @@ const flexFlow = anyOrderFactory({
default: 'row',
},
});
const fontVariant = tokenStream => [tokenStream.match(WORD)];
const fontWeight = tokenStream => tokenStream.match(WORD);
const fontVariant = tokenStream => [tokenStream.match(IDENT)];
const fontWeight = tokenStream => tokenStream.match(WORD); // Also match numbers as strings
const shadowOffset = shadowOffsetFactory();
const textShadowOffset = shadowOffsetFactory();

Expand All @@ -54,6 +55,7 @@ module.exports = {
flex,
flexFlow,
font,
fontFamily,
fontVariant,
fontWeight,
margin,
Expand Down