mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
drop unused files (#5453)
This commit is contained in:
parent
ec0562b887
commit
249f6ff596
@ -1,93 +0,0 @@
|
||||
import postcss from 'postcss'
|
||||
import cloneNodes from '../util/cloneNodes'
|
||||
import buildMediaQuery from '../util/buildMediaQuery'
|
||||
import buildSelectorVariant from '../util/buildSelectorVariant'
|
||||
import { tap } from '../util/tap'
|
||||
|
||||
function isLayer(node) {
|
||||
if (Array.isArray(node)) {
|
||||
return node.length === 1 && isLayer(node[0])
|
||||
}
|
||||
return node.type === 'atrule' && node.name === 'layer'
|
||||
}
|
||||
|
||||
function layerNodes(nodes) {
|
||||
return isLayer(nodes) ? nodes[0].nodes : nodes
|
||||
}
|
||||
|
||||
export default function (config) {
|
||||
return function (css) {
|
||||
// Wrap any `responsive` rules with a copy of their parent `layer` to
|
||||
// ensure the layer isn't lost when copying to the `screens` location.
|
||||
css.walkAtRules('layer', (layerAtRule) => {
|
||||
let layer = layerAtRule.params
|
||||
layerAtRule.walkAtRules('responsive', (responsiveAtRule) => {
|
||||
let nestedlayerAtRule = postcss.atRule({
|
||||
name: 'layer',
|
||||
params: layer,
|
||||
})
|
||||
nestedlayerAtRule.prepend(responsiveAtRule.nodes)
|
||||
responsiveAtRule.removeAll()
|
||||
responsiveAtRule.prepend(nestedlayerAtRule)
|
||||
})
|
||||
})
|
||||
|
||||
let {
|
||||
theme: { screens },
|
||||
separator,
|
||||
} = config
|
||||
let responsiveRules = postcss.root()
|
||||
let finalRules = []
|
||||
|
||||
css.walkAtRules('responsive', (atRule) => {
|
||||
let nodes = atRule.nodes
|
||||
responsiveRules.append(...cloneNodes(nodes))
|
||||
|
||||
// If the parent is already a `layer` (this is true for anything coming from
|
||||
// a plugin, including core plugins) we don't want to create a double nested
|
||||
// layer, so only insert the layer children. If there is no parent layer,
|
||||
// preserve the layer information when inserting the nodes.
|
||||
if (isLayer(atRule.parent)) {
|
||||
atRule.before(layerNodes(nodes))
|
||||
} else {
|
||||
atRule.before(nodes)
|
||||
}
|
||||
atRule.remove()
|
||||
})
|
||||
|
||||
for (let [screen, value] of Object.entries(screens ?? {})) {
|
||||
let mediaQuery = postcss.atRule({
|
||||
name: 'media',
|
||||
params: buildMediaQuery(value),
|
||||
})
|
||||
|
||||
mediaQuery.append(
|
||||
tap(responsiveRules.clone(), (clonedRoot) => {
|
||||
clonedRoot.walkRules((rule) => {
|
||||
rule.selectors = rule.selectors.map((selector) =>
|
||||
buildSelectorVariant(selector, screen, separator, (message) => {
|
||||
throw rule.error(message)
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
finalRules.push(mediaQuery)
|
||||
}
|
||||
|
||||
let hasScreenRules = finalRules.some((i) => i.nodes.length !== 0)
|
||||
|
||||
css.walkAtRules('tailwind', (atRule) => {
|
||||
if (atRule.params !== 'screens') {
|
||||
return
|
||||
}
|
||||
|
||||
if (hasScreenRules) {
|
||||
atRule.before(finalRules)
|
||||
}
|
||||
|
||||
atRule.remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import parser from 'postcss-selector-parser'
|
||||
import { tap } from './tap'
|
||||
import { useMemo } from './useMemo'
|
||||
|
||||
const buildSelectorVariant = useMemo(
|
||||
(selector, variantName, separator, onError = () => {}) => {
|
||||
return parser((selectors) => {
|
||||
tap(selectors.first.filter(({ type }) => type === 'class').pop(), (classSelector) => {
|
||||
if (classSelector === undefined) {
|
||||
onError('Variant cannot be generated because selector contains no classes.')
|
||||
return
|
||||
}
|
||||
|
||||
classSelector.value = `${variantName}${separator}${classSelector.value}`
|
||||
})
|
||||
}).processSync(selector)
|
||||
},
|
||||
(selector, variantName, separator) => [selector, variantName, separator].join('||')
|
||||
)
|
||||
|
||||
export default buildSelectorVariant
|
||||
@ -1,22 +0,0 @@
|
||||
export function disposables() {
|
||||
let disposables = []
|
||||
|
||||
let api = {
|
||||
add(cb) {
|
||||
disposables.push(cb)
|
||||
|
||||
return () => {
|
||||
let idx = disposables.indexOf(cb)
|
||||
if (idx !== -1) disposables.splice(idx, 1)
|
||||
}
|
||||
},
|
||||
dispose() {
|
||||
disposables.splice(0).forEach((dispose) => dispose())
|
||||
},
|
||||
}
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
// A shared disposables collection
|
||||
export let shared = disposables()
|
||||
@ -1,47 +0,0 @@
|
||||
import postcss from 'postcss'
|
||||
import selectorParser from 'postcss-selector-parser'
|
||||
import { useMemo } from './useMemo'
|
||||
|
||||
let classNameParser = selectorParser((selectors) => {
|
||||
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
||||
})
|
||||
|
||||
let getClassNameFromSelector = useMemo(
|
||||
(selector) => classNameParser.transformSync(selector),
|
||||
(selector) => selector
|
||||
)
|
||||
|
||||
export default function generateVariantFunction(generator, options = {}) {
|
||||
return {
|
||||
options,
|
||||
handler: (container, config) => {
|
||||
let cloned = postcss.root({ nodes: container.clone().nodes })
|
||||
|
||||
container.before(
|
||||
(
|
||||
generator({
|
||||
container: cloned,
|
||||
separator: config.separator,
|
||||
modifySelectors: (modifierFunction) => {
|
||||
cloned.each((rule) => {
|
||||
if (rule.type !== 'rule') {
|
||||
return
|
||||
}
|
||||
|
||||
rule.selectors = rule.selectors.map((selector) => {
|
||||
return modifierFunction({
|
||||
get className() {
|
||||
return getClassNameFromSelector(selector)
|
||||
},
|
||||
selector,
|
||||
})
|
||||
})
|
||||
})
|
||||
return cloned
|
||||
},
|
||||
}) ?? cloned
|
||||
).nodes
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export default function (importantVal, selector) {
|
||||
return `${importantVal} ${selector}`
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
export default function prefixNegativeModifiers(base, modifier) {
|
||||
if (modifier === '-') {
|
||||
return `-${base}`
|
||||
} else if (modifier.startsWith('-')) {
|
||||
return `-${base}-${modifier.slice(1)}`
|
||||
} else {
|
||||
return `${base}-${modifier}`
|
||||
}
|
||||
}
|
||||
@ -1,165 +0,0 @@
|
||||
import dlv from 'dlv'
|
||||
import postcss from 'postcss'
|
||||
import Node from 'postcss/lib/node'
|
||||
import escapeClassName from './escapeClassName'
|
||||
import generateVariantFunction from './generateVariantFunction'
|
||||
import parseObjectStyles from './parseObjectStyles'
|
||||
import prefixSelector from './prefixSelector'
|
||||
import wrapWithVariants from './wrapWithVariants'
|
||||
import cloneNodes from './cloneNodes'
|
||||
import transformThemeValue from './transformThemeValue'
|
||||
import nameClass from './nameClass'
|
||||
import isKeyframeRule from './isKeyframeRule'
|
||||
import { toPath } from './toPath'
|
||||
import { defaults } from './defaults'
|
||||
|
||||
function parseStyles(styles) {
|
||||
if (!Array.isArray(styles)) {
|
||||
return parseStyles([styles])
|
||||
}
|
||||
|
||||
return styles.flatMap((style) => (style instanceof Node ? style : parseObjectStyles(style)))
|
||||
}
|
||||
|
||||
function wrapWithLayer(rules, layer) {
|
||||
return postcss
|
||||
.atRule({ name: 'layer', params: layer })
|
||||
.append(cloneNodes(Array.isArray(rules) ? rules : [rules]))
|
||||
}
|
||||
|
||||
export default function (plugins, config) {
|
||||
const pluginBaseStyles = []
|
||||
const pluginComponents = []
|
||||
const pluginUtilities = []
|
||||
const pluginVariantGenerators = {}
|
||||
|
||||
const applyConfiguredPrefix = (selector) => {
|
||||
return prefixSelector(config.prefix, selector)
|
||||
}
|
||||
|
||||
function addUtilities(utilities, options) {
|
||||
const defaultOptions = {
|
||||
variants: [],
|
||||
respectPrefix: true,
|
||||
respectImportant: true,
|
||||
}
|
||||
|
||||
options = Array.isArray(options)
|
||||
? Object.assign({}, defaultOptions, { variants: options })
|
||||
: defaults(options, defaultOptions)
|
||||
|
||||
const styles = postcss.root({ nodes: parseStyles(utilities) })
|
||||
|
||||
styles.walkRules((rule) => {
|
||||
if (options.respectPrefix && !isKeyframeRule(rule)) {
|
||||
rule.selector = applyConfiguredPrefix(rule.selector)
|
||||
}
|
||||
|
||||
if (options.respectImportant && config.important) {
|
||||
rule.__tailwind = {
|
||||
...rule.__tailwind,
|
||||
important: config.important,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pluginUtilities.push(
|
||||
wrapWithLayer(wrapWithVariants(styles.nodes, options.variants), 'utilities')
|
||||
)
|
||||
}
|
||||
|
||||
const getConfigValue = (path, defaultValue) => (path ? dlv(config, path, defaultValue) : config)
|
||||
|
||||
plugins.forEach((plugin) => {
|
||||
if (plugin.__isOptionsFunction) {
|
||||
plugin = plugin()
|
||||
}
|
||||
|
||||
const handler = typeof plugin === 'function' ? plugin : plugin?.handler ?? (() => {})
|
||||
|
||||
handler({
|
||||
postcss,
|
||||
config: getConfigValue,
|
||||
theme: (path, defaultValue) => {
|
||||
let [pathRoot, ...subPaths] = toPath(path)
|
||||
let value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
|
||||
|
||||
return transformThemeValue(pathRoot)(value)
|
||||
},
|
||||
corePlugins: (path) => {
|
||||
if (Array.isArray(config.corePlugins)) {
|
||||
return config.corePlugins.includes(path)
|
||||
}
|
||||
|
||||
return getConfigValue(`corePlugins.${path}`, true)
|
||||
},
|
||||
variants: (path, defaultValue) => {
|
||||
if (Array.isArray(config.variants)) {
|
||||
return config.variants
|
||||
}
|
||||
|
||||
return getConfigValue(`variants.${path}`, defaultValue)
|
||||
},
|
||||
e: escapeClassName,
|
||||
prefix: applyConfiguredPrefix,
|
||||
addUtilities,
|
||||
matchUtilities: (matches, { values, variants, respectPrefix, respectImportant }) => {
|
||||
let modifierValues = Object.entries(values || {})
|
||||
|
||||
let result = Object.entries(matches).flatMap(([name, utilityFunction]) => {
|
||||
return modifierValues
|
||||
.map(([modifier, value]) => {
|
||||
let declarations = utilityFunction(value, {
|
||||
includeRules(rules, options) {
|
||||
addUtilities(rules, options)
|
||||
},
|
||||
})
|
||||
|
||||
if (!declarations) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
[nameClass(name, modifier)]: declarations,
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
})
|
||||
|
||||
addUtilities(result, { variants, respectPrefix, respectImportant })
|
||||
},
|
||||
addComponents: (components, options) => {
|
||||
const defaultOptions = { variants: [], respectPrefix: true }
|
||||
|
||||
options = Array.isArray(options)
|
||||
? Object.assign({}, defaultOptions, { variants: options })
|
||||
: defaults(options, defaultOptions)
|
||||
|
||||
const styles = postcss.root({ nodes: parseStyles(components) })
|
||||
|
||||
styles.walkRules((rule) => {
|
||||
if (options.respectPrefix && !isKeyframeRule(rule)) {
|
||||
rule.selector = applyConfiguredPrefix(rule.selector)
|
||||
}
|
||||
})
|
||||
|
||||
pluginComponents.push(
|
||||
wrapWithLayer(wrapWithVariants(styles.nodes, options.variants), 'components')
|
||||
)
|
||||
},
|
||||
addBase: (baseStyles) => {
|
||||
pluginBaseStyles.push(wrapWithLayer(parseStyles(baseStyles), 'base'))
|
||||
},
|
||||
addVariant: (name, generator, options = {}) => {
|
||||
pluginVariantGenerators[name] = generateVariantFunction(generator, options)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
base: pluginBaseStyles,
|
||||
components: pluginComponents,
|
||||
utilities: pluginUtilities,
|
||||
variantGenerators: pluginVariantGenerators,
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
import { shared } from './disposables'
|
||||
|
||||
export function useMemo(cb, keyResolver) {
|
||||
let cache = new Map()
|
||||
|
||||
function clearCache() {
|
||||
cache.clear()
|
||||
shared.add(clearCache)
|
||||
}
|
||||
|
||||
shared.add(clearCache)
|
||||
|
||||
return (...args) => {
|
||||
let key = keyResolver(...args)
|
||||
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key)
|
||||
}
|
||||
|
||||
let result = cb(...args)
|
||||
cache.set(key, result)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import valueParser from 'postcss-value-parser'
|
||||
|
||||
export default function usesCustomProperties(value) {
|
||||
let foundCustomProperty = false
|
||||
|
||||
valueParser(value).walk((node) => {
|
||||
if (node.type === 'function' && node.value === 'var') {
|
||||
foundCustomProperty = true
|
||||
}
|
||||
return !foundCustomProperty
|
||||
})
|
||||
|
||||
return foundCustomProperty
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import postcss from 'postcss'
|
||||
import cloneNodes from './cloneNodes'
|
||||
|
||||
export default function wrapWithVariants(rules, variants) {
|
||||
let foundVariantAtRule = false
|
||||
|
||||
postcss.root({ nodes: rules }).walkAtRules('variants', () => {
|
||||
foundVariantAtRule = true
|
||||
})
|
||||
|
||||
if (foundVariantAtRule) {
|
||||
return cloneNodes(rules)
|
||||
}
|
||||
|
||||
return postcss
|
||||
.atRule({
|
||||
name: 'variants',
|
||||
params: variants.join(', '),
|
||||
})
|
||||
.append(cloneNodes(Array.isArray(rules) ? rules : [rules]))
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import prefixNegativeModifiers from '../src/util/prefixNegativeModifiers'
|
||||
|
||||
test('it does not prefix classes using standard syntax', () => {
|
||||
expect(prefixNegativeModifiers('base', 'modifier')).toEqual('base-modifier')
|
||||
})
|
||||
|
||||
test('it prefixes classes using negative syntax', () => {
|
||||
expect(prefixNegativeModifiers('base', '-modifier')).toEqual('-base-modifier')
|
||||
})
|
||||
|
||||
test('it prefixes classes and omits suffix using default negative syntax', () => {
|
||||
expect(prefixNegativeModifiers('base', '-')).toEqual('-base')
|
||||
})
|
||||
@ -1,440 +0,0 @@
|
||||
import postcss from 'postcss'
|
||||
import plugin from '../src/lib/substituteResponsiveAtRules'
|
||||
import config from '../stubs/defaultConfig.stub.js'
|
||||
|
||||
function run(input, opts = config) {
|
||||
return postcss([plugin(opts)]).process(input, { from: undefined })
|
||||
}
|
||||
|
||||
test('it can generate responsive variants', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.banana { color: yellow; }
|
||||
.chocolate { color: brown; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.banana { color: yellow; }
|
||||
.chocolate { color: brown; }
|
||||
@media (min-width: 500px) {
|
||||
.sm\\:banana { color: yellow; }
|
||||
.sm\\:chocolate { color: brown; }
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
.md\\:banana { color: yellow; }
|
||||
.md\\:chocolate { color: brown; }
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
.lg\\:banana { color: yellow; }
|
||||
.lg\\:chocolate { color: brown; }
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('it can generate responsive variants with a custom separator', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.banana { color: yellow; }
|
||||
.chocolate { color: brown; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.banana { color: yellow; }
|
||||
.chocolate { color: brown; }
|
||||
@media (min-width: 500px) {
|
||||
.sm__banana { color: yellow; }
|
||||
.sm__chocolate { color: brown; }
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
.md__banana { color: yellow; }
|
||||
.md__chocolate { color: brown; }
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
.lg__banana { color: yellow; }
|
||||
.lg__chocolate { color: brown; }
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: '__',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('it can generate responsive variants when classes have non-standard characters', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.hover\\:banana { color: yellow; }
|
||||
.chocolate-2\\.5 { color: brown; }
|
||||
.group:hover .group-hover\\:toast { color: black; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.hover\\:banana { color: yellow; }
|
||||
.chocolate-2\\.5 { color: brown; }
|
||||
.group:hover .group-hover\\:toast { color: black; }
|
||||
@media (min-width: 500px) {
|
||||
.sm\\:hover\\:banana { color: yellow; }
|
||||
.sm\\:chocolate-2\\.5 { color: brown; }
|
||||
.group:hover .sm\\:group-hover\\:toast { color: black; }
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
.md\\:hover\\:banana { color: yellow; }
|
||||
.md\\:chocolate-2\\.5 { color: brown; }
|
||||
.group:hover .md\\:group-hover\\:toast { color: black; }
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
.lg\\:hover\\:banana { color: yellow; }
|
||||
.lg\\:chocolate-2\\.5 { color: brown; }
|
||||
.group:hover .lg\\:group-hover\\:toast { color: black; }
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('responsive variants are grouped', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.banana { color: yellow; }
|
||||
}
|
||||
|
||||
.apple { color: red; }
|
||||
|
||||
@responsive {
|
||||
.chocolate { color: brown; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.banana { color: yellow; }
|
||||
.apple { color: red; }
|
||||
.chocolate { color: brown; }
|
||||
@media (min-width: 500px) {
|
||||
.sm\\:banana { color: yellow; }
|
||||
.sm\\:chocolate { color: brown; }
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
.md\\:banana { color: yellow; }
|
||||
.md\\:chocolate { color: brown; }
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
.lg\\:banana { color: yellow; }
|
||||
.lg\\:chocolate { color: brown; }
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('it can generate responsive variants for nested at-rules', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.banana { color: yellow; }
|
||||
|
||||
@supports(display: grid) {
|
||||
.grid\\:banana { color: blue; }
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.banana {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
@supports(display: grid) {
|
||||
.grid\\:banana {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 500px) {
|
||||
.sm\\:banana {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
@supports(display: grid) {
|
||||
.sm\\:grid\\:banana {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.lg\\:banana {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
@supports(display: grid) {
|
||||
.lg\\:grid\\:banana {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('it can generate responsive variants for deeply nested at-rules', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.banana { color: yellow; }
|
||||
|
||||
@supports(display: grid) {
|
||||
@supports(display: flex) {
|
||||
.flex-grid\\:banana { color: blue; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.banana {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
@supports(display: grid) {
|
||||
@supports(display: flex) {
|
||||
.flex-grid\\:banana {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 500px) {
|
||||
.sm\\:banana {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
@supports(display: grid) {
|
||||
@supports(display: flex) {
|
||||
.sm\\:flex-grid\\:banana {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.lg\\:banana {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
@supports(display: grid) {
|
||||
@supports(display: flex) {
|
||||
.lg\\:flex-grid\\:banana {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('screen prefix is only applied to the last class in a selector', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.banana li * .sandwich #foo > div { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.banana li * .sandwich #foo > div { color: yellow; }
|
||||
@media (min-width: 500px) {
|
||||
.banana li * .sm\\:sandwich #foo > div { color: yellow; }
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
.banana li * .md\\:sandwich #foo > div { color: yellow; }
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
.banana li * .lg\\:sandwich #foo > div { color: yellow; }
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('responsive variants are generated for all selectors in a rule', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.foo, .bar { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
.foo, .bar { color: yellow; }
|
||||
@media (min-width: 500px) {
|
||||
.sm\\:foo, .sm\\:bar { color: yellow; }
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
.md\\:foo, .md\\:bar { color: yellow; }
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
.lg\\:foo, .lg\\:bar { color: yellow; }
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).then((result) => {
|
||||
expect(result.css).toMatchCss(output)
|
||||
expect(result.warnings().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
test('selectors with no classes cannot be made responsive', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
div { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
expect.assertions(1)
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).catch((e) => {
|
||||
expect(e).toMatchObject({ name: 'CssSyntaxError' })
|
||||
})
|
||||
})
|
||||
|
||||
test('all selectors in a rule must contain classes', () => {
|
||||
const input = `
|
||||
@responsive {
|
||||
.foo, div { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
expect.assertions(1)
|
||||
return run(input, {
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '500px',
|
||||
md: '750px',
|
||||
lg: '1000px',
|
||||
},
|
||||
},
|
||||
separator: ':',
|
||||
}).catch((e) => {
|
||||
expect(e).toMatchObject({ name: 'CssSyntaxError' })
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user