Skip to content

Commit

Permalink
Improve type checking for formal syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
lzt1008 committed Sep 17, 2022
1 parent 4fddd2d commit b37a44a
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/corePlugins.js
Expand Up @@ -1481,7 +1481,7 @@ export let corePlugins = {
},

backgroundSize: createUtilityPlugin('backgroundSize', [['bg', ['background-size']]], {
type: ['lookup', 'length', 'percentage'],
type: ['lookup', 'length', 'percentage', 'size'],
}),

backgroundAttachment: ({ addUtilities }) => {
Expand Down
22 changes: 11 additions & 11 deletions src/util/dataTypes.js
Expand Up @@ -6,6 +6,10 @@ let cssFunctions = ['min', 'max', 'clamp', 'calc']

// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types

function isCSSFunction(value) {
return cssFunctions.some((fn) => new RegExp(`^${fn}\\(.*\\)`).test(value))
}

// This is not a data type, but rather a function that can normalize the
// correct values.
export function normalize(value, isRoot = true) {
Expand Down Expand Up @@ -55,13 +59,11 @@ export function url(value) {
}

export function number(value) {
return !isNaN(Number(value)) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?`).test(value))
return !isNaN(Number(value)) || isCSSFunction(value)
}

export function percentage(value) {
return splitAtTopLevelOnly(value, '_').every((part) => {
return /%$/g.test(part) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?%`).test(part))
})
return (value.endsWith('%') && number(value.slice(0, -1))) || isCSSFunction(value)
}

let lengthUnits = [
Expand All @@ -84,13 +86,11 @@ let lengthUnits = [
]
let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
export function length(value) {
return splitAtTopLevelOnly(value, '_').every((part) => {
return (
part === '0' ||
new RegExp(`${lengthUnitsPattern}$`).test(part) ||
cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?${lengthUnitsPattern}`).test(part))
)
})
return (
value === '0' ||
new RegExp(`^[+-]?[0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`).test(value) ||
isCSSFunction(value)
)
}

let lineWidths = new Set(['thin', 'medium', 'thick'])
Expand Down
2 changes: 2 additions & 0 deletions src/util/pluginUtils.js
Expand Up @@ -18,6 +18,7 @@ import {
shadow,
} from './dataTypes'
import negateValue from './negateValue'
import { backgroundSize } from './validateFormalSyntax'

export function updateAllClasses(selectors, updateClass) {
let parser = selectorParser((selectors) => {
Expand Down Expand Up @@ -162,6 +163,7 @@ let typeMap = {
'absolute-size': guess(absoluteSize),
'relative-size': guess(relativeSize),
shadow: guess(shadow),
size: guess(backgroundSize),
}

let supportedTypes = Object.keys(typeMap)
Expand Down
34 changes: 34 additions & 0 deletions src/util/validateFormalSyntax.js
@@ -0,0 +1,34 @@
import { length, percentage } from './dataTypes'
import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'

/**
*
* https://developer.mozilla.org/en-US/docs/Web/CSS/background-size#formal_syntax
*
* background-size =
* <bg-size>#
*
* <bg-size> =
* [ <length-percentage [0,∞]> | auto ]{1,2} |
* cover |
* contain
*
* <length-percentage> =
* <length> |
* <percentage>
*
* @param {string} value
*/
export function backgroundSize(value) {
const keywordValues = ['cover', 'contain']
// the <length-percentage> type will probably be a css function
// so we have to use `splitAtTopLevelOnly`
return splitAtTopLevelOnly(value, ',').every((part) => {
const sizes = splitAtTopLevelOnly(part, '_').filter(Boolean)
if (sizes.length === 1 && keywordValues.includes(sizes[0])) return true

if (sizes.length !== 1 && sizes.length !== 2) return false

return sizes.every((size) => length(size) || percentage(size) || size === 'auto')
})
}
36 changes: 36 additions & 0 deletions tests/arbitrary-values.test.js
Expand Up @@ -416,3 +416,39 @@ it('should correctly validate each part when checking for `percentage` data type
`)
})
})

it('should correctly validate background size', () => {
let config = {
content: [{ raw: html`<div class="bg-[auto_auto,cover,_contain,10px,10px_10%]"></div>` }],
corePlugins: { preflight: false },
plugins: [],
}

let input = css`
@tailwind utilities;
`

return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.bg-\[auto_auto\2c cover\2c _contain\2c 10px\2c 10px_10\%\] {
background-size: auto auto, cover, contain, 10px, 10px 10%;
}
`)
})
})

it('should correctly validate combination of percentage and length', () => {
let config = {
content: [{ raw: html`<div class="bg-[50px_10%] bg-[50%_10%] bg-[50px_10px]"></div>` }],
corePlugins: { preflight: false },
plugins: [],
}

let input = css`
@tailwind utilities;
`

return run(input, config).then((result) => {
expect(result.css.trim()).toHaveLength(0)
})
})

0 comments on commit b37a44a

Please sign in to comment.