postcss-calc 11.2.1 → 11.2.3
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/LICENSE +0 -0
- package/README.md +47 -35
- package/package.json +15 -15
- package/src/index.js +2 -1
- package/src/lib/analyze.js +0 -6
- package/src/lib/compile.js +1 -1
- package/src/lib/function-analyzers.js +119 -0
- package/src/lib/functions.js +25 -175
- package/src/lib/node.js +61 -4
- package/src/lib/parser/opaque.js +152 -0
- package/src/lib/parser/tokens.js +242 -0
- package/src/lib/parser.js +32 -377
- package/src/lib/serialize/expression.js +329 -0
- package/src/lib/serialize/precedence.js +47 -0
- package/src/lib/serialize/precision.js +244 -0
- package/src/lib/serialize.js +24 -561
- package/src/lib/simplify/bucket.js +22 -5
- package/src/lib/simplify/cancel.js +35 -11
- package/src/lib/simplify/clamp.js +2 -2
- package/src/lib/simplify/exact.js +18 -0
- package/src/lib/simplify/fold.js +15 -1
- package/src/lib/simplify/min-max.js +2 -2
- package/src/lib/simplify/product-barrier.js +55 -0
- package/src/lib/simplify/product-quotient.js +141 -0
- package/src/lib/simplify/product.js +92 -48
- package/src/lib/simplify/sum.js +6 -2
- package/src/lib/simplify.js +22 -5
- package/src/lib/types.js +94 -0
- package/src/reduce.js +2 -1
- package/types/lib/function-analyzers.d.ts +27 -0
- package/types/lib/functions.d.ts +3 -28
- package/types/lib/node.d.ts +17 -2
- package/types/lib/parser/opaque.d.ts +12 -0
- package/types/lib/parser/tokens.d.ts +83 -0
- package/types/lib/parser.d.ts +4 -44
- package/types/lib/serialize/expression.d.ts +55 -0
- package/types/lib/serialize/precedence.d.ts +18 -0
- package/types/lib/serialize/precision.d.ts +102 -0
- package/types/lib/simplify/bucket.d.ts +4 -1
- package/types/lib/simplify/cancel.d.ts +11 -7
- package/types/lib/simplify/exact.d.ts +9 -0
- package/types/lib/simplify/fold.d.ts +14 -1
- package/types/lib/simplify/product-barrier.d.ts +26 -0
- package/types/lib/simplify/product-quotient.d.ts +48 -0
- package/types/lib/simplify/product.d.ts +3 -7
- package/types/lib/simplify/sum.d.ts +2 -1
- package/types/lib/simplify.d.ts +3 -8
- package/types/lib/types.d.ts +48 -0
- package/types/lib/calculation-type.d.ts +0 -18
package/src/lib/types.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `percent` marks a value that resolves in the same percentage context as its
|
|
3
|
+
* peers (a pure percentage). It is only produced at leaves, by abs(), and by
|
|
4
|
+
* homogeneous sums and calls — never by a product, where an unpaired
|
|
5
|
+
* percentage could no longer cancel against anything.
|
|
6
|
+
* @typedef {{kind: 'number'} | {kind: 'dimension', base: string | null} | {kind: 'unknown', percent?: true} | {kind: 'failure'}} CalculationType
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** @type {CalculationType} */ const numberType = { kind: 'number' };
|
|
10
|
+
/** @type {CalculationType} */ const unknownType = { kind: 'unknown' };
|
|
11
|
+
/** @type {CalculationType} */ const percentageType = {
|
|
12
|
+
kind: 'unknown',
|
|
13
|
+
percent: true,
|
|
14
|
+
};
|
|
15
|
+
/** @type {CalculationType} */ const failureType = { kind: 'failure' };
|
|
16
|
+
|
|
17
|
+
/** @param {CalculationType} type @return {boolean} */
|
|
18
|
+
function isFailure(type) {
|
|
19
|
+
return type.kind === 'failure';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @param {CalculationType} type @return {boolean} */
|
|
23
|
+
function isPercentage(type) {
|
|
24
|
+
return type.kind === 'unknown' && type.percent === true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @param {CalculationType} a @param {CalculationType} b @return {CalculationType} */
|
|
28
|
+
function addTypes(a, b) {
|
|
29
|
+
if (isFailure(a) || isFailure(b)) return failureType;
|
|
30
|
+
if (a.kind === 'unknown' || b.kind === 'unknown') {
|
|
31
|
+
// A pure percentage keeps its contextual type so a surrounding product
|
|
32
|
+
// can cancel `% / %`; mixing it with any other opaque operand loses the
|
|
33
|
+
// guarantee that it resolves in the same context as its peers. A known
|
|
34
|
+
// number + percentage sum is likewise kept unknown and valid: the spec
|
|
35
|
+
// resolves the percentage against its surrounding context, which this
|
|
36
|
+
// coarse type model does not track.
|
|
37
|
+
return isPercentage(a) && isPercentage(b) ? percentageType : unknownType;
|
|
38
|
+
}
|
|
39
|
+
if (a.kind === 'number' && b.kind === 'number') return numberType;
|
|
40
|
+
if (a.kind === 'dimension' && b.kind === 'dimension') {
|
|
41
|
+
if (a.base === null || b.base === null) return unknownType;
|
|
42
|
+
return a.base === b.base ? a : failureType;
|
|
43
|
+
}
|
|
44
|
+
return failureType;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check an all-number function without assuming anything about an unresolved
|
|
49
|
+
* operand. A concrete dimension can never become a number, so it is still a
|
|
50
|
+
* definite error when another argument is opaque.
|
|
51
|
+
* @param {CalculationType[]} args
|
|
52
|
+
* @param {number} min
|
|
53
|
+
* @param {number} max
|
|
54
|
+
* @return {CalculationType}
|
|
55
|
+
*/
|
|
56
|
+
function numberArguments(args, min, max) {
|
|
57
|
+
if (args.length < min || args.length > max) return failureType;
|
|
58
|
+
let hasUnknown = false;
|
|
59
|
+
for (const arg of args) {
|
|
60
|
+
if (arg.kind === 'dimension') return failureType;
|
|
61
|
+
hasUnknown = hasUnknown || arg.kind === 'unknown';
|
|
62
|
+
}
|
|
63
|
+
return hasUnknown ? unknownType : numberType;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check values that must share a calculation type. Unknown operands remain
|
|
68
|
+
* unknown: they might resolve to the concrete type required by their peers.
|
|
69
|
+
* @param {CalculationType[]} args
|
|
70
|
+
* @param {number} min
|
|
71
|
+
* @param {number} max
|
|
72
|
+
* @return {CalculationType}
|
|
73
|
+
*/
|
|
74
|
+
function matchingArguments(args, min, max) {
|
|
75
|
+
if (args.length < min || args.length > max) return failureType;
|
|
76
|
+
let result = args[0];
|
|
77
|
+
for (let index = 1; index < args.length; index++) {
|
|
78
|
+
result = addTypes(result, args[index]);
|
|
79
|
+
if (isFailure(result)) return failureType;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
addTypes,
|
|
86
|
+
failureType,
|
|
87
|
+
isFailure,
|
|
88
|
+
isPercentage,
|
|
89
|
+
matchingArguments,
|
|
90
|
+
numberArguments,
|
|
91
|
+
numberType,
|
|
92
|
+
percentageType,
|
|
93
|
+
unknownType,
|
|
94
|
+
};
|
package/src/reduce.js
CHANGED
|
@@ -61,11 +61,12 @@ function reduceCalc(value, opts) {
|
|
|
61
61
|
|
|
62
62
|
/** @type {ResolvedReduceCalcOptions} */
|
|
63
63
|
const options = {
|
|
64
|
-
precision: 5,
|
|
65
64
|
warnWhenCannotResolve: false,
|
|
66
65
|
unwrapSingleNegativeNumber: false,
|
|
67
66
|
unwrapSingleValue: false,
|
|
68
67
|
...opts,
|
|
68
|
+
// An explicit `precision: undefined` keeps the default.
|
|
69
|
+
precision: opts?.precision ?? 5,
|
|
69
70
|
};
|
|
70
71
|
/** @type {import('@csstools/css-tokenizer').CSSToken[]} */
|
|
71
72
|
let tokens;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type Node = import('./node.js').Node;
|
|
2
|
+
export type CalculationType = import('./types.js').CalculationType;
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./node.js').Node} Node
|
|
5
|
+
* @typedef {import('./types.js').CalculationType} CalculationType
|
|
6
|
+
*/
|
|
7
|
+
/** @param {CalculationType[]} args @return {CalculationType} */
|
|
8
|
+
declare function analyzeTrig(args: CalculationType[]): CalculationType;
|
|
9
|
+
/** @param {CalculationType[]} args @return {CalculationType} */
|
|
10
|
+
declare function analyzeInverseTrig(args: CalculationType[]): CalculationType;
|
|
11
|
+
/** @param {CalculationType[]} args @return {CalculationType} */
|
|
12
|
+
declare function analyzeIdentity(args: CalculationType[]): CalculationType;
|
|
13
|
+
/** @param {CalculationType[]} args @return {CalculationType} */
|
|
14
|
+
declare function analyzeSign(args: CalculationType[]): CalculationType;
|
|
15
|
+
/** @param {CalculationType[]} args @param {Node[]} nodes @return {CalculationType} */
|
|
16
|
+
declare function analyzeRound(args: CalculationType[], nodes: Node[]): CalculationType;
|
|
17
|
+
/** @param {CalculationType[]} args @return {CalculationType} */
|
|
18
|
+
declare function analyzeAtan2(args: CalculationType[]): CalculationType;
|
|
19
|
+
/** @param {CalculationType[]} args @return {CalculationType} */
|
|
20
|
+
declare function analyzeCalc(args: CalculationType[]): CalculationType;
|
|
21
|
+
/** @param {Node} node @return {boolean} */
|
|
22
|
+
declare function isRoundStrategy(node: Node): boolean;
|
|
23
|
+
/** @param {Node} node @param {number} index @return {boolean} */
|
|
24
|
+
declare function isClampKeyword(node: Node, index: number): boolean;
|
|
25
|
+
/** @param {CalculationType[]} args @param {Node[]} nodes @return {CalculationType} */
|
|
26
|
+
declare function analyzeClamp(args: CalculationType[], nodes: Node[]): CalculationType;
|
|
27
|
+
export { analyzeAtan2, analyzeCalc, analyzeClamp, analyzeIdentity, analyzeInverseTrig, analyzeRound, analyzeSign, analyzeTrig, isClampKeyword, isRoundStrategy, };
|
package/types/lib/functions.d.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
|
+
import { addTypes, failureType, isFailure, isPercentage, numberType, percentageType, unknownType } from './types.js';
|
|
1
2
|
export type Node = import('./node.js').Node;
|
|
2
|
-
export type CalculationType =
|
|
3
|
-
kind: 'number';
|
|
4
|
-
} | {
|
|
5
|
-
kind: 'dimension';
|
|
6
|
-
base: string | null;
|
|
7
|
-
} | {
|
|
8
|
-
kind: 'unknown';
|
|
9
|
-
percent?: true;
|
|
10
|
-
} | {
|
|
11
|
-
kind: 'failure';
|
|
12
|
-
};
|
|
3
|
+
export type CalculationType = import('./types.js').CalculationType;
|
|
13
4
|
export type MathSimplifier = (name: string, args: Node[]) => Node;
|
|
14
5
|
export type TypeAnalyzer = (args: CalculationType[], nodes: Node[]) => CalculationType;
|
|
15
6
|
export type MathFunction = {
|
|
@@ -19,26 +10,10 @@ export type MathFunction = {
|
|
|
19
10
|
calculation?: boolean;
|
|
20
11
|
};
|
|
21
12
|
/** @typedef {import('./node.js').Node} Node */
|
|
22
|
-
/**
|
|
23
|
-
* `percent` marks a value that resolves in the same percentage context as its
|
|
24
|
-
* peers (a pure percentage). It is only produced at leaves, by abs(), and by
|
|
25
|
-
* homogeneous sums and calls — never by a product, where an unpaired
|
|
26
|
-
* percentage could no longer cancel against anything.
|
|
27
|
-
* @typedef {{kind: 'number'} | {kind: 'dimension', base: string | null} | {kind: 'unknown', percent?: true} | {kind: 'failure'}} CalculationType
|
|
28
|
-
*/
|
|
13
|
+
/** @typedef {import('./types.js').CalculationType} CalculationType */
|
|
29
14
|
/** @typedef {(name: string, args: Node[]) => Node} MathSimplifier */
|
|
30
15
|
/** @typedef {(args: CalculationType[], nodes: Node[]) => CalculationType} TypeAnalyzer */
|
|
31
16
|
/** @typedef {{analyze: TypeAnalyzer, simplify?: MathSimplifier, isKeyword?: (node: Node, index: number) => boolean, calculation?: boolean}} MathFunction */
|
|
32
|
-
/** @type {CalculationType} */ declare const numberType: CalculationType;
|
|
33
|
-
/** @type {CalculationType} */ declare const unknownType: CalculationType;
|
|
34
|
-
/** @type {CalculationType} */ declare const percentageType: CalculationType;
|
|
35
|
-
/** @type {CalculationType} */ declare const failureType: CalculationType;
|
|
36
|
-
/** @param {CalculationType} type @return {boolean} */
|
|
37
|
-
declare function isFailure(type: CalculationType): boolean;
|
|
38
|
-
/** @param {CalculationType} type @return {boolean} */
|
|
39
|
-
declare function isPercentage(type: CalculationType): boolean;
|
|
40
|
-
/** @param {CalculationType} a @param {CalculationType} b @return {CalculationType} */
|
|
41
|
-
declare function addTypes(a: CalculationType, b: CalculationType): CalculationType;
|
|
42
17
|
declare const mathFunctions: Map<string, MathFunction>;
|
|
43
18
|
/**
|
|
44
19
|
* @param {string} name
|
package/types/lib/node.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export type ProductFactor = {
|
|
|
42
42
|
export type Product = {
|
|
43
43
|
type: 'Product';
|
|
44
44
|
factors: ProductFactor[];
|
|
45
|
+
grouped?: boolean;
|
|
45
46
|
};
|
|
46
47
|
export type Node = Num | Dim | Ident | Call | OpaqueCall | Sum | Product;
|
|
47
48
|
/**
|
|
@@ -54,7 +55,7 @@ export type Node = Num | Dim | Ident | Call | OpaqueCall | Sum | Product;
|
|
|
54
55
|
* @typedef {{sign: 1 | -1, node: Node}} SumTerm Sign is always +1 when node is Num or Dim.
|
|
55
56
|
* @typedef {{type: 'Sum', terms: SumTerm[], grouped?: boolean}} Sum
|
|
56
57
|
* @typedef {{exponent: 1 | -1, node: Node}} ProductFactor exponent +1 = numerator, -1 = denominator.
|
|
57
|
-
* @typedef {{type: 'Product', factors: ProductFactor[]}} Product
|
|
58
|
+
* @typedef {{type: 'Product', factors: ProductFactor[], grouped?: boolean}} Product
|
|
58
59
|
* @typedef {Num | Dim | Ident | Call | OpaqueCall | Sum | Product} Node
|
|
59
60
|
*/
|
|
60
61
|
/**
|
|
@@ -99,10 +100,24 @@ declare function mkSum(rawTerms: SumTerm[]): Node;
|
|
|
99
100
|
* @return {Node}
|
|
100
101
|
*/
|
|
101
102
|
declare function mkProduct(rawFactors: ProductFactor[]): Node;
|
|
103
|
+
/**
|
|
104
|
+
* Mark a node as a source parenthesized group. A Sum or Product keeps its
|
|
105
|
+
* structure; any other node becomes a single-factor grouped Product.
|
|
106
|
+
* @param {Node} node
|
|
107
|
+
* @return {Node}
|
|
108
|
+
*/
|
|
109
|
+
declare function mkGroup(node: Node): Node;
|
|
110
|
+
/**
|
|
111
|
+
* Group a parenthesized expression when its tokens must stay bound: a sum, or
|
|
112
|
+
* a product around a substitution function.
|
|
113
|
+
* @param {Node} node
|
|
114
|
+
* @return {Node}
|
|
115
|
+
*/
|
|
116
|
+
declare function groupSubstitution(node: Node): Node;
|
|
102
117
|
/**
|
|
103
118
|
* Negate any node, preserving canonical form.
|
|
104
119
|
* @param {Node} node
|
|
105
120
|
* @return {Node}
|
|
106
121
|
*/
|
|
107
122
|
declare function negate(node: Node): Node;
|
|
108
|
-
export { num, dim, ident, call, opaqueCall, mkSum, mkProduct, negate };
|
|
123
|
+
export { num, dim, ident, call, opaqueCall, mkSum, mkProduct, mkGroup, groupSubstitution, negate, };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type CSSToken = import('@csstools/css-tokenizer').CSSToken;
|
|
2
|
+
export type Node = import('../node.js').Node;
|
|
3
|
+
export type OpaqueComponent = import('../node.js').OpaqueComponent;
|
|
4
|
+
export type ParseInput = import('./tokens.js').ParseInput;
|
|
5
|
+
export type Cursor = import('./tokens.js').Cursor;
|
|
6
|
+
export type FunctionToken = import('./tokens.js').FunctionToken;
|
|
7
|
+
export type ParseRange = (input: ParseInput, start: number, end: number) => Node;
|
|
8
|
+
/** @param {ParseInput} input @param {Cursor} cursor @param {FunctionToken} token @param {string} name @param {string} rawName @param {ParseRange} parseRange @return {Node} */
|
|
9
|
+
declare function parseOpaqueCall(input: ParseInput, cursor: Cursor, token: FunctionToken, name: string, rawName: string, parseRange: ParseRange): Node;
|
|
10
|
+
/** @param {ParseInput} input @param {Cursor} cursor @param {FunctionToken} token @param {string} name @param {string} rawName @param {ParseRange} parseRange @return {Node} */
|
|
11
|
+
declare function parseVar(input: ParseInput, cursor: Cursor, token: FunctionToken, name: string, rawName: string, parseRange: ParseRange): Node;
|
|
12
|
+
export { parseOpaqueCall, parseVar };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export type CSSToken = import('@csstools/css-tokenizer').CSSToken;
|
|
2
|
+
export type BlockIndex = ReturnType<typeof import('../block-index.js').indexBlocks>;
|
|
3
|
+
export type TokenBase = {
|
|
4
|
+
raw: string;
|
|
5
|
+
pos: number;
|
|
6
|
+
ws: boolean;
|
|
7
|
+
index: number;
|
|
8
|
+
};
|
|
9
|
+
export type NumberToken = TokenBase & {
|
|
10
|
+
type: 'number';
|
|
11
|
+
value: number;
|
|
12
|
+
signCharacter?: '+' | '-';
|
|
13
|
+
};
|
|
14
|
+
export type DimensionToken = TokenBase & {
|
|
15
|
+
type: 'dimension';
|
|
16
|
+
value: number;
|
|
17
|
+
unit: string;
|
|
18
|
+
rawUnit: string;
|
|
19
|
+
signCharacter?: '+' | '-';
|
|
20
|
+
};
|
|
21
|
+
export type IdentToken = TokenBase & {
|
|
22
|
+
type: 'ident';
|
|
23
|
+
value: string;
|
|
24
|
+
};
|
|
25
|
+
export type FunctionToken = TokenBase & {
|
|
26
|
+
type: 'function';
|
|
27
|
+
value: string;
|
|
28
|
+
};
|
|
29
|
+
export type Punctuator = '(' | ')' | ',' | '+' | '-' | '*' | '/';
|
|
30
|
+
export type PunctToken = TokenBase & {
|
|
31
|
+
type: 'punct';
|
|
32
|
+
value: Punctuator;
|
|
33
|
+
};
|
|
34
|
+
export type EofToken = TokenBase & {
|
|
35
|
+
type: 'eof';
|
|
36
|
+
value: '';
|
|
37
|
+
raw: '';
|
|
38
|
+
};
|
|
39
|
+
export type Token = NumberToken | DimensionToken | IdentToken | FunctionToken | PunctToken | EofToken;
|
|
40
|
+
export type ParseInput = Readonly<{
|
|
41
|
+
tokens: CSSToken[];
|
|
42
|
+
end: number;
|
|
43
|
+
index: BlockIndex;
|
|
44
|
+
}>;
|
|
45
|
+
/** @param {string} raw @param {string} decoded */
|
|
46
|
+
declare function sourceSpelling(raw: string, decoded: string): string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Mutable navigation state only. `index` is always the next native token
|
|
49
|
+
* position; trivia is intentionally left visible to `scanToken`.
|
|
50
|
+
*/
|
|
51
|
+
declare class Cursor {
|
|
52
|
+
/** @type {number} */
|
|
53
|
+
index: number;
|
|
54
|
+
/** @type {boolean} */
|
|
55
|
+
firstToken: boolean;
|
|
56
|
+
/** @type {Token | null} */
|
|
57
|
+
lookahead: Token | null;
|
|
58
|
+
/** @type {number} */
|
|
59
|
+
lookaheadNextIndex: number;
|
|
60
|
+
/** @param {number} start */
|
|
61
|
+
constructor(start: number);
|
|
62
|
+
/** @param {number} index @return {void} */
|
|
63
|
+
skipTo(index: number): void;
|
|
64
|
+
}
|
|
65
|
+
/** @param {ParseInput} input @param {number} index @return {number} */
|
|
66
|
+
declare function eofPositionAt(input: ParseInput, index: number): number;
|
|
67
|
+
/** @param {ParseInput} input @param {Cursor} cursor @return {Token} */
|
|
68
|
+
declare function peekToken(input: ParseInput, cursor: Cursor): Token;
|
|
69
|
+
/**
|
|
70
|
+
* Consume the cached token and advance to its native next index. This is one
|
|
71
|
+
* of the only two operations allowed to advance `cursor.index`.
|
|
72
|
+
* @param {ParseInput} input
|
|
73
|
+
* @param {Cursor} cursor
|
|
74
|
+
* @return {Token}
|
|
75
|
+
*/
|
|
76
|
+
declare function takeToken(input: ParseInput, cursor: Cursor): Token;
|
|
77
|
+
/** @param {ParseInput} input @param {Cursor} cursor @param {Punctuator} value @param {Punctuator} [value2] @return {boolean} */
|
|
78
|
+
declare function isPunct(input: ParseInput, cursor: Cursor, value: Punctuator, value2?: Punctuator): boolean;
|
|
79
|
+
/** @param {ParseInput} input @param {Cursor} cursor @param {Punctuator} value @return {boolean} */
|
|
80
|
+
declare function matchPunct(input: ParseInput, cursor: Cursor, value: Punctuator): boolean;
|
|
81
|
+
/** @param {ParseInput} input @param {Cursor} cursor @param {Punctuator} value @return {PunctToken} */
|
|
82
|
+
declare function expectPunct(input: ParseInput, cursor: Cursor, value: Punctuator): PunctToken;
|
|
83
|
+
export { Cursor, eofPositionAt, expectPunct, isPunct, matchPunct, peekToken, sourceSpelling, takeToken, };
|
package/types/lib/parser.d.ts
CHANGED
|
@@ -1,49 +1,9 @@
|
|
|
1
1
|
export type CSSToken = import('@csstools/css-tokenizer').CSSToken;
|
|
2
2
|
export type Node = import('./node.js').Node;
|
|
3
|
-
export type
|
|
4
|
-
export type
|
|
5
|
-
export type
|
|
6
|
-
|
|
7
|
-
pos: number;
|
|
8
|
-
ws: boolean;
|
|
9
|
-
index: number;
|
|
10
|
-
};
|
|
11
|
-
export type NumberToken = TokenBase & {
|
|
12
|
-
type: 'number';
|
|
13
|
-
value: number;
|
|
14
|
-
signCharacter?: '+' | '-';
|
|
15
|
-
};
|
|
16
|
-
export type DimensionToken = TokenBase & {
|
|
17
|
-
type: 'dimension';
|
|
18
|
-
value: number;
|
|
19
|
-
unit: string;
|
|
20
|
-
rawUnit: string;
|
|
21
|
-
signCharacter?: '+' | '-';
|
|
22
|
-
};
|
|
23
|
-
export type IdentToken = TokenBase & {
|
|
24
|
-
type: 'ident';
|
|
25
|
-
value: string;
|
|
26
|
-
};
|
|
27
|
-
export type FunctionToken = TokenBase & {
|
|
28
|
-
type: 'function';
|
|
29
|
-
value: string;
|
|
30
|
-
};
|
|
31
|
-
export type Punctuator = '(' | ')' | ',' | '+' | '-' | '*' | '/';
|
|
32
|
-
export type PunctToken = TokenBase & {
|
|
33
|
-
type: 'punct';
|
|
34
|
-
value: Punctuator;
|
|
35
|
-
};
|
|
36
|
-
export type EofToken = TokenBase & {
|
|
37
|
-
type: 'eof';
|
|
38
|
-
value: '';
|
|
39
|
-
raw: '';
|
|
40
|
-
};
|
|
41
|
-
export type Token = NumberToken | DimensionToken | IdentToken | FunctionToken | PunctToken | EofToken;
|
|
42
|
-
export type ParseInput = Readonly<{
|
|
43
|
-
tokens: CSSToken[];
|
|
44
|
-
end: number;
|
|
45
|
-
index: BlockIndex;
|
|
46
|
-
}>;
|
|
3
|
+
export type BlockIndex = import('./parser/tokens.js').BlockIndex;
|
|
4
|
+
export type ParseInput = import('./parser/tokens.js').ParseInput;
|
|
5
|
+
export type Token = import('./parser/tokens.js').Token;
|
|
6
|
+
export type FunctionToken = import('./parser/tokens.js').FunctionToken;
|
|
47
7
|
/** @param {CSSToken[]} tokens @param {number} start @param {number} end @param {BlockIndex} index @return {Node} */
|
|
48
8
|
declare function parse(tokens: CSSToken[], start: number, end: number, index: BlockIndex): Node;
|
|
49
9
|
export { parse };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type Node = import('../node.js').Node;
|
|
2
|
+
export type Sum = import('../node.js').Sum;
|
|
3
|
+
export type SumTerm = import('../node.js').SumTerm;
|
|
4
|
+
export type ProductFactor = import('../node.js').ProductFactor;
|
|
5
|
+
export type SerializeSession = import('./precision.js').SerializeSession;
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('../node.js').Node} Node
|
|
8
|
+
* @typedef {import('../node.js').Sum} Sum
|
|
9
|
+
* @typedef {import('../node.js').SumTerm} SumTerm
|
|
10
|
+
* @typedef {import('../node.js').ProductFactor} ProductFactor
|
|
11
|
+
* @typedef {import('./precision.js').SerializeSession} SerializeSession
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @param {Node} node
|
|
15
|
+
* @param {SerializeSession} session
|
|
16
|
+
* @param {number} [parentPrecedence]
|
|
17
|
+
* @param {boolean} [groupedRequired]
|
|
18
|
+
* @return {void}
|
|
19
|
+
*/
|
|
20
|
+
declare function emitNode(node: Node, session: SerializeSession, parentPrecedence?: number, groupedRequired?: boolean): void;
|
|
21
|
+
/**
|
|
22
|
+
* @param {Node} node
|
|
23
|
+
* @param {SerializeSession} session
|
|
24
|
+
* @return {void}
|
|
25
|
+
*/
|
|
26
|
+
declare function emitLeadingNeg(node: Node, session: SerializeSession): void;
|
|
27
|
+
/**
|
|
28
|
+
* @param {import('../node.js').Call} node
|
|
29
|
+
* @param {SerializeSession} session
|
|
30
|
+
* @param {string} [callNameOverride]
|
|
31
|
+
* @return {void}
|
|
32
|
+
*/
|
|
33
|
+
declare function emitCall(node: import('../node.js').Call, session: SerializeSession, callNameOverride?: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* @param {import('../node.js').OpaqueCall} node
|
|
36
|
+
* @param {SerializeSession} session
|
|
37
|
+
* @param {string} [callNameOverride]
|
|
38
|
+
* @return {void}
|
|
39
|
+
*/
|
|
40
|
+
declare function emitOpaqueCall(node: import('../node.js').OpaqueCall, session: SerializeSession, callNameOverride?: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* @param {Node} node
|
|
43
|
+
* @param {SerializeSession} session
|
|
44
|
+
* @param {string} wrapper
|
|
45
|
+
* @return {void}
|
|
46
|
+
*/
|
|
47
|
+
declare function emitMathResult(node: Node, session: SerializeSession, wrapper: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* @param {Node} node
|
|
50
|
+
* @param {SerializeSession} session
|
|
51
|
+
* @param {string[]} [buffer]
|
|
52
|
+
* @return {void}
|
|
53
|
+
*/
|
|
54
|
+
declare function emitNestedMathResult(node: Node, session: SerializeSession, buffer?: string[]): void;
|
|
55
|
+
export { emitNode, emitLeadingNeg, emitCall, emitOpaqueCall, emitMathResult, emitNestedMathResult, };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type Node = import('../node.js').Node;
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('../node.js').Node} Node
|
|
4
|
+
*/
|
|
5
|
+
declare const SUM_PRECEDENCE = 1;
|
|
6
|
+
declare const PRODUCT_PRECEDENCE = 2;
|
|
7
|
+
declare const ATOMIC_PRECEDENCE = 3;
|
|
8
|
+
declare const UNARY_PRECEDENCE = 3;
|
|
9
|
+
/** @param {Node} node @return {number} */
|
|
10
|
+
declare function precedence(node: Node): number;
|
|
11
|
+
/**
|
|
12
|
+
* @param {Node} node
|
|
13
|
+
* @param {number} parentPrecedence
|
|
14
|
+
* @param {boolean} groupedRequired
|
|
15
|
+
* @return {boolean}
|
|
16
|
+
*/
|
|
17
|
+
declare function needsParentheses(node: Node, parentPrecedence: number, groupedRequired: boolean): boolean;
|
|
18
|
+
export { SUM_PRECEDENCE, PRODUCT_PRECEDENCE, ATOMIC_PRECEDENCE, UNARY_PRECEDENCE, precedence, needsParentheses, };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export type Node = import('../node.js').Node;
|
|
2
|
+
export type Num = import('../node.js').Num;
|
|
3
|
+
export type Dim = import('../node.js').Dim;
|
|
4
|
+
export type SerializeSession = {
|
|
5
|
+
buffer: string[];
|
|
6
|
+
precision: number | false;
|
|
7
|
+
scalarPolicy: 'standard' | 'unwrap-all';
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {import('../node.js').Node} Node
|
|
11
|
+
* @typedef {import('../node.js').Num} Num
|
|
12
|
+
* @typedef {import('../node.js').Dim} Dim
|
|
13
|
+
* @typedef {object} SerializeSession
|
|
14
|
+
* @property {string[]} buffer
|
|
15
|
+
* @property {number | false} precision
|
|
16
|
+
* @property {'standard' | 'unwrap-all'} scalarPolicy
|
|
17
|
+
*/
|
|
18
|
+
declare const NOISE_FLOOR = 1e-12;
|
|
19
|
+
/**
|
|
20
|
+
* Divide a decimal digit string by 10^k, rounding half away from zero, and
|
|
21
|
+
* return the resulting integer digit string. `digits` has no leading zeros.
|
|
22
|
+
* @param {string} digits
|
|
23
|
+
* @param {number} k
|
|
24
|
+
* @return {string}
|
|
25
|
+
*/
|
|
26
|
+
declare function divideByPowerOfTen(digits: string, k: number): string;
|
|
27
|
+
/**
|
|
28
|
+
* Round the shortest decimal representation of a non-negative double to `p`
|
|
29
|
+
* fractional digits, half away from zero.
|
|
30
|
+
*
|
|
31
|
+
* `Number(text + 'e' + p)` reads the exact intended decimal (so `1.005` at
|
|
32
|
+
* precision 2 becomes `1.01`), but it is only exact while the shifted value
|
|
33
|
+
* fits in `Number.MAX_SAFE_INTEGER`; beyond that the intermediate double
|
|
34
|
+
* rounds and can move the rounding boundary (e.g. `312834450754803.44` at
|
|
35
|
+
* precision 1 or 6 drifted to `312834450754803.5`). Round the decimal digits
|
|
36
|
+
* directly instead.
|
|
37
|
+
*
|
|
38
|
+
* @param {number} abs
|
|
39
|
+
* @param {number} p
|
|
40
|
+
* @return {number}
|
|
41
|
+
*/
|
|
42
|
+
declare function roundDecimal(abs: number, p: number): number;
|
|
43
|
+
/**
|
|
44
|
+
* @param {number} v
|
|
45
|
+
* @param {number | false} prec
|
|
46
|
+
* @return {number}
|
|
47
|
+
*/
|
|
48
|
+
declare function round(v: number, prec: number | false): number;
|
|
49
|
+
/** @param {number} v @return {boolean} */
|
|
50
|
+
declare function isDegenerate(v: number): boolean;
|
|
51
|
+
/** @param {number} v @return {string} */
|
|
52
|
+
declare function degenerateKeyword(v: number): string;
|
|
53
|
+
/** @param {number} v @return {string} */
|
|
54
|
+
declare function serializeNumber(v: number): string;
|
|
55
|
+
/**
|
|
56
|
+
* @param {import('../node.js').Num | import('../node.js').Dim} node
|
|
57
|
+
* @param {number | false} precision
|
|
58
|
+
* @param {number} [value]
|
|
59
|
+
* @return {number}
|
|
60
|
+
*/
|
|
61
|
+
declare function roundedScalarValue(node: import('../node.js').Num | import('../node.js').Dim, precision: number | false, value?: number): number;
|
|
62
|
+
/**
|
|
63
|
+
* @param {import('../node.js').Num | import('../node.js').Dim} node
|
|
64
|
+
* @param {string[]} buffer
|
|
65
|
+
* @param {number} value
|
|
66
|
+
* @return {void}
|
|
67
|
+
*/
|
|
68
|
+
declare function emitRoundedScalar(node: import('../node.js').Num | import('../node.js').Dim, buffer: string[], value: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* @param {import('../node.js').Num | import('../node.js').Dim} node
|
|
71
|
+
* @param {SerializeSession} session
|
|
72
|
+
* @param {number} [value]
|
|
73
|
+
* @return {number}
|
|
74
|
+
*/
|
|
75
|
+
declare function emitFiniteScalar(node: import('../node.js').Num | import('../node.js').Dim, session: SerializeSession, value?: number): number;
|
|
76
|
+
/**
|
|
77
|
+
* @param {import('../node.js').Num | import('../node.js').Dim} node
|
|
78
|
+
* @param {SerializeSession} session
|
|
79
|
+
* @param {number} [value]
|
|
80
|
+
* @param {boolean} [bare] emit negative zero without its calc() wrapper
|
|
81
|
+
* @return {void}
|
|
82
|
+
*/
|
|
83
|
+
declare function emitScalar(node: import('../node.js').Num | import('../node.js').Dim, session: SerializeSession, value?: number, bare?: boolean): void;
|
|
84
|
+
/**
|
|
85
|
+
* @param {string[]} buffer
|
|
86
|
+
* @param {import('../node.js').Num | import('../node.js').Dim} node
|
|
87
|
+
* @return {void}
|
|
88
|
+
*/
|
|
89
|
+
declare function emitSignedZero(buffer: string[], node: import('../node.js').Num | import('../node.js').Dim): void;
|
|
90
|
+
/**
|
|
91
|
+
* Negative zero as an unwrapped product, valid only where a `<calc-product>`
|
|
92
|
+
* may appear without changing how the surrounding expression parses.
|
|
93
|
+
* @param {string[]} buffer
|
|
94
|
+
* @param {import('../node.js').Num | import('../node.js').Dim} node
|
|
95
|
+
* @return {void}
|
|
96
|
+
*/
|
|
97
|
+
declare function emitBareSignedZero(buffer: string[], node: import('../node.js').Num | import('../node.js').Dim): void;
|
|
98
|
+
/** @param {Node} node @return {node is import('../node.js').Num | import('../node.js').Dim} */
|
|
99
|
+
declare function isScalar(node: Node): node is import('../node.js').Num | import('../node.js').Dim;
|
|
100
|
+
/** @param {Node} node @return {node is import('../node.js').Num | import('../node.js').Dim} */
|
|
101
|
+
declare function isSignedZero(node: Node): node is import('../node.js').Num | import('../node.js').Dim;
|
|
102
|
+
export { NOISE_FLOOR, divideByPowerOfTen, roundDecimal, round, isDegenerate, degenerateKeyword, serializeNumber, roundedScalarValue, emitRoundedScalar, emitFiniteScalar, emitScalar, emitSignedZero, emitBareSignedZero, isScalar, isSignedZero, };
|
|
@@ -19,7 +19,10 @@ export type UnitBucket = {
|
|
|
19
19
|
/** Mutates `buckets` in place — totals of survivor buckets accumulate the
|
|
20
20
|
* converted values of merged neighbors. Caller must not reuse the input.
|
|
21
21
|
* @param {UnitBucket[]} buckets
|
|
22
|
+
* @param {number | false} [precision] A conversion that is not exact at this
|
|
23
|
+
* precision is not merged. When only the reverse direction is exact, the
|
|
24
|
+
* survivor switches to the other bucket's unit.
|
|
22
25
|
* @return {UnitBucket[]}
|
|
23
26
|
*/
|
|
24
|
-
declare function mergeConvertibleBuckets(buckets: UnitBucket[]): UnitBucket[];
|
|
27
|
+
declare function mergeConvertibleBuckets(buckets: UnitBucket[], precision?: number | false): UnitBucket[];
|
|
25
28
|
export { mergeConvertibleBuckets };
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* If `dims` contain exactly one numerator / one denominator pair with the
|
|
3
|
-
* same base type and convertible units, return the
|
|
4
|
-
*
|
|
5
|
-
* Otherwise return null. Used by `simplifyProduct` for typed
|
|
6
|
-
* (§10.2). More complex cancellation (e.g. `px^2 / px`) is left
|
|
3
|
+
* same base type and convertible units, return the two sides expressed in a
|
|
4
|
+
* common unit, so the caller can fold them into its own numerator and
|
|
5
|
+
* denominator. Otherwise return null. Used by `simplifyProduct` for typed
|
|
6
|
+
* division (§10.2). More complex cancellation (e.g. `px^2 / px`) is left
|
|
7
7
|
* unreduced — consumers rarely rely on it and the spec doesn't require it.
|
|
8
8
|
* @template {{ exponent: 1 | -1, value: number, unit: string }} D
|
|
9
9
|
* @param {D[]} dims
|
|
10
|
-
* @
|
|
10
|
+
* @param {number | false} [precision] The denominator is converted into the
|
|
11
|
+
* numerator's unit when that is exact at this precision (`1px / 1in` →
|
|
12
|
+
* `1 / 96`); otherwise the numerator is converted (`1in / 1px` → `96 / 1`).
|
|
13
|
+
* @return {{ numerator: number, denominator: number, remaining: D[] } | null}
|
|
11
14
|
*/
|
|
12
15
|
declare function tryCancelPair<D extends {
|
|
13
16
|
exponent: 1 | -1;
|
|
14
17
|
value: number;
|
|
15
18
|
unit: string;
|
|
16
|
-
}>(dims: D[]): {
|
|
17
|
-
|
|
19
|
+
}>(dims: D[], precision?: number | false): {
|
|
20
|
+
numerator: number;
|
|
21
|
+
denominator: number;
|
|
18
22
|
remaining: D[];
|
|
19
23
|
} | null;
|
|
20
24
|
export { tryCancelPair };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A value is exact at `precision` when rounding it for output loses nothing
|
|
3
|
+
* beyond float noise.
|
|
4
|
+
* @param {number} value
|
|
5
|
+
* @param {number | false} precision
|
|
6
|
+
* @return {boolean}
|
|
7
|
+
*/
|
|
8
|
+
declare function isExact(value: number, precision: number | false): boolean;
|
|
9
|
+
export { isExact };
|
|
@@ -15,6 +15,19 @@ export type BaseType = import('../convertUnits.js').BaseType;
|
|
|
15
15
|
declare function foldResult(fold: {
|
|
16
16
|
unit: string;
|
|
17
17
|
}, value: number): Num | Dim;
|
|
18
|
+
/**
|
|
19
|
+
* Return the argument that produced a folded min/max/clamp value, so the
|
|
20
|
+
* result keeps its own unit, spelling and zero sign. Falls back to a fresh
|
|
21
|
+
* node when no argument matches (NaN).
|
|
22
|
+
* @param {Node[]} args
|
|
23
|
+
* @param {{ values: number[], unit: string }} fold
|
|
24
|
+
* @param {number} value
|
|
25
|
+
* @return {Node}
|
|
26
|
+
*/
|
|
27
|
+
declare function chosenArg(args: Node[], fold: {
|
|
28
|
+
values: number[];
|
|
29
|
+
unit: string;
|
|
30
|
+
}, value: number): Node;
|
|
18
31
|
/**
|
|
19
32
|
* @param {Node[]} args
|
|
20
33
|
* @return {{ values: number[], unit: string } | null}
|
|
@@ -23,4 +36,4 @@ declare function foldConstArgs(args: Node[]): {
|
|
|
23
36
|
values: number[];
|
|
24
37
|
unit: string;
|
|
25
38
|
} | null;
|
|
26
|
-
export { foldConstArgs, foldResult };
|
|
39
|
+
export { foldConstArgs, foldResult, chosenArg };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type Node = import('../node.js').Node;
|
|
2
|
+
export type ProductFactor = import('../node.js').ProductFactor;
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('../node.js').Node} Node
|
|
5
|
+
* @typedef {import('../node.js').ProductFactor} ProductFactor
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A substitution function (`var()`, `env()`, `attr()`, ...) is replaced by
|
|
9
|
+
* tokens, not by a value, so factors cannot move or cancel across it. Any
|
|
10
|
+
* function the parser does not recognise (`anchor()`, `foo()`, ...) is also an
|
|
11
|
+
* OpaqueCall and is treated the same, conservatively. A grouped Product is a
|
|
12
|
+
* parenthesized substitution and is equally opaque.
|
|
13
|
+
* @param {Node} node
|
|
14
|
+
* @return {boolean}
|
|
15
|
+
*/
|
|
16
|
+
declare function isBarrier(node: Node): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Flatten a simplified factor through nested ungrouped Products and the
|
|
19
|
+
* canonical negation form, so barriers are visible at the top level.
|
|
20
|
+
* @param {ProductFactor[]} out
|
|
21
|
+
* @param {1 | -1} exponent
|
|
22
|
+
* @param {Node} node
|
|
23
|
+
* @return {boolean} Whether a barrier was pushed.
|
|
24
|
+
*/
|
|
25
|
+
declare function flattenFactor(out: ProductFactor[], exponent: 1 | -1, node: Node): boolean;
|
|
26
|
+
export { flattenFactor, isBarrier };
|