postcss-convert-values 9.0.4 → 9.0.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postcss-convert-values",
3
- "version": "9.0.4",
3
+ "version": "9.0.5",
4
4
  "description": "Convert values with PostCSS (e.g. ms -> s)",
5
5
  "exports": {
6
6
  ".": {
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "browserslist": "^4.29.1",
44
- "cssnano-utils": "^8.0.0"
44
+ "cssnano-utils": "^8.0.1"
45
45
  },
46
46
  "bugs": {
47
47
  "url": "https://github.com/cssnano/cssnano/issues"
@@ -1,11 +1,11 @@
1
- export const lengthConv = new Map([
1
+ const lengthConv = new Map([
2
2
  ['in', 288],
3
3
  ['px', 3],
4
4
  ['pt', 4],
5
5
  ['pc', 48],
6
6
  ]);
7
7
 
8
- export const metricConv = new Map([
8
+ const metricConv = new Map([
9
9
  ['cm', 40],
10
10
  ['mm', 4],
11
11
  ['q', 1],
@@ -0,0 +1,332 @@
1
+ import cssnanoUtils from 'cssnano-utils';
2
+ import convert, {
3
+ dropLeadingZero,
4
+ roundToPrecision,
5
+ timeConv,
6
+ angleConv,
7
+ freqConv,
8
+ } from './convert.js';
9
+
10
+ const {
11
+ TokenType,
12
+ calcSumFunctions,
13
+ closeForOpening,
14
+ decoded,
15
+ lengthUnits,
16
+ mathFunctions,
17
+ numericSource,
18
+ tokens,
19
+ } = cssnanoUtils;
20
+
21
+ /* Functions in which a zero cannot lose its unit. Every math function qualifies
22
+ * because `calc(0%)` and `calc(0)` differ in type; the rest are listed per
23
+ * grammar, e.g. conic-gradient stops take an <angle-percentage>, which no
24
+ * unitless zero represents, while linear-gradient stops take a
25
+ * <length-percentage>, which it does. */
26
+ const functionsPreservingZeroUnits = new Set([
27
+ ...mathFunctions,
28
+ ...calcSumFunctions,
29
+ 'anchor',
30
+ 'anchor-size',
31
+ 'contrast-color',
32
+ 'view',
33
+ 'color-mix',
34
+ 'hsl',
35
+ 'hsla',
36
+ 'hwb',
37
+ 'linear',
38
+ 'conic-gradient',
39
+ 'repeating-conic-gradient',
40
+ 'cross-fade',
41
+ 'rgb',
42
+ 'rgba',
43
+ 'oklch',
44
+ 'oklab',
45
+ 'lab',
46
+ 'lch',
47
+ 'color',
48
+ 'palette-mix',
49
+ ]);
50
+
51
+ const convertibleUnits = new Set([
52
+ ...timeConv.keys(),
53
+ ...angleConv.keys(),
54
+ ...freqConv.keys(),
55
+ 'rad',
56
+ ]);
57
+
58
+ /** @typedef {Parameters<typeof convert>[2]} ConvertOptions */
59
+ /** @typedef {{precision?: false | number, transformCustomProperties?: boolean} & ConvertOptions & { overrideBrowserslist?: string | string[] } & import('browserslist').Options} Options */
60
+ /** @typedef {{keepZeroPercent: boolean, keepZeroLength: boolean, clampAlpha: boolean, isAlpha: boolean, isFont: boolean}} ReplacementFlags */
61
+
62
+ /** @param {number} number @param {string} unit @param {string} raw @param {Options} opts @param {boolean} keepZeroPercent @param {boolean} keepZeroLength @param {boolean} hasDecimal @return {string} */
63
+ function parseNumber(
64
+ number,
65
+ unit,
66
+ raw,
67
+ opts,
68
+ keepZeroPercent,
69
+ keepZeroLength,
70
+ hasDecimal
71
+ ) {
72
+ if (raw.includes('\\')) return raw;
73
+ const lowerCasedUnit = unit.toLowerCase();
74
+ if (
75
+ unit !== '' &&
76
+ unit !== '%' &&
77
+ !lengthUnits.has(lowerCasedUnit) &&
78
+ !convertibleUnits.has(lowerCasedUnit)
79
+ )
80
+ return raw;
81
+ let num = number;
82
+ if (
83
+ typeof opts.precision === 'number' &&
84
+ opts.precision >= 0 &&
85
+ (hasDecimal || !Number.isInteger(num))
86
+ ) {
87
+ num = roundToPrecision(num, opts.precision);
88
+ }
89
+ if (num === 0) {
90
+ if (convertibleUnits.has(lowerCasedUnit)) {
91
+ return convert(0, unit, opts);
92
+ }
93
+ if (unit === '%') {
94
+ return keepZeroPercent ? '0%' : '0';
95
+ }
96
+ if (lengthUnits.has(lowerCasedUnit)) {
97
+ return keepZeroLength ? '0' + unit : '0';
98
+ }
99
+ return '0';
100
+ }
101
+ return convert(num, unit, opts);
102
+ }
103
+
104
+ /** @param {string} value @param {number} number @param {string} unit @param {Options} opts @param {boolean} [clamp] @return {string} */
105
+ function clampOpacity(value, number, unit, opts, clamp = true) {
106
+ if (unit === '%') {
107
+ if (clamp) {
108
+ if (number >= 100) return '1';
109
+ if (number <= 0) return '0';
110
+ }
111
+ let decimalNumber = Number((number / 100).toPrecision(15));
112
+ if (typeof opts.precision === 'number' && opts.precision >= 0) {
113
+ decimalNumber = roundToPrecision(decimalNumber, opts.precision);
114
+ }
115
+ const decimal = dropLeadingZero(decimalNumber);
116
+ return decimal.length < value.length ? decimal : value;
117
+ }
118
+ if (clamp) {
119
+ if (number > 1) return '1';
120
+ if (number < 0) return '0';
121
+ }
122
+ return value;
123
+ }
124
+
125
+ /** @param {string} str @return {string} */
126
+ export function stripVendorPrefix(str) {
127
+ if (str.charCodeAt(0) !== 45 /* '-' */) return str;
128
+ if (str.startsWith('-webkit-')) return str.slice(8);
129
+ if (str.startsWith('-moz-')) return str.slice(5);
130
+ if (str.startsWith('-ms-')) return str.slice(4);
131
+ if (str.startsWith('-o-')) return str.slice(3);
132
+ return str;
133
+ }
134
+
135
+ /**
136
+ * @param {string} replacement
137
+ * @param {string} raw
138
+ * @param {boolean} isAlpha
139
+ * @param {boolean} isConvertibleZero
140
+ * @return {boolean}
141
+ */
142
+ function shouldReplace(replacement, raw, isAlpha, isConvertibleZero) {
143
+ if (replacement === raw) return false;
144
+ if (replacement.length < raw.length) return true;
145
+ return (isAlpha || isConvertibleZero) && replacement.length <= raw.length;
146
+ }
147
+
148
+ class FrameStack {
149
+ /** @type {string[]} */
150
+ #closeStack = [];
151
+ #keepZeroPercent;
152
+ #keepZeroLength;
153
+ #keepZeroUnitsDepth = -1;
154
+ #skippedDepth = -1;
155
+
156
+ /**
157
+ * @param {boolean} keepZeroPercent
158
+ * @param {boolean} keepZeroLength
159
+ */
160
+ constructor(keepZeroPercent, keepZeroLength) {
161
+ this.#keepZeroPercent = keepZeroPercent;
162
+ this.#keepZeroLength = keepZeroLength;
163
+ }
164
+
165
+ get isTopLevel() {
166
+ return this.#closeStack.length === 0;
167
+ }
168
+
169
+ get isSkipped() {
170
+ return this.#skippedDepth !== -1;
171
+ }
172
+
173
+ get keepZeroPercent() {
174
+ return this.#keepZeroPercent || this.#keepZeroUnitsDepth !== -1;
175
+ }
176
+
177
+ get keepZeroLength() {
178
+ return this.#keepZeroLength || this.#keepZeroUnitsDepth !== -1;
179
+ }
180
+
181
+ /**
182
+ * @param {ReturnType<typeof tokens>[number]} token
183
+ * @return {boolean}
184
+ */
185
+ advance(token) {
186
+ const type = token[0];
187
+ if (type === TokenType.Function) {
188
+ const name = stripVendorPrefix(decoded(token).toLowerCase());
189
+ const keepInFunction = functionsPreservingZeroUnits.has(name);
190
+ const depth = this.#closeStack.length + 1;
191
+ this.#closeStack.push(TokenType.CloseParen);
192
+ if (keepInFunction && this.#keepZeroUnitsDepth === -1) {
193
+ this.#keepZeroUnitsDepth = depth;
194
+ }
195
+ if (name === 'url' && this.#skippedDepth === -1) {
196
+ this.#skippedDepth = depth;
197
+ }
198
+ return true;
199
+ }
200
+ if (
201
+ type === TokenType.OpenParen ||
202
+ type === TokenType.OpenSquare ||
203
+ type === TokenType.OpenCurly
204
+ ) {
205
+ const close = closeForOpening(type);
206
+ if (close !== undefined) this.#closeStack.push(close);
207
+ return true;
208
+ }
209
+ if (
210
+ type === TokenType.CloseParen ||
211
+ type === TokenType.CloseSquare ||
212
+ type === TokenType.CloseCurly
213
+ ) {
214
+ if (
215
+ this.#closeStack.length > 0 &&
216
+ this.#closeStack[this.#closeStack.length - 1] === type
217
+ ) {
218
+ const depth = this.#closeStack.length;
219
+ if (depth === this.#keepZeroUnitsDepth) this.#keepZeroUnitsDepth = -1;
220
+ if (depth === this.#skippedDepth) this.#skippedDepth = -1;
221
+ this.#closeStack.pop();
222
+ }
223
+ return true;
224
+ }
225
+ return false;
226
+ }
227
+ }
228
+
229
+ /** @typedef {{index: number, start: number, end: number, raw: string, number: number, unit: string, hasDecimal: boolean}} NumericSource */
230
+
231
+ /**
232
+ * @param {NumericSource} source
233
+ * @param {boolean} isLineHeight
234
+ * @param {FrameStack} frames
235
+ * @param {Options} opts
236
+ * @param {ReplacementFlags} flags
237
+ * @return {{start: number, end: number, text: string} | undefined}
238
+ */
239
+ function processNumericToken(source, isLineHeight, frames, opts, flags) {
240
+ const currentKeepZeroPercent = frames.keepZeroPercent || isLineHeight;
241
+ const currentKeepZeroLength = frames.keepZeroLength || isLineHeight;
242
+
243
+ const converted = parseNumber(
244
+ source.number,
245
+ source.unit,
246
+ source.raw,
247
+ opts,
248
+ currentKeepZeroPercent,
249
+ currentKeepZeroLength,
250
+ source.hasDecimal
251
+ );
252
+ const isAlpha = flags.isAlpha && frames.isTopLevel;
253
+ const replacement = isAlpha
254
+ ? clampOpacity(
255
+ converted,
256
+ source.number,
257
+ source.unit,
258
+ opts,
259
+ flags.clampAlpha
260
+ )
261
+ : converted;
262
+ const isConvertibleZero =
263
+ source.number === 0 && convertibleUnits.has(source.unit.toLowerCase());
264
+
265
+ if (shouldReplace(replacement, source.raw, isAlpha, isConvertibleZero)) {
266
+ return {
267
+ start: source.start,
268
+ end: source.end,
269
+ text: replacement,
270
+ };
271
+ }
272
+ return undefined;
273
+ }
274
+
275
+ /**
276
+ * @param {string} value
277
+ * @param {ReplacementFlags} flags
278
+ * @param {Options} opts
279
+ * @return {{start: number, end: number, text: string}[]}
280
+ */
281
+ export function findReplacements(value, flags, opts) {
282
+ /** @type {{start: number, end: number, text: string}[]} */
283
+ const replacements = [];
284
+ const frames = new FrameStack(flags.keepZeroPercent, flags.keepZeroLength);
285
+ let afterFontSlash = false;
286
+
287
+ const input = tokens(value, { unicodeRangesAllowed: true });
288
+ for (let index = 0; index < input.length; index++) {
289
+ const token = input[index];
290
+ if (frames.advance(token)) {
291
+ if (frames.isTopLevel && afterFontSlash) afterFontSlash = false;
292
+ continue;
293
+ }
294
+ if (frames.isSkipped) continue;
295
+
296
+ const type = token[0];
297
+ if (frames.isTopLevel) {
298
+ if (type === TokenType.Whitespace || type === TokenType.Comment) {
299
+ continue;
300
+ }
301
+ if (flags.isFont && type === TokenType.Delim && token[1] === '/') {
302
+ afterFontSlash = true;
303
+ continue;
304
+ }
305
+ }
306
+
307
+ const wasAfterFontSlash = frames.isTopLevel && afterFontSlash;
308
+ if (afterFontSlash) afterFontSlash = false;
309
+
310
+ if (
311
+ type !== TokenType.Number &&
312
+ type !== TokenType.Dimension &&
313
+ type !== TokenType.Percentage
314
+ ) {
315
+ continue;
316
+ }
317
+
318
+ const source = numericSource(input, index);
319
+ if (!source) continue;
320
+ index = source.index;
321
+
322
+ const edit = processNumericToken(
323
+ source,
324
+ wasAfterFontSlash,
325
+ frames,
326
+ opts,
327
+ flags
328
+ );
329
+ if (edit) replacements.push(edit);
330
+ }
331
+ return replacements;
332
+ }
@@ -1,24 +1,8 @@
1
1
  import cssnanoUtils from 'cssnano-utils';
2
- import convert, {
3
- dropLeadingZero,
4
- roundToPrecision,
5
- timeConv,
6
- angleConv,
7
- freqConv,
8
- } from './convert.js';
9
- import { syntaxAllowsPercentage } from './parse-syntax.js';
2
+ import { syntaxAllowsPercentage } from './parseSyntax.js';
3
+ import { findReplacements, stripVendorPrefix } from './findReplacements.js';
10
4
 
11
- const {
12
- TokenType,
13
- applyEdits,
14
- calcSumFunctions,
15
- closeForOpening,
16
- decoded,
17
- lengthUnits,
18
- mathFunctions,
19
- numericSource,
20
- tokens,
21
- } = cssnanoUtils;
5
+ const { applyEdits } = cssnanoUtils;
22
6
 
23
7
  const flexBasisProperties = new Set([
24
8
  'flex',
@@ -26,6 +10,7 @@ const flexBasisProperties = new Set([
26
10
  'flex-basic',
27
11
  'flex-preferred-size',
28
12
  ]);
13
+
29
14
  const alphaProperties = new Set([
30
15
  'opacity',
31
16
  'shape-image-threshold',
@@ -34,6 +19,7 @@ const alphaProperties = new Set([
34
19
  'stop-opacity',
35
20
  'flood-opacity',
36
21
  ]);
22
+
37
23
  const zeroPercentRetention = new Set([
38
24
  'descent-override',
39
25
  'ascent-override',
@@ -50,6 +36,7 @@ const zeroPercentRetention = new Set([
50
36
  'stroke-dashoffset',
51
37
  'stroke-width',
52
38
  ]);
39
+
53
40
  const zeroUnitRetention = {
54
41
  ie11Percent: new Set(['max-height', 'height', 'min-width']),
55
42
  keyframePercent: new Set([
@@ -59,118 +46,8 @@ const zeroUnitRetention = {
59
46
  'stroke-width',
60
47
  ]),
61
48
  };
62
- /* Functions in which a zero cannot lose its unit. Every math function qualifies
63
- * because `calc(0%)` and `calc(0)` differ in type; the rest are listed per
64
- * grammar, e.g. conic-gradient stops take an <angle-percentage>, which no
65
- * unitless zero represents, while linear-gradient stops take a
66
- * <length-percentage>, which it does. */
67
- const functionsPreservingZeroUnits = new Set([
68
- ...mathFunctions,
69
- ...calcSumFunctions,
70
- 'anchor',
71
- 'anchor-size',
72
- 'contrast-color',
73
- 'view',
74
- 'color-mix',
75
- 'hsl',
76
- 'hsla',
77
- 'hwb',
78
- 'linear',
79
- 'conic-gradient',
80
- 'repeating-conic-gradient',
81
- 'cross-fade',
82
- 'rgb',
83
- 'rgba',
84
- 'oklch',
85
- 'oklab',
86
- 'lab',
87
- 'lch',
88
- 'color',
89
- 'palette-mix',
90
- ]);
91
-
92
- const convertibleUnits = new Set([
93
- ...timeConv.keys(),
94
- ...angleConv.keys(),
95
- ...freqConv.keys(),
96
- 'rad',
97
- ]);
98
-
99
- /** @typedef {Parameters<typeof convert>[2]} ConvertOptions */
100
- /** @typedef {{precision?: false | number, transformCustomProperties?: boolean} & ConvertOptions & { overrideBrowserslist?: string | string[] } & import('browserslist').Options} Options */
101
-
102
- /** @param {number} number @param {string} unit @param {string} raw @param {Options} opts @param {boolean} keepZeroPercent @param {boolean} keepZeroLength @param {boolean} hasDecimal @return {string} */
103
- function parseNumber(
104
- number,
105
- unit,
106
- raw,
107
- opts,
108
- keepZeroPercent,
109
- keepZeroLength,
110
- hasDecimal
111
- ) {
112
- if (raw.includes('\\')) return raw;
113
- const lowerCasedUnit = unit.toLowerCase();
114
- if (
115
- unit !== '' &&
116
- unit !== '%' &&
117
- !lengthUnits.has(lowerCasedUnit) &&
118
- !convertibleUnits.has(lowerCasedUnit)
119
- )
120
- return raw;
121
- let num = number;
122
- if (
123
- typeof opts.precision === 'number' &&
124
- opts.precision >= 0 &&
125
- (hasDecimal || !Number.isInteger(num))
126
- ) {
127
- num = roundToPrecision(num, opts.precision);
128
- }
129
- if (num === 0) {
130
- if (convertibleUnits.has(lowerCasedUnit)) {
131
- return convert(0, unit, opts);
132
- }
133
- if (unit === '%') {
134
- return keepZeroPercent ? '0%' : '0';
135
- }
136
- if (lengthUnits.has(lowerCasedUnit)) {
137
- return keepZeroLength ? '0' + unit : '0';
138
- }
139
- return '0';
140
- }
141
- return convert(num, unit, opts);
142
- }
143
-
144
- /** @param {string} value @param {number} number @param {string} unit @param {Options} opts @param {boolean} [clamp] @return {string} */
145
- function clampOpacity(value, number, unit, opts, clamp = true) {
146
- if (unit === '%') {
147
- if (clamp) {
148
- if (number >= 100) return '1';
149
- if (number <= 0) return '0';
150
- }
151
- let decimalNumber = Number((number / 100).toPrecision(15));
152
- if (typeof opts.precision === 'number' && opts.precision >= 0) {
153
- decimalNumber = roundToPrecision(decimalNumber, opts.precision);
154
- }
155
- const decimal = dropLeadingZero(decimalNumber);
156
- return decimal.length < value.length ? decimal : value;
157
- }
158
- if (clamp) {
159
- if (number > 1) return '1';
160
- if (number < 0) return '0';
161
- }
162
- return value;
163
- }
164
49
 
165
- /** @param {string} str @return {string} */
166
- function stripVendorPrefix(str) {
167
- if (str.charCodeAt(0) !== 45 /* '-' */) return str;
168
- if (str.startsWith('-webkit-')) return str.slice(8);
169
- if (str.startsWith('-moz-')) return str.slice(5);
170
- if (str.startsWith('-ms-')) return str.slice(4);
171
- if (str.startsWith('-o-')) return str.slice(3);
172
- return str;
173
- }
50
+ /** @typedef {import('./findReplacements.js').Options} Options */
174
51
 
175
52
  /** @type {WeakMap<import('postcss').AtRule, boolean>} */
176
53
  const atPropertySyntaxCache = new WeakMap();
@@ -209,7 +86,10 @@ function isInsideKeyframes(decl) {
209
86
 
210
87
  /** @param {string} property @param {Options} opts @return {boolean} */
211
88
  function skipsTransformation(property, opts) {
212
- return property.startsWith('--') && !opts.transformCustomProperties;
89
+ return (
90
+ property === 'unicode-range' ||
91
+ (property.startsWith('--') && !opts.transformCustomProperties)
92
+ );
213
93
  }
214
94
 
215
95
  /**
@@ -217,7 +97,7 @@ function skipsTransformation(property, opts) {
217
97
  * @param {string} lowerCasedProp
218
98
  * @param {string} strippedProp
219
99
  * @param {boolean | string[]} supportsIE
220
- * @return {{keepZeroPercent: boolean, keepZeroLength: boolean, hasParentContext: boolean, clampAlpha: boolean}}
100
+ * @return {import('./findReplacements.js').ReplacementFlags & { hasParentContext: boolean }}
221
101
  */
222
102
  function getDeclarationFlags(decl, lowerCasedProp, strippedProp, supportsIE) {
223
103
  const isLineHeight = strippedProp === 'line-height';
@@ -227,10 +107,9 @@ function getDeclarationFlags(decl, lowerCasedProp, strippedProp, supportsIE) {
227
107
  decl.parent?.type === 'atrule' &&
228
108
  /** @type {import('postcss').AtRule} */ (decl.parent).name.toLowerCase() ===
229
109
  'property';
230
- const isAlphaProp = alphaProperties.has(strippedProp);
110
+ const isAlpha = alphaProperties.has(strippedProp);
231
111
  const isKeyframeProp = zeroUnitRetention.keyframePercent.has(strippedProp);
232
- const inKeyframes =
233
- (isKeyframeProp || isAlphaProp) && isInsideKeyframes(decl);
112
+ const inKeyframes = (isKeyframeProp || isAlpha) && isInsideKeyframes(decl);
234
113
  const hasParentContext = lowerCasedProp === 'initial-value' || inKeyframes;
235
114
 
236
115
  const hasIESupport =
@@ -257,212 +136,16 @@ function getDeclarationFlags(decl, lowerCasedProp, strippedProp, supportsIE) {
257
136
  isAtProperty;
258
137
 
259
138
  const clampAlpha = !inKeyframes;
260
-
261
- return { keepZeroPercent, keepZeroLength, hasParentContext, clampAlpha };
262
- }
263
-
264
- /**
265
- * @param {string} replacement
266
- * @param {string} raw
267
- * @param {boolean} isAlpha
268
- * @param {boolean} isConvertibleZero
269
- * @return {boolean}
270
- */
271
- function shouldReplace(replacement, raw, isAlpha, isConvertibleZero) {
272
- if (replacement === raw) return false;
273
- if (replacement.length < raw.length) return true;
274
- return (isAlpha || isConvertibleZero) && replacement.length <= raw.length;
275
- }
276
-
277
- class FrameStack {
278
- /** @type {string[]} */
279
- #closeStack = [];
280
- #keepZeroPercent;
281
- #keepZeroLength;
282
- #keepZeroUnitsDepth = -1;
283
- #skippedDepth = -1;
284
-
285
- /**
286
- * @param {boolean} keepZeroPercent
287
- * @param {boolean} keepZeroLength
288
- */
289
- constructor(keepZeroPercent, keepZeroLength) {
290
- this.#keepZeroPercent = keepZeroPercent;
291
- this.#keepZeroLength = keepZeroLength;
292
- }
293
-
294
- get isTopLevel() {
295
- return this.#closeStack.length === 0;
296
- }
297
-
298
- get isSkipped() {
299
- return this.#skippedDepth !== -1;
300
- }
301
-
302
- get keepZeroPercent() {
303
- return this.#keepZeroPercent || this.#keepZeroUnitsDepth !== -1;
304
- }
305
-
306
- get keepZeroLength() {
307
- return this.#keepZeroLength || this.#keepZeroUnitsDepth !== -1;
308
- }
309
-
310
- /**
311
- * @param {ReturnType<typeof tokens>[number]} token
312
- * @return {boolean}
313
- */
314
- advance(token) {
315
- const type = token[0];
316
- if (type === TokenType.Function) {
317
- const name = stripVendorPrefix(decoded(token).toLowerCase());
318
- const keepInFunction = functionsPreservingZeroUnits.has(name);
319
- const depth = this.#closeStack.length + 1;
320
- this.#closeStack.push(TokenType.CloseParen);
321
- if (keepInFunction && this.#keepZeroUnitsDepth === -1) {
322
- this.#keepZeroUnitsDepth = depth;
323
- }
324
- if (name === 'url' && this.#skippedDepth === -1) {
325
- this.#skippedDepth = depth;
326
- }
327
- return true;
328
- }
329
- if (
330
- type === TokenType.OpenParen ||
331
- type === TokenType.OpenSquare ||
332
- type === TokenType.OpenCurly
333
- ) {
334
- const close = closeForOpening(type);
335
- if (close !== undefined) this.#closeStack.push(close);
336
- return true;
337
- }
338
- if (
339
- type === TokenType.CloseParen ||
340
- type === TokenType.CloseSquare ||
341
- type === TokenType.CloseCurly
342
- ) {
343
- if (
344
- this.#closeStack.length > 0 &&
345
- this.#closeStack[this.#closeStack.length - 1] === type
346
- ) {
347
- const depth = this.#closeStack.length;
348
- if (depth === this.#keepZeroUnitsDepth) this.#keepZeroUnitsDepth = -1;
349
- if (depth === this.#skippedDepth) this.#skippedDepth = -1;
350
- this.#closeStack.pop();
351
- }
352
- return true;
353
- }
354
- return false;
355
- }
356
- }
357
-
358
- /** @typedef {{index: number, start: number, end: number, raw: string, number: number, unit: string, hasDecimal: boolean}} NumericSource */
359
-
360
- /**
361
- * @param {NumericSource} source
362
- * @param {boolean} isLineHeight
363
- * @param {string} strippedProp
364
- * @param {FrameStack} frames
365
- * @param {Options} opts
366
- * @param {boolean} clampAlpha
367
- * @return {{start: number, end: number, text: string} | undefined}
368
- */
369
- function processNumericToken(
370
- source,
371
- isLineHeight,
372
- strippedProp,
373
- frames,
374
- opts,
375
- clampAlpha
376
- ) {
377
- const currentKeepZeroPercent = frames.keepZeroPercent || isLineHeight;
378
- const currentKeepZeroLength = frames.keepZeroLength || isLineHeight;
379
-
380
- const converted = parseNumber(
381
- source.number,
382
- source.unit,
383
- source.raw,
384
- opts,
385
- currentKeepZeroPercent,
386
- currentKeepZeroLength,
387
- source.hasDecimal
388
- );
389
- const isAlpha = alphaProperties.has(strippedProp) && frames.isTopLevel;
390
- const replacement = isAlpha
391
- ? clampOpacity(converted, source.number, source.unit, opts, clampAlpha)
392
- : converted;
393
- const isConvertibleZero =
394
- source.number === 0 && convertibleUnits.has(source.unit.toLowerCase());
395
-
396
- if (shouldReplace(replacement, source.raw, isAlpha, isConvertibleZero)) {
397
- return {
398
- start: source.start,
399
- end: source.end,
400
- text: replacement,
401
- };
402
- }
403
- return undefined;
404
- }
405
-
406
- /**
407
- * @param {string} value
408
- * @param {string} strippedProp
409
- * @param {{keepZeroPercent: boolean, keepZeroLength: boolean, clampAlpha: boolean}} flags
410
- * @param {Options} opts
411
- * @return {{start: number, end: number, text: string}[]}
412
- */
413
- function findReplacements(value, strippedProp, flags, opts) {
414
- /** @type {{start: number, end: number, text: string}[]} */
415
- const replacements = [];
416
- const frames = new FrameStack(flags.keepZeroPercent, flags.keepZeroLength);
417
139
  const isFont = strippedProp === 'font';
418
- let afterFontSlash = false;
419
-
420
- const input = tokens(value);
421
- for (let index = 0; index < input.length; index++) {
422
- const token = input[index];
423
- if (frames.advance(token)) {
424
- if (frames.isTopLevel && afterFontSlash) afterFontSlash = false;
425
- continue;
426
- }
427
- if (frames.isSkipped) continue;
428
-
429
- const type = token[0];
430
- if (frames.isTopLevel) {
431
- if (type === TokenType.Whitespace || type === TokenType.Comment) {
432
- continue;
433
- }
434
- if (isFont && type === TokenType.Delim && token[1] === '/') {
435
- afterFontSlash = true;
436
- continue;
437
- }
438
- }
439
140
 
440
- const wasAfterFontSlash = frames.isTopLevel && afterFontSlash;
441
- if (afterFontSlash) afterFontSlash = false;
442
-
443
- if (
444
- type !== TokenType.Number &&
445
- type !== TokenType.Dimension &&
446
- type !== TokenType.Percentage
447
- ) {
448
- continue;
449
- }
450
-
451
- const source = numericSource(input, index);
452
- if (!source) continue;
453
- index = source.index;
454
-
455
- const edit = processNumericToken(
456
- source,
457
- wasAfterFontSlash,
458
- strippedProp,
459
- frames,
460
- opts,
461
- flags.clampAlpha
462
- );
463
- if (edit) replacements.push(edit);
464
- }
465
- return replacements;
141
+ return {
142
+ keepZeroPercent,
143
+ keepZeroLength,
144
+ clampAlpha,
145
+ isAlpha,
146
+ isFont,
147
+ hasParentContext,
148
+ };
466
149
  }
467
150
 
468
151
  /** @param {import('postcss').Declaration} decl @param {string} newValue @return {void} */
@@ -499,11 +182,11 @@ function syncDeclValue(decl, newValue, originalValue) {
499
182
  /** @param {Options} opts @param {boolean | string[]} supportsIE @param {import('postcss').Declaration} decl @param {Map<string, string>} [cache] @return {void} */
500
183
  export default function transform(opts, supportsIE, decl, cache) {
501
184
  const lowerCasedProp = decl.prop.toLowerCase();
502
- if (skipsTransformation(lowerCasedProp, opts)) return;
185
+ const strippedProp = stripVendorPrefix(lowerCasedProp);
186
+ if (skipsTransformation(strippedProp, opts)) return;
503
187
  const value = getRawOrDeclarationValue(decl);
504
188
  if (!/[0-9]/v.test(value)) return;
505
189
 
506
- const strippedProp = stripVendorPrefix(lowerCasedProp);
507
190
  const flags = getDeclarationFlags(
508
191
  decl,
509
192
  lowerCasedProp,
@@ -520,7 +203,7 @@ export default function transform(opts, supportsIE, decl, cache) {
520
203
  return;
521
204
  }
522
205
 
523
- const replacements = findReplacements(value, strippedProp, flags, opts);
206
+ const replacements = findReplacements(value, flags, opts);
524
207
  const result = replacements.length ? applyEdits(value, replacements) : value;
525
208
  if (cacheKey !== undefined) {
526
209
  cache?.set(cacheKey, result);
@@ -1,5 +1,3 @@
1
- export declare const lengthConv: Map<string, number>;
2
- export declare const metricConv: Map<string, number>;
3
1
  export declare const timeConv: Map<string, number>;
4
2
  export declare const angleConv: Map<string, number>;
5
3
  export declare const freqConv: Map<string, number>;
@@ -1 +1 @@
1
- {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/lib/convert.js"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,qBAKrB,CAAC;AAEH,eAAO,MAAM,UAAU,qBAIrB,CAAC;AAEH,eAAO,MAAM,QAAQ,qBAGnB,CAAC;AAEH,eAAO,MAAM,SAAS,qBAIpB,CAAC;AAEH,eAAO,MAAM,QAAQ,qBAGnB,CAAC;AAEC,YAAoF,cAAc,GAAxF;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAC,CAAgB;AAAtG,yGAAyG;AAEzG;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAJ3B,MAI2B,EAAE,SAAS,EAHtC,MAGsC,GAFrC,MAAM,CAgBjB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAH3B,MAG2B,GAF1B,MAAM,CAgBjB;AA0CD;;;;;GAKG;AACH,QAAA,MAAM,OAAO,WALF,MAAM,QACN,MAAM,WACN,cAAc,KACb,MAkCX,CAAC;eAEa,OAAO"}
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/lib/convert.js"],"names":[],"mappings":"AAaA,eAAO,MAAM,QAAQ,qBAGnB,CAAC;AAEH,eAAO,MAAM,SAAS,qBAIpB,CAAC;AAEH,eAAO,MAAM,QAAQ,qBAGnB,CAAC;AAEC,YAAoF,cAAc,GAAxF;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAC,CAAgB;AAAtG,yGAAyG;AAEzG;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAJ3B,MAI2B,EAAE,SAAS,EAHtC,MAGsC,GAFrC,MAAM,CAgBjB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAH3B,MAG2B,GAF1B,MAAM,CAgBjB;AA0CD;;;;;GAKG;AACH,QAAA,MAAM,OAAO,WALF,MAAM,QACN,MAAM,WACN,cAAc,KACb,MAkCX,CAAC;eAEa,OAAO"}
@@ -0,0 +1,38 @@
1
+ import convert from './convert.js';
2
+ export type ConvertOptions = Parameters<typeof convert>[2];
3
+ export type Options = {
4
+ precision?: false | number;
5
+ transformCustomProperties?: boolean;
6
+ } & ConvertOptions & {
7
+ overrideBrowserslist?: string | string[];
8
+ } & import('browserslist').Options;
9
+ export type ReplacementFlags = {
10
+ keepZeroPercent: boolean;
11
+ keepZeroLength: boolean;
12
+ clampAlpha: boolean;
13
+ isAlpha: boolean;
14
+ isFont: boolean;
15
+ };
16
+ /** @param {string} str @return {string} */
17
+ export declare function stripVendorPrefix(str: string): string;
18
+ export type NumericSource = {
19
+ index: number;
20
+ start: number;
21
+ end: number;
22
+ raw: string;
23
+ number: number;
24
+ unit: string;
25
+ hasDecimal: boolean;
26
+ };
27
+ /**
28
+ * @param {string} value
29
+ * @param {ReplacementFlags} flags
30
+ * @param {Options} opts
31
+ * @return {{start: number, end: number, text: string}[]}
32
+ */
33
+ export declare function findReplacements(value: string, flags: ReplacementFlags, opts: Options): {
34
+ start: number;
35
+ end: number;
36
+ text: string;
37
+ }[];
38
+ //# sourceMappingURL=findReplacements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findReplacements.d.ts","sourceRoot":"","sources":["../../src/lib/findReplacements.js"],"names":[],"mappings":"AACA,OAAO,OAMN,MAAM,cAAc,CAAC;AAkDlB,YAAyC,cAAc,GAA7C,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAgB;AACvD,YAA8K,OAAO,GAA3K;IAAC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAAC,yBAAyB,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG,cAAc,GAAG;IAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,GAAG,OAAO,cAAc,EAAE,OAAO,CAAS;AACrL,YAAuH,gBAAgB,GAA7H;IAAC,eAAe,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,CAAkB;AAiE3I,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,GAAG,EADzB,MACyB,GADJ,MAAM,CAQtC;AAgGG,YAAuH,aAAa,GAA1H;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAC,CAAe;AA8CxI;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAL3B,MAK2B,EAAE,KAAK,EAJlC,gBAIkC,EAAE,IAAI,EAHxC,OAGwC,GAFvC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,EAAE,CAqDvD"}
@@ -1,3 +1,3 @@
1
1
  /** @param {string} value @return {boolean} */
2
2
  export declare function syntaxAllowsPercentage(value: string): boolean;
3
- //# sourceMappingURL=parse-syntax.d.ts.map
3
+ //# sourceMappingURL=parseSyntax.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseSyntax.d.ts","sourceRoot":"","sources":["../../src/lib/parseSyntax.js"],"names":[],"mappings":"AA6NA,8CAA8C;AAC9C,wBAAgB,sBAAsB,CAAC,KAAK,EADhC,MACgC,GADT,OAAO,CAYzC"}
@@ -1,20 +1,4 @@
1
- import convert from './convert.js';
2
- export type ConvertOptions = Parameters<typeof convert>[2];
3
- export type Options = {
4
- precision?: false | number;
5
- transformCustomProperties?: boolean;
6
- } & ConvertOptions & {
7
- overrideBrowserslist?: string | string[];
8
- } & import('browserslist').Options;
9
- export type NumericSource = {
10
- index: number;
11
- start: number;
12
- end: number;
13
- raw: string;
14
- number: number;
15
- unit: string;
16
- hasDecimal: boolean;
17
- };
1
+ export type Options = import('./findReplacements.js').Options;
18
2
  /** @param {Options} opts @param {boolean | string[]} supportsIE @param {import('postcss').Declaration} decl @param {Map<string, string>} [cache] @return {void} */
19
3
  export default function transform(opts: Options, supportsIE: boolean | string[], decl: import('postcss').Declaration, cache?: Map<string, string>): void;
20
4
  //# sourceMappingURL=transform.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/lib/transform.js"],"names":[],"mappings":"AACA,OAAO,OAMN,MAAM,cAAc,CAAC;AA2FlB,YAAyC,cAAc,GAA7C,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAgB;AACvD,YAA8K,OAAO,GAA3K;IAAC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAAC,yBAAyB,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG,cAAc,GAAG;IAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,GAAG,OAAO,cAAc,EAAE,OAAO,CAAS;AAkQrL,YAAuH,aAAa,GAA1H;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAC,CAAe;AA6IxI,mKAAmK;AACnK,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,IAAI,EAD1B,OAC0B,EAAE,UAAU,EADhB,OAAO,GAAG,MAAM,EACA,EAAE,IAAI,EADiB,OAAO,SAAS,EAAE,WACnC,EAAE,KAAK,AAD8C,CAAqC,EAA7B,GAAG,CAAC,MAAM,EAAE,MAAM,CACxE,GAD4F,IAAI,CA8B9J"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/lib/transform.js"],"names":[],"mappings":"AAiDI,YAAmD,OAAO,GAAhD,OAAO,uBAAuB,EAAE,OAAO,CAAS;AAoI9D,mKAAmK;AACnK,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,IAAI,EAD1B,OAC0B,EAAE,UAAU,EADhB,OAAO,GAAG,MAAM,EACA,EAAE,IAAI,EADiB,OAAO,SAAS,EAAE,WACnC,EAAE,KAAK,AAD8C,CAAqC,EAA7B,GAAG,CAAC,MAAM,EAAE,MAAM,CACxE,GAD4F,IAAI,CA8B9J"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"parse-syntax.d.ts","sourceRoot":"","sources":["../../src/lib/parse-syntax.js"],"names":[],"mappings":"AA6NA,8CAA8C;AAC9C,wBAAgB,sBAAsB,CAAC,KAAK,EADhC,MACgC,GADT,OAAO,CAYzC"}
File without changes