mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
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
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,9 +10,6 @@ index.html
|
|||||||
yarn.lock
|
yarn.lock
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
|
||||||
# "External" plugins
|
|
||||||
/nesting
|
|
||||||
|
|
||||||
# Perf related files
|
# Perf related files
|
||||||
isolate*.log
|
isolate*.log
|
||||||
|
|
||||||
|
|||||||
3
dist/.gitignore
vendored
3
dist/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
!.npmignore
|
|
||||||
0
dist/.npmignore
vendored
0
dist/.npmignore
vendored
@ -5,7 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "parcel build ./src/index.html --no-cache",
|
"build": "parcel build ./src/index.html --no-cache",
|
||||||
"dev": "parcel watch ./src/index.html --no-cache",
|
"dev": "parcel watch ./src/index.html --no-cache",
|
||||||
"test": "jest"
|
"test": "jest --runInBand"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"displayName": "parcel",
|
"displayName": "parcel",
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -127,9 +127,7 @@ describe.skip('watcher', () => {
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
@ -140,9 +138,6 @@ describe.skip('watcher', () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "NODE_ENV=production postcss ./src/index.css -o ./dist/main.css",
|
"build": "NODE_ENV=production postcss ./src/index.css -o ./dist/main.css",
|
||||||
"test": "jest"
|
"test": "jest --runInBand"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"displayName": "PostCSS CLI",
|
"displayName": "PostCSS CLI",
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -109,9 +109,7 @@ describe('watcher', () => {
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
@ -122,9 +120,6 @@ describe('watcher', () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"test": "jest"
|
"test": "jest --runInBand"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"displayName": "rollup.js",
|
"displayName": "rollup.js",
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -105,9 +105,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
@ -118,9 +116,6 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -115,9 +115,7 @@ describe('Build command', () => {
|
|||||||
|
|
||||||
let customConfig = `module.exports = ${JSON.stringify(
|
let customConfig = `module.exports = ${JSON.stringify(
|
||||||
{
|
{
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontWeight: {
|
fontWeight: {
|
||||||
@ -125,9 +123,6 @@ describe('Build command', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
@ -258,7 +253,7 @@ describe('Build command', () => {
|
|||||||
|
|
||||||
expect(combined).toMatchInlineSnapshot(`
|
expect(combined).toMatchInlineSnapshot(`
|
||||||
"
|
"
|
||||||
tailwindcss v2.2.8
|
tailwindcss v2.2.9
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
tailwindcss build [options]
|
tailwindcss build [options]
|
||||||
@ -267,8 +262,7 @@ describe('Build command', () => {
|
|||||||
-i, --input Input file
|
-i, --input Input file
|
||||||
-o, --output Output file
|
-o, --output Output file
|
||||||
-w, --watch Watch for changes and rebuild as needed
|
-w, --watch Watch for changes and rebuild as needed
|
||||||
--jit Build using JIT mode
|
--content Content paths to use for removing unused classes
|
||||||
--purge Content paths to use for removing unused classes
|
|
||||||
--postcss Load custom PostCSS configuration
|
--postcss Load custom PostCSS configuration
|
||||||
-m, --minify Minify the output
|
-m, --minify Minify the output
|
||||||
-c, --config Path to a custom config file
|
-c, --config Path to a custom config file
|
||||||
@ -298,34 +292,6 @@ describe('Init command', () => {
|
|||||||
expect((await readOutputFile('../full.config.js')).split('\n').length).toBeGreaterThan(50)
|
expect((await readOutputFile('../full.config.js')).split('\n').length).toBeGreaterThan(50)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('--jit', async () => {
|
|
||||||
cleanupFile('with-jit.config.js')
|
|
||||||
|
|
||||||
let { combined } = await $(`${EXECUTABLE} init with-jit.config.js --jit`)
|
|
||||||
|
|
||||||
expect(combined).toMatchInlineSnapshot(`
|
|
||||||
"
|
|
||||||
Created Tailwind CSS config file: with-jit.config.js
|
|
||||||
"
|
|
||||||
`)
|
|
||||||
|
|
||||||
expect(await readOutputFile('../with-jit.config.js')).toContain("mode: 'jit'")
|
|
||||||
})
|
|
||||||
|
|
||||||
test('--full, --jit', async () => {
|
|
||||||
cleanupFile('full-with-jit.config.js')
|
|
||||||
|
|
||||||
let { combined } = await $(`${EXECUTABLE} init full-with-jit.config.js --jit --full`)
|
|
||||||
|
|
||||||
expect(combined).toMatchInlineSnapshot(`
|
|
||||||
"
|
|
||||||
Created Tailwind CSS config file: full-with-jit.config.js
|
|
||||||
"
|
|
||||||
`)
|
|
||||||
|
|
||||||
expect(await readOutputFile('../full-with-jit.config.js')).toContain("mode: 'jit'")
|
|
||||||
})
|
|
||||||
|
|
||||||
test('--postcss', async () => {
|
test('--postcss', async () => {
|
||||||
expect(await fileExists('postcss.config.js')).toBe(true)
|
expect(await fileExists('postcss.config.js')).toBe(true)
|
||||||
await removeFile('postcss.config.js')
|
await removeFile('postcss.config.js')
|
||||||
@ -348,13 +314,12 @@ describe('Init command', () => {
|
|||||||
|
|
||||||
expect(combined).toMatchInlineSnapshot(`
|
expect(combined).toMatchInlineSnapshot(`
|
||||||
"
|
"
|
||||||
tailwindcss v2.2.8
|
tailwindcss v2.2.9
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
tailwindcss init [options]
|
tailwindcss init [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--jit Initialize for JIT mode
|
|
||||||
-f, --full Initialize a full \`tailwind.config.js\` file
|
-f, --full Initialize a full \`tailwind.config.js\` file
|
||||||
-p, --postcss Initialize a \`postcss.config.js\` file
|
-p, --postcss Initialize a \`postcss.config.js\` file
|
||||||
-h, --help Display usage information
|
-h, --help Display usage information
|
||||||
|
|||||||
@ -32,19 +32,14 @@ describe('static build', () => {
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: {
|
content: {
|
||||||
content: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
safelist: ['bg-red-500','bg-red-600']
|
safelist: ['bg-red-500','bg-red-600']
|
||||||
},
|
},
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
@ -212,9 +207,7 @@ describe('watcher', () => {
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
@ -225,9 +218,6 @@ describe('watcher', () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
"browser": "./src/index.js",
|
"browser": "./src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"test": "jest"
|
"test": "jest --runInBand"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"displayName": "vite",
|
"displayName": "vite",
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./index.html'],
|
content: ['./index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -132,9 +132,7 @@ describe('watcher', () => {
|
|||||||
'tailwind.config.js',
|
'tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./index.html'],
|
content: ['./index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
@ -145,9 +143,6 @@ describe('watcher', () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode=production",
|
"build": "webpack --mode=production",
|
||||||
"dev": "webpack --mode=development --watch",
|
"dev": "webpack --mode=development --watch",
|
||||||
"test": "jest"
|
"test": "jest --runInBand"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"displayName": "webpack 4",
|
"displayName": "webpack 4",
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -103,9 +103,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
@ -116,9 +114,6 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -103,9 +103,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
@ -116,9 +114,6 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
@ -234,19 +229,14 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
|
|||||||
'../tailwind.config.js',
|
'../tailwind.config.js',
|
||||||
javascript`
|
javascript`
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: {
|
content: {
|
||||||
content: ['./src/index.html'],
|
content: ['./src/index.html'],
|
||||||
safelist: ['bg-red-500','bg-red-600']
|
safelist: ['bg-red-500','bg-red-600']
|
||||||
},
|
},
|
||||||
mode: 'jit',
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false,
|
preflight: false,
|
||||||
},
|
},
|
||||||
|
|||||||
177
package-lock.json
generated
177
package-lock.json
generated
@ -9,7 +9,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"arg": "^5.0.1",
|
"arg": "^5.0.1",
|
||||||
"bytes": "^3.0.0",
|
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"chokidar": "^3.5.2",
|
"chokidar": "^3.5.2",
|
||||||
"color": "^4.0.1",
|
"color": "^4.0.1",
|
||||||
@ -18,14 +17,10 @@
|
|||||||
"didyoumean": "^1.2.2",
|
"didyoumean": "^1.2.2",
|
||||||
"dlv": "^1.1.3",
|
"dlv": "^1.1.3",
|
||||||
"fast-glob": "^3.2.7",
|
"fast-glob": "^3.2.7",
|
||||||
"fs-extra": "^10.0.0",
|
|
||||||
"glob-parent": "^6.0.1",
|
"glob-parent": "^6.0.1",
|
||||||
"html-tags": "^3.1.0",
|
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash.topath": "^4.5.2",
|
|
||||||
"modern-normalize": "^1.1.0",
|
"modern-normalize": "^1.1.0",
|
||||||
"node-emoji": "^1.11.0",
|
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"object-hash": "^2.2.0",
|
"object-hash": "^2.2.0",
|
||||||
"postcss-js": "^3.0.3",
|
"postcss-js": "^3.0.3",
|
||||||
@ -33,8 +28,6 @@
|
|||||||
"postcss-nested": "5.0.6",
|
"postcss-nested": "5.0.6",
|
||||||
"postcss-selector-parser": "^6.0.6",
|
"postcss-selector-parser": "^6.0.6",
|
||||||
"postcss-value-parser": "^4.1.0",
|
"postcss-value-parser": "^4.1.0",
|
||||||
"pretty-hrtime": "^1.0.3",
|
|
||||||
"purgecss": "^4.0.3",
|
|
||||||
"quick-lru": "^5.1.1",
|
"quick-lru": "^5.1.1",
|
||||||
"reduce-css-calc": "^2.1.8",
|
"reduce-css-calc": "^2.1.8",
|
||||||
"resolve": "^1.20.0",
|
"resolve": "^1.20.0",
|
||||||
@ -53,7 +46,6 @@
|
|||||||
"@vercel/ncc": "^0.29.2",
|
"@vercel/ncc": "^0.29.2",
|
||||||
"autoprefixer": "^10.3.3",
|
"autoprefixer": "^10.3.3",
|
||||||
"babel-jest": "^27.0.6",
|
"babel-jest": "^27.0.6",
|
||||||
"clean-css": "5.1.4",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cssnano": "^5.0.8",
|
"cssnano": "^5.0.8",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
@ -3090,14 +3082,6 @@
|
|||||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/bytes": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cache-base": {
|
"node_modules/cache-base": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
||||||
@ -3409,27 +3393,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/clean-css": {
|
|
||||||
"version": "5.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.4.tgz",
|
|
||||||
"integrity": "sha512-e6JAuR0T2ahg7fOSv98Nxqh7mHWOac5TaCSgrr61h/6mkPLwlxX38hzob4h6IKj/UHlrrLXvAEjWqXlvi8r8lQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"source-map": "~0.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/clean-css/node_modules/source-map": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cliui": {
|
"node_modules/cliui": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
@ -5094,19 +5057,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
|
||||||
"version": "10.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
|
|
||||||
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"graceful-fs": "^4.2.0",
|
|
||||||
"jsonfile": "^6.0.1",
|
|
||||||
"universalify": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs-readdir-recursive": {
|
"node_modules/fs-readdir-recursive": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
||||||
@ -5297,7 +5247,8 @@
|
|||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.6",
|
"version": "4.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
|
||||||
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
|
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/has": {
|
"node_modules/has": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@ -5411,14 +5362,6 @@
|
|||||||
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/html-tags": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/http-proxy-agent": {
|
"node_modules/http-proxy-agent": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
|
||||||
@ -7016,6 +6959,7 @@
|
|||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
@ -7151,11 +7095,6 @@
|
|||||||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
|
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.topath": {
|
|
||||||
"version": "4.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
|
|
||||||
"integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.truncate": {
|
"node_modules/lodash.truncate": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
||||||
@ -7483,14 +7422,6 @@
|
|||||||
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
|
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/node-emoji": {
|
|
||||||
"version": "1.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz",
|
|
||||||
"integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
|
|
||||||
"dependencies": {
|
|
||||||
"lodash": "^4.17.21"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-environment-flags": {
|
"node_modules/node-environment-flags": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
|
||||||
@ -8686,6 +8617,7 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
|
||||||
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
|
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@ -8734,28 +8666,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/purgecss": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==",
|
|
||||||
"dependencies": {
|
|
||||||
"commander": "^6.0.0",
|
|
||||||
"glob": "^7.0.0",
|
|
||||||
"postcss": "^8.2.1",
|
|
||||||
"postcss-selector-parser": "^6.0.2"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"purgecss": "bin/purgecss.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/purgecss/node_modules/commander": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
@ -10119,6 +10029,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
@ -12744,11 +12655,6 @@
|
|||||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"bytes": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
|
||||||
},
|
|
||||||
"cache-base": {
|
"cache-base": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
||||||
@ -12988,23 +12894,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"clean-css": {
|
|
||||||
"version": "5.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.4.tgz",
|
|
||||||
"integrity": "sha512-e6JAuR0T2ahg7fOSv98Nxqh7mHWOac5TaCSgrr61h/6mkPLwlxX38hzob4h6IKj/UHlrrLXvAEjWqXlvi8r8lQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"source-map": "~0.6.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
@ -14272,16 +14161,6 @@
|
|||||||
"map-cache": "^0.2.2"
|
"map-cache": "^0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
|
||||||
"version": "10.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
|
|
||||||
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
|
|
||||||
"requires": {
|
|
||||||
"graceful-fs": "^4.2.0",
|
|
||||||
"jsonfile": "^6.0.1",
|
|
||||||
"universalify": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fs-readdir-recursive": {
|
"fs-readdir-recursive": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
||||||
@ -14416,7 +14295,8 @@
|
|||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.2.6",
|
"version": "4.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
|
||||||
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
|
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"has": {
|
"has": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@ -14502,11 +14382,6 @@
|
|||||||
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"html-tags": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg=="
|
|
||||||
},
|
|
||||||
"http-proxy-agent": {
|
"http-proxy-agent": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
|
||||||
@ -15725,6 +15600,7 @@
|
|||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.6",
|
"graceful-fs": "^4.1.6",
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
@ -15840,11 +15716,6 @@
|
|||||||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
|
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lodash.topath": {
|
|
||||||
"version": "4.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
|
|
||||||
"integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak="
|
|
||||||
},
|
|
||||||
"lodash.truncate": {
|
"lodash.truncate": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
||||||
@ -16108,14 +15979,6 @@
|
|||||||
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
|
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node-emoji": {
|
|
||||||
"version": "1.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz",
|
|
||||||
"integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
|
|
||||||
"requires": {
|
|
||||||
"lodash": "^4.17.21"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node-environment-flags": {
|
"node-environment-flags": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
|
||||||
@ -16927,7 +16790,8 @@
|
|||||||
"pretty-hrtime": {
|
"pretty-hrtime": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
|
||||||
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE="
|
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@ -16964,24 +16828,6 @@
|
|||||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"purgecss": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==",
|
|
||||||
"requires": {
|
|
||||||
"commander": "^6.0.0",
|
|
||||||
"glob": "^7.0.0",
|
|
||||||
"postcss": "^8.2.1",
|
|
||||||
"postcss-selector-parser": "^6.0.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"commander": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"queue-microtask": {
|
"queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
@ -18060,7 +17906,8 @@
|
|||||||
"universalify": {
|
"universalify": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
|
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"unset-value": {
|
"unset-value": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|||||||
12
package.json
12
package.json
@ -22,14 +22,12 @@
|
|||||||
"babelify": "babel src --out-dir lib --copy-files",
|
"babelify": "babel src --out-dir lib --copy-files",
|
||||||
"postbabelify": "ncc build lib/cli-peer-dependencies.js -o peers",
|
"postbabelify": "ncc build lib/cli-peer-dependencies.js -o peers",
|
||||||
"rebuild-fixtures": "npm run babelify && babel-node scripts/rebuildFixtures.js",
|
"rebuild-fixtures": "npm run babelify && babel-node scripts/rebuildFixtures.js",
|
||||||
"prepublishOnly": "npm install --force && npm run babelify && babel-node scripts/build.js && node scripts/build-plugins.js",
|
"prepublishOnly": "npm install --force && npm run babelify",
|
||||||
"style": "eslint .",
|
"style": "eslint .",
|
||||||
"test": "cross-env TAILWIND_MODE=build jest",
|
"test": "cross-env TAILWIND_MODE=build jest",
|
||||||
"test:integrations": "npm run test --prefix ./integrations",
|
"test:integrations": "npm run test --prefix ./integrations",
|
||||||
"install:integrations": "node scripts/install-integrations.js",
|
"install:integrations": "node scripts/install-integrations.js",
|
||||||
"posttest": "npm run style",
|
"posttest": "npm run style",
|
||||||
"compat": "node scripts/compat.js --prepare",
|
|
||||||
"compat:restore": "node scripts/compat.js --restore",
|
|
||||||
"generate:plugin-list": "babel-node scripts/create-plugin-list.js"
|
"generate:plugin-list": "babel-node scripts/create-plugin-list.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -52,7 +50,6 @@
|
|||||||
"@vercel/ncc": "^0.29.2",
|
"@vercel/ncc": "^0.29.2",
|
||||||
"autoprefixer": "^10.3.3",
|
"autoprefixer": "^10.3.3",
|
||||||
"babel-jest": "^27.0.6",
|
"babel-jest": "^27.0.6",
|
||||||
"clean-css": "5.1.4",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cssnano": "^5.0.8",
|
"cssnano": "^5.0.8",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
@ -71,7 +68,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"arg": "^5.0.1",
|
"arg": "^5.0.1",
|
||||||
"bytes": "^3.0.0",
|
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"chokidar": "^3.5.2",
|
"chokidar": "^3.5.2",
|
||||||
"color": "^4.0.1",
|
"color": "^4.0.1",
|
||||||
@ -80,14 +76,10 @@
|
|||||||
"didyoumean": "^1.2.2",
|
"didyoumean": "^1.2.2",
|
||||||
"dlv": "^1.1.3",
|
"dlv": "^1.1.3",
|
||||||
"fast-glob": "^3.2.7",
|
"fast-glob": "^3.2.7",
|
||||||
"fs-extra": "^10.0.0",
|
|
||||||
"glob-parent": "^6.0.1",
|
"glob-parent": "^6.0.1",
|
||||||
"html-tags": "^3.1.0",
|
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash.topath": "^4.5.2",
|
|
||||||
"modern-normalize": "^1.1.0",
|
"modern-normalize": "^1.1.0",
|
||||||
"node-emoji": "^1.11.0",
|
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"object-hash": "^2.2.0",
|
"object-hash": "^2.2.0",
|
||||||
"postcss-js": "^3.0.3",
|
"postcss-js": "^3.0.3",
|
||||||
@ -95,8 +87,6 @@
|
|||||||
"postcss-nested": "5.0.6",
|
"postcss-nested": "5.0.6",
|
||||||
"postcss-selector-parser": "^6.0.6",
|
"postcss-selector-parser": "^6.0.6",
|
||||||
"postcss-value-parser": "^4.1.0",
|
"postcss-value-parser": "^4.1.0",
|
||||||
"pretty-hrtime": "^1.0.3",
|
|
||||||
"purgecss": "^4.0.3",
|
|
||||||
"quick-lru": "^5.1.1",
|
"quick-lru": "^5.1.1",
|
||||||
"reduce-css-calc": "^2.1.8",
|
"reduce-css-calc": "^2.1.8",
|
||||||
"resolve": "^1.20.0",
|
"resolve": "^1.20.0",
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"devDependencies": {
|
|
||||||
"cssnano": "^4"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"purgecss": "^4.0.3",
|
|
||||||
"autoprefixer": "^9",
|
|
||||||
"postcss": "^7",
|
|
||||||
"postcss-functions": "^3",
|
|
||||||
"postcss-js": "^2",
|
|
||||||
"postcss-nested": "^4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
let postcss = require('postcss')
|
|
||||||
let nesting = require('./plugin')
|
|
||||||
|
|
||||||
module.exports = postcss.plugin('tailwindcss/nesting', nesting)
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
let fs = require('fs')
|
|
||||||
let path = require('path')
|
|
||||||
|
|
||||||
let plugins = fs.readdirSync(fromRootPath('plugins'))
|
|
||||||
|
|
||||||
for (let plugin of plugins) {
|
|
||||||
// Cleanup
|
|
||||||
let pluginDest = fromRootPath(plugin)
|
|
||||||
if (fs.existsSync(pluginDest)) {
|
|
||||||
fs.rmdirSync(pluginDest, { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy plugin over
|
|
||||||
copyFolder(fromRootPath('plugins', plugin), pluginDest, (file) => {
|
|
||||||
// Ignore test files
|
|
||||||
if (file.endsWith('.test.js')) return false
|
|
||||||
// Ignore postcss7 files
|
|
||||||
if (file.endsWith('.postcss7.js')) return false
|
|
||||||
// Ignore postcss8 files
|
|
||||||
if (file.endsWith('.postcss8.js')) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
function fromRootPath(...paths) {
|
|
||||||
return path.resolve(process.cwd(), ...paths)
|
|
||||||
}
|
|
||||||
|
|
||||||
function copy(fromPath, toPath) {
|
|
||||||
fs.mkdirSync(path.dirname(toPath), { recursive: true }) // Ensure folder exists
|
|
||||||
fs.copyFileSync(fromPath, toPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyFolder(fromPath, toPath, shouldCopy = () => true) {
|
|
||||||
let stats = fs.statSync(fromPath)
|
|
||||||
if (stats.isDirectory()) {
|
|
||||||
let filesAndFolders = fs.readdirSync(fromPath)
|
|
||||||
for (let file of filesAndFolders) {
|
|
||||||
copyFolder(path.resolve(fromPath, file), path.resolve(toPath, file), shouldCopy)
|
|
||||||
}
|
|
||||||
} else if (shouldCopy(fromPath)) {
|
|
||||||
copy(fromPath, toPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
import fs from 'fs'
|
|
||||||
import postcss from 'postcss'
|
|
||||||
import tailwind from '..'
|
|
||||||
import CleanCSS from 'clean-css'
|
|
||||||
|
|
||||||
function buildDistFile(filename, config = {}, outFilename = filename) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
console.log(`Processing ./${filename}.css...`)
|
|
||||||
|
|
||||||
fs.readFile(`./${filename}.css`, (err, css) => {
|
|
||||||
if (err) throw err
|
|
||||||
|
|
||||||
return postcss([tailwind(config), require('autoprefixer')])
|
|
||||||
.process(css, {
|
|
||||||
from: `./${filename}.css`,
|
|
||||||
to: `./dist/${outFilename}.css`,
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
fs.writeFileSync(`./dist/${outFilename}.css`, result.css)
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
const minified = new CleanCSS().minify(result.css)
|
|
||||||
fs.writeFileSync(`./dist/${outFilename}.min.css`, minified.styles)
|
|
||||||
})
|
|
||||||
.then(resolve)
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error)
|
|
||||||
reject()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
console.info('Building Tailwind!')
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
buildDistFile('base'),
|
|
||||||
buildDistFile('components'),
|
|
||||||
buildDistFile('utilities'),
|
|
||||||
buildDistFile('tailwind'),
|
|
||||||
buildDistFile('tailwind', { darkMode: 'class' }, 'tailwind-dark'),
|
|
||||||
buildDistFile('tailwind', { future: 'all', experimental: 'all' }, 'tailwind-experimental'),
|
|
||||||
]).then(() => {
|
|
||||||
console.log('Finished Building Tailwind!')
|
|
||||||
})
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
let fs = require('fs')
|
|
||||||
let path = require('path')
|
|
||||||
let merge = require('lodash/merge')
|
|
||||||
let fastGlob = require('fast-glob')
|
|
||||||
|
|
||||||
let postcss7 = fastGlob.sync(['./**/*.postcss7.*']).filter((file) => !file.startsWith('lib/'))
|
|
||||||
let postcss8 = fastGlob.sync(['./**/*.postcss8.*']).filter((file) => !file.startsWith('lib/'))
|
|
||||||
|
|
||||||
if (process.argv.includes('--prepare')) {
|
|
||||||
if (postcss8.length > 0) {
|
|
||||||
console.error('\n\n[ABORT] Already in PostCSS 7 compatibility mode!\n\n')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mainPackageJson = require('../package.json')
|
|
||||||
let compatPackageJson = require('../package.postcss7.json')
|
|
||||||
|
|
||||||
// Use postcss7 files
|
|
||||||
for (let file of postcss7) {
|
|
||||||
let bareFile = file.replace('.postcss7', '')
|
|
||||||
let postcss8File = file.replace('.postcss7', '.postcss8')
|
|
||||||
|
|
||||||
// Backup
|
|
||||||
copy(fromRootPath(bareFile), fromRootPath(postcss8File))
|
|
||||||
|
|
||||||
// Swap
|
|
||||||
copy(fromRootPath(file), fromRootPath(bareFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deep merge package.json contents
|
|
||||||
let packageJson = merge({}, mainPackageJson, compatPackageJson)
|
|
||||||
|
|
||||||
// Remove peerDependencies
|
|
||||||
delete packageJson.peerDependencies
|
|
||||||
|
|
||||||
// Cleanup devDependencies
|
|
||||||
for (let key in packageJson.devDependencies) {
|
|
||||||
if (key.includes('postcss')) delete packageJson.devDependencies[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use new name
|
|
||||||
packageJson.name = '@tailwindcss/postcss7-compat'
|
|
||||||
|
|
||||||
// Make sure you can publish
|
|
||||||
packageJson.publishConfig = { access: 'public' }
|
|
||||||
|
|
||||||
// Write package.json with the new contents
|
|
||||||
fs.writeFileSync(fromRootPath('package.json'), JSON.stringify(packageJson, null, 2), 'utf8')
|
|
||||||
|
|
||||||
// Print some useful information to make publishing easy
|
|
||||||
console.log()
|
|
||||||
console.log('You can safely publish `tailwindcss` in PostCSS 7 compatibility mode:\n')
|
|
||||||
console.log()
|
|
||||||
} else if (process.argv.includes('--restore')) {
|
|
||||||
if (postcss8.length === 0) {
|
|
||||||
console.error('\n\n[ABORT] Already in latest PostCSS mode!\n\n')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use postcss8 files
|
|
||||||
for (let file of postcss8) {
|
|
||||||
let bareFile = file.replace('.postcss8', '')
|
|
||||||
|
|
||||||
// Restore
|
|
||||||
copy(fromRootPath(file), fromRootPath(bareFile))
|
|
||||||
|
|
||||||
// Remove
|
|
||||||
fs.unlinkSync(fromRootPath(file))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done
|
|
||||||
console.log()
|
|
||||||
console.log('Restored from PostCSS 7 mode to latest PostCSS mode!')
|
|
||||||
console.log()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
function fromRootPath(...paths) {
|
|
||||||
return path.resolve(process.cwd(), ...paths)
|
|
||||||
}
|
|
||||||
|
|
||||||
function copy(fromPath, toPath) {
|
|
||||||
fs.copyFileSync(fromPath, toPath)
|
|
||||||
}
|
|
||||||
142
src/cli.js
142
src/cli.js
@ -9,11 +9,11 @@ import fs from 'fs'
|
|||||||
import postcssrc from 'postcss-load-config'
|
import postcssrc from 'postcss-load-config'
|
||||||
import { cosmiconfig } from 'cosmiconfig'
|
import { cosmiconfig } from 'cosmiconfig'
|
||||||
import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
|
import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
|
||||||
import tailwindJit from './jit/processTailwindFeatures'
|
import tailwind from './processTailwindFeatures'
|
||||||
import tailwindAot from './processTailwindFeatures'
|
|
||||||
import resolveConfigInternal from '../resolveConfig'
|
import resolveConfigInternal from '../resolveConfig'
|
||||||
import fastGlob from 'fast-glob'
|
import fastGlob from 'fast-glob'
|
||||||
import getModuleDependencies from './lib/getModuleDependencies'
|
import getModuleDependencies from './lib/getModuleDependencies'
|
||||||
|
import log from './util/log'
|
||||||
import packageJson from '../package.json'
|
import packageJson from '../package.json'
|
||||||
|
|
||||||
let env = {
|
let env = {
|
||||||
@ -86,7 +86,9 @@ function help({ message, usage, commands, options }) {
|
|||||||
|
|
||||||
console.log()
|
console.log()
|
||||||
console.log('Options:')
|
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) {
|
if (flags.length === 1) {
|
||||||
console.log(
|
console.log(
|
||||||
' '.repeat(indent + 4 /* 4 = "-i, ".length */),
|
' '.repeat(indent + 4 /* 4 = "-i, ".length */),
|
||||||
@ -126,7 +128,6 @@ let commands = {
|
|||||||
init: {
|
init: {
|
||||||
run: init,
|
run: init,
|
||||||
args: {
|
args: {
|
||||||
'--jit': { type: Boolean, description: 'Initialize for JIT mode' },
|
|
||||||
'--full': { type: Boolean, description: 'Initialize a full `tailwind.config.js` file' },
|
'--full': { type: Boolean, description: 'Initialize a full `tailwind.config.js` file' },
|
||||||
'--postcss': { type: Boolean, description: 'Initialize a `postcss.config.js` file' },
|
'--postcss': { type: Boolean, description: 'Initialize a `postcss.config.js` file' },
|
||||||
'-f': '--full',
|
'-f': '--full',
|
||||||
@ -139,8 +140,14 @@ let commands = {
|
|||||||
'--input': { type: String, description: 'Input file' },
|
'--input': { type: String, description: 'Input file' },
|
||||||
'--output': { type: String, description: 'Output file' },
|
'--output': { type: String, description: 'Output file' },
|
||||||
'--watch': { type: Boolean, description: 'Watch for changes and rebuild as needed' },
|
'--watch': { type: Boolean, description: 'Watch for changes and rebuild as needed' },
|
||||||
'--jit': { type: Boolean, description: 'Build using JIT mode' },
|
'--content': {
|
||||||
'--purge': { type: String, description: 'Content paths to use for removing unused classes' },
|
type: String,
|
||||||
|
description: 'Content paths to use for removing unused classes',
|
||||||
|
},
|
||||||
|
'--purge': {
|
||||||
|
type: String,
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
'--postcss': {
|
'--postcss': {
|
||||||
type: oneOf(String, Boolean),
|
type: oneOf(String, Boolean),
|
||||||
description: 'Load custom PostCSS configuration',
|
description: 'Load custom PostCSS configuration',
|
||||||
@ -306,15 +313,6 @@ function init() {
|
|||||||
// Change colors import
|
// Change colors import
|
||||||
stubFile = stubFile.replace('../colors', 'tailwindcss/colors')
|
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')
|
fs.writeFileSync(tailwindConfigLocation, stubFile, 'utf8')
|
||||||
|
|
||||||
messages.push(`Created Tailwind CSS config file: ${path.basename(tailwindConfigLocation)}`)
|
messages.push(`Created Tailwind CSS config file: ${path.basename(tailwindConfigLocation)}`)
|
||||||
@ -420,39 +418,21 @@ async function build() {
|
|||||||
let resolvedConfig = resolveConfigInternal(config)
|
let resolvedConfig = resolveConfigInternal(config)
|
||||||
|
|
||||||
if (args['--purge']) {
|
if (args['--purge']) {
|
||||||
resolvedConfig.purge = {
|
log.warn(['The `--purge` flag has been deprecated.', 'Please use `--content` instead.'])
|
||||||
enabled: true,
|
if (!args['--content']) {
|
||||||
content: args['--purge'].split(/(?<!{[^}]+),/),
|
args['--content'] = ['--purge']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args['--jit']) {
|
if (args['--content']) {
|
||||||
resolvedConfig.mode = 'jit'
|
resolvedConfig.content = args['--content'].split(/(?<!{[^}]+),/)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolvedConfig
|
return resolvedConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractContent(config) {
|
function extractContent(config) {
|
||||||
let content = Array.isArray(config.purge) ? config.purge : config.purge.content
|
return config.content.content.concat(config.content.safelist)
|
||||||
|
|
||||||
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}'.`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractFileGlobs(config) {
|
function extractFileGlobs(config) {
|
||||||
@ -473,7 +453,7 @@ async function build() {
|
|||||||
function getChangedContent(config) {
|
function getChangedContent(config) {
|
||||||
let changedContent = []
|
let changedContent = []
|
||||||
|
|
||||||
// Resolve globs from the purge config
|
// Resolve globs from the content config
|
||||||
let globs = extractFileGlobs(config)
|
let globs = extractFileGlobs(config)
|
||||||
let files = fastGlob.sync(globs)
|
let files = fastGlob.sync(globs)
|
||||||
|
|
||||||
@ -496,26 +476,18 @@ async function build() {
|
|||||||
let config = resolveConfig()
|
let config = resolveConfig()
|
||||||
let changedContent = getChangedContent(config)
|
let changedContent = getChangedContent(config)
|
||||||
|
|
||||||
let tailwindPlugin =
|
let tailwindPlugin = () => {
|
||||||
config.mode === 'jit'
|
return {
|
||||||
? () => {
|
postcssPlugin: 'tailwindcss',
|
||||||
return {
|
Once(root, { result }) {
|
||||||
postcssPlugin: 'tailwindcss',
|
tailwind(({ createContext }) => {
|
||||||
Once(root, { result }) {
|
return () => {
|
||||||
tailwindJit(({ createContext }) => {
|
return createContext(config, changedContent)
|
||||||
return () => {
|
|
||||||
return createContext(config, changedContent)
|
|
||||||
}
|
|
||||||
})(root, result)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
})(root, result)
|
||||||
: () => {
|
},
|
||||||
return {
|
}
|
||||||
postcssPlugin: 'tailwindcss',
|
}
|
||||||
plugins: [tailwindAot(() => config, configPath)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tailwindPlugin.postcss = true
|
tailwindPlugin.postcss = true
|
||||||
|
|
||||||
@ -637,39 +609,31 @@ async function build() {
|
|||||||
async function rebuild(config) {
|
async function rebuild(config) {
|
||||||
env.DEBUG && console.time('Finished in')
|
env.DEBUG && console.time('Finished in')
|
||||||
|
|
||||||
let tailwindPlugin =
|
let tailwindPlugin = () => {
|
||||||
config.mode === 'jit'
|
return {
|
||||||
? () => {
|
postcssPlugin: 'tailwindcss',
|
||||||
return {
|
Once(root, { result }) {
|
||||||
postcssPlugin: 'tailwindcss',
|
env.DEBUG && console.time('Compiling CSS')
|
||||||
Once(root, { result }) {
|
tailwind(({ createContext }) => {
|
||||||
env.DEBUG && console.time('Compiling CSS')
|
console.error()
|
||||||
tailwindJit(({ createContext }) => {
|
console.error('Rebuilding...')
|
||||||
console.error()
|
|
||||||
console.error('Rebuilding...')
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (context !== null) {
|
if (context !== null) {
|
||||||
context.changedContent = changedContent.splice(0)
|
context.changedContent = changedContent.splice(0)
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
env.DEBUG && console.time('Creating context')
|
env.DEBUG && console.time('Creating context')
|
||||||
context = createContext(config, changedContent.splice(0))
|
context = createContext(config, changedContent.splice(0))
|
||||||
env.DEBUG && console.timeEnd('Creating context')
|
env.DEBUG && console.timeEnd('Creating context')
|
||||||
return context
|
return context
|
||||||
}
|
|
||||||
})(root, result)
|
|
||||||
env.DEBUG && console.timeEnd('Compiling CSS')
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
})(root, result)
|
||||||
: () => {
|
env.DEBUG && console.timeEnd('Compiling CSS')
|
||||||
return {
|
},
|
||||||
postcssPlugin: 'tailwindcss',
|
}
|
||||||
plugins: [tailwindAot(() => config, configPath)],
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tailwindPlugin.postcss = true
|
tailwindPlugin.postcss = true
|
||||||
|
|
||||||
|
|||||||
@ -1,57 +1,335 @@
|
|||||||
import * as plugins from './plugins/index.js'
|
import postcss from 'postcss'
|
||||||
import configurePlugins from './util/configurePlugins'
|
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) {
|
export default {
|
||||||
let lowestBefore = -1
|
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) {
|
addVariant(
|
||||||
let index = items.indexOf(before)
|
'first-line',
|
||||||
if (index >= 0 && (index < lowestBefore || lowestBefore === -1)) {
|
transformAllSelectors((selector) => {
|
||||||
lowestBefore = index
|
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) {
|
let groupMarker = prefixSelector(config('prefix'), '.group')
|
||||||
return items
|
for (let variant of pseudoVariants) {
|
||||||
}
|
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||||
|
let groupVariantName = `group-${variantName}`
|
||||||
|
|
||||||
items = [...items]
|
addVariant(
|
||||||
let fromIndex = items.indexOf(item)
|
groupVariantName,
|
||||||
let toIndex = lowestBefore
|
transformAllSelectors((selector) => {
|
||||||
items.splice(fromIndex, 1)
|
let variantSelector = updateAllClasses(selector, (className) => {
|
||||||
items.splice(toIndex, 0, item)
|
if (`.${className}` === groupMarker) return className
|
||||||
return items
|
return `${groupVariantName}${config('separator')}${className}`
|
||||||
}
|
})
|
||||||
|
|
||||||
export default function ({ corePlugins: corePluginConfig }) {
|
if (variantSelector === selector) {
|
||||||
let pluginOrder = Object.keys(plugins)
|
return null
|
||||||
|
}
|
||||||
pluginOrder = configurePlugins(corePluginConfig, pluginOrder)
|
|
||||||
pluginOrder = move(pluginOrder, 'transform', ['translate', 'rotate', 'skew', 'scale'])
|
return applyPseudoToMarker(
|
||||||
pluginOrder = move(pluginOrder, 'filter', [
|
variantSelector,
|
||||||
'blur',
|
groupMarker,
|
||||||
'brightness',
|
state,
|
||||||
'contrast',
|
(marker, selector) => `${marker} ${selector}`
|
||||||
'dropShadow',
|
)
|
||||||
'grayscale',
|
})
|
||||||
'hueRotate',
|
)
|
||||||
'invert',
|
}
|
||||||
'saturate',
|
|
||||||
'sepia',
|
let peerMarker = prefixSelector(config('prefix'), '.peer')
|
||||||
])
|
for (let variant of pseudoVariants) {
|
||||||
pluginOrder = move(pluginOrder, 'backdropFilter', [
|
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
|
||||||
'backdropBlur',
|
let peerVariantName = `peer-${variantName}`
|
||||||
'backdropBrightness',
|
|
||||||
'backdropContrast',
|
addVariant(
|
||||||
'backdropGrayscale',
|
peerVariantName,
|
||||||
'backdropHueRotate',
|
transformAllSelectors((selector) => {
|
||||||
'backdropInvert',
|
let variantSelector = updateAllClasses(selector, (className) => {
|
||||||
'backdropOpacity',
|
if (`.${className}` === peerMarker) return className
|
||||||
'backdropSaturate',
|
return `${peerVariantName}${config('separator')}${className}`
|
||||||
'backdropSepia',
|
})
|
||||||
])
|
|
||||||
|
if (variantSelector === selector) {
|
||||||
return pluginOrder.map((pluginName) => {
|
return null
|
||||||
return plugins[pluginName]()
|
}
|
||||||
})
|
|
||||||
|
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 setupTrackingContext from './lib/setupTrackingContext'
|
||||||
import fs from 'fs'
|
import setupWatchingContext from './lib/setupWatchingContext'
|
||||||
|
|
||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
import getModuleDependencies from './lib/getModuleDependencies'
|
|
||||||
import registerConfigAsDependency from './lib/registerConfigAsDependency'
|
|
||||||
import processTailwindFeatures from './processTailwindFeatures'
|
import processTailwindFeatures from './processTailwindFeatures'
|
||||||
import formatCSS from './lib/formatCSS'
|
import { env } from './lib/sharedState'
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
module.exports = function tailwindcss(configOrPath) {
|
||||||
return {
|
return {
|
||||||
postcssPlugin: 'tailwindcss',
|
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 postcss from 'postcss'
|
||||||
import { resolveMatches } from './generateRules'
|
import { resolveMatches } from './generateRules'
|
||||||
import bigSign from '../../util/bigSign'
|
import bigSign from '../util/bigSign'
|
||||||
import escapeClassName from '../../util/escapeClassName'
|
import escapeClassName from '../util/escapeClassName'
|
||||||
|
|
||||||
function buildApplyCache(applyCandidates, context) {
|
function buildApplyCache(applyCandidates, context) {
|
||||||
for (let candidate of applyCandidates) {
|
for (let candidate of applyCandidates) {
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import * as sharedState from './sharedState'
|
import * as sharedState from './sharedState'
|
||||||
import { generateRules } from './generateRules'
|
import { generateRules } from './generateRules'
|
||||||
import bigSign from '../../util/bigSign'
|
import bigSign from '../util/bigSign'
|
||||||
import cloneNodes from '../../util/cloneNodes'
|
import cloneNodes from '../util/cloneNodes'
|
||||||
|
|
||||||
let env = sharedState.env
|
let env = sharedState.env
|
||||||
let contentMatchCache = sharedState.contentMatchCache
|
let contentMatchCache = sharedState.contentMatchCache
|
||||||
@ -30,19 +30,18 @@ const builtInTransformers = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getExtractor(tailwindConfig, fileExtension) {
|
function getExtractor(tailwindConfig, fileExtension) {
|
||||||
let extractors = (tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.extract) || {}
|
let extractors = tailwindConfig.content.extract
|
||||||
const purgeOptions =
|
let contentOptions = tailwindConfig.content.options
|
||||||
(tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.options) || {}
|
|
||||||
|
|
||||||
if (typeof extractors === 'function') {
|
if (typeof extractors === 'function') {
|
||||||
extractors = {
|
extractors = {
|
||||||
DEFAULT: extractors,
|
DEFAULT: extractors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (purgeOptions.defaultExtractor) {
|
if (contentOptions.defaultExtractor) {
|
||||||
extractors.DEFAULT = purgeOptions.defaultExtractor
|
extractors.DEFAULT = contentOptions.defaultExtractor
|
||||||
}
|
}
|
||||||
for (let { extensions, extractor } of purgeOptions.extractors || []) {
|
for (let { extensions, extractor } of contentOptions.extractors || []) {
|
||||||
for (let extension of extensions) {
|
for (let extension of extensions) {
|
||||||
extractors[extension] = extractor
|
extractors[extension] = extractor
|
||||||
}
|
}
|
||||||
@ -57,8 +56,7 @@ function getExtractor(tailwindConfig, fileExtension) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTransformer(tailwindConfig, fileExtension) {
|
function getTransformer(tailwindConfig, fileExtension) {
|
||||||
let transformers =
|
let transformers = tailwindConfig.content.transform
|
||||||
(tailwindConfig && tailwindConfig.purge && tailwindConfig.purge.transform) || {}
|
|
||||||
|
|
||||||
if (typeof transformers === 'function') {
|
if (typeof transformers === 'function') {
|
||||||
transformers = {
|
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 postcss from 'postcss'
|
||||||
import selectorParser from 'postcss-selector-parser'
|
import selectorParser from 'postcss-selector-parser'
|
||||||
import parseObjectStyles from '../../util/parseObjectStyles'
|
import parseObjectStyles from '../util/parseObjectStyles'
|
||||||
import isPlainObject from '../../util/isPlainObject'
|
import isPlainObject from '../util/isPlainObject'
|
||||||
import prefixSelector from '../../util/prefixSelector'
|
import prefixSelector from '../util/prefixSelector'
|
||||||
import { updateAllClasses } from '../../util/pluginUtils'
|
import { updateAllClasses } from '../util/pluginUtils'
|
||||||
|
|
||||||
let classNameParser = selectorParser((selectors) => {
|
let classNameParser = selectorParser((selectors) => {
|
||||||
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
import log from '../util/log'
|
||||||
|
|
||||||
export default function normalizeTailwindDirectives(root) {
|
export default function normalizeTailwindDirectives(root) {
|
||||||
let tailwindDirectives = new Set()
|
let tailwindDirectives = new Set()
|
||||||
let layerDirectives = new Set()
|
let layerDirectives = new Set()
|
||||||
@ -38,6 +40,11 @@ export default function normalizeTailwindDirectives(root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (['layer', 'responsive', 'variants'].includes(atRule.name)) {
|
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)
|
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 dlv from 'dlv'
|
||||||
import selectorParser from 'postcss-selector-parser'
|
import selectorParser from 'postcss-selector-parser'
|
||||||
|
|
||||||
import transformThemeValue from '../../util/transformThemeValue'
|
import transformThemeValue from '../util/transformThemeValue'
|
||||||
import parseObjectStyles from '../../util/parseObjectStyles'
|
import parseObjectStyles from '../util/parseObjectStyles'
|
||||||
import prefixSelector from '../../util/prefixSelector'
|
import prefixSelector from '../util/prefixSelector'
|
||||||
import isPlainObject from '../../util/isPlainObject'
|
import isPlainObject from '../util/isPlainObject'
|
||||||
import escapeClassName from '../../util/escapeClassName'
|
import escapeClassName from '../util/escapeClassName'
|
||||||
import nameClass from '../../util/nameClass'
|
import nameClass from '../util/nameClass'
|
||||||
import { coerceValue } from '../../util/pluginUtils'
|
import { coerceValue } from '../util/pluginUtils'
|
||||||
import bigSign from '../../util/bigSign'
|
import bigSign from '../util/bigSign'
|
||||||
import corePlugins from '../corePlugins'
|
import corePlugins from '../corePlugins'
|
||||||
import * as sharedState from './sharedState'
|
import * as sharedState from './sharedState'
|
||||||
import { env } from './sharedState'
|
import { env } from './sharedState'
|
||||||
@ -5,17 +5,17 @@ import fastGlob from 'fast-glob'
|
|||||||
import LRU from 'quick-lru'
|
import LRU from 'quick-lru'
|
||||||
import normalizePath from 'normalize-path'
|
import normalizePath from 'normalize-path'
|
||||||
|
|
||||||
import hash from '../../util/hashConfig'
|
import hash from '../util/hashConfig'
|
||||||
import getModuleDependencies from '../../lib/getModuleDependencies'
|
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 { env } from './sharedState'
|
||||||
|
|
||||||
import { getContext, getFileModifiedMap } from './setupContextUtils'
|
import { getContext, getFileModifiedMap } from './setupContextUtils'
|
||||||
import parseDependency from '../../util/parseDependency'
|
import parseDependency from '../util/parseDependency'
|
||||||
|
|
||||||
let configPathCache = new LRU({ maxSize: 100 })
|
let configPathCache = new LRU({ maxSize: 100 })
|
||||||
|
|
||||||
@ -26,13 +26,9 @@ function getCandidateFiles(context, tailwindConfig) {
|
|||||||
return candidateFilesCache.get(context)
|
return candidateFilesCache.get(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
let purgeContent = Array.isArray(tailwindConfig.purge)
|
let candidateFiles = tailwindConfig.content.content
|
||||||
? tailwindConfig.purge
|
|
||||||
: tailwindConfig.purge.content
|
|
||||||
|
|
||||||
let candidateFiles = purgeContent
|
|
||||||
.filter((item) => typeof item === 'string')
|
.filter((item) => typeof item === 'string')
|
||||||
.map((purgePath) => normalizePath(path.resolve(purgePath)))
|
.map((contentPath) => normalizePath(path.resolve(contentPath)))
|
||||||
|
|
||||||
return candidateFilesCache.set(context, candidateFiles).get(context)
|
return candidateFilesCache.set(context, candidateFiles).get(context)
|
||||||
}
|
}
|
||||||
@ -81,29 +77,9 @@ function getTailwindConfig(configOrPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
|
function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
|
||||||
let changedContent = (
|
let changedContent = context.tailwindConfig.content.content
|
||||||
Array.isArray(context.tailwindConfig.purge)
|
|
||||||
? context.tailwindConfig.purge
|
|
||||||
: context.tailwindConfig.purge.content
|
|
||||||
)
|
|
||||||
.filter((item) => typeof item.raw === 'string')
|
.filter((item) => typeof item.raw === 'string')
|
||||||
.concat(
|
.concat(context.tailwindConfig.content.safelist)
|
||||||
(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}'.`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.map(({ raw, extension }) => ({ content: raw, extension }))
|
.map(({ raw, extension }) => ({ content: raw, extension }))
|
||||||
|
|
||||||
for (let changedFile of resolveChangedFiles(candidateFiles, fileModifiedMap)) {
|
for (let changedFile of resolveChangedFiles(candidateFiles, fileModifiedMap)) {
|
||||||
@ -7,11 +7,11 @@ import fastGlob from 'fast-glob'
|
|||||||
import LRU from 'quick-lru'
|
import LRU from 'quick-lru'
|
||||||
import normalizePath from 'normalize-path'
|
import normalizePath from 'normalize-path'
|
||||||
|
|
||||||
import hash from '../../util/hashConfig'
|
import hash from '../util/hashConfig'
|
||||||
import log from '../../util/log'
|
import log from '../util/log'
|
||||||
import getModuleDependencies from '../../lib/getModuleDependencies'
|
import getModuleDependencies from '../lib/getModuleDependencies'
|
||||||
import resolveConfig from '../../../resolveConfig'
|
import resolveConfig from '../../resolveConfig'
|
||||||
import resolveConfigPath from '../../util/resolveConfigPath'
|
import resolveConfigPath from '../util/resolveConfigPath'
|
||||||
import { getContext } from './setupContextUtils'
|
import { getContext } from './setupContextUtils'
|
||||||
|
|
||||||
// This is used to trigger rebuilds. Just updating the timestamp
|
// This is used to trigger rebuilds. Just updating the timestamp
|
||||||
@ -147,13 +147,9 @@ function getCandidateFiles(context, tailwindConfig) {
|
|||||||
return candidateFilesCache.get(context)
|
return candidateFilesCache.get(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
let purgeContent = Array.isArray(tailwindConfig.purge)
|
let candidateFiles = tailwindConfig.content.content
|
||||||
? tailwindConfig.purge
|
|
||||||
: tailwindConfig.purge.content
|
|
||||||
|
|
||||||
let candidateFiles = purgeContent
|
|
||||||
.filter((item) => typeof item === 'string')
|
.filter((item) => typeof item === 'string')
|
||||||
.map((purgePath) => normalizePath(path.resolve(purgePath)))
|
.map((contentPath) => normalizePath(path.resolve(contentPath)))
|
||||||
|
|
||||||
return candidateFilesCache.set(context, candidateFiles).get(context)
|
return candidateFilesCache.set(context, candidateFiles).get(context)
|
||||||
}
|
}
|
||||||
@ -189,29 +185,9 @@ function getTailwindConfig(configOrPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolvedChangedContent(context, candidateFiles) {
|
function resolvedChangedContent(context, candidateFiles) {
|
||||||
let changedContent = (
|
let changedContent = context.tailwindConfig.content.content
|
||||||
Array.isArray(context.tailwindConfig.purge)
|
|
||||||
? context.tailwindConfig.purge
|
|
||||||
: context.tailwindConfig.purge.content
|
|
||||||
)
|
|
||||||
.filter((item) => typeof item.raw === 'string')
|
.filter((item) => typeof item.raw === 'string')
|
||||||
.concat(
|
.concat(context.tailwindConfig.content.safelist)
|
||||||
(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}'.`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.map(({ raw, extension }) => ({ content: raw, extension }))
|
.map(({ raw, extension }) => ({ content: raw, extension }))
|
||||||
|
|
||||||
for (let changedFile of resolveChangedFiles(context, candidateFiles)) {
|
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 }) {
|
return function ({ matchUtilities, theme, variants, prefix }) {
|
||||||
let prefixName = (name) => prefix(`.${name}`).slice(1)
|
let prefixName = (name) => prefix(`.${name}`).slice(1)
|
||||||
let keyframes = Object.fromEntries(
|
let keyframes = Object.fromEntries(
|
||||||
Object.entries(theme('keyframes') || {}).map(([key, value]) => {
|
Object.entries(theme('keyframes') ?? {}).map(([key, value]) => {
|
||||||
return [
|
return [
|
||||||
key,
|
key,
|
||||||
[
|
[
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-blur': (value) => {
|
'backdrop-blur': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-blur': `blur(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-brightness': (value) => {
|
'backdrop-brightness': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-brightness': `brightness(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-contrast': (value) => {
|
'backdrop-contrast': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-contrast': `contrast(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, addBase, addUtilities, variants }) {
|
return function ({ addBase, addUtilities, variants }) {
|
||||||
if (config('mode') === 'jit') {
|
addBase({
|
||||||
addBase({
|
'@defaults backdrop-filter': {
|
||||||
'@defaults backdrop-filter': {
|
'--tw-backdrop-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-opacity': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-opacity': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-backdrop-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-backdrop-filter': [
|
||||||
'--tw-backdrop-filter': [
|
'var(--tw-backdrop-blur)',
|
||||||
'var(--tw-backdrop-blur)',
|
'var(--tw-backdrop-brightness)',
|
||||||
'var(--tw-backdrop-brightness)',
|
'var(--tw-backdrop-contrast)',
|
||||||
'var(--tw-backdrop-contrast)',
|
'var(--tw-backdrop-grayscale)',
|
||||||
'var(--tw-backdrop-grayscale)',
|
'var(--tw-backdrop-hue-rotate)',
|
||||||
'var(--tw-backdrop-hue-rotate)',
|
'var(--tw-backdrop-invert)',
|
||||||
'var(--tw-backdrop-invert)',
|
'var(--tw-backdrop-opacity)',
|
||||||
'var(--tw-backdrop-opacity)',
|
'var(--tw-backdrop-saturate)',
|
||||||
'var(--tw-backdrop-saturate)',
|
'var(--tw-backdrop-sepia)',
|
||||||
'var(--tw-backdrop-sepia)',
|
].join(' '),
|
||||||
].join(' '),
|
},
|
||||||
|
})
|
||||||
|
addUtilities(
|
||||||
|
{
|
||||||
|
'.backdrop-filter': {
|
||||||
|
'@defaults backdrop-filter': {},
|
||||||
|
'backdrop-filter': 'var(--tw-backdrop-filter)',
|
||||||
},
|
},
|
||||||
})
|
'.backdrop-filter-none': { 'backdrop-filter': 'none' },
|
||||||
addUtilities(
|
},
|
||||||
{
|
variants('backdropFilter')
|
||||||
'.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')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-grayscale': (value) => {
|
'backdrop-grayscale': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-grayscale': `grayscale(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-hue-rotate': (value) => {
|
'backdrop-hue-rotate': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-hue-rotate': `hue-rotate(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-invert': (value) => {
|
'backdrop-invert': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-invert': `invert(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-opacity': (value) => {
|
'backdrop-opacity': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-opacity': `opacity(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-saturate': (value) => {
|
'backdrop-saturate': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-saturate': `saturate(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'backdrop-sepia': (value) => {
|
'backdrop-sepia': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-backdrop-sepia': `sepia(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
blur: (value) => {
|
blur: (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-blur': `blur(${value})`,
|
'--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'
|
import withAlphaVariable from '../util/withAlphaVariable'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, addBase, matchUtilities, theme, variants, corePlugins }) {
|
return function ({ addBase, matchUtilities, theme, variants, corePlugins }) {
|
||||||
if (config('mode') === 'jit') {
|
if (!corePlugins('borderOpacity')) {
|
||||||
if (!corePlugins('borderOpacity')) {
|
addBase({
|
||||||
addBase({
|
'@defaults border-width': {
|
||||||
'@defaults border-width': {
|
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
|
||||||
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
} else {
|
|
||||||
addBase({
|
|
||||||
'@defaults border-width': withAlphaVariable({
|
|
||||||
color: theme('borderColor.DEFAULT', 'currentColor'),
|
|
||||||
property: 'border-color',
|
|
||||||
variable: '--tw-border-opacity',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (!corePlugins('borderOpacity')) {
|
addBase({
|
||||||
addBase({
|
'@defaults border-width': withAlphaVariable({
|
||||||
'*, ::before, ::after': {
|
color: theme('borderColor.DEFAULT', 'currentColor'),
|
||||||
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
|
property: 'border-color',
|
||||||
},
|
variable: '--tw-border-opacity',
|
||||||
})
|
}),
|
||||||
} else {
|
})
|
||||||
addBase({
|
|
||||||
'*, ::before, ::after': withAlphaVariable({
|
|
||||||
color: theme('borderColor.DEFAULT', 'currentColor'),
|
|
||||||
property: 'border-color',
|
|
||||||
variable: '--tw-border-opacity',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
@ -60,70 +42,66 @@ export default function () {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (config('mode') === 'jit') {
|
matchUtilities(
|
||||||
matchUtilities(
|
{
|
||||||
{
|
'border-t': (value) => {
|
||||||
'border-t': (value) => {
|
if (!corePlugins('borderOpacity')) {
|
||||||
if (!corePlugins('borderOpacity')) {
|
return {
|
||||||
return {
|
'border-top-color': value,
|
||||||
'border-top-color': value,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return withAlphaVariable({
|
return withAlphaVariable({
|
||||||
color: value,
|
color: value,
|
||||||
property: 'border-top-color',
|
property: 'border-top-color',
|
||||||
variable: '--tw-border-opacity',
|
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',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
'border-r': (value) => {
|
||||||
values: (({ DEFAULT: _, ...colors }) => colors)(
|
if (!corePlugins('borderOpacity')) {
|
||||||
flattenColorPalette(theme('borderColor'))
|
return {
|
||||||
),
|
'border-right-color': value,
|
||||||
variants: variants('borderColor'),
|
}
|
||||||
type: 'color',
|
}
|
||||||
}
|
|
||||||
)
|
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'
|
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function (helpers) {
|
return createUtilityPlugin(
|
||||||
if (helpers.config('mode') === 'jit') {
|
'borderWidth',
|
||||||
createUtilityPlugin(
|
[
|
||||||
'borderWidth',
|
['border', [['@defaults border-width', {}], 'border-width']],
|
||||||
[
|
[
|
||||||
['border', [['@defaults border-width', {}], 'border-width']],
|
['border-t', [['@defaults border-width', {}], 'border-top-width']],
|
||||||
[
|
['border-r', [['@defaults border-width', {}], 'border-right-width']],
|
||||||
['border-t', [['@defaults border-width', {}], 'border-top-width']],
|
['border-b', [['@defaults border-width', {}], 'border-bottom-width']],
|
||||||
['border-r', [['@defaults border-width', {}], 'border-right-width']],
|
['border-l', [['@defaults border-width', {}], 'border-left-width']],
|
||||||
['border-b', [['@defaults border-width', {}], 'border-bottom-width']],
|
],
|
||||||
['border-l', [['@defaults border-width', {}], 'border-left-width']],
|
],
|
||||||
],
|
{ resolveArbitraryValue: asLength }
|
||||||
],
|
)
|
||||||
{ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,25 +8,14 @@ let defaultBoxShadow = [
|
|||||||
].join(', ')
|
].join(', ')
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, addBase, addUtilities, theme, variants }) {
|
return function ({ matchUtilities, addBase, theme, variants }) {
|
||||||
if (config('mode') === 'jit') {
|
addBase({
|
||||||
addBase({
|
'@defaults box-shadow': {
|
||||||
'@defaults box-shadow': {
|
'--tw-ring-offset-shadow': '0 0 #0000',
|
||||||
'--tw-ring-offset-shadow': '0 0 #0000',
|
'--tw-ring-shadow': '0 0 #0000',
|
||||||
'--tw-ring-shadow': '0 0 #0000',
|
'--tw-shadow': '0 0 #0000',
|
||||||
'--tw-shadow': '0 0 #0000',
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
} else {
|
|
||||||
addUtilities(
|
|
||||||
{
|
|
||||||
'*, ::before, ::after': {
|
|
||||||
'--tw-shadow': '0 0 #0000',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ respectImportant: false }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
@ -34,7 +23,7 @@ export default function () {
|
|||||||
value = transformValue(value)
|
value = transformValue(value)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(config('mode') === 'jit' ? { '@defaults box-shadow': {} } : {}),
|
'@defaults box-shadow': {},
|
||||||
'--tw-shadow': value === 'none' ? '0 0 #0000' : value,
|
'--tw-shadow': value === 'none' ? '0 0 #0000' : value,
|
||||||
'box-shadow': defaultBoxShadow,
|
'box-shadow': defaultBoxShadow,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
brightness: (value) => {
|
brightness: (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-brightness': `brightness(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
contrast: (value) => {
|
contrast: (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-contrast': `contrast(${value})`,
|
'--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'
|
import nameClass from '../util/nameClass'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, addUtilities, theme, variants }) {
|
return function ({ addUtilities, theme, variants }) {
|
||||||
const utilities = _.fromPairs(
|
const utilities = _.fromPairs(
|
||||||
_.map(theme('dropShadow'), (value, modifier) => {
|
_.map(theme('dropShadow'), (value, modifier) => {
|
||||||
return [
|
return [
|
||||||
@ -11,12 +11,8 @@ export default function () {
|
|||||||
'--tw-drop-shadow': Array.isArray(value)
|
'--tw-drop-shadow': Array.isArray(value)
|
||||||
? value.map((v) => `drop-shadow(${v})`).join(' ')
|
? value.map((v) => `drop-shadow(${v})`).join(' ')
|
||||||
: `drop-shadow(${value})`,
|
: `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 () {
|
export default function () {
|
||||||
return function ({ config, addBase, addUtilities, variants }) {
|
return function ({ addBase, addUtilities, variants }) {
|
||||||
if (config('mode') === 'jit') {
|
addBase({
|
||||||
addBase({
|
'@defaults filter': {
|
||||||
'@defaults filter': {
|
'--tw-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-blur': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-grayscale': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-invert': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-saturate': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-sepia': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-drop-shadow': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-drop-shadow': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-filter': [
|
||||||
'--tw-filter': [
|
'var(--tw-blur)',
|
||||||
'var(--tw-blur)',
|
'var(--tw-brightness)',
|
||||||
'var(--tw-brightness)',
|
'var(--tw-contrast)',
|
||||||
'var(--tw-contrast)',
|
'var(--tw-grayscale)',
|
||||||
'var(--tw-grayscale)',
|
'var(--tw-hue-rotate)',
|
||||||
'var(--tw-hue-rotate)',
|
'var(--tw-invert)',
|
||||||
'var(--tw-invert)',
|
'var(--tw-saturate)',
|
||||||
'var(--tw-saturate)',
|
'var(--tw-sepia)',
|
||||||
'var(--tw-sepia)',
|
'var(--tw-drop-shadow)',
|
||||||
'var(--tw-drop-shadow)',
|
].join(' '),
|
||||||
].join(' '),
|
},
|
||||||
},
|
})
|
||||||
})
|
addUtilities(
|
||||||
addUtilities(
|
{
|
||||||
{
|
'.filter': { '@defaults filter': {}, filter: 'var(--tw-filter)' },
|
||||||
'.filter': { '@defaults filter': {}, filter: 'var(--tw-filter)' },
|
'.filter-none': { filter: 'none' },
|
||||||
'.filter-none': { filter: 'none' },
|
},
|
||||||
},
|
variants('filter')
|
||||||
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')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
grayscale: (value) => {
|
grayscale: (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-grayscale': `grayscale(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
'hue-rotate': (value) => {
|
'hue-rotate': (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-hue-rotate': `hue-rotate(${value})`,
|
'--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 () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
invert: (value) => {
|
invert: (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-invert': `invert(${value})`,
|
'--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'
|
import { withAlphaValue } from '../util/withAlphaVariable'
|
||||||
|
|
||||||
export default function () {
|
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 ringOpacityDefault = theme('ringOpacity.DEFAULT', '0.5')
|
||||||
let ringColorDefault = withAlphaValue(
|
let ringColorDefault = withAlphaValue(
|
||||||
theme('ringColor.DEFAULT'),
|
theme('ringColor.DEFAULT'),
|
||||||
@ -9,39 +9,23 @@ export default function () {
|
|||||||
`rgba(147, 197, 253, ${ringOpacityDefault})`
|
`rgba(147, 197, 253, ${ringOpacityDefault})`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (config('mode') === 'jit') {
|
addBase({
|
||||||
addBase({
|
'@defaults ring-width': {
|
||||||
'@defaults ring-width': {
|
'--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',
|
||||||
'--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',
|
'--tw-ring-offset-width': theme('ringOffsetWidth.DEFAULT', '0px'),
|
||||||
'--tw-ring-offset-width': theme('ringOffsetWidth.DEFAULT', '0px'),
|
'--tw-ring-offset-color': theme('ringOffsetColor.DEFAULT', '#fff'),
|
||||||
'--tw-ring-offset-color': theme('ringOffsetColor.DEFAULT', '#fff'),
|
'--tw-ring-color': ringColorDefault,
|
||||||
'--tw-ring-color': ringColorDefault,
|
'--tw-ring-offset-shadow': '0 0 #0000',
|
||||||
'--tw-ring-offset-shadow': '0 0 #0000',
|
'--tw-ring-shadow': '0 0 #0000',
|
||||||
'--tw-ring-shadow': '0 0 #0000',
|
'--tw-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 }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
ring: (value) => {
|
ring: (value) => {
|
||||||
return {
|
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-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)`,
|
'--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(${value} + var(--tw-ring-offset-width)) var(--tw-ring-color)`,
|
||||||
'box-shadow': [
|
'box-shadow': [
|
||||||
@ -62,7 +46,7 @@ export default function () {
|
|||||||
addUtilities(
|
addUtilities(
|
||||||
{
|
{
|
||||||
'.ring-inset': {
|
'.ring-inset': {
|
||||||
...(config('mode') === 'jit' ? { '@defaults ring-width': {} } : {}),
|
'@defaults ring-width': {},
|
||||||
'--tw-ring-inset': 'inset',
|
'--tw-ring-inset': 'inset',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,16 +1,7 @@
|
|||||||
import createUtilityPlugin from '../util/createUtilityPlugin'
|
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, ...rest }) {
|
return createUtilityPlugin('rotate', [
|
||||||
if (config('mode') === 'jit') {
|
['rotate', [['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']]],
|
||||||
return createUtilityPlugin('rotate', [
|
])
|
||||||
[
|
|
||||||
'rotate',
|
|
||||||
[['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']],
|
|
||||||
],
|
|
||||||
])({ config, ...rest })
|
|
||||||
} else {
|
|
||||||
return createUtilityPlugin('rotate', [['rotate', ['--tw-rotate']]])({ config, ...rest })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
saturate: (value) => {
|
saturate: (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-saturate': `saturate(${value})`,
|
'--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'
|
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, ...rest }) {
|
return createUtilityPlugin('scale', [
|
||||||
if (config('mode') === 'jit') {
|
[
|
||||||
return createUtilityPlugin('scale', [
|
'scale',
|
||||||
[
|
[
|
||||||
'scale',
|
['@defaults transform', {}],
|
||||||
[
|
'--tw-scale-x',
|
||||||
['@defaults transform', {}],
|
'--tw-scale-y',
|
||||||
'--tw-scale-x',
|
['transform', 'var(--tw-transform)'],
|
||||||
'--tw-scale-y',
|
],
|
||||||
['transform', 'var(--tw-transform)'],
|
],
|
||||||
],
|
[
|
||||||
],
|
[
|
||||||
[
|
'scale-x',
|
||||||
[
|
[['@defaults transform', {}], '--tw-scale-x', ['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)']],
|
||||||
'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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, matchUtilities, theme, variants }) {
|
return function ({ matchUtilities, theme, variants }) {
|
||||||
matchUtilities(
|
matchUtilities(
|
||||||
{
|
{
|
||||||
sepia: (value) => {
|
sepia: (value) => {
|
||||||
return {
|
return {
|
||||||
'--tw-sepia': `sepia(${value})`,
|
'--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'
|
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, ...rest }) {
|
return createUtilityPlugin('skew', [
|
||||||
if (config('mode') === 'jit') {
|
[
|
||||||
return createUtilityPlugin('skew', [
|
[
|
||||||
[
|
'skew-x',
|
||||||
[
|
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
|
||||||
'skew-x',
|
],
|
||||||
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
|
[
|
||||||
],
|
'skew-y',
|
||||||
[
|
[['@defaults transform', {}], '--tw-skew-y', ['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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,32 @@
|
|||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, addBase, addUtilities, variants }) {
|
return function ({ addBase, addUtilities, variants }) {
|
||||||
if (config('mode') === 'jit') {
|
addBase({
|
||||||
addBase({
|
'@defaults transform': {
|
||||||
'@defaults transform': {
|
'--tw-translate-x': '0',
|
||||||
'--tw-translate-x': '0',
|
'--tw-translate-y': '0',
|
||||||
'--tw-translate-y': '0',
|
'--tw-rotate': '0',
|
||||||
'--tw-rotate': '0',
|
'--tw-skew-x': '0',
|
||||||
'--tw-skew-x': '0',
|
'--tw-skew-y': '0',
|
||||||
'--tw-skew-y': '0',
|
'--tw-scale-x': '1',
|
||||||
'--tw-scale-x': '1',
|
'--tw-scale-y': '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': [
|
'--tw-transform': [
|
||||||
'translateX(var(--tw-translate-x))',
|
'translateX(var(--tw-translate-x))',
|
||||||
'translateY(var(--tw-translate-y))',
|
'translateY(var(--tw-translate-y))',
|
||||||
@ -20,80 +37,19 @@ export default function () {
|
|||||||
'scaleY(var(--tw-scale-y))',
|
'scaleY(var(--tw-scale-y))',
|
||||||
].join(' '),
|
].join(' '),
|
||||||
},
|
},
|
||||||
})
|
'.transform-gpu': {
|
||||||
addUtilities(
|
'--tw-transform': [
|
||||||
{
|
'translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)',
|
||||||
'.transform': {
|
'rotate(var(--tw-rotate))',
|
||||||
'@defaults transform': {},
|
'skewX(var(--tw-skew-x))',
|
||||||
transform: 'var(--tw-transform)',
|
'skewY(var(--tw-skew-y))',
|
||||||
},
|
'scaleX(var(--tw-scale-x))',
|
||||||
'.transform-cpu': {
|
'scaleY(var(--tw-scale-y))',
|
||||||
'--tw-transform': [
|
].join(' '),
|
||||||
'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' },
|
|
||||||
},
|
},
|
||||||
variants('transform')
|
'.transform-none': { transform: 'none' },
|
||||||
)
|
},
|
||||||
} else {
|
variants('transform')
|
||||||
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')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,16 @@
|
|||||||
import createUtilityPlugin from '../util/createUtilityPlugin'
|
import createUtilityPlugin from '../util/createUtilityPlugin'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function ({ config, ...rest }) {
|
return createUtilityPlugin('translate', [
|
||||||
if (config('mode') === 'jit') {
|
[
|
||||||
return createUtilityPlugin('translate', [
|
[
|
||||||
[
|
'translate-x',
|
||||||
[
|
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
|
||||||
'translate-x',
|
],
|
||||||
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
|
[
|
||||||
],
|
'translate-y',
|
||||||
[
|
[['@defaults transform', {}], '--tw-translate-y', ['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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,79 +1,41 @@
|
|||||||
import _ from 'lodash'
|
import normalizeTailwindDirectives from './lib/normalizeTailwindDirectives'
|
||||||
import postcss from 'postcss'
|
import expandTailwindAtRules from './lib/expandTailwindAtRules'
|
||||||
|
import expandApplyAtRules from './lib/expandApplyAtRules'
|
||||||
import substituteTailwindAtRules from './lib/substituteTailwindAtRules'
|
|
||||||
import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions'
|
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 substituteScreenAtRules from './lib/substituteScreenAtRules'
|
||||||
import substituteClassApplyAtRules from './lib/substituteClassApplyAtRules'
|
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
|
||||||
import applyImportantConfiguration from './lib/applyImportantConfiguration'
|
import collapseAdjacentRules from './lib/collapseAdjacentRules'
|
||||||
import purgeUnusedStyles from './lib/purgeUnusedStyles'
|
import { createContext } from './lib/setupContextUtils'
|
||||||
|
|
||||||
import corePlugins from './corePlugins'
|
export default function processTailwindFeatures(setupContext) {
|
||||||
import processPlugins from './util/processPlugins'
|
return function (root, result) {
|
||||||
import cloneNodes from './util/cloneNodes'
|
let tailwindDirectives = normalizeTailwindDirectives(root)
|
||||||
import { issueFlagNotices } from './featureFlags.js'
|
|
||||||
|
|
||||||
import hash from './util/hashConfig'
|
let context = setupContext({
|
||||||
import log from './util/log'
|
tailwindDirectives,
|
||||||
import { shared } from './util/disposables'
|
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
|
if (context.tailwindConfig.separator === '-') {
|
||||||
let processedPlugins = null
|
throw new Error(
|
||||||
let getProcessedPlugins = null
|
"The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead."
|
||||||
|
|
||||||
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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
getProcessedPlugins = function () {
|
|
||||||
return {
|
|
||||||
...processedPlugins,
|
|
||||||
base: cloneNodes(processedPlugins.base),
|
|
||||||
components: cloneNodes(processedPlugins.components),
|
|
||||||
utilities: cloneNodes(processedPlugins.utilities),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerDependency(dependency) {
|
expandTailwindAtRules(context)(root, result)
|
||||||
result.messages.push({
|
expandApplyAtRules(context)(root, result)
|
||||||
plugin: 'tailwindcss',
|
evaluateTailwindFunctions(context)(root, result)
|
||||||
parent: result.opts.from,
|
substituteScreenAtRules(context)(root, result)
|
||||||
...dependency,
|
resolveDefaultsAtRules(context)(root, result)
|
||||||
})
|
collapseAdjacentRules(context)(root, result)
|
||||||
}
|
|
||||||
|
|
||||||
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') })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export default function createUtilityPlugin(
|
|||||||
{
|
{
|
||||||
values: filterDefault
|
values: filterDefault
|
||||||
? Object.fromEntries(
|
? Object.fromEntries(
|
||||||
Object.entries(theme(themeKey) || {}).filter(([modifier]) => modifier !== 'DEFAULT')
|
Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
|
||||||
)
|
)
|
||||||
: theme(themeKey),
|
: theme(themeKey),
|
||||||
variants: variants(themeKey),
|
variants: variants(themeKey),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
const flattenColorPalette = (colors) =>
|
const flattenColorPalette = (colors) =>
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{},
|
{},
|
||||||
...Object.entries(colors || {}).flatMap(([color, values]) =>
|
...Object.entries(colors ?? {}).flatMap(([color, values]) =>
|
||||||
typeof values == 'object'
|
typeof values == 'object'
|
||||||
? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
|
? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
|
||||||
[color + (number === 'DEFAULT' ? '' : `-${number}`)]: hex,
|
[color + (number === 'DEFAULT' ? '' : `-${number}`)]: hex,
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import corePluginList from '../corePluginList'
|
|||||||
import configurePlugins from './configurePlugins'
|
import configurePlugins from './configurePlugins'
|
||||||
import defaultConfig from '../../stubs/defaultConfig.stub'
|
import defaultConfig from '../../stubs/defaultConfig.stub'
|
||||||
import colors from '../../colors'
|
import colors from '../../colors'
|
||||||
|
import log from './log'
|
||||||
|
|
||||||
const configUtils = {
|
const configUtils = {
|
||||||
colors,
|
colors,
|
||||||
@ -257,30 +258,86 @@ function resolvePluginLists(pluginLists) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function resolveConfig(configs) {
|
export default function resolveConfig(configs) {
|
||||||
const allConfigs = [
|
let allConfigs = [
|
||||||
...extractPluginConfigs(configs),
|
...extractPluginConfigs(configs),
|
||||||
{
|
{
|
||||||
darkMode: false,
|
content: [],
|
||||||
prefix: '',
|
prefix: '',
|
||||||
important: false,
|
important: false,
|
||||||
separator: ':',
|
separator: ':',
|
||||||
variantOrder: defaultConfig.variantOrder,
|
variantOrder: defaultConfig.variantOrder,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const { variantOrder } = allConfigs.find((c) => c.variantOrder)
|
let { variantOrder } = allConfigs.find((c) => c.variantOrder)
|
||||||
|
|
||||||
return defaults(
|
return normalizeConfig(
|
||||||
{
|
defaults(
|
||||||
theme: resolveFunctionKeys(
|
{
|
||||||
mergeExtensions(mergeThemes(map(allConfigs, (t) => get(t, 'theme', {}))))
|
theme: resolveFunctionKeys(
|
||||||
),
|
mergeExtensions(mergeThemes(map(allConfigs, (t) => get(t, 'theme', {}))))
|
||||||
variants: resolveVariants(
|
),
|
||||||
allConfigs.map((c) => get(c, 'variants', {})),
|
variants: resolveVariants(
|
||||||
variantOrder
|
allConfigs.map((c) => get(c, 'variants', {})),
|
||||||
),
|
variantOrder
|
||||||
corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
|
),
|
||||||
plugins: resolvePluginLists(configs.map((c) => get(c, 'plugins', []))),
|
corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
|
||||||
},
|
plugins: resolvePluginLists(configs.map((c) => get(c, 'plugins', []))),
|
||||||
...allConfigs
|
},
|
||||||
|
...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
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
const colors = require('../colors')
|
const colors = require('../colors')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: [],
|
content: [],
|
||||||
presets: [],
|
presets: [],
|
||||||
darkMode: false, // or 'media' or 'class'
|
darkMode: 'media', // or 'class'
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
sm: '640px',
|
sm: '640px',
|
||||||
@ -822,150 +822,5 @@ module.exports = {
|
|||||||
'active',
|
'active',
|
||||||
'disabled',
|
'disabled',
|
||||||
],
|
],
|
||||||
variants: {
|
|
||||||
accessibility: ['responsive', 'focus-within', 'focus'],
|
|
||||||
alignContent: ['responsive'],
|
|
||||||
alignItems: ['responsive'],
|
|
||||||
alignSelf: ['responsive'],
|
|
||||||
animation: ['responsive'],
|
|
||||||
appearance: ['responsive'],
|
|
||||||
backdropBlur: ['responsive'],
|
|
||||||
backdropBrightness: ['responsive'],
|
|
||||||
backdropContrast: ['responsive'],
|
|
||||||
backdropFilter: ['responsive'],
|
|
||||||
backdropGrayscale: ['responsive'],
|
|
||||||
backdropHueRotate: ['responsive'],
|
|
||||||
backdropInvert: ['responsive'],
|
|
||||||
backdropOpacity: ['responsive'],
|
|
||||||
backdropSaturate: ['responsive'],
|
|
||||||
backdropSepia: ['responsive'],
|
|
||||||
backgroundAttachment: ['responsive'],
|
|
||||||
backgroundBlendMode: ['responsive'],
|
|
||||||
backgroundClip: ['responsive'],
|
|
||||||
backgroundColor: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
backgroundImage: ['responsive'],
|
|
||||||
backgroundOpacity: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
backgroundPosition: ['responsive'],
|
|
||||||
backgroundRepeat: ['responsive'],
|
|
||||||
backgroundSize: ['responsive'],
|
|
||||||
backgroundOrigin: ['responsive'],
|
|
||||||
blur: ['responsive'],
|
|
||||||
borderCollapse: ['responsive'],
|
|
||||||
borderColor: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
borderOpacity: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
borderRadius: ['responsive'],
|
|
||||||
borderStyle: ['responsive'],
|
|
||||||
borderWidth: ['responsive'],
|
|
||||||
boxDecorationBreak: ['responsive'],
|
|
||||||
boxShadow: ['responsive', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
boxSizing: ['responsive'],
|
|
||||||
brightness: ['responsive'],
|
|
||||||
clear: ['responsive'],
|
|
||||||
container: ['responsive'],
|
|
||||||
contrast: ['responsive'],
|
|
||||||
cursor: ['responsive'],
|
|
||||||
display: ['responsive'],
|
|
||||||
divideColor: ['responsive', 'dark'],
|
|
||||||
divideOpacity: ['responsive', 'dark'],
|
|
||||||
divideStyle: ['responsive'],
|
|
||||||
divideWidth: ['responsive'],
|
|
||||||
dropShadow: ['responsive'],
|
|
||||||
fill: ['responsive'],
|
|
||||||
filter: ['responsive'],
|
|
||||||
flex: ['responsive'],
|
|
||||||
flexDirection: ['responsive'],
|
|
||||||
flexGrow: ['responsive'],
|
|
||||||
flexShrink: ['responsive'],
|
|
||||||
flexWrap: ['responsive'],
|
|
||||||
float: ['responsive'],
|
|
||||||
fontFamily: ['responsive'],
|
|
||||||
fontSize: ['responsive'],
|
|
||||||
fontSmoothing: ['responsive'],
|
|
||||||
fontStyle: ['responsive'],
|
|
||||||
fontVariantNumeric: ['responsive'],
|
|
||||||
fontWeight: ['responsive'],
|
|
||||||
gap: ['responsive'],
|
|
||||||
gradientColorStops: ['responsive', 'dark', 'hover', 'focus'],
|
|
||||||
grayscale: ['responsive'],
|
|
||||||
gridAutoColumns: ['responsive'],
|
|
||||||
gridAutoFlow: ['responsive'],
|
|
||||||
gridAutoRows: ['responsive'],
|
|
||||||
gridColumn: ['responsive'],
|
|
||||||
gridColumnEnd: ['responsive'],
|
|
||||||
gridColumnStart: ['responsive'],
|
|
||||||
gridRow: ['responsive'],
|
|
||||||
gridRowEnd: ['responsive'],
|
|
||||||
gridRowStart: ['responsive'],
|
|
||||||
gridTemplateColumns: ['responsive'],
|
|
||||||
gridTemplateRows: ['responsive'],
|
|
||||||
height: ['responsive'],
|
|
||||||
hueRotate: ['responsive'],
|
|
||||||
inset: ['responsive'],
|
|
||||||
invert: ['responsive'],
|
|
||||||
isolation: ['responsive'],
|
|
||||||
justifyContent: ['responsive'],
|
|
||||||
justifyItems: ['responsive'],
|
|
||||||
justifySelf: ['responsive'],
|
|
||||||
letterSpacing: ['responsive'],
|
|
||||||
lineHeight: ['responsive'],
|
|
||||||
listStylePosition: ['responsive'],
|
|
||||||
listStyleType: ['responsive'],
|
|
||||||
margin: ['responsive'],
|
|
||||||
maxHeight: ['responsive'],
|
|
||||||
maxWidth: ['responsive'],
|
|
||||||
minHeight: ['responsive'],
|
|
||||||
minWidth: ['responsive'],
|
|
||||||
mixBlendMode: ['responsive'],
|
|
||||||
objectFit: ['responsive'],
|
|
||||||
objectPosition: ['responsive'],
|
|
||||||
opacity: ['responsive', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
order: ['responsive'],
|
|
||||||
outline: ['responsive', 'focus-within', 'focus'],
|
|
||||||
overflow: ['responsive'],
|
|
||||||
overscrollBehavior: ['responsive'],
|
|
||||||
padding: ['responsive'],
|
|
||||||
placeContent: ['responsive'],
|
|
||||||
placeItems: ['responsive'],
|
|
||||||
placeSelf: ['responsive'],
|
|
||||||
placeholderColor: ['responsive', 'dark', 'focus'],
|
|
||||||
placeholderOpacity: ['responsive', 'dark', 'focus'],
|
|
||||||
pointerEvents: ['responsive'],
|
|
||||||
position: ['responsive'],
|
|
||||||
resize: ['responsive'],
|
|
||||||
ringColor: ['responsive', 'dark', 'focus-within', 'focus'],
|
|
||||||
ringOffsetColor: ['responsive', 'dark', 'focus-within', 'focus'],
|
|
||||||
ringOffsetWidth: ['responsive', 'focus-within', 'focus'],
|
|
||||||
ringOpacity: ['responsive', 'dark', 'focus-within', 'focus'],
|
|
||||||
ringWidth: ['responsive', 'focus-within', 'focus'],
|
|
||||||
rotate: ['responsive', 'hover', 'focus'],
|
|
||||||
saturate: ['responsive'],
|
|
||||||
scale: ['responsive', 'hover', 'focus'],
|
|
||||||
sepia: ['responsive'],
|
|
||||||
skew: ['responsive', 'hover', 'focus'],
|
|
||||||
space: ['responsive'],
|
|
||||||
stroke: ['responsive'],
|
|
||||||
strokeWidth: ['responsive'],
|
|
||||||
tableLayout: ['responsive'],
|
|
||||||
textAlign: ['responsive'],
|
|
||||||
textColor: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
textDecoration: ['responsive', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
textOpacity: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
|
|
||||||
textOverflow: ['responsive'],
|
|
||||||
textTransform: ['responsive'],
|
|
||||||
transform: ['responsive'],
|
|
||||||
transformOrigin: ['responsive'],
|
|
||||||
transitionDelay: ['responsive'],
|
|
||||||
transitionDuration: ['responsive'],
|
|
||||||
transitionProperty: ['responsive'],
|
|
||||||
transitionTimingFunction: ['responsive'],
|
|
||||||
translate: ['responsive', 'hover', 'focus'],
|
|
||||||
userSelect: ['responsive'],
|
|
||||||
verticalAlign: ['responsive'],
|
|
||||||
visibility: ['responsive'],
|
|
||||||
whitespace: ['responsive'],
|
|
||||||
width: ['responsive'],
|
|
||||||
wordBreak: ['responsive'],
|
|
||||||
zIndex: ['responsive', 'focus-within', 'focus'],
|
|
||||||
},
|
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: [],
|
content: [],
|
||||||
darkMode: false, // or 'media' or 'class'
|
darkMode: 'media', // or 'class'
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,336 +1,400 @@
|
|||||||
import _ from 'lodash'
|
import path from 'path'
|
||||||
import postcss from 'postcss'
|
import postcss from 'postcss'
|
||||||
import processPlugins from '../src/util/processPlugins'
|
|
||||||
import container from '../src/plugins/container'
|
|
||||||
|
|
||||||
function css(nodes) {
|
import tailwind from '../src'
|
||||||
return postcss.root({ nodes }).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
function config(overrides) {
|
function run(input, config = {}) {
|
||||||
return _.defaultsDeep(overrides, {
|
return postcss(tailwind(config)).process(input, {
|
||||||
theme: {
|
from: path.resolve(__filename),
|
||||||
screens: {
|
|
||||||
sm: '576px',
|
|
||||||
md: '768px',
|
|
||||||
lg: '992px',
|
|
||||||
xl: '1200px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
prefix: '',
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
test('options are not required', () => {
|
function css(templates) {
|
||||||
const { components } = processPlugins([container()], config())
|
return templates.join('')
|
||||||
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
function html(templates) {
|
||||||
@layer components {
|
return templates.join('')
|
||||||
@variants {
|
}
|
||||||
.container { width: 100% }
|
|
||||||
@media (min-width: 576px) {
|
test('options are not required', () => {
|
||||||
.container { max-width: 576px }
|
let config = {
|
||||||
}
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
@media (min-width: 768px) {
|
}
|
||||||
.container { max-width: 768px }
|
|
||||||
}
|
let content = css`
|
||||||
@media (min-width: 992px) {
|
@tailwind components;
|
||||||
.container { max-width: 992px }
|
`
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
return run(content, config).then((result) => {
|
||||||
.container { max-width: 1200px }
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.container {
|
||||||
|
max-width: 640px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 768px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1024px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1280px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1536px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1536px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('screens can be passed explicitly', () => {
|
test('screens can be passed explicitly', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
container: {
|
||||||
container: {
|
screens: ['400px', '500px'],
|
||||||
screens: ['400px', '500px'],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants {
|
`
|
||||||
.container { width: 100% }
|
|
||||||
@media (min-width: 400px) {
|
return run(content, config).then((result) => {
|
||||||
.container { max-width: 400px }
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
}
|
.container {
|
||||||
@media (min-width: 500px) {
|
width: 100%;
|
||||||
.container { max-width: 500px }
|
}
|
||||||
|
@media (min-width: 400px) {
|
||||||
|
.container {
|
||||||
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 500px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('screens are ordered ascending by min-width', () => {
|
test('screens are ordered ascending by min-width', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
container: {
|
||||||
container: {
|
screens: ['500px', '400px'],
|
||||||
screens: ['500px', '400px'],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants {
|
`
|
||||||
.container { width: 100% }
|
|
||||||
@media (min-width: 400px) {
|
return run(content, config).then((result) => {
|
||||||
.container { max-width: 400px }
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
}
|
.container {
|
||||||
@media (min-width: 500px) {
|
width: 100%;
|
||||||
.container { max-width: 500px }
|
}
|
||||||
|
@media (min-width: 400px) {
|
||||||
|
.container {
|
||||||
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 500px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('screens are deduplicated by min-width', () => {
|
test('screens are deduplicated by min-width', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
container: {
|
||||||
container: {
|
screens: {
|
||||||
screens: {
|
sm: '576px',
|
||||||
sm: '576px',
|
md: '768px',
|
||||||
md: '768px',
|
'sm-only': { min: '576px', max: '767px' },
|
||||||
'sm-only': { min: '576px', max: '767px' },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants {
|
`
|
||||||
.container { width: 100% }
|
|
||||||
@media (min-width: 576px) {
|
return run(content, config).then((result) => {
|
||||||
.container { max-width: 576px }
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
}
|
.container {
|
||||||
@media (min-width: 768px) {
|
width: 100%;
|
||||||
.container { max-width: 768px }
|
}
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
.container {
|
||||||
|
max-width: 576px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 768px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('the container can be centered by default', () => {
|
test('the container can be centered by default', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
container: {
|
||||||
container: {
|
center: true,
|
||||||
center: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants {
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
@media (min-width: 640px) {
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
max-width: 640px;
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto
|
|
||||||
}
|
|
||||||
@media (min-width: 576px) {
|
|
||||||
.container { max-width: 576px }
|
|
||||||
}
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.container { max-width: 768px }
|
|
||||||
}
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
.container { max-width: 992px }
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
.container { max-width: 1200px }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 768px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1024px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1280px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1536px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1536px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('horizontal padding can be included by default', () => {
|
test('horizontal padding can be included by default', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
container: {
|
||||||
container: {
|
padding: '2rem',
|
||||||
padding: '2rem',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants {
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 2rem;
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
@media (min-width: 640px) {
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
max-width: 640px;
|
||||||
padding-right: 2rem;
|
|
||||||
padding-left: 2rem
|
|
||||||
}
|
|
||||||
@media (min-width: 576px) {
|
|
||||||
.container { max-width: 576px }
|
|
||||||
}
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.container { max-width: 768px }
|
|
||||||
}
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
.container { max-width: 992px }
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
.container { max-width: 1200px }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 768px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1024px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1280px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1536px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1536px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('responsive horizontal padding can be included by default', () => {
|
test('responsive horizontal padding can be included by default', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
screens: {
|
||||||
screens: {
|
sm: '576px',
|
||||||
sm: '576px',
|
md: { min: '768px' },
|
||||||
md: { min: '768px' },
|
lg: { 'min-width': '992px' },
|
||||||
lg: { 'min-width': '992px' },
|
xl: { min: '1200px', max: '1600px' },
|
||||||
xl: { min: '1200px', max: '1600px' },
|
},
|
||||||
},
|
container: {
|
||||||
container: {
|
padding: {
|
||||||
padding: {
|
DEFAULT: '1rem',
|
||||||
DEFAULT: '1rem',
|
sm: '2rem',
|
||||||
sm: '2rem',
|
lg: '4rem',
|
||||||
lg: '4rem',
|
xl: '5rem',
|
||||||
xl: '5rem',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants {
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 1rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
@media (min-width: 576px) {
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
max-width: 576px;
|
||||||
padding-right: 1rem;
|
padding-right: 2rem;
|
||||||
padding-left: 1rem
|
padding-left: 2rem;
|
||||||
}
|
|
||||||
@media (min-width: 576px) {
|
|
||||||
.container {
|
|
||||||
max-width: 576px;
|
|
||||||
padding-right: 2rem;
|
|
||||||
padding-left: 2rem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.container { max-width: 768px }
|
|
||||||
}
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
.container {
|
|
||||||
max-width: 992px;
|
|
||||||
padding-right: 4rem;
|
|
||||||
padding-left: 4rem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
.container {
|
|
||||||
max-width: 1200px;
|
|
||||||
padding-right: 5rem;
|
|
||||||
padding-left: 5rem
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 768px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.container {
|
||||||
|
max-width: 992px;
|
||||||
|
padding-right: 4rem;
|
||||||
|
padding-left: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
padding-right: 5rem;
|
||||||
|
padding-left: 5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('setting all options at once', () => {
|
test('setting all options at once', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
container: {
|
||||||
container: {
|
screens: ['400px', '500px'],
|
||||||
screens: ['400px', '500px'],
|
center: true,
|
||||||
center: true,
|
padding: '2rem',
|
||||||
padding: '2rem',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants {
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
padding-right: 2rem;
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
@media (min-width: 400px) {
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
max-width: 400px;
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
padding-right: 2rem;
|
|
||||||
padding-left: 2rem
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.container { max-width: 400px }
|
|
||||||
}
|
|
||||||
@media (min-width: 500px) {
|
|
||||||
.container { max-width: 500px }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@media (min-width: 500px) {
|
||||||
`)
|
.container {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('container can use variants', () => {
|
test('container can use variants', () => {
|
||||||
const { components } = processPlugins(
|
let config = {
|
||||||
[container()],
|
content: [{ raw: html`<div class="lg:hover:container"></div>` }],
|
||||||
config({
|
theme: {
|
||||||
theme: {
|
container: {
|
||||||
container: {
|
screens: ['400px', '500px'],
|
||||||
screens: ['400px', '500px'],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
variants: {
|
},
|
||||||
container: ['responsive', 'hover'],
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(css(components)).toMatchCss(`
|
let content = css`
|
||||||
@layer components {
|
@tailwind components;
|
||||||
@variants responsive, hover {
|
`
|
||||||
.container {
|
|
||||||
width: 100%
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.lg\\:hover\\:container:hover {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
@media (min-width: 400px) {
|
@media (min-width: 400px) {
|
||||||
.container {
|
.lg\\:hover\\:container:hover {
|
||||||
max-width: 400px
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (min-width: 500px) {
|
@media (min-width: 500px) {
|
||||||
.container {
|
.lg\\:hover\\:container:hover {
|
||||||
max-width: 500px
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
`)
|
||||||
`)
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -5,139 +5,124 @@ import tailwind from '../src/index'
|
|||||||
import { cjsConfigFile, defaultConfigFile } from '../src/constants'
|
import { cjsConfigFile, defaultConfigFile } from '../src/constants'
|
||||||
import inTempDirectory from '../jest/runInTempDirectory'
|
import inTempDirectory from '../jest/runInTempDirectory'
|
||||||
|
|
||||||
|
// NOTE: If we ever want to abstract this logic, then we have to watch out
|
||||||
|
// because in most tests we default to an empty object here. However, in this
|
||||||
|
// tests we do want to check the difference between no config (undefined) and a
|
||||||
|
// config (empty object or full object).
|
||||||
|
function run(input, config /* Undefined is important in this case */) {
|
||||||
|
return postcss(tailwind(config)).process(input, {
|
||||||
|
from: path.resolve(__filename),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function css(templates) {
|
||||||
|
return templates.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function html(templates) {
|
||||||
|
return templates.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function javascript(templates) {
|
||||||
|
return templates.join('')
|
||||||
|
}
|
||||||
|
|
||||||
test('it uses the values from the custom config file', () => {
|
test('it uses the values from the custom config file', () => {
|
||||||
return postcss([tailwind(path.resolve(`${__dirname}/fixtures/custom-config.js`))])
|
let config = require(path.resolve(`${__dirname}/fixtures/custom-config.js`))
|
||||||
.process(
|
|
||||||
`
|
let content = css`
|
||||||
@responsive {
|
@tailwind utilities;
|
||||||
.foo {
|
`
|
||||||
color: blue;
|
|
||||||
}
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
@media (min-width: 400px) {
|
||||||
|
.mobile\\:font-bold {
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
`,
|
}
|
||||||
{ from: undefined }
|
`)
|
||||||
)
|
})
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('custom config can be passed as an object', () => {
|
test('custom config can be passed as an object', () => {
|
||||||
return postcss([
|
let config = {
|
||||||
tailwind({
|
content: [{ raw: html`<div class="mobile:font-bold"></div>` }],
|
||||||
|
theme: {
|
||||||
|
screens: {
|
||||||
|
mobile: '400px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = css`
|
||||||
|
@tailwind utilities;
|
||||||
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
@media (min-width: 400px) {
|
||||||
|
.mobile\\:font-bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('custom config path can be passed using `config` property in an object', () => {
|
||||||
|
let config = {
|
||||||
|
config: path.resolve(`${__dirname}/fixtures/custom-config.js`),
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = css`
|
||||||
|
@tailwind utilities;
|
||||||
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
@media (min-width: 400px) {
|
||||||
|
.mobile\\:font-bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('custom config can be passed under the `config` property', () => {
|
||||||
|
let config = {
|
||||||
|
config: {
|
||||||
|
content: [{ raw: html`<div class="mobile:font-bold"></div>` }],
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
mobile: '400px',
|
mobile: '400px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
])
|
}
|
||||||
.process(
|
|
||||||
`
|
|
||||||
@responsive {
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{ from: undefined }
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect(result.css).toMatchCss(expected)
|
let content = css`
|
||||||
})
|
@tailwind utilities;
|
||||||
})
|
`
|
||||||
|
|
||||||
test('custom config path can be passed using `config` property in an object', () => {
|
return run(content, config).then((result) => {
|
||||||
return postcss([tailwind({ config: path.resolve(`${__dirname}/fixtures/custom-config.js`) })])
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
.process(
|
@media (min-width: 400px) {
|
||||||
`
|
.mobile\\:font-bold {
|
||||||
@responsive {
|
font-weight: 700;
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`,
|
}
|
||||||
{ from: undefined }
|
`)
|
||||||
)
|
})
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('custom config can be passed under the `config` property', () => {
|
|
||||||
return postcss([
|
|
||||||
tailwind({
|
|
||||||
config: {
|
|
||||||
theme: {
|
|
||||||
screens: {
|
|
||||||
mobile: '400px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
.process(
|
|
||||||
`
|
|
||||||
@responsive {
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{ from: undefined }
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('tailwind.config.cjs is picked up by default', () => {
|
test('tailwind.config.cjs is picked up by default', () => {
|
||||||
return inTempDirectory(() => {
|
return inTempDirectory(() => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.resolve(cjsConfigFile),
|
path.resolve(cjsConfigFile),
|
||||||
`module.exports = {
|
javascript`module.exports = {
|
||||||
|
content: [{ raw: '<div class="mobile:font-bold"></div>' }],
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
mobile: '400px',
|
mobile: '400px',
|
||||||
@ -146,29 +131,19 @@ test('tailwind.config.cjs is picked up by default', () => {
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
return postcss([tailwind])
|
let content = css`
|
||||||
.process(
|
@tailwind utilities;
|
||||||
`
|
`
|
||||||
@responsive {
|
|
||||||
.foo {
|
return run(content).then((result) => {
|
||||||
color: blue;
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
}
|
@media (min-width: 400px) {
|
||||||
|
.mobile\\:font-bold {
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
`,
|
}
|
||||||
{ from: undefined }
|
`)
|
||||||
)
|
})
|
||||||
.then((result) => {
|
|
||||||
expect(result.css).toMatchCss(`
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -176,7 +151,8 @@ test('tailwind.config.js is picked up by default', () => {
|
|||||||
return inTempDirectory(() => {
|
return inTempDirectory(() => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.resolve(defaultConfigFile),
|
path.resolve(defaultConfigFile),
|
||||||
`module.exports = {
|
javascript`module.exports = {
|
||||||
|
content: [{ raw: '<div class="mobile:font-bold"></div>' }],
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
mobile: '400px',
|
mobile: '400px',
|
||||||
@ -185,29 +161,19 @@ test('tailwind.config.js is picked up by default', () => {
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
return postcss([tailwind])
|
let content = css`
|
||||||
.process(
|
@tailwind utilities;
|
||||||
`
|
`
|
||||||
@responsive {
|
|
||||||
.foo {
|
return run(content).then((result) => {
|
||||||
color: blue;
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
}
|
@media (min-width: 400px) {
|
||||||
|
.mobile\\:font-bold {
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
`,
|
}
|
||||||
{ from: undefined }
|
`)
|
||||||
)
|
})
|
||||||
.then((result) => {
|
|
||||||
expect(result.css).toMatchCss(`
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -215,7 +181,8 @@ test('tailwind.config.cjs is picked up by default when passing an empty object',
|
|||||||
return inTempDirectory(() => {
|
return inTempDirectory(() => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.resolve(cjsConfigFile),
|
path.resolve(cjsConfigFile),
|
||||||
`module.exports = {
|
javascript`module.exports = {
|
||||||
|
content: [{ raw: '<div class="mobile:font-bold"></div>' }],
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
mobile: '400px',
|
mobile: '400px',
|
||||||
@ -224,29 +191,19 @@ test('tailwind.config.cjs is picked up by default when passing an empty object',
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
return postcss([tailwind({})])
|
let content = css`
|
||||||
.process(
|
@tailwind utilities;
|
||||||
`
|
`
|
||||||
@responsive {
|
|
||||||
.foo {
|
return run(content, {}).then((result) => {
|
||||||
color: blue;
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
}
|
@media (min-width: 400px) {
|
||||||
|
.mobile\\:font-bold {
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
`,
|
}
|
||||||
{ from: undefined }
|
`)
|
||||||
)
|
})
|
||||||
.then((result) => {
|
|
||||||
expect(result.css).toMatchCss(`
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -254,7 +211,8 @@ test('tailwind.config.js is picked up by default when passing an empty object',
|
|||||||
return inTempDirectory(() => {
|
return inTempDirectory(() => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.resolve(defaultConfigFile),
|
path.resolve(defaultConfigFile),
|
||||||
`module.exports = {
|
javascript`module.exports = {
|
||||||
|
content: [{ raw: '<div class="mobile:font-bold"></div>' }],
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
mobile: '400px',
|
mobile: '400px',
|
||||||
@ -263,300 +221,240 @@ test('tailwind.config.js is picked up by default when passing an empty object',
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
return postcss([tailwind({})])
|
let content = css`
|
||||||
.process(
|
@tailwind utilities;
|
||||||
`
|
`
|
||||||
@responsive {
|
|
||||||
.foo {
|
return run(content, {}).then((result) => {
|
||||||
color: blue;
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
}
|
@media (min-width: 400px) {
|
||||||
|
.mobile\\:font-bold {
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
`,
|
}
|
||||||
{ from: undefined }
|
`)
|
||||||
)
|
})
|
||||||
.then((result) => {
|
|
||||||
expect(result.css).toMatchCss(`
|
|
||||||
.foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.mobile\\:foo {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('the default config can be overridden using the presets key', () => {
|
test('the default config can be overridden using the presets key', () => {
|
||||||
return postcss([
|
let config = {
|
||||||
tailwind({
|
content: [{ raw: html`<div class="min-h-0 min-h-primary min-h-secondary"></div>` }],
|
||||||
presets: [
|
presets: [
|
||||||
{
|
{
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: { minHeight: { secondary: '24px' } },
|
||||||
minHeight: {
|
|
||||||
24: '24px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
corePlugins: ['minHeight'],
|
|
||||||
variants: { minHeight: [] },
|
|
||||||
},
|
},
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: { minHeight: { 48: '48px' } },
|
|
||||||
},
|
},
|
||||||
}),
|
],
|
||||||
])
|
theme: {
|
||||||
.process(
|
extend: { minHeight: { primary: '48px' } },
|
||||||
`
|
},
|
||||||
@tailwind utilities
|
}
|
||||||
`,
|
|
||||||
{ from: undefined }
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.min-h-0 {
|
|
||||||
min-height: 0px;
|
|
||||||
}
|
|
||||||
.min-h-24 {
|
|
||||||
min-height: 24px;
|
|
||||||
}
|
|
||||||
.min-h-48 {
|
|
||||||
min-height: 48px;
|
|
||||||
}
|
|
||||||
.min-h-full {
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
.min-h-screen {
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect(result.css).toMatchCss(expected)
|
let content = css`
|
||||||
})
|
@tailwind utilities;
|
||||||
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.min-h-0 {
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
.min-h-primary {
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
.min-h-secondary {
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('presets can be functions', () => {
|
test('presets can be functions', () => {
|
||||||
return postcss([
|
let config = {
|
||||||
tailwind({
|
content: [{ raw: html`<div class="min-h-0 min-h-primary min-h-secondary"></div>` }],
|
||||||
presets: [
|
presets: [
|
||||||
() => ({
|
() => ({
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: { minHeight: { secondary: '24px' } },
|
||||||
minHeight: {
|
},
|
||||||
24: '24px',
|
}),
|
||||||
},
|
],
|
||||||
},
|
theme: {
|
||||||
},
|
extend: { minHeight: { primary: '48px' } },
|
||||||
corePlugins: ['minHeight'],
|
},
|
||||||
variants: { minHeight: [] },
|
}
|
||||||
}),
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: { minHeight: { 48: '48px' } },
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
.process(
|
|
||||||
`
|
|
||||||
@tailwind utilities
|
|
||||||
`,
|
|
||||||
{ from: undefined }
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.min-h-0 {
|
|
||||||
min-height: 0px;
|
|
||||||
}
|
|
||||||
.min-h-24 {
|
|
||||||
min-height: 24px;
|
|
||||||
}
|
|
||||||
.min-h-48 {
|
|
||||||
min-height: 48px;
|
|
||||||
}
|
|
||||||
.min-h-full {
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
.min-h-screen {
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect(result.css).toMatchCss(expected)
|
let content = css`
|
||||||
})
|
@tailwind utilities;
|
||||||
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.min-h-0 {
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
.min-h-primary {
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
.min-h-secondary {
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('the default config can be removed by using an empty presets key in a preset', () => {
|
test('the default config can be removed by using an empty presets key in a preset', () => {
|
||||||
return postcss([
|
let config = {
|
||||||
tailwind({
|
content: [{ raw: html`<div class="min-h-0 min-h-primary min-h-secondary"></div>` }],
|
||||||
presets: [
|
presets: [
|
||||||
{
|
{
|
||||||
presets: [],
|
presets: [],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: { minHeight: { secondary: '24px' } },
|
||||||
minHeight: {
|
|
||||||
24: '24px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
corePlugins: ['minHeight'],
|
|
||||||
variants: { minHeight: [] },
|
|
||||||
},
|
},
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: { minHeight: { 48: '48px' } },
|
|
||||||
},
|
},
|
||||||
}),
|
],
|
||||||
])
|
theme: {
|
||||||
.process(
|
extend: { minHeight: { primary: '48px' } },
|
||||||
`
|
},
|
||||||
@tailwind utilities
|
}
|
||||||
`,
|
|
||||||
{ from: undefined }
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.min-h-24 {
|
|
||||||
min-height: 24px;
|
|
||||||
}
|
|
||||||
.min-h-48 {
|
|
||||||
min-height: 48px;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect(result.css).toMatchCss(expected)
|
let content = css`
|
||||||
})
|
@tailwind utilities;
|
||||||
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.min-h-primary {
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
.min-h-secondary {
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('presets can have their own presets', () => {
|
test('presets can have their own presets', () => {
|
||||||
return postcss([
|
let config = {
|
||||||
tailwind({
|
content: [{ raw: html`<div class="bg-transparent bg-black bg-white bg-red"></div>` }],
|
||||||
presets: [
|
presets: [
|
||||||
{
|
{
|
||||||
presets: [],
|
presets: [],
|
||||||
theme: {
|
theme: {
|
||||||
colors: { red: '#dd0000' },
|
colors: { red: '#dd0000' },
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
presets: [
|
|
||||||
{
|
|
||||||
presets: [],
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
transparent: 'transparent',
|
|
||||||
red: '#ff0000',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
black: 'black',
|
|
||||||
red: '#ee0000',
|
|
||||||
},
|
|
||||||
backgroundColor: (theme) => theme('colors'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
corePlugins: ['backgroundColor'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: { colors: { white: 'white' } },
|
|
||||||
},
|
},
|
||||||
}),
|
{
|
||||||
])
|
presets: [
|
||||||
.process(
|
{
|
||||||
`
|
presets: [],
|
||||||
@tailwind utilities
|
theme: {
|
||||||
`,
|
colors: {
|
||||||
{ from: undefined }
|
transparent: 'transparent',
|
||||||
)
|
red: '#ff0000',
|
||||||
.then((result) => {
|
},
|
||||||
const expected = `
|
},
|
||||||
.bg-transparent {
|
},
|
||||||
background-color: transparent;
|
],
|
||||||
}
|
theme: {
|
||||||
.bg-red {
|
extend: {
|
||||||
background-color: #ee0000;
|
colors: {
|
||||||
}
|
black: 'black',
|
||||||
.bg-black {
|
red: '#ee0000',
|
||||||
background-color: black;
|
},
|
||||||
}
|
backgroundColor: (theme) => theme('colors'),
|
||||||
.bg-white {
|
},
|
||||||
background-color: white;
|
},
|
||||||
}
|
corePlugins: ['backgroundColor'],
|
||||||
`
|
},
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: { colors: { white: 'white' } },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(result.css).toMatchCss(expected)
|
let content = css`
|
||||||
})
|
@tailwind utilities;
|
||||||
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.bg-transparent {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.bg-black {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.bg-white {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.bg-red {
|
||||||
|
background-color: #ee0000;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('function presets can be mixed with object presets', () => {
|
test('function presets can be mixed with object presets', () => {
|
||||||
return postcss([
|
let config = {
|
||||||
tailwind({
|
content: [{ raw: html`<div class="bg-transparent bg-black bg-white bg-red"></div>` }],
|
||||||
presets: [
|
presets: [
|
||||||
() => ({
|
() => ({
|
||||||
presets: [],
|
presets: [],
|
||||||
theme: {
|
theme: {
|
||||||
colors: { red: '#dd0000' },
|
colors: { red: '#dd0000' },
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
presets: [
|
|
||||||
() => ({
|
|
||||||
presets: [],
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
transparent: 'transparent',
|
|
||||||
red: '#ff0000',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
black: 'black',
|
|
||||||
red: '#ee0000',
|
|
||||||
},
|
|
||||||
backgroundColor: (theme) => theme('colors'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
corePlugins: ['backgroundColor'],
|
|
||||||
},
|
},
|
||||||
],
|
}),
|
||||||
theme: {
|
{
|
||||||
extend: { colors: { white: 'white' } },
|
presets: [
|
||||||
|
() => ({
|
||||||
|
presets: [],
|
||||||
|
theme: {
|
||||||
|
colors: {
|
||||||
|
transparent: 'transparent',
|
||||||
|
red: '#ff0000',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
black: 'black',
|
||||||
|
red: '#ee0000',
|
||||||
|
},
|
||||||
|
backgroundColor: (theme) => theme('colors'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
corePlugins: ['backgroundColor'],
|
||||||
},
|
},
|
||||||
}),
|
],
|
||||||
])
|
theme: {
|
||||||
.process(
|
extend: { colors: { white: 'white' } },
|
||||||
`
|
},
|
||||||
@tailwind utilities
|
}
|
||||||
`,
|
|
||||||
{ from: undefined }
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
const expected = `
|
|
||||||
.bg-transparent {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
.bg-red {
|
|
||||||
background-color: #ee0000;
|
|
||||||
}
|
|
||||||
.bg-black {
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
.bg-white {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect(result.css).toMatchCss(expected)
|
let content = css`
|
||||||
})
|
@tailwind utilities;
|
||||||
|
`
|
||||||
|
|
||||||
|
return run(content, config).then((result) => {
|
||||||
|
expect(result.css).toMatchFormattedCss(css`
|
||||||
|
.bg-transparent {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.bg-black {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.bg-white {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.bg-red {
|
||||||
|
background-color: #ee0000;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,358 +0,0 @@
|
|||||||
import postcss from 'postcss'
|
|
||||||
import tailwind from '../src/index'
|
|
||||||
import createPlugin from '../src/util/createPlugin'
|
|
||||||
|
|
||||||
function run(input, config = {}) {
|
|
||||||
return postcss([tailwind(config)]).process(input, { from: undefined })
|
|
||||||
}
|
|
||||||
|
|
||||||
test('user-defined dark mode variants do not stack', () => {
|
|
||||||
const input = `
|
|
||||||
@variants dark, hover {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.custom-dark .custom-dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const userPlugin = createPlugin(function ({ addVariant }) {
|
|
||||||
addVariant('dark', function ({ modifySelectors }) {
|
|
||||||
modifySelectors(function ({ className }) {
|
|
||||||
return `.custom-dark .custom-dark\\:${className}`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return postcss([tailwind({ plugins: [userPlugin] })])
|
|
||||||
.process(input, { from: undefined })
|
|
||||||
.then((result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('dark mode variants can be generated using the class strategy', () => {
|
|
||||||
const input = `
|
|
||||||
@variants dark {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return run(input, { darkMode: 'media' }).then((result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('dark mode variants can be generated even when the user has their own plugins array', () => {
|
|
||||||
const input = `
|
|
||||||
@variants dark {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return run(input, { darkMode: 'media', plugins: [] }).then((result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('dark mode variants can be generated using the class strategy', () => {
|
|
||||||
const input = `
|
|
||||||
@variants dark {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return run(input, { darkMode: 'class' }).then((result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('dark mode variants can be disabled', () => {
|
|
||||||
const input = `
|
|
||||||
@variants dark {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return run(input, { dark: false }).then((result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('dark mode variants are disabled by default', () => {
|
|
||||||
const input = `
|
|
||||||
@variants dark {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return run(input).then((result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('dark mode variants stack with other variants', () => {
|
|
||||||
const input = `
|
|
||||||
@variants responsive, dark, hover, focus {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 500px) {
|
|
||||||
.sm\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.sm\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.sm\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.sm\\:dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.sm\\:dark\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.sm\\:dark\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 800px) {
|
|
||||||
.lg\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.lg\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.lg\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.lg\\:dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.lg\\:dark\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.lg\\:dark\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return run(input, { darkMode: 'media', theme: { screens: { sm: '500px', lg: '800px' } } }).then(
|
|
||||||
(result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('dark mode variants stack with other variants when using the class strategy', () => {
|
|
||||||
const input = `
|
|
||||||
@variants responsive, dark, group-hover, hover, focus {
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const expected = `
|
|
||||||
.text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.group:hover .group-hover\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .group:hover .dark\\:group-hover\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .dark\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .dark\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
@media (min-width: 500px) {
|
|
||||||
.sm\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.group:hover .sm\\:group-hover\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.sm\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.sm\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .sm\\:dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .group:hover .sm\\:dark\\:group-hover\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .sm\\:dark\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .sm\\:dark\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 800px) {
|
|
||||||
.lg\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.group:hover .lg\\:group-hover\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.lg\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.lg\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .lg\\:dark\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .group:hover .lg\\:dark\\:group-hover\\:text-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .lg\\:dark\\:hover\\:text-red:hover {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.dark .lg\\:dark\\:focus\\:text-red:focus {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
expect.assertions(2)
|
|
||||||
|
|
||||||
return run(input, { darkMode: 'class', theme: { screens: { sm: '500px', lg: '800px' } } }).then(
|
|
||||||
(result) => {
|
|
||||||
expect(result.css).toMatchCss(expected)
|
|
||||||
expect(result.warnings().length).toBe(0)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
1
tests/fixtures/custom-config.js
vendored
1
tests/fixtures/custom-config.js
vendored
@ -1,4 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
content: [{ raw: '<div class="mobile:font-bold"></div>' }],
|
||||||
theme: {
|
theme: {
|
||||||
screens: {
|
screens: {
|
||||||
mobile: '400px',
|
mobile: '400px',
|
||||||
|
|||||||
2
tests/fixtures/custom-purge-config.js
vendored
2
tests/fixtures/custom-purge-config.js
vendored
@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: ['./tests/fixtures/*.html'],
|
content: ['./tests/fixtures/*.html'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user