├── .husky
├── .gitignore
└── pre-commit
├── packages
├── grunt-purgecss
│ ├── __tests__
│ │ ├── fixtures
│ │ │ ├── expected
│ │ │ │ ├── footer.css
│ │ │ │ ├── menu.css
│ │ │ │ ├── profile.css
│ │ │ │ └── simple.css
│ │ │ └── src
│ │ │ │ ├── menu.css
│ │ │ │ ├── footer.css
│ │ │ │ ├── profile.css
│ │ │ │ └── simple
│ │ │ │ ├── simple.html
│ │ │ │ └── simple.css
│ │ └── index.test.ts
│ ├── src
│ │ ├── types
│ │ │ └── index.d.ts
│ │ └── index.ts
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── build.ts
│ ├── Gruntfile.js
│ └── package.json
├── purgecss
│ ├── __tests__
│ │ ├── test_examples
│ │ │ ├── keyframes
│ │ │ │ ├── index.html
│ │ │ │ ├── keyframes.html
│ │ │ │ ├── index.css
│ │ │ │ └── keyframes.css
│ │ │ ├── pseudo-class
│ │ │ │ ├── pseudo_class.js
│ │ │ │ ├── not.html
│ │ │ │ ├── not.css
│ │ │ │ ├── pseudo_class.css
│ │ │ │ ├── nth_child.html
│ │ │ │ ├── is.html
│ │ │ │ ├── nth_child.css
│ │ │ │ ├── where.html
│ │ │ │ ├── pseudo_selector.css
│ │ │ │ ├── where.css
│ │ │ │ ├── is.css
│ │ │ │ └── pseudo_selector.html
│ │ │ ├── rejectedCss
│ │ │ │ ├── simple.js
│ │ │ │ ├── empty-parent-node.js
│ │ │ │ ├── simple.css
│ │ │ │ └── empty-parent-node.css
│ │ │ ├── others
│ │ │ │ ├── remove_unused.js
│ │ │ │ ├── special_characters.js
│ │ │ │ ├── remove_unused.css
│ │ │ │ └── special_characters.css
│ │ │ ├── cli
│ │ │ │ └── simple
│ │ │ │ │ └── src
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── style2.css
│ │ │ │ │ ├── style.css
│ │ │ │ │ └── content.html
│ │ │ ├── delimited
│ │ │ │ ├── delimited.html
│ │ │ │ └── delimited.css
│ │ │ ├── pseudo-elements
│ │ │ │ ├── pseudo-elements.html
│ │ │ │ └── pseudo-elements.css
│ │ │ ├── css-variables
│ │ │ │ ├── variables.html
│ │ │ │ └── variables.css
│ │ │ ├── safelist
│ │ │ │ ├── safelist_keyframes.html
│ │ │ │ ├── safelist_css_variables.html
│ │ │ │ ├── safelist.html
│ │ │ │ ├── safelist_patterns_greedy.html
│ │ │ │ ├── safelist_patterns_children.css
│ │ │ │ ├── safelist_patterns_greedy.css
│ │ │ │ ├── safelist_patterns_children.html
│ │ │ │ ├── blocklist.html
│ │ │ │ ├── safelist.css
│ │ │ │ ├── blocklist.css
│ │ │ │ ├── safelist_css_variables.css
│ │ │ │ └── safelist_keyframes.css
│ │ │ ├── skipped-content
│ │ │ │ ├── skippedFolder
│ │ │ │ │ └── skipped.html
│ │ │ │ ├── unskipped.html
│ │ │ │ └── simple.css
│ │ │ ├── rejected
│ │ │ │ ├── simple.js
│ │ │ │ └── simple.css
│ │ │ ├── comments
│ │ │ │ ├── ignore_comment.html
│ │ │ │ ├── ignore_comment_range.html
│ │ │ │ ├── ignore_comment.css
│ │ │ │ └── ignore_comment_range.css
│ │ │ ├── media-queries
│ │ │ │ ├── media_queries.html
│ │ │ │ └── media_queries.css
│ │ │ ├── font-faces
│ │ │ │ ├── font_face.html
│ │ │ │ └── font_face.css
│ │ │ ├── attributes
│ │ │ │ ├── attribute_selector.html
│ │ │ │ └── attribute_selector.css
│ │ │ └── chaining-rules
│ │ │ │ ├── index.html
│ │ │ │ └── index.css
│ │ ├── purgecss.config.js
│ │ ├── utils.ts
│ │ ├── chaining-rules.test.ts
│ │ ├── skipped-content.test.ts
│ │ ├── globs.test.ts
│ │ ├── cli
│ │ │ ├── cli-console-output.test.ts
│ │ │ ├── cli-file-output.test.ts
│ │ │ ├── cli-options.test.ts
│ │ │ └── cli-multiple-files-output.test.ts
│ │ ├── font-faces.test.ts
│ │ ├── delimited.test.ts
│ │ ├── performance.test.ts
│ │ ├── sourcemap.test.ts
│ │ ├── media-queries.test.ts
│ │ ├── pseudo-elements.test.ts
│ │ ├── css-variables.test.ts
│ │ ├── raw-css-name.test.ts
│ │ ├── comments.test.ts
│ │ ├── rejectedCss.test.ts
│ │ └── rejected.test.ts
│ ├── src
│ │ ├── internal-safelist.ts
│ │ ├── constants.ts
│ │ ├── options.ts
│ │ └── ExtractorResultSets.ts
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── package.json
│ ├── build.ts
│ └── README.md
├── vue-cli-plugin-purgecss
│ ├── index.js
│ ├── logo.png
│ ├── generator
│ │ ├── index.js
│ │ └── templates
│ │ │ └── postcss.config.js
│ ├── package.json
│ └── README.md
├── purgecss-webpack-plugin
│ ├── __tests__
│ │ ├── cases
│ │ │ ├── simple
│ │ │ │ ├── src
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── style.css
│ │ │ │ │ └── content.html
│ │ │ │ ├── expected
│ │ │ │ │ └── styles.css
│ │ │ │ ├── purgecss.config.js
│ │ │ │ └── webpack.config.js
│ │ │ ├── simple-with-exclusion
│ │ │ │ ├── src
│ │ │ │ │ ├── legacy.js
│ │ │ │ │ ├── style.css
│ │ │ │ │ ├── style_that_we_want_to_purge.css
│ │ │ │ │ └── index.js
│ │ │ │ ├── expected
│ │ │ │ │ ├── legacy.css
│ │ │ │ │ └── bundle.css
│ │ │ │ └── webpack.config.js
│ │ │ └── path-and-safelist-functions
│ │ │ │ ├── src
│ │ │ │ ├── index.js
│ │ │ │ ├── content.html
│ │ │ │ └── style.css
│ │ │ │ ├── expected
│ │ │ │ └── styles.css
│ │ │ │ └── webpack.config.js
│ │ └── index.test.ts
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── build.ts
│ ├── src
│ │ └── types
│ │ │ └── index.ts
│ └── package.json
├── postcss-purgecss
│ ├── __tests__
│ │ └── fixtures
│ │ │ ├── expected
│ │ │ ├── simple.css
│ │ │ ├── other-plugins.css
│ │ │ └── font-keyframes.css
│ │ │ └── src
│ │ │ ├── config-test
│ │ │ ├── purgecss.config.js
│ │ │ ├── index.html
│ │ │ ├── expected.css
│ │ │ └── index.css
│ │ │ ├── simple
│ │ │ ├── simple.html
│ │ │ └── simple.css
│ │ │ ├── other-plugins
│ │ │ ├── other-plugins.html
│ │ │ └── other-plugins.css
│ │ │ └── font-keyframes
│ │ │ ├── font-keyframes.html
│ │ │ └── font-keyframes.css
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── build.ts
│ ├── src
│ │ └── types
│ │ │ └── index.ts
│ └── package.json
├── gulp-purgecss
│ ├── __tests__
│ │ ├── test.html
│ │ ├── buffer.test.ts
│ │ └── stream.test.ts
│ ├── jest.config.ts
│ ├── src
│ │ └── types
│ │ │ └── index.ts
│ ├── tsconfig.json
│ ├── build.ts
│ ├── package.json
│ └── README.md
├── purgecss-from-jsx
│ ├── jest.config.ts
│ ├── README.md
│ ├── tsconfig.json
│ ├── build.ts
│ ├── __tests__
│ │ ├── data.ts
│ │ └── index.test.ts
│ └── package.json
├── purgecss-from-pug
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── build.ts
│ ├── README.md
│ ├── package.json
│ ├── __tests__
│ │ ├── data.ts
│ │ └── index.test.ts
│ └── src
│ │ └── index.ts
├── purgecss-from-tsx
│ ├── jest.config.ts
│ ├── README.md
│ ├── tsconfig.json
│ ├── build.ts
│ ├── src
│ │ └── index.ts
│ ├── __tests__
│ │ ├── data.ts
│ │ └── index.test.ts
│ └── package.json
├── rollup-plugin-purgecss
│ ├── __tests__
│ │ ├── assets
│ │ │ ├── actual_a.css
│ │ │ ├── expect_a.css
│ │ │ ├── test_a.css
│ │ │ └── test_a.html
│ │ ├── fixtures
│ │ │ └── basic
│ │ │ │ └── index.js
│ │ └── index.test.ts
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── build.ts
│ ├── src
│ │ ├── types
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── package.json
│ └── README.md
├── purgecss-from-html
│ ├── jest.config.ts
│ ├── README.md
│ ├── tsconfig.json
│ ├── build.ts
│ ├── package.json
│ └── __tests__
│ │ ├── data.ts
│ │ └── index.test.ts
└── purgecss-with-wordpress
│ ├── package.json
│ ├── index.js
│ ├── LICENSE
│ └── README.md
├── types
├── vinyl-sourcemaps-apply
│ └── index.d.ts
└── acorn-jsx-walk
│ └── index.d.ts
├── lerna.json
├── docs
├── .vuepress
│ ├── public
│ │ ├── favicon.ico
│ │ ├── full-human.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── apple-touch-icon.png
│ │ ├── mstile-150x150.png
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── browserconfig.xml
│ │ ├── site.webmanifest
│ │ └── safari-pinned-tab.svg
│ ├── client.ts
│ └── theme
│ │ ├── client.ts
│ │ ├── layouts
│ │ └── Layout.vue
│ │ ├── index.ts
│ │ └── components
│ │ └── CarbonAds.vue
├── plugins
│ ├── gulp.md
│ └── postcss.md
├── guides
│ ├── wordpress.md
│ └── vue.md
├── introduction.md
├── getting-started.md
├── api.md
├── README.md
└── ant_design.md
├── firebase.json
├── scripts
└── verify-commit.ts
├── .github
├── dependabot.yml
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.yml
├── workflows
│ ├── stale-issues.yml
│ └── build-and-test.yml
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── tsconfig.json
├── .vscode
└── launch.json
├── SECURITY.md
├── jest.config.ts
├── LICENSE
├── eslint.config.mjs
├── .gitignore
├── CONTRIBUTING.md
└── package.json
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npm run prettier
2 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/expected/footer.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/expected/menu.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/expected/profile.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/src/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import "grunt";
2 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/keyframes/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js:
--------------------------------------------------------------------------------
1 | "div";
2 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/rejectedCss/simple.js:
--------------------------------------------------------------------------------
1 |
2 | "critical"
3 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/others/remove_unused.js:
--------------------------------------------------------------------------------
1 | ".used-class";
2 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.js:
--------------------------------------------------------------------------------
1 | "used-class"
--------------------------------------------------------------------------------
/packages/vue-cli-plugin-purgecss/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (api, options) => {};
2 |
--------------------------------------------------------------------------------
/types/vinyl-sourcemaps-apply/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module "vinyl-sourcemaps-apply";
2 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/index.js:
--------------------------------------------------------------------------------
1 | import './style.css'
2 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/cli/simple/src/index.js:
--------------------------------------------------------------------------------
1 | import './style.css'
2 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/not.html:
--------------------------------------------------------------------------------
1 | anything
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/delimited/delimited.html:
--------------------------------------------------------------------------------
1 | hello
2 | world
3 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-elements/pseudo-elements.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/expected/simple.css:
--------------------------------------------------------------------------------
1 | .used-class {
2 | color: black;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/expected/simple.css:
--------------------------------------------------------------------------------
1 | .used-class {
2 | color: black;
3 | }
4 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json",
3 | "version": "7.0.2"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/src/menu.css:
--------------------------------------------------------------------------------
1 | .menu-unused-class {
2 | background: red;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/legacy.js:
--------------------------------------------------------------------------------
1 | import './style.css'
2 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/css-variables/variables.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/purgecss/src/internal-safelist.ts:
--------------------------------------------------------------------------------
1 | export const CSS_SAFELIST = ["*", ":root", ":after", ":before"];
2 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/src/footer.css:
--------------------------------------------------------------------------------
1 | .footer-unused-class {
2 | background: black;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js:
--------------------------------------------------------------------------------
1 | import "./style.css";
2 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/skipped-content/skippedFolder/skipped.html:
--------------------------------------------------------------------------------
1 | anything
2 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/favicon.ico
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/src/profile.css:
--------------------------------------------------------------------------------
1 | .profile-unused-class {
2 | background: hotpink;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/delimited/delimited.css:
--------------------------------------------------------------------------------
1 | .unused-class-name, h1, p a{
2 | color: red;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/full-human.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/full-human.png
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/expected/other-plugins.css:
--------------------------------------------------------------------------------
1 | .prefixed-used-class {
2 | color: black;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/purgecss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['index.html'],
3 | css: ['style.css']
4 | }
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/favicon-32x32.png
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/rejected/simple.js:
--------------------------------------------------------------------------------
1 | "single";
2 |
3 | "double-class";
4 |
5 | "triple-simple-class";
6 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/skipped-content/unskipped.html:
--------------------------------------------------------------------------------
1 | anything
2 | anything
--------------------------------------------------------------------------------
/docs/.vuepress/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/mstile-150x150.png
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hmmmm
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/vue-cli-plugin-purgecss/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/packages/vue-cli-plugin-purgecss/logo.png
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/comments/ignore_comment.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hmmmm
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/keyframes/keyframes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/others/special_characters.js:
--------------------------------------------------------------------------------
1 | "@home";
2 |
3 | "+rounded";
4 |
5 | "button";
6 |
7 | ".md:w-1/3";
8 |
--------------------------------------------------------------------------------
/packages/purgecss/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "purgecss");
3 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FullHuman/purgecss/HEAD/docs/.vuepress/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/cli/simple/src/style2.css:
--------------------------------------------------------------------------------
1 | .world {
2 | color: green;
3 | }
4 |
5 | .unused {
6 | color: blue;
7 | }
8 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hmmmm
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/rejectedCss/simple.css:
--------------------------------------------------------------------------------
1 | .critical {
2 | color: red;
3 | }
4 |
5 | .rejected {
6 | color: blue;
7 | }
8 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "grunt-purgecss");
3 |
--------------------------------------------------------------------------------
/packages/gulp-purgecss/__tests__/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/gulp-purgecss/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "gulp-purgecss");
3 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/not.css:
--------------------------------------------------------------------------------
1 | .bar:not(.foo) {
2 | color: red;
3 | }
4 | .bar:not(.foo-bar) {
5 | color: blue;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "postcss-purgecss");
3 |
--------------------------------------------------------------------------------
/packages/purgecss-from-jsx/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "purgecss-from-jsx");
3 |
--------------------------------------------------------------------------------
/packages/purgecss-from-pug/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "purgecss-from-pug");
3 |
--------------------------------------------------------------------------------
/packages/purgecss-from-tsx/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "purgecss-from-tsx");
3 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.css:
--------------------------------------------------------------------------------
1 | div:before {
2 | color: black;
3 | }
4 |
5 | .row:after {
6 | color: black;
7 | }
8 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/__tests__/assets/actual_a.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | color: red;
3 | }
4 |
5 | .used,
6 | .there {
7 | color: green;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/__tests__/assets/expect_a.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | color: red;
3 | }
4 |
5 | .used,
6 | .there {
7 | color: green;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/src/simple/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/config-test/purgecss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [`${__dirname}/index.html`],
3 | fontFace: true
4 | }
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/simple/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/purgecss-from-html/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "purgecss-from-html");
3 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/__tests__/fixtures/basic/index.js:
--------------------------------------------------------------------------------
1 | import "../../assets/test_a.css";
2 |
3 | export default function noop() {
4 | return;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/media-queries/media_queries.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | 'alone'
4 |
5 | 'id-in-media'
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "rollup-plugin-purgecss");
3 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { createConfig } from "./../../jest.config";
2 | export default createConfig(__dirname, "purgecss-webpack-plugin");
3 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 66666px) {
2 | .used-class, .unused-class {
3 | color: black;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/config-test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/font-faces/font_face.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/types/acorn-jsx-walk/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module "acorn-jsx-walk" {
2 | import * as walk from "acorn-walk";
3 | export function extend(base: walk.RecursiveVisitors): void;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/other-plugins/other-plugins.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/.vuepress/client.ts:
--------------------------------------------------------------------------------
1 | import { defineClientConfig } from "@vuepress/client";
2 |
3 | export default defineClientConfig({
4 | enhance() {},
5 | setup() {},
6 | rootComponents: [],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/font-keyframes/font-keyframes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/skipped-content/simple.css:
--------------------------------------------------------------------------------
1 | .black {
2 | color: black;
3 | }
4 |
5 | .red {
6 | color: red;
7 | }
8 |
9 | .green {
10 | color: green;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/is.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/rejected/simple.css:
--------------------------------------------------------------------------------
1 | .single {
2 | color: black;
3 | }
4 |
5 | .double-class {
6 | color: black;
7 | }
8 |
9 | .triple-simple-class {
10 | color: black;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.css:
--------------------------------------------------------------------------------
1 | .card {}
2 | .title {}
3 | .card .content {}
4 |
5 | .btn {}
6 | .btn .red {}
7 | .btngreen {}
8 |
9 | .card .btn .yellow {}
10 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/__tests__/assets/test_a.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | color: red;
3 | }
4 |
5 | .used,
6 | .there {
7 | color: green;
8 | }
9 |
10 | .unused {
11 | color: #ff00ff;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.css:
--------------------------------------------------------------------------------
1 | .some-item:nth-child(2n){
2 | color: green
3 | }
4 | .some-item:nth-child(2n+1){
5 | color: blue
6 | }
7 | canvas {
8 | display: none
9 | }
10 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/where.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/.vuepress/theme/client.ts:
--------------------------------------------------------------------------------
1 | import { defineClientConfig } from "@vuepress/client";
2 | import Layout from "./layouts/Layout.vue";
3 |
4 | export default defineClientConfig({
5 | layouts: {
6 | Layout,
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/__tests__/fixtures/src/simple/simple.css:
--------------------------------------------------------------------------------
1 | .used-class {
2 | color: black;
3 | }
4 |
5 | .unused-class {
6 | color: black;
7 | }
8 |
9 | .another-one-not-found {
10 | color: black;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/simple/simple.css:
--------------------------------------------------------------------------------
1 | .used-class {
2 | color: black;
3 | }
4 |
5 | .unused-class {
6 | color: black;
7 | }
8 |
9 | .another-one-not-found {
10 | color: black;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/comments/ignore_comment.css:
--------------------------------------------------------------------------------
1 | /* purgecss ignore */
2 | h1 {
3 | color: blue;
4 |
5 | }
6 |
7 | h3 {
8 | /* purgecss ignore current */
9 | color: red;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/others/remove_unused.css:
--------------------------------------------------------------------------------
1 | .used-class {
2 | color: black;
3 | }
4 |
5 | .unused-class {
6 | color: black;
7 | }
8 |
9 | .another-one-not-found {
10 | color: black;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/purgecss-from-html/README.md:
--------------------------------------------------------------------------------
1 | # `purgecss-from-html`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const purgecssFromHtml = require('purgecss-from-html');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/purgecss-from-jsx/README.md:
--------------------------------------------------------------------------------
1 | # `purgecss-from-jsx`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const purgecssFromJsx = require('purgecss-from-jsx');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/purgecss-from-tsx/README.md:
--------------------------------------------------------------------------------
1 | # `purgecss-from-tsx`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const purgecssFromTsx = require('purgecss-from-tsx');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style.css:
--------------------------------------------------------------------------------
1 | .legacy-unused-class {
2 | color: red;
3 | }
4 |
5 | .unused-too {
6 | color: blue;
7 | }
8 |
9 | .hello {
10 | color: red;
11 | }
12 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "cleanUrls": true,
4 | "public": "docs/.vuepress/dist",
5 | "ignore": [
6 | "firebase.json",
7 | "**/.*",
8 | "**/node_modules/**"
9 | ]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/other-plugins/other-plugins.css:
--------------------------------------------------------------------------------
1 | .used-class {
2 | color: black;
3 | }
4 |
5 | .unused-class {
6 | color: black;
7 | }
8 |
9 | .another-one-not-found {
10 | color: black;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.css:
--------------------------------------------------------------------------------
1 | .card {}
2 | .card[data-v-test] {}
3 | .card.card--large {}
4 | .card[data-v-test].card--large {}
5 | .card .card-content {}
6 | .card[data-v-test] .card-content {}
7 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/__tests__/assets/test_a.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/cli/simple/src/style.css:
--------------------------------------------------------------------------------
1 | .hello {
2 | color: red;
3 | }
4 |
5 | .unused {
6 | color: blue;
7 | }
8 |
9 | .safelisted {
10 | color: green;
11 | }
12 |
13 | md\:w-2\/3 {
14 | color: red;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css:
--------------------------------------------------------------------------------
1 | .hello {
2 | color: red;
3 | }
4 |
5 | .unused {
6 | color: blue;
7 | }
8 |
9 | .safelisted {
10 | color: green;
11 | }
12 |
13 | md\:w-2\/3 {
14 | color: red;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/vue-cli-plugin-purgecss/generator/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (api, options) => {
2 | api.extendPackage({
3 | devDependencies: {
4 | "@fullhuman/postcss-purgecss": "^4.0.0",
5 | },
6 | });
7 | api.render("./templates", options);
8 | };
9 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/others/special_characters.css:
--------------------------------------------------------------------------------
1 | .\@home {
2 | color: black;
3 | }
4 |
5 | .button.\+rounded {
6 | color: black;
7 | }
8 |
9 | .md\:w-1\/3 {
10 | color: green;
11 | }
12 |
13 | .\32 -panels {
14 | color: red;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/content.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Purgecss webpack test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css:
--------------------------------------------------------------------------------
1 | .hello {
2 | color: red;
3 | }
4 |
5 | .unused {
6 | color: blue;
7 | }
8 |
9 | .safelisted {
10 | color: green;
11 | }
12 |
13 | md\:w-2\/3 {
14 | color: red;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/cli/simple/src/content.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Purgecss webpack test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/gulp-purgecss/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import { UserDefinedOptions as PurgeCSSUserDefinedOptions } from "purgecss";
2 |
3 | /**
4 | * @public
5 | */
6 | export interface UserDefinedOptions
7 | extends Omit {
8 | content: string[];
9 | }
10 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/content.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Purgecss webpack test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/purgecss-from-html/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | },
9 | "include": ["src"],
10 | "exclude": ["./lib"]
11 | }
--------------------------------------------------------------------------------
/packages/grunt-purgecss/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "baseUrl": ".",
6 | "paths": {
7 | "purgecss": ["../purgecss/src"],
8 | }
9 | },
10 | "include": ["./src/**/*", "./../purgecss/src/*"]
11 | }
--------------------------------------------------------------------------------
/docs/.vuepress/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #ffffff
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/blocklist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | is it blue?
5 | is it red?
6 | Title
7 | random
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/purgecss-from-pug/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | },
9 | "include": ["./src/**/*", "./rollup.config.ts"],
10 | "exclude": ["./lib"]
11 | }
--------------------------------------------------------------------------------
/docs/.vuepress/theme/layouts/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/index.js:
--------------------------------------------------------------------------------
1 | import "./style_that_we_want_to_purge.css";
2 |
3 | function component() {
4 | const element = document.createElement("div");
5 |
6 | element.classList.add("hello");
7 | element.classList.add("md:w-2/3");
8 | return element;
9 | }
10 |
11 | document.body.appendChild(component());
12 |
--------------------------------------------------------------------------------
/packages/purgecss-from-jsx/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | "typeRoots": ["./../../types", "./../../node_modules/@types"]
9 | },
10 | "include": ["src", "./../../types/**/*"],
11 | "exclude": ["./lib"]
12 | }
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | pdf
6 | go to website
7 | hello
8 | hello
9 |
10 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | "paths": {
9 | "purgecss": ["../purgecss/src"],
10 | }
11 | },
12 | "include": ["src", "./../purgecss/src/*"],
13 | "exclude": ["./lib"]
14 | }
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/style.css:
--------------------------------------------------------------------------------
1 | .hello {
2 | color: red;
3 | }
4 |
5 | .unused {
6 | color: blue;
7 | }
8 |
9 | .safelisted {
10 | color: green;
11 | }
12 |
13 | .safelistedPattern {
14 | color: rebeccapurple;
15 | }
16 |
17 | .safelistedPatternChildren > p a {
18 | color: orange;
19 | }
20 |
21 | md\:w-2\/3 {
22 | color: red;
23 | }
24 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: blue;
3 | }
4 |
5 | .random {
6 | color: green;
7 | }
8 |
9 | #yep {
10 | color: red;
11 | }
12 |
13 | button {
14 | color: rebeccapurple;
15 | }
16 |
17 | .nav-blue {
18 | background-color: blue;
19 | }
20 |
21 | .nav-red {
22 | background-color: red;
23 | }
24 |
25 | [data-v-test] {
26 | color: green;
27 | }
28 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | "paths": {
9 | "purgecss": ["../purgecss/src"],
10 | }
11 | },
12 | "include": ["./src/**/*", "./../purgecss/src/*"],
13 | "exclude": ["./lib"]
14 | }
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | "paths": {
9 | "purgecss": ["../purgecss/src"],
10 | }
11 | },
12 | "include": ["./src/**/*", "./../purgecss/src/*"],
13 | "exclude": ["./lib"]
14 | }
--------------------------------------------------------------------------------
/scripts/verify-commit.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 |
3 | const message = fs.readFileSync(process.env.HUSKY_GIT_PARAMS!, "utf-8").trim();
4 |
5 | const commitRE =
6 | /^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip)(\(.+\))?: .{1,50}/;
7 |
8 | if (!commitRE.test(message)) {
9 | console.log();
10 | console.error(`invalid commit message format.`);
11 | process.exit(1);
12 | }
13 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.css:
--------------------------------------------------------------------------------
1 | /* purgecss start ignore */
2 | h1 {
3 | color: blue;
4 | }
5 |
6 | h3 {
7 | color: green;
8 | }
9 | /* purgecss end ignore */
10 |
11 | h4 {
12 | color: purple;
13 | }
14 |
15 | /* purgecss start ignore */
16 |
17 | /* do not purge */
18 |
19 | h5 {
20 | color: pink;
21 | }
22 |
23 | h6 {
24 | color: lightcoral;
25 | }
26 |
27 | /* purgecss end ignore */
--------------------------------------------------------------------------------
/packages/purgecss/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const IGNORE_ANNOTATION_CURRENT = "purgecss ignore current";
2 | export const IGNORE_ANNOTATION_NEXT = "purgecss ignore";
3 | export const IGNORE_ANNOTATION_START = "purgecss start ignore";
4 | export const IGNORE_ANNOTATION_END = "purgecss end ignore";
5 | export const CONFIG_FILENAME = "purgecss.config.js";
6 |
7 | // Error Message
8 | export const ERROR_CONFIG_FILE_LOADING = "Error loading the config file";
9 |
--------------------------------------------------------------------------------
/packages/purgecss/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": ".temp/",
7 | "baseUrl": ".",
8 | "paths": {
9 | "purgecss-from-html": ["../purgecss-from-html/src"],
10 | }
11 | },
12 | "include": ["src", "package.json", "__tests__"],
13 | "exclude": ["lib"]
14 | }
--------------------------------------------------------------------------------
/packages/purgecss-from-tsx/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | "rootDir": "./../../",
9 | "paths": {
10 | "@fullhuman/purgecss-from-jsx": ["../purgecss-from-jsx/src"]
11 | }
12 | },
13 | "include": ["src", "../purgecss-from-jsx/src", "./../../types/**/*"],
14 | "exclude": ["./lib"]
15 | }
--------------------------------------------------------------------------------
/packages/gulp-purgecss/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./lib",
6 | "declarationDir": "./.temp/",
7 | "baseUrl": ".",
8 | "paths": {
9 | "purgecss": ["../purgecss/src"],
10 | },
11 | "typeRoots": ["./../../types", "./../../node_modules/@types"]
12 | },
13 | "include": ["src", "./../purgecss/src/*", "./../../types/**/*"],
14 | "exclude": ["./lib"]
15 | }
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/legacy.css:
--------------------------------------------------------------------------------
1 | /*!******************************************************************************!*\
2 | !*** css ../../../../../node_modules/css-loader/dist/cjs.js!./src/style.css ***!
3 | \******************************************************************************/
4 | .legacy-unused-class {
5 | color: red;
6 | }
7 |
8 | .unused-too {
9 | color: blue;
10 | }
11 |
12 | .hello {
13 | color: red;
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/blocklist.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: blue;
3 | }
4 |
5 | .random {
6 | color: green;
7 | }
8 |
9 | #yep {
10 | color: red;
11 | }
12 |
13 | button {
14 | color: rebeccapurple;
15 | }
16 |
17 | .nav-blue {
18 | background-color: blue;
19 | }
20 |
21 | .nav-red {
22 | background-color: red;
23 | }
24 |
25 | [data-v-test] {
26 | color: green;
27 | }
28 |
29 | .nav-blue .random {
30 | color: green;
31 | }
--------------------------------------------------------------------------------
/docs/.vuepress/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/docs/.vuepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | import { getDirname, path } from "@vuepress/utils";
2 | import { defaultTheme, DefaultThemeOptions } from "@vuepress/theme-default";
3 |
4 | const __dirname = getDirname(import.meta.url);
5 |
6 | const localTheme = (options: DefaultThemeOptions) => {
7 | return {
8 | name: "vuepress-theme-local",
9 | extends: defaultTheme(options),
10 | // path to the client config of your theme
11 | clientConfigFile: path.resolve(__dirname, "client.ts"),
12 | };
13 | };
14 |
15 | export default localTheme;
16 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/bundle.css:
--------------------------------------------------------------------------------
1 | /*!****************************************************************************************************!*\
2 | !*** css ../../../../../node_modules/css-loader/dist/cjs.js!./src/style_that_we_want_to_purge.css ***!
3 | \****************************************************************************************************/
4 | .hello {
5 | color: red;
6 | }
7 |
8 | .safelisted {
9 | color: green;
10 | }
11 |
12 | md\:w-2\/3 {
13 | color: red;
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css:
--------------------------------------------------------------------------------
1 | /*!******************************************************************************!*\
2 | !*** css ../../../../../node_modules/css-loader/dist/cjs.js!./src/style.css ***!
3 | \******************************************************************************/
4 | .hello {
5 | color: red;
6 | }
7 |
8 | .safelisted {
9 | color: green;
10 | }
11 |
12 | md\:w-2\/3 {
13 | color: red;
14 | }
15 |
16 |
17 | /*# sourceMappingURL=styles.css.map */
18 | /*# sourceMappingURL=styles.css.map*/
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: monthly
7 | open-pull-requests-limit: 10
8 | reviewers:
9 | - Ffloriel
10 | ignore:
11 | - dependency-name: commander
12 | versions:
13 | - 7.0.0
14 | - 7.1.0
15 | - dependency-name: eslint
16 | versions:
17 | - 7.21.0
18 | - dependency-name: "@typescript-eslint/parser"
19 | versions:
20 | - 4.14.1
21 | - package-ecosystem: github-actions
22 | directory: "/"
23 | schedule:
24 | interval: monthly
25 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple/purgecss.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const glob = require("fast-glob");
3 |
4 | const customExtractor = (content) => {
5 | const res = content.match(/[A-z0-9-:/]+/g) || [];
6 | return res;
7 | };
8 |
9 | const PATHS = {
10 | src: path.join(__dirname, "src"),
11 | };
12 |
13 | module.exports = {
14 | paths: glob.sync(`${PATHS.src}/*`),
15 | safelist: ["safelisted"],
16 | extractors: [
17 | {
18 | extractor: customExtractor,
19 | extensions: ["html", "js"],
20 | },
21 | ],
22 | }
23 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.css:
--------------------------------------------------------------------------------
1 | .some-item:nth-child(2n + 1) {
2 | color: green;
3 | }
4 |
5 | .some-item:nth-child(2n) {
6 | font-size: 1rem;
7 | }
8 |
9 | .some-item:nth-of-type(n+3) {
10 | color: goldenrod;
11 | }
12 |
13 | .some-item:nth-of-type(-1n+6) {
14 | color: brown;
15 | }
16 |
17 | .some-item:nth-of-type(-n+6) {
18 | color: brown;
19 | }
20 |
21 | .unused:only-child() {
22 | color: red;
23 | }
24 |
25 | .used:only-child() {
26 | color: blue;
27 | }
28 |
29 | .odd-item:nth-child(odd) {
30 | color: grey;
31 | }
32 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/chaining-rules/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TODO supply a title
5 |
6 |
7 |
8 |
9 |
10 |
11 | TODO write content
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/where.css:
--------------------------------------------------------------------------------
1 | .root :where(.a, .b) .unused {
2 | color: red;
3 | }
4 |
5 | .root :where(.a, .unused) .c {
6 | color: blue;
7 | }
8 |
9 | .root :where(.a, .b) .c :where(.unused, .unused2) {
10 | color: green;
11 | }
12 |
13 | .root:where(.unused) .c {
14 | color: rebeccapurple;
15 | }
16 |
17 | .root:where(.a) .c {
18 | color: cyan;
19 | }
20 |
21 | .root :where(.unused) .c,
22 | .root :where(.a, .b) .c :where(.unused, .unused2) {
23 | display: flex;
24 | }
25 |
26 | .\[\&\:where\(\.a\)\]\:text-black:where(.a) {
27 | color: black;
28 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "sourceMap": true,
5 | "target": "es2019",
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "strict": true,
9 | "allowJs": false,
10 | "noUnusedLocals": true,
11 | "experimentalDecorators": true,
12 | "resolveJsonModule": true,
13 | "esModuleInterop": true,
14 | "removeComments": false,
15 | "lib": ["esnext", "dom"],
16 | "types": ["jest"],
17 | "rootDir": "."
18 | },
19 | "ts-node": {
20 | "compilerOptions": {
21 | "module": "CommonJS"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/expected/styles.css:
--------------------------------------------------------------------------------
1 | /*!******************************************************************************!*\
2 | !*** css ../../../../../node_modules/css-loader/dist/cjs.js!./src/style.css ***!
3 | \******************************************************************************/
4 | .hello {
5 | color: red;
6 | }
7 |
8 | .safelisted {
9 | color: green;
10 | }
11 |
12 | .safelistedPattern {
13 | color: rebeccapurple;
14 | }
15 |
16 | .safelistedPatternChildren > p a {
17 | color: orange;
18 | }
19 |
20 | md\:w-2\/3 {
21 | color: red;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/media-queries/media_queries.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 600px) {
2 | div.media-class {
3 | color: black;
4 | }
5 |
6 | .alone, .unused-class {
7 | color: black;
8 | }
9 |
10 | #id-in-media {
11 | color: black;
12 | }
13 |
14 | body {
15 | color: black;
16 | }
17 | }
18 |
19 | @media (max-width: 960px){
20 | .alone, .unused-class {
21 | color: black;
22 | }
23 | *,
24 | :before,
25 | :after {
26 | background: black;
27 | }
28 | }
29 |
30 | @media (max-width: 66666px){
31 | .unused-class, .unused-class2 {
32 | color: black;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/purgecss-from-pug/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("purgecss-from-pug", ["pug-lexer"]);
15 | await buildRollup(rollupConfig);
16 | await extractAPI(__dirname);
17 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
18 | recursive: true,
19 | force: true,
20 | });
21 | })();
22 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/is.css:
--------------------------------------------------------------------------------
1 | .root :is(.a, .b) .unused {
2 | color: red;
3 | }
4 |
5 | .root :is(.a, .unused) .c {
6 | color: blue;
7 | }
8 |
9 | .root :is(.a, .b) .c :is(.unused, .unused2) {
10 | color: green;
11 | }
12 |
13 | .root:is(.unused) .c {
14 | color: rebeccapurple;
15 | }
16 |
17 | .root:is(.a) .c {
18 | color: cyan;
19 | }
20 |
21 | .root :is(.unused) .c,
22 | .root :is(.a, .b) .c :is(.unused, .unused2) {
23 | display: flex;
24 | }
25 |
26 | .\[\&\:is\(\.a\)\]\:text-black:is(.a) {
27 | color: black;
28 | }
29 |
30 | :is(.b) {
31 | color: black;
32 | }
33 |
34 | :is(.unused) {
35 | color: chartreuse;
36 | }
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 | - 1
20 | - 2
21 | - 3
22 | - 4
23 | - 5
24 | - 6
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: ['Ffloriel']
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("postcss-purgecss", [
15 | "postcss",
16 | "purgecss",
17 | "path",
18 | ]);
19 | await buildRollup(rollupConfig);
20 | await extractAPI(__dirname);
21 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
22 | recursive: true,
23 | force: true,
24 | });
25 | })();
26 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "name": "vscode-jest-tests",
10 | "request": "launch",
11 | "args": [
12 | "--runInBand"
13 | ],
14 | "cwd": "${workspaceFolder}",
15 | "console": "integratedTerminal",
16 | "internalConsoleOptions": "neverOpen",
17 | "disableOptimisticBPs": true,
18 | "program": "${workspaceFolder}/node_modules/jest/bin/jest"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/packages/purgecss-from-html/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("purgecss-from-html", [
15 | "parse5",
16 | "parse5-htmlparser2-tree-adapter",
17 | ]);
18 | await buildRollup(rollupConfig);
19 | await extractAPI(__dirname);
20 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
21 | recursive: true,
22 | force: true,
23 | });
24 | })();
25 |
--------------------------------------------------------------------------------
/packages/purgecss-from-pug/README.md:
--------------------------------------------------------------------------------
1 | # `purgecss-from-pug`
2 |
3 | A PurgeCSS extractor for PUG files that automatically generates a list of used CSS selectors from your PUG files. This extractor can be used with PurgeCSS to eliminate unused CSS and reduce the size of your production builds.
4 |
5 | ## Usage
6 |
7 | ```
8 | import { PurgeCSS } from 'purgecss'
9 | import { purgeCSSFromPug } from 'purgecss-from-pug'
10 | const options = {
11 | content: [], // files to extract the selectors from
12 | css: [], // css
13 | extractors: [
14 | {
15 | extractor: purgeCSSFromPug,
16 | extensions: ['pug']
17 | },
18 | ]
19 | }
20 |
21 | const purgecss = await new PurgeCSS().purge();
22 | ```
23 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/chaining-rules/index.css:
--------------------------------------------------------------------------------
1 | .parent1 a{
2 | color:red;
3 | }
4 | .parent1 p{
5 | color:red;
6 | }
7 | .parent1 h1{
8 | color:red;
9 | }
10 | .parent1.def{
11 | color:red;
12 | }
13 | .parent1.d22222ef{
14 | color:red;
15 | }
16 | .parent1.d222222222222222222ef{
17 | color:red;
18 | }
19 | .parent.def1{
20 | color:red;
21 | }
22 | .parent.def2{
23 | color:red;
24 | }
25 | .parent.de1{
26 | color:red;
27 | }
28 | .parent.d3ef1{
29 | color:red;
30 | }
31 | .parent.d33ef1{
32 | color:red;
33 | }
34 | .parent2.def{
35 | color:red;
36 | }
37 | .parent3.def1{
38 | color:red;
39 | }
40 |
41 | [href^='#'] {
42 | color: green;
43 | }
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary-color: blue;
3 | --secondary-color: indigo;
4 | --tertiary-color: aqua;
5 | --unused-color: violet;
6 | --used-color: rebeccapurple;
7 | --accent-color: orange;
8 | }
9 |
10 | .button {
11 | --button-color: var(--tertiary-color);
12 | --border-color: linear-gradient(to top, var(--secondary-color), var(--used-color, white));
13 |
14 | background-color: var(--primary-color);
15 | color: var(--accent-color);
16 | border-color: var(--border-color);
17 | }
18 |
19 | .button:focus {
20 | background-color: var(--accent-color);
21 | color: var(--primary-color);
22 | }
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/utils.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 |
3 | export const ROOT_TEST_EXAMPLES = "./__tests__/test_examples/";
4 | export const CLI_TEST_FOLDER = path.resolve(
5 | __dirname,
6 | "./test_examples/cli/simple/",
7 | );
8 |
9 | export function findInCSS(
10 | expect: jest.Expect,
11 | selectors: string[],
12 | css: string,
13 | ): void {
14 | selectors.forEach((selector) => {
15 | expect(css.includes(selector)).toBe(true);
16 | });
17 | }
18 |
19 | export function notFindInCSS(
20 | expect: jest.Expect,
21 | selectors: string[],
22 | css: string,
23 | ): void {
24 | selectors.forEach((selector) => {
25 | expect(css.includes(selector)).toBe(false);
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("rollup-plugin-purgecss", [
15 | "fs",
16 | "rollup-pluginutils",
17 | "purgecss",
18 | ]);
19 | await buildRollup(rollupConfig);
20 | await extractAPI(__dirname);
21 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
22 | recursive: true,
23 | force: true,
24 | });
25 | })();
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: Feature request
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/packages/purgecss-from-tsx/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("purgecss-from-tsx", [
15 | "acorn",
16 | "@fullhuman/purgecss-from-jsx",
17 | "typescript",
18 | ]);
19 | await buildRollup(rollupConfig);
20 | await extractAPI(__dirname);
21 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
22 | recursive: true,
23 | force: true,
24 | });
25 | })();
26 |
--------------------------------------------------------------------------------
/packages/purgecss-from-jsx/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("purgecss-from-jsx", [
15 | "acorn",
16 | "acorn-walk",
17 | "acorn-jsx",
18 | "acorn-jsx-walk",
19 | ]);
20 | await buildRollup(rollupConfig);
21 | await extractAPI(__dirname);
22 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
23 | recursive: true,
24 | force: true,
25 | });
26 | })();
27 |
--------------------------------------------------------------------------------
/packages/purgecss-from-jsx/__tests__/data.ts:
--------------------------------------------------------------------------------
1 | export const TEST_1_CONTENT = `
2 | import React from "react";
3 |
4 | class MyComponent extends React.Component {
5 | render() {
6 | return (
7 |
8 | Well
9 |
10 |
11 |
12 |
13 | );
14 | }
15 | }
16 |
17 | export default MyComponent;
18 | `;
19 |
20 | export const TEST_1_TAG = ["div", "a", "input"];
21 |
22 | export const TEST_1_CLASS = ["test-container", "test-footer", "a-link"];
23 |
24 | export const TEST_1_ID = ["a-link", "blo"];
25 |
--------------------------------------------------------------------------------
/packages/purgecss/src/options.ts:
--------------------------------------------------------------------------------
1 | import { ExtractorResult, Options } from "./types/";
2 |
3 | /**
4 | * @public
5 | */
6 | export const defaultOptions: Options = {
7 | css: [],
8 | content: [],
9 | defaultExtractor: (content: string): ExtractorResult =>
10 | content.match(/[A-Za-z0-9_-]+/g) || [],
11 | extractors: [],
12 | fontFace: false,
13 | keyframes: false,
14 | rejected: false,
15 | rejectedCss: false,
16 | sourceMap: false,
17 | stdin: false,
18 | stdout: false,
19 | variables: false,
20 | safelist: {
21 | standard: [],
22 | deep: [],
23 | greedy: [],
24 | variables: [],
25 | keyframes: [],
26 | },
27 | blocklist: [],
28 | skippedContentGlobs: [],
29 | dynamicAttributes: [],
30 | };
31 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | This is the list of versions of PurgeCSS which are currently being supported with security updates.
6 |
7 | | Version | Supported |
8 | | ------- | ------------------ |
9 | | 5.0.x | :white_check_mark: |
10 | | < 5.0 | :x: |
11 |
12 | ## Reporting a Vulnerability
13 |
14 | To report a vulnerability, please report it directly on GitHub. If you are not able to report it on GitHub,
15 | you can send an email with the details to contact@full-human.com. The vulnerability report must include a
16 | proof-of-concept of the exploit, or at least a few pointers that can help us assess the risk level.
17 | Your report will be acknowledged within 2 business days.
18 |
--------------------------------------------------------------------------------
/packages/gulp-purgecss/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("gulp-purgecss", [
15 | "through2",
16 | "plugin-error",
17 | "purgecss",
18 | "glob",
19 | "vinyl-sourcemaps-apply",
20 | ]);
21 | await buildRollup(rollupConfig);
22 | await extractAPI(__dirname);
23 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
24 | recursive: true,
25 | force: true,
26 | });
27 | })();
28 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 |
9 | (async () => {
10 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
11 | recursive: true,
12 | force: true,
13 | });
14 | const rollupConfig = createRollupConfig("purgecss-webpack-plugin", [
15 | "fs",
16 | "path",
17 | "purgecss",
18 | "webpack",
19 | "webpack-sources",
20 | ]);
21 | await buildRollup(rollupConfig);
22 | await extractAPI(__dirname);
23 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
24 | recursive: true,
25 | force: true,
26 | });
27 | })();
28 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/chaining-rules.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "./../src/index";
2 |
3 | import { ROOT_TEST_EXAMPLES, notFindInCSS } from "./utils";
4 |
5 | describe("chaining rules", () => {
6 | let purgedCSS: string;
7 | beforeAll(async () => {
8 | const resultsPurge = await new PurgeCSS().purge({
9 | content: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.html`],
10 | css: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.css`],
11 | });
12 | purgedCSS = resultsPurge[0].css;
13 | });
14 | it("keeps parent1 selector", () => {
15 | expect(purgedCSS.includes("parent1")).toBe(true);
16 | });
17 | it("removes parent3, d33ef1, .parent2", () => {
18 | notFindInCSS(expect, ["parent3", "d33ef1", "parent2"], purgedCSS);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/keyframes/index.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes rotateAni {
2 | from {
3 | transform: rotate(0deg);
4 | }
5 | to {
6 | transform: rotate(359deg);
7 | }
8 | }
9 | @keyframes rotateAni {
10 | from {
11 | transform: rotate(0deg);
12 | }
13 | to {
14 | transform: rotate(359deg);
15 | }
16 | }
17 |
18 | .rotate {
19 | animation: rotateAni 200ms ease-out both;
20 | }
21 |
22 | @-webkit-keyframes flashAni
23 | {
24 | from, 50%, to {
25 | opacity: 1;
26 | }
27 |
28 | 25%, 75% {
29 | opacity: 0;
30 | }
31 | }
32 | @keyframes flashAni
33 | {
34 | from, 50%, to {
35 | opacity: 1;
36 | }
37 |
38 | 25%, 75% {
39 | opacity: 0;
40 | }
41 | }
42 |
43 | .flash {
44 | animation-name: flashAni;
45 | }
--------------------------------------------------------------------------------
/.github/workflows/stale-issues.yml:
--------------------------------------------------------------------------------
1 | name: 'Close stale issues and PRs'
2 | on:
3 | schedule:
4 | - cron: '0 0 * * *'
5 |
6 | jobs:
7 | stale:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/stale@v10
11 | with:
12 | stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
13 | stale-pr-message: 'This PR is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
14 | days-before-stale: 180
15 | days-before-close: 5
16 | exempt-all-issue-assignees: true
17 | exempt-all-issue-milestones: true
18 | exempt-issue-labels: bug
--------------------------------------------------------------------------------
/packages/postcss-purgecss/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | UserDefinedOptions as PurgeCSSUserDefinedOptions,
3 | RawContent,
4 | } from "purgecss";
5 |
6 | export {
7 | UserDefinedOptions as PurgeCSSUserDefinedOptions,
8 | RawContent,
9 | UserDefinedSafelist,
10 | ComplexSafelist,
11 | ExtractorFunction,
12 | ExtractorResult,
13 | StringRegExpArray,
14 | RawCSS,
15 | Extractors,
16 | ExtractorResultDetailed,
17 | } from "purgecss";
18 |
19 | /**
20 | * {@inheritDoc purgecss#UserDefinedOptions}
21 | *
22 | * @public
23 | */
24 | export interface UserDefinedOptions
25 | extends Omit {
26 | content?: PurgeCSSUserDefinedOptions["content"];
27 | contentFunction?: (sourceFile: string) => Array;
28 | }
29 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | RawContent,
3 | UserDefinedOptions as PurgeCSSUserDefinedOptions,
4 | } from "purgecss";
5 |
6 | /**
7 | * @public
8 | */
9 | export type OutputFunction = (css: string, styles: string[]) => void;
10 |
11 | /**
12 | * {@inheritDoc purgecss#UserDefinedOptions}
13 | *
14 | * @public
15 | */
16 | export type UserDefinedOptions = Omit<
17 | PurgeCSSUserDefinedOptions,
18 | "css" | "output" | "rejectedCss"
19 | > & {
20 | contentFunction?: (sourceFile: string) => Array;
21 | output: PurgeCSSUserDefinedOptions["output"] | OutputFunction | boolean;
22 | insert?: boolean;
23 | include?: string | RegExp | (string | RegExp)[];
24 | exclude?: string | RegExp | (string | RegExp)[];
25 | dest?: string;
26 | };
27 |
--------------------------------------------------------------------------------
/packages/vue-cli-plugin-purgecss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fullhuman/vue-cli-plugin-purgecss",
3 | "version": "7.0.2",
4 | "description": "vue-cli plugin to add PurgeCSS",
5 | "author": "Ffloriel",
6 | "homepage": "https://purgecss.com",
7 | "main": "index.js",
8 | "keywords": [
9 | "vue-cli",
10 | "vue-cli-3",
11 | "purgecss",
12 | "plugin",
13 | "remove",
14 | "unused",
15 | "css",
16 | "optimization"
17 | ],
18 | "license": "MIT",
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/FullHuman/purgecss.git"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/FullHuman/purgecss/issues"
25 | },
26 | "publishConfig": {
27 | "access": "public",
28 | "registry": "https://registry.npmjs.org/"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import type { JestConfigWithTsJest } from "ts-jest";
2 |
3 | const config: JestConfigWithTsJest = {
4 | preset: "ts-jest",
5 | coverageDirectory: "coverage",
6 | coverageReporters: ["html", "lcov", "text"],
7 | collectCoverageFrom: ["/src/**/*.ts"],
8 | moduleFileExtensions: ["ts", "tsx", "js", "json"],
9 | moduleNameMapper: {
10 | "^purgecss-from-html$": "/../purgecss-from-html/src",
11 | },
12 | testMatch: ["/__tests__/**/*test.ts"],
13 | transform: {
14 | "^.+.tsx?$": ["ts-jest", {}],
15 | },
16 | };
17 |
18 | export function createConfig(
19 | rootDir: string,
20 | displayName: string,
21 | ): JestConfigWithTsJest {
22 | return {
23 | ...config,
24 | rootDir,
25 | displayName,
26 | };
27 | }
28 |
29 | export default config;
30 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/build.ts:
--------------------------------------------------------------------------------
1 | import typescript from "@rollup/plugin-typescript";
2 | import { buildRollup, createRollupConfig } from "../../scripts/build";
3 | import { promises as asyncFs } from "fs";
4 | import * as path from "path";
5 | import { RollupOptions } from "rollup";
6 |
7 | (async () => {
8 | await asyncFs.rm(path.resolve(__dirname, "tasks"), {
9 | recursive: true,
10 | force: true,
11 | });
12 | const rollupConfig: RollupOptions = {
13 | ...createRollupConfig("grunt-purgecss", ["purgecss"]),
14 | output: [
15 | {
16 | exports: "auto",
17 | file: "./tasks/purgecss.js",
18 | format: "cjs",
19 | },
20 | ],
21 | plugins: [
22 | typescript({
23 | outDir: "./tasks",
24 | }),
25 | ],
26 | };
27 | await buildRollup(rollupConfig);
28 | })();
29 |
--------------------------------------------------------------------------------
/packages/purgecss-with-wordpress/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "purgecss-with-wordpress",
3 | "version": "7.0.2",
4 | "description": "PurgeCSS with wordpress",
5 | "author": "Ffloriel",
6 | "homepage": "https://purgecss.com",
7 | "keywords": [
8 | "wordpress",
9 | "optimize",
10 | "optimization",
11 | "remove",
12 | "unused",
13 | "css",
14 | "html",
15 | "rules",
16 | "purge",
17 | "uncss",
18 | "purify"
19 | ],
20 | "license": "MIT",
21 | "main": "index.js",
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/FullHuman/purgecss.git"
25 | },
26 | "scripts": {},
27 | "bugs": {
28 | "url": "https://github.com/FullHuman/purgecss/issues"
29 | },
30 | "publishConfig": {
31 | "access": "public",
32 | "registry": "https://registry.npmjs.org/"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/purgecss-from-tsx/src/index.ts:
--------------------------------------------------------------------------------
1 | import purgeFromJsx from "@fullhuman/purgecss-from-jsx";
2 | import acorn from "acorn";
3 | import * as ts from "typescript";
4 |
5 | /**
6 | * Create function to extract selectors from tsx code
7 | *
8 | * @param options - acorn options
9 | * @returns the function to extract selectors from tsx code
10 | *
11 | * @public
12 | */
13 | function purgeFromTsx(options?: {
14 | acornOptions?: acorn.Options;
15 | tsOptions?: ts.CompilerOptions;
16 | }): (content: string) => string[] {
17 | return (content: string): string[] => {
18 | return purgeFromJsx(options?.acornOptions)(
19 | ts.transpileModule(content, {
20 | compilerOptions: {
21 | jsx: ts.JsxEmit.Preserve,
22 | ...options?.tsOptions,
23 | },
24 | }).outputText,
25 | );
26 | };
27 | }
28 |
29 | export default purgeFromTsx;
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Before you open an issue, please check if a similar issue already exists or has been closed before.
2 |
3 | ### When reporting a bug, please try to include the following:
4 | - [ ] A descriptive title
5 | - [ ] An *isolated* way to reproduce the behavior (example: GitHub repository with code isolated to the issue that anyone can clone to observe the problem)
6 | - [ ] What package and version you're using, and the platform(s) you're running it on
7 | - [ ] The behavior you expect to see, and the actual behavior
8 |
9 | ### When you open an issue for a feature request, please add as much detail as possible:
10 | - [ ] A descriptive title
11 | - [ ] A description of the problem you're trying to solve, including *why* you think this is a problem
12 | - [ ] An overview of the suggested solution
13 | - [ ] If the feature changes current behavior, reasons why your solution is better
14 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/skipped-content.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "./../src/index";
2 |
3 | import { ROOT_TEST_EXAMPLES } from "./utils";
4 |
5 | describe("skipped-content", () => {
6 | let purgedCSS: string;
7 |
8 | beforeAll(async () => {
9 | const resultsPurge = await new PurgeCSS().purge({
10 | content: [`${ROOT_TEST_EXAMPLES}skipped-content/**/*.html`],
11 | css: [`${ROOT_TEST_EXAMPLES}skipped-content/simple.css`],
12 | skippedContentGlobs: [
13 | `${ROOT_TEST_EXAMPLES}skipped-content/skippedFolder/**`,
14 | ],
15 | });
16 | purgedCSS = resultsPurge[0].css;
17 | });
18 |
19 | it("purges appropriate CSS rules when skippedContentGlobs is set", () => {
20 | expect(purgedCSS.includes(".red")).toBe(true);
21 | expect(purgedCSS.includes(".black")).toBe(true);
22 | expect(purgedCSS.includes(".green")).toBe(false);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = grunt => {
2 |
3 | // Project configuration.
4 | grunt.initConfig({
5 | // Configuration to be run (and then tested).
6 | purgecss: {
7 | simple: {
8 | options: {
9 | content: ['./__tests__/fixtures/src/simple/**/*.html']
10 | },
11 | files: {
12 | '__tests__/tmp/menu.css': ['__tests__/fixtures/src/menu.css'],
13 | '__tests__/tmp/profile.css': ['__tests__/fixtures/src/profile.css'],
14 | '__tests__/tmp/footer.css': ['__tests__/fixtures/src/footer.css'],
15 | '__tests__/tmp/simple.css': ['__tests__/fixtures/src/simple/simple.css']
16 | }
17 | }
18 | }
19 | });
20 |
21 | // Actually load this plugin's task(s).
22 | grunt.loadTasks('tasks');
23 |
24 | // By default, lint and run all tests.
25 | grunt.registerTask('default', ['purgecss']);
26 |
27 | };
28 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js:
--------------------------------------------------------------------------------
1 | const MiniCssExtractPlugin = require("mini-css-extract-plugin");
2 | const { PurgeCSSPlugin } = require("../../../src/");
3 |
4 | module.exports = {
5 | mode: "development",
6 | devtool: "source-map",
7 | optimization: {
8 | splitChunks: {
9 | cacheGroups: {
10 | styles: {
11 | name: "styles",
12 | type: "css/mini-extract",
13 | test: /\.css$/,
14 | chunks: "all",
15 | enforce: true,
16 | },
17 | },
18 | },
19 | },
20 | entry: "./src/index.js",
21 | module: {
22 | rules: [
23 | {
24 | test: /\.css$/,
25 | use: [MiniCssExtractPlugin.loader, "css-loader"],
26 | },
27 | ],
28 | },
29 | plugins: [
30 | new MiniCssExtractPlugin({
31 | filename: "[name].css",
32 | }),
33 | new PurgeCSSPlugin({}),
34 | ],
35 | };
36 |
--------------------------------------------------------------------------------
/packages/purgecss-from-pug/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "purgecss-from-pug",
3 | "version": "7.0.2",
4 | "description": "Pug extractor for PurgeCSS",
5 | "author": "Ffloriel",
6 | "homepage": "https://github.com/FullHuman/purgecss#readme",
7 | "license": "ISC",
8 | "main": "lib/purgecss-from-pug.js",
9 | "directories": {
10 | "lib": "lib",
11 | "test": "__tests__"
12 | },
13 | "files": [
14 | "lib"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/FullHuman/purgecss.git"
19 | },
20 | "scripts": {
21 | "build": "ts-node build.ts",
22 | "test": "jest"
23 | },
24 | "bugs": {
25 | "url": "https://github.com/FullHuman/purgecss/issues"
26 | },
27 | "devDependencies": {
28 | "pug-lexer": "^5.0.0",
29 | "pug-parser": "^6.0.0"
30 | },
31 | "publishConfig": {
32 | "access": "public",
33 | "registry": "https://registry.npmjs.org/"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/globs.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "../src/";
2 | import { ROOT_TEST_EXAMPLES } from "./utils";
3 |
4 | describe("Glob", () => {
5 | it("glob expressions in content/css work", async () => {
6 | const resultsPurge = await new PurgeCSS().purge({
7 | content: [`${ROOT_TEST_EXAMPLES}comments/**/*.{js,html,json,svg}`],
8 | css: [`${ROOT_TEST_EXAMPLES}comments/**/*.css`],
9 | });
10 |
11 | expect(resultsPurge[0].file).toBe(
12 | `${ROOT_TEST_EXAMPLES}comments/ignore_comment.css`,
13 | );
14 | expect(resultsPurge[0].css.includes("/* purgecss ignore */")).toBe(false);
15 | expect(resultsPurge[0].css.includes("/* purgecss ignore current */")).toBe(
16 | false,
17 | );
18 |
19 | expect(resultsPurge[1].file).toBe(
20 | `${ROOT_TEST_EXAMPLES}comments/ignore_comment_range.css`,
21 | );
22 | expect(resultsPurge[1].css.includes("h4")).toBe(false);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | name: Build / Test
2 | permissions:
3 | contents: read
4 |
5 | on: [push, pull_request]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 |
11 | strategy:
12 | matrix:
13 | node-version: [20.x]
14 |
15 | steps:
16 | - uses: actions/checkout@v6
17 | - name: Use Node.js ${{ matrix.node-version }}
18 | uses: actions/setup-node@v6
19 | with:
20 | node-version: ${{ matrix.node-version }}
21 | - name: npm install, lerna bootstrap, run build and test
22 | run: |
23 | npm i -g npm
24 | npm i
25 | npm run build --if-present
26 | npm run lint
27 | npm run test -- -- --coverage
28 | - name: Coveralls GitHub Action
29 | uses: coverallsapp/github-action@v2.3.7
30 | with:
31 | github-token: ${{ secrets.GITHUB_TOKEN }}
32 | base-path: ./packages/purgecss
33 | env:
34 | CI: true
35 |
36 |
--------------------------------------------------------------------------------
/packages/purgecss-from-html/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "purgecss-from-html",
3 | "version": "7.0.2",
4 | "description": "HTML extractor for PurgeCSS",
5 | "author": "Ffloriel",
6 | "homepage": "https://github.com/FullHuman/purgecss#readme",
7 | "license": "ISC",
8 | "main": "lib/purgecss-from-html.js",
9 | "directories": {
10 | "lib": "lib",
11 | "test": "__tests__"
12 | },
13 | "files": [
14 | "lib"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/FullHuman/purgecss.git"
19 | },
20 | "scripts": {
21 | "build": "ts-node build.ts",
22 | "test": "jest"
23 | },
24 | "bugs": {
25 | "url": "https://github.com/FullHuman/purgecss/issues"
26 | },
27 | "dependencies": {
28 | "parse5": "^7.1.2",
29 | "parse5-htmlparser2-tree-adapter": "^7.0.0"
30 | },
31 | "publishConfig": {
32 | "access": "public",
33 | "registry": "https://registry.npmjs.org/"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/cli/cli-console-output.test.ts:
--------------------------------------------------------------------------------
1 | import { Command } from "commander";
2 | import { parseCommandOptions, run } from "../../src/bin";
3 | import { CLI_TEST_FOLDER } from "../utils";
4 |
5 | describe("PurgeCSS CLI console output", () => {
6 | const program = parseCommandOptions(new Command());
7 |
8 | it("should log the result if output is not specified", async () => {
9 | const originalConsoleLog = console.log;
10 | console.log = jest.fn();
11 | program.parse([
12 | "purgecss",
13 | "",
14 | "--content",
15 | `${CLI_TEST_FOLDER}/src/content.html`,
16 | `${CLI_TEST_FOLDER}/src/*.js`,
17 | "--css",
18 | `${CLI_TEST_FOLDER}/src/style.css`,
19 | ]);
20 | await run(program);
21 | expect(console.log).toHaveBeenCalledWith(
22 | `[{"css":".hello {\\n color: red;\\n}\\n","file":"${CLI_TEST_FOLDER}/src/style.css"}]`,
23 | );
24 | console.log = originalConsoleLog;
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/__tests__/index.test.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | import { rollup } from "rollup";
4 | import purgecss from "./../src/";
5 |
6 | describe("rollup-plugin-purgecss", () => {
7 | it("remove unused css", async () => {
8 | const bundle = await rollup({
9 | input: path.resolve(__dirname, "fixtures/basic/index.js"),
10 | plugins: [
11 | purgecss({
12 | content: [path.resolve(__dirname, "assets/test_a.html")],
13 | output: path.resolve(__dirname, "assets/actual_a.css"),
14 | }),
15 | ],
16 | });
17 | await bundle.generate({ format: "cjs", exports: "auto" });
18 |
19 | const actualA = fs
20 | .readFileSync(path.resolve(__dirname, "assets/actual_a.css"))
21 | .toString();
22 | const expectA = fs
23 | .readFileSync(path.resolve(__dirname, "assets/expect_a.css"))
24 | .toString();
25 | expect(actualA).toEqual(expectA);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/font-faces.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "./../src/index";
2 | import { ROOT_TEST_EXAMPLES } from "./utils";
3 |
4 | describe("purge unused font-face", () => {
5 | let purgedCSS: string;
6 | beforeAll(async () => {
7 | const resultPurge = await new PurgeCSS().purge({
8 | content: [`${ROOT_TEST_EXAMPLES}font-faces/font_face.html`],
9 | css: [`${ROOT_TEST_EXAMPLES}font-faces/font_face.css`],
10 | fontFace: true,
11 | });
12 | purgedCSS = resultPurge[0].css;
13 | });
14 | it("keep @font-face 'Cerebri Bold'", () => {
15 | expect(
16 | purgedCSS.includes(`src: url('../fonts/CerebriSans-Bold.eot?')`),
17 | ).toBe(true);
18 | });
19 | it("keep @font-face 'Cerebri Sans'", () => {
20 | expect(
21 | purgedCSS.includes(`src: url('../fonts/CerebriSans-Regular.eot?')`),
22 | ).toBe(true);
23 | });
24 | it("remove @font-face 'OtherFont'", () => {
25 | expect(purgedCSS.includes(`src: url('xxx')`)).toBe(false);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/delimited.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "./../src/index";
2 | import { ROOT_TEST_EXAMPLES } from "./utils";
3 |
4 | describe("delimited", () => {
5 | let purgedCSS: string;
6 | beforeAll(async () => {
7 | const resultPurge = await new PurgeCSS().purge({
8 | content: [`${ROOT_TEST_EXAMPLES}delimited/delimited.html`],
9 | css: [`${ROOT_TEST_EXAMPLES}delimited/delimited.css`],
10 | });
11 | purgedCSS = resultPurge[0].css;
12 | });
13 | it("removes the extra comma", () => {
14 | const commaCount = purgedCSS
15 | .split("")
16 | .reduce((total, chr) => (chr === "," ? total + 1 : total), 0);
17 |
18 | expect(commaCount).toBe(0);
19 | });
20 |
21 | it("finds h1", () => {
22 | expect(purgedCSS.includes("h1")).toBe(true);
23 | });
24 |
25 | it("removes p", () => {
26 | expect(purgedCSS.includes("p")).toBe(false);
27 | });
28 |
29 | it("removes .unused-class-name", () => {
30 | expect(purgedCSS.includes(".unused-class-name")).toBe(false);
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/performance.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "../src/index";
2 |
3 | describe("performance", () => {
4 | it("should not suffer from tons of content and css", function () {
5 | const start = Date.now();
6 | return new PurgeCSS()
7 | .purge({
8 | // Use all of the .js files in node_modules to purge all of the .css
9 | // files in __tests__/test_examples, including tailwind.css, which is
10 | // a whopping 908KB of CSS before purging.
11 | content: ["./node_modules/**/*.js"],
12 | css: ["./__tests__/test_examples/*/*.css"],
13 | })
14 | .then((results) => {
15 | expect(results.length).toBeGreaterThanOrEqual(1);
16 | expect(
17 | results.some((result) => {
18 | return result.file && result.file.endsWith("/tailwind.css");
19 | }),
20 | ).toBe(true);
21 | results.forEach((result) => expect(typeof result.css).toBe("string"));
22 | console.log("performance test took", Date.now() - start, "ms");
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/packages/purgecss-from-tsx/__tests__/data.ts:
--------------------------------------------------------------------------------
1 | export const TEST_1_CONTENT = `
2 | import PropTypes from "prop-types";
3 | import React from "react";
4 |
5 | type MyComponentProps = {
6 | login: any;
7 | };
8 |
9 | type MyComponentState = {
10 | username: string;
11 | password: string;
12 | };
13 |
14 | class MyComponent extends React.Component {
15 | static propTypes: any;
16 |
17 | render() {
18 | return (
19 |
20 | Well
21 |
22 |
23 |
24 |
25 | );
26 | }
27 | }
28 |
29 | MyComponent.propTypes = {
30 | login: PropTypes.func.isRequired
31 | };
32 |
33 | export default MyComponent;
34 | `;
35 |
36 | export const TEST_1_TAG = ["div", "a", "input"];
37 |
38 | export const TEST_1_CLASS = ["test-container", "test-footer", "a-link"];
39 |
40 | export const TEST_1_ID = ["a-link", "blo"];
41 |
--------------------------------------------------------------------------------
/packages/purgecss-from-tsx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fullhuman/purgecss-from-tsx",
3 | "version": "7.0.2",
4 | "description": "TSX extractor for PurgeCSS",
5 | "author": "Ffloriel",
6 | "homepage": "https://github.com/FullHuman/purgecss#readme",
7 | "license": "ISC",
8 | "main": "./lib/purgecss-from-tsx.js",
9 | "module": "./lib/purgecss-from-tsx.esm.js",
10 | "types": "./lib/purgecss-from-tsx.d.ts",
11 | "directories": {
12 | "lib": "lib",
13 | "test": "__tests__"
14 | },
15 | "files": [
16 | "lib"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/FullHuman/purgecss.git"
21 | },
22 | "scripts": {
23 | "build": "ts-node build.ts",
24 | "test": "jest"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/FullHuman/purgecss/issues"
28 | },
29 | "dependencies": {
30 | "@fullhuman/purgecss-from-jsx": "^7.0.2",
31 | "acorn": "^8.7.0",
32 | "typescript": "^5.6.2"
33 | },
34 | "publishConfig": {
35 | "access": "public",
36 | "registry": "https://registry.npmjs.org/"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/keyframes/keyframes.css:
--------------------------------------------------------------------------------
1 | @keyframes bounce {
2 | from, 20%, 53%, 80%, to {
3 | animation-timing-function: cubic-bezier(0.3, 0.1, 0.9, 1.000);
4 | transform: translate3d(1, 1, 0);
5 | }
6 | }
7 |
8 | .bounce {
9 | -webkit-animation-name: bounce;
10 | animation-name: bounce;
11 | -webkit-transform-origin: center bottom;
12 | transform-origin: center bottom;
13 | }
14 |
15 | @keyframes flash {
16 | from, 50%, to {
17 | opacity: 1;
18 | }
19 |
20 | 25%, 75% {
21 | opacity: 0.5;
22 | }
23 | }
24 |
25 | .flash {
26 | animation: flash
27 | }
28 |
29 | @keyframes scale {
30 | from {
31 | transform: scale(1);
32 | }
33 |
34 | to {
35 | transform: scale(2);
36 | }
37 | }
38 |
39 | @keyframes spin {
40 | from {
41 | transform: rotate(0deg);
42 | }
43 |
44 | to {
45 | transform: rotate(360deg);
46 | }
47 | }
48 |
49 | .scale-spin {
50 | animation: spin 300ms linear infinite forwards,scale 300ms linear infinite alternate;
51 | }
52 |
--------------------------------------------------------------------------------
/packages/purgecss-with-wordpress/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | safelist: [
3 | "rtl",
4 | "home",
5 | "blog",
6 | "archive",
7 | "date",
8 | "error404",
9 | "logged-in",
10 | "admin-bar",
11 | "no-customize-support",
12 | "custom-background",
13 | "wp-custom-logo",
14 | "alignnone",
15 | "alignright",
16 | "alignleft",
17 | "alignwide",
18 | "alignfull",
19 | "wp-caption",
20 | "wp-caption-text",
21 | "screen-reader-text",
22 | "comment-list",
23 | "wp-social-link",
24 | /^search(-.*)?$/,
25 | /^(.*)-template(-.*)?$/,
26 | /^(.*)?-?single(-.*)?$/,
27 | /^postid-(.*)?$/,
28 | /^attachmentid-(.*)?$/,
29 | /^attachment(-.*)?$/,
30 | /^page(-.*)?$/,
31 | /^(post-type-)?archive(-.*)?$/,
32 | /^author(-.*)?$/,
33 | /^category(-.*)?$/,
34 | /^tag(-.*)?$/,
35 | /^tax-(.*)?$/,
36 | /^term-(.*)?$/,
37 | /^(.*)?-?paged(-.*)?$/,
38 | /^wp-block-(.*)?$/,
39 | /^has-(.*)?$/,
40 | /^is-(.*)?$/,
41 | /^wp-embed-(.*)?$/,
42 | /^blocks-gallery-(.*)?$/,
43 | ],
44 | };
45 |
--------------------------------------------------------------------------------
/packages/purgecss-from-jsx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fullhuman/purgecss-from-jsx",
3 | "version": "7.0.2",
4 | "description": "JSX extractor for PurgeCSS",
5 | "author": "Ffloriel",
6 | "homepage": "https://github.com/FullHuman/purgecss#readme",
7 | "license": "MIT",
8 | "main": "lib/purgecss-from-jsx.js",
9 | "module": "lib/purgecss-from-jsx.esm.js",
10 | "types": "lib/purgecss-from-jsx.d.ts",
11 | "directories": {
12 | "lib": "lib",
13 | "test": "__tests__"
14 | },
15 | "files": [
16 | "lib"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/FullHuman/purgecss.git"
21 | },
22 | "scripts": {
23 | "build": "ts-node build.ts",
24 | "test": "jest"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/FullHuman/purgecss/issues"
28 | },
29 | "dependencies": {
30 | "acorn": "^8.7.0",
31 | "acorn-jsx": "^5.3.1",
32 | "acorn-jsx-walk": "^2.0.0",
33 | "acorn-walk": "^8.2.0"
34 | },
35 | "publishConfig": {
36 | "access": "public",
37 | "registry": "https://registry.npmjs.org/"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/purgecss-from-pug/__tests__/data.ts:
--------------------------------------------------------------------------------
1 | export const TEST_1_CONTENT = `
2 | html
3 | head
4 | title It's just a test
5 | body
6 | div(class="test-container") Well
7 | div(class="test-footer" id="an-id") I see a div
8 | a(class="a-link" id="a-link" href="#") and a link
9 | div(class="first-class second-class") This div has two classes
10 | input#blo.enabled(type="text" disabled)
11 | `;
12 |
13 | export const TEST_1_TAG = [
14 | "html",
15 | "head",
16 | "title",
17 | "body",
18 | "div",
19 | "a",
20 | "input",
21 | ];
22 |
23 | export const TEST_1_CLASS = [
24 | "test-container",
25 | "test-footer",
26 | "a-link",
27 | "first-class",
28 | "second-class",
29 | "enabled",
30 | ];
31 |
32 | export const TEST_1_ID = ["a-link", "blo"];
33 |
34 | export const TEST_1_ATTRIBUTE_NAMES = ["class", "id", "type", "disabled"];
35 |
36 | export const TEST_1_ATTRIBUTE_VALUES = [
37 | "test-container",
38 | "test-footer",
39 | "a-link",
40 | "first-class",
41 | "second-class",
42 | "enabled",
43 | "blo",
44 | "text",
45 | "true",
46 | "false",
47 | ];
48 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/sourcemap.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "../src/";
2 | import { ROOT_TEST_EXAMPLES } from "./utils";
3 |
4 | describe("source map option", () => {
5 | it("contains the source map inlined in the CSS file", async () => {
6 | const resultsPurge = await new PurgeCSS().purge({
7 | content: [`${ROOT_TEST_EXAMPLES}others/remove_unused.js`],
8 | css: [`${ROOT_TEST_EXAMPLES}others/remove_unused.css`],
9 | sourceMap: true,
10 | });
11 |
12 | expect(resultsPurge[0].css).toContain(
13 | "sourceMappingURL=data:application/json;base64",
14 | );
15 | });
16 |
17 | it("contains the source map separately when setting inline to false", async () => {
18 | const resultsPurge = await new PurgeCSS().purge({
19 | content: [`${ROOT_TEST_EXAMPLES}others/remove_unused.js`],
20 | css: [`${ROOT_TEST_EXAMPLES}others/remove_unused.css`],
21 | sourceMap: {
22 | inline: false,
23 | },
24 | });
25 |
26 | expect(resultsPurge[0].sourceMap).toContain(
27 | 'sources":["__tests__/test_examples/others/remove_unused.css"]',
28 | );
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Full Human
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fullhuman/postcss-purgecss",
3 | "version": "7.0.2",
4 | "description": "PostCSS plugin for PurgeCSS",
5 | "author": "FoundrySH ",
6 | "homepage": "https://purgecss.com",
7 | "license": "MIT",
8 | "main": "lib/postcss-purgecss.js",
9 | "module": "lib/postcss-purgecss.esm.js",
10 | "types": "lib/postcss-purgecss.d.ts",
11 | "directories": {
12 | "lib": "lib",
13 | "test": "__tests__"
14 | },
15 | "files": [
16 | "lib"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/FullHuman/purgecss.git"
21 | },
22 | "scripts": {
23 | "build": "ts-node build.ts",
24 | "test": "jest"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/FullHuman/purgecss/issues"
28 | },
29 | "dependencies": {
30 | "purgecss": "^7.0.2"
31 | },
32 | "devDependencies": {
33 | "postcss": "^8.4.47"
34 | },
35 | "peerDependencies": {
36 | "postcss": "^8.0.0"
37 | },
38 | "publishConfig": {
39 | "access": "public",
40 | "registry": "https://registry.npmjs.org/"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/rollup-plugin-purgecss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rollup-plugin-purgecss",
3 | "version": "7.0.2",
4 | "description": "Rollup plugin for purgecss",
5 | "main": "lib/rollup-plugin-purgecss.js",
6 | "module": "./lib/rollup-plugin-purgecss.es.js",
7 | "jsnext:main": "./lib/rollup-plugin-purgecss.es.js",
8 | "directories": {
9 | "lib": "lib",
10 | "test": "__tests__"
11 | },
12 | "scripts": {
13 | "build": "ts-node build.ts",
14 | "test": "jest"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/FullHuman/purgecss.git"
19 | },
20 | "keywords": [
21 | "rollup-plugin",
22 | "purgecss",
23 | "remove",
24 | "unused",
25 | "css"
26 | ],
27 | "author": "Ffloriel",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/FullHuman/purgecss/issues"
31 | },
32 | "homepage": "https://purgecss.com",
33 | "dependencies": {
34 | "purgecss": "^5.0.0",
35 | "rollup-pluginutils": "^2.8.0"
36 | },
37 | "publishConfig": {
38 | "access": "public",
39 | "registry": "https://registry.npmjs.org/"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grunt-purgecss",
3 | "version": "7.0.2",
4 | "description": "Grunt plugin for PurgeCSS",
5 | "author": "Ffloriel",
6 | "homepage": "https://purgecss.com",
7 | "keywords": [
8 | "optimize",
9 | "optimization",
10 | "remove",
11 | "unused",
12 | "css",
13 | "html",
14 | "rules",
15 | "purge",
16 | "uncss",
17 | "purify"
18 | ],
19 | "license": "MIT",
20 | "types": "./tasks/purgecss.d.ts",
21 | "files": [
22 | "tasks"
23 | ],
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/FullHuman/purgecss.git",
27 | "directory": "packages/grunt-purgecss"
28 | },
29 | "scripts": {
30 | "build": "ts-node build.ts",
31 | "test": "jest"
32 | },
33 | "dependencies": {
34 | "purgecss": "^5.0.0"
35 | },
36 | "devDependencies": {
37 | "@types/grunt": "^0.4.25",
38 | "grunt": "~1.6.1"
39 | },
40 | "bugs": {
41 | "url": "https://github.com/FullHuman/purgecss/issues"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.org/"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js:
--------------------------------------------------------------------------------
1 | const IN_PRODUCTION = process.env.NODE_ENV === "production";
2 |
3 | module.exports = {
4 | plugins: [
5 | IN_PRODUCTION &&
6 | require("@fullhuman/postcss-purgecss")({
7 | content: [`./public/**/*.html`, `./src/**/*.vue`],
8 | defaultExtractor(content) {
9 | let previous;
10 | let contentWithoutStyleBlocks = content;
11 | do {
12 | previous = contentWithoutStyleBlocks;
13 | contentWithoutStyleBlocks = contentWithoutStyleBlocks.replace(
14 | /
70 |
--------------------------------------------------------------------------------
/packages/grunt-purgecss/src/index.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS, defaultOptions, UserDefinedOptions } from "purgecss";
2 |
3 | function getAvailableFiles(
4 | grunt: IGrunt,
5 | files: string[] | undefined = [],
6 | ): string[] {
7 | return files.filter((filepath) => {
8 | // Warn on and remove invalid source files (if nonull was set).
9 | if (!grunt.file.exists(filepath)) {
10 | grunt.log.warn(`Source file "${filepath}" not found.`);
11 | return false;
12 | }
13 | return true;
14 | });
15 | }
16 |
17 | function gruntPurgeCSS(grunt: IGrunt): void {
18 | grunt.registerMultiTask("purgecss", "Grunt plugin for PurgeCSS", function () {
19 | const done = this.async();
20 | const options = this.options(defaultOptions);
21 | const promisedPurgedFiles = [];
22 | for (const file of this.files) {
23 | const source = getAvailableFiles(grunt, file.src);
24 | const purgedCss = new PurgeCSS()
25 | .purge({
26 | ...options,
27 | css: source,
28 | })
29 | .then((purgeCSSResults) => {
30 | if (typeof file.dest === "undefined") {
31 | throw new Error(`Destination file not found`);
32 | }
33 |
34 | grunt.file.write(file.dest, purgeCSSResults[0].css);
35 | // Print a success message
36 | grunt.log.writeln(`File "${file.dest}" created.`);
37 | });
38 |
39 | promisedPurgedFiles.push(purgedCss);
40 | }
41 | Promise.all(promisedPurgedFiles)
42 | .then(() => {
43 | done();
44 | })
45 | .catch(() => done(false));
46 | });
47 | }
48 |
49 | export default gruntPurgeCSS;
50 |
--------------------------------------------------------------------------------
/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const glob = require("fast-glob");
3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin");
4 | const {PurgeCSSPlugin} = require("../../../src/");
5 |
6 | const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || [];
7 |
8 | const PATHS = {
9 | src: path.join(__dirname, "src"),
10 | };
11 |
12 | module.exports = {
13 | mode: "development",
14 | devtool: false,
15 | entry: "./src/index.js",
16 | context: path.resolve(__dirname),
17 | optimization: {
18 | splitChunks: {
19 | cacheGroups: {
20 | styles: {
21 | name: "styles",
22 | type: "css/mini-extract",
23 | test: /\.css$/,
24 | chunks: "all",
25 | enforce: true,
26 | },
27 | },
28 | },
29 | },
30 | module: {
31 | rules: [
32 | {
33 | test: /\.css$/,
34 | use: [MiniCssExtractPlugin.loader, "css-loader"],
35 | },
36 | ],
37 | },
38 | plugins: [
39 | new MiniCssExtractPlugin({
40 | filename: "[name].css",
41 | }),
42 | new PurgeCSSPlugin({
43 | paths: () => glob.sync(`${PATHS.src}/*`),
44 | safelist: () => {
45 | return {
46 | standard: ["safelisted", /^safelistedPat/],
47 | deep: [/^safelistedPatternChildren/],
48 | greedy: [/^safelistedPatternGreedy/],
49 | };
50 | },
51 | extractors: [
52 | {
53 | extractor: customExtractor,
54 | extensions: ["html", "js"],
55 | },
56 | ],
57 | }),
58 | ],
59 | };
60 |
--------------------------------------------------------------------------------
/packages/purgecss/build.ts:
--------------------------------------------------------------------------------
1 | import {
2 | buildRollup,
3 | createRollupConfig,
4 | extractAPI,
5 | } from "../../scripts/build";
6 | import { promises as asyncFs } from "fs";
7 | import * as path from "path";
8 | import typescript from "@rollup/plugin-typescript";
9 | // import { terser } from "rollup-plugin-terser";
10 | import json from "@rollup/plugin-json";
11 | import { RollupOptions } from "rollup";
12 |
13 | const external = [
14 | "postcss",
15 | "postcss-selector-parser",
16 | "glob",
17 | "path",
18 | "fs",
19 | "util",
20 | ];
21 |
22 | const cliBundle: RollupOptions = {
23 | external: [...external, "commander"],
24 | input: "./src/bin.ts",
25 | output: {
26 | banner: "#!/usr/bin/env node",
27 | file: "./bin/purgecss.js",
28 | footer: "main();",
29 | format: "cjs",
30 | },
31 | plugins: [
32 | json(),
33 | typescript({
34 | tsconfig: "./tsconfig.json",
35 | declaration: false,
36 | declarationDir: undefined,
37 | composite: false,
38 | sourceMap: false,
39 | outDir: "./bin",
40 | }),
41 | // terser(),
42 | ],
43 | };
44 |
45 | (async () => {
46 | await asyncFs.rm(path.resolve(__dirname, "lib"), {
47 | recursive: true,
48 | force: true,
49 | });
50 | await asyncFs.rm(path.resolve(__dirname, "bin"), {
51 | recursive: true,
52 | force: true,
53 | });
54 | const rollupConfig = createRollupConfig("purgecss", external);
55 | await buildRollup(rollupConfig);
56 | await buildRollup(cliBundle);
57 | await extractAPI(__dirname);
58 | await asyncFs.rm(path.resolve(__dirname, "lib", ".temp"), {
59 | recursive: true,
60 | force: true,
61 | });
62 | })();
63 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## [Code of Conduct](./.github/CODE_OF_CONDUCT.md)
2 |
3 | FullHuman has adopted the Contributor Covenant Code of Conduct for all of its
4 | project. Please read the text so that you understand how to conduct while
5 | contributing to this project.
6 |
7 | ## Semantic Versioning
8 |
9 | Purgecss use [SemVer](http://semver.org/) for versioning.
10 |
11 | ## Sending a Pull Request
12 |
13 | **Before submitting a pull request,** please make sure the following is done:
14 |
15 | 1. Fork [the repository](https://github.com/FullHuman/purgecss)
16 | and create your branch from `main`.
17 | 2. If you've added code that should be tested, add tests!
18 | 3. If you've changed APIs, update the documentation.
19 | 4. Ensure the test suite passes (`npm test`).
20 | 4. Make sure your code lints (`npm run lint`).
21 |
22 | ### Development Workflow
23 |
24 | After cloning Purgecss, run `npm i && npm run bootstrap` to fetch its dependencies. Then, you can run
25 | several commands:
26 |
27 | * `npm run build` will build cjs and es module of all PurgeCSS packages in their `lib` folder.
28 | * `npm run lint` checks the code style.
29 | * `npm test` runs the complete test suite.
30 | * `npm test -- --watch` runs an interactive test watcher.
31 | * `npm test ` runs tests with matching filenames.
32 | * `npm run build` creates the cjs and es module of all Purgecss packages in their `lib` folder.
33 |
34 | Make sure that your pull request contains unit tests for any new functionality.
35 | This way we can ensure that we don't break your code in the future.
36 |
37 | ### License
38 |
39 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
40 | for details.
41 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/comments.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "./../src/index";
2 | import { findInCSS, ROOT_TEST_EXAMPLES } from "./utils";
3 |
4 | describe("ignore comment", () => {
5 | let purgedCSS: string;
6 | beforeAll(async () => {
7 | const resultsPurge = await new PurgeCSS().purge({
8 | content: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment.html`],
9 | css: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment.css`],
10 | });
11 | purgedCSS = resultsPurge[0].css;
12 | });
13 | it("ignores h1 h2", () => {
14 | expect(purgedCSS.includes("h1")).toBe(true);
15 | expect(purgedCSS.includes("h3")).toBe(true);
16 | });
17 |
18 | it("removes the comment", () => {
19 | expect(purgedCSS.includes("/* purgecss ignore */")).toBe(false);
20 | expect(purgedCSS.includes("/* purgecss ignore current */")).toBe(false);
21 | });
22 | });
23 |
24 | describe("ignore comment range", () => {
25 | let purgedCSS: string;
26 | beforeAll(async () => {
27 | const resultsPurge = await new PurgeCSS().purge({
28 | content: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment_range.html`],
29 | css: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment_range.css`],
30 | });
31 | purgedCSS = resultsPurge[0].css;
32 | });
33 |
34 | it("ignores h1, h3, h5, h6", () => {
35 | findInCSS(expect, ["h1", "h3", "h5", "h6"], purgedCSS);
36 | });
37 |
38 | it("removes h4", () => {
39 | expect(purgedCSS.includes("h4")).toBe(false);
40 | });
41 |
42 | it("removes the comments", () => {
43 | expect(purgedCSS.includes("/* purgecss start ignore */")).toBe(false);
44 | expect(purgedCSS.includes("/* purgecss end ignore */")).toBe(false);
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/packages/purgecss-from-html/__tests__/data.ts:
--------------------------------------------------------------------------------
1 | export const TEST_1_CONTENT = `
2 |
3 |
4 | It's just a test
5 |
6 |
7 | Well
8 |
9 |
10 |
11 |
12 |
13 |
14 | `;
15 |
16 | export const TEST_1_TAG = [
17 | "html",
18 | "head",
19 | "title",
20 | "body",
21 | "div",
22 | "a",
23 | "input",
24 | ];
25 |
26 | export const TEST_1_CLASS = ["test-container", "test-footer", "a-link"];
27 |
28 | export const TEST_1_ID = ["a-link", "blo"];
29 |
30 | export const TEST_1_ATTRIBUTES = {
31 | NAMES: ["href", "id", "class", "type", "disabled"],
32 | VALUES: ["#", "a-link", "a-link", "text", ...TEST_1_CLASS, ...TEST_1_ID],
33 | };
34 |
35 | export const TEST_2_CONTENT = `
36 |
37 |
38 |
Well
39 |
40 |
41 |
42 |
43 |
44 |
45 |
50 |
51 |
61 | `;
62 |
63 | export const TEST_2_TAG = ["div", "a", "input"];
64 |
65 | export const TEST_2_CLASS = ["test-container", "test-footer", "a-link"];
66 |
67 | export const TEST_2_ID = ["a-link", "blo"];
68 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
30 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/pseudo-elements/pseudo-elements.css:
--------------------------------------------------------------------------------
1 | /* Remove inner border and padding from Firefox, but don't restore the outline like Normalize. */
2 |
3 | ::-moz-focus-inner {
4 | padding: 0;
5 | border-style: none;
6 | }
7 |
8 |
9 | /* Fix height of inputs with a type of datetime-local, date, month, week, or time
10 | See https://github.com/twbs/bootstrap/issues/18842 */
11 |
12 | ::-webkit-datetime-edit-fields-wrapper,
13 | ::-webkit-datetime-edit-text,
14 | ::-webkit-datetime-edit-minute,
15 | ::-webkit-datetime-edit-hour-field,
16 | ::-webkit-datetime-edit-day-field,
17 | ::-webkit-datetime-edit-month-field,
18 | ::-webkit-datetime-edit-year-field {
19 | padding: 0;
20 | }
21 |
22 | ::-webkit-inner-spin-button {
23 | height: auto;
24 | }
25 |
26 |
27 | /* Remove the inner padding in Chrome and Safari on macOS. */
28 |
29 | ::-webkit-search-decoration {
30 | -webkit-appearance: none;
31 | }
32 |
33 | /* Remove padding around color pickers in webkit browsers */
34 |
35 | ::-webkit-color-swatch-wrapper {
36 | padding: 0;
37 | }
38 |
39 |
40 | /* Inherit font family and line height for file input buttons */
41 |
42 | ::file-selector-button {
43 | font: inherit;
44 | }
45 |
46 | /* 1. Change font properties to `inherit`
47 | 2. Correct the inability to style clickable types in iOS and Safari. */
48 |
49 | ::-webkit-file-upload-button {
50 | font: inherit;
51 | -webkit-appearance: button;
52 | }
53 |
54 | ::grammar-error {
55 | text-decoration: underline green;
56 | color: green;
57 | }
58 |
59 | .used::grammar-error {
60 | text-decoration: underline blue;
61 | color: blue;
62 | }
63 |
64 | .unused::grammar-error {
65 | text-decoration: underline red;
66 | color: red;
67 | }
68 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/test_examples/css-variables/variables.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --color-first: var(--wrong-order);
3 | --primary-color: blue;
4 | --secondary-color: indigo;
5 | --tertiary-color: aqua;
6 | --unused-color: violet;
7 | --used-color: rebeccapurple;
8 | --accent-color: orange;
9 | --wrong-order: yellow;
10 | --outline-color: coral;
11 | --random: var(--not-existing);
12 | }
13 |
14 | .button {
15 | --button-color: var(--tertiary-color);
16 | --border-color: linear-gradient(to top, var(--secondary-color), var(--used-color, white));
17 |
18 | background-color: var(--primary-color);
19 | color: var(--accent-color);
20 | border-color: var(--border-color);
21 | }
22 |
23 | .button, .unused-class {
24 | outline-color: var(--outline-color);
25 | }
26 |
27 | .button:focus {
28 | background-color: var(--accent-color);
29 | color: var(--primary-color);
30 | border-color: var(--color-first);
31 | }
32 |
33 | @media (min-width: 1024px) {
34 | :root {
35 | --color-first: var(--wrong-order);
36 | --primary-color: blue;
37 | --secondary-color: indigo;
38 | --tertiary-color: aqua;
39 | --unused-color: violet;
40 | --used-color: rebeccapurple;
41 | --accent-color: orange;
42 | --wrong-order: yellow;
43 | --random: var(--not-existing);
44 | }
45 |
46 | .button {
47 | --button-color: var(--tertiary-color);
48 | --border-color: linear-gradient(to top, var(--secondary-color), var(--used-color, white));
49 |
50 | background-color: var(--primary-color);
51 | color: var(--accent-color);
52 | border-color: var(--border-color);
53 | }
54 |
55 | .button:focus {
56 | background-color: var(--accent-color);
57 | color: var(--primary-color);
58 | border-color: var(--color-first);
59 | }
60 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "root",
3 | "private": true,
4 | "workspaces": [
5 | "packages/*"
6 | ],
7 | "devDependencies": {
8 | "@eslint/eslintrc": "^3.1.0",
9 | "@eslint/js": "^9.11.1",
10 | "@microsoft/api-documenter": "^7.25.14",
11 | "@microsoft/api-extractor": "^7.47.9",
12 | "@rollup/plugin-json": "^6.1.0",
13 | "@rollup/plugin-typescript": "^12.1.0",
14 | "@types/jest": "^29.5.13",
15 | "@types/node": "^22.7.4",
16 | "@typescript-eslint/eslint-plugin": "^8.48.1",
17 | "@typescript-eslint/parser": "^8.48.1",
18 | "@vuepress/bundler-vite": "^2.0.0-rc.26",
19 | "@vuepress/plugin-markdown-tab": "^2.0.0-rc.120",
20 | "@vuepress/plugin-search": "^2.0.0-rc.120",
21 | "@vuepress/theme-default": "2.0.0-rc.120",
22 | "conventional-changelog": "^7.1.1",
23 | "eslint": "^9.11.1",
24 | "eslint-plugin-tsdoc": "^0.4.0",
25 | "globals": "^16.5.0",
26 | "husky": "^9.1.6",
27 | "jest": "^29.7.0",
28 | "lerna": "^9.0.3",
29 | "prettier": "^3.3.3",
30 | "rollup": "^4.22.5",
31 | "sass-embedded": "^1.81.0",
32 | "ts-jest": "^29.2.5",
33 | "ts-node": "^10.9.2",
34 | "typescript": "^5.6.2",
35 | "vuepress": "2.0.0-rc.26"
36 | },
37 | "scripts": {
38 | "build": "lerna run build",
39 | "docs:dev": "vuepress dev docs",
40 | "docs:build": "vuepress build docs",
41 | "generate-api-reference": "api-documenter markdown -i ./docs/.vuepress/.temp/api-reference/ -o ./docs/api-reference/",
42 | "generate-changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
43 | "lint": "eslint . --fix --ignore-pattern **/lib/** --ignore-pattern **/bin/**",
44 | "prettier": "prettier --write --parser typescript '**/*.ts'",
45 | "test": "lerna run test",
46 | "prepare": "husky || true"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/expected/font-keyframes.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Cerebri Sans';
3 | font-weight: 400;
4 | font-style: normal;
5 | src: url('../fonts/CerebriSans-Regular.eot?') format('eot'), url('../fonts/CerebriSans-Regular.otf') format('opentype'), url('../fonts/CerebriSans-Regular.svg#Cerebri_Sans') format('svg'), url('../fonts/CerebriSans-Regular.ttf') format('truetype'), url('../fonts/CerebriSans-Regular.woff') format('woff');
6 | }
7 |
8 | @font-face {
9 | font-family: 'Cerebri Bold';
10 | font-weight: 400;
11 | font-style: normal;
12 | src: url('../fonts/CerebriSans-Bold.eot?') format('eot'), url('../fonts/CerebriSans-Bold.otf') format('opentype'), url('../fonts/CerebriSans-Bold.svg#Cerebri_Sans') format('svg'), url('../fonts/CerebriSans-Bold.ttf') format('truetype'), url('../fonts/CerebriSans-Bold.woff') format('woff');
13 | }
14 |
15 | .used {
16 | color: red;
17 | font-family: 'Cerebri Sans';
18 | }
19 |
20 | .used2 {
21 | color: blue;
22 | font-family: Cerebri Bold, serif;
23 | }
24 |
25 |
26 | @keyframes bounce {
27 | from, 20%, 53%, 80%, to {
28 | animation-timing-function: cubic-bezier(0.3, 0.1, 0.9, 1.000);
29 | transform: translate3d(1, 1, 0);
30 | }
31 | }
32 |
33 | .bounce {
34 | -webkit-animation-name: bounce;
35 | animation-name: bounce;
36 | -webkit-transform-origin: center bottom;
37 | transform-origin: center bottom;
38 | }
39 |
40 | @keyframes scale {
41 | from {
42 | transform: scale(1);
43 | }
44 |
45 | to {
46 | transform: scale(2);
47 | }
48 | }
49 |
50 | @keyframes spin {
51 | from {
52 | transform: rotate(0deg);
53 | }
54 |
55 | to {
56 | transform: rotate(360deg);
57 | }
58 | }
59 |
60 | .scale-spin {
61 | animation: spin 300ms linear infinite forwards,scale 300ms linear infinite alternate;
62 | }
63 |
--------------------------------------------------------------------------------
/packages/postcss-purgecss/__tests__/fixtures/src/config-test/expected.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Cerebri Sans';
3 | font-weight: 400;
4 | font-style: normal;
5 | src: url('../fonts/CerebriSans-Regular.eot?') format('eot'), url('../fonts/CerebriSans-Regular.otf') format('opentype'), url('../fonts/CerebriSans-Regular.svg#Cerebri_Sans') format('svg'), url('../fonts/CerebriSans-Regular.ttf') format('truetype'), url('../fonts/CerebriSans-Regular.woff') format('woff');
6 | }
7 |
8 | @font-face {
9 | font-family: 'Cerebri Bold';
10 | font-weight: 400;
11 | font-style: normal;
12 | src: url('../fonts/CerebriSans-Bold.eot?') format('eot'), url('../fonts/CerebriSans-Bold.otf') format('opentype'), url('../fonts/CerebriSans-Bold.svg#Cerebri_Sans') format('svg'), url('../fonts/CerebriSans-Bold.ttf') format('truetype'), url('../fonts/CerebriSans-Bold.woff') format('woff');
13 | }
14 |
15 | .used {
16 | color: red;
17 | font-family: 'Cerebri Sans';
18 | }
19 |
20 | .used2 {
21 | color: blue;
22 | font-family: Cerebri Bold, serif;
23 | }
24 |
25 |
26 | @keyframes bounce {
27 | from, 20%, 53%, 80%, to {
28 | animation-timing-function: cubic-bezier(0.3, 0.1, 0.9, 1.000);
29 | transform: translate3d(1, 1, 0);
30 | }
31 | }
32 |
33 | .bounce {
34 | -webkit-animation-name: bounce;
35 | animation-name: bounce;
36 | -webkit-transform-origin: center bottom;
37 | transform-origin: center bottom;
38 | }
39 |
40 | @keyframes scale {
41 | from {
42 | transform: scale(1);
43 | }
44 |
45 | to {
46 | transform: scale(2);
47 | }
48 | }
49 |
50 | @keyframes spin {
51 | from {
52 | transform: rotate(0deg);
53 | }
54 |
55 | to {
56 | transform: rotate(360deg);
57 | }
58 | }
59 |
60 | .scale-spin {
61 | animation: spin 300ms linear infinite forwards,scale 300ms linear infinite alternate;
62 | }
63 |
--------------------------------------------------------------------------------
/packages/purgecss/__tests__/rejectedCss.test.ts:
--------------------------------------------------------------------------------
1 | import { PurgeCSS } from "./../src/index";
2 | import { ROOT_TEST_EXAMPLES } from "./utils";
3 |
4 | describe("rejectedCss", () => {
5 | it("returns the rejected css as part of the result", async () => {
6 | expect.assertions(1);
7 | const resultsPurge = await new PurgeCSS().purge({
8 | content: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.js`],
9 | css: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.css`],
10 | rejectedCss: true,
11 | });
12 | const expected = `.rejected {\n color: blue;\n}`;
13 | expect(resultsPurge[0].rejectedCss?.trim()).toBe(expected.trim());
14 | });
15 | it("contains the rejected selectors as part of the rejected css", async () => {
16 | expect.assertions(1);
17 | const resultsPurge = await new PurgeCSS().purge({
18 | content: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.js`],
19 | css: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.css`],
20 | rejected: true,
21 | rejectedCss: true,
22 | });
23 | expect(resultsPurge[0].rejectedCss?.trim()).toContain(
24 | resultsPurge[0].rejected?.[0],
25 | );
26 | });
27 | /**
28 | * https://github.com/FullHuman/purgecss/pull/763#discussion_r754618902
29 | */
30 | it("preserves the node correctly", async () => {
31 | expect.assertions(2);
32 | const resultsPurge = await new PurgeCSS().purge({
33 | content: [`${ROOT_TEST_EXAMPLES}rejectedCss/empty-parent-node.js`],
34 | css: [`${ROOT_TEST_EXAMPLES}rejectedCss/empty-parent-node.css`],
35 | rejectedCss: true,
36 | });
37 | const expectedRejectedCss = `@media (max-width: 66666px) {\n .unused-class {\n color: black;\n }\n}`;
38 | const expectedPurgedCss = `@media (max-width: 66666px) {\n .used-class {\n color: black;\n }\n}`;
39 | expect(resultsPurge[0].rejectedCss?.trim()).toEqual(expectedRejectedCss);
40 | expect(resultsPurge[0].css.trim()).toEqual(expectedPurgedCss);
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/docs/plugins/gulp.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Gulp
3 | lang: en-US
4 | meta:
5 | - name: description
6 | content: PurgeCSS is a tool to remove unused CSS from your project. You can use it with gulp plugin.
7 | - itemprop: description
8 | content: PurgeCSS is a tool to remove unused CSS from your project. You can use it with gulp plugin.
9 | - property: og:url
10 | content: https://purgecss.com/plugins/gulp
11 | - property: og:site_name
12 | content: purgecss.com
13 | - property: og:type
14 | content: website
15 | - property: og:image
16 | content: https://i.imgur.com/UEiUiJ0.png
17 | - property: og:locale
18 | content: en_US
19 | - property: og:title
20 | content: Remove unused CSS - PurgeCSS
21 | - property: og:description
22 | content: PurgeCSS is a tool to remove unused CSS from your project. You can use it with gulp plugin.
23 | ---
24 |
25 | # Gulp
26 |
27 | ## Installation
28 |
29 | ```sh
30 | npm i -D gulp-purgecss
31 | npm install --save-dev gulp-purgecss
32 | ```
33 |
34 | ## Usage
35 |
36 | By default, `purgecss` outputs the source CSS _with unused selectors removed_:
37 |
38 | ```js
39 | const gulp = require('gulp')
40 | const purgecss = require('gulp-purgecss')
41 |
42 | gulp.task('purgecss', () => {
43 | return gulp.src('src/**/*.css')
44 | .pipe(purgecss({
45 | content: ['src/**/*.html']
46 | }))
47 | .pipe(gulp.dest('build/css'))
48 | })
49 | ```
50 |
51 | By setting the `rejected` option, you can 'invert' the output to list _only the removed selectors_:
52 |
53 | ```js
54 | const gulp = require('gulp')
55 | const rename = require('gulp-rename')
56 | const purgecss = require('gulp-purgecss')
57 |
58 | gulp.task('purgecss-rejected', () => {
59 | return gulp.src('src/**/*.css')
60 | .pipe(rename({
61 | suffix: '.rejected'
62 | }))
63 | .pipe(purgecss({
64 | content: ['src/**/*.html'],
65 | rejected: true
66 | }))
67 | .pipe(gulp.dest('build/css'))
68 | })
69 | ```
70 |
--------------------------------------------------------------------------------
/docs/guides/wordpress.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: WordPress
3 | lang: en-US
4 | meta:
5 | - name: description
6 | content: PurgeCSS can be used for WordPress development. A module exists to ease the process and provide common safelist items.
7 | - itemprop: description
8 | content: PurgeCSS can be used for WordPress development. A module exists to ease the process and provide common safelist items.
9 | - property: og:url
10 | content: https://purgecss.com/guides/wordpress
11 | - property: og:site_name
12 | content: purgecss.com
13 | - property: og:type
14 | content: website
15 | - property: og:image
16 | content: https://i.imgur.com/UEiUiJ0.png
17 | - property: og:locale
18 | content: en_US
19 | - property: og:title
20 | content: Remove unused CSS - PurgeCSS
21 | - property: og:description
22 | content: PurgeCSS can be used for WordPress development. A module exists to ease the process and provide common safelist items.
23 | ---
24 |
25 | # WordPress
26 |
27 | If you want to use PurgeCSS with WordPress, you might need to safelist classes generated by WordPress to avoid them being removed by PurgeCSS. `purgecss-with-wordpress` contains the classes needed to be safelisted.
28 |
29 | ## Installation
30 |
31 | You need to install [purgecss](https://github.com/FullHuman/purgecss) first.
32 |
33 | Install `purgecss-with-wordpress`:
34 | ```sh
35 | npm i --save-dev purgecss-with-wordpress
36 | ```
37 |
38 | ## Usage
39 |
40 | ```js{2,7,8}
41 | import Purgecss from 'purgecss'
42 | import purgecssWordpress from 'purgecss-with-wordpress'
43 |
44 | const purgeCss = new Purgecss({
45 | content: ['**/*.html'],
46 | css: ['**/*.css'],
47 | safelist: purgecssWordpress.safelist
48 | })
49 | const result = purgecss.purge()
50 | ```
51 |
52 | If you have additional classes you want to include, you can include them using the spread operator:
53 |
54 | ```js
55 | {
56 | safelist: [
57 | ...purgecssWordpress.safelist,
58 | 'red',
59 | 'blue',
60 | /^red/,
61 | /blue$/,
62 | ]
63 | }
64 | ```
65 |
--------------------------------------------------------------------------------
/docs/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | lang: en-US
4 | meta:
5 | - name: description
6 | content: PurgeCSS is a tool to remove unused CSS from your project. It can be used as part of your development workflow. PurgeCSS comes with a JavaScript API, a CLI, and plugins for popular build tools.
7 | - itemprop: description
8 | content: PurgeCSS is a tool to remove unused CSS from your project. It can be used as part of your development workflow. PurgeCSS comes with a JavaScript API, a CLI, and plugins for popular build tools.
9 | - property: og:url
10 | content: https://purgecss.com
11 | - property: og:site_name
12 | content: purgecss.com
13 | - property: og:type
14 | content: website
15 | - property: og:image
16 | content: https://i.imgur.com/UEiUiJ0.png
17 | - property: og:locale
18 | content: en_US
19 | - property: og:title
20 | content: Remove unused CSS - PurgeCSS
21 | - property: og:description
22 | content: PurgeCSS is a tool to remove unused CSS from your project. It can be used as part of your development workflow. PurgeCSS comes with a JavaScript API, a CLI, and plugins for popular build tools.
23 | ---
24 |
25 | # About PurgeCSS
26 |
27 | PurgeCSS is a tool to remove unused CSS. It can be part of your development workflow.
28 | When you are building a website, you might decide to use a CSS framework like TailwindCSS, Bootstrap, MaterializeCSS, Foundation, etc... But you will only use a small set of the framework, and a lot of unused CSS styles will be included.
29 |
30 | This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your CSS files. Then it matches the selectors used in your files with the one in your content files. It removes unused selectors from your CSS, resulting in smaller CSS files.
31 |
32 | ## Sponsors 🥰
33 |
34 | [
](https://www.bairesdev.com/sponsoring-open-source-projects/)
35 | [
](https://full-human.health/)
36 |
--------------------------------------------------------------------------------
/packages/gulp-purgecss/README.md:
--------------------------------------------------------------------------------
1 | # gulp-purgecss
2 |
3 | [](https://www.npmjs.com/package/gulp-purgecss)
4 | []()
5 |
6 |
7 | > [gulp](http://gulpjs.com/) plugin to removed unused CSS, using [purgecss](https://github.com/FullHuman/purgecss)
8 |
9 | ## Regarding Issues
10 |
11 | This is just a simple [gulp](https://github.com/gulpjs/gulp) plugin, which means it's nothing more than a thin wrapper around `purgecss`. If it looks like you are having CSS related issues, please go to the [purgecss](https://github.com/FullHuman/purgecss/issues) repo. Only create a new issue if it looks like you're having a problem with the plugin itself.
12 |
13 | ## Install
14 |
15 | ```
16 | npm i -D gulp-purgecss
17 | npm install --save-dev gulp-purgecss
18 | ```
19 |
20 | ## Usage
21 |
22 | By default, `purgecss` outputs the source CSS _with unused selectors removed_:
23 |
24 | ```js
25 | const gulp = require('gulp')
26 | const purgecss = require('gulp-purgecss')
27 |
28 | gulp.task('purgecss', () => {
29 | return gulp.src('src/**/*.css')
30 | .pipe(purgecss({
31 | content: ['src/**/*.html']
32 | }))
33 | .pipe(gulp.dest('build/css'))
34 | })
35 | ```
36 |
37 | By setting the `rejected` option, you can 'invert' the output to list _only the removed selectors_:
38 |
39 | ```js
40 | const gulp = require('gulp')
41 | const rename = require('gulp-rename')
42 | const purgecss = require('gulp-purgecss')
43 |
44 | gulp.task('purgecss-rejected', () => {
45 | return gulp.src('src/**/*.css')
46 | .pipe(rename({
47 | suffix: '.rejected'
48 | }))
49 | .pipe(purgecss({
50 | content: ['src/**/*.html'],
51 | rejected: true
52 | }))
53 | .pipe(gulp.dest('build/css'))
54 | })
55 | ```
56 |
57 | ## Versioning
58 |
59 | We use [SemVer](http://semver.org/) for versioning.
60 |
61 | ## License
62 |
63 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
64 |
65 |
--------------------------------------------------------------------------------
/docs/guides/vue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Vue
3 | lang: en-US
4 | meta:
5 | - name: description
6 | content: PurgeCSS can be used with Vue with the webpack plugin.
7 | - itemprop: description
8 | content: PurgeCSS can be used with Vue with the webpack plugin.
9 | - property: og:url
10 | content: https://purgecss.com/guides/vue
11 | - property: og:site_name
12 | content: purgecss.com
13 | - property: og:type
14 | content: website
15 | - property: og:image
16 | content: https://i.imgur.com/UEiUiJ0.png
17 | - property: og:locale
18 | content: en_US
19 | - property: og:title
20 | content: Remove unused CSS - PurgeCSS
21 | - property: og:description
22 | content: PurgeCSS can be used with Vue with the webpack plugin.
23 | ---
24 |
25 | # Vue
26 |
27 | ## Use the vue CLI plugin
28 |
29 | 
30 |
31 | ### Install
32 |
33 | If you haven't yet installed vue-cli 3, first follow the install instructions here: https://github.com/vuejs/vue-cli
34 |
35 | Generate a project using vue-cli 3.0:
36 |
37 | ```sh
38 | vue create my-app
39 | ```
40 |
41 | Before installing the PurgeCSS plugin, make sure to commit or stash your changes in case you need to revert the changes.
42 |
43 | To install the PurgeCSS plugin simply navigate to your application folder and add PurgeCSS.
44 |
45 | ```sh
46 | cd my-app
47 |
48 | vue add @fullhuman/purgecss
49 | ```
50 |
51 | The PurgeCSS plugin will generate a `postcss.config.js` file with PurgeCSS configured in it. You can then modify the PurgeCSS options.
52 |
53 | ### Usage
54 |
55 | Below are the PurgeCSS options set by this plugin:
56 |
57 | ```js
58 | {
59 | content: [ `./public/**/*.html`, `./src/**/*.vue` ],
60 | defaultExtractor (content) {
61 | const contentWithoutStyleBlocks = content.replace(/