Skip to content

Commit 9e1ca0b

Browse files
committed
feat: parse positive numeric
1 parent 4ebab41 commit 9e1ca0b

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

lib/parsers.js

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,21 @@ function serializeNumber(n) {
132132
* https://drafts.csswg.org/css-values-4/#integers
133133
* https://drafts.csswg.org/cssom/#ref-for-integer-value
134134
*/
135-
exports.parseInteger = function parseInteger(val) {
135+
exports.parseInteger = function parseInteger(val, positive = false) {
136136
const calculated = exports.parseCalc(val);
137137
if (calculated) {
138138
val = calculated;
139139
}
140140
const res = numberRegEx.exec(val);
141141
if (res) {
142142
const [, , integer, decimals, onlyDecimals, exponent] = res;
143-
if (integer === undefined || decimals || onlyDecimals || (exponent && exponent.slice(1) < 0)) {
143+
const invalid =
144+
integer === undefined ||
145+
decimals ||
146+
onlyDecimals ||
147+
(exponent && exponent.slice(1) < 0) ||
148+
(positive && integer < 0);
149+
if (invalid) {
144150
return null;
145151
}
146152
return serializeNumber(val);
@@ -152,12 +158,15 @@ exports.parseInteger = function parseInteger(val) {
152158
* https://drafts.csswg.org/css-values-4/#numbers
153159
* https://drafts.csswg.org/cssom/#ref-for-number-value
154160
*/
155-
exports.parseNumber = function parseNumber(val) {
161+
exports.parseNumber = function parseNumber(val, positive = false) {
156162
const calculated = exports.parseCalc(val);
157163
if (calculated) {
158164
val = calculated;
159165
}
160166
if (numberRegEx.test(val)) {
167+
if (positive && val < 0) {
168+
return null;
169+
}
161170
return serializeNumber(val);
162171
}
163172
return exports.parseCustomVariable(val);
@@ -167,11 +176,11 @@ exports.parseNumber = function parseNumber(val) {
167176
* https://drafts.csswg.org/css-values-4/#lengths
168177
* https://drafts.csswg.org/cssom/#ref-for-length-value
169178
*/
170-
exports.parseLength = function parseLength(val, resolve = false) {
179+
exports.parseLength = function parseLength(val, resolve = false, positive = false) {
171180
if (val === '0') {
172181
return '0px';
173182
}
174-
const calculated = exports.parseCalc(val, v => parseLength(v, resolve));
183+
const calculated = exports.parseCalc(val, v => parseLength(v, resolve, positive));
175184
if (calculated) {
176185
if (!resolve) {
177186
return calculated;
@@ -181,6 +190,9 @@ exports.parseLength = function parseLength(val, resolve = false) {
181190
const res = lengthRegEx.exec(val);
182191
if (res) {
183192
let [, number, , , , , , unit] = res;
193+
if (positive && number < 0) {
194+
return null;
195+
}
184196
unit = unit.toLowerCase();
185197
if (resolve) {
186198
switch (unit) {
@@ -219,11 +231,11 @@ exports.parseLength = function parseLength(val, resolve = false) {
219231
* https://drafts.csswg.org/css-values-4/#percentages
220232
* https://drafts.csswg.org/cssom/#ref-for-percentage-value
221233
*/
222-
exports.parsePercentage = function parsePercentage(val, resolve = false) {
234+
exports.parsePercentage = function parsePercentage(val, resolve = false, positive = false) {
223235
if (val === '0') {
224236
return '0%';
225237
}
226-
const calculated = exports.parseCalc(val, v => parsePercentage(v, resolve));
238+
const calculated = exports.parseCalc(val, v => parsePercentage(v, resolve, positive));
227239
if (calculated) {
228240
if (!resolve) {
229241
return calculated;
@@ -233,13 +245,18 @@ exports.parsePercentage = function parsePercentage(val, resolve = false) {
233245
const res = percentageRegEx.exec(val);
234246
if (res) {
235247
const [, number] = res;
248+
if (positive && number < 0) {
249+
return null;
250+
}
236251
return serializeNumber(number) + '%';
237252
}
238253
return exports.parseCustomVariable(val);
239254
};
240255

241-
exports.parseLengthOrPercentage = function parseLengthOrPercentage(val, resolve) {
242-
return exports.parseLength(val, resolve) || exports.parsePercentage(val, resolve);
256+
exports.parseLengthOrPercentage = function parseLengthOrPercentage(val, resolve, positive) {
257+
return (
258+
exports.parseLength(val, resolve, positive) || exports.parsePercentage(val, resolve, positive)
259+
);
243260
};
244261

245262
/**
@@ -315,8 +332,10 @@ exports.parseAngle = function parseAngle(val, resolve = false) {
315332
return exports.parseCustomVariable(val);
316333
};
317334

318-
exports.parseAngleOrPercentage = function parseAngleOrPercentage(val, resolve) {
319-
return exports.parseAngle(val, resolve) || exports.parsePercentage(val, resolve);
335+
exports.parseAngleOrPercentage = function parseAngleOrPercentage(val, resolve, positive) {
336+
return (
337+
exports.parseAngle(val, resolve, positive) || exports.parsePercentage(val, resolve, positive)
338+
);
320339
};
321340

322341
/**

lib/parsers.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ describe('parseInteger', () => {
77
const invalid = ['string', '1px', '#1', '1.1', '.1', '1e-1', 'calc(1 / 2)'];
88
invalid.forEach(input => expect(parsers.parseInteger(input)).toBeNull());
99
});
10+
it('returns null for negative values when positive is required', () => {
11+
expect(parsers.parseInteger(-1, true)).toBeNull();
12+
});
1013
it('parses integer with exponent', () => {
1114
expect(parsers.parseInteger('1e1')).toBe('10');
1215
expect(parsers.parseInteger('1e+1')).toBe('10');
@@ -23,6 +26,9 @@ describe('parseNumber', () => {
2326
const invalid = ['string', '1px', '#1', 'calc(1 * 1px)'];
2427
invalid.forEach(input => expect(parsers.parseNumber(input)).toBeNull());
2528
});
29+
it('returns null for negative values when positive is required', () => {
30+
expect(parsers.parseNumber(-1, true)).toBeNull();
31+
});
2632
it('parses number with exponent', () => {
2733
expect(parsers.parseNumber('1e1')).toBe('10');
2834
expect(parsers.parseNumber('1e+1')).toBe('10');
@@ -46,6 +52,10 @@ describe('parseLength', () => {
4652
const invalid = ['string', '1', 'px', '1%', '#1px', '1px%', 'calc(1 + 1)', 'calc(1 * 1%)'];
4753
invalid.forEach(input => expect(parsers.parseLength(input)).toBeNull());
4854
});
55+
it('returns null for negative values when positive is required', () => {
56+
expect(parsers.parseLength('-1px', false, true)).toBeNull();
57+
expect(parsers.parseLength('calc(-1px)', true, true)).toBeNull();
58+
});
4959
it('parses 0 to 0px', () => {
5060
expect(parsers.parseLength('0')).toBe('0px');
5161
});
@@ -84,6 +94,10 @@ describe('parsePercentage', () => {
8494
const invalid = ['string', '1', '%', '1px', '#1%', '1%%', 'calc(1 + 1)', 'calc(1 * 1px)'];
8595
invalid.forEach(input => expect(parsers.parsePercentage(input)).toBeNull());
8696
});
97+
it('returns null for negative values when positive is required', () => {
98+
expect(parsers.parsePercentage('-1%', false, true)).toBeNull();
99+
expect(parsers.parsePercentage('calc(-1%)', true, true)).toBeNull();
100+
});
87101
it('parses percentage with exponent', () => {
88102
expect(parsers.parsePercentage('1e1%')).toBe('10%');
89103
expect(parsers.parsePercentage('1e+1%')).toBe('10%');

0 commit comments

Comments
 (0)