postcss-calc 11.2.0 → 11.2.2
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 +44 -16
- package/package.json +13 -13
- package/src/index.js +2 -1
- package/src/lib/analyze.js +81 -43
- package/src/lib/compile.js +1 -1
- package/src/lib/functions.js +40 -10
- package/src/lib/node.js +62 -5
- package/src/lib/parser/opaque.js +151 -0
- package/src/lib/parser/tokens.js +242 -0
- package/src/lib/parser.js +27 -382
- package/src/lib/serialize/expression.js +376 -0
- package/src/lib/serialize/precision.js +244 -0
- package/src/lib/serialize.js +24 -517
- 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.js +263 -48
- package/src/lib/simplify/sum.js +6 -2
- package/src/lib/simplify.js +22 -5
- package/src/reduce.js +2 -1
- package/types/lib/analyze.d.ts +27 -0
- package/types/lib/functions.d.ts +19 -1
- 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 +95 -0
- package/types/lib/serialize/precision.d.ts +101 -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.d.ts +3 -7
- package/types/lib/simplify/sum.d.ts +2 -1
- package/types/lib/simplify.d.ts +3 -8
- package/src/lib/calculation-type.js +0 -25
|
@@ -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,95 @@
|
|
|
1
|
+
export type Node = import('../node.js').Node;
|
|
2
|
+
export type Sum = import('../node.js').Sum;
|
|
3
|
+
export type Product = import('../node.js').Product;
|
|
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').Product} Product
|
|
10
|
+
* @typedef {import('../node.js').ProductFactor} ProductFactor
|
|
11
|
+
* @typedef {import('./precision.js').SerializeSession} SerializeSession
|
|
12
|
+
*/
|
|
13
|
+
declare const SUM_PRECEDENCE = 1;
|
|
14
|
+
declare const PRODUCT_PRECEDENCE = 2;
|
|
15
|
+
declare const ATOMIC_PRECEDENCE = 3;
|
|
16
|
+
declare const UNARY_PRECEDENCE = 3;
|
|
17
|
+
/** @param {Node} node @return {number} */
|
|
18
|
+
declare function precedence(node: Node): number;
|
|
19
|
+
/**
|
|
20
|
+
* @param {Node} node
|
|
21
|
+
* @param {number} parentPrecedence
|
|
22
|
+
* @param {boolean} groupedRequired
|
|
23
|
+
* @return {boolean}
|
|
24
|
+
*/
|
|
25
|
+
declare function needsParentheses(node: Node, parentPrecedence: number, groupedRequired: boolean): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* @param {Node} node
|
|
28
|
+
* @param {SerializeSession} session
|
|
29
|
+
* @param {number} [parentPrecedence]
|
|
30
|
+
* @param {boolean} [groupedRequired]
|
|
31
|
+
* @return {void}
|
|
32
|
+
*/
|
|
33
|
+
declare function emitNode(node: Node, session: SerializeSession, parentPrecedence?: number, groupedRequired?: boolean): void;
|
|
34
|
+
/**
|
|
35
|
+
* @param {import('../node.js').Call} node
|
|
36
|
+
* @param {SerializeSession} session
|
|
37
|
+
* @param {string} [callNameOverride]
|
|
38
|
+
* @return {void}
|
|
39
|
+
*/
|
|
40
|
+
declare function emitCall(node: import('../node.js').Call, session: SerializeSession, callNameOverride?: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* @param {import('../node.js').OpaqueCall} node
|
|
43
|
+
* @param {SerializeSession} session
|
|
44
|
+
* @param {string} [callNameOverride]
|
|
45
|
+
* @return {void}
|
|
46
|
+
*/
|
|
47
|
+
declare function emitOpaqueCall(node: import('../node.js').OpaqueCall, session: SerializeSession, callNameOverride?: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* @param {import('../node.js').SumTerm} term
|
|
50
|
+
* @param {1 | -1} multiplier
|
|
51
|
+
* @param {number | false} precision
|
|
52
|
+
* @return {1 | -1}
|
|
53
|
+
*/
|
|
54
|
+
declare function termSign(term: import('../node.js').SumTerm, multiplier: 1 | -1, precision: number | false): 1 | -1;
|
|
55
|
+
/**
|
|
56
|
+
* @param {import('../node.js').SumTerm[]} terms
|
|
57
|
+
* @param {SerializeSession} session
|
|
58
|
+
* @param {1 | -1} [multiplier]
|
|
59
|
+
* @return {void}
|
|
60
|
+
*/
|
|
61
|
+
declare function emitSumTerms(terms: import('../node.js').SumTerm[], session: SerializeSession, multiplier?: 1 | -1): void;
|
|
62
|
+
/** @param {Sum} sum @param {SerializeSession} session @return {void} */
|
|
63
|
+
declare function emitSum(sum: Sum, session: SerializeSession): void;
|
|
64
|
+
/**
|
|
65
|
+
* @param {Node} node
|
|
66
|
+
* @param {SerializeSession} session
|
|
67
|
+
* @return {void}
|
|
68
|
+
*/
|
|
69
|
+
declare function emitLeadingNeg(node: Node, session: SerializeSession): void;
|
|
70
|
+
/**
|
|
71
|
+
* @param {ProductFactor[]} factors
|
|
72
|
+
* @param {SerializeSession} session
|
|
73
|
+
* @param {number} [start]
|
|
74
|
+
* @param {number} [coefficientValue]
|
|
75
|
+
* @param {import('../node.js').Num} [coefficientNode]
|
|
76
|
+
* @return {void}
|
|
77
|
+
*/
|
|
78
|
+
declare function emitProductFactors(factors: ProductFactor[], session: SerializeSession, start?: number, coefficientValue?: number, coefficientNode?: import('../node.js').Num): void;
|
|
79
|
+
/** @param {Product} product @param {SerializeSession} session @return {void} */
|
|
80
|
+
declare function emitProduct(product: Product, session: SerializeSession): void;
|
|
81
|
+
/**
|
|
82
|
+
* @param {Node} node
|
|
83
|
+
* @param {SerializeSession} session
|
|
84
|
+
* @param {string} wrapper
|
|
85
|
+
* @return {void}
|
|
86
|
+
*/
|
|
87
|
+
declare function emitMathResult(node: Node, session: SerializeSession, wrapper: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* @param {Node} node
|
|
90
|
+
* @param {SerializeSession} session
|
|
91
|
+
* @param {string[]} [buffer]
|
|
92
|
+
* @return {void}
|
|
93
|
+
*/
|
|
94
|
+
declare function emitNestedMathResult(node: Node, session: SerializeSession, buffer?: string[]): void;
|
|
95
|
+
export { SUM_PRECEDENCE, PRODUCT_PRECEDENCE, ATOMIC_PRECEDENCE, UNARY_PRECEDENCE, precedence, needsParentheses, emitNode, emitCall, emitOpaqueCall, termSign, emitSumTerms, emitSum, emitLeadingNeg, emitProductFactors, emitProduct, emitMathResult, emitNestedMathResult, };
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
* @return {void}
|
|
81
|
+
*/
|
|
82
|
+
declare function emitScalar(node: import('../node.js').Num | import('../node.js').Dim, session: SerializeSession, value?: number): void;
|
|
83
|
+
/**
|
|
84
|
+
* @param {string[]} buffer
|
|
85
|
+
* @param {import('../node.js').Num | import('../node.js').Dim} node
|
|
86
|
+
* @return {void}
|
|
87
|
+
*/
|
|
88
|
+
declare function emitSignedZero(buffer: string[], node: import('../node.js').Num | import('../node.js').Dim): void;
|
|
89
|
+
/** @param {Node} node @return {node is import('../node.js').Num | import('../node.js').Dim} */
|
|
90
|
+
declare function isScalar(node: Node): node is import('../node.js').Num | import('../node.js').Dim;
|
|
91
|
+
/** @param {Node} node @return {node is import('../node.js').Num | import('../node.js').Dim} */
|
|
92
|
+
declare function isSignedZero(node: Node): node is import('../node.js').Num | import('../node.js').Dim;
|
|
93
|
+
/**
|
|
94
|
+
* Whether a scalar node is strictly negative after precision rounding
|
|
95
|
+
* (excluding signed zero and sub-precision values that round to zero).
|
|
96
|
+
* @param {Node} node
|
|
97
|
+
* @param {number | false} precision
|
|
98
|
+
* @return {node is import('../node.js').Num | import('../node.js').Dim}
|
|
99
|
+
*/
|
|
100
|
+
declare function isEffectivelyNegative(node: Node, precision: number | false): node is import('../node.js').Num | import('../node.js').Dim;
|
|
101
|
+
export { NOISE_FLOOR, divideByPowerOfTen, roundDecimal, round, isDegenerate, degenerateKeyword, serializeNumber, roundedScalarValue, emitRoundedScalar, emitFiniteScalar, emitScalar, emitSignedZero, isScalar, isSignedZero, isEffectivelyNegative, };
|
|
@@ -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 };
|
|
@@ -2,16 +2,12 @@ export type Node = import('../node.js').Node;
|
|
|
2
2
|
export type Product = import('../node.js').Product;
|
|
3
3
|
export type ProductFactor = import('../node.js').ProductFactor;
|
|
4
4
|
export type SimplifyFn = import('../simplify.js').SimplifyFn;
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {import('../node.js').Node} Node
|
|
7
|
-
* @typedef {import('../node.js').Product} Product
|
|
8
|
-
* @typedef {import('../node.js').ProductFactor} ProductFactor
|
|
9
|
-
* @typedef {import('../simplify.js').SimplifyFn} SimplifyFn
|
|
10
|
-
*/
|
|
11
5
|
/**
|
|
12
6
|
* @param {Product} product
|
|
13
7
|
* @param {SimplifyFn} simplify
|
|
8
|
+
* @param {number | false} [precision] A quotient that is not exact at this
|
|
9
|
+
* precision stays symbolic (`100% / 3`) instead of being rounded.
|
|
14
10
|
* @return {Node}
|
|
15
11
|
*/
|
|
16
|
-
declare function simplifyProduct(product: Product, simplify: SimplifyFn): Node;
|
|
12
|
+
declare function simplifyProduct(product: Product, simplify: SimplifyFn, precision?: number | false): Node;
|
|
17
13
|
export { simplifyProduct };
|
|
@@ -6,7 +6,8 @@ export type UnitBucket = import('./bucket.js').UnitBucket;
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {Sum} sum
|
|
8
8
|
* @param {SimplifyFn} simplify
|
|
9
|
+
* @param {number | false} [precision]
|
|
9
10
|
* @return {Node}
|
|
10
11
|
*/
|
|
11
|
-
declare function simplifySum(sum: Sum, simplify: SimplifyFn): Node;
|
|
12
|
+
declare function simplifySum(sum: Sum, simplify: SimplifyFn, precision?: number | false): Node;
|
|
12
13
|
export { simplifySum };
|
package/types/lib/simplify.d.ts
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
export type Node = import('./node.js').Node;
|
|
2
2
|
export type SimplifyFn = (node: Node) => Node;
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('./node.js').Node} Node
|
|
5
|
-
*
|
|
6
|
-
* Recursive simplifier reference, threaded into Sum/Product/Call/OpaqueCall. Lets
|
|
7
|
-
* leaf fold modules avoid circular imports of the entry function.
|
|
8
|
-
* @typedef {(node: Node) => Node} SimplifyFn
|
|
9
|
-
*/
|
|
10
3
|
/**
|
|
11
4
|
* Simplify is an independent, composable AST transformation. It may
|
|
12
5
|
* synthesize canonical nodes while preserving the Node -> Node contract.
|
|
13
6
|
* @param {Node} node
|
|
7
|
+
* @param {number | false} [precision] When set, divisions and unit
|
|
8
|
+
* conversions that are not exact at this precision stay symbolic.
|
|
14
9
|
* @param {number} [depth]
|
|
15
10
|
* @return {Node}
|
|
16
11
|
*/
|
|
17
|
-
declare function simplify(node: Node, depth?: number): Node;
|
|
12
|
+
declare function simplify(node: Node, precision?: number | false, depth?: number): Node;
|
|
18
13
|
export { simplify };
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// Compatibility facade for the former calculation-type module. New code uses
|
|
2
|
-
// analyze() for the complete result and limits.js for depth policy.
|
|
3
|
-
|
|
4
|
-
import { analyze } from './analyze.js';
|
|
5
|
-
import { MAX_CALCULATION_DEPTH, checkCalculationDepth } from './limits.js';
|
|
6
|
-
|
|
7
|
-
/** @typedef {import('./node.js').Node} Node */
|
|
8
|
-
|
|
9
|
-
/** @typedef {{kind: 'number'} | {kind: 'dimension', base: string | null} | {kind: 'unknown'} | {kind: 'failure'}} CalculationType */
|
|
10
|
-
|
|
11
|
-
/** @param {Node} node @return {CalculationType} */
|
|
12
|
-
function checkCalculationType(node) {
|
|
13
|
-
const result = analyze(node);
|
|
14
|
-
if (!result.valid) return { kind: 'failure' };
|
|
15
|
-
if (result.type === 'number') return { kind: 'number' };
|
|
16
|
-
if (result.type === 'unknown') return { kind: 'unknown' };
|
|
17
|
-
return { kind: 'dimension', base: result.type.dimension };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
MAX_CALCULATION_DEPTH,
|
|
22
|
-
checkCalculationDepth,
|
|
23
|
-
checkCalculationType,
|
|
24
|
-
analyze,
|
|
25
|
-
};
|