postcss-calc 11.0.3 → 11.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -8
- package/package.json +10 -6
- package/src/index.js +37 -163
- package/src/lib/node.js +5 -5
- package/src/lib/parser.js +40 -33
- package/src/lib/serialize.js +73 -33
- package/src/lib/simplify/bucket.js +1 -1
- package/src/lib/simplify/call.js +30 -2
- package/src/lib/simplify/clamp.js +25 -3
- package/src/lib/simplify/fold.js +14 -1
- package/src/lib/simplify/hypot.js +3 -3
- package/src/lib/simplify/min-max.js +3 -3
- package/src/lib/simplify/mod-rem.js +3 -3
- package/src/lib/simplify/round.js +4 -4
- package/src/lib/tokenizer.js +42 -30
- package/src/reduce.js +176 -0
- package/types/index.d.ts +0 -15
- package/types/lib/parser.d.ts +16 -0
- package/types/lib/serialize.d.ts +4 -0
- package/types/lib/simplify/call.d.ts +11 -1
- package/types/lib/simplify/fold.d.ts +14 -1
- package/types/lib/tokenizer.d.ts +3 -1
- package/types/reduce.d.ts +41 -0
- package/types/lib/type.d.ts +0 -23
package/src/lib/serialize.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Spec: https://www.w3.org/TR/css-values-4/#serialize-a-calculation-tree
|
|
2
|
-
// Outer calc() is added
|
|
3
|
-
//
|
|
4
|
-
|
|
2
|
+
// Outer calc() is added when the top-level result contains an arithmetic
|
|
3
|
+
// operator, or when a finite scalar is negative.
|
|
4
|
+
|
|
5
|
+
import { num, dim } from './node.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @typedef {import('./node.js').Node} Node
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
* @typedef {object} SerializeOptions
|
|
12
13
|
* @property {number | false} [precision] Decimal places for numbers. `false` disables rounding. Default 5.
|
|
13
14
|
* @property {string} [calcName] Wrapper name to use when `calc()` is needed. Default `'calc'`.
|
|
15
|
+
* @property {boolean} [unwrapSingleNegativeNumber] Serialize finite negative scalars without a wrapper. Internal selector-only mode.
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
// Below this is float noise, not a value: `0.1 + 0.2 - 0.3` is 5.5e-17.
|
|
@@ -78,6 +80,20 @@ function serializeNumber(v) {
|
|
|
78
80
|
return text;
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Round and serialize a finite scalar once so callers can use the same value
|
|
85
|
+
* to decide its syntactic context and render its text.
|
|
86
|
+
*
|
|
87
|
+
* @param {import('./node.js').Num | import('./node.js').Dim} node
|
|
88
|
+
* @param {number | false} prec
|
|
89
|
+
* @return {{value: number, text: string}}
|
|
90
|
+
*/
|
|
91
|
+
function serializeScalar(node, prec) {
|
|
92
|
+
const value = round(node.value, prec);
|
|
93
|
+
const text = `${serializeNumber(value)}${node.type === 'Dim' ? node.unit : ''}`;
|
|
94
|
+
return { value, text };
|
|
95
|
+
}
|
|
96
|
+
|
|
81
97
|
/**
|
|
82
98
|
* @param {Node} node
|
|
83
99
|
* @param {SerializeOptions} [opts]
|
|
@@ -96,6 +112,22 @@ function serialize(node, opts = {}) {
|
|
|
96
112
|
return `${calcName}(${degenerateKeyword(node.value)} * 1${node.unit})`;
|
|
97
113
|
}
|
|
98
114
|
|
|
115
|
+
if (node.type === 'Num' || node.type === 'Dim') {
|
|
116
|
+
const scalar = serializeScalar(node, prec);
|
|
117
|
+
|
|
118
|
+
// A finite negative scalar must stay inside calc() so CSS parses it as a
|
|
119
|
+
// calculation result (and can apply range clamping) rather than as an
|
|
120
|
+
// invalid bare value. Base this on the serialized value so tiny negative
|
|
121
|
+
// floating-point noise that rounds to zero does not get wrapped.
|
|
122
|
+
if (scalar.value < 0) {
|
|
123
|
+
return opts.unwrapSingleNegativeNumber
|
|
124
|
+
? scalar.text
|
|
125
|
+
: `${calcName}(${scalar.text})`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return scalar.text;
|
|
129
|
+
}
|
|
130
|
+
|
|
99
131
|
// A grouped sum with a leading negative term is the canonical result of
|
|
100
132
|
// negating a parenthesized expression. Re-invert its terms for the body so
|
|
101
133
|
// the grouping survives as `-(...)` instead of becoming `-a - b`.
|
|
@@ -105,22 +137,14 @@ function serialize(node, opts = {}) {
|
|
|
105
137
|
node.terms.length > 1 &&
|
|
106
138
|
displaySign(node.terms[0]).sign === -1
|
|
107
139
|
) {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
})),
|
|
114
|
-
});
|
|
115
|
-
return `${calcName}(-(${serializeExpr(body, prec)}))`;
|
|
140
|
+
const invertedTerms = node.terms.map((t) => ({
|
|
141
|
+
sign: /** @type {1 | -1} */ (-t.sign),
|
|
142
|
+
node: t.node,
|
|
143
|
+
}));
|
|
144
|
+
return `${calcName}(-(${serializeSumTerms(invertedTerms, prec)}))`;
|
|
116
145
|
}
|
|
117
146
|
|
|
118
|
-
if (
|
|
119
|
-
node.type === 'Num' ||
|
|
120
|
-
node.type === 'Dim' ||
|
|
121
|
-
node.type === 'Ident' ||
|
|
122
|
-
node.type === 'Call'
|
|
123
|
-
) {
|
|
147
|
+
if (node.type === 'Ident' || node.type === 'Call') {
|
|
124
148
|
return serializeExpr(node, prec);
|
|
125
149
|
}
|
|
126
150
|
|
|
@@ -146,7 +170,7 @@ function serializeExpr(node, prec) {
|
|
|
146
170
|
if (isDegenerate(node.value)) {
|
|
147
171
|
return degenerateKeyword(node.value);
|
|
148
172
|
}
|
|
149
|
-
return
|
|
173
|
+
return serializeScalar(node, prec).text;
|
|
150
174
|
case 'Dim':
|
|
151
175
|
if (isDegenerate(node.value)) {
|
|
152
176
|
// Nested degenerate Dim wraps in calc() so the `<kw> * 1<unit>` form
|
|
@@ -154,7 +178,7 @@ function serializeExpr(node, prec) {
|
|
|
154
178
|
// inside a Product — `0 * Dim(Infinity, px)` would re-fold as NaN.
|
|
155
179
|
return `calc(${degenerateKeyword(node.value)} * 1${node.unit})`;
|
|
156
180
|
}
|
|
157
|
-
return
|
|
181
|
+
return serializeScalar(node, prec).text;
|
|
158
182
|
case 'Ident':
|
|
159
183
|
return node.name;
|
|
160
184
|
case 'Call': {
|
|
@@ -182,27 +206,27 @@ function displaySign(term) {
|
|
|
182
206
|
if (node.type === 'Num' && Number.isFinite(node.value) && node.value < 0) {
|
|
183
207
|
return {
|
|
184
208
|
sign: /** @type {1 | -1} */ (-sign),
|
|
185
|
-
magnitude:
|
|
209
|
+
magnitude: num(-node.value),
|
|
186
210
|
};
|
|
187
211
|
}
|
|
188
212
|
if (node.type === 'Dim' && Number.isFinite(node.value) && node.value < 0) {
|
|
189
213
|
return {
|
|
190
214
|
sign: /** @type {1 | -1} */ (-sign),
|
|
191
|
-
magnitude:
|
|
215
|
+
magnitude: dim(-node.value, node.unit),
|
|
192
216
|
};
|
|
193
217
|
}
|
|
194
218
|
return { sign, magnitude: node };
|
|
195
219
|
}
|
|
196
220
|
|
|
197
221
|
/**
|
|
198
|
-
* @param {
|
|
222
|
+
* @param {import('./node.js').SumTerm[]} terms
|
|
199
223
|
* @param {number | false} prec
|
|
200
224
|
* @return {string}
|
|
201
225
|
*/
|
|
202
|
-
function
|
|
226
|
+
function serializeSumTerms(terms, prec) {
|
|
203
227
|
let out = '';
|
|
204
|
-
for (
|
|
205
|
-
const { sign, magnitude } = displaySign(
|
|
228
|
+
for (let i = 0; i < terms.length; i++) {
|
|
229
|
+
const { sign, magnitude } = displaySign(terms[i]);
|
|
206
230
|
if (i === 0) {
|
|
207
231
|
if (magnitude.type === 'Sum' && magnitude.grouped) {
|
|
208
232
|
const body = `(${serializeExpr(magnitude, prec)})`;
|
|
@@ -225,6 +249,15 @@ function serializeSum(sum, prec) {
|
|
|
225
249
|
return out;
|
|
226
250
|
}
|
|
227
251
|
|
|
252
|
+
/**
|
|
253
|
+
* @param {Sum} sum
|
|
254
|
+
* @param {number | false} prec
|
|
255
|
+
* @return {string}
|
|
256
|
+
*/
|
|
257
|
+
function serializeSum(sum, prec) {
|
|
258
|
+
return serializeSumTerms(sum.terms, prec);
|
|
259
|
+
}
|
|
260
|
+
|
|
228
261
|
/**
|
|
229
262
|
* Fold a leading negation into a finite leading Num if there is one
|
|
230
263
|
* (`-(0.5 * x)` → `-0.5 * x`); else use `-(…)` for Sum/Product or `-x`.
|
|
@@ -249,11 +282,8 @@ function serializeLeadingNeg(node, prec) {
|
|
|
249
282
|
const negatedFactors =
|
|
250
283
|
negatedValue === 1
|
|
251
284
|
? rest
|
|
252
|
-
: [
|
|
253
|
-
|
|
254
|
-
...rest,
|
|
255
|
-
];
|
|
256
|
-
return serializeProduct({ type: 'Product', factors: negatedFactors }, prec);
|
|
285
|
+
: [{ exponent: 1, node: num(negatedValue) }, ...rest];
|
|
286
|
+
return serializeFactors(negatedFactors, prec);
|
|
257
287
|
}
|
|
258
288
|
const body = serializeExpr(node, prec);
|
|
259
289
|
return node.type === 'Sum' || node.type === 'Product'
|
|
@@ -262,13 +292,14 @@ function serializeLeadingNeg(node, prec) {
|
|
|
262
292
|
}
|
|
263
293
|
|
|
264
294
|
/**
|
|
265
|
-
* @param {
|
|
295
|
+
* @param {ProductFactor[]} factors
|
|
266
296
|
* @param {number | false} prec
|
|
267
297
|
* @return {string}
|
|
268
298
|
*/
|
|
269
|
-
function
|
|
299
|
+
function serializeFactors(factors, prec) {
|
|
270
300
|
let out = '';
|
|
271
|
-
for (
|
|
301
|
+
for (let i = 0; i < factors.length; i++) {
|
|
302
|
+
const f = factors[i];
|
|
272
303
|
let body = serializeExpr(f.node, prec);
|
|
273
304
|
// A Sum factor needs parens: `a * (b + c)`. Flat canonical form means
|
|
274
305
|
// this is the only place parens are required.
|
|
@@ -285,4 +316,13 @@ function serializeProduct(product, prec) {
|
|
|
285
316
|
return out;
|
|
286
317
|
}
|
|
287
318
|
|
|
319
|
+
/**
|
|
320
|
+
* @param {Product} product
|
|
321
|
+
* @param {number | false} prec
|
|
322
|
+
* @return {string}
|
|
323
|
+
*/
|
|
324
|
+
function serializeProduct(product, prec) {
|
|
325
|
+
return serializeFactors(product.factors, prec);
|
|
326
|
+
}
|
|
327
|
+
|
|
288
328
|
export { serialize };
|
|
@@ -21,7 +21,7 @@ import { convert } from '../convertUnits.js';
|
|
|
21
21
|
* @return {UnitBucket[]}
|
|
22
22
|
*/
|
|
23
23
|
function mergeConvertibleBuckets(buckets) {
|
|
24
|
-
const ordered =
|
|
24
|
+
const ordered = buckets.sort((a, b) => a.order - b.order);
|
|
25
25
|
/** @type {Set<string>} */ const merged = new Set();
|
|
26
26
|
/** @type {UnitBucket[]} */ const out = [];
|
|
27
27
|
for (const b of ordered) {
|
package/src/lib/simplify/call.js
CHANGED
|
@@ -26,7 +26,7 @@ import { call } from '../node.js';
|
|
|
26
26
|
// Bare CSS math functions with implemented simplification semantics, keyed
|
|
27
27
|
// by lowercase name. calc() and its vendor-prefixed forms are handled
|
|
28
28
|
// separately as wrappers in simplifyCall. This map is the single source of
|
|
29
|
-
// truth for
|
|
29
|
+
// truth for dispatch, `isSupportedMathFunction`, and `QUICK_MATH_TEST`.
|
|
30
30
|
/** @type {Map<string, MathSimplifier>} */
|
|
31
31
|
const MATH_SIMPLIFIERS = new Map([
|
|
32
32
|
['min', simplifyMinMax],
|
|
@@ -51,6 +51,29 @@ const MATH_SIMPLIFIERS = new Map([
|
|
|
51
51
|
['exp', (_name, args) => simplifyExp(args)],
|
|
52
52
|
]);
|
|
53
53
|
|
|
54
|
+
const mathFnNames = [...MATH_SIMPLIFIERS.keys()].sort(
|
|
55
|
+
(a, b) => b.length - a.length
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const QUICK_MATH_TEST = new RegExp(
|
|
59
|
+
`(?:-(?:webkit|moz)-)?(?:calc|${mathFnNames.join('|')})\\(`,
|
|
60
|
+
'i'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Fast check to determine whether a CSS component value could contain
|
|
65
|
+
* a supported calculation or math function call (or an escape sequence
|
|
66
|
+
* that could decode to one).
|
|
67
|
+
*
|
|
68
|
+
* @param {string} value
|
|
69
|
+
* @return {boolean}
|
|
70
|
+
*/
|
|
71
|
+
function hasPotentialMathFunction(value) {
|
|
72
|
+
return (
|
|
73
|
+
value.includes('(') && (QUICK_MATH_TEST.test(value) || value.includes('\\'))
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
54
77
|
/**
|
|
55
78
|
* Whether a bare CSS math function has an implemented simplifier.
|
|
56
79
|
*
|
|
@@ -91,4 +114,9 @@ function simplifyCall(node, simplify) {
|
|
|
91
114
|
return call(node.name, args);
|
|
92
115
|
}
|
|
93
116
|
|
|
94
|
-
export {
|
|
117
|
+
export {
|
|
118
|
+
isSupportedMathFunction,
|
|
119
|
+
simplifyCall,
|
|
120
|
+
hasPotentialMathFunction,
|
|
121
|
+
QUICK_MATH_TEST,
|
|
122
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { foldConstArgs } from './fold.js';
|
|
1
|
+
import { call } from '../node.js';
|
|
2
|
+
import { foldConstArgs, foldResult } from './fold.js';
|
|
3
|
+
import { simplifyMinMax } from './min-max.js';
|
|
3
4
|
|
|
4
5
|
/** @typedef {import('../node.js').Node} Node */
|
|
5
6
|
|
|
@@ -9,16 +10,37 @@ import { foldConstArgs } from './fold.js';
|
|
|
9
10
|
*/
|
|
10
11
|
function simplifyClamp(args) {
|
|
11
12
|
if (args.length === 3) {
|
|
13
|
+
const minNone = isNone(args[0]);
|
|
14
|
+
const maxNone = isNone(args[2]);
|
|
15
|
+
|
|
16
|
+
if (minNone && maxNone) {
|
|
17
|
+
return args[1];
|
|
18
|
+
}
|
|
19
|
+
if (minNone) {
|
|
20
|
+
return simplifyMinMax('min', [args[1], args[2]]);
|
|
21
|
+
}
|
|
22
|
+
if (maxNone) {
|
|
23
|
+
return simplifyMinMax('max', [args[0], args[1]]);
|
|
24
|
+
}
|
|
25
|
+
|
|
12
26
|
const fold = foldConstArgs(args);
|
|
13
27
|
if (fold !== null) {
|
|
14
28
|
const [lo, v, hi] = /** @type {[number, number, number]} */ (fold.values);
|
|
15
29
|
// Spec §10.8: clamp(MIN, VAL, MAX) = max(MIN, min(VAL, MAX)). The
|
|
16
30
|
// outer max(MIN, …) means MIN wins when MIN > MAX — not MAX.
|
|
17
31
|
const clamped = Math.max(lo, Math.min(v, hi));
|
|
18
|
-
return fold
|
|
32
|
+
return foldResult(fold, clamped);
|
|
19
33
|
}
|
|
20
34
|
}
|
|
21
35
|
return call('clamp', args);
|
|
22
36
|
}
|
|
23
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {Node} node
|
|
40
|
+
* @return {boolean}
|
|
41
|
+
*/
|
|
42
|
+
function isNone(node) {
|
|
43
|
+
return node.type === 'Ident' && node.name.toLowerCase() === 'none';
|
|
44
|
+
}
|
|
45
|
+
|
|
24
46
|
export { simplifyClamp };
|
package/src/lib/simplify/fold.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
import { num, dim } from '../node.js';
|
|
1
2
|
import { baseOf, convert } from '../convertUnits.js';
|
|
2
3
|
|
|
3
4
|
/** @typedef {import('../node.js').Node} Node */
|
|
5
|
+
/** @typedef {import('../node.js').Num} Num */
|
|
6
|
+
/** @typedef {import('../node.js').Dim} Dim */
|
|
4
7
|
/** @typedef {import('../convertUnits.js').BaseType} BaseType */
|
|
5
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Construct a Num or Dim node from a folded result.
|
|
11
|
+
* @param {{ unit: string }} fold
|
|
12
|
+
* @param {number} value
|
|
13
|
+
* @return {Num | Dim}
|
|
14
|
+
*/
|
|
15
|
+
function foldResult(fold, value) {
|
|
16
|
+
return fold.unit === '' ? num(value) : dim(value, fold.unit);
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
/**
|
|
7
20
|
* @param {Node[]} args
|
|
8
21
|
* @return {{ values: number[], unit: string } | null}
|
|
@@ -62,4 +75,4 @@ function foldDimArgs(args, unit, base) {
|
|
|
62
75
|
return { values, unit };
|
|
63
76
|
}
|
|
64
77
|
|
|
65
|
-
export { foldConstArgs };
|
|
78
|
+
export { foldConstArgs, foldResult };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// §10.5 — hypot. Empty args return null from foldConstArgs naturally.
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { foldConstArgs } from './fold.js';
|
|
3
|
+
import { call } from '../node.js';
|
|
4
|
+
import { foldConstArgs, foldResult } from './fold.js';
|
|
5
5
|
|
|
6
6
|
/** @typedef {import('../node.js').Node} Node */
|
|
7
7
|
|
|
@@ -16,7 +16,7 @@ function simplifyHypot(args) {
|
|
|
16
16
|
}
|
|
17
17
|
const sumSq = fold.values.reduce((acc, v) => acc + v * v, 0);
|
|
18
18
|
const result = Math.sqrt(sumSq);
|
|
19
|
-
return fold
|
|
19
|
+
return foldResult(fold, result);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export { simplifyHypot };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { foldConstArgs } from './fold.js';
|
|
1
|
+
import { call } from '../node.js';
|
|
2
|
+
import { foldConstArgs, foldResult } from './fold.js';
|
|
3
3
|
|
|
4
4
|
/** @typedef {import('../node.js').Node} Node */
|
|
5
5
|
|
|
@@ -13,7 +13,7 @@ function simplifyMinMax(name, args) {
|
|
|
13
13
|
if (fold !== null) {
|
|
14
14
|
const fn = name.toLowerCase() === 'min' ? Math.min : Math.max;
|
|
15
15
|
const value = fn(...fold.values);
|
|
16
|
-
return fold
|
|
16
|
+
return foldResult(fold, value);
|
|
17
17
|
}
|
|
18
18
|
return call(name, args);
|
|
19
19
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { num,
|
|
2
|
-
import { foldConstArgs } from './fold.js';
|
|
1
|
+
import { num, call } from '../node.js';
|
|
2
|
+
import { foldConstArgs, foldResult } from './fold.js';
|
|
3
3
|
|
|
4
4
|
/** @typedef {import('../node.js').Node} Node */
|
|
5
5
|
|
|
@@ -23,7 +23,7 @@ function simplifyModRem(name, args) {
|
|
|
23
23
|
if (Number.isNaN(result)) {
|
|
24
24
|
return num(Number.NaN);
|
|
25
25
|
}
|
|
26
|
-
return fold
|
|
26
|
+
return foldResult(fold, result);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { num,
|
|
2
|
-
import { foldConstArgs } from './fold.js';
|
|
1
|
+
import { num, ident, call } from '../node.js';
|
|
2
|
+
import { foldConstArgs, foldResult } from './fold.js';
|
|
3
3
|
|
|
4
4
|
/** @typedef {import('../node.js').Node} Node */
|
|
5
5
|
|
|
@@ -58,14 +58,14 @@ function simplifyRound(args) {
|
|
|
58
58
|
} else {
|
|
59
59
|
result = a < 0 || Object.is(a, -0) ? -0 : 0;
|
|
60
60
|
}
|
|
61
|
-
return fold
|
|
61
|
+
return foldResult(fold, result);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
const result = applyRound(strategy, a, b);
|
|
65
65
|
if (Number.isNaN(result)) {
|
|
66
66
|
return num(Number.NaN);
|
|
67
67
|
}
|
|
68
|
-
return fold
|
|
68
|
+
return foldResult(fold, result);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
package/src/lib/tokenizer.js
CHANGED
|
@@ -26,6 +26,32 @@ function tokenize(input) {
|
|
|
26
26
|
return tokenizeTokens(tokenizeCss({ css: input }), input.length);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* CSS absorbs leading signs (`-5px` is one token); the parser expects
|
|
31
|
+
* punct sign + unsigned numeric, so split them back out.
|
|
32
|
+
* @param {Token[]} tokens
|
|
33
|
+
* @param {string} raw
|
|
34
|
+
* @param {string | undefined} unit
|
|
35
|
+
* @param {number} pos
|
|
36
|
+
* @param {boolean} ws
|
|
37
|
+
* @return {void}
|
|
38
|
+
*/
|
|
39
|
+
function pushNumeric(tokens, raw, unit, pos, ws) {
|
|
40
|
+
let value = /** @type {RegExpExecArray} */ (NUMERIC_RAW.exec(raw))[0];
|
|
41
|
+
const sign = value[0];
|
|
42
|
+
if (sign === '+' || sign === '-') {
|
|
43
|
+
tokens.push({ type: 'punct', value: sign, pos, ws });
|
|
44
|
+
value = value.slice(1);
|
|
45
|
+
pos += 1;
|
|
46
|
+
ws = false;
|
|
47
|
+
}
|
|
48
|
+
if (unit === undefined) {
|
|
49
|
+
tokens.push({ type: 'number', value, pos, ws });
|
|
50
|
+
} else {
|
|
51
|
+
tokens.push({ type: 'dimension', value, unit, pos, ws });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
/**
|
|
30
56
|
* Convert a slice of an existing CSS token stream into the token subset used
|
|
31
57
|
* by the calculation parser. Token positions remain relative to the original
|
|
@@ -33,52 +59,38 @@ function tokenize(input) {
|
|
|
33
59
|
*
|
|
34
60
|
* @param {import('@csstools/css-tokenizer').CSSToken[]} cssTokens
|
|
35
61
|
* @param {number} eofPosition
|
|
62
|
+
* @param {number} [start]
|
|
63
|
+
* @param {number} [end]
|
|
36
64
|
* @return {Token[]}
|
|
37
65
|
*/
|
|
38
|
-
function tokenizeTokens(
|
|
66
|
+
function tokenizeTokens(
|
|
67
|
+
cssTokens,
|
|
68
|
+
eofPosition,
|
|
69
|
+
start = 0,
|
|
70
|
+
end = cssTokens.length
|
|
71
|
+
) {
|
|
39
72
|
/** @type {Token[]} */
|
|
40
73
|
const tokens = [];
|
|
41
74
|
let ws = true;
|
|
42
75
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @param {string} raw
|
|
47
|
-
* @param {string | undefined} unit
|
|
48
|
-
* @param {number} pos
|
|
49
|
-
* @return {void}
|
|
50
|
-
*/
|
|
51
|
-
function pushNumeric(raw, unit, pos) {
|
|
52
|
-
let value = /** @type {RegExpExecArray} */ (NUMERIC_RAW.exec(raw))[0];
|
|
53
|
-
const sign = value[0];
|
|
54
|
-
if (sign === '+' || sign === '-') {
|
|
55
|
-
tokens.push({ type: 'punct', value: sign, pos, ws });
|
|
56
|
-
value = value.slice(1);
|
|
57
|
-
pos += 1;
|
|
58
|
-
ws = false;
|
|
59
|
-
}
|
|
60
|
-
if (unit === undefined) {
|
|
61
|
-
tokens.push({ type: 'number', value, pos, ws });
|
|
62
|
-
} else {
|
|
63
|
-
tokens.push({ type: 'dimension', value, unit, pos, ws });
|
|
64
|
-
}
|
|
65
|
-
ws = false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
for (const t of cssTokens) {
|
|
76
|
+
for (let i = start; i < end; i++) {
|
|
77
|
+
const t = cssTokens[i];
|
|
69
78
|
switch (t[0]) {
|
|
70
79
|
case CssType.Whitespace:
|
|
71
80
|
case CssType.Comment:
|
|
72
81
|
ws = true;
|
|
73
82
|
continue;
|
|
74
83
|
case CssType.Number:
|
|
75
|
-
pushNumeric(t[1], undefined, t[2]);
|
|
84
|
+
pushNumeric(tokens, t[1], undefined, t[2], ws);
|
|
85
|
+
ws = false;
|
|
76
86
|
continue;
|
|
77
87
|
case CssType.Dimension:
|
|
78
|
-
pushNumeric(t[1], t[4].unit, t[2]);
|
|
88
|
+
pushNumeric(tokens, t[1], t[4].unit, t[2], ws);
|
|
89
|
+
ws = false;
|
|
79
90
|
continue;
|
|
80
91
|
case CssType.Percentage:
|
|
81
|
-
pushNumeric(t[1], '%', t[2]);
|
|
92
|
+
pushNumeric(tokens, t[1], '%', t[2], ws);
|
|
93
|
+
ws = false;
|
|
82
94
|
continue;
|
|
83
95
|
case CssType.Ident:
|
|
84
96
|
tokens.push({ type: 'ident', value: t[4].value, pos: t[2], ws });
|