Skip to content

Commit d9a7a71

Browse files
fix: numeric parsing (shellscape#109)
* Update Numeric Tests * Update Numeric parsing
1 parent 8151483 commit d9a7a71

File tree

6 files changed

+128
-26
lines changed

6 files changed

+128
-26
lines changed

lib/ValuesParser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ module.exports = class ValuesParser extends Parser {
179179
} else if (Word.testWord(tokens, this)) {
180180
// we need to catch variables before the numeric and operator tests
181181
Word.fromTokens(tokens, this);
182-
} else if (Numeric.test(value) || Numeric.testUnit(value)) {
182+
} else if (Numeric.test(value)) {
183183
Numeric.fromTokens(tokens, this);
184184
} else if (UnicodeRange.test(value)) {
185185
UnicodeRange.fromTokens(tokens, this);

lib/nodes/Numeric.js

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,52 @@
88
The above copyright notice and this permission notice shall be
99
included in all copies or substantial portions of this Source Code Form.
1010
*/
11-
const isNumber = require('is-number');
1211

1312
const { registerWalker } = require('../walker');
1413

1514
const Node = require('./Node');
1615

17-
const unitRegex = /%|ch|cm|em|ex|in|mm|ms|pc|pt|px|s|q|rem|vh|vmax|vmin|vw$/i;
16+
/** A Number is:
17+
* 1. None or one plus or minus symbol; then
18+
* 2. Either,
19+
* 2.1. One or more digits; and / or,
20+
* 2.2. One period symbol; followed by,
21+
* 2.2.1. One or more digits;
22+
* then,
23+
* 3. If one "e" letter,
24+
* 3.1. One "e" letter; followed by,
25+
* 3.1.1. None or one plus or minus symbol; followed by,
26+
* 3.1.1.1. One or more digits.
27+
* @see https://drafts.csswg.org/css-syntax/#consume-a-number
28+
*/
29+
const numberRegex = /^([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[Ee][+-]?\d+)?)$/;
30+
31+
/** A Unit is:
32+
* 1. Either,
33+
* 1.1. One dash; followed by,
34+
* 1.1.1. One letter, non-ASCII, underscore, dash; or,
35+
* 1.1.2. One escape slash; followed by,
36+
* 1.1.2.1 One non-newline;
37+
* or,
38+
* 1.2. One letter, non-ASCII, underscore; or,
39+
* 1.3. One escape slash; followed by,
40+
* 1.3.1. One non-newline;
41+
* then,
42+
* 2. Zero or more of;
43+
* 2.1 One letter, non-ASCII, underscore, dash; then / or,
44+
* 2.2 One escape slash; followed by,
45+
* 2.2.1. One non-newline.
46+
* @see https://drafts.csswg.org/css-syntax/#consume-numeric-token
47+
*/
48+
const unitRegex = /^(-?(?:[-A-Z_a-z]|[^\x00-\x7F]|\\[^\n\f\r])(?:[-\w]|[^\x00-\x7F]|\\[^\n\f\r])*|%)$/; // eslint-disable-line no-control-regex
49+
50+
/** A Numeric is:
51+
* 1. One Number; followed by,
52+
* 1.1 Zero or one Unit.
53+
*/
54+
const numericRegex = new RegExp(
55+
`^${numberRegex.source.slice(1, -1) + unitRegex.source.slice(1, -1)}?$`
56+
);
1857

1958
class Numeric extends Node {
2059
constructor(options = {}) {
@@ -25,33 +64,17 @@ class Numeric extends Node {
2564

2665
static fromTokens(tokens, parser) {
2766
parser.fromFirst(tokens, Numeric);
28-
let [[, value]] = tokens;
29-
const unit = Numeric.parseUnit(value);
30-
value = value.replace(unit, '');
67+
68+
const [[, rawValue]] = tokens;
69+
const [, value, unit = ''] = rawValue.match(numericRegex);
3170

3271
const { lastNode } = parser;
33-
lastNode.unit = unit || '';
72+
lastNode.unit = unit;
3473
lastNode.value = value;
3574
}
3675

37-
static parseUnit(what) {
38-
const matches = what.match(unitRegex);
39-
const [result] = matches || [];
40-
return result;
41-
}
42-
4376
static test(what) {
44-
return isNumber(what);
45-
}
46-
47-
static testUnit(what) {
48-
const unit = Numeric.parseUnit(what);
49-
50-
if (unit) {
51-
const remaining = what.replace(unit, '');
52-
return isNumber(remaining);
53-
}
54-
return false;
77+
return numericRegex.test(what);
5578
}
5679
}
5780

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
],
3636
"dependencies": {
3737
"color-name": "^1.1.4",
38-
"is-number": "^7.0.0",
3938
"is-url-superb": "^3.0.0",
4039
"postcss": "^7.0.5",
4140
"url-regex": "^5.0.0"

test/fixtures/numeric.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ module.exports = {
3636
'500ms',
3737
'0.5s + 0.5s',
3838
'-.3s',
39-
'-.3s + 0.5s'
39+
'-.3s + 0.5s',
40+
'1138--thx',
41+
'32deg'
4042
],
4143
throws: ['+-2.', '.', '.rem', '.2.3rem']
4244
};

test/snapshots/numeric.test.js.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,3 +1340,81 @@ Generated by [AVA](https://ava.li).
13401340
value: '500',
13411341
},
13421342
]
1343+
1344+
## 1138--thx
1345+
1346+
> Snapshot 1
1347+
1348+
'1138--thx'
1349+
1350+
> Snapshot 2
1351+
1352+
'1138--thx'
1353+
1354+
> Snapshot 3
1355+
1356+
[
1357+
Numeric {
1358+
raws: {
1359+
after: '',
1360+
before: '',
1361+
},
1362+
source: {
1363+
end: {
1364+
column: 1,
1365+
line: 1,
1366+
},
1367+
input: Input {
1368+
css: '1138--thx',
1369+
hasBOM: false,
1370+
id: '<input css 28>',
1371+
},
1372+
start: {
1373+
column: 1,
1374+
line: 1,
1375+
},
1376+
},
1377+
type: 'numeric',
1378+
unit: '--thx',
1379+
value: '1138',
1380+
},
1381+
]
1382+
1383+
## 32deg
1384+
1385+
> Snapshot 1
1386+
1387+
'32deg'
1388+
1389+
> Snapshot 2
1390+
1391+
'32deg'
1392+
1393+
> Snapshot 3
1394+
1395+
[
1396+
Numeric {
1397+
raws: {
1398+
after: '',
1399+
before: '',
1400+
},
1401+
source: {
1402+
end: {
1403+
column: 1,
1404+
line: 1,
1405+
},
1406+
input: Input {
1407+
css: '32deg',
1408+
hasBOM: false,
1409+
id: '<input css 29>',
1410+
},
1411+
start: {
1412+
column: 1,
1413+
line: 1,
1414+
},
1415+
},
1416+
type: 'numeric',
1417+
unit: 'deg',
1418+
value: '32',
1419+
},
1420+
]

test/snapshots/numeric.test.js.snap

198 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)