Remove AOT (#5340)
* make `jit` mode the default when no mode is specified
* unify JIT and AOT codepaths
* ensure `Object.entries` on undefined doesn't break
It could be that sometimes you don't have values in your config (e.g.: `presets: []`), this in turn will break some plugins where we assume we have a value.
* drop AOT specific tests
These tests are all covered by JIT mode already and were AOT specific.
* simplify tests, and add a few
Some of the tests were written for AOT specifically, some were missing. We also updated the way we write those tests, essentially making Tailwind a blackbox, by testing against the final output.
Now that JIT mode is the default, this is super fast because we only generate what is used, instead of partially testing in a 3MB file or building it all, then purging.
* add some todo's to make sure we warn in a few cases
* make `darkMode: 'media'`, the default
This also includes moving dark mode tests to its own dedicated file.
* remove PostCSS 7 compat mode
* update CLI to be JIT-first
* fix integration tests
This is not a _real_ fix, but it does solve the broken test for now.
* warn when using @responsive or @variants
* remove the JIT preview warning
* remove AOT-only code paths
* remove all `mode: 'jit'` blocks
Also remove `variants: {}` since they are not useful in `JIT` mode
anymore.
* drop unused dependencies
* rename `purge` to `content`
* remove static CDN builds
* mark `--purge` as deprecated in the CLI
This will still work, but a warning will be printed and it won't show up
in the `--help` output.
* cleanup nesting plugin
We don't have to duplicate it anymore since there is no PostCSS 7
version anymore.
* make sure integration tests run in band
* cleanup folder structure
* make sure nesting folder is available
* simplify resolving of purge/content information
This commit is contained in:
parent
911e755056
commit
691ed02f63
141 changed files with 1991 additions and 10609 deletions
142
src/cli.js
142
src/cli.js
|
|
@ -9,11 +9,11 @@ import fs from 'fs'
|
|||
import postcssrc from 'postcss-load-config'
|
||||
import { cosmiconfig } from 'cosmiconfig'
|
||||
import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
|
||||
import tailwindJit from './jit/processTailwindFeatures'
|
||||
import tailwindAot from './processTailwindFeatures'
|
||||
import tailwind from './processTailwindFeatures'
|
||||
import resolveConfigInternal from '../resolveConfig'
|
||||
import fastGlob from 'fast-glob'
|
||||
import getModuleDependencies from './lib/getModuleDependencies'
|
||||
import log from './util/log'
|
||||
import packageJson from '../package.json'
|
||||
|
||||
let env = {
|
||||
|
|
@ -86,7 +86,9 @@ function help({ message, usage, commands, options }) {
|
|||
|
||||
console.log()
|
||||
console.log('Options:')
|
||||
for (let { flags, description } of Object.values(groupedOptions)) {
|
||||
for (let { flags, description, deprecated } of Object.values(groupedOptions)) {
|
||||
if (deprecated) continue
|
||||
|
||||
if (flags.length === 1) {
|
||||
console.log(
|
||||
' '.repeat(indent + 4 /* 4 = "-i, ".length */),
|
||||
|
|
@ -126,7 +128,6 @@ let commands = {
|
|||
init: {
|
||||
run: init,
|
||||
args: {
|
||||
'--jit': { type: Boolean, description: 'Initialize for JIT mode' },
|
||||
'--full': { type: Boolean, description: 'Initialize a full `tailwind.config.js` file' },
|
||||
'--postcss': { type: Boolean, description: 'Initialize a `postcss.config.js` file' },
|
||||
'-f': '--full',
|
||||
|
|
@ -139,8 +140,14 @@ let commands = {
|
|||
'--input': { type: String, description: 'Input file' },
|
||||
'--output': { type: String, description: 'Output file' },
|
||||
'--watch': { type: Boolean, description: 'Watch for changes and rebuild as needed' },
|
||||
'--jit': { type: Boolean, description: 'Build using JIT mode' },
|
||||
'--purge': { type: String, description: 'Content paths to use for removing unused classes' },
|
||||
'--content': {
|
||||
type: String,
|
||||
description: 'Content paths to use for removing unused classes',
|
||||
},
|
||||
'--purge': {
|
||||
type: String,
|
||||
deprecated: true,
|
||||
},
|
||||
'--postcss': {
|
||||
type: oneOf(String, Boolean),
|
||||
description: 'Load custom PostCSS configuration',
|
||||
|
|
@ -306,15 +313,6 @@ function init() {
|
|||
// Change colors import
|
||||
stubFile = stubFile.replace('../colors', 'tailwindcss/colors')
|
||||
|
||||
// --jit mode
|
||||
if (args['--jit']) {
|
||||
// Add jit mode
|
||||
stubFile = stubFile.replace('module.exports = {', "module.exports = {\n mode: 'jit',")
|
||||
|
||||
// Deleting variants
|
||||
stubFile = stubFile.replace(/variants: {(.*)},\n /gs, '')
|
||||
}
|
||||
|
||||
fs.writeFileSync(tailwindConfigLocation, stubFile, 'utf8')
|
||||
|
||||
messages.push(`Created Tailwind CSS config file: ${path.basename(tailwindConfigLocation)}`)
|
||||
|
|
@ -420,39 +418,21 @@ async function build() {
|
|||
let resolvedConfig = resolveConfigInternal(config)
|
||||
|
||||
if (args['--purge']) {
|
||||
resolvedConfig.purge = {
|
||||
enabled: true,
|
||||
content: args['--purge'].split(/(?<!{[^}]+),/),
|
||||
log.warn(['The `--purge` flag has been deprecated.', 'Please use `--content` instead.'])
|
||||
if (!args['--content']) {
|
||||
args['--content'] = ['--purge']
|
||||
}
|
||||
}
|
||||
|
||||
if (args['--jit']) {
|
||||
resolvedConfig.mode = 'jit'
|
||||
if (args['--content']) {
|
||||
resolvedConfig.content = args['--content'].split(/(?<!{[^}]+),/)
|
||||
}
|
||||
|
||||
return resolvedConfig
|
||||
}
|
||||
|
||||
function extractContent(config) {
|
||||
let content = Array.isArray(config.purge) ? config.purge : config.purge.content
|
||||
|
||||
return content.concat(
|
||||
(config.purge?.safelist ?? []).map((content) => {
|
||||
if (typeof content === 'string') {
|
||||
return { raw: content, extension: 'html' }
|
||||
}
|
||||
|
||||
if (content instanceof RegExp) {
|
||||
throw new Error(
|
||||
"Values inside 'purge.safelist' can only be of type 'string', found 'regex'."
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Values inside 'purge.safelist' can only be of type 'string', found '${typeof content}'.`
|
||||
)
|
||||
})
|
||||
)
|
||||
return config.content.content.concat(config.content.safelist)
|
||||
}
|
||||
|
||||
function extractFileGlobs(config) {
|
||||
|
|
@ -473,7 +453,7 @@ async function build() {
|
|||
function getChangedContent(config) {
|
||||
let changedContent = []
|
||||
|
||||
// Resolve globs from the purge config
|
||||
// Resolve globs from the content config
|
||||
let globs = extractFileGlobs(config)
|
||||
let files = fastGlob.sync(globs)
|
||||
|
||||
|
|
@ -496,26 +476,18 @@ async function build() {
|
|||
let config = resolveConfig()
|
||||
let changedContent = getChangedContent(config)
|
||||
|
||||
let tailwindPlugin =
|
||||
config.mode === 'jit'
|
||||
? () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
Once(root, { result }) {
|
||||
tailwindJit(({ createContext }) => {
|
||||
return () => {
|
||||
return createContext(config, changedContent)
|
||||
}
|
||||
})(root, result)
|
||||
},
|
||||
let tailwindPlugin = () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
Once(root, { result }) {
|
||||
tailwind(({ createContext }) => {
|
||||
return () => {
|
||||
return createContext(config, changedContent)
|
||||
}
|
||||
}
|
||||
: () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
plugins: [tailwindAot(() => config, configPath)],
|
||||
}
|
||||
}
|
||||
})(root, result)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
tailwindPlugin.postcss = true
|
||||
|
||||
|
|
@ -637,39 +609,31 @@ async function build() {
|
|||
async function rebuild(config) {
|
||||
env.DEBUG && console.time('Finished in')
|
||||
|
||||
let tailwindPlugin =
|
||||
config.mode === 'jit'
|
||||
? () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
Once(root, { result }) {
|
||||
env.DEBUG && console.time('Compiling CSS')
|
||||
tailwindJit(({ createContext }) => {
|
||||
console.error()
|
||||
console.error('Rebuilding...')
|
||||
let tailwindPlugin = () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
Once(root, { result }) {
|
||||
env.DEBUG && console.time('Compiling CSS')
|
||||
tailwind(({ createContext }) => {
|
||||
console.error()
|
||||
console.error('Rebuilding...')
|
||||
|
||||
return () => {
|
||||
if (context !== null) {
|
||||
context.changedContent = changedContent.splice(0)
|
||||
return context
|
||||
}
|
||||
return () => {
|
||||
if (context !== null) {
|
||||
context.changedContent = changedContent.splice(0)
|
||||
return context
|
||||
}
|
||||
|
||||
env.DEBUG && console.time('Creating context')
|
||||
context = createContext(config, changedContent.splice(0))
|
||||
env.DEBUG && console.timeEnd('Creating context')
|
||||
return context
|
||||
}
|
||||
})(root, result)
|
||||
env.DEBUG && console.timeEnd('Compiling CSS')
|
||||
},
|
||||
env.DEBUG && console.time('Creating context')
|
||||
context = createContext(config, changedContent.splice(0))
|
||||
env.DEBUG && console.timeEnd('Creating context')
|
||||
return context
|
||||
}
|
||||
}
|
||||
: () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
plugins: [tailwindAot(() => config, configPath)],
|
||||
}
|
||||
}
|
||||
})(root, result)
|
||||
env.DEBUG && console.timeEnd('Compiling CSS')
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
tailwindPlugin.postcss = true
|
||||
|
||||
|
|
|
|||
|
|
@ -1,57 +1,335 @@
|
|||
import * as plugins from './plugins/index.js'
|
||||
import configurePlugins from './util/configurePlugins'
|
||||
import postcss from 'postcss'
|
||||
import * as corePlugins from './plugins'
|
||||
import buildMediaQuery from './util/buildMediaQuery'
|
||||
import prefixSelector from './util/prefixSelector'
|
||||
import {
|
||||
applyPseudoToMarker,
|
||||
updateLastClasses,
|
||||
updateAllClasses,
|
||||
transformAllSelectors,
|
||||
transformAllClasses,
|
||||
transformLastClasses,
|
||||
} from './util/pluginUtils'
|
||||
import log from './util/log'
|
||||
|
||||
function move(items, item, befores) {
|
||||
let lowestBefore = -1
|
||||
export default {
|
||||
pseudoElementVariants: function ({ config, addVariant }) {
|
||||
addVariant(
|
||||
'first-letter',
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`first-letter${config('separator')}${className}`, '::first-letter')
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
for (let before of befores) {
|
||||
let index = items.indexOf(before)
|
||||
if (index >= 0 && (index < lowestBefore || lowestBefore === -1)) {
|
||||
lowestBefore = index
|
||||
addVariant(
|
||||
'first-line',
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`first-line${config('separator')}${className}`, '::first-line')
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
addVariant('marker', [
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
return `marker${config('separator')}${className}`
|
||||
})
|
||||
|
||||
return `${variantSelector} *::marker`
|
||||
}),
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`marker${config('separator')}${className}`, '::marker')
|
||||
})
|
||||
}),
|
||||
])
|
||||
|
||||
addVariant('selection', [
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
return `selection${config('separator')}${className}`
|
||||
})
|
||||
|
||||
return `${variantSelector} *::selection`
|
||||
}),
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`selection${config('separator')}${className}`, '::selection')
|
||||
})
|
||||
}),
|
||||
])
|
||||
|
||||
addVariant(
|
||||
'before',
|
||||
transformAllSelectors(
|
||||
(selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`before${config('separator')}${className}`, '::before')
|
||||
})
|
||||
},
|
||||
{
|
||||
withRule: (rule) => {
|
||||
let foundContent = false
|
||||
rule.walkDecls('content', () => {
|
||||
foundContent = true
|
||||
})
|
||||
if (!foundContent) {
|
||||
rule.prepend(postcss.decl({ prop: 'content', value: '""' }))
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
addVariant(
|
||||
'after',
|
||||
transformAllSelectors(
|
||||
(selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`after${config('separator')}${className}`, '::after')
|
||||
})
|
||||
},
|
||||
{
|
||||
withRule: (rule) => {
|
||||
let foundContent = false
|
||||
rule.walkDecls('content', () => {
|
||||
foundContent = true
|
||||
})
|
||||
if (!foundContent) {
|
||||
rule.prepend(postcss.decl({ prop: 'content', value: '""' }))
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
pseudoClassVariants: function ({ config, addVariant }) {
|
||||
let pseudoVariants = [
|
||||
// Positional
|
||||
['first', 'first-child'],
|
||||
['last', 'last-child'],
|
||||
['only', 'only-child'],
|
||||
['odd', 'nth-child(odd)'],
|
||||
['even', 'nth-child(even)'],
|
||||
'first-of-type',
|
||||
'last-of-type',
|
||||
'only-of-type',
|
||||
|
||||
// State
|
||||
'visited',
|
||||
'target',
|
||||
|
||||
// Forms
|
||||
'default',
|
||||
'checked',
|
||||
'indeterminate',
|
||||
'placeholder-shown',
|
||||
'autofill',
|
||||
'required',
|
||||
'valid',
|
||||
'invalid',
|
||||
'in-range',
|
||||
'out-of-range',
|
||||
'read-only',
|
||||
|
||||
// Content
|
||||
'empty',
|
||||
|
||||
// Interactive
|
||||
'focus-within',
|
||||
'hover',
|
||||
'focus',
|
||||
'focus-visible',
|
||||
'active',
|
||||
'disabled',
|
||||
]
|
||||
|
||||
for (let variant of pseudoVariants) {
|
||||
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||
|
||||
addVariant(
|
||||
variantName,
|
||||
transformAllClasses((className, { withPseudo }) => {
|
||||
return withPseudo(`${variantName}${config('separator')}${className}`, `:${state}`)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (items.indexOf(item) === -1 || lowestBefore === -1) {
|
||||
return items
|
||||
}
|
||||
let groupMarker = prefixSelector(config('prefix'), '.group')
|
||||
for (let variant of pseudoVariants) {
|
||||
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||
let groupVariantName = `group-${variantName}`
|
||||
|
||||
items = [...items]
|
||||
let fromIndex = items.indexOf(item)
|
||||
let toIndex = lowestBefore
|
||||
items.splice(fromIndex, 1)
|
||||
items.splice(toIndex, 0, item)
|
||||
return items
|
||||
}
|
||||
|
||||
export default function ({ corePlugins: corePluginConfig }) {
|
||||
let pluginOrder = Object.keys(plugins)
|
||||
|
||||
pluginOrder = configurePlugins(corePluginConfig, pluginOrder)
|
||||
pluginOrder = move(pluginOrder, 'transform', ['translate', 'rotate', 'skew', 'scale'])
|
||||
pluginOrder = move(pluginOrder, 'filter', [
|
||||
'blur',
|
||||
'brightness',
|
||||
'contrast',
|
||||
'dropShadow',
|
||||
'grayscale',
|
||||
'hueRotate',
|
||||
'invert',
|
||||
'saturate',
|
||||
'sepia',
|
||||
])
|
||||
pluginOrder = move(pluginOrder, 'backdropFilter', [
|
||||
'backdropBlur',
|
||||
'backdropBrightness',
|
||||
'backdropContrast',
|
||||
'backdropGrayscale',
|
||||
'backdropHueRotate',
|
||||
'backdropInvert',
|
||||
'backdropOpacity',
|
||||
'backdropSaturate',
|
||||
'backdropSepia',
|
||||
])
|
||||
|
||||
return pluginOrder.map((pluginName) => {
|
||||
return plugins[pluginName]()
|
||||
})
|
||||
addVariant(
|
||||
groupVariantName,
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
if (`.${className}` === groupMarker) return className
|
||||
return `${groupVariantName}${config('separator')}${className}`
|
||||
})
|
||||
|
||||
if (variantSelector === selector) {
|
||||
return null
|
||||
}
|
||||
|
||||
return applyPseudoToMarker(
|
||||
variantSelector,
|
||||
groupMarker,
|
||||
state,
|
||||
(marker, selector) => `${marker} ${selector}`
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
let peerMarker = prefixSelector(config('prefix'), '.peer')
|
||||
for (let variant of pseudoVariants) {
|
||||
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||
let peerVariantName = `peer-${variantName}`
|
||||
|
||||
addVariant(
|
||||
peerVariantName,
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
if (`.${className}` === peerMarker) return className
|
||||
return `${peerVariantName}${config('separator')}${className}`
|
||||
})
|
||||
|
||||
if (variantSelector === selector) {
|
||||
return null
|
||||
}
|
||||
|
||||
return applyPseudoToMarker(variantSelector, peerMarker, state, (marker, selector) =>
|
||||
selector.trim().startsWith('~') ? `${marker}${selector}` : `${marker} ~ ${selector}`
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
directionVariants: function ({ config, addVariant }) {
|
||||
addVariant(
|
||||
'ltr',
|
||||
transformAllSelectors(
|
||||
(selector) =>
|
||||
`[dir="ltr"] ${updateAllClasses(
|
||||
selector,
|
||||
(className) => `ltr${config('separator')}${className}`
|
||||
)}`
|
||||
)
|
||||
)
|
||||
|
||||
addVariant(
|
||||
'rtl',
|
||||
transformAllSelectors(
|
||||
(selector) =>
|
||||
`[dir="rtl"] ${updateAllClasses(
|
||||
selector,
|
||||
(className) => `rtl${config('separator')}${className}`
|
||||
)}`
|
||||
)
|
||||
)
|
||||
},
|
||||
reducedMotionVariants: function ({ config, addVariant }) {
|
||||
addVariant(
|
||||
'motion-safe',
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `motion-safe${config('separator')}${className}`
|
||||
},
|
||||
{
|
||||
wrap: () =>
|
||||
postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-reduced-motion: no-preference)',
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
addVariant(
|
||||
'motion-reduce',
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `motion-reduce${config('separator')}${className}`
|
||||
},
|
||||
{
|
||||
wrap: () =>
|
||||
postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-reduced-motion: reduce)',
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
darkVariants: function ({ config, addVariant }) {
|
||||
let mode = config('darkMode', 'media')
|
||||
if (mode === false) {
|
||||
mode = 'media'
|
||||
log.warn([
|
||||
'`darkMode` is set to `false` in your config.',
|
||||
'This will behave just like the `media` value.',
|
||||
])
|
||||
}
|
||||
|
||||
if (mode === 'class') {
|
||||
addVariant(
|
||||
'dark',
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateLastClasses(selector, (className) => {
|
||||
return `dark${config('separator')}${className}`
|
||||
})
|
||||
|
||||
if (variantSelector === selector) {
|
||||
return null
|
||||
}
|
||||
|
||||
let darkSelector = prefixSelector(config('prefix'), `.dark`)
|
||||
|
||||
return `${darkSelector} ${variantSelector}`
|
||||
})
|
||||
)
|
||||
} else if (mode === 'media') {
|
||||
addVariant(
|
||||
'dark',
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `dark${config('separator')}${className}`
|
||||
},
|
||||
{
|
||||
wrap: () =>
|
||||
postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-color-scheme: dark)',
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
screenVariants: function ({ config, theme, addVariant }) {
|
||||
for (let screen in theme('screens')) {
|
||||
let size = theme('screens')[screen]
|
||||
let query = buildMediaQuery(size)
|
||||
|
||||
addVariant(
|
||||
screen,
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `${screen}${config('separator')}${className}`
|
||||
},
|
||||
{ wrap: () => postcss.atRule({ name: 'media', params: query }) }
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
...Object.fromEntries(
|
||||
Object.entries(corePlugins).map(([pluginName, plugin]) => {
|
||||
return [pluginName, plugin()]
|
||||
})
|
||||
),
|
||||
}
|
||||
|
|
|
|||
117
src/index.js
117
src/index.js
|
|
@ -1,98 +1,33 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import getModuleDependencies from './lib/getModuleDependencies'
|
||||
import registerConfigAsDependency from './lib/registerConfigAsDependency'
|
||||
import setupTrackingContext from './lib/setupTrackingContext'
|
||||
import setupWatchingContext from './lib/setupWatchingContext'
|
||||
import processTailwindFeatures from './processTailwindFeatures'
|
||||
import formatCSS from './lib/formatCSS'
|
||||
import resolveConfig from './util/resolveConfig'
|
||||
import getAllConfigs from './util/getAllConfigs'
|
||||
import { supportedConfigFiles } from './constants'
|
||||
import defaultConfig from '../stubs/defaultConfig.stub.js'
|
||||
|
||||
import jitPlugins from './jit'
|
||||
|
||||
function resolveConfigPath(filePath) {
|
||||
// require('tailwindcss')({ theme: ..., variants: ... })
|
||||
if (_.isObject(filePath) && !_.has(filePath, 'config') && !_.isEmpty(filePath)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// require('tailwindcss')({ config: 'custom-config.js' })
|
||||
if (_.isObject(filePath) && _.has(filePath, 'config') && _.isString(filePath.config)) {
|
||||
return path.resolve(filePath.config)
|
||||
}
|
||||
|
||||
// require('tailwindcss')({ config: { theme: ..., variants: ... } })
|
||||
if (_.isObject(filePath) && _.has(filePath, 'config') && _.isObject(filePath.config)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// require('tailwindcss')('custom-config.js')
|
||||
if (_.isString(filePath)) {
|
||||
return path.resolve(filePath)
|
||||
}
|
||||
|
||||
// require('tailwindcss')
|
||||
for (const configFile of supportedConfigFiles) {
|
||||
try {
|
||||
const configPath = path.resolve(configFile)
|
||||
fs.accessSync(configPath)
|
||||
return configPath
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const getConfigFunction = (config) => () => {
|
||||
if (_.isUndefined(config)) {
|
||||
return resolveConfig([
|
||||
...getAllConfigs(defaultConfig),
|
||||
{ corePlugins: { caretColor: false, content: false } },
|
||||
])
|
||||
}
|
||||
|
||||
// Skip this if Jest is running: https://github.com/facebook/jest/pull/9841#issuecomment-621417584
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
if (!_.isObject(config)) {
|
||||
getModuleDependencies(config).forEach((mdl) => {
|
||||
delete require.cache[require.resolve(mdl.file)]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const configObject = _.isObject(config) ? _.get(config, 'config', config) : require(config)
|
||||
|
||||
return resolveConfig([
|
||||
...getAllConfigs(configObject),
|
||||
{ corePlugins: { caretColor: false, content: false } },
|
||||
])
|
||||
}
|
||||
|
||||
module.exports = function tailwindcss(config) {
|
||||
const resolvedConfigPath = resolveConfigPath(config)
|
||||
const getConfig = getConfigFunction(resolvedConfigPath || config)
|
||||
const mode = _.get(getConfig(), 'mode', 'aot')
|
||||
|
||||
if (mode === 'jit') {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
plugins: jitPlugins(config),
|
||||
}
|
||||
}
|
||||
|
||||
const plugins = []
|
||||
|
||||
if (!_.isUndefined(resolvedConfigPath)) {
|
||||
plugins.push(registerConfigAsDependency(resolvedConfigPath))
|
||||
}
|
||||
import { env } from './lib/sharedState'
|
||||
|
||||
module.exports = function tailwindcss(configOrPath) {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
plugins: [...plugins, processTailwindFeatures(getConfig), formatCSS],
|
||||
plugins: [
|
||||
env.DEBUG &&
|
||||
function (root) {
|
||||
console.log('\n')
|
||||
console.time('JIT TOTAL')
|
||||
return root
|
||||
},
|
||||
function (root, result) {
|
||||
let setupContext =
|
||||
env.TAILWIND_MODE === 'watch'
|
||||
? setupWatchingContext(configOrPath)
|
||||
: setupTrackingContext(configOrPath)
|
||||
|
||||
processTailwindFeatures(setupContext)(root, result)
|
||||
},
|
||||
env.DEBUG &&
|
||||
function (root) {
|
||||
console.timeEnd('JIT TOTAL')
|
||||
console.log('\n')
|
||||
return root
|
||||
},
|
||||
].filter(Boolean),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
import _ from 'lodash'
|
||||
import postcss from 'postcss'
|
||||
|
||||
import getModuleDependencies from './lib/getModuleDependencies'
|
||||
import registerConfigAsDependency from './lib/registerConfigAsDependency'
|
||||
import processTailwindFeatures from './processTailwindFeatures'
|
||||
import formatCSS from './lib/formatCSS'
|
||||
import resolveConfig from './util/resolveConfig'
|
||||
import getAllConfigs from './util/getAllConfigs'
|
||||
import { supportedConfigFiles } from './constants'
|
||||
import defaultConfig from '../stubs/defaultConfig.stub.js'
|
||||
|
||||
import jitPlugins from './jit'
|
||||
|
||||
function resolveConfigPath(filePath) {
|
||||
// require('tailwindcss')({ theme: ..., variants: ... })
|
||||
if (_.isObject(filePath) && !_.has(filePath, 'config') && !_.isEmpty(filePath)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// require('tailwindcss')({ config: 'custom-config.js' })
|
||||
if (_.isObject(filePath) && _.has(filePath, 'config') && _.isString(filePath.config)) {
|
||||
return path.resolve(filePath.config)
|
||||
}
|
||||
|
||||
// require('tailwindcss')({ config: { theme: ..., variants: ... } })
|
||||
if (_.isObject(filePath) && _.has(filePath, 'config') && _.isObject(filePath.config)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// require('tailwindcss')('custom-config.js')
|
||||
if (_.isString(filePath)) {
|
||||
return path.resolve(filePath)
|
||||
}
|
||||
|
||||
// require('tailwindcss')
|
||||
for (const configFile of supportedConfigFiles) {
|
||||
try {
|
||||
const configPath = path.resolve(configFile)
|
||||
fs.accessSync(configPath)
|
||||
return configPath
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const getConfigFunction = (config) => () => {
|
||||
if (_.isUndefined(config)) {
|
||||
return resolveConfig([...getAllConfigs(defaultConfig)])
|
||||
}
|
||||
|
||||
// Skip this if Jest is running: https://github.com/facebook/jest/pull/9841#issuecomment-621417584
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
if (!_.isObject(config)) {
|
||||
getModuleDependencies(config).forEach((mdl) => {
|
||||
delete require.cache[require.resolve(mdl.file)]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const configObject = _.isObject(config) ? _.get(config, 'config', config) : require(config)
|
||||
|
||||
return resolveConfig([...getAllConfigs(configObject)])
|
||||
}
|
||||
|
||||
const plugin = postcss.plugin('tailwindcss', (config) => {
|
||||
const resolvedConfigPath = resolveConfigPath(config)
|
||||
const getConfig = getConfigFunction(resolvedConfigPath || config)
|
||||
const mode = _.get(getConfig(), 'mode', 'aot')
|
||||
|
||||
if (mode === 'jit') {
|
||||
return postcss(jitPlugins(config))
|
||||
}
|
||||
|
||||
const plugins = []
|
||||
if (!_.isUndefined(resolvedConfigPath)) {
|
||||
plugins.push(registerConfigAsDependency(resolvedConfigPath))
|
||||
}
|
||||
|
||||
return postcss([...plugins, processTailwindFeatures(getConfig), formatCSS])
|
||||
})
|
||||
|
||||
module.exports = plugin
|
||||
|
|
@ -1,325 +0,0 @@
|
|||
import postcss from 'postcss'
|
||||
import * as corePlugins from '../plugins'
|
||||
import buildMediaQuery from '../util/buildMediaQuery'
|
||||
import prefixSelector from '../util/prefixSelector'
|
||||
import {
|
||||
applyPseudoToMarker,
|
||||
updateLastClasses,
|
||||
updateAllClasses,
|
||||
transformAllSelectors,
|
||||
transformAllClasses,
|
||||
transformLastClasses,
|
||||
} from '../util/pluginUtils'
|
||||
|
||||
export default {
|
||||
pseudoElementVariants: function ({ config, addVariant }) {
|
||||
addVariant(
|
||||
'first-letter',
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`first-letter${config('separator')}${className}`, '::first-letter')
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
addVariant(
|
||||
'first-line',
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`first-line${config('separator')}${className}`, '::first-line')
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
addVariant('marker', [
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
return `marker${config('separator')}${className}`
|
||||
})
|
||||
|
||||
return `${variantSelector} *::marker`
|
||||
}),
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`marker${config('separator')}${className}`, '::marker')
|
||||
})
|
||||
}),
|
||||
])
|
||||
|
||||
addVariant('selection', [
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
return `selection${config('separator')}${className}`
|
||||
})
|
||||
|
||||
return `${variantSelector} *::selection`
|
||||
}),
|
||||
transformAllSelectors((selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`selection${config('separator')}${className}`, '::selection')
|
||||
})
|
||||
}),
|
||||
])
|
||||
|
||||
addVariant(
|
||||
'before',
|
||||
transformAllSelectors(
|
||||
(selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`before${config('separator')}${className}`, '::before')
|
||||
})
|
||||
},
|
||||
{
|
||||
withRule: (rule) => {
|
||||
let foundContent = false
|
||||
rule.walkDecls('content', () => {
|
||||
foundContent = true
|
||||
})
|
||||
if (!foundContent) {
|
||||
rule.prepend(postcss.decl({ prop: 'content', value: '""' }))
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
addVariant(
|
||||
'after',
|
||||
transformAllSelectors(
|
||||
(selector) => {
|
||||
return updateAllClasses(selector, (className, { withPseudo }) => {
|
||||
return withPseudo(`after${config('separator')}${className}`, '::after')
|
||||
})
|
||||
},
|
||||
{
|
||||
withRule: (rule) => {
|
||||
let foundContent = false
|
||||
rule.walkDecls('content', () => {
|
||||
foundContent = true
|
||||
})
|
||||
if (!foundContent) {
|
||||
rule.prepend(postcss.decl({ prop: 'content', value: '""' }))
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
pseudoClassVariants: function ({ config, addVariant }) {
|
||||
let pseudoVariants = [
|
||||
// Positional
|
||||
['first', 'first-child'],
|
||||
['last', 'last-child'],
|
||||
['only', 'only-child'],
|
||||
['odd', 'nth-child(odd)'],
|
||||
['even', 'nth-child(even)'],
|
||||
'first-of-type',
|
||||
'last-of-type',
|
||||
'only-of-type',
|
||||
|
||||
// State
|
||||
'visited',
|
||||
'target',
|
||||
|
||||
// Forms
|
||||
'default',
|
||||
'checked',
|
||||
'indeterminate',
|
||||
'placeholder-shown',
|
||||
'autofill',
|
||||
'required',
|
||||
'valid',
|
||||
'invalid',
|
||||
'in-range',
|
||||
'out-of-range',
|
||||
'read-only',
|
||||
|
||||
// Content
|
||||
'empty',
|
||||
|
||||
// Interactive
|
||||
'focus-within',
|
||||
'hover',
|
||||
'focus',
|
||||
'focus-visible',
|
||||
'active',
|
||||
'disabled',
|
||||
]
|
||||
|
||||
for (let variant of pseudoVariants) {
|
||||
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||
|
||||
addVariant(
|
||||
variantName,
|
||||
transformAllClasses((className, { withPseudo }) => {
|
||||
return withPseudo(`${variantName}${config('separator')}${className}`, `:${state}`)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
let groupMarker = prefixSelector(config('prefix'), '.group')
|
||||
for (let variant of pseudoVariants) {
|
||||
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||
let groupVariantName = `group-${variantName}`
|
||||
|
||||
addVariant(
|
||||
groupVariantName,
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
if (`.${className}` === groupMarker) return className
|
||||
return `${groupVariantName}${config('separator')}${className}`
|
||||
})
|
||||
|
||||
if (variantSelector === selector) {
|
||||
return null
|
||||
}
|
||||
|
||||
return applyPseudoToMarker(
|
||||
variantSelector,
|
||||
groupMarker,
|
||||
state,
|
||||
(marker, selector) => `${marker} ${selector}`
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
let peerMarker = prefixSelector(config('prefix'), '.peer')
|
||||
for (let variant of pseudoVariants) {
|
||||
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||
let peerVariantName = `peer-${variantName}`
|
||||
|
||||
addVariant(
|
||||
peerVariantName,
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateAllClasses(selector, (className) => {
|
||||
if (`.${className}` === peerMarker) return className
|
||||
return `${peerVariantName}${config('separator')}${className}`
|
||||
})
|
||||
|
||||
if (variantSelector === selector) {
|
||||
return null
|
||||
}
|
||||
|
||||
return applyPseudoToMarker(variantSelector, peerMarker, state, (marker, selector) =>
|
||||
selector.trim().startsWith('~') ? `${marker}${selector}` : `${marker} ~ ${selector}`
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
directionVariants: function ({ config, addVariant }) {
|
||||
addVariant(
|
||||
'ltr',
|
||||
transformAllSelectors(
|
||||
(selector) =>
|
||||
`[dir="ltr"] ${updateAllClasses(
|
||||
selector,
|
||||
(className) => `ltr${config('separator')}${className}`
|
||||
)}`
|
||||
)
|
||||
)
|
||||
|
||||
addVariant(
|
||||
'rtl',
|
||||
transformAllSelectors(
|
||||
(selector) =>
|
||||
`[dir="rtl"] ${updateAllClasses(
|
||||
selector,
|
||||
(className) => `rtl${config('separator')}${className}`
|
||||
)}`
|
||||
)
|
||||
)
|
||||
},
|
||||
reducedMotionVariants: function ({ config, addVariant }) {
|
||||
addVariant(
|
||||
'motion-safe',
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `motion-safe${config('separator')}${className}`
|
||||
},
|
||||
{
|
||||
wrap: () =>
|
||||
postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-reduced-motion: no-preference)',
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
addVariant(
|
||||
'motion-reduce',
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `motion-reduce${config('separator')}${className}`
|
||||
},
|
||||
{
|
||||
wrap: () =>
|
||||
postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-reduced-motion: reduce)',
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
darkVariants: function ({ config, addVariant }) {
|
||||
if (config('darkMode') === 'class') {
|
||||
addVariant(
|
||||
'dark',
|
||||
transformAllSelectors((selector) => {
|
||||
let variantSelector = updateLastClasses(selector, (className) => {
|
||||
return `dark${config('separator')}${className}`
|
||||
})
|
||||
|
||||
if (variantSelector === selector) {
|
||||
return null
|
||||
}
|
||||
|
||||
let darkSelector = prefixSelector(config('prefix'), `.dark`)
|
||||
|
||||
return `${darkSelector} ${variantSelector}`
|
||||
})
|
||||
)
|
||||
} else if (config('darkMode') === 'media') {
|
||||
addVariant(
|
||||
'dark',
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `dark${config('separator')}${className}`
|
||||
},
|
||||
{
|
||||
wrap: () =>
|
||||
postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-color-scheme: dark)',
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
screenVariants: function ({ config, theme, addVariant }) {
|
||||
for (let screen in theme('screens')) {
|
||||
let size = theme('screens')[screen]
|
||||
let query = buildMediaQuery(size)
|
||||
|
||||
addVariant(
|
||||
screen,
|
||||
transformLastClasses(
|
||||
(className) => {
|
||||
return `${screen}${config('separator')}${className}`
|
||||
},
|
||||
{ wrap: () => postcss.atRule({ name: 'media', params: query }) }
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
...Object.fromEntries(
|
||||
Object.entries(corePlugins).map(([pluginName, plugin]) => {
|
||||
return [pluginName, plugin()]
|
||||
})
|
||||
),
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import setupTrackingContext from './lib/setupTrackingContext'
|
||||
import setupWatchingContext from './lib/setupWatchingContext'
|
||||
import { env } from './lib/sharedState'
|
||||
import processTailwindFeatures from './processTailwindFeatures'
|
||||
|
||||
export default function (configOrPath = {}) {
|
||||
return [
|
||||
env.DEBUG &&
|
||||
function (root) {
|
||||
console.log('\n')
|
||||
console.time('JIT TOTAL')
|
||||
return root
|
||||
},
|
||||
function (root, result) {
|
||||
let setupContext =
|
||||
env.TAILWIND_MODE === 'watch'
|
||||
? setupWatchingContext(configOrPath)
|
||||
: setupTrackingContext(configOrPath)
|
||||
|
||||
processTailwindFeatures(setupContext)(root, result)
|
||||
},
|
||||
env.DEBUG &&
|
||||
function (root) {
|
||||
console.timeEnd('JIT TOTAL')
|
||||
console.log('\n')
|
||||
return root
|
||||
},
|
||||
].filter(Boolean)
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import normalizeTailwindDirectives from './lib/normalizeTailwindDirectives'
|
||||
import expandTailwindAtRules from './lib/expandTailwindAtRules'
|
||||
import expandApplyAtRules from './lib/expandApplyAtRules'
|
||||
import evaluateTailwindFunctions from '../lib/evaluateTailwindFunctions'
|
||||
import substituteScreenAtRules from '../lib/substituteScreenAtRules'
|
||||
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
|
||||
import collapseAdjacentRules from './lib/collapseAdjacentRules'
|
||||
import { createContext } from './lib/setupContextUtils'
|
||||
import log from '../util/log'
|
||||
|
||||
let warned = false
|
||||
|
||||
export default function processTailwindFeatures(setupContext) {
|
||||
return function (root, result) {
|
||||
if (!warned) {
|
||||
log.warn([
|
||||
`You have enabled the JIT engine which is currently in preview.`,
|
||||
'Preview features are not covered by semver, may introduce breaking changes, and can change at any time.',
|
||||
])
|
||||
warned = true
|
||||
}
|
||||
|
||||
let tailwindDirectives = normalizeTailwindDirectives(root)
|
||||
|
||||
let context = setupContext({
|
||||
tailwindDirectives,
|
||||
registerDependency(dependency) {
|
||||
result.messages.push({
|
||||
plugin: 'tailwindcss',
|
||||
parent: result.opts.from,
|
||||
...dependency,
|
||||
})
|
||||
},
|
||||
createContext(tailwindConfig, changedContent) {
|
||||
return createContext(tailwindConfig, changedContent, tailwindDirectives, root)
|
||||
},
|
||||
})(root, result)
|
||||
|
||||
if (context.tailwindConfig.separator === '-') {
|
||||
throw new Error(
|
||||
"The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead."
|
||||
)
|
||||
}
|
||||
|
||||
expandTailwindAtRules(context)(root, result)
|
||||
expandApplyAtRules(context)(root, result)
|
||||
evaluateTailwindFunctions(context)(root, result)
|
||||
substituteScreenAtRules(context)(root, result)
|
||||
resolveDefaultsAtRules(context)(root, result)
|
||||
collapseAdjacentRules(context)(root, result)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
export default function applyImportantConfiguration(_config) {
|
||||
return function (css) {
|
||||
css.walkRules((rule) => {
|
||||
const important = rule.__tailwind ? rule.__tailwind.important : false
|
||||
|
||||
if (!important) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof important === 'string') {
|
||||
rule.selectors = rule.selectors.map((selector) => {
|
||||
return `${rule.__tailwind.important} ${selector}`
|
||||
})
|
||||
} else {
|
||||
rule.walkDecls((decl) => (decl.important = true))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import postcss from 'postcss'
|
||||
|
||||
export default function convertLayerAtRulesToControlComments() {
|
||||
return function (css) {
|
||||
css.walkAtRules('layer', (atRule) => {
|
||||
const layer = atRule.params
|
||||
|
||||
if (!['base', 'components', 'utilities'].includes(layer)) {
|
||||
return
|
||||
}
|
||||
|
||||
atRule.before(postcss.comment({ text: `tailwind start ${layer}` }))
|
||||
atRule.before(atRule.nodes)
|
||||
atRule.before(postcss.comment({ text: `tailwind end ${layer}` }))
|
||||
atRule.remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import postcss from 'postcss'
|
||||
import { resolveMatches } from './generateRules'
|
||||
import bigSign from '../../util/bigSign'
|
||||
import escapeClassName from '../../util/escapeClassName'
|
||||
import bigSign from '../util/bigSign'
|
||||
import escapeClassName from '../util/escapeClassName'
|
||||
|
||||
function buildApplyCache(applyCandidates, context) {
|
||||
for (let candidate of applyCandidates) {
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import * as sharedState from './sharedState'
|
||||
import { generateRules } from './generateRules'
|
||||
import bigSign from '../../util/bigSign'
|
||||
import cloneNodes from '../../util/cloneNodes'
|
||||
import bigSign from '../util/bigSign'
|
||||
import cloneNodes from '../util/cloneNodes'
|
||||
|
||||
let env = sharedState.env
|
||||
let contentMatchCache = sharedState.contentMatchCache
|
||||
|
|
@ -30,19 +30,18 @@ const builtInTransformers = {
|
|||
}
|
||||
|
||||
function getExtractor(tailwindConfig, fileExtension) {
|
||||
let extractors = (tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.extract) || {}
|
||||
const purgeOptions =
|
||||
(tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.options) || {}
|
||||
let extractors = tailwindConfig.content.extract
|
||||
let contentOptions = tailwindConfig.content.options
|
||||
|
||||
if (typeof extractors === 'function') {
|
||||
extractors = {
|
||||
DEFAULT: extractors,
|
||||
}
|
||||
}
|
||||
if (purgeOptions.defaultExtractor) {
|
||||
extractors.DEFAULT = purgeOptions.defaultExtractor
|
||||
if (contentOptions.defaultExtractor) {
|
||||
extractors.DEFAULT = contentOptions.defaultExtractor
|
||||
}
|
||||
for (let { extensions, extractor } of purgeOptions.extractors || []) {
|
||||
for (let { extensions, extractor } of contentOptions.extractors || []) {
|
||||
for (let extension of extensions) {
|
||||
extractors[extension] = extractor
|
||||
}
|
||||
|
|
@ -57,8 +56,7 @@ function getExtractor(tailwindConfig, fileExtension) {
|
|||
}
|
||||
|
||||
function getTransformer(tailwindConfig, fileExtension) {
|
||||
let transformers =
|
||||
(tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.transform) || {}
|
||||
let transformers = tailwindConfig.content.transform
|
||||
|
||||
if (typeof transformers === 'function') {
|
||||
transformers = {
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
function indentRecursive(node, indent = 0) {
|
||||
node.each &&
|
||||
node.each((child, i) => {
|
||||
if (!child.raws.before || child.raws.before.includes('\n')) {
|
||||
child.raws.before = `\n${node.type !== 'rule' && i > 0 ? '\n' : ''}${' '.repeat(indent)}`
|
||||
}
|
||||
child.raws.after = `\n${' '.repeat(indent)}`
|
||||
indentRecursive(child, indent + 1)
|
||||
})
|
||||
}
|
||||
|
||||
export default function formatNodes(root) {
|
||||
indentRecursive(root)
|
||||
if (root.first) {
|
||||
root.first.raws.before = ''
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import postcss from 'postcss'
|
||||
import selectorParser from 'postcss-selector-parser'
|
||||
import parseObjectStyles from '../../util/parseObjectStyles'
|
||||
import isPlainObject from '../../util/isPlainObject'
|
||||
import prefixSelector from '../../util/prefixSelector'
|
||||
import { updateAllClasses } from '../../util/pluginUtils'
|
||||
import parseObjectStyles from '../util/parseObjectStyles'
|
||||
import isPlainObject from '../util/isPlainObject'
|
||||
import prefixSelector from '../util/prefixSelector'
|
||||
import { updateAllClasses } from '../util/pluginUtils'
|
||||
|
||||
let classNameParser = selectorParser((selectors) => {
|
||||
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import log from '../util/log'
|
||||
|
||||
export default function normalizeTailwindDirectives(root) {
|
||||
let tailwindDirectives = new Set()
|
||||
let layerDirectives = new Set()
|
||||
|
|
@ -38,6 +40,11 @@ export default function normalizeTailwindDirectives(root) {
|
|||
}
|
||||
|
||||
if (['layer', 'responsive', 'variants'].includes(atRule.name)) {
|
||||
if (['responsive', 'variants'].includes(atRule.name)) {
|
||||
log.warn([
|
||||
`'@${atRule.name}' is deprecated, use '@layer utilities' or '@layer components' instead.`,
|
||||
])
|
||||
}
|
||||
layerDirectives.add(atRule)
|
||||
}
|
||||
})
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
import _ from 'lodash'
|
||||
import postcss from 'postcss'
|
||||
import PurgeCSS, { defaultOptions, standardizeSafelist, mergeExtractorSelectors } from 'purgecss'
|
||||
import log from '../util/log'
|
||||
import htmlTags from 'html-tags'
|
||||
import path from 'path'
|
||||
import parseDependency from '../util/parseDependency'
|
||||
import normalizePath from 'normalize-path'
|
||||
|
||||
function removeTailwindMarkers(css) {
|
||||
css.walkAtRules('tailwind', (rule) => rule.remove())
|
||||
css.walkComments((comment) => {
|
||||
switch (comment.text.trim()) {
|
||||
case 'tailwind start base':
|
||||
case 'tailwind end base':
|
||||
case 'tailwind start components':
|
||||
case 'tailwind start utilities':
|
||||
case 'tailwind end components':
|
||||
case 'tailwind end utilities':
|
||||
comment.remove()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function tailwindExtractor(content) {
|
||||
// Capture as liberally as possible, including things like `h-(screen-1.5)`
|
||||
const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []
|
||||
const broadMatchesWithoutTrailingSlash = broadMatches.map((match) => _.trimEnd(match, '\\'))
|
||||
|
||||
// Capture classes within other delimiters like .block(class="w-1/2") in Pug
|
||||
const innerMatches = content.match(/[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g) || []
|
||||
|
||||
return broadMatches.concat(broadMatchesWithoutTrailingSlash).concat(innerMatches)
|
||||
}
|
||||
|
||||
function getTransformer(config, fileExtension) {
|
||||
let transformers = (config.purge && config.purge.transform) || {}
|
||||
|
||||
if (typeof transformers === 'function') {
|
||||
transformers = {
|
||||
DEFAULT: transformers,
|
||||
}
|
||||
}
|
||||
|
||||
return transformers[fileExtension] || transformers.DEFAULT || ((content) => content)
|
||||
}
|
||||
|
||||
export default function purgeUnusedUtilities(config, configChanged, registerDependency) {
|
||||
const purgeEnabled = _.get(
|
||||
config,
|
||||
'purge.enabled',
|
||||
config.purge !== false && config.purge !== undefined && process.env.NODE_ENV === 'production'
|
||||
)
|
||||
|
||||
if (!purgeEnabled) {
|
||||
return removeTailwindMarkers
|
||||
}
|
||||
|
||||
// Skip if `purge: []` since that's part of the default config
|
||||
if (Array.isArray(config.purge) && config.purge.length === 0) {
|
||||
if (configChanged) {
|
||||
log.warn([
|
||||
'Tailwind is not purging unused styles because no template paths have been provided.',
|
||||
'If you have manually configured PurgeCSS outside of Tailwind or are deliberately not removing unused styles, set `purge: false` in your Tailwind config file to silence this warning.',
|
||||
'https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css',
|
||||
])
|
||||
}
|
||||
|
||||
return removeTailwindMarkers
|
||||
}
|
||||
|
||||
const extractors = config.purge.extract || {}
|
||||
const transformers = config.purge.transform || {}
|
||||
let { defaultExtractor: originalDefaultExtractor, ...purgeOptions } = config.purge.options || {}
|
||||
|
||||
if (config.purge?.safelist && !purgeOptions.hasOwnProperty('safelist')) {
|
||||
purgeOptions.safelist = config.purge.safelist
|
||||
}
|
||||
|
||||
if (!originalDefaultExtractor) {
|
||||
originalDefaultExtractor =
|
||||
typeof extractors === 'function' ? extractors : extractors.DEFAULT || tailwindExtractor
|
||||
}
|
||||
|
||||
const defaultExtractor = (content) => {
|
||||
const preserved = originalDefaultExtractor(content)
|
||||
|
||||
if (_.get(config, 'purge.preserveHtmlElements', true)) {
|
||||
preserved.push(...htmlTags)
|
||||
}
|
||||
|
||||
return preserved
|
||||
}
|
||||
|
||||
// If `extractors` is a function then we don't have any file-specific extractors,
|
||||
// only a default one.
|
||||
let fileSpecificExtractors = typeof extractors === 'function' ? {} : extractors
|
||||
|
||||
// PurgeCSS doesn't support "transformers," so we implement those using extractors.
|
||||
// If we have a custom transformer for an extension, but not a matching extractor,
|
||||
// then we need to create an extractor that we can augment later.
|
||||
if (typeof transformers !== 'function') {
|
||||
for (let [extension] of Object.entries(transformers)) {
|
||||
if (!fileSpecificExtractors[extension]) {
|
||||
fileSpecificExtractors[extension] = defaultExtractor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Augment file-specific extractors by running the transformer before we extract classes.
|
||||
fileSpecificExtractors = Object.entries(fileSpecificExtractors).map(([extension, extractor]) => {
|
||||
return {
|
||||
extensions: [extension],
|
||||
extractor: (content) => {
|
||||
const transformer = getTransformer(config, extension)
|
||||
return extractor(transformer(content))
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
let content = (
|
||||
Array.isArray(config.purge) ? config.purge : config.purge.content || purgeOptions.content || []
|
||||
).map((item) => {
|
||||
if (typeof item === 'string') {
|
||||
return normalizePath(path.resolve(item))
|
||||
}
|
||||
return item
|
||||
})
|
||||
|
||||
for (let fileOrGlob of content.filter((item) => typeof item === 'string')) {
|
||||
registerDependency(parseDependency(fileOrGlob))
|
||||
}
|
||||
|
||||
let hasLayers = false
|
||||
|
||||
const mode = _.get(config, 'purge.mode', 'layers')
|
||||
return postcss([
|
||||
function (css) {
|
||||
if (!['all', 'layers'].includes(mode)) {
|
||||
throw new Error('Purge `mode` must be one of `layers` or `all`.')
|
||||
}
|
||||
|
||||
if (mode === 'all') {
|
||||
return
|
||||
}
|
||||
|
||||
const layers = _.get(config, 'purge.layers', ['base', 'components', 'utilities'])
|
||||
|
||||
css.walkComments((comment) => {
|
||||
switch (comment.text.trim()) {
|
||||
case `purgecss start ignore`:
|
||||
comment.before(postcss.comment({ text: 'purgecss end ignore' }))
|
||||
break
|
||||
case `purgecss end ignore`:
|
||||
comment.before(postcss.comment({ text: 'purgecss end ignore' }))
|
||||
comment.text = 'purgecss start ignore'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
layers.forEach((layer) => {
|
||||
switch (comment.text.trim()) {
|
||||
case `tailwind start ${layer}`:
|
||||
comment.text = 'purgecss end ignore'
|
||||
hasLayers = true
|
||||
break
|
||||
case `tailwind end ${layer}`:
|
||||
comment.text = 'purgecss start ignore'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
|
||||
css.append(postcss.comment({ text: 'purgecss end ignore' }))
|
||||
},
|
||||
removeTailwindMarkers,
|
||||
|
||||
async function (css) {
|
||||
if (mode === 'layers' && !hasLayers) {
|
||||
return
|
||||
}
|
||||
const purgeCSS = new PurgeCSS()
|
||||
purgeCSS.options = {
|
||||
...defaultOptions,
|
||||
|
||||
defaultExtractor: (content) => {
|
||||
const transformer = getTransformer(config)
|
||||
return defaultExtractor(transformer(content))
|
||||
},
|
||||
extractors: fileSpecificExtractors,
|
||||
...purgeOptions,
|
||||
safelist: standardizeSafelist(purgeOptions.safelist),
|
||||
}
|
||||
|
||||
if (purgeCSS.options.variables) {
|
||||
purgeCSS.variablesStructure.safelist = purgeCSS.options.safelist.variables || []
|
||||
}
|
||||
|
||||
const fileFormatContents = content.filter((o) => typeof o === 'string')
|
||||
const rawFormatContents = content.filter((o) => typeof o === 'object')
|
||||
|
||||
const cssFileSelectors = await purgeCSS.extractSelectorsFromFiles(
|
||||
fileFormatContents,
|
||||
purgeCSS.options.extractors
|
||||
)
|
||||
const cssRawSelectors = await purgeCSS.extractSelectorsFromString(
|
||||
rawFormatContents,
|
||||
purgeCSS.options.extractors
|
||||
)
|
||||
const cssSelectors = mergeExtractorSelectors(cssFileSelectors, cssRawSelectors)
|
||||
purgeCSS.walkThroughCSS(css, cssSelectors)
|
||||
if (purgeCSS.options.fontFace) purgeCSS.removeUnusedFontFaces()
|
||||
if (purgeCSS.options.keyframes) purgeCSS.removeUnusedKeyframes()
|
||||
if (purgeCSS.options.variables) purgeCSS.removeUnusedCSSVariables()
|
||||
},
|
||||
])
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import fs from 'fs'
|
||||
import getModuleDependencies from './getModuleDependencies'
|
||||
|
||||
export default function (configFile) {
|
||||
if (!fs.existsSync(configFile)) {
|
||||
throw new Error(`Specified Tailwind config file "${configFile}" doesn't exist.`)
|
||||
}
|
||||
|
||||
return function (css, opts) {
|
||||
getModuleDependencies(configFile).forEach((mdl) => {
|
||||
opts.messages.push({
|
||||
type: 'dependency',
|
||||
parent: css.source.input.file,
|
||||
file: mdl.file,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -4,14 +4,14 @@ import postcss from 'postcss'
|
|||
import dlv from 'dlv'
|
||||
import selectorParser from 'postcss-selector-parser'
|
||||
|
||||
import transformThemeValue from '../../util/transformThemeValue'
|
||||
import parseObjectStyles from '../../util/parseObjectStyles'
|
||||
import prefixSelector from '../../util/prefixSelector'
|
||||
import isPlainObject from '../../util/isPlainObject'
|
||||
import escapeClassName from '../../util/escapeClassName'
|
||||
import nameClass from '../../util/nameClass'
|
||||
import { coerceValue } from '../../util/pluginUtils'
|
||||
import bigSign from '../../util/bigSign'
|
||||
import transformThemeValue from '../util/transformThemeValue'
|
||||
import parseObjectStyles from '../util/parseObjectStyles'
|
||||
import prefixSelector from '../util/prefixSelector'
|
||||
import isPlainObject from '../util/isPlainObject'
|
||||
import escapeClassName from '../util/escapeClassName'
|
||||
import nameClass from '../util/nameClass'
|
||||
import { coerceValue } from '../util/pluginUtils'
|
||||
import bigSign from '../util/bigSign'
|
||||
import corePlugins from '../corePlugins'
|
||||
import * as sharedState from './sharedState'
|
||||
import { env } from './sharedState'
|
||||
|
|
@ -5,17 +5,17 @@ import fastGlob from 'fast-glob'
|
|||
import LRU from 'quick-lru'
|
||||
import normalizePath from 'normalize-path'
|
||||
|
||||
import hash from '../../util/hashConfig'
|
||||
import getModuleDependencies from '../../lib/getModuleDependencies'
|
||||
import hash from '../util/hashConfig'
|
||||
import getModuleDependencies from '../lib/getModuleDependencies'
|
||||
|
||||
import resolveConfig from '../../../resolveConfig'
|
||||
import resolveConfig from '../../resolveConfig'
|
||||
|
||||
import resolveConfigPath from '../../util/resolveConfigPath'
|
||||
import resolveConfigPath from '../util/resolveConfigPath'
|
||||
|
||||
import { env } from './sharedState'
|
||||
|
||||
import { getContext, getFileModifiedMap } from './setupContextUtils'
|
||||
import parseDependency from '../../util/parseDependency'
|
||||
import parseDependency from '../util/parseDependency'
|
||||
|
||||
let configPathCache = new LRU({ maxSize: 100 })
|
||||
|
||||
|
|
@ -26,13 +26,9 @@ function getCandidateFiles(context, tailwindConfig) {
|
|||
return candidateFilesCache.get(context)
|
||||
}
|
||||
|
||||
let purgeContent = Array.isArray(tailwindConfig.purge)
|
||||
? tailwindConfig.purge
|
||||
: tailwindConfig.purge.content
|
||||
|
||||
let candidateFiles = purgeContent
|
||||
let candidateFiles = tailwindConfig.content.content
|
||||
.filter((item) => typeof item === 'string')
|
||||
.map((purgePath) => normalizePath(path.resolve(purgePath)))
|
||||
.map((contentPath) => normalizePath(path.resolve(contentPath)))
|
||||
|
||||
return candidateFilesCache.set(context, candidateFiles).get(context)
|
||||
}
|
||||
|
|
@ -81,29 +77,9 @@ function getTailwindConfig(configOrPath) {
|
|||
}
|
||||
|
||||
function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
|
||||
let changedContent = (
|
||||
Array.isArray(context.tailwindConfig.purge)
|
||||
? context.tailwindConfig.purge
|
||||
: context.tailwindConfig.purge.content
|
||||
)
|
||||
let changedContent = context.tailwindConfig.content.content
|
||||
.filter((item) => typeof item.raw === 'string')
|
||||
.concat(
|
||||
(context.tailwindConfig.purge?.safelist ?? []).map((content) => {
|
||||
if (typeof content === 'string') {
|
||||
return { raw: content, extension: 'html' }
|
||||
}
|
||||
|
||||
if (content instanceof RegExp) {
|
||||
throw new Error(
|
||||
"Values inside 'purge.safelist' can only be of type 'string', found 'regex'."
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Values inside 'purge.safelist' can only be of type 'string', found '${typeof content}'.`
|
||||
)
|
||||
})
|
||||
)
|
||||
.concat(context.tailwindConfig.content.safelist)
|
||||
.map(({ raw, extension }) => ({ content: raw, extension }))
|
||||
|
||||
for (let changedFile of resolveChangedFiles(candidateFiles, fileModifiedMap)) {
|
||||
|
|
@ -7,11 +7,11 @@ import fastGlob from 'fast-glob'
|
|||
import LRU from 'quick-lru'
|
||||
import normalizePath from 'normalize-path'
|
||||
|
||||
import hash from '../../util/hashConfig'
|
||||
import log from '../../util/log'
|
||||
import getModuleDependencies from '../../lib/getModuleDependencies'
|
||||
import resolveConfig from '../../../resolveConfig'
|
||||
import resolveConfigPath from '../../util/resolveConfigPath'
|
||||
import hash from '../util/hashConfig'
|
||||
import log from '../util/log'
|
||||
import getModuleDependencies from '../lib/getModuleDependencies'
|
||||
import resolveConfig from '../../resolveConfig'
|
||||
import resolveConfigPath from '../util/resolveConfigPath'
|
||||
import { getContext } from './setupContextUtils'
|
||||
|
||||
// This is used to trigger rebuilds. Just updating the timestamp
|
||||
|
|
@ -147,13 +147,9 @@ function getCandidateFiles(context, tailwindConfig) {
|
|||
return candidateFilesCache.get(context)
|
||||
}
|
||||
|
||||
let purgeContent = Array.isArray(tailwindConfig.purge)
|
||||
? tailwindConfig.purge
|
||||
: tailwindConfig.purge.content
|
||||
|
||||
let candidateFiles = purgeContent
|
||||
let candidateFiles = tailwindConfig.content.content
|
||||
.filter((item) => typeof item === 'string')
|
||||
.map((purgePath) => normalizePath(path.resolve(purgePath)))
|
||||
.map((contentPath) => normalizePath(path.resolve(contentPath)))
|
||||
|
||||
return candidateFilesCache.set(context, candidateFiles).get(context)
|
||||
}
|
||||
|
|
@ -189,29 +185,9 @@ function getTailwindConfig(configOrPath) {
|
|||
}
|
||||
|
||||
function resolvedChangedContent(context, candidateFiles) {
|
||||
let changedContent = (
|
||||
Array.isArray(context.tailwindConfig.purge)
|
||||
? context.tailwindConfig.purge
|
||||
: context.tailwindConfig.purge.content
|
||||
)
|
||||
let changedContent = context.tailwindConfig.content.content
|
||||
.filter((item) => typeof item.raw === 'string')
|
||||
.concat(
|
||||
(context.tailwindConfig.purge?.safelist ?? []).map((content) => {
|
||||
if (typeof content === 'string') {
|
||||
return { raw: content, extension: 'html' }
|
||||
}
|
||||
|
||||
if (content instanceof RegExp) {
|
||||
throw new Error(
|
||||
"Values inside 'purge.safelist' can only be of type 'string', found 'regex'."
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Values inside 'purge.safelist' can only be of type 'string', found '${typeof content}'.`
|
||||
)
|
||||
})
|
||||
)
|
||||
.concat(context.tailwindConfig.content.safelist)
|
||||
.map(({ raw, extension }) => ({ content: raw, extension }))
|
||||
|
||||
for (let changedFile of resolveChangedFiles(context, candidateFiles)) {
|
||||
|
|
@ -1,409 +0,0 @@
|
|||
import _ from 'lodash'
|
||||
import selectorParser from 'postcss-selector-parser'
|
||||
import postcss from 'postcss'
|
||||
import didYouMean from 'didyoumean'
|
||||
import substituteTailwindAtRules from './substituteTailwindAtRules'
|
||||
import evaluateTailwindFunctions from './evaluateTailwindFunctions'
|
||||
import substituteVariantsAtRules from './substituteVariantsAtRules'
|
||||
import substituteResponsiveAtRules from './substituteResponsiveAtRules'
|
||||
import convertLayerAtRulesToControlComments from './convertLayerAtRulesToControlComments'
|
||||
import substituteScreenAtRules from './substituteScreenAtRules'
|
||||
import prefixSelector from '../util/prefixSelector'
|
||||
import { useMemo } from '../util/useMemo'
|
||||
|
||||
function hasAtRule(css, atRule, condition) {
|
||||
let found = false
|
||||
|
||||
css.walkAtRules(
|
||||
atRule,
|
||||
condition === undefined
|
||||
? () => {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
: (node) => {
|
||||
if (condition(node)) {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
function cloneWithoutChildren(node) {
|
||||
if (node.type === 'atrule') {
|
||||
return postcss.atRule({ name: node.name, params: node.params })
|
||||
}
|
||||
|
||||
if (node.type === 'rule') {
|
||||
return postcss.rule({ name: node.name, selectors: node.selectors })
|
||||
}
|
||||
|
||||
const clone = node.clone()
|
||||
clone.removeAll()
|
||||
return clone
|
||||
}
|
||||
|
||||
const tailwindApplyPlaceholder = selectorParser.attribute({
|
||||
attribute: '__TAILWIND-APPLY-PLACEHOLDER__',
|
||||
})
|
||||
|
||||
function generateRulesFromApply({ rule, utilityName: className, classPosition }, replaceWiths) {
|
||||
const parser = selectorParser((selectors) => {
|
||||
let i = 0
|
||||
selectors.walkClasses((c) => {
|
||||
if (classPosition === i++ && c.value === className) {
|
||||
c.replaceWith(tailwindApplyPlaceholder)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const processedSelectors = _.flatMap(rule.selectors, (selector) => {
|
||||
// You could argue we should make this replacement at the AST level, but if we believe
|
||||
// the placeholder string is safe from collisions then it is safe to do this is a simple
|
||||
// string replacement, and much, much faster.
|
||||
return replaceWiths.map((replaceWith) =>
|
||||
parser.processSync(selector).replace('[__TAILWIND-APPLY-PLACEHOLDER__]', replaceWith)
|
||||
)
|
||||
})
|
||||
|
||||
const cloned = rule.clone()
|
||||
let current = cloned
|
||||
let parent = rule.parent
|
||||
|
||||
while (parent && parent.type !== 'root') {
|
||||
const parentClone = cloneWithoutChildren(parent)
|
||||
|
||||
parentClone.append(current)
|
||||
current.parent = parentClone
|
||||
current = parentClone
|
||||
parent = parent.parent
|
||||
}
|
||||
|
||||
cloned.selectors = processedSelectors
|
||||
return current
|
||||
}
|
||||
|
||||
const extractUtilityNamesParser = selectorParser((selectors) => {
|
||||
let classes = []
|
||||
selectors.walkClasses((c) => classes.push(c.value))
|
||||
return classes
|
||||
})
|
||||
|
||||
const extractUtilityNames = useMemo(
|
||||
(selector) => extractUtilityNamesParser.transformSync(selector),
|
||||
(selector) => selector
|
||||
)
|
||||
|
||||
const cloneRuleWithParent = useMemo(
|
||||
(rule) => rule.clone({ parent: rule.parent }),
|
||||
(rule) => rule
|
||||
)
|
||||
|
||||
function buildCssUtilityMap(css, startIndex) {
|
||||
let index = startIndex
|
||||
const utilityMap = {}
|
||||
|
||||
function handle(getRule, rule) {
|
||||
const utilityNames = extractUtilityNames(rule.selector)
|
||||
|
||||
utilityNames.forEach((utilityName, i) => {
|
||||
if (utilityMap[utilityName] === undefined) {
|
||||
utilityMap[utilityName] = []
|
||||
}
|
||||
|
||||
utilityMap[utilityName].push({
|
||||
index,
|
||||
utilityName,
|
||||
classPosition: i,
|
||||
...getRule(rule),
|
||||
})
|
||||
index++
|
||||
})
|
||||
}
|
||||
|
||||
// This is the end user's css. This might contain rules that we want to
|
||||
// apply. We want immediate copies of everything in case that we have user
|
||||
// defined classes that are recursively applied. Down below we are modifying
|
||||
// the rules directly. We could do a better solution where we keep track of a
|
||||
// dependency tree, but that is a bit more complex. Might revisit later,
|
||||
// we'll see how this turns out!
|
||||
css.walkRules(handle.bind(null, (rule) => ({ rule: cloneRuleWithParent(rule) })))
|
||||
|
||||
return utilityMap
|
||||
}
|
||||
|
||||
const buildLookupTreeUtilityMap = useMemo(
|
||||
(lookupTree) => {
|
||||
let index = 0
|
||||
const utilityMap = {}
|
||||
|
||||
function handle(getRule, rule) {
|
||||
const utilityNames = extractUtilityNames(rule.selector)
|
||||
|
||||
utilityNames.forEach((utilityName, i) => {
|
||||
if (utilityMap[utilityName] === undefined) {
|
||||
utilityMap[utilityName] = []
|
||||
}
|
||||
|
||||
utilityMap[utilityName].push({
|
||||
index,
|
||||
utilityName,
|
||||
classPosition: i,
|
||||
...getRule(rule),
|
||||
})
|
||||
index++
|
||||
})
|
||||
}
|
||||
|
||||
// Lookup tree is the big lookup tree, making the rule lazy allows us to save
|
||||
// some memory because we don't need everything.
|
||||
lookupTree.walkRules(
|
||||
handle.bind(null, (rule) => ({
|
||||
get rule() {
|
||||
return cloneRuleWithParent(rule)
|
||||
},
|
||||
}))
|
||||
)
|
||||
|
||||
return utilityMap
|
||||
},
|
||||
(tree) => tree
|
||||
)
|
||||
|
||||
function mergeAdjacentRules(initialRule, rulesToInsert) {
|
||||
let previousRule = initialRule
|
||||
|
||||
rulesToInsert.forEach((toInsert) => {
|
||||
if (
|
||||
toInsert.type === 'rule' &&
|
||||
previousRule.type === 'rule' &&
|
||||
toInsert.selector === previousRule.selector
|
||||
) {
|
||||
previousRule.append(toInsert.nodes)
|
||||
} else if (
|
||||
toInsert.type === 'atrule' &&
|
||||
previousRule.type === 'atrule' &&
|
||||
toInsert.params === previousRule.params
|
||||
) {
|
||||
const merged = mergeAdjacentRules(
|
||||
previousRule.nodes[previousRule.nodes.length - 1],
|
||||
toInsert.nodes
|
||||
)
|
||||
|
||||
previousRule.append(merged)
|
||||
} else {
|
||||
previousRule = toInsert
|
||||
}
|
||||
|
||||
toInsert.walk((n) => {
|
||||
if (n.nodes && n.nodes.length === 0) {
|
||||
n.remove()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return rulesToInsert.filter((r) => r.nodes.length > 0)
|
||||
}
|
||||
|
||||
function makeExtractUtilityRules(css, lookupTree, config) {
|
||||
const lookupTreeUtilityMap = buildLookupTreeUtilityMap(lookupTree)
|
||||
const lookupTreeUtilityMapKeys = Object.keys(lookupTreeUtilityMap)
|
||||
const utilityMap = buildCssUtilityMap(css, lookupTreeUtilityMapKeys.length)
|
||||
|
||||
function getUtility(utilityName) {
|
||||
const utility = []
|
||||
if (lookupTreeUtilityMap[utilityName]) {
|
||||
utility.push(...lookupTreeUtilityMap[utilityName])
|
||||
}
|
||||
if (utilityMap[utilityName]) {
|
||||
utility.push(...utilityMap[utilityName])
|
||||
}
|
||||
if (utility.length > 0) return utility
|
||||
}
|
||||
|
||||
return function extractUtilityRules(utilityNames, rule) {
|
||||
const combined = []
|
||||
|
||||
utilityNames.forEach((utilityName) => {
|
||||
const utility = getUtility(utilityName)
|
||||
if (utility === undefined) {
|
||||
// Look for prefixed utility in case the user has goofed
|
||||
const prefixedUtilityName = prefixSelector(config.prefix, `.${utilityName}`).slice(1)
|
||||
|
||||
const prefixedUtility = getUtility(prefixedUtilityName)
|
||||
if (prefixedUtility !== undefined) {
|
||||
throw rule.error(
|
||||
`The \`${utilityName}\` class does not exist, but \`${prefixedUtilityName}\` does. Did you forget the prefix?`
|
||||
)
|
||||
}
|
||||
|
||||
const suggestedClass = didYouMean(
|
||||
utilityName,
|
||||
Object.keys(utilityMap).concat(lookupTreeUtilityMapKeys)
|
||||
)
|
||||
const suggestionMessage = suggestedClass ? `, but \`${suggestedClass}\` does` : ''
|
||||
|
||||
throw rule.error(
|
||||
`The \`${utilityName}\` class does not exist${suggestionMessage}. If you're sure that \`${utilityName}\` exists, make sure that any \`@import\` statements are being properly processed before Tailwind CSS sees your CSS, as \`@apply\` can only be used for classes in the same CSS tree.`,
|
||||
{ word: utilityName }
|
||||
)
|
||||
}
|
||||
|
||||
combined.push(...utility)
|
||||
})
|
||||
|
||||
return combined.sort((a, b) => a.index - b.index)
|
||||
}
|
||||
}
|
||||
|
||||
function findParent(rule, predicate) {
|
||||
let parent = rule.parent
|
||||
while (parent) {
|
||||
if (predicate(parent)) {
|
||||
return parent
|
||||
}
|
||||
|
||||
parent = parent.parent
|
||||
}
|
||||
|
||||
throw new Error('No parent could be found')
|
||||
}
|
||||
|
||||
function processApplyAtRules(css, lookupTree, config) {
|
||||
const extractUtilityRules = makeExtractUtilityRules(css, lookupTree, config)
|
||||
|
||||
do {
|
||||
css.walkAtRules('apply', (applyRule) => {
|
||||
const parent = applyRule.parent // Direct parent
|
||||
const nearestParentRule = findParent(applyRule, (r) => r.type === 'rule')
|
||||
const currentUtilityNames = extractUtilityNames(nearestParentRule.selector)
|
||||
|
||||
const [importantEntries, applyUtilityNames, important = importantEntries.length > 0] =
|
||||
_.partition(applyRule.params.split(/[\s\t\n]+/g), (n) => n === '!important')
|
||||
|
||||
if (_.intersection(applyUtilityNames, currentUtilityNames).length > 0) {
|
||||
const currentUtilityName = _.intersection(applyUtilityNames, currentUtilityNames)[0]
|
||||
throw parent.error(
|
||||
`You cannot \`@apply\` the \`${currentUtilityName}\` utility here because it creates a circular dependency.`
|
||||
)
|
||||
}
|
||||
|
||||
// Extract any post-apply declarations and re-insert them after apply rules
|
||||
const afterRule = parent.clone({ raws: {} })
|
||||
afterRule.nodes = afterRule.nodes.slice(parent.index(applyRule) + 1)
|
||||
parent.nodes = parent.nodes.slice(0, parent.index(applyRule) + 1)
|
||||
|
||||
// Sort applys to match CSS source order
|
||||
const applys = extractUtilityRules(applyUtilityNames, applyRule)
|
||||
|
||||
// Get new rules with the utility portion of the selector replaced with the new selector
|
||||
const rulesToInsert = []
|
||||
|
||||
applys.forEach(
|
||||
nearestParentRule === parent
|
||||
? (util) => rulesToInsert.push(generateRulesFromApply(util, parent.selectors))
|
||||
: (util) => util.rule.nodes.forEach((n) => afterRule.append(n.clone()))
|
||||
)
|
||||
|
||||
rulesToInsert.forEach((rule) => {
|
||||
if (rule.type === 'atrule') {
|
||||
rule.walkRules((rule) => {
|
||||
rule.__tailwind = { ...rule.__tailwind, important }
|
||||
})
|
||||
} else {
|
||||
rule.__tailwind = { ...rule.__tailwind, important }
|
||||
}
|
||||
})
|
||||
|
||||
const { nodes } = _.tap(postcss.root({ nodes: rulesToInsert }), (root) => {
|
||||
root.walkDecls((d) => {
|
||||
d.important = important
|
||||
})
|
||||
})
|
||||
|
||||
const mergedRules = mergeAdjacentRules(nearestParentRule, [...nodes, afterRule])
|
||||
|
||||
applyRule.remove()
|
||||
parent.after(mergedRules)
|
||||
|
||||
// If the base rule has nothing in it (all applys were pseudo or responsive variants),
|
||||
// remove the rule fuggit.
|
||||
if (parent.nodes.length === 0) {
|
||||
parent.remove()
|
||||
}
|
||||
})
|
||||
|
||||
// We already know that we have at least 1 @apply rule. Otherwise this
|
||||
// function would not have been called. Therefore we can execute this code
|
||||
// at least once. This also means that in the best case scenario we only
|
||||
// call this 2 times, instead of 3 times.
|
||||
// 1st time -> before we call this function
|
||||
// 2nd time -> when we check if we have to do this loop again (because do {} while (check))
|
||||
// .. instead of
|
||||
// 1st time -> before we call this function
|
||||
// 2nd time -> when we check the first time (because while (check) do {})
|
||||
// 3rd time -> when we re-check to see if we should do this loop again
|
||||
} while (hasAtRule(css, 'apply'))
|
||||
|
||||
return css
|
||||
}
|
||||
|
||||
let defaultTailwindTree = new Map()
|
||||
|
||||
export default function substituteClassApplyAtRules(config, getProcessedPlugins, configChanged) {
|
||||
return function (css) {
|
||||
// We can stop already when we don't have any @apply rules. Vue users: you're welcome!
|
||||
if (!hasAtRule(css, 'apply')) {
|
||||
return css
|
||||
}
|
||||
|
||||
let requiredTailwindAtRules = ['base', 'components', 'utilities']
|
||||
if (
|
||||
hasAtRule(css, 'tailwind', (node) => {
|
||||
let idx = requiredTailwindAtRules.indexOf(node.params)
|
||||
if (idx !== -1) requiredTailwindAtRules.splice(idx, 1)
|
||||
if (requiredTailwindAtRules.length <= 0) return true
|
||||
return false
|
||||
})
|
||||
) {
|
||||
// Tree already contains all the at rules (requiredTailwindAtRules)
|
||||
return processApplyAtRules(css, postcss.root(), config)
|
||||
}
|
||||
|
||||
let lookupKey = requiredTailwindAtRules.join(',')
|
||||
|
||||
// We mutated the `requiredTailwindAtRules`, but when we hit this point in
|
||||
// time, it means that we don't have all the atrules. The missing atrules
|
||||
// are listed inside the requiredTailwindAtRules, which we can use to fill
|
||||
// in the missing pieces.
|
||||
//
|
||||
// Important for <style> blocks in Vue components.
|
||||
const generateLookupTree =
|
||||
configChanged || !defaultTailwindTree.has(lookupKey)
|
||||
? () => {
|
||||
return postcss([
|
||||
substituteTailwindAtRules(config, getProcessedPlugins()),
|
||||
evaluateTailwindFunctions({ tailwindConfig: config }),
|
||||
substituteVariantsAtRules(config, getProcessedPlugins()),
|
||||
substituteResponsiveAtRules(config),
|
||||
convertLayerAtRulesToControlComments(config),
|
||||
substituteScreenAtRules({ tailwindConfig: config }),
|
||||
])
|
||||
.process(requiredTailwindAtRules.map((rule) => `@tailwind ${rule};`).join('\n'), {
|
||||
from: __filename,
|
||||
})
|
||||
.then((result) => {
|
||||
defaultTailwindTree.set(lookupKey, result)
|
||||
return result
|
||||
})
|
||||
}
|
||||
: () => Promise.resolve(defaultTailwindTree.get(lookupKey))
|
||||
|
||||
return generateLookupTree().then((result) => {
|
||||
return processApplyAtRules(css, result.root, config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
import _ from 'lodash'
|
||||
import postcss from 'postcss'
|
||||
|
||||
function updateSource(nodes, source) {
|
||||
return _.tap(Array.isArray(nodes) ? postcss.root({ nodes }) : nodes, (tree) => {
|
||||
tree.walk((node) => (node.source = source))
|
||||
})
|
||||
}
|
||||
|
||||
export default function (
|
||||
_config,
|
||||
{ base: pluginBase, components: pluginComponents, utilities: pluginUtilities }
|
||||
) {
|
||||
return function (css) {
|
||||
css.walkAtRules('import', (atRule) => {
|
||||
if (atRule.params === '"tailwindcss/base"' || atRule.params === "'tailwindcss/base'") {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'base'
|
||||
}
|
||||
|
||||
if (
|
||||
atRule.params === '"tailwindcss/components"' ||
|
||||
atRule.params === "'tailwindcss/components'"
|
||||
) {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'components'
|
||||
}
|
||||
|
||||
if (
|
||||
atRule.params === '"tailwindcss/utilities"' ||
|
||||
atRule.params === "'tailwindcss/utilities'"
|
||||
) {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'utilities'
|
||||
}
|
||||
|
||||
if (atRule.params === '"tailwindcss/screens"' || atRule.params === "'tailwindcss/screens'") {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'screens'
|
||||
}
|
||||
})
|
||||
|
||||
let includesScreensExplicitly = false
|
||||
const layers = {
|
||||
base: [],
|
||||
components: [],
|
||||
utilities: [],
|
||||
}
|
||||
|
||||
css.walkAtRules('layer', (atRule) => {
|
||||
if (!['base', 'components', 'utilities'].includes(atRule.params)) {
|
||||
return
|
||||
}
|
||||
|
||||
layers[atRule.params].push(atRule)
|
||||
})
|
||||
|
||||
css.walkAtRules('tailwind', (atRule) => {
|
||||
if (atRule.params === 'preflight') {
|
||||
// prettier-ignore
|
||||
throw atRule.error("`@tailwind preflight` is not a valid at-rule in Tailwind v2.0, use `@tailwind base` instead.", { word: 'preflight' })
|
||||
}
|
||||
|
||||
if (atRule.params === 'base') {
|
||||
atRule.after(layers.base)
|
||||
atRule.after(updateSource(pluginBase, atRule.source))
|
||||
}
|
||||
|
||||
if (atRule.params === 'components') {
|
||||
atRule.after(layers.components)
|
||||
atRule.after(updateSource(pluginComponents, atRule.source))
|
||||
}
|
||||
|
||||
if (atRule.params === 'utilities') {
|
||||
atRule.after(layers.utilities)
|
||||
atRule.after(updateSource(pluginUtilities, atRule.source))
|
||||
}
|
||||
|
||||
if (atRule.params === 'screens') {
|
||||
includesScreensExplicitly = true
|
||||
}
|
||||
})
|
||||
|
||||
if (!includesScreensExplicitly) {
|
||||
css.append([postcss.atRule({ name: 'tailwind', params: 'screens' })])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
import _ from 'lodash'
|
||||
import postcss from 'postcss'
|
||||
import selectorParser from 'postcss-selector-parser'
|
||||
import generateVariantFunction from '../util/generateVariantFunction'
|
||||
import prefixSelector from '../util/prefixSelector'
|
||||
import buildSelectorVariant from '../util/buildSelectorVariant'
|
||||
|
||||
function generatePseudoClassVariant(pseudoClass, selectorPrefix = pseudoClass) {
|
||||
return generateVariantFunction(({ modifySelectors, separator }) => {
|
||||
const parser = selectorParser((selectors) => {
|
||||
selectors.walkClasses((sel) => {
|
||||
sel.value = `${selectorPrefix}${separator}${sel.value}`
|
||||
sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `:${pseudoClass}` }))
|
||||
})
|
||||
})
|
||||
|
||||
return modifySelectors(({ selector }) => parser.processSync(selector))
|
||||
})
|
||||
}
|
||||
|
||||
function ensureIncludesDefault(variants) {
|
||||
return variants.includes('DEFAULT') ? variants : ['DEFAULT', ...variants]
|
||||
}
|
||||
|
||||
const defaultVariantGenerators = (config) => ({
|
||||
DEFAULT: generateVariantFunction(() => {}),
|
||||
dark: generateVariantFunction(
|
||||
({ container, separator, modifySelectors }) => {
|
||||
if (config.darkMode === false) {
|
||||
return postcss.root()
|
||||
}
|
||||
|
||||
if (config.darkMode === 'media') {
|
||||
const modified = modifySelectors(({ selector }) => {
|
||||
return buildSelectorVariant(selector, 'dark', separator, (message) => {
|
||||
throw container.error(message)
|
||||
})
|
||||
})
|
||||
const mediaQuery = postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-color-scheme: dark)',
|
||||
})
|
||||
mediaQuery.append(modified)
|
||||
container.append(mediaQuery)
|
||||
return container
|
||||
}
|
||||
|
||||
if (config.darkMode === 'class') {
|
||||
const modified = modifySelectors(({ selector }) => {
|
||||
return buildSelectorVariant(selector, 'dark', separator, (message) => {
|
||||
throw container.error(message)
|
||||
})
|
||||
})
|
||||
|
||||
modified.walkRules((rule) => {
|
||||
rule.selectors = rule.selectors.map((selector) => {
|
||||
return `${prefixSelector(config.prefix, '.dark')} ${selector}`
|
||||
})
|
||||
})
|
||||
|
||||
return modified
|
||||
}
|
||||
|
||||
throw new Error("The `darkMode` config option must be either 'media' or 'class'.")
|
||||
},
|
||||
{ unstable_stack: true }
|
||||
),
|
||||
'motion-safe': generateVariantFunction(
|
||||
({ container, separator, modifySelectors }) => {
|
||||
const modified = modifySelectors(({ selector }) => {
|
||||
return buildSelectorVariant(selector, 'motion-safe', separator, (message) => {
|
||||
throw container.error(message)
|
||||
})
|
||||
})
|
||||
const mediaQuery = postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-reduced-motion: no-preference)',
|
||||
})
|
||||
mediaQuery.append(modified)
|
||||
container.append(mediaQuery)
|
||||
},
|
||||
{ unstable_stack: true }
|
||||
),
|
||||
'motion-reduce': generateVariantFunction(
|
||||
({ container, separator, modifySelectors }) => {
|
||||
const modified = modifySelectors(({ selector }) => {
|
||||
return buildSelectorVariant(selector, 'motion-reduce', separator, (message) => {
|
||||
throw container.error(message)
|
||||
})
|
||||
})
|
||||
const mediaQuery = postcss.atRule({
|
||||
name: 'media',
|
||||
params: '(prefers-reduced-motion: reduce)',
|
||||
})
|
||||
mediaQuery.append(modified)
|
||||
container.append(mediaQuery)
|
||||
},
|
||||
{ unstable_stack: true }
|
||||
),
|
||||
'group-hover': generateVariantFunction(({ modifySelectors, separator }) => {
|
||||
const parser = selectorParser((selectors) => {
|
||||
selectors.walkClasses((sel) => {
|
||||
sel.value = `group-hover${separator}${sel.value}`
|
||||
sel.parent.insertBefore(
|
||||
sel,
|
||||
selectorParser().astSync(prefixSelector(config.prefix, '.group:hover '))
|
||||
)
|
||||
})
|
||||
})
|
||||
return modifySelectors(({ selector }) => parser.processSync(selector))
|
||||
}),
|
||||
'group-focus': generateVariantFunction(({ modifySelectors, separator }) => {
|
||||
const parser = selectorParser((selectors) => {
|
||||
selectors.walkClasses((sel) => {
|
||||
sel.value = `group-focus${separator}${sel.value}`
|
||||
sel.parent.insertBefore(
|
||||
sel,
|
||||
selectorParser().astSync(prefixSelector(config.prefix, '.group:focus '))
|
||||
)
|
||||
})
|
||||
})
|
||||
return modifySelectors(({ selector }) => parser.processSync(selector))
|
||||
}),
|
||||
hover: generatePseudoClassVariant('hover'),
|
||||
'focus-within': generatePseudoClassVariant('focus-within'),
|
||||
'focus-visible': generatePseudoClassVariant('focus-visible'),
|
||||
'read-only': generatePseudoClassVariant('read-only'),
|
||||
focus: generatePseudoClassVariant('focus'),
|
||||
active: generatePseudoClassVariant('active'),
|
||||
visited: generatePseudoClassVariant('visited'),
|
||||
disabled: generatePseudoClassVariant('disabled'),
|
||||
checked: generatePseudoClassVariant('checked'),
|
||||
first: generatePseudoClassVariant('first-child', 'first'),
|
||||
last: generatePseudoClassVariant('last-child', 'last'),
|
||||
odd: generatePseudoClassVariant('nth-child(odd)', 'odd'),
|
||||
even: generatePseudoClassVariant('nth-child(even)', 'even'),
|
||||
empty: generatePseudoClassVariant('empty'),
|
||||
})
|
||||
|
||||
function prependStackableVariants(atRule, variants, stackableVariants) {
|
||||
if (!_.some(variants, (v) => stackableVariants.includes(v))) {
|
||||
return variants
|
||||
}
|
||||
|
||||
if (_.every(variants, (v) => stackableVariants.includes(v))) {
|
||||
return variants
|
||||
}
|
||||
|
||||
const variantsParent = postcss.atRule({
|
||||
name: 'variants',
|
||||
params: variants.filter((v) => stackableVariants.includes(v)).join(', '),
|
||||
})
|
||||
atRule.before(variantsParent)
|
||||
variantsParent.append(atRule)
|
||||
variants = _.without(variants, ...stackableVariants)
|
||||
|
||||
return variants
|
||||
}
|
||||
|
||||
export default function (config, { variantGenerators: pluginVariantGenerators }) {
|
||||
return function (css) {
|
||||
const variantGenerators = {
|
||||
...defaultVariantGenerators(config),
|
||||
...pluginVariantGenerators,
|
||||
}
|
||||
|
||||
const stackableVariants = Object.entries(variantGenerators)
|
||||
.filter(([_variant, { options }]) => options.unstable_stack)
|
||||
.map(([variant]) => variant)
|
||||
|
||||
let variantsFound = false
|
||||
|
||||
do {
|
||||
variantsFound = false
|
||||
css.walkAtRules('variants', (atRule) => {
|
||||
variantsFound = true
|
||||
|
||||
let variants = postcss.list.comma(atRule.params).filter((variant) => variant !== '')
|
||||
|
||||
if (variants.includes('responsive')) {
|
||||
const responsiveParent = postcss.atRule({ name: 'responsive' })
|
||||
atRule.before(responsiveParent)
|
||||
responsiveParent.append(atRule)
|
||||
}
|
||||
|
||||
const remainingVariants = prependStackableVariants(atRule, variants, stackableVariants)
|
||||
|
||||
_.forEach(_.without(ensureIncludesDefault(remainingVariants), 'responsive'), (variant) => {
|
||||
if (!variantGenerators[variant]) {
|
||||
throw new Error(
|
||||
`Your config mentions the "${variant}" variant, but "${variant}" doesn't appear to be a variant. Did you forget or misconfigure a plugin that supplies that variant?`
|
||||
)
|
||||
}
|
||||
variantGenerators[variant].handler(atRule, config)
|
||||
})
|
||||
|
||||
atRule.remove()
|
||||
})
|
||||
} while (variantsFound)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ export default function () {
|
|||
return function ({ matchUtilities, theme, variants, prefix }) {
|
||||
let prefixName = (name) => prefix(`.${name}`).slice(1)
|
||||
let keyframes = Object.fromEntries(
|
||||
Object.entries(theme('keyframes') || {}).map(([key, value]) => {
|
||||
Object.entries(theme('keyframes') ?? {}).map(([key, value]) => {
|
||||
return [
|
||||
key,
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-blur': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-blur': `blur(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-brightness': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-brightness': `brightness(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-contrast': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-contrast': `contrast(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,69 +1,38 @@
|
|||
export default function () {
|
||||
return function ({ config, addBase, addUtilities, variants }) {
|
||||
if (config('mode') === 'jit') {
|
||||
addBase({
|
||||
'@defaults backdrop-filter': {
|
||||
'--tw-backdrop-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-opacity': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-filter': [
|
||||
'var(--tw-backdrop-blur)',
|
||||
'var(--tw-backdrop-brightness)',
|
||||
'var(--tw-backdrop-contrast)',
|
||||
'var(--tw-backdrop-grayscale)',
|
||||
'var(--tw-backdrop-hue-rotate)',
|
||||
'var(--tw-backdrop-invert)',
|
||||
'var(--tw-backdrop-opacity)',
|
||||
'var(--tw-backdrop-saturate)',
|
||||
'var(--tw-backdrop-sepia)',
|
||||
].join(' '),
|
||||
return function ({ addBase, addUtilities, variants }) {
|
||||
addBase({
|
||||
'@defaults backdrop-filter': {
|
||||
'--tw-backdrop-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-opacity': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-filter': [
|
||||
'var(--tw-backdrop-blur)',
|
||||
'var(--tw-backdrop-brightness)',
|
||||
'var(--tw-backdrop-contrast)',
|
||||
'var(--tw-backdrop-grayscale)',
|
||||
'var(--tw-backdrop-hue-rotate)',
|
||||
'var(--tw-backdrop-invert)',
|
||||
'var(--tw-backdrop-opacity)',
|
||||
'var(--tw-backdrop-saturate)',
|
||||
'var(--tw-backdrop-sepia)',
|
||||
].join(' '),
|
||||
},
|
||||
})
|
||||
addUtilities(
|
||||
{
|
||||
'.backdrop-filter': {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
},
|
||||
})
|
||||
addUtilities(
|
||||
{
|
||||
'.backdrop-filter': {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
},
|
||||
'.backdrop-filter-none': { 'backdrop-filter': 'none' },
|
||||
},
|
||||
variants('backdropFilter')
|
||||
)
|
||||
} else {
|
||||
addUtilities(
|
||||
{
|
||||
'.backdrop-filter': {
|
||||
'--tw-backdrop-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-opacity': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-backdrop-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'backdrop-filter': [
|
||||
'var(--tw-backdrop-blur)',
|
||||
'var(--tw-backdrop-brightness)',
|
||||
'var(--tw-backdrop-contrast)',
|
||||
'var(--tw-backdrop-grayscale)',
|
||||
'var(--tw-backdrop-hue-rotate)',
|
||||
'var(--tw-backdrop-invert)',
|
||||
'var(--tw-backdrop-opacity)',
|
||||
'var(--tw-backdrop-saturate)',
|
||||
'var(--tw-backdrop-sepia)',
|
||||
].join(' '),
|
||||
},
|
||||
'.backdrop-filter-none': { 'backdrop-filter': 'none' },
|
||||
},
|
||||
variants('backdropFilter')
|
||||
)
|
||||
}
|
||||
'.backdrop-filter-none': { 'backdrop-filter': 'none' },
|
||||
},
|
||||
variants('backdropFilter')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-grayscale': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-grayscale': `grayscale(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-hue-rotate': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-hue-rotate': `hue-rotate(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-invert': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-invert': `invert(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-opacity': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-opacity': `opacity(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-saturate': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-saturate': `saturate(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'backdrop-sepia': (value) => {
|
||||
return {
|
||||
'--tw-backdrop-sepia': `sepia(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults backdrop-filter': {},
|
||||
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
blur: (value) => {
|
||||
return {
|
||||
'--tw-blur': `blur(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,39 +2,21 @@ import flattenColorPalette from '../util/flattenColorPalette'
|
|||
import withAlphaVariable from '../util/withAlphaVariable'
|
||||
|
||||
export default function () {
|
||||
return function ({ config, addBase, matchUtilities, theme, variants, corePlugins }) {
|
||||
if (config('mode') === 'jit') {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
addBase({
|
||||
'@defaults border-width': {
|
||||
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
addBase({
|
||||
'@defaults border-width': withAlphaVariable({
|
||||
color: theme('borderColor.DEFAULT', 'currentColor'),
|
||||
property: 'border-color',
|
||||
variable: '--tw-border-opacity',
|
||||
}),
|
||||
})
|
||||
}
|
||||
return function ({ addBase, matchUtilities, theme, variants, corePlugins }) {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
addBase({
|
||||
'@defaults border-width': {
|
||||
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
addBase({
|
||||
'*, ::before, ::after': {
|
||||
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
addBase({
|
||||
'*, ::before, ::after': withAlphaVariable({
|
||||
color: theme('borderColor.DEFAULT', 'currentColor'),
|
||||
property: 'border-color',
|
||||
variable: '--tw-border-opacity',
|
||||
}),
|
||||
})
|
||||
}
|
||||
addBase({
|
||||
'@defaults border-width': withAlphaVariable({
|
||||
color: theme('borderColor.DEFAULT', 'currentColor'),
|
||||
property: 'border-color',
|
||||
variable: '--tw-border-opacity',
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
matchUtilities(
|
||||
|
|
@ -60,70 +42,66 @@ export default function () {
|
|||
}
|
||||
)
|
||||
|
||||
if (config('mode') === 'jit') {
|
||||
matchUtilities(
|
||||
{
|
||||
'border-t': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-top-color': value,
|
||||
}
|
||||
matchUtilities(
|
||||
{
|
||||
'border-t': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-top-color': value,
|
||||
}
|
||||
}
|
||||
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-top-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
'border-r': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-right-color': value,
|
||||
}
|
||||
}
|
||||
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-right-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
'border-b': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-bottom-color': value,
|
||||
}
|
||||
}
|
||||
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-bottom-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
'border-l': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-left-color': value,
|
||||
}
|
||||
}
|
||||
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-left-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-top-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
{
|
||||
values: (({ DEFAULT: _, ...colors }) => colors)(
|
||||
flattenColorPalette(theme('borderColor'))
|
||||
),
|
||||
variants: variants('borderColor'),
|
||||
type: 'color',
|
||||
}
|
||||
)
|
||||
}
|
||||
'border-r': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-right-color': value,
|
||||
}
|
||||
}
|
||||
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-right-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
'border-b': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-bottom-color': value,
|
||||
}
|
||||
}
|
||||
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-bottom-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
'border-l': (value) => {
|
||||
if (!corePlugins('borderOpacity')) {
|
||||
return {
|
||||
'border-left-color': value,
|
||||
}
|
||||
}
|
||||
|
||||
return withAlphaVariable({
|
||||
color: value,
|
||||
property: 'border-left-color',
|
||||
variable: '--tw-border-opacity',
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
values: (({ DEFAULT: _, ...colors }) => colors)(flattenColorPalette(theme('borderColor'))),
|
||||
variants: variants('borderColor'),
|
||||
type: 'color',
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,35 +2,17 @@ import { asLength } from '../util/pluginUtils'
|
|||
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||
|
||||
export default function () {
|
||||
return function (helpers) {
|
||||
if (helpers.config('mode') === 'jit') {
|
||||
createUtilityPlugin(
|
||||
'borderWidth',
|
||||
[
|
||||
['border', [['@defaults border-width', {}], 'border-width']],
|
||||
[
|
||||
['border-t', [['@defaults border-width', {}], 'border-top-width']],
|
||||
['border-r', [['@defaults border-width', {}], 'border-right-width']],
|
||||
['border-b', [['@defaults border-width', {}], 'border-bottom-width']],
|
||||
['border-l', [['@defaults border-width', {}], 'border-left-width']],
|
||||
],
|
||||
],
|
||||
{ resolveArbitraryValue: asLength }
|
||||
)(helpers)
|
||||
} else {
|
||||
createUtilityPlugin(
|
||||
'borderWidth',
|
||||
[
|
||||
['border', ['border-width']],
|
||||
[
|
||||
['border-t', ['border-top-width']],
|
||||
['border-r', ['border-right-width']],
|
||||
['border-b', ['border-bottom-width']],
|
||||
['border-l', ['border-left-width']],
|
||||
],
|
||||
],
|
||||
{ resolveArbitraryValue: asLength }
|
||||
)(helpers)
|
||||
}
|
||||
}
|
||||
return createUtilityPlugin(
|
||||
'borderWidth',
|
||||
[
|
||||
['border', [['@defaults border-width', {}], 'border-width']],
|
||||
[
|
||||
['border-t', [['@defaults border-width', {}], 'border-top-width']],
|
||||
['border-r', [['@defaults border-width', {}], 'border-right-width']],
|
||||
['border-b', [['@defaults border-width', {}], 'border-bottom-width']],
|
||||
['border-l', [['@defaults border-width', {}], 'border-left-width']],
|
||||
],
|
||||
],
|
||||
{ resolveArbitraryValue: asLength }
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,25 +8,14 @@ let defaultBoxShadow = [
|
|||
].join(', ')
|
||||
|
||||
export default function () {
|
||||
return function ({ config, matchUtilities, addBase, addUtilities, theme, variants }) {
|
||||
if (config('mode') === 'jit') {
|
||||
addBase({
|
||||
'@defaults box-shadow': {
|
||||
'--tw-ring-offset-shadow': '0 0 #0000',
|
||||
'--tw-ring-shadow': '0 0 #0000',
|
||||
'--tw-shadow': '0 0 #0000',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
addUtilities(
|
||||
{
|
||||
'*, ::before, ::after': {
|
||||
'--tw-shadow': '0 0 #0000',
|
||||
},
|
||||
},
|
||||
{ respectImportant: false }
|
||||
)
|
||||
}
|
||||
return function ({ matchUtilities, addBase, theme, variants }) {
|
||||
addBase({
|
||||
'@defaults box-shadow': {
|
||||
'--tw-ring-offset-shadow': '0 0 #0000',
|
||||
'--tw-ring-shadow': '0 0 #0000',
|
||||
'--tw-shadow': '0 0 #0000',
|
||||
},
|
||||
})
|
||||
|
||||
matchUtilities(
|
||||
{
|
||||
|
|
@ -34,7 +23,7 @@ export default function () {
|
|||
value = transformValue(value)
|
||||
|
||||
return {
|
||||
...(config('mode') === 'jit' ? { '@defaults box-shadow': {} } : {}),
|
||||
'@defaults box-shadow': {},
|
||||
'--tw-shadow': value === 'none' ? '0 0 #0000' : value,
|
||||
'box-shadow': defaultBoxShadow,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
brightness: (value) => {
|
||||
return {
|
||||
'--tw-brightness': `brightness(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
contrast: (value) => {
|
||||
return {
|
||||
'--tw-contrast': `contrast(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import _ from 'lodash'
|
|||
import nameClass from '../util/nameClass'
|
||||
|
||||
export default function () {
|
||||
return function ({ config, addUtilities, theme, variants }) {
|
||||
return function ({ addUtilities, theme, variants }) {
|
||||
const utilities = _.fromPairs(
|
||||
_.map(theme('dropShadow'), (value, modifier) => {
|
||||
return [
|
||||
|
|
@ -11,12 +11,8 @@ export default function () {
|
|||
'--tw-drop-shadow': Array.isArray(value)
|
||||
? value.map((v) => `drop-shadow(${v})`).join(' ')
|
||||
: `drop-shadow(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,66 +1,35 @@
|
|||
export default function () {
|
||||
return function ({ config, addBase, addUtilities, variants }) {
|
||||
if (config('mode') === 'jit') {
|
||||
addBase({
|
||||
'@defaults filter': {
|
||||
'--tw-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-drop-shadow': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-filter': [
|
||||
'var(--tw-blur)',
|
||||
'var(--tw-brightness)',
|
||||
'var(--tw-contrast)',
|
||||
'var(--tw-grayscale)',
|
||||
'var(--tw-hue-rotate)',
|
||||
'var(--tw-invert)',
|
||||
'var(--tw-saturate)',
|
||||
'var(--tw-sepia)',
|
||||
'var(--tw-drop-shadow)',
|
||||
].join(' '),
|
||||
},
|
||||
})
|
||||
addUtilities(
|
||||
{
|
||||
'.filter': { '@defaults filter': {}, filter: 'var(--tw-filter)' },
|
||||
'.filter-none': { filter: 'none' },
|
||||
},
|
||||
variants('filter')
|
||||
)
|
||||
} else {
|
||||
addUtilities(
|
||||
{
|
||||
'.filter': {
|
||||
'--tw-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-drop-shadow': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
filter: [
|
||||
'var(--tw-blur)',
|
||||
'var(--tw-brightness)',
|
||||
'var(--tw-contrast)',
|
||||
'var(--tw-grayscale)',
|
||||
'var(--tw-hue-rotate)',
|
||||
'var(--tw-invert)',
|
||||
'var(--tw-saturate)',
|
||||
'var(--tw-sepia)',
|
||||
'var(--tw-drop-shadow)',
|
||||
].join(' '),
|
||||
},
|
||||
'.filter-none': { filter: 'none' },
|
||||
},
|
||||
variants('filter')
|
||||
)
|
||||
}
|
||||
return function ({ addBase, addUtilities, variants }) {
|
||||
addBase({
|
||||
'@defaults filter': {
|
||||
'--tw-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-drop-shadow': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-filter': [
|
||||
'var(--tw-blur)',
|
||||
'var(--tw-brightness)',
|
||||
'var(--tw-contrast)',
|
||||
'var(--tw-grayscale)',
|
||||
'var(--tw-hue-rotate)',
|
||||
'var(--tw-invert)',
|
||||
'var(--tw-saturate)',
|
||||
'var(--tw-sepia)',
|
||||
'var(--tw-drop-shadow)',
|
||||
].join(' '),
|
||||
},
|
||||
})
|
||||
addUtilities(
|
||||
{
|
||||
'.filter': { '@defaults filter': {}, filter: 'var(--tw-filter)' },
|
||||
'.filter-none': { filter: 'none' },
|
||||
},
|
||||
variants('filter')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
grayscale: (value) => {
|
||||
return {
|
||||
'--tw-grayscale': `grayscale(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
'hue-rotate': (value) => {
|
||||
return {
|
||||
'--tw-hue-rotate': `hue-rotate(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
invert: (value) => {
|
||||
return {
|
||||
'--tw-invert': `invert(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { withAlphaValue } from '../util/withAlphaVariable'
|
||||
|
||||
export default function () {
|
||||
return function ({ config, matchUtilities, addBase, addUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, addBase, addUtilities, theme, variants }) {
|
||||
let ringOpacityDefault = theme('ringOpacity.DEFAULT', '0.5')
|
||||
let ringColorDefault = withAlphaValue(
|
||||
theme('ringColor.DEFAULT'),
|
||||
|
|
@ -9,39 +9,23 @@ export default function () {
|
|||
`rgba(147, 197, 253, ${ringOpacityDefault})`
|
||||
)
|
||||
|
||||
if (config('mode') === 'jit') {
|
||||
addBase({
|
||||
'@defaults ring-width': {
|
||||
'--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-ring-offset-width': theme('ringOffsetWidth.DEFAULT', '0px'),
|
||||
'--tw-ring-offset-color': theme('ringOffsetColor.DEFAULT', '#fff'),
|
||||
'--tw-ring-color': ringColorDefault,
|
||||
'--tw-ring-offset-shadow': '0 0 #0000',
|
||||
'--tw-ring-shadow': '0 0 #0000',
|
||||
'--tw-shadow': '0 0 #0000',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
addUtilities(
|
||||
{
|
||||
'*, ::before, ::after': {
|
||||
'--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-ring-offset-width': theme('ringOffsetWidth.DEFAULT', '0px'),
|
||||
'--tw-ring-offset-color': theme('ringOffsetColor.DEFAULT', '#fff'),
|
||||
'--tw-ring-color': ringColorDefault,
|
||||
'--tw-ring-offset-shadow': '0 0 #0000',
|
||||
'--tw-ring-shadow': '0 0 #0000',
|
||||
},
|
||||
},
|
||||
{ respectImportant: false }
|
||||
)
|
||||
}
|
||||
addBase({
|
||||
'@defaults ring-width': {
|
||||
'--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||
'--tw-ring-offset-width': theme('ringOffsetWidth.DEFAULT', '0px'),
|
||||
'--tw-ring-offset-color': theme('ringOffsetColor.DEFAULT', '#fff'),
|
||||
'--tw-ring-color': ringColorDefault,
|
||||
'--tw-ring-offset-shadow': '0 0 #0000',
|
||||
'--tw-ring-shadow': '0 0 #0000',
|
||||
'--tw-shadow': '0 0 #0000',
|
||||
},
|
||||
})
|
||||
|
||||
matchUtilities(
|
||||
{
|
||||
ring: (value) => {
|
||||
return {
|
||||
...(config('mode') === 'jit' ? { '@defaults ring-width': {} } : {}),
|
||||
'@defaults ring-width': {},
|
||||
'--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,
|
||||
'--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(${value} + var(--tw-ring-offset-width)) var(--tw-ring-color)`,
|
||||
'box-shadow': [
|
||||
|
|
@ -62,7 +46,7 @@ export default function () {
|
|||
addUtilities(
|
||||
{
|
||||
'.ring-inset': {
|
||||
...(config('mode') === 'jit' ? { '@defaults ring-width': {} } : {}),
|
||||
'@defaults ring-width': {},
|
||||
'--tw-ring-inset': 'inset',
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,7 @@
|
|||
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||
|
||||
export default function () {
|
||||
return function ({ config, ...rest }) {
|
||||
if (config('mode') === 'jit') {
|
||||
return createUtilityPlugin('rotate', [
|
||||
[
|
||||
'rotate',
|
||||
[['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
])({ config, ...rest })
|
||||
} else {
|
||||
return createUtilityPlugin('rotate', [['rotate', ['--tw-rotate']]])({ config, ...rest })
|
||||
}
|
||||
}
|
||||
return createUtilityPlugin('rotate', [
|
||||
['rotate', [['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']]],
|
||||
])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
saturate: (value) => {
|
||||
return {
|
||||
'--tw-saturate': `saturate(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,37 +1,25 @@
|
|||
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||
|
||||
export default function () {
|
||||
return function ({ config, ...rest }) {
|
||||
if (config('mode') === 'jit') {
|
||||
return createUtilityPlugin('scale', [
|
||||
[
|
||||
'scale',
|
||||
[
|
||||
['@defaults transform', {}],
|
||||
'--tw-scale-x',
|
||||
'--tw-scale-y',
|
||||
['transform', 'var(--tw-transform)'],
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'scale-x',
|
||||
[['@defaults transform', {}], '--tw-scale-x', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
[
|
||||
'scale-y',
|
||||
[['@defaults transform', {}], '--tw-scale-y', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
],
|
||||
])({ config, ...rest })
|
||||
} else {
|
||||
return createUtilityPlugin('scale', [
|
||||
['scale', ['--tw-scale-x', '--tw-scale-y']],
|
||||
[
|
||||
['scale-x', ['--tw-scale-x']],
|
||||
['scale-y', ['--tw-scale-y']],
|
||||
],
|
||||
])({ config, ...rest })
|
||||
}
|
||||
}
|
||||
return createUtilityPlugin('scale', [
|
||||
[
|
||||
'scale',
|
||||
[
|
||||
['@defaults transform', {}],
|
||||
'--tw-scale-x',
|
||||
'--tw-scale-y',
|
||||
['transform', 'var(--tw-transform)'],
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'scale-x',
|
||||
[['@defaults transform', {}], '--tw-scale-x', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
[
|
||||
'scale-y',
|
||||
[['@defaults transform', {}], '--tw-scale-y', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
],
|
||||
])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
export default function () {
|
||||
return function ({ config, matchUtilities, theme, variants }) {
|
||||
return function ({ matchUtilities, theme, variants }) {
|
||||
matchUtilities(
|
||||
{
|
||||
sepia: (value) => {
|
||||
return {
|
||||
'--tw-sepia': `sepia(${value})`,
|
||||
...(config('mode') === 'jit'
|
||||
? {
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
: {}),
|
||||
'@defaults filter': {},
|
||||
filter: 'var(--tw-filter)',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,27 +1,16 @@
|
|||
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||
|
||||
export default function () {
|
||||
return function ({ config, ...rest }) {
|
||||
if (config('mode') === 'jit') {
|
||||
return createUtilityPlugin('skew', [
|
||||
[
|
||||
[
|
||||
'skew-x',
|
||||
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
[
|
||||
'skew-y',
|
||||
[['@defaults transform', {}], '--tw-skew-y', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
],
|
||||
])({ config, ...rest })
|
||||
} else {
|
||||
return createUtilityPlugin('skew', [
|
||||
[
|
||||
['skew-x', ['--tw-skew-x']],
|
||||
['skew-y', ['--tw-skew-y']],
|
||||
],
|
||||
])({ config, ...rest })
|
||||
}
|
||||
}
|
||||
return createUtilityPlugin('skew', [
|
||||
[
|
||||
[
|
||||
'skew-x',
|
||||
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
[
|
||||
'skew-y',
|
||||
[['@defaults transform', {}], '--tw-skew-y', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
],
|
||||
])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,32 @@
|
|||
export default function () {
|
||||
return function ({ config, addBase, addUtilities, variants }) {
|
||||
if (config('mode') === 'jit') {
|
||||
addBase({
|
||||
'@defaults transform': {
|
||||
'--tw-translate-x': '0',
|
||||
'--tw-translate-y': '0',
|
||||
'--tw-rotate': '0',
|
||||
'--tw-skew-x': '0',
|
||||
'--tw-skew-y': '0',
|
||||
'--tw-scale-x': '1',
|
||||
'--tw-scale-y': '1',
|
||||
return function ({ addBase, addUtilities, variants }) {
|
||||
addBase({
|
||||
'@defaults transform': {
|
||||
'--tw-translate-x': '0',
|
||||
'--tw-translate-y': '0',
|
||||
'--tw-rotate': '0',
|
||||
'--tw-skew-x': '0',
|
||||
'--tw-skew-y': '0',
|
||||
'--tw-scale-x': '1',
|
||||
'--tw-scale-y': '1',
|
||||
'--tw-transform': [
|
||||
'translateX(var(--tw-translate-x))',
|
||||
'translateY(var(--tw-translate-y))',
|
||||
'rotate(var(--tw-rotate))',
|
||||
'skewX(var(--tw-skew-x))',
|
||||
'skewY(var(--tw-skew-y))',
|
||||
'scaleX(var(--tw-scale-x))',
|
||||
'scaleY(var(--tw-scale-y))',
|
||||
].join(' '),
|
||||
},
|
||||
})
|
||||
addUtilities(
|
||||
{
|
||||
'.transform': {
|
||||
'@defaults transform': {},
|
||||
transform: 'var(--tw-transform)',
|
||||
},
|
||||
'.transform-cpu': {
|
||||
'--tw-transform': [
|
||||
'translateX(var(--tw-translate-x))',
|
||||
'translateY(var(--tw-translate-y))',
|
||||
|
|
@ -20,80 +37,19 @@ export default function () {
|
|||
'scaleY(var(--tw-scale-y))',
|
||||
].join(' '),
|
||||
},
|
||||
})
|
||||
addUtilities(
|
||||
{
|
||||
'.transform': {
|
||||
'@defaults transform': {},
|
||||
transform: 'var(--tw-transform)',
|
||||
},
|
||||
'.transform-cpu': {
|
||||
'--tw-transform': [
|
||||
'translateX(var(--tw-translate-x))',
|
||||
'translateY(var(--tw-translate-y))',
|
||||
'rotate(var(--tw-rotate))',
|
||||
'skewX(var(--tw-skew-x))',
|
||||
'skewY(var(--tw-skew-y))',
|
||||
'scaleX(var(--tw-scale-x))',
|
||||
'scaleY(var(--tw-scale-y))',
|
||||
].join(' '),
|
||||
},
|
||||
'.transform-gpu': {
|
||||
'--tw-transform': [
|
||||
'translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)',
|
||||
'rotate(var(--tw-rotate))',
|
||||
'skewX(var(--tw-skew-x))',
|
||||
'skewY(var(--tw-skew-y))',
|
||||
'scaleX(var(--tw-scale-x))',
|
||||
'scaleY(var(--tw-scale-y))',
|
||||
].join(' '),
|
||||
},
|
||||
'.transform-none': { transform: 'none' },
|
||||
'.transform-gpu': {
|
||||
'--tw-transform': [
|
||||
'translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)',
|
||||
'rotate(var(--tw-rotate))',
|
||||
'skewX(var(--tw-skew-x))',
|
||||
'skewY(var(--tw-skew-y))',
|
||||
'scaleX(var(--tw-scale-x))',
|
||||
'scaleY(var(--tw-scale-y))',
|
||||
].join(' '),
|
||||
},
|
||||
variants('transform')
|
||||
)
|
||||
} else {
|
||||
addUtilities(
|
||||
{
|
||||
'.transform': {
|
||||
'--tw-translate-x': '0',
|
||||
'--tw-translate-y': '0',
|
||||
'--tw-rotate': '0',
|
||||
'--tw-skew-x': '0',
|
||||
'--tw-skew-y': '0',
|
||||
'--tw-scale-x': '1',
|
||||
'--tw-scale-y': '1',
|
||||
transform: [
|
||||
'translateX(var(--tw-translate-x))',
|
||||
'translateY(var(--tw-translate-y))',
|
||||
'rotate(var(--tw-rotate))',
|
||||
'skewX(var(--tw-skew-x))',
|
||||
'skewY(var(--tw-skew-y))',
|
||||
'scaleX(var(--tw-scale-x))',
|
||||
'scaleY(var(--tw-scale-y))',
|
||||
].join(' '),
|
||||
},
|
||||
'.transform-gpu': {
|
||||
'--tw-translate-x': '0',
|
||||
'--tw-translate-y': '0',
|
||||
'--tw-rotate': '0',
|
||||
'--tw-skew-x': '0',
|
||||
'--tw-skew-y': '0',
|
||||
'--tw-scale-x': '1',
|
||||
'--tw-scale-y': '1',
|
||||
transform: [
|
||||
'translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)',
|
||||
'rotate(var(--tw-rotate))',
|
||||
'skewX(var(--tw-skew-x))',
|
||||
'skewY(var(--tw-skew-y))',
|
||||
'scaleX(var(--tw-scale-x))',
|
||||
'scaleY(var(--tw-scale-y))',
|
||||
].join(' '),
|
||||
},
|
||||
'.transform-none': { transform: 'none' },
|
||||
},
|
||||
variants('transform')
|
||||
)
|
||||
}
|
||||
'.transform-none': { transform: 'none' },
|
||||
},
|
||||
variants('transform')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,16 @@
|
|||
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||
|
||||
export default function () {
|
||||
return function ({ config, ...rest }) {
|
||||
if (config('mode') === 'jit') {
|
||||
return createUtilityPlugin('translate', [
|
||||
[
|
||||
[
|
||||
'translate-x',
|
||||
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
[
|
||||
'translate-y',
|
||||
[['@defaults transform', {}], '--tw-translate-y', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
],
|
||||
])({ config, ...rest })
|
||||
} else {
|
||||
return createUtilityPlugin('translate', [
|
||||
[
|
||||
['translate-x', ['--tw-translate-x']],
|
||||
['translate-y', ['--tw-translate-y']],
|
||||
],
|
||||
])({ config, ...rest })
|
||||
}
|
||||
}
|
||||
return createUtilityPlugin('translate', [
|
||||
[
|
||||
[
|
||||
'translate-x',
|
||||
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
[
|
||||
'translate-y',
|
||||
[['@defaults transform', {}], '--tw-translate-y', ['transform', 'var(--tw-transform)']],
|
||||
],
|
||||
],
|
||||
])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,79 +1,41 @@
|
|||
import _ from 'lodash'
|
||||
import postcss from 'postcss'
|
||||
|
||||
import substituteTailwindAtRules from './lib/substituteTailwindAtRules'
|
||||
import normalizeTailwindDirectives from './lib/normalizeTailwindDirectives'
|
||||
import expandTailwindAtRules from './lib/expandTailwindAtRules'
|
||||
import expandApplyAtRules from './lib/expandApplyAtRules'
|
||||
import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions'
|
||||
import substituteVariantsAtRules from './lib/substituteVariantsAtRules'
|
||||
import substituteResponsiveAtRules from './lib/substituteResponsiveAtRules'
|
||||
import convertLayerAtRulesToControlComments from './lib/convertLayerAtRulesToControlComments'
|
||||
import substituteScreenAtRules from './lib/substituteScreenAtRules'
|
||||
import substituteClassApplyAtRules from './lib/substituteClassApplyAtRules'
|
||||
import applyImportantConfiguration from './lib/applyImportantConfiguration'
|
||||
import purgeUnusedStyles from './lib/purgeUnusedStyles'
|
||||
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
|
||||
import collapseAdjacentRules from './lib/collapseAdjacentRules'
|
||||
import { createContext } from './lib/setupContextUtils'
|
||||
|
||||
import corePlugins from './corePlugins'
|
||||
import processPlugins from './util/processPlugins'
|
||||
import cloneNodes from './util/cloneNodes'
|
||||
import { issueFlagNotices } from './featureFlags.js'
|
||||
export default function processTailwindFeatures(setupContext) {
|
||||
return function (root, result) {
|
||||
let tailwindDirectives = normalizeTailwindDirectives(root)
|
||||
|
||||
import hash from './util/hashConfig'
|
||||
import log from './util/log'
|
||||
import { shared } from './util/disposables'
|
||||
let context = setupContext({
|
||||
tailwindDirectives,
|
||||
registerDependency(dependency) {
|
||||
result.messages.push({
|
||||
plugin: 'tailwindcss',
|
||||
parent: result.opts.from,
|
||||
...dependency,
|
||||
})
|
||||
},
|
||||
createContext(tailwindConfig, changedContent) {
|
||||
return createContext(tailwindConfig, changedContent, tailwindDirectives, root)
|
||||
},
|
||||
})(root, result)
|
||||
|
||||
let previousConfig = null
|
||||
let processedPlugins = null
|
||||
let getProcessedPlugins = null
|
||||
|
||||
export default function (getConfig) {
|
||||
return function (css, result) {
|
||||
const config = getConfig()
|
||||
const configChanged = hash(previousConfig) !== hash(config)
|
||||
previousConfig = config
|
||||
|
||||
if (configChanged) {
|
||||
shared.dispose()
|
||||
if (config.target) {
|
||||
log.warn([
|
||||
'The `target` feature has been removed in Tailwind CSS v2.0.',
|
||||
'Please remove this option from your config file to silence this warning.',
|
||||
])
|
||||
}
|
||||
|
||||
issueFlagNotices(config)
|
||||
|
||||
processedPlugins = processPlugins(
|
||||
[...corePlugins(config), ..._.get(config, 'plugins', [])],
|
||||
config
|
||||
if (context.tailwindConfig.separator === '-') {
|
||||
throw new Error(
|
||||
"The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead."
|
||||
)
|
||||
|
||||
getProcessedPlugins = function () {
|
||||
return {
|
||||
...processedPlugins,
|
||||
base: cloneNodes(processedPlugins.base),
|
||||
components: cloneNodes(processedPlugins.components),
|
||||
utilities: cloneNodes(processedPlugins.utilities),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function registerDependency(dependency) {
|
||||
result.messages.push({
|
||||
plugin: 'tailwindcss',
|
||||
parent: result.opts.from,
|
||||
...dependency,
|
||||
})
|
||||
}
|
||||
|
||||
return postcss([
|
||||
substituteTailwindAtRules(config, getProcessedPlugins()),
|
||||
evaluateTailwindFunctions({ tailwindConfig: config }),
|
||||
substituteVariantsAtRules(config, getProcessedPlugins()),
|
||||
substituteResponsiveAtRules(config),
|
||||
convertLayerAtRulesToControlComments(config),
|
||||
substituteScreenAtRules({ tailwindConfig: config }),
|
||||
substituteClassApplyAtRules(config, getProcessedPlugins, configChanged),
|
||||
applyImportantConfiguration(config),
|
||||
purgeUnusedStyles(config, configChanged, registerDependency),
|
||||
]).process(css, { from: _.get(css, 'source.input.file') })
|
||||
expandTailwindAtRules(context)(root, result)
|
||||
expandApplyAtRules(context)(root, result)
|
||||
evaluateTailwindFunctions(context)(root, result)
|
||||
substituteScreenAtRules(context)(root, result)
|
||||
resolveDefaultsAtRules(context)(root, result)
|
||||
collapseAdjacentRules(context)(root, result)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default function createUtilityPlugin(
|
|||
{
|
||||
values: filterDefault
|
||||
? Object.fromEntries(
|
||||
Object.entries(theme(themeKey) || {}).filter(([modifier]) => modifier !== 'DEFAULT')
|
||||
Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
|
||||
)
|
||||
: theme(themeKey),
|
||||
variants: variants(themeKey),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const flattenColorPalette = (colors) =>
|
||||
Object.assign(
|
||||
{},
|
||||
...Object.entries(colors || {}).flatMap(([color, values]) =>
|
||||
...Object.entries(colors ?? {}).flatMap(([color, values]) =>
|
||||
typeof values == 'object'
|
||||
? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
|
||||
[color + (number === 'DEFAULT' ? '' : `-${number}`)]: hex,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import corePluginList from '../corePluginList'
|
|||
import configurePlugins from './configurePlugins'
|
||||
import defaultConfig from '../../stubs/defaultConfig.stub'
|
||||
import colors from '../../colors'
|
||||
import log from './log'
|
||||
|
||||
const configUtils = {
|
||||
colors,
|
||||
|
|
@ -257,30 +258,86 @@ function resolvePluginLists(pluginLists) {
|
|||
}
|
||||
|
||||
export default function resolveConfig(configs) {
|
||||
const allConfigs = [
|
||||
let allConfigs = [
|
||||
...extractPluginConfigs(configs),
|
||||
{
|
||||
darkMode: false,
|
||||
content: [],
|
||||
prefix: '',
|
||||
important: false,
|
||||
separator: ':',
|
||||
variantOrder: defaultConfig.variantOrder,
|
||||
},
|
||||
]
|
||||
const { variantOrder } = allConfigs.find((c) => c.variantOrder)
|
||||
let { variantOrder } = allConfigs.find((c) => c.variantOrder)
|
||||
|
||||
return defaults(
|
||||
{
|
||||
theme: resolveFunctionKeys(
|
||||
mergeExtensions(mergeThemes(map(allConfigs, (t) => get(t, 'theme', {}))))
|
||||
),
|
||||
variants: resolveVariants(
|
||||
allConfigs.map((c) => get(c, 'variants', {})),
|
||||
variantOrder
|
||||
),
|
||||
corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
|
||||
plugins: resolvePluginLists(configs.map((c) => get(c, 'plugins', []))),
|
||||
},
|
||||
...allConfigs
|
||||
return normalizeConfig(
|
||||
defaults(
|
||||
{
|
||||
theme: resolveFunctionKeys(
|
||||
mergeExtensions(mergeThemes(map(allConfigs, (t) => get(t, 'theme', {}))))
|
||||
),
|
||||
variants: resolveVariants(
|
||||
allConfigs.map((c) => get(c, 'variants', {})),
|
||||
variantOrder
|
||||
),
|
||||
corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
|
||||
plugins: resolvePluginLists(configs.map((c) => get(c, 'plugins', []))),
|
||||
},
|
||||
...allConfigs
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let warnedAbout = new Set()
|
||||
function normalizeConfig(config) {
|
||||
if (!warnedAbout.has('purge-deprecation') && config.hasOwnProperty('purge')) {
|
||||
log.warn([
|
||||
'The `purge` option in your tailwind.config.js file has been deprecated.',
|
||||
'Please rename this to `content` instead.',
|
||||
])
|
||||
warnedAbout.add('purge-deprecation')
|
||||
}
|
||||
|
||||
config.content = {
|
||||
content: (() => {
|
||||
let { content, purge } = config
|
||||
|
||||
if (Array.isArray(content)) return content
|
||||
if (Array.isArray(content?.content)) return content.content
|
||||
if (Array.isArray(purge)) return purge
|
||||
if (Array.isArray(purge?.content)) return purge.content
|
||||
|
||||
return []
|
||||
})(),
|
||||
safelist: (() => {
|
||||
let { content, purge } = config
|
||||
|
||||
let [safelisKey, safelistPaths] = (() => {
|
||||
if (Array.isArray(content?.safelist)) return ['content.safelist', content.safelist]
|
||||
if (Array.isArray(purge?.safelist)) return ['purge.safelist', purge.safelist]
|
||||
return [null, []]
|
||||
})()
|
||||
|
||||
return safelistPaths.map((content) => {
|
||||
if (typeof content === 'string') {
|
||||
return { raw: content, extension: 'html' }
|
||||
}
|
||||
|
||||
if (content instanceof RegExp) {
|
||||
throw new Error(
|
||||
`Values inside '${safelistKey}' can only be of type 'string', found 'regex'.`
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Values inside '${safelisKey}' can only be of type 'string', found '${typeof content}'.`
|
||||
)
|
||||
})
|
||||
})(),
|
||||
extract: config.content?.extract || config.purge?.extract || {},
|
||||
options: config.content?.options || config.purge?.options || {},
|
||||
transform: config.content?.transform || config.purge?.transform || {},
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue