Skip to content

Commit 83d470b

Browse files
committed
Allow omitting quotes on font family
1 parent 8ff1ccf commit 83d470b

File tree

5 files changed

+94
-17
lines changed

5 files changed

+94
-17
lines changed

src/index.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,57 @@ it('allows line height as multiple', () => runTest([
320320
lineHeight: 24,
321321
}));
322322

323+
it('transforms font without quotes', () => runTest([
324+
['font', 'bold italic small-caps 16px/18px Helvetica Neue'],
325+
], {
326+
fontFamily: 'Helvetica Neue',
327+
fontSize: 16,
328+
fontWeight: 'bold',
329+
fontStyle: 'italic',
330+
fontVariant: ['small-caps'],
331+
lineHeight: 18,
332+
}));
333+
334+
it('transforms font-family with double quotes', () => runTest([
335+
['font-family', '"Helvetica Neue"'],
336+
], {
337+
fontFamily: 'Helvetica Neue',
338+
}));
339+
340+
it('transforms font-family with single quotes', () => runTest([
341+
['font-family', '\'Helvetica Neue\''],
342+
], {
343+
fontFamily: 'Helvetica Neue',
344+
}));
345+
346+
it('transforms font-family without quotes', () => runTest([
347+
['font-family', 'Helvetica Neue'],
348+
], {
349+
fontFamily: 'Helvetica Neue',
350+
}));
351+
352+
it('transforms font-family with quotes with otherwise invalid values', () => runTest([
353+
['font-family', '"Goudy Bookletter 1911"'],
354+
], {
355+
fontFamily: 'Goudy Bookletter 1911',
356+
}));
357+
358+
it('transforms font-family with quotes with escaped values', () => runTest([
359+
['font-family', '"test\\A test"'],
360+
], {
361+
fontFamily: 'test\ntest',
362+
}));
363+
364+
it('transforms font-family with quotes with escaped quote', () => runTest([
365+
['font-family', '"test\\"test"'],
366+
], {
367+
fontFamily: 'test"test',
368+
}));
369+
370+
it('does not transform invalid unquoted font-family', () => {
371+
expect(() => transformCss([['font-family', 'Goudy Bookletter 1911']])).toThrow();
372+
});
373+
323374
it('allows blacklisting shorthands', () => {
324375
const actualStyles = transformCss([['border-radius', '50']], ['borderRadius']);
325376
expect(actualStyles).toEqual({ borderRadius: 50 });

src/tokenTypes.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
const { stringify } = require('postcss-value-parser');
22
const cssColorKeywords = require('css-color-keywords');
33

4+
const matchString = (node) => {
5+
if (node.type !== 'string') return null;
6+
return node.value
7+
.replace(/\\([0-9a-f]{1,6})(?:\s|$)/gi, (match, charCode) => (
8+
String.fromCharCode(parseInt(charCode, 16))
9+
))
10+
.replace(/\\/g, '');
11+
};
12+
413
const hexColorRe = /^(#(?:[0-9a-f]{3,4}){1,2})$/i;
514
const cssFunctionNameRe = /^(rgba?|hsla?|hwb|lab|lch|gray|color)$/;
615

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

1625
const noneRe = /^(none)$/;
26+
const identRe = /(^-?[_a-z][_a-z0-9-]*$)/i;
1727
// Note if these are wrong, you'll need to change index.js too
1828
const numberRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)$/;
1929
const lengthRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)px$/;
@@ -41,12 +51,13 @@ module.exports.tokens = {
4151
SPACE: noopToken(node => node.type === 'space'),
4252
SLASH: noopToken(node => node.type === 'div' && node.value === '/'),
4353
COMMA: noopToken(node => node.type === 'div' && node.value === ','),
44-
STRING: valueForTypeToken('string'),
4554
WORD: valueForTypeToken('word'),
4655
NONE: regExpToken(noneRe),
4756
NUMBER: regExpToken(numberRe, Number),
4857
LENGTH: regExpToken(lengthRe, Number),
4958
ANGLE: regExpToken(angleRe),
5059
PERCENT: regExpToken(percentRe),
60+
IDENT: regExpToken(identRe),
61+
STRING: matchString,
5162
COLOR: matchColor,
5263
};

src/transforms/font.js

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
const parseFontFamily = require('./fontFamily');
12
const { regExpToken, tokens } = require('../tokenTypes');
23

3-
const { SPACE, LENGTH, NUMBER, SLASH, WORD, STRING } = tokens;
4+
const { SPACE, LENGTH, NUMBER, SLASH } = tokens;
45
const NORMAL = regExpToken(/^(normal)$/);
56
const STYLE = regExpToken(/^(italic)$/);
67
const WEIGHT = regExpToken(/^([1-9]00|bold)$/);
@@ -16,7 +17,7 @@ module.exports = (tokenStream) => {
1617
let fontVariant;
1718
// let fontSize;
1819
let lineHeight;
19-
let fontFamily;
20+
// let fontFamily;
2021

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

4950
tokenStream.expect(SPACE);
5051

51-
if (tokenStream.match(STRING)) {
52-
fontFamily = tokenStream.lastValue;
53-
} else {
54-
fontFamily = tokenStream.expect(WORD);
55-
while (tokenStream.hasTokens()) {
56-
const nextWord = tokenStream.expect(WORD);
57-
fontFamily += ` ${nextWord}`;
58-
}
59-
}
60-
61-
tokenStream.expectEmpty();
52+
const fontFamily = parseFontFamily(tokenStream);
6253

6354
if (fontStyle === undefined) fontStyle = defaultFontStyle;
6455
if (fontWeight === undefined) fontWeight = defaultFontWeight;

src/transforms/fontFamily.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const { tokens } = require('../tokenTypes');
2+
3+
const { SPACE, IDENT, STRING } = tokens;
4+
5+
module.exports = (tokenStream) => {
6+
let fontFamily;
7+
8+
if (tokenStream.match(STRING)) {
9+
fontFamily = tokenStream.lastValue;
10+
} else {
11+
fontFamily = tokenStream.expect(IDENT);
12+
while (tokenStream.hasTokens()) {
13+
tokenStream.expect(SPACE);
14+
const nextIdent = tokenStream.expect(IDENT);
15+
fontFamily += ` ${nextIdent}`;
16+
}
17+
}
18+
19+
tokenStream.expectEmpty();
20+
21+
return fontFamily;
22+
};

src/transforms/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
const { regExpToken, tokens } = require('../tokenTypes');
22
const flex = require('./flex');
33
const font = require('./font');
4+
const fontFamily = require('./fontFamily');
45
const transform = require('./transform');
56
const { directionFactory, anyOrderFactory, shadowOffsetFactory } = require('./util');
67

7-
const { WORD, COLOR } = tokens;
8+
const { IDENT, WORD, COLOR } = tokens;
89

910
const background = tokenStream => ({ $merge: { backgroundColor: tokenStream.match(COLOR) } });
1011
const border = anyOrderFactory({
@@ -40,8 +41,8 @@ const flexFlow = anyOrderFactory({
4041
default: 'row',
4142
},
4243
});
43-
const fontVariant = tokenStream => [tokenStream.match(WORD)];
44-
const fontWeight = tokenStream => tokenStream.match(WORD);
44+
const fontVariant = tokenStream => [tokenStream.match(IDENT)];
45+
const fontWeight = tokenStream => tokenStream.match(WORD); // Also match numbers as strings
4546
const shadowOffset = shadowOffsetFactory();
4647
const textShadowOffset = shadowOffsetFactory();
4748

@@ -54,6 +55,7 @@ module.exports = {
5455
flex,
5556
flexFlow,
5657
font,
58+
fontFamily,
5759
fontVariant,
5860
fontWeight,
5961
margin,

0 commit comments

Comments
 (0)