mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Integrate PurgeCSS directly into Tailwind
This commit is contained in:
parent
857e2d376d
commit
ec0b7a2be1
15
__tests__/fixtures/purge-example.html
Normal file
15
__tests__/fixtures/purge-example.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!-- Basic HTML -->
|
||||
<div class="bg-red-500 md:bg-blue-300 w-1/2"></div>
|
||||
|
||||
<!-- Vue dynamic classes -->
|
||||
<span :class="{ block: enabled, 'md:flow-root': !enabled }"></span>
|
||||
|
||||
<!-- JSX with template strings -->
|
||||
<script>
|
||||
function Component() {
|
||||
return <div class={`h-screen`}></div>
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Custom classes with really weird characters -->
|
||||
<div class="min-h-(screen-4) bg-black! font-%#$@ w-(1/2+8)"></div>
|
||||
628
__tests__/fixtures/tailwind-output-purged.css
Normal file
628
__tests__/fixtures/tailwind-output-purged.css
Normal file
@ -0,0 +1,628 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually forked from SUIT CSS Base: https://github.com/suitcss/base
|
||||
* A thin layer on top of normalize.css that provides a starting point more
|
||||
* suitable for web applications.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Removes the default spacing and border for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Work around a Firefox/IE bug where the transparent `button` background
|
||||
* results in a loss of the default `button` focus styles.
|
||||
*/
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tailwind custom reset styles
|
||||
*/
|
||||
|
||||
/**
|
||||
* 1. Use the user's configured `sans` font-family (with Tailwind's default
|
||||
* sans-serif font stack as a fallback) as a sane default.
|
||||
* 2. Use Tailwind's default "normal" line-height so the user isn't forced
|
||||
* to override it to ensure consistency even when using the default theme.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: 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"; /* 1 */
|
||||
line-height: 1.5; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent padding and border from affecting element width.
|
||||
*
|
||||
* We used to set this in the html element and inherit from
|
||||
* the parent element for everything else. This caused issues
|
||||
* in shadow-dom-enhanced elements like <details> where the content
|
||||
* is wrapped by a div with box-sizing set to `content-box`.
|
||||
*
|
||||
* https://github.com/mozdevs/cssremedy/issues/4
|
||||
*
|
||||
*
|
||||
* 2. Allow adding a border to an element by just adding a border-width.
|
||||
*
|
||||
* By default, the way the browser specifies that an element should have no
|
||||
* border is by setting it's border-style to `none` in the user-agent
|
||||
* stylesheet.
|
||||
*
|
||||
* In order to easily add borders to elements by just setting the `border-width`
|
||||
* property, we change the default border-style for all elements to `solid`, and
|
||||
* use border-width to hide them instead. This way our `border` utilities only
|
||||
* need to set the `border-width` property instead of the entire `border`
|
||||
* shorthand, making our border utilities much more straightforward to compose.
|
||||
*
|
||||
* https://github.com/tailwindcss/tailwindcss/pull/116
|
||||
*/
|
||||
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box; /* 1 */
|
||||
border-width: 0; /* 2 */
|
||||
border-style: solid; /* 2 */
|
||||
border-color: #e2e8f0; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure horizontal rules are visible by default
|
||||
*/
|
||||
|
||||
hr {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the `border-style: none` reset that Normalize applies to images so that
|
||||
* our `border-{width}` utilities have the expected effect.
|
||||
*
|
||||
* The Normalize reset is unnecessary for us since we default the border-width
|
||||
* to 0 on all elements.
|
||||
*
|
||||
* https://github.com/tailwindcss/tailwindcss/issues/362
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset links to optimize for opt-in styling instead of
|
||||
* opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset form element properties that are easy to forget to
|
||||
* style explicitly so you don't inadvertently introduce
|
||||
* styles that deviate from your design system. These styles
|
||||
* supplement a partial reset that is already applied by
|
||||
* normalize.css.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
padding: 0;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the configured 'mono' font family for elements that
|
||||
* are expected to be rendered with a monospace font, falling
|
||||
* back to the system monospace stack if there is no configured
|
||||
* 'mono' font family.
|
||||
*/
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make replaced elements `display: block` by default as that's
|
||||
* the behavior you want almost all of the time. Inspired by
|
||||
* CSS Remedy, with `svg` added as well.
|
||||
*
|
||||
* https://github.com/mozdevs/cssremedy/issues/14
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrain images and videos to the parent width and preserve
|
||||
* their instrinsic aspect ratio.
|
||||
*
|
||||
* https://github.com/mozdevs/cssremedy/issues/14
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.bg-red-500 {
|
||||
--bg-opacity: 1;
|
||||
background-color: #f56565;
|
||||
background-color: rgba(245, 101, 101, var(--bg-opacity));
|
||||
}
|
||||
|
||||
.bg-black\! {
|
||||
--bg-opacity: 1;
|
||||
background-color: #000;
|
||||
background-color: rgba(0, 0, 0, var(--bg-opacity));
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.font-\%\#\$\@ {
|
||||
font-family: Comic Sans;
|
||||
}
|
||||
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.w-\(1\/2\+8\) {
|
||||
width: calc(50% + 2rem);
|
||||
}
|
||||
|
||||
.w-1\/2 {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.example {
|
||||
font-weight: 700;
|
||||
color: #f56565;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:bg-blue-300 {
|
||||
--bg-opacity: 1;
|
||||
background-color: #90cdf4;
|
||||
background-color: rgba(144, 205, 244, var(--bg-opacity));
|
||||
}
|
||||
|
||||
.md\:flow-root {
|
||||
display: flow-root;
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,8 @@ test('it can generate responsive variants', () => {
|
||||
.banana { color: yellow; }
|
||||
.chocolate { color: brown; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -52,6 +54,8 @@ test('it can generate responsive variants with a custom separator', () => {
|
||||
.banana { color: yellow; }
|
||||
.chocolate { color: brown; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -92,6 +96,8 @@ test('it can generate responsive variants when classes have non-standard charact
|
||||
.hover\\:banana { color: yellow; }
|
||||
.chocolate-2\\.5 { color: brown; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -137,6 +143,8 @@ test('responsive variants are grouped', () => {
|
||||
@responsive {
|
||||
.chocolate { color: brown; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -181,6 +189,8 @@ test('it can generate responsive variants for nested at-rules', () => {
|
||||
.grid\\:banana { color: blue; }
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -244,6 +254,8 @@ test('it can generate responsive variants for deeply nested at-rules', () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -307,6 +319,8 @@ test('screen prefix is only applied to the last class in a selector', () => {
|
||||
@responsive {
|
||||
.banana li * .sandwich #foo > div { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -342,6 +356,8 @@ test('responsive variants are generated for all selectors in a rule', () => {
|
||||
@responsive {
|
||||
.foo, .bar { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
|
||||
const output = `
|
||||
@ -377,6 +393,8 @@ test('selectors with no classes cannot be made responsive', () => {
|
||||
@responsive {
|
||||
div { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
expect.assertions(1)
|
||||
return run(input, {
|
||||
@ -398,6 +416,8 @@ test('all selectors in a rule must contain classes', () => {
|
||||
@responsive {
|
||||
.foo, div { color: yellow; }
|
||||
}
|
||||
|
||||
@tailwind screens;
|
||||
`
|
||||
expect.assertions(1)
|
||||
return run(input, {
|
||||
|
||||
@ -77,3 +77,123 @@ it('generates the right CSS with implicit screen utilities', () => {
|
||||
expect(result.css).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('generates the right CSS when "important" is enabled', () => {
|
||||
const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`)
|
||||
const input = fs.readFileSync(inputPath, 'utf8')
|
||||
|
||||
return postcss([tailwind({ ...config, important: true })])
|
||||
.process(input, { from: inputPath })
|
||||
.then(result => {
|
||||
const expected = fs.readFileSync(
|
||||
path.resolve(`${__dirname}/fixtures/tailwind-output-important.css`),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
expect(result.css).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('purges unused classes', () => {
|
||||
const OLD_NODE_ENV = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = 'production'
|
||||
const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`)
|
||||
const input = fs.readFileSync(inputPath, 'utf8')
|
||||
|
||||
return postcss([
|
||||
tailwind({
|
||||
...config,
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'black!': '#000',
|
||||
},
|
||||
spacing: {
|
||||
'(1/2+8)': 'calc(50% + 2rem)',
|
||||
},
|
||||
minHeight: {
|
||||
'(screen-100)': 'calc(100vh - 1rem)',
|
||||
},
|
||||
fontFamily: {
|
||||
'%#$@': 'Comic Sans',
|
||||
},
|
||||
},
|
||||
},
|
||||
purge: [path.resolve(`${__dirname}/fixtures/**/*.html`)],
|
||||
}),
|
||||
])
|
||||
.process(input, { from: inputPath })
|
||||
.then(result => {
|
||||
process.env.NODE_ENV = OLD_NODE_ENV
|
||||
const expected = fs.readFileSync(
|
||||
path.resolve(`${__dirname}/fixtures/tailwind-output-purged.css`),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
expect(result.css).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not purge except in production', () => {
|
||||
const OLD_NODE_ENV = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = 'development'
|
||||
const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`)
|
||||
const input = fs.readFileSync(inputPath, 'utf8')
|
||||
|
||||
return postcss([
|
||||
tailwind({
|
||||
...config,
|
||||
purge: [path.resolve(`${__dirname}/fixtures/**/*.html`)],
|
||||
}),
|
||||
])
|
||||
.process(input, { from: inputPath })
|
||||
.then(result => {
|
||||
process.env.NODE_ENV = OLD_NODE_ENV
|
||||
const expected = fs.readFileSync(
|
||||
path.resolve(`${__dirname}/fixtures/tailwind-output.css`),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
expect(result.css).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('purges outside of production if explicitly enabled', () => {
|
||||
const OLD_NODE_ENV = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = 'development'
|
||||
const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`)
|
||||
const input = fs.readFileSync(inputPath, 'utf8')
|
||||
|
||||
return postcss([
|
||||
tailwind({
|
||||
...config,
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'black!': '#000',
|
||||
},
|
||||
spacing: {
|
||||
'(1/2+8)': 'calc(50% + 2rem)',
|
||||
},
|
||||
minHeight: {
|
||||
'(screen-100)': 'calc(100vh - 1rem)',
|
||||
},
|
||||
fontFamily: {
|
||||
'%#$@': 'Comic Sans',
|
||||
},
|
||||
},
|
||||
},
|
||||
purge: { enabled: true, paths: [path.resolve(`${__dirname}/fixtures/**/*.html`)] },
|
||||
}),
|
||||
])
|
||||
.process(input, { from: inputPath })
|
||||
.then(result => {
|
||||
process.env.NODE_ENV = OLD_NODE_ENV
|
||||
const expected = fs.readFileSync(
|
||||
path.resolve(`${__dirname}/fixtures/tailwind-output-purged.css`),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
expect(result.css).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
"rimraf": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fullhuman/postcss-purgecss": "^2.1.2",
|
||||
"autoprefixer": "^9.4.5",
|
||||
"bytes": "^3.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
|
||||
@ -39,6 +39,29 @@ Promise.all([
|
||||
to: '__tests__/fixtures/tailwind-output-important.css',
|
||||
config: { important: true },
|
||||
}),
|
||||
build({
|
||||
from: '__tests__/fixtures/tailwind-input.css',
|
||||
to: '__tests__/fixtures/tailwind-output-purged.css',
|
||||
config: {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'black!': '#000',
|
||||
},
|
||||
spacing: {
|
||||
'(1/2+8)': 'calc(50% + 2rem)',
|
||||
},
|
||||
minHeight: {
|
||||
'(screen-100)': 'calc(100vh - 1rem)',
|
||||
},
|
||||
fontFamily: {
|
||||
'%#$@': 'Comic Sans',
|
||||
},
|
||||
},
|
||||
},
|
||||
purge: { enabled: true, paths: ['./__tests__/fixtures/**/*.html'] },
|
||||
},
|
||||
}),
|
||||
]).then(() => {
|
||||
console.log('\nFinished rebuilding fixtures.')
|
||||
console.log(
|
||||
|
||||
56
src/lib/purgeUnusedUtilities.js
Normal file
56
src/lib/purgeUnusedUtilities.js
Normal file
@ -0,0 +1,56 @@
|
||||
import _ from 'lodash'
|
||||
import postcss from 'postcss'
|
||||
import purgecss from '@fullhuman/postcss-purgecss'
|
||||
|
||||
export default function purgeUnusedUtilities(config) {
|
||||
const purgeEnabled =
|
||||
_.get(config, 'purge.enabled', false) === true ||
|
||||
(config.purge !== undefined && process.env.NODE_ENV === 'production')
|
||||
|
||||
if (!purgeEnabled) {
|
||||
return function(css) {
|
||||
css.walkComments(comment => {
|
||||
switch (comment.text.trim()) {
|
||||
case 'tailwind start components':
|
||||
case 'tailwind start utilities':
|
||||
case 'tailwind start screens':
|
||||
case 'tailwind end components':
|
||||
case 'tailwind end utilities':
|
||||
case 'tailwind end screens':
|
||||
comment.remove()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return postcss([
|
||||
function(css) {
|
||||
css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
|
||||
css.append(postcss.comment({ text: 'purgecss end ignore' }))
|
||||
|
||||
css.walkComments(comment => {
|
||||
switch (comment.text.trim()) {
|
||||
case 'tailwind start components':
|
||||
case 'tailwind start utilities':
|
||||
case 'tailwind start screens':
|
||||
comment.text = 'purgecss end ignore'
|
||||
break
|
||||
case 'tailwind end components':
|
||||
case 'tailwind end utilities':
|
||||
case 'tailwind end screens':
|
||||
comment.text = 'purgecss start ignore'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
},
|
||||
purgecss({
|
||||
content: Array.isArray(config.purge) ? config.purge : config.purge.paths,
|
||||
defaultExtractor: content => content.match(/[^<>"'`\s]+(?<![:=])/g) || [],
|
||||
}),
|
||||
])
|
||||
}
|
||||
@ -43,22 +43,16 @@ export default function(config) {
|
||||
|
||||
const hasScreenRules = finalRules.some(i => i.nodes.length !== 0)
|
||||
|
||||
if (!hasScreenRules) {
|
||||
return
|
||||
}
|
||||
|
||||
let includesScreensExplicitly = false
|
||||
|
||||
css.walkAtRules('tailwind', atRule => {
|
||||
if (atRule.params === 'screens') {
|
||||
atRule.replaceWith(finalRules)
|
||||
includesScreensExplicitly = true
|
||||
if (atRule.params !== 'screens') {
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if (!includesScreensExplicitly) {
|
||||
css.append(finalRules)
|
||||
return
|
||||
}
|
||||
if (hasScreenRules) {
|
||||
atRule.before(finalRules)
|
||||
}
|
||||
|
||||
atRule.remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,8 @@ export default function(
|
||||
}
|
||||
})
|
||||
|
||||
let includesScreensExplicitly = false
|
||||
|
||||
css.walkAtRules('tailwind', atRule => {
|
||||
if (atRule.params === 'preflight') {
|
||||
// prettier-ignore
|
||||
@ -52,14 +54,32 @@ export default function(
|
||||
}
|
||||
|
||||
if (atRule.params === 'components') {
|
||||
atRule.before(postcss.comment({ text: 'tailwind start components' }))
|
||||
atRule.before(updateSource(pluginComponents, atRule.source))
|
||||
atRule.after(postcss.comment({ text: 'tailwind end components' }))
|
||||
atRule.remove()
|
||||
}
|
||||
|
||||
if (atRule.params === 'utilities') {
|
||||
atRule.before(postcss.comment({ text: 'tailwind start utilities' }))
|
||||
atRule.before(updateSource(pluginUtilities, atRule.source))
|
||||
atRule.after(postcss.comment({ text: 'tailwind end utilities' }))
|
||||
atRule.remove()
|
||||
}
|
||||
|
||||
if (atRule.params === 'screens') {
|
||||
includesScreensExplicitly = true
|
||||
atRule.before(postcss.comment({ text: 'tailwind start screens' }))
|
||||
atRule.after(postcss.comment({ text: 'tailwind end screens' }))
|
||||
}
|
||||
})
|
||||
|
||||
if (!includesScreensExplicitly) {
|
||||
css.append([
|
||||
postcss.comment({ text: 'tailwind start screens' }),
|
||||
postcss.atRule({ name: 'tailwind', params: 'screens' }),
|
||||
postcss.comment({ text: 'tailwind end screens' }),
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import substituteVariantsAtRules from './lib/substituteVariantsAtRules'
|
||||
import substituteResponsiveAtRules from './lib/substituteResponsiveAtRules'
|
||||
import substituteScreenAtRules from './lib/substituteScreenAtRules'
|
||||
import substituteClassApplyAtRules from './lib/substituteClassApplyAtRules'
|
||||
import purgeUnusedUtilities from './lib/purgeUnusedUtilities'
|
||||
|
||||
import corePlugins from './corePlugins'
|
||||
import processPlugins from './util/processPlugins'
|
||||
@ -23,6 +24,7 @@ export default function(getConfig) {
|
||||
substituteResponsiveAtRules(config),
|
||||
substituteScreenAtRules(config),
|
||||
substituteClassApplyAtRules(config, processedPlugins.utilities),
|
||||
purgeUnusedUtilities(config),
|
||||
]).process(css, { from: _.get(css, 'source.input.file') })
|
||||
}
|
||||
}
|
||||
|
||||
41
yarn.lock
41
yarn.lock
@ -845,6 +845,14 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@fullhuman/postcss-purgecss@^2.1.2":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.1.2.tgz#8fe4d4ae2b58214b5452cb490a31c7146517442f"
|
||||
integrity sha512-Jf34YVBK9GtXTblpu0svNUJdA7rTQoRMz+yEJe6mwTnXDIGipWLzaX/VgU/x6IPC6WvU5SY/XlawwqhxoyFPTg==
|
||||
dependencies:
|
||||
postcss "7.0.27"
|
||||
purgecss "^2.1.2"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b"
|
||||
@ -1715,6 +1723,11 @@ commander@^4.0.1:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.0.1.tgz#b67622721785993182e807f4883633e6401ba53c"
|
||||
integrity sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==
|
||||
|
||||
commander@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
@ -4266,6 +4279,15 @@ postcss-value-parser@^4.0.3:
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d"
|
||||
integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==
|
||||
|
||||
postcss@7.0.27, postcss@^7.0.11, postcss@^7.0.18, postcss@^7.0.21, postcss@^7.0.27:
|
||||
version "7.0.27"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
|
||||
integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
|
||||
dependencies:
|
||||
chalk "^2.4.2"
|
||||
source-map "^0.6.1"
|
||||
supports-color "^6.1.0"
|
||||
|
||||
postcss@^6.0.9:
|
||||
version "6.0.23"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
|
||||
@ -4275,15 +4297,6 @@ postcss@^6.0.9:
|
||||
source-map "^0.6.1"
|
||||
supports-color "^5.4.0"
|
||||
|
||||
postcss@^7.0.11, postcss@^7.0.18, postcss@^7.0.21, postcss@^7.0.27:
|
||||
version "7.0.27"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
|
||||
integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
|
||||
dependencies:
|
||||
chalk "^2.4.2"
|
||||
source-map "^0.6.1"
|
||||
supports-color "^6.1.0"
|
||||
|
||||
prelude-ls@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||
@ -4362,6 +4375,16 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
purgecss@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-2.1.2.tgz#96f666d04c56705208aaa1a544b5f22e13828955"
|
||||
integrity sha512-5oDBxiT9VonwKmEMohPFRFZrj8fdSVKxHPwq7G5Rx/2pXicZFJu+D4m5bb3NuV0sSK3ooNxq5jFIwwHzifP5FA==
|
||||
dependencies:
|
||||
commander "^5.0.0"
|
||||
glob "^7.0.0"
|
||||
postcss "7.0.27"
|
||||
postcss-selector-parser "^6.0.2"
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user