mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Improve type checking for formal syntax (#9448)
* Improve type checking for formal syntax * Add test * Change order of test class name * fix failing tests * prefer `position` over `size` for backwards compatibility reasons Previously `bg-[10px_10%]` generated `background-position: 10px 10%` before we introduced the fallback plugins. Therefore we should prefer `position` over `size` as the default for backwards compatibility. * update changelog * ensure correct order Thanks Prettier! * update changelog Co-authored-by: lzt1008 <lzt1008@live.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: liangzhengtai <liangzhengtai_i@didiglobal.com>
This commit is contained in:
parent
94d6e7299a
commit
727de668fd
@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Revert change that only listened for stdin close on TTYs ([#9331](https://github.com/tailwindlabs/tailwindcss/pull/9331))
|
||||
- Ignore unset values (like `null` or `undefined`) when resolving the classList for intellisense ([#9385](https://github.com/tailwindlabs/tailwindcss/pull/9385))
|
||||
- Implement fallback plugins when arbitrary values result in css from multiple plugins ([#9376](https://github.com/tailwindlabs/tailwindcss/pull/9376))
|
||||
- Improve type checking for formal syntax ([#9349](https://github.com/tailwindlabs/tailwindcss/pull/9349), [#9448](https://github.com/tailwindlabs/tailwindcss/pull/9448))
|
||||
|
||||
## [3.1.8] - 2022-08-05
|
||||
|
||||
|
||||
@ -1482,7 +1482,7 @@ export let corePlugins = {
|
||||
},
|
||||
|
||||
backgroundSize: createUtilityPlugin('backgroundSize', [['bg', ['background-size']]], {
|
||||
type: ['lookup', ['length', { preferOnConflict: true }], 'percentage'],
|
||||
type: ['lookup', 'length', 'percentage', 'size'],
|
||||
}),
|
||||
|
||||
backgroundAttachment: ({ addUtilities }) => {
|
||||
@ -1503,7 +1503,7 @@ export let corePlugins = {
|
||||
},
|
||||
|
||||
backgroundPosition: createUtilityPlugin('backgroundPosition', [['bg', ['background-position']]], {
|
||||
type: ['lookup', 'position'],
|
||||
type: ['lookup', ['position', { preferOnConflict: true }]],
|
||||
}),
|
||||
|
||||
backgroundRepeat: ({ addUtilities }) => {
|
||||
|
||||
@ -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) {
|
||||
@ -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 = [
|
||||
@ -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'])
|
||||
|
||||
@ -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) => {
|
||||
@ -162,6 +163,7 @@ export let typeMap = {
|
||||
'absolute-size': guess(absoluteSize),
|
||||
'relative-size': guess(relativeSize),
|
||||
shadow: guess(shadow),
|
||||
size: guess(backgroundSize),
|
||||
}
|
||||
|
||||
let supportedTypes = Object.keys(typeMap)
|
||||
|
||||
34
src/util/validateFormalSyntax.js
Normal file
34
src/util/validateFormalSyntax.js
Normal file
@ -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) {
|
||||
let keywordValues = ['cover', 'contain']
|
||||
// the <length-percentage> type will probably be a css function
|
||||
// so we have to use `splitAtTopLevelOnly`
|
||||
return splitAtTopLevelOnly(value, ',').every((part) => {
|
||||
let 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')
|
||||
})
|
||||
}
|
||||
@ -285,7 +285,7 @@ it('should pick the fallback plugin when arbitrary values collide', () => {
|
||||
}
|
||||
|
||||
.bg-\[200px_100px\] {
|
||||
background-size: 200px 100px;
|
||||
background-position: 200px 100px;
|
||||
}
|
||||
`)
|
||||
})
|
||||
@ -311,7 +311,7 @@ it('should warn and not generate if arbitrary values are ambiguous (without fall
|
||||
plugins: [
|
||||
function ({ matchUtilities }) {
|
||||
matchUtilities({ foo: (value) => ({ value }) }, { type: ['position'] })
|
||||
matchUtilities({ foo: (value) => ({ value }) }, { type: ['length'] })
|
||||
matchUtilities({ foo: (value) => ({ value }) }, { type: ['size'] })
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -463,3 +463,77 @@ 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).toMatchFormattedCss(css`
|
||||
.bg-\[50px_10\%\] {
|
||||
background-position: 50px 10%;
|
||||
}
|
||||
.bg-\[50\%_10\%\] {
|
||||
background-position: 50% 10%;
|
||||
}
|
||||
.bg-\[50px_10px\] {
|
||||
background-position: 50px 10px;
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
it('can explicitly specify type for percentage and length', () => {
|
||||
let config = {
|
||||
content: [
|
||||
{ raw: html`<div class="bg-[size:50px_10%] bg-[50px_10px] bg-[position:50%_10%]"></div>` },
|
||||
],
|
||||
corePlugins: { preflight: false },
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.bg-\[size\:50px_10\%\] {
|
||||
background-size: 50px 10%;
|
||||
}
|
||||
.bg-\[50px_10px\] {
|
||||
background-position: 50px 10px;
|
||||
}
|
||||
.bg-\[position\:50\%_10\%\] {
|
||||
background-position: 50% 10%;
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user