From 477dd06ec364a4b7976444661259670b94bcaf2f Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Fri, 7 May 2021 18:46:19 +0100 Subject: [PATCH] Resolve purge paths (#4214) * resolve purge paths * add test for purgecss pattern resolution * resolve purgecss patterns relative to the config file if there is one * account for raw content when transforming purgecss options * append test name to postcss `from` option in purge tests fixes tests hanging * add test for relative purge path resolution in JIT mode --- src/index.js | 2 +- src/jit/lib/setupContext.js | 9 +- src/lib/purgeUnusedStyles.js | 16 +- src/processTailwindFeatures.js | 4 +- tests/fixtures/custom-purge-config.js | 20 + tests/jit/relative-purge-paths.config.js | 7 + tests/jit/relative-purge-paths.test.css | 757 +++++++++++++++++++++++ tests/jit/relative-purge-paths.test.html | 147 +++++ tests/jit/relative-purge-paths.test.js | 25 + tests/purgeUnusedStyles.test.js | 70 ++- 10 files changed, 1018 insertions(+), 39 deletions(-) create mode 100644 tests/fixtures/custom-purge-config.js create mode 100644 tests/jit/relative-purge-paths.config.js create mode 100644 tests/jit/relative-purge-paths.test.css create mode 100644 tests/jit/relative-purge-paths.test.html create mode 100644 tests/jit/relative-purge-paths.test.js diff --git a/src/index.js b/src/index.js index 00102e3b0..df34da4d6 100644 --- a/src/index.js +++ b/src/index.js @@ -97,7 +97,7 @@ module.exports = function (config) { return { postcssPlugin: 'tailwindcss', - plugins: [...plugins, processTailwindFeatures(getConfig), formatCSS], + plugins: [...plugins, processTailwindFeatures(getConfig, resolvedConfigPath), formatCSS], } } diff --git a/src/jit/lib/setupContext.js b/src/jit/lib/setupContext.js index d664f6674..ec7036838 100644 --- a/src/jit/lib/setupContext.js +++ b/src/jit/lib/setupContext.js @@ -787,7 +787,14 @@ export default function setupContext(configOrPath) { configDependencies: new Set(), candidateFiles: purgeContent .filter((item) => typeof item === 'string') - .map((path) => normalizePath(path)), + .map((purgePath) => + normalizePath( + path.resolve( + userConfigPath === null ? process.cwd() : path.dirname(userConfigPath), + purgePath + ) + ) + ), rawContent: purgeContent .filter((item) => typeof item.raw === 'string') .map(({ raw, extension }) => ({ content: raw, extension })), diff --git a/src/lib/purgeUnusedStyles.js b/src/lib/purgeUnusedStyles.js index e75da457d..65cd3c99e 100644 --- a/src/lib/purgeUnusedStyles.js +++ b/src/lib/purgeUnusedStyles.js @@ -3,6 +3,7 @@ import postcss from 'postcss' import purgecss from '@fullhuman/postcss-purgecss' import log from '../util/log' import htmlTags from 'html-tags' +import path from 'path' function removeTailwindMarkers(css) { css.walkAtRules('tailwind', (rule) => rule.remove()) @@ -33,7 +34,7 @@ export function tailwindExtractor(content) { return broadMatches.concat(broadMatchesWithoutTrailingSlash).concat(innerMatches) } -export default function purgeUnusedUtilities(config, configChanged) { +export default function purgeUnusedUtilities(config, configChanged, resolvedConfigPath) { const purgeEnabled = _.get( config, 'purge.enabled', @@ -104,7 +105,6 @@ export default function purgeUnusedUtilities(config, configChanged) { }, removeTailwindMarkers, purgecss({ - content: Array.isArray(config.purge) ? config.purge : config.purge.content, defaultExtractor: (content) => { const extractor = defaultExtractor || tailwindExtractor const preserved = [...extractor(content)] @@ -116,6 +116,18 @@ export default function purgeUnusedUtilities(config, configChanged) { return preserved }, ...purgeOptions, + content: (Array.isArray(config.purge) + ? config.purge + : config.purge.content || purgeOptions.content || [] + ).map((item) => { + if (typeof item === 'string') { + return path.resolve( + resolvedConfigPath ? path.dirname(resolvedConfigPath) : process.cwd(), + item + ) + } + return item + }), }), ]) } diff --git a/src/processTailwindFeatures.js b/src/processTailwindFeatures.js index 89402476c..d3bc8898e 100644 --- a/src/processTailwindFeatures.js +++ b/src/processTailwindFeatures.js @@ -24,7 +24,7 @@ let previousConfig = null let processedPlugins = null let getProcessedPlugins = null -export default function (getConfig) { +export default function (getConfig, resolvedConfigPath) { return function (css) { const config = getConfig() const configChanged = hash(previousConfig) !== hash(config) @@ -65,7 +65,7 @@ export default function (getConfig) { substituteScreenAtRules(config), substituteClassApplyAtRules(config, getProcessedPlugins, configChanged), applyImportantConfiguration(config), - purgeUnusedStyles(config, configChanged), + purgeUnusedStyles(config, configChanged, resolvedConfigPath), ]).process(css, { from: _.get(css, 'source.input.file') }) } } diff --git a/tests/fixtures/custom-purge-config.js b/tests/fixtures/custom-purge-config.js new file mode 100644 index 000000000..7bc873b42 --- /dev/null +++ b/tests/fixtures/custom-purge-config.js @@ -0,0 +1,20 @@ +module.exports = { + purge: ['./*.html'], + theme: { + extend: { + colors: { + 'black!': '#000', + }, + spacing: { + 1.5: '0.375rem', + '(1/2+8)': 'calc(50% + 2rem)', + }, + minHeight: { + '(screen-4)': 'calc(100vh - 1rem)', + }, + fontFamily: { + '%#$@': 'Comic Sans', + }, + }, + }, +} diff --git a/tests/jit/relative-purge-paths.config.js b/tests/jit/relative-purge-paths.config.js new file mode 100644 index 000000000..c864b2dcd --- /dev/null +++ b/tests/jit/relative-purge-paths.config.js @@ -0,0 +1,7 @@ +module.exports = { + mode: 'jit', + purge: ['./relative-purge-paths.test.html'], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], +} diff --git a/tests/jit/relative-purge-paths.test.css b/tests/jit/relative-purge-paths.test.css new file mode 100644 index 000000000..aec70a93c --- /dev/null +++ b/tests/jit/relative-purge-paths.test.css @@ -0,0 +1,757 @@ +* { + --tw-shadow: 0 0 #0000; + --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; +} +.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; + } +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} +.pointer-events-none { + pointer-events: none; +} +.invisible { + visibility: hidden; +} +.absolute { + position: absolute; +} +.inset-0 { + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} +.inset-y-4 { + top: 1rem; + bottom: 1rem; +} +.inset-x-2 { + left: 0.5rem; + right: 0.5rem; +} +.top-6 { + top: 1.5rem; +} +.right-8 { + right: 2rem; +} +.bottom-12 { + bottom: 3rem; +} +.left-16 { + left: 4rem; +} +.isolate { + isolation: isolate; +} +.isolation-auto { + isolation: auto; +} +.z-30 { + z-index: 30; +} +.order-last { + order: 9999; +} +.order-2 { + order: 2; +} +.col-span-3 { + grid-column: span 3 / span 3; +} +.col-start-1 { + grid-column-start: 1; +} +.col-end-4 { + grid-column-end: 4; +} +.row-span-2 { + grid-row: span 2 / span 2; +} +.row-start-3 { + grid-row-start: 3; +} +.row-end-5 { + grid-row-end: 5; +} +.float-right { + float: right; +} +.clear-left { + clear: left; +} +.m-4 { + margin: 1rem; +} +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} +.mx-auto { + margin-left: auto; + margin-right: auto; +} +.mt-0 { + margin-top: 0px; +} +.mr-1 { + margin-right: 0.25rem; +} +.mb-3 { + margin-bottom: 0.75rem; +} +.ml-4 { + margin-left: 1rem; +} +.box-border { + box-sizing: border-box; +} +.inline-grid { + display: inline-grid; +} +.hidden { + display: none; +} +.h-16 { + height: 4rem; +} +.max-h-screen { + max-height: 100vh; +} +.min-h-0 { + min-height: 0px; +} +.w-12 { + width: 3rem; +} +.min-w-min { + min-width: min-content; +} +.max-w-full { + max-width: 100%; +} +.flex-1 { + flex: 1 1 0%; +} +.flex-shrink { + flex-shrink: 1; +} +.flex-shrink-0 { + flex-shrink: 0; +} +.flex-grow { + flex-grow: 1; +} +.flex-grow-0 { + flex-grow: 0; +} +.table-fixed { + table-layout: fixed; +} +.border-collapse { + border-collapse: collapse; +} +.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)); +} +.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)); +} +.origin-top-right { + transform-origin: top right; +} +.translate-x-5 { + --tw-translate-x: 1.25rem; +} +.-translate-x-4 { + --tw-translate-x: -1rem; +} +.translate-y-6 { + --tw-translate-y: 1.5rem; +} +.-translate-x-3 { + --tw-translate-x: -0.75rem; +} +.rotate-3 { + --tw-rotate: 3deg; +} +.skew-y-12 { + --tw-skew-y: 12deg; +} +.skew-x-12 { + --tw-skew-x: 12deg; +} +.scale-95 { + --tw-scale-x: 0.95; + --tw-scale-y: 0.95; +} +.animate-none { + animation: none; +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} +.animate-spin { + animation: spin 1s linear infinite; +} +.cursor-pointer { + cursor: pointer; +} +.select-none { + user-select: none; +} +.resize-none { + resize: none; +} +.list-inside { + list-style-position: inside; +} +.list-disc { + list-style-type: disc; +} +.appearance-none { + appearance: none; +} +.auto-cols-min { + grid-auto-columns: min-content; +} +.grid-flow-row { + grid-auto-flow: row; +} +.auto-rows-max { + grid-auto-rows: max-content; +} +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} +.grid-rows-3 { + grid-template-rows: repeat(3, minmax(0, 1fr)); +} +.flex-row-reverse { + flex-direction: row-reverse; +} +.flex-wrap { + flex-wrap: wrap; +} +.place-content-start { + place-content: start; +} +.place-items-end { + place-items: end; +} +.content-center { + align-content: center; +} +.items-start { + align-items: flex-start; +} +.justify-center { + justify-content: center; +} +.justify-items-end { + justify-items: end; +} +.gap-4 { + gap: 1rem; +} +.gap-x-2 { + column-gap: 0.5rem; +} +.gap-y-3 { + row-gap: 0.75rem; +} +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} +.space-y-reverse > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 1; +} +.space-x-reverse > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 1; +} +.divide-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-x-reverse: 0; + border-right-width: calc(2px * var(--tw-divide-x-reverse)); + border-left-width: calc(2px * calc(1 - var(--tw-divide-x-reverse))); +} +.divide-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(4px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(4px * var(--tw-divide-y-reverse)); +} +.divide-x-0 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-x-reverse: 0; + border-right-width: calc(0px * var(--tw-divide-x-reverse)); + border-left-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); +} +.divide-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); +} +.divide-dotted > :not([hidden]) ~ :not([hidden]) { + border-style: dotted; +} +.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-divide-opacity)); +} +.divide-opacity-50 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 0.5; +} +.place-self-center { + place-self: center; +} +.self-end { + align-self: flex-end; +} +.justify-self-start { + justify-self: start; +} +.overflow-hidden { + overflow: hidden; +} +.overscroll-contain { + overscroll-behavior: contain; +} +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.overflow-ellipsis { + text-overflow: ellipsis; +} +.whitespace-nowrap { + white-space: nowrap; +} +.break-words { + overflow-wrap: break-word; +} +.rounded-md { + border-radius: 0.375rem; +} +.border { + border-width: 1px; +} +.border-2 { + border-width: 2px; +} +.border-solid { + border-style: solid; +} +.border-black { + --tw-border-opacity: 1; + border-color: rgba(0, 0, 0, var(--tw-border-opacity)); +} +.border-opacity-10 { + --tw-border-opacity: 0.1; +} +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgba(16, 185, 129, var(--tw-bg-opacity)); +} +.bg-opacity-20 { + --tw-bg-opacity: 0.2; +} +.bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); +} +.from-red-300 { + --tw-gradient-from: #fca5a5; + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(252, 165, 165, 0)); +} +.via-purple-200 { + --tw-gradient-stops: var(--tw-gradient-from), #ddd6fe, + var(--tw-gradient-to, rgba(221, 214, 254, 0)); +} +.to-blue-400 { + --tw-gradient-to: #60a5fa; +} +.decoration-slice { + box-decoration-break: slice; +} +.decoration-clone { + box-decoration-break: clone; +} +.bg-cover { + background-size: cover; +} +.bg-local { + background-attachment: local; +} +.bg-clip-border { + background-clip: border-box; +} +.bg-top { + background-position: top; +} +.bg-no-repeat { + background-repeat: no-repeat; +} +.bg-origin-border { + background-origin: border-box; +} +.bg-origin-padding { + background-origin: padding-box; +} +.bg-origin-content { + background-origin: content-box; +} +.fill-current { + fill: currentColor; +} +.stroke-current { + stroke: currentColor; +} +.stroke-2 { + stroke-width: 2; +} +.object-cover { + object-fit: cover; +} +.object-bottom { + object-position: bottom; +} +.p-4 { + padding: 1rem; +} +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} +.pt-1 { + padding-top: 0.25rem; +} +.pr-2 { + padding-right: 0.5rem; +} +.pb-3 { + padding-bottom: 0.75rem; +} +.pl-4 { + padding-left: 1rem; +} +.text-center { + text-align: center; +} +.align-middle { + vertical-align: middle; +} +.font-sans { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol', 'Noto Color Emoji'; +} +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} +.font-medium { + font-weight: 500; +} +.uppercase { + text-transform: uppercase; +} +.not-italic { + font-style: normal; +} +.ordinal, +.slashed-zero, +.lining-nums, +.oldstyle-nums, +.proportional-nums, +.tabular-nums, +.diagonal-fractions, +.stacked-fractions { + --tw-ordinal: var(--tw-empty, /*!*/ /*!*/); + --tw-slashed-zero: var(--tw-empty, /*!*/ /*!*/); + --tw-numeric-figure: var(--tw-empty, /*!*/ /*!*/); + --tw-numeric-spacing: var(--tw-empty, /*!*/ /*!*/); + --tw-numeric-fraction: var(--tw-empty, /*!*/ /*!*/); + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) + var(--tw-numeric-spacing) var(--tw-numeric-fraction); +} +.ordinal { + --tw-ordinal: ordinal; +} +.tabular-nums { + --tw-numeric-spacing: tabular-nums; +} +.diagonal-fractions { + --tw-numeric-fraction: diagonal-fractions; +} +.leading-relaxed { + line-height: 1.625; +} +.leading-5 { + line-height: 1.25rem; +} +.tracking-tight { + letter-spacing: -0.025em; +} +.text-indigo-500 { + --tw-text-opacity: 1; + color: rgba(99, 102, 241, var(--tw-text-opacity)); +} +.text-opacity-10 { + --tw-text-opacity: 0.1; +} +.underline { + text-decoration: underline; +} +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.placeholder-green-300::placeholder { + --tw-placeholder-opacity: 1; + color: rgba(110, 231, 183, var(--tw-placeholder-opacity)); +} +.placeholder-opacity-60::placeholder { + --tw-placeholder-opacity: 0.6; +} +.opacity-90 { + opacity: 0.9; +} +.bg-blend-darken { + background-blend-mode: darken; +} +.bg-blend-difference { + background-blend-mode: difference; +} +.mix-blend-multiply { + mix-blend-mode: multiply; +} +.mix-blend-saturation { + mix-blend-mode: saturation; +} +.shadow { + --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); +} +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); +} +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); +} +.outline-none { + outline: 2px solid transparent; + outline-offset: 2px; +} +.outline-black { + outline: 2px dotted black; + outline-offset: 2px; +} +.ring { + --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(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} +.ring-4 { + --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(4px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} +.ring-white { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(255, 255, 255, var(--tw-ring-opacity)); +} +.ring-opacity-40 { + --tw-ring-opacity: 0.4; +} +.ring-offset-2 { + --tw-ring-offset-width: 2px; +} +.ring-offset-blue-300 { + --tw-ring-offset-color: #93c5fd; +} +.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); +} +.filter-none { + filter: none; +} +.blur-md { + --tw-blur: blur(12px); +} +.brightness-150 { + --tw-brightness: brightness(1.5); +} +.contrast-50 { + --tw-contrast: contrast(0.5); +} +.drop-shadow-md { + --tw-drop-shadow: drop-shadow(0 4px 3px rgba(0, 0, 0, 0.07)) + drop-shadow(0 2px 2px rgba(0, 0, 0, 0.06)); +} +.grayscale { + --tw-grayscale: grayscale(100%); +} +.hue-rotate-60 { + --tw-hue-rotate: hue-rotate(60deg); +} +.invert { + --tw-invert: invert(100%); +} +.saturate-200 { + --tw-saturate: saturate(2); +} +.sepia { + --tw-sepia: sepia(100%); +} +.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); +} +.backdrop-filter-none { + backdrop-filter: none; +} +.backdrop-blur-lg { + --tw-backdrop-blur: blur(16px); +} +.backdrop-brightness-50 { + --tw-backdrop-brightness: brightness(0.5); +} +.backdrop-contrast-0 { + --tw-backdrop-contrast: contrast(0); +} +.backdrop-grayscale { + --tw-backdrop-grayscale: grayscale(100%); +} +.backdrop-hue-rotate-90 { + --tw-backdrop-hue-rotate: hue-rotate(90deg); +} +.backdrop-invert { + --tw-backdrop-invert: invert(100%); +} +.backdrop-opacity-75 { + --tw-backdrop-opacity: opacity(0.75); +} +.backdrop-saturate-150 { + --tw-backdrop-saturate: saturate(1.5); +} +.backdrop-sepia { + --tw-backdrop-sepia: sepia(100%); +} +.transition { + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, + transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.delay-300 { + transition-delay: 300ms; +} +.duration-200 { + transition-duration: 200ms; +} +.ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} diff --git a/tests/jit/relative-purge-paths.test.html b/tests/jit/relative-purge-paths.test.html new file mode 100644 index 000000000..1e97f5fdf --- /dev/null +++ b/tests/jit/relative-purge-paths.test.html @@ -0,0 +1,147 @@ + + + + + + + Title + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + diff --git a/tests/jit/relative-purge-paths.test.js b/tests/jit/relative-purge-paths.test.js new file mode 100644 index 000000000..d759cac76 --- /dev/null +++ b/tests/jit/relative-purge-paths.test.js @@ -0,0 +1,25 @@ +import postcss from 'postcss' +import fs from 'fs' +import path from 'path' +import tailwind from '../../src/jit/index.js' + +function run(input, config = {}) { + return postcss(tailwind(config)).process(input, { + from: path.resolve(__filename), + }) +} + +test('relative purge paths', () => { + let css = ` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(css, path.resolve(__dirname, './relative-purge-paths.config.js')).then((result) => { + let expectedPath = path.resolve(__dirname, './relative-purge-paths.test.css') + let expected = fs.readFileSync(expectedPath, 'utf8') + + expect(result.css).toMatchFormattedCss(expected) + }) +}) diff --git a/tests/purgeUnusedStyles.test.js b/tests/purgeUnusedStyles.test.js index 3fbc14534..59724b535 100644 --- a/tests/purgeUnusedStyles.test.js +++ b/tests/purgeUnusedStyles.test.js @@ -4,6 +4,7 @@ import postcss from 'postcss' import tailwind from '../src/index' import { tailwindExtractor } from '../src/lib/purgeUnusedStyles' import defaultConfig from '../stubs/defaultConfig.stub.js' +import customConfig from './fixtures/custom-purge-config.js' function suppressConsoleLogs(cb, type = 'warn') { return () => { @@ -38,25 +39,13 @@ async function inProduction(callback) { return result } +function withTestName(path) { + return `${path}?test=${expect.getState().currentTestName}` +} + const config = { ...defaultConfig, - theme: { - extend: { - colors: { - 'black!': '#000', - }, - spacing: { - 1.5: '0.375rem', - '(1/2+8)': 'calc(50% + 2rem)', - }, - minHeight: { - '(screen-4)': 'calc(100vh - 1rem)', - }, - fontFamily: { - '%#$@': 'Comic Sans', - }, - }, - }, + ...customConfig, } delete config.presets @@ -108,7 +97,22 @@ test('purges unused classes', () => { purge: [path.resolve(`${__dirname}/fixtures/**/*.html`)], }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) + .then((result) => { + assertPurged(result) + }) + }) + ) +}) + +test('purge patterns are resolved relative to the config file', () => { + return inProduction( + suppressConsoleLogs(() => { + const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`) + const input = fs.readFileSync(inputPath, 'utf8') + + return postcss([tailwind(path.resolve(`${__dirname}/fixtures/custom-purge-config.js`))]) + .process(input, { from: withTestName(inputPath) }) .then((result) => { assertPurged(result) }) @@ -275,7 +279,7 @@ test('purges unused classes with important string', () => { purge: [path.resolve(`${__dirname}/fixtures/**/*.html`)], }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { assertPurged(result) }) @@ -298,7 +302,7 @@ test('mode must be a valid value', () => { content: [path.resolve(`${__dirname}/fixtures/**/*.html`)], }, }), - ]).process(input, { from: inputPath }) + ]).process(input, { from: withTestName(inputPath) }) ).rejects.toThrow() }) ) @@ -319,7 +323,7 @@ test('components are purged by default in layers mode', () => { purge: [path.resolve(`${__dirname}/fixtures/**/*.html`)], }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { expect(result.css).not.toContain('.container') assertPurged(result) @@ -347,7 +351,7 @@ test('you can specify which layers to purge', () => { }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const rules = extractRules(result.root) expect(rules).toContain('optgroup') @@ -377,7 +381,7 @@ test('you can purge just base and component layers (but why)', () => { }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const rules = extractRules(result.root) expect(rules).not.toContain('[type="checkbox"]') @@ -439,7 +443,7 @@ test( purge: [path.resolve(`${__dirname}/fixtures/**/*.html`)], }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const expected = fs.readFileSync( path.resolve(`${__dirname}/fixtures/tailwind-output.css`), @@ -465,7 +469,7 @@ test('does not purge if the array is empty', () => { purge: [], }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { process.env.NODE_ENV = OLD_NODE_ENV const expected = fs.readFileSync( @@ -491,7 +495,7 @@ test('does not purge if explicitly disabled', () => { purge: { enabled: false }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const expected = fs.readFileSync( path.resolve(`${__dirname}/fixtures/tailwind-output.css`), @@ -516,7 +520,7 @@ test('does not purge if purge is simply false', () => { purge: false, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const expected = fs.readFileSync( path.resolve(`${__dirname}/fixtures/tailwind-output.css`), @@ -541,7 +545,7 @@ test('purges outside of production if explicitly enabled', () => { purge: { enabled: true, content: [path.resolve(`${__dirname}/fixtures/**/*.html`)] }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { assertPurged(result) }) @@ -567,7 +571,7 @@ test( }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { expect(result.css).toContain('.md\\:bg-green-500') assertPurged(result) @@ -596,7 +600,7 @@ test( css.walkComments((c) => c.remove()) }, ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { expect(result.css).toContain('html') expect(result.css).toContain('body') @@ -624,7 +628,7 @@ test('element selectors are preserved by default', () => { }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const rules = extractRules(result.root) ;[ @@ -678,7 +682,7 @@ test('element selectors are preserved even when defaultExtractor is overridden', }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const rules = extractRules(result.root) ;[ @@ -729,7 +733,7 @@ test('preserving element selectors can be disabled', () => { }, }), ]) - .process(input, { from: inputPath }) + .process(input, { from: withTestName(inputPath) }) .then((result) => { const rules = extractRules(result.root)