postcss-normalize-timing-functions 9.0.2 → 9.0.4
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/package.json +3 -2
- package/src/index.js +88 -98
- package/types/index.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-normalize-timing-functions",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.4",
|
|
4
4
|
"description": "Normalize CSS animation/transition timing functions.",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
],
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"
|
|
21
|
+
"@csstools/css-tokenizer": "^4.0.2",
|
|
22
|
+
"cssnano-utils": "^8.0.0"
|
|
22
23
|
},
|
|
23
24
|
"author": {
|
|
24
25
|
"name": "Ben Briggs",
|
package/src/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import cssnanoUtils from 'cssnano-utils';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const { TokenType, asciiLowerCase, decoded } = cssnanoUtils;
|
|
4
|
+
/** @import {CSSToken} from '@csstools/css-tokenizer' */
|
|
5
|
+
/** @type {(source: string) => {tokens: readonly CSSToken[], endForOpening(index: number): number | undefined, topLevelSegments(start: number, end: number): {startIndex: number, endIndex: number}[]} | undefined} */
|
|
6
|
+
const getBalancedTokens = cssnanoUtils.balancedTokens;
|
|
5
7
|
const animationTransitionRegex =
|
|
6
|
-
/^(
|
|
8
|
+
/^(?:-[A-Za-z0-9_]+-)?(?:[aA][nN][iI][mM][aA][tT][iI][oO][nN]|[tT][rR][aA][nN][sS][iI][tT][iI][oO][nN])(?:-[tT][iI][mM][iI][nN][gG]-[fF][uU][nN][cC][tT][iI][oO][nN])?$/v;
|
|
7
9
|
|
|
8
10
|
/* Works because toString() normalizes the formatting,
|
|
9
11
|
so comparing the string forms behaves the same as number equality*/
|
|
@@ -14,103 +16,62 @@ const conversions = new Map([
|
|
|
14
16
|
[[0, 0, 0.58, 1].toString(), 'ease-out'],
|
|
15
17
|
[[0.42, 0, 0.58, 1].toString(), 'ease-in-out'],
|
|
16
18
|
]);
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const lowerCasedValue = node.value.toLowerCase();
|
|
31
|
-
|
|
32
|
-
if (lowerCasedValue === 'steps') {
|
|
33
|
-
return normalizeSteps(node);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (lowerCasedValue === 'cubic-bezier') {
|
|
37
|
-
return normalizeCubicBezier(node);
|
|
19
|
+
/** @param {readonly CSSToken[]} input @param {{startIndex: number, endIndex: number}} segment @return {CSSToken | undefined} */
|
|
20
|
+
function singleToken(input, segment) {
|
|
21
|
+
/** @type {CSSToken | undefined} */
|
|
22
|
+
let result;
|
|
23
|
+
for (let index = segment.startIndex; index < segment.endIndex; index++) {
|
|
24
|
+
const token = input[index];
|
|
25
|
+
if (token[0] === TokenType.Whitespace || token[0] === TokenType.Comment)
|
|
26
|
+
continue;
|
|
27
|
+
if (result) return;
|
|
28
|
+
result = token;
|
|
38
29
|
}
|
|
30
|
+
return result;
|
|
39
31
|
}
|
|
40
32
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
function normalizeSteps(node) {
|
|
46
|
-
const count = node.nodes[0];
|
|
47
|
-
const position = node.nodes[2];
|
|
48
|
-
const isSingleStep = count.type === 'word' && getValue(count) === 1;
|
|
49
|
-
|
|
50
|
-
if (isSingleStep && isStepPosition(position, 'start', 'jump-start')) {
|
|
51
|
-
/** @type string */ (node.type) = 'word';
|
|
52
|
-
node.value = 'step-start';
|
|
53
|
-
|
|
54
|
-
delete (/** @type Partial<valueParser.FunctionNode> */ (node).nodes);
|
|
55
|
-
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (isSingleStep && isStepPosition(position, 'end', 'jump-end')) {
|
|
60
|
-
/** @type string */ (node.type) = 'word';
|
|
61
|
-
node.value = 'step-end';
|
|
62
|
-
|
|
63
|
-
delete (/** @type Partial<valueParser.FunctionNode> */ (node).nodes);
|
|
64
|
-
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// The end case is actually the browser default, so it isn't required.
|
|
69
|
-
if (isStepPosition(position, 'end', 'jump-end')) {
|
|
70
|
-
node.nodes = [count];
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return false;
|
|
33
|
+
/** @param {string} value @param {CSSToken} token @return {string} */
|
|
34
|
+
function tokenSource(value, token) {
|
|
35
|
+
return value.slice(token[2], token[3] + 1);
|
|
76
36
|
}
|
|
77
37
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
* @param {valueParser.FunctionNode} node
|
|
93
|
-
* @return {void}
|
|
94
|
-
*/
|
|
95
|
-
function normalizeCubicBezier(node) {
|
|
96
|
-
const values = node.nodes
|
|
97
|
-
.filter((list, index) => {
|
|
98
|
-
return index % 2 === 0;
|
|
99
|
-
})
|
|
100
|
-
.map(getValue);
|
|
101
|
-
|
|
102
|
-
if (values.length !== 4) {
|
|
103
|
-
return;
|
|
38
|
+
/** @param {string} value @param {readonly CSSToken[]} input @param {NonNullable<ReturnType<typeof getBalancedTokens>>} structure @param {number} index @param {number} end @return {string | null} */
|
|
39
|
+
function reduceFunction(value, input, structure, index, end) {
|
|
40
|
+
const name = asciiLowerCase(decoded(input[index]));
|
|
41
|
+
const segments = structure.topLevelSegments(index + 1, end);
|
|
42
|
+
if (name === 'cubic-bezier' && segments.length === 4) {
|
|
43
|
+
const values = segments.map((segment) => {
|
|
44
|
+
const token = singleToken(input, segment);
|
|
45
|
+
return token?.[0] === TokenType.Number
|
|
46
|
+
? /** @type {{value: number}} */ (token[4]).value
|
|
47
|
+
: Number.NaN;
|
|
48
|
+
});
|
|
49
|
+
return values.every((number) => !Number.isNaN(number))
|
|
50
|
+
? (conversions.get(values.toString()) ?? null)
|
|
51
|
+
: null;
|
|
104
52
|
}
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
53
|
+
if (name !== 'steps' || segments.length !== 2) return null;
|
|
54
|
+
const count = singleToken(input, segments[0]);
|
|
55
|
+
if (
|
|
56
|
+
!count ||
|
|
57
|
+
count[0] !== TokenType.Number ||
|
|
58
|
+
!Number.isInteger(/** @type {{value: number}} */ (count[4]).value) ||
|
|
59
|
+
/** @type {{value: number}} */ (count[4]).value < 1
|
|
60
|
+
)
|
|
61
|
+
return null;
|
|
62
|
+
const position = singleToken(input, segments[1]);
|
|
63
|
+
if (!position || position[0] !== TokenType.Ident) return null;
|
|
64
|
+
const positionName = asciiLowerCase(decoded(position));
|
|
65
|
+
const countValue = /** @type {{value: number}} */ (count[4]).value;
|
|
66
|
+
if (countValue === 1) {
|
|
67
|
+
if (positionName === 'start' || positionName === 'jump-start')
|
|
68
|
+
return 'step-start';
|
|
69
|
+
if (positionName === 'end' || positionName === 'jump-end')
|
|
70
|
+
return 'step-end';
|
|
113
71
|
}
|
|
72
|
+
return positionName === 'end' || positionName === 'jump-end'
|
|
73
|
+
? `steps(${tokenSource(value, count)})`
|
|
74
|
+
: null;
|
|
114
75
|
}
|
|
115
76
|
|
|
116
77
|
/**
|
|
@@ -118,7 +79,27 @@ function normalizeCubicBezier(node) {
|
|
|
118
79
|
* @return {string}
|
|
119
80
|
*/
|
|
120
81
|
function transform(value) {
|
|
121
|
-
|
|
82
|
+
const structure = getBalancedTokens(value);
|
|
83
|
+
if (!structure) return value;
|
|
84
|
+
const { tokens: input } = structure;
|
|
85
|
+
/** @type {{start:number,end:number,text:string}[]} */
|
|
86
|
+
const edits = [];
|
|
87
|
+
for (let index = 0; index < input.length; index++) {
|
|
88
|
+
if (input[index][0] !== TokenType.Function) continue;
|
|
89
|
+
const end = structure.endForOpening(index);
|
|
90
|
+
if (end === undefined) continue;
|
|
91
|
+
const replacement = reduceFunction(value, input, structure, index, end);
|
|
92
|
+
if (replacement)
|
|
93
|
+
edits.push({
|
|
94
|
+
start: input[index][2],
|
|
95
|
+
end: input[end][3] + 1,
|
|
96
|
+
text: replacement,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
let result = value;
|
|
100
|
+
for (const edit of edits.toSorted((a, b) => b.start - a.start))
|
|
101
|
+
result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
|
|
102
|
+
return result;
|
|
122
103
|
}
|
|
123
104
|
|
|
124
105
|
/**
|
|
@@ -134,22 +115,31 @@ function pluginCreator() {
|
|
|
134
115
|
const cache = new Map();
|
|
135
116
|
|
|
136
117
|
css.walkDecls(animationTransitionRegex, (decl) => {
|
|
137
|
-
const value =
|
|
118
|
+
const value =
|
|
119
|
+
decl.raws.value?.value === decl.value
|
|
120
|
+
? (decl.raws.value.raw ?? decl.value)
|
|
121
|
+
: decl.value;
|
|
138
122
|
|
|
139
123
|
if (cache.has(value)) {
|
|
140
|
-
decl
|
|
124
|
+
assignValue(decl, cache.get(value));
|
|
141
125
|
|
|
142
126
|
return;
|
|
143
127
|
}
|
|
144
128
|
|
|
145
129
|
const result = transform(value);
|
|
146
130
|
|
|
147
|
-
decl
|
|
131
|
+
assignValue(decl, result);
|
|
148
132
|
cache.set(value, result);
|
|
149
133
|
});
|
|
150
134
|
},
|
|
151
135
|
};
|
|
152
136
|
}
|
|
137
|
+
|
|
138
|
+
/** @param {import('postcss').Declaration} decl @param {string} value */
|
|
139
|
+
function assignValue(decl, value) {
|
|
140
|
+
decl.value = value;
|
|
141
|
+
if (decl.raws.value?.raw) decl.raws.value = { raw: value, value };
|
|
142
|
+
}
|
|
153
143
|
/** @type {true} */
|
|
154
144
|
pluginCreator.postcss = true;
|
|
155
145
|
const moduleExports = pluginCreator;
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAwGA;;GAEG;AACH,iBAAS,aAAa,IAFV,OAAO,SAAS,EAAE,MAAM,CA8BnC;kBA5BQ,aAAa;iBAmCX,IAAI;;AAEf,QAAA,MAAM,aAAa,sBAAgB,CAAC;AAEpC,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,aAAa,IAAI,gBAAgB,EAAE,CAAC"}
|