Skip to content

Commit 6afcb8d

Browse files
committed
Allow replacing <dimension> with a numeric substitution
Before this commit, math functions could only replace CSS dimension types like <length>, <angle>, etc... which are all defined as <dimension>. This type was directly used for type checking the math function. But they must also be allowed to replace a standalone <dimension> in <mf-value> and since f2f2a0f, in <progress-source> and <input-position>. <mf-value> did not need type checking because it is entirely validated against the grammar of the corresponding <mf-name> in a postprocessing action. <progress-source> and <input-position> have <dimension> replaced with <calc-sum> type checked in a postprocessing action. This will be removed in next commit. A math function replacing a standalone <dimension> must simply match one the CSS dimension types that a math function can resolve to.
1 parent d1f28b2 commit 6afcb8d

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

__tests__/value.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,15 +1565,16 @@ describe('<calc()>', () => {
15651565
['<length>', 'calc(1px * 1%)'],
15661566
['<length>', 'calc(1px / 1%)'],
15671567
['<length>', 'calc(1% / 1px)'],
1568+
['<dimension>', 'calc(1n)'],
1569+
['<dimension>', 'calc(1px + 1s)'],
1570+
['<dimension> | <percentage>', 'calc(1px + 1%)'],
15681571
['<percentage>', 'calc(1)'],
15691572
['<percentage>', 'calc(1px)'],
15701573
['<percentage>', 'calc(1% + 1)'],
15711574
['<percentage>', 'calc(1% - 1)'],
15721575
['<percentage>', 'calc(1 / 1%)'],
15731576
['<percentage>', 'calc(1% * 1%)'],
15741577
['<percentage>', 'calc(1% / 1%)'],
1575-
// <dimension> does not match a type that a math function can resolve to
1576-
['<dimension>', 'calc(1n)'],
15771578
// 0 is parsed as <number> in calculations
15781579
['<length>', 'calc(0 + 1px)'],
15791580
['<length>', 'calc(0 - 1px)'],
@@ -1641,6 +1642,7 @@ describe('<calc()>', () => {
16411642
// <number>, <dimension>, <percentage>
16421643
['<number>', 'CALC(1)', 'calc(1)'],
16431644
['<length>', 'calc(1px)'],
1645+
['<dimension>', 'calc(1px)'],
16441646
['<percentage>', 'calc(1%)'],
16451647
// <calc-keyword>
16461648
['<number>', 'calc(e)', `calc(${Math.E.toFixed(6)})`],

lib/parse/replace.js

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
const { colorFunctionKeywords, colorSpaces, colorSpaceKeywords } = require('../values/colors.js')
3+
const { dimensionTypes, getCalculationType, matchNumericType } = require('./types.js')
34
const { findSibling, isProducedBy } = require('../utils/context.js')
4-
const { getCalculationType, matchNumericType } = require('./types.js')
55
const createError = require('../error.js')
66
const { isDelimiter } = require('../utils/value.js')
77
const { simplifyCalculation } = require('./simplify.js')
@@ -37,6 +37,35 @@ function replaceForgivingSelector({ context, input }, parser) {
3737
return null
3838
}
3939

40+
/**
41+
* @param {object} node
42+
* @returns {string|string[]}
43+
*/
44+
function getReplacedNumericType({ definition: { name } }) {
45+
switch (name) {
46+
case '<dimension>':
47+
return dimensionTypes
48+
case '<integer>':
49+
return '<number>'
50+
default:
51+
return name
52+
}
53+
}
54+
55+
/**
56+
* @param {object} node
57+
* @returns {string|null}
58+
*/
59+
function getPercentageResolutionType({ definition: { name }, parent }) {
60+
if (name === '<dimension>' || name === '<percentage>') {
61+
return null
62+
}
63+
if (parent?.definition.type === '|' && parent.definition.value.some(definition => definition.name === '<percentage>')) {
64+
return name
65+
}
66+
return null
67+
}
68+
4069
/**
4170
* @param {object} node
4271
* @param {object} parser
@@ -46,6 +75,12 @@ function replaceForgivingSelector({ context, input }, parser) {
4675
function replaceNumeric(node, parser) {
4776

4877
const { context, definition, input, parent } = node
78+
79+
// Do not try replacing <dimension> produced by a CSS dimension type
80+
if (definition.name === '<dimension>' && parent?.definition.type === 'non-terminal') {
81+
return null
82+
}
83+
4984
const name = input.next().name?.toLowerCase()
5085
const topLevel = !isProducedBy(node, '<calc-value>')
5186

@@ -74,14 +109,11 @@ function replaceNumeric(node, parser) {
74109
if (topLevel && !isProducedBy(node, '<mf-value>')) {
75110

76111
const { name, range } = definition
77-
const replacedType = name === '<integer>' ? '<number>' : name
78-
const hasResolutionType = name !== '<percentage>'
79-
&& parent?.definition.type === '|'
80-
&& parent.definition.value.some(definition => definition.name === '<percentage>')
81-
const resolutionType = hasResolutionType ? name : null
82-
const numericType = getCalculationType(match, resolutionType)
83-
84-
if (matchNumericType(numericType, replacedType, resolutionType)) {
112+
const replacedType = getReplacedNumericType(node)
113+
const resolutionType = getPercentageResolutionType(node)
114+
const type = getCalculationType(match, resolutionType)
115+
116+
if (matchNumericType(type, replacedType, resolutionType)) {
85117
return {
86118
...match,
87119
range,
@@ -176,6 +208,7 @@ module.exports = {
176208
'<angle>': replaceNumeric,
177209
'<calc-keyword>': replaceCalcKeyword,
178210
'<complex-real-selector>': replaceForgivingSelector,
211+
'<dimension>': replaceNumeric,
179212
'<flex>': replaceNumeric,
180213
'<frequency>': replaceNumeric,
181214
'<integer>': replaceNumeric,

0 commit comments

Comments
 (0)