From 331e893490b3936e76b829d43cf11361c4323ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20Rouleau?= Date: Fri, 29 May 2020 17:11:48 -0400 Subject: [PATCH 01/36] Add whitelistPatternsGreedy option --- docs/configuration.md | 31 ++++++--- docs/plugins/webpack.md | 10 ++- docs/whitelisting.md | 9 +-- packages/gulp-purgecss/src/types/index.ts | 1 + packages/postcss-purgecss/src/types/index.ts | 1 + packages/purgecss-webpack-plugin/README.md | 10 ++- .../webpack.config.js | 1 + packages/purgecss-webpack-plugin/src/index.ts | 4 ++ .../src/types/index.ts | 1 + .../whitelist_patterns_greedy.css | 6 ++ .../whitelist_patterns_greedy.html | 5 ++ packages/purgecss/__tests__/whitelist.test.ts | 38 +++++++++++ packages/purgecss/src/index.ts | 68 ++++++++++++++----- packages/purgecss/src/options.ts | 1 + packages/purgecss/src/types/index.ts | 2 + 15 files changed, 155 insertions(+), 33 deletions(-) create mode 100644 packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.css create mode 100644 packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.html diff --git a/docs/configuration.md b/docs/configuration.md index 494843e8..03e0fd41 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -51,6 +51,7 @@ interface UserDefinedOptions { whitelist?: string[] whitelistPatterns?: Array whitelistPatternsChildren?: Array + whitelistPatternsGreedy?: Array } interface RawContent { @@ -168,7 +169,7 @@ You can learn more about extractors [here](extractors.md). - **fontFace \(default: false\)** -If there are any unused @font-face rules in your css, you can remove them by setting the `fontFace` option to `true` +If there are any unused @font-face rules in your css, you can remove them by setting the `fontFace` option to `true`. ```js await new PurgeCSS().purge({ @@ -192,7 +193,7 @@ await new PurgeCSS().purge({ - **variables \(default: false\)** -If your are using Custom Properties (CSS variables), or a library using them such as Bootstrap, you can remove unused CSS variables by setting the `variables` option to `true`. +If you are using Custom Properties (CSS variables), or a library using them such as Bootstrap, you can remove unused CSS variables by setting the `variables` option to `true`. ```js await new PurgeCSS().purge({ @@ -204,8 +205,7 @@ await new PurgeCSS().purge({ - **rejected \(default: false\)** -It can sometimes be more practical to scan through the removed list to see if there's anything obviously wrong. -If you want to do it, use the `rejected` option. +It can sometimes be more practical to scan through the removed list to see if there's anything obviously wrong. If you want to do it, use the `rejected` option. ```js await new PurgeCSS().purge({ @@ -217,7 +217,7 @@ await new PurgeCSS().purge({ - **whitelist** -You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the options `whitelist` and `whitelistPatterns`. +You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the options `whitelist`, `whitelistPatterns`, `whitelistPatternsChildren`, and `whitelistPatternsGreedy`. ```js const purgecss = await new PurgeCSS().purge({ @@ -227,7 +227,7 @@ const purgecss = await new PurgeCSS().purge({ }) ``` -In the example, the selectors `.random`, `#yep`, `button` will be left in the final CSS. +In this example, the selectors `.random`, `#yep`, `button` will be left in the final CSS. - **whitelistPatterns** @@ -241,11 +241,11 @@ const purgecss = await new PurgeCSS().purge({ }) ``` -In the example, selectors ending with `red` such as `.bg-red` will be left in the final CSS. +In this example, selectors ending with `red` such as `.bg-red` will be left in the final CSS. - **whitelistPatternsChildren** -You can whitelist selectors based on a regular expression with `whitelistPatternsChildren`. Contrary to `whitelistPatterns`, it will also whitelist children of the selectors. +You can whitelist selectors and their children based on a regular expression with `whitelistPatternsChildren`. ```js const purgecss = await new PurgeCSS().purge({ @@ -255,5 +255,18 @@ const purgecss = await new PurgeCSS().purge({ }) ``` -In the example, selectors such as `red p` or `.bg-red .child-of-bg` will be left in the final CSS. +In this example, selectors such as `.bg-red .child-of-bg` will be left in the final CSS, even if `child-of-bg` is not found. +- **whitelistPatternsGreedy** + +Finally, you can whitelist whole selectors if any part of that selector matches a regular expression with `whitelistPatternsGreedy`. + +```js +const purgecss = await new PurgeCSS().purge({ + content: [], // content + css: [], // css + whitelistPatternsGreedy: [/red$/] +}) +``` + +In this example, selectors such as `button.bg-red.nonexistent-class` will be left in the final CSS, even if `button` and `nonexistent-class` are not found. diff --git a/docs/plugins/webpack.md b/docs/plugins/webpack.md index f0bcf965..21ad158c 100644 --- a/docs/plugins/webpack.md +++ b/docs/plugins/webpack.md @@ -121,7 +121,7 @@ new PurgecssPlugin({ }) ``` -* #### whitelist, whitelistPatterns and whitelistPatternsChildren +* #### whitelist, whitelistPatterns, whitelistPatternsChildren, and whitelistPatternsGreedy Similar as for the `paths` option, you can also define functions for the following options: @@ -141,11 +141,17 @@ function collectWhitelistPatternsChildren() { return [/^whitelisted-/]; } +function collectWhitelistPatternsGreedy() { + // do something to collect the whitelist + return [/^whitelisted-/]; +} + // In the webpack configuration new PurgecssPlugin({ whitelist: collectWhitelist, whitelistPatterns: collectWhitelistPatterns, - whitelistPatternsChildren: collectWhitelistPatternsChildren + whitelistPatternsChildren: collectWhitelistPatternsChildren, + whitelistPatternsGreedy: collectWhitelistPatternsGreedy }) ``` diff --git a/docs/whitelisting.md b/docs/whitelisting.md index 40c0ad32..4580b56e 100644 --- a/docs/whitelisting.md +++ b/docs/whitelisting.md @@ -10,7 +10,7 @@ meta: # Whitelisting -You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the PurgeCSS options `whitelist`, `whitelistPatterns`, `whitelistPatternsChildren`, or directly in your CSS with a special comment. +You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the PurgeCSS options `whitelist`, `whitelistPatterns`, `whitelistPatternsChildren`, `whitelistPatternsGreedy`, or directly in your CSS with a special comment. ## Specific selectors @@ -28,18 +28,19 @@ In the example, the selectors `.random`, `#yep`, `button` will be left in the fi ## Patterns -You can whitelist selectors based on a regular expression with `whitelistPatterns` and `whitelistPatternsChildren`. +You can whitelist selectors based on a regular expression with `whitelistPatterns`, `whitelistPatternsChildren`, and `whitelistPatternsGreedy`. ```javascript const purgecss = new Purgecss({ content: [], // content css: [], // css whitelistPatterns: [/red$/], - whitelistPatternsChildren: [/blue$/] + whitelistPatternsChildren: [/blue$/], + whitelistPatternsGreedy: [/yellow$/] }) ``` -In the example, selectors ending with `red` such as `.bg-red`, and children of selectors ending with `blue` such as `blue p` or `.bg-blue .child-of-bg`, will be left in the final CSS. +In the example, selectors ending with `red` such as `.bg-red`, selectors ending with `blue` as well as their children such as `blue p` or `.bg-blue .child-of-bg`, and selectors that have any part ending with `yellow` such as `button.bg-yellow.other-class`, will be left in the final CSS. Patterns are regular expressions. You can use [regexr](https://regexr.com) to verify the regular expressions correspond to what you are looking for. diff --git a/packages/gulp-purgecss/src/types/index.ts b/packages/gulp-purgecss/src/types/index.ts index f27e7405..d0cd5171 100644 --- a/packages/gulp-purgecss/src/types/index.ts +++ b/packages/gulp-purgecss/src/types/index.ts @@ -24,4 +24,5 @@ export interface UserDefinedOptions { whitelist?: string[]; whitelistPatterns?: Array; whitelistPatternsChildren?: Array; + whitelistPatternsGreedy?: Array; } diff --git a/packages/postcss-purgecss/src/types/index.ts b/packages/postcss-purgecss/src/types/index.ts index b96ffca6..96409716 100644 --- a/packages/postcss-purgecss/src/types/index.ts +++ b/packages/postcss-purgecss/src/types/index.ts @@ -26,4 +26,5 @@ export interface UserDefinedOptions { whitelist?: string[]; whitelistPatterns?: Array; whitelistPatternsChildren?: Array; + whitelistPatternsGreedy?: Array; } diff --git a/packages/purgecss-webpack-plugin/README.md b/packages/purgecss-webpack-plugin/README.md index 5c1e87c0..2469825c 100644 --- a/packages/purgecss-webpack-plugin/README.md +++ b/packages/purgecss-webpack-plugin/README.md @@ -157,7 +157,7 @@ new PurgecssPlugin({ }) ``` -* #### whitelist, whitelistPatterns and whitelistPatternsChildren +* #### whitelist, whitelistPatterns, whitelistPatternsChildren, and whitelistPatternsGreedy Similar as for the `paths` option, you also can define functions for the these options: @@ -176,11 +176,17 @@ function collectWhitelistPatternsChildren() { return [/^whitelisted-/]; } +function collectWhitelistPatternsGreedy() { + // do something to collect the whitelist + return [/^whitelisted-/]; +} + // In the webpack configuration new PurgecssPlugin({ whitelist: collectWhitelist, whitelistPatterns: collectWhitelistPatterns, - whitelistPatternsChildren: collectWhitelistPatternsChildren + whitelistPatternsChildren: collectWhitelistPatternsChildren, + whitelistPatternsGreedy: collectWhitelistPatternsGreedy }) ``` diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js index 47cfd8d3..2c18d6a2 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js @@ -42,6 +42,7 @@ module.exports = { whitelist: () => ['whitelisted'], whitelistPatterns: () => [/^whitelistedPat/], whitelistPatternsChildren: () => [/^whitelistedPatternChildren/], + whitelistPatternsGreedy: () => [/^whitelistedPatternGreedy/], extractors: [ { extractor: customExtractor, diff --git a/packages/purgecss-webpack-plugin/src/index.ts b/packages/purgecss-webpack-plugin/src/index.ts index 6e8ad031..7b4e42d4 100644 --- a/packages/purgecss-webpack-plugin/src/index.ts +++ b/packages/purgecss-webpack-plugin/src/index.ts @@ -116,6 +116,9 @@ export default class PurgeCSSPlugin { if (typeof options.whitelistPatternsChildren === "function") { options.whitelistPatternsChildren = options.whitelistPatternsChildren(); } + if (typeof options.whitelistPatternsGreedy === "function") { + options.whitelistPatternsGreedy = options.whitelistPatternsGreedy(); + } const purgecss = await new PurgeCSS().purge({ content: options.content, @@ -130,6 +133,7 @@ export default class PurgeCSSPlugin { whitelist: options.whitelist, whitelistPatterns: options.whitelistPatterns, whitelistPatternsChildren: options.whitelistPatternsChildren, + whitelistPatternsGreedy: options.whitelistPatternsGreedy, }); const purged = purgecss[0]; diff --git a/packages/purgecss-webpack-plugin/src/types/index.ts b/packages/purgecss-webpack-plugin/src/types/index.ts index 40592acc..97ef6381 100644 --- a/packages/purgecss-webpack-plugin/src/types/index.ts +++ b/packages/purgecss-webpack-plugin/src/types/index.ts @@ -31,6 +31,7 @@ export interface UserDefinedOptions { whitelist?: string[] | WhitelistFunction; whitelistPatterns?: Array | WhitelistPatternsFunction; whitelistPatternsChildren?: Array | WhitelistPatternsFunction; + whitelistPatternsGreedy?: Array | WhitelistPatternsFunction; only?: string[]; } diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.css b/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.css new file mode 100644 index 00000000..a1d69aa9 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.css @@ -0,0 +1,6 @@ +.card {} +.card[data-v-test] {} +.card.card--large {} +.card[data-v-test].card--large {} +.card .card-content {} +.card[data-v-test] .card-content {} diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.html b/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.html new file mode 100644 index 00000000..64ca52ef --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.html @@ -0,0 +1,5 @@ + + +
+ + diff --git a/packages/purgecss/__tests__/whitelist.test.ts b/packages/purgecss/__tests__/whitelist.test.ts index e04a57a4..f62c7390 100644 --- a/packages/purgecss/__tests__/whitelist.test.ts +++ b/packages/purgecss/__tests__/whitelist.test.ts @@ -87,3 +87,41 @@ describe("whitelistPatternsChildren", () => { expect(purgedCSS.includes(".btn__green")).toBe(false); }); }); + +describe("whitelistPatternsGreedy", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [ + `${root}whitelist_patterns_greedy/whitelist_patterns_greedy.html`, + ], + css: [`${root}whitelist_patterns_greedy/whitelist_patterns_greedy.css`], + whitelistPatternsGreedy: [/data-v-.*/], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds card", () => { + expect(purgedCSS.includes(".card")).toBe(true); + }); + + it("finds card with data-v attribute", () => { + expect(purgedCSS.includes(".card[data-v-test]")).toBe(true); + }); + + it("excludes card--large", () => { + expect(purgedCSS.includes(".card.card--large")).toBe(false); + }); + + it("finds card--large with data-v attribute", () => { + expect(purgedCSS.includes(".card[data-v-test].card--large")).toBe(true); + }); + + it("excludes card-content", () => { + expect(purgedCSS.includes(".card .card-content")).toBe(false); + }); + + it("finds card-content inside card with data-v attribute", () => { + expect(purgedCSS.includes(".card[data-v-test] .card-content")).toBe(true); + }); +}); diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index ed93c1ca..59d8a6cd 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -551,6 +551,19 @@ class PurgeCSS { ); } + /** + * Check if the selector is whitelisted by the whitelistPatternsGreedy option + * @param selector selector + */ + private isSelectorWhitelistedGreedy(selector: string): boolean { + return ( + this.options.whitelistPatternsGreedy && + this.options.whitelistPatternsGreedy.some((pattern: RegExp) => + pattern.test(selector) + ) + ); + } + /** * Remove unused css * @param userOptions PurgeCSS options @@ -618,6 +631,15 @@ class PurgeCSS { } } + /** + * Transform a selector node into a string + */ + private getSelectorValue(selector: selectorParser.Node): string | undefined { + return ( + (selector.type === "attribute" && selector.attribute) || selector.value + ); + } + /** * Determine if the selector should be kept, based on the selectors found in the files * @param selector set of css selectors found in the content files or string @@ -630,58 +652,72 @@ class PurgeCSS { // ignore the selector if it is inside a pseudo class if (isInPseudoClass(selector)) return true; + // if there is any greedy whitelist pattern, run all the selector parts through them + // if there is any match, return true + if (this.options.whitelistPatternsGreedy?.length) { + const selectorParts = selector.nodes.map(this.getSelectorValue); + if ( + selectorParts.some( + (selectorPart) => + selectorPart && this.isSelectorWhitelistedGreedy(selectorPart) + ) + ) { + return true; + } + } + let isPresent = false; - for (const nodeSelector of selector.nodes) { - const val = - (nodeSelector.type === "attribute" && nodeSelector.attribute) || - nodeSelector.value; + for (const selectorNode of selector.nodes) { + const selectorValue = this.getSelectorValue(selectorNode); // if the selector is whitelisted with children // returns true to keep all children selectors - if (val && this.isSelectorWhitelistedChildren(val)) { + if (selectorValue && this.isSelectorWhitelistedChildren(selectorValue)) { return true; } // The selector is found in the internal and user-defined whitelist if ( - val && - (CSS_WHITELIST.includes(val) || this.isSelectorWhitelisted(val)) + selectorValue && + (CSS_WHITELIST.includes(selectorValue) || + this.isSelectorWhitelisted(selectorValue)) ) { isPresent = true; continue; } - switch (nodeSelector.type) { + switch (selectorNode.type) { case "attribute": // `value` is a dynamic attribute, highly used in input element // the choice is to always leave `value` as it can change based on the user // idem for `checked`, `selected`, `open` isPresent = ["value", "checked", "selected", "open"].includes( - nodeSelector.attribute + selectorNode.attribute ) ? true - : isAttributeFound(nodeSelector, selectorsFromExtractor); + : isAttributeFound(selectorNode, selectorsFromExtractor); break; case "class": - isPresent = isClassFound(nodeSelector, selectorsFromExtractor); + isPresent = isClassFound(selectorNode, selectorsFromExtractor); break; case "id": - isPresent = isIdentifierFound(nodeSelector, selectorsFromExtractor); + isPresent = isIdentifierFound(selectorNode, selectorsFromExtractor); break; case "tag": - isPresent = isTagFound(nodeSelector, selectorsFromExtractor); + isPresent = isTagFound(selectorNode, selectorsFromExtractor); break; default: - break; + continue; } - // selector is not in whitelist children or in whitelist - // and it has not been found as an attribute/class/identifier/tag + // selector is not whitelisted + // and it has not been found as an attribute/class/id/tag if (!isPresent) { return false; } } + return isPresent; } diff --git a/packages/purgecss/src/options.ts b/packages/purgecss/src/options.ts index 12a28453..b543cf98 100644 --- a/packages/purgecss/src/options.ts +++ b/packages/purgecss/src/options.ts @@ -15,4 +15,5 @@ export const defaultOptions: Options = { whitelist: [], whitelistPatterns: [], whitelistPatternsChildren: [], + whitelistPatternsGreedy: [], }; diff --git a/packages/purgecss/src/types/index.ts b/packages/purgecss/src/types/index.ts index df4dc6d1..2e956647 100644 --- a/packages/purgecss/src/types/index.ts +++ b/packages/purgecss/src/types/index.ts @@ -55,6 +55,7 @@ export interface UserDefinedOptions { whitelist?: string[]; whitelistPatterns?: Array; whitelistPatternsChildren?: Array; + whitelistPatternsGreedy?: Array; } export interface Options { @@ -72,6 +73,7 @@ export interface Options { whitelist: string[]; whitelistPatterns: Array; whitelistPatternsChildren: Array; + whitelistPatternsGreedy: Array; } export interface ResultPurge { From b4c40bed9a3fe5075e58dcdf792f545375ceb33b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 13 Jun 2020 11:46:31 +0000 Subject: [PATCH 02/36] build(deps-dev): bump pug-parser from 5.0.1 to 6.0.0 Bumps [pug-parser](https://github.com/pugjs/pug) from 5.0.1 to 6.0.0. - [Release notes](https://github.com/pugjs/pug/releases) - [Commits](https://github.com/pugjs/pug/compare/pug-parser@5.0.1...pug-parser@6.0.0) Signed-off-by: dependabot-preview[bot] --- packages/purgecss-from-pug/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purgecss-from-pug/package.json b/packages/purgecss-from-pug/package.json index 0f9bb1ab..01648a94 100644 --- a/packages/purgecss-from-pug/package.json +++ b/packages/purgecss-from-pug/package.json @@ -25,6 +25,6 @@ }, "devDependencies": { "pug-lexer": "^5.0.0", - "pug-parser": "^5.0.1" + "pug-parser": "^6.0.0" } } From a65c38b5c9b96af004eff4a33ec502c8e39dd7cb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 11:03:53 +0000 Subject: [PATCH 03/36] build(deps-dev): bump @types/jest from 25.2.3 to 26.0.3 Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 25.2.3 to 26.0.3. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ca37d77..abc65bb0 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "vue-cli-plugin-purgecss" ], "devDependencies": { - "@types/jest": "^25.2.1", + "@types/jest": "^26.0.3", "@types/node": "^14.0.5", "@typescript-eslint/eslint-plugin": "^2.28.0", "@typescript-eslint/parser": "^2.28.0", From 52a9d87da6f0b3f89b68e9361626f8172a6dc8e6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 11:06:17 +0000 Subject: [PATCH 04/36] build(deps): bump through2 from 3.0.2 to 4.0.1 Bumps [through2](https://github.com/rvagg/through2) from 3.0.2 to 4.0.1. - [Release notes](https://github.com/rvagg/through2/releases) - [Commits](https://github.com/rvagg/through2/compare/v3.0.2...v4.0.1) Signed-off-by: dependabot-preview[bot] --- packages/gulp-purgecss/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gulp-purgecss/package.json b/packages/gulp-purgecss/package.json index cfeddfab..232bd7c7 100644 --- a/packages/gulp-purgecss/package.json +++ b/packages/gulp-purgecss/package.json @@ -38,7 +38,7 @@ "glob": "^7.1.6", "plugin-error": "^1.0.1", "purgecss": "^2.3.0", - "through2": "^3.0.1" + "through2": "^4.0.1" }, "devDependencies": { "@types/through2": "^2.0.34", From dfac4eff7029029b7faecdfdec1e1ef39658562b Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Thu, 2 Jul 2020 20:30:19 +0100 Subject: [PATCH 05/36] refactor(purgecss): align test folders with tests files - align test folders with ts tests files - rename whitelist tests to safelist --- .../purgecss/__tests__/attributes.test.ts | 9 +- .../purgecss/__tests__/chaining-rules.test.ts | 19 +- packages/purgecss/__tests__/comments.test.ts | 15 +- .../purgecss/__tests__/css-variables.test.ts | 6 +- packages/purgecss/__tests__/delimited.test.ts | 6 +- .../purgecss/__tests__/font-faces.test.ts | 6 +- packages/purgecss/__tests__/index.test.ts | 10 +- packages/purgecss/__tests__/keyframes.test.ts | 14 +- .../purgecss/__tests__/media-queries.test.ts | 6 +- .../purgecss/__tests__/pseudo-class.test.ts | 22 +- packages/purgecss/__tests__/rejected.test.ts | 18 +- packages/purgecss/__tests__/safelist.test.ts | 128 + .../attribute_selector.css | 0 .../attribute_selector.html | 0 .../bootstrap/modified-bootstrap.css | 6702 ----------------- .../test_examples/camel_case/camel_case.css | 7 - .../test_examples/camel_case/camel_case.js | 3 - .../index.css | 0 .../index.html | 0 .../test_examples/combined/combined.css | 7 - .../test_examples/combined/combined.js | 3 - .../ignore_comment.css | 0 .../ignore_comment.html | 0 .../ignore_comment_range.css} | 0 .../ignore_comment_range.html} | 0 .../variables.css | 0 .../variables.html | 0 .../{font_face => font-faces}/font_face.css | 0 .../{font_face => font-faces}/font_face.html | 0 .../media_queries.css | 0 .../media_queries.html | 0 .../multiple_files/multiple_files.css | 3 - .../multiple_files/multiple_files.html | 1 - .../multiple_files/multiple_files.js | 1 - .../multiple_files/multiple_files2.css | 7 - .../remove_unused.css | 0 .../test_examples/others/remove_unused.js | 1 + .../special_characters.css | 0 .../others/special_characters.js | 7 + .../{not => pseudo-class}/not.css | 0 .../{not => pseudo-class}/not.html | 0 .../{nth_child => pseudo-class}/nth_child.css | 0 .../nth_child.html | 0 .../pseudo_class.css | 0 .../pseudo-class/pseudo_class.js | 1 + .../pseudo_selector.css | 0 .../pseudo_selector.html | 0 .../pseudo_class/pseudo_class.js | 1 - .../{simple => rejected}/simple.css | 0 .../test_examples/rejected/simple.js | 5 + .../remove_unused/remove_unused.js | 1 - .../{whitelist => safelist}/whitelist.css | 2 +- .../{whitelist => safelist}/whitelist.html | 0 .../whitelist_patterns_children.css | 0 .../whitelist_patterns_children.html | 0 .../whitelist_patterns_greedy.css | 0 .../whitelist_patterns_greedy.html | 0 .../__tests__/test_examples/simple/simple.js | 5 - .../special_characters/special_characters.js | 7 - .../__tests__/test_examples/tables/tables.css | 8 - .../test_examples/tables/tables.html | 9 - .../test_examples/wildcard/wildcard.css | 21 - .../test_examples/wildcard/wildcard.html | 9 - packages/purgecss/__tests__/utils.ts | 22 + packages/purgecss/__tests__/whitelist.test.ts | 127 - 65 files changed, 225 insertions(+), 6994 deletions(-) create mode 100644 packages/purgecss/__tests__/safelist.test.ts rename packages/purgecss/__tests__/test_examples/{attribute_selector => attributes}/attribute_selector.css (100%) rename packages/purgecss/__tests__/test_examples/{attribute_selector => attributes}/attribute_selector.html (100%) delete mode 100644 packages/purgecss/__tests__/test_examples/bootstrap/modified-bootstrap.css delete mode 100644 packages/purgecss/__tests__/test_examples/camel_case/camel_case.css delete mode 100644 packages/purgecss/__tests__/test_examples/camel_case/camel_case.js rename packages/purgecss/__tests__/test_examples/{chaining_rules => chaining-rules}/index.css (100%) rename packages/purgecss/__tests__/test_examples/{chaining_rules => chaining-rules}/index.html (100%) delete mode 100644 packages/purgecss/__tests__/test_examples/combined/combined.css delete mode 100644 packages/purgecss/__tests__/test_examples/combined/combined.js rename packages/purgecss/__tests__/test_examples/{ignore_comment => comments}/ignore_comment.css (100%) rename packages/purgecss/__tests__/test_examples/{ignore_comment => comments}/ignore_comment.html (100%) rename packages/purgecss/__tests__/test_examples/{ignore_comment_range/index.css => comments/ignore_comment_range.css} (100%) rename packages/purgecss/__tests__/test_examples/{ignore_comment_range/index.html => comments/ignore_comment_range.html} (100%) rename packages/purgecss/__tests__/test_examples/{variables => css-variables}/variables.css (100%) rename packages/purgecss/__tests__/test_examples/{variables => css-variables}/variables.html (100%) rename packages/purgecss/__tests__/test_examples/{font_face => font-faces}/font_face.css (100%) rename packages/purgecss/__tests__/test_examples/{font_face => font-faces}/font_face.html (100%) rename packages/purgecss/__tests__/test_examples/{media_queries => media-queries}/media_queries.css (100%) rename packages/purgecss/__tests__/test_examples/{media_queries => media-queries}/media_queries.html (100%) delete mode 100644 packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.css delete mode 100644 packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.html delete mode 100644 packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.js delete mode 100644 packages/purgecss/__tests__/test_examples/multiple_files/multiple_files2.css rename packages/purgecss/__tests__/test_examples/{remove_unused => others}/remove_unused.css (100%) create mode 100644 packages/purgecss/__tests__/test_examples/others/remove_unused.js rename packages/purgecss/__tests__/test_examples/{special_characters => others}/special_characters.css (100%) create mode 100644 packages/purgecss/__tests__/test_examples/others/special_characters.js rename packages/purgecss/__tests__/test_examples/{not => pseudo-class}/not.css (100%) rename packages/purgecss/__tests__/test_examples/{not => pseudo-class}/not.html (100%) rename packages/purgecss/__tests__/test_examples/{nth_child => pseudo-class}/nth_child.css (100%) rename packages/purgecss/__tests__/test_examples/{nth_child => pseudo-class}/nth_child.html (100%) rename packages/purgecss/__tests__/test_examples/{pseudo_class => pseudo-class}/pseudo_class.css (100%) create mode 100644 packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js rename packages/purgecss/__tests__/test_examples/{pseudo_selector => pseudo-class}/pseudo_selector.css (100%) rename packages/purgecss/__tests__/test_examples/{pseudo_selector => pseudo-class}/pseudo_selector.html (100%) delete mode 100644 packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.js rename packages/purgecss/__tests__/test_examples/{simple => rejected}/simple.css (100%) create mode 100644 packages/purgecss/__tests__/test_examples/rejected/simple.js delete mode 100644 packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.js rename packages/purgecss/__tests__/test_examples/{whitelist => safelist}/whitelist.css (90%) rename packages/purgecss/__tests__/test_examples/{whitelist => safelist}/whitelist.html (100%) rename packages/purgecss/__tests__/test_examples/{whitelist_patterns_children => safelist}/whitelist_patterns_children.css (100%) rename packages/purgecss/__tests__/test_examples/{whitelist_patterns_children => safelist}/whitelist_patterns_children.html (100%) rename packages/purgecss/__tests__/test_examples/{whitelist_patterns_greedy => safelist}/whitelist_patterns_greedy.css (100%) rename packages/purgecss/__tests__/test_examples/{whitelist_patterns_greedy => safelist}/whitelist_patterns_greedy.html (100%) delete mode 100644 packages/purgecss/__tests__/test_examples/simple/simple.js delete mode 100644 packages/purgecss/__tests__/test_examples/special_characters/special_characters.js delete mode 100644 packages/purgecss/__tests__/test_examples/tables/tables.css delete mode 100644 packages/purgecss/__tests__/test_examples/tables/tables.html delete mode 100644 packages/purgecss/__tests__/test_examples/wildcard/wildcard.css delete mode 100644 packages/purgecss/__tests__/test_examples/wildcard/wildcard.html create mode 100644 packages/purgecss/__tests__/utils.ts delete mode 100644 packages/purgecss/__tests__/whitelist.test.ts diff --git a/packages/purgecss/__tests__/attributes.test.ts b/packages/purgecss/__tests__/attributes.test.ts index 734fa4dd..87bcf189 100644 --- a/packages/purgecss/__tests__/attributes.test.ts +++ b/packages/purgecss/__tests__/attributes.test.ts @@ -1,17 +1,16 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("attributes", () => { let purgedCSS: string; - beforeAll(async (done) => { + beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}attribute_selector/attribute_selector.html`], - css: [`${root}attribute_selector/attribute_selector.css`], + content: [`${ROOT_TEST_EXAMPLES}attributes/attribute_selector.html`], + css: [`${ROOT_TEST_EXAMPLES}attributes/attribute_selector.css`], }); purgedCSS = resultsPurge[0].css; - done(); }); it("always keep attribute when attribute is 'value'", () => { diff --git a/packages/purgecss/__tests__/chaining-rules.test.ts b/packages/purgecss/__tests__/chaining-rules.test.ts index af9ed184..71ba9c37 100644 --- a/packages/purgecss/__tests__/chaining-rules.test.ts +++ b/packages/purgecss/__tests__/chaining-rules.test.ts @@ -1,27 +1,20 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES, notFindInCSS } from "./utils"; describe("chaining rules", () => { let purgedCSS: string; - beforeAll(async (done) => { + beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}chaining_rules/index.html`], - css: [`${root}chaining_rules/index.css`], + content: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.html`], + css: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.css`], }); purgedCSS = resultsPurge[0].css; - done(); }); it("keeps parent1 selector", () => { expect(purgedCSS.includes("parent1")).toBe(true); }); - it("removes parent3 selector", () => { - expect(purgedCSS.includes("parent3")).toBe(false); - }); - it("removes d33ef1 selector", () => { - expect(purgedCSS.includes("d33ef1")).toBe(false); - }); - it("removes .parent2", () => { - expect(purgedCSS.includes("parent2")).toBe(false); + it("removes parent3, d33ef1, .parent2", () => { + notFindInCSS(expect, ["parent3", "d33ef1", "parent2"], purgedCSS); }); }); diff --git a/packages/purgecss/__tests__/comments.test.ts b/packages/purgecss/__tests__/comments.test.ts index ea4fa186..fe8cea35 100644 --- a/packages/purgecss/__tests__/comments.test.ts +++ b/packages/purgecss/__tests__/comments.test.ts @@ -1,13 +1,12 @@ import PurgeCSS from "./../src/index"; - -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES, findInCSS } from "./utils"; describe("ignore comment", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}ignore_comment/ignore_comment.html`], - css: [`${root}ignore_comment/ignore_comment.css`], + content: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment.html`], + css: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -27,16 +26,14 @@ describe("ignore comment range", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}ignore_comment_range/index.html`], - css: [`${root}ignore_comment_range/index.css`], + content: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment_range.html`], + css: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment_range.css`], }); purgedCSS = resultsPurge[0].css; }); it("ignores h1, h3, h5, h6", () => { - ["h1", "h3", "h5", "h6"].forEach((selector) => { - expect(purgedCSS.includes(selector)).toBe(true); - }); + findInCSS(expect, ["h1", "h3", "h5", "h6"], purgedCSS); }); it("removes h4", () => { diff --git a/packages/purgecss/__tests__/css-variables.test.ts b/packages/purgecss/__tests__/css-variables.test.ts index 02f38880..7c5b44ef 100644 --- a/packages/purgecss/__tests__/css-variables.test.ts +++ b/packages/purgecss/__tests__/css-variables.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("purge unused css variables", () => { let purgedCSS: string; beforeAll(async (done) => { const resultPurge = await new PurgeCSS().purge({ - content: [`${root}variables/variables.html`], - css: [`${root}variables/variables.css`], + content: [`${ROOT_TEST_EXAMPLES}css-variables/variables.html`], + css: [`${ROOT_TEST_EXAMPLES}css-variables/variables.css`], variables: true, }); purgedCSS = resultPurge[0].css; diff --git a/packages/purgecss/__tests__/delimited.test.ts b/packages/purgecss/__tests__/delimited.test.ts index 44cfa8e5..9f209a59 100644 --- a/packages/purgecss/__tests__/delimited.test.ts +++ b/packages/purgecss/__tests__/delimited.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("delimited", () => { let purgedCSS: string; beforeAll(async (done) => { const resultPurge = await new PurgeCSS().purge({ - content: [`${root}delimited/delimited.html`], - css: [`${root}delimited/delimited.css`], + content: [`${ROOT_TEST_EXAMPLES}delimited/delimited.html`], + css: [`${ROOT_TEST_EXAMPLES}delimited/delimited.css`], }); purgedCSS = resultPurge[0].css; done(); diff --git a/packages/purgecss/__tests__/font-faces.test.ts b/packages/purgecss/__tests__/font-faces.test.ts index 2d7097d7..16dbd36b 100644 --- a/packages/purgecss/__tests__/font-faces.test.ts +++ b/packages/purgecss/__tests__/font-faces.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("purge unused font-face", () => { let purgedCSS: string; beforeAll(async (done) => { const resultPurge = await new PurgeCSS().purge({ - content: [`${root}font_face/font_face.html`], - css: [`${root}font_face/font_face.css`], + content: [`${ROOT_TEST_EXAMPLES}font-faces/font_face.html`], + css: [`${ROOT_TEST_EXAMPLES}font-faces/font_face.css`], fontFace: true, }); purgedCSS = resultPurge[0].css; diff --git a/packages/purgecss/__tests__/index.test.ts b/packages/purgecss/__tests__/index.test.ts index 4414454c..f3056376 100644 --- a/packages/purgecss/__tests__/index.test.ts +++ b/packages/purgecss/__tests__/index.test.ts @@ -1,7 +1,7 @@ import PurgeCSS from "./../src/index"; import { ExtractorResult } from "../src/types"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("purgecss with config file", () => { it("initialize without error with a config file specified", () => { @@ -26,8 +26,8 @@ describe("filters out unused selectors", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}remove_unused/remove_unused.js`], - css: [`${root}remove_unused/remove_unused.css`], + content: [`${ROOT_TEST_EXAMPLES}others/remove_unused.js`], + css: [`${ROOT_TEST_EXAMPLES}others/remove_unused.css`], }); purgedCSS = resultsPurge[0].css; }); @@ -51,8 +51,8 @@ describe("special characters, with custom Extractor", () => { beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}special_characters/special_characters.js`], - css: [`${root}special_characters/special_characters.css`], + content: [`${ROOT_TEST_EXAMPLES}others/special_characters.js`], + css: [`${ROOT_TEST_EXAMPLES}others/special_characters.css`], extractors: [ { extractor: CustomExtractor, diff --git a/packages/purgecss/__tests__/keyframes.test.ts b/packages/purgecss/__tests__/keyframes.test.ts index 9ecc0124..4cda3c3b 100644 --- a/packages/purgecss/__tests__/keyframes.test.ts +++ b/packages/purgecss/__tests__/keyframes.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("keyframes", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}keyframes/keyframes.html`], - css: [`${root}keyframes/keyframes.css`], + content: [`${ROOT_TEST_EXAMPLES}keyframes/keyframes.html`], + css: [`${ROOT_TEST_EXAMPLES}keyframes/keyframes.css`], keyframes: true, }); purgedCSS = resultsPurge[0].css; @@ -28,8 +28,8 @@ describe("purge unused keyframe animations", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}keyframes/index.html`], - css: [`${root}keyframes/index.css`], + content: [`${ROOT_TEST_EXAMPLES}keyframes/index.html`], + css: [`${ROOT_TEST_EXAMPLES}keyframes/index.css`], keyframes: true, }); purgedCSS = resultsPurge[0].css; @@ -46,8 +46,8 @@ describe("do not purge keyframes if option set to false", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}keyframes/index.html`], - css: [`${root}keyframes/index.css`], + content: [`${ROOT_TEST_EXAMPLES}keyframes/index.html`], + css: [`${ROOT_TEST_EXAMPLES}keyframes/index.css`], keyframes: false, }); purgedCSS = resultsPurge[0].css; diff --git a/packages/purgecss/__tests__/media-queries.test.ts b/packages/purgecss/__tests__/media-queries.test.ts index 476c24c1..6439f1b7 100644 --- a/packages/purgecss/__tests__/media-queries.test.ts +++ b/packages/purgecss/__tests__/media-queries.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("media queries", () => { let purgecssResult: string; beforeAll(async (done) => { const purgecss = await new PurgeCSS().purge({ - content: [`${root}media_queries/media_queries.html`], - css: [`${root}media_queries/media_queries.css`], + content: [`${ROOT_TEST_EXAMPLES}media-queries/media_queries.html`], + css: [`${ROOT_TEST_EXAMPLES}media-queries/media_queries.css`], }); purgecssResult = purgecss[0].css; done(); diff --git a/packages/purgecss/__tests__/pseudo-class.test.ts b/packages/purgecss/__tests__/pseudo-class.test.ts index ef7415c3..759946b4 100644 --- a/packages/purgecss/__tests__/pseudo-class.test.ts +++ b/packages/purgecss/__tests__/pseudo-class.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe(":not pseudo class", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}not/not.html`], - css: [`${root}not/not.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/not.html`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/not.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -25,8 +25,8 @@ describe("pseudo selectors", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}pseudo_selector/pseudo_selector.html`], - css: [`${root}pseudo_selector/pseudo_selector.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_selector.html`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_selector.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -68,8 +68,8 @@ describe("nth-child", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}nth_child/nth_child.html`], - css: [`${root}nth_child/nth_child.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/nth_child.html`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/nth_child.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -88,8 +88,8 @@ describe("nth-child", () => { describe("pseudo classes", () => { it("finds div:before", async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}pseudo_class/pseudo_class.js`], - css: [`${root}pseudo_class/pseudo_class.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.js`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.css`], }); const purgedCSS = resultsPurge[0].css; expect(purgedCSS.includes("div:before")).toBe(true); @@ -97,8 +97,8 @@ describe("pseudo classes", () => { it("removes row:after", async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}pseudo_class/pseudo_class.js`], - css: [`${root}pseudo_class/pseudo_class.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.js`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.css`], }); const purgedCSS = resultsPurge[0].css; expect(purgedCSS.includes("row:after")).toBe(false); diff --git a/packages/purgecss/__tests__/rejected.test.ts b/packages/purgecss/__tests__/rejected.test.ts index e062100b..762906f2 100644 --- a/packages/purgecss/__tests__/rejected.test.ts +++ b/packages/purgecss/__tests__/rejected.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("rejected", () => { it("does not return the rejected selectors if rejected set to false", async (done) => { expect.assertions(1); const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}simple/simple.js`], - css: [`${root}simple/simple.css`], + content: [`${ROOT_TEST_EXAMPLES}rejected/simple.js`], + css: [`${ROOT_TEST_EXAMPLES}rejected/simple.css`], }); expect(resultsPurge[0].rejected).toBe(undefined); done(); @@ -16,8 +16,8 @@ describe("rejected", () => { it("returns an empty array if no selectors are rejected", async (done) => { expect.assertions(1); const purgecssResult = await new PurgeCSS().purge({ - content: [`${root}simple/simple.js`], - css: [`${root}simple/simple.css`], + content: [`${ROOT_TEST_EXAMPLES}rejected/simple.js`], + css: [`${ROOT_TEST_EXAMPLES}rejected/simple.css`], rejected: true, }); expect(purgecssResult[0].rejected).toEqual([]); @@ -27,8 +27,8 @@ describe("rejected", () => { it("returns the list of rejected selectors", async (done) => { expect.assertions(1); const purgecssResult = await new PurgeCSS().purge({ - content: [`${root}remove_unused/remove_unused.js`], - css: [`${root}remove_unused/remove_unused.css`], + content: [`${ROOT_TEST_EXAMPLES}others/remove_unused.js`], + css: [`${ROOT_TEST_EXAMPLES}others/remove_unused.css`], rejected: true, }); expect(purgecssResult[0].rejected).toEqual([ @@ -41,8 +41,8 @@ describe("rejected", () => { it("returns the list of rejected selectors with chaining rules", async (done) => { expect.assertions(1); const purgecssResult = await new PurgeCSS().purge({ - content: [`${root}chaining_rules/index.html`], - css: [`${root}chaining_rules/index.css`], + content: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.html`], + css: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.css`], rejected: true, }); expect(purgecssResult[0].rejected).toEqual([ diff --git a/packages/purgecss/__tests__/safelist.test.ts b/packages/purgecss/__tests__/safelist.test.ts new file mode 100644 index 00000000..7bf54387 --- /dev/null +++ b/packages/purgecss/__tests__/safelist.test.ts @@ -0,0 +1,128 @@ +import PurgeCSS from "./../src/index"; + +import { ROOT_TEST_EXAMPLES, findInCSS, notFindInCSS } from "./utils"; + +describe("safelist string", () => { + let purgedCSS = ""; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.css`], + safelist: ["random", "h1", "yep", "button"], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS(expect, [".random", "h1", "#yep", "button"], purgedCSS); + }); +}); + +describe("safelist regular expression", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.css`], + safelist: [/nav-/, /data-v-.*/], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS(expect, [".nav-blue", ".nav-red", "[data-v-test]"], purgedCSS); + }); +}); + +describe("safelist option: standard", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.css`], + safelist: { + standard: ["random", "h1", "yep", "button", /nav-/, /data-v-.*/], + }, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS( + expect, + [ + ".random", + "h1", + "#yep", + "button", + ".nav-blue", + ".nav-red", + "[data-v-test]", + ], + purgedCSS + ); + }); +}); + +describe("safelist option: deep", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [ + `${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_children.html`, + ], + css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_children.css`], + safelist: { + deep: [/^card$/], + }, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS( + expect, + [".card", ".card .content", ".btn", ".card .btn .yellow"], + purgedCSS + ); + }); + + it("excludes selectors not safelisted", () => { + notFindInCSS(expect, [".title", ".btn .red", ".btn__green"], purgedCSS); + }); +}); + +describe("safelist option: greedy", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_greedy.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_greedy.css`], + safelist: { + greedy: [/data-v-.*/], + }, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS( + expect, + [ + ".card", + ".card[data-v-test]", + ".card[data-v-test].card--large", + ".card[data-v-test] .card-content", + ], + purgedCSS + ); + }); + + it("excludes selectors not safelisted", () => { + notFindInCSS( + expect, + [".card.card--large", ".card .card-content"], + purgedCSS + ); + }); +}); diff --git a/packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.css b/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.css rename to packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css diff --git a/packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.html b/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.html rename to packages/purgecss/__tests__/test_examples/attributes/attribute_selector.html diff --git a/packages/purgecss/__tests__/test_examples/bootstrap/modified-bootstrap.css b/packages/purgecss/__tests__/test_examples/bootstrap/modified-bootstrap.css deleted file mode 100644 index df6d7985..00000000 --- a/packages/purgecss/__tests__/test_examples/bootstrap/modified-bootstrap.css +++ /dev/null @@ -1,6702 +0,0 @@ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -template { - display: none; -} -a { - background-color: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -td, -th { - padding: 0; -} - -@media print { - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - .navbar { - display: none; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 11px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group .form-control:focus { - z-index: 3; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 2; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 3; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - padding-right: 15px; - padding-left: 15px; - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} -.embed-responsive-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - background-color: rgba(0, 0, 0, 0); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -10px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -10px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -10px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-header:before, -.modal-header:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-header:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -.boomshaka { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.css b/packages/purgecss/__tests__/test_examples/camel_case/camel_case.css deleted file mode 100644 index 5efb47ac..00000000 --- a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.css +++ /dev/null @@ -1,7 +0,0 @@ -.testFoo { - color: black; -} - -#camelCase { - color: black; -} \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.js b/packages/purgecss/__tests__/test_examples/camel_case/camel_case.js deleted file mode 100644 index 7de25f6b..00000000 --- a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.js +++ /dev/null @@ -1,3 +0,0 @@ -'testFoo' - -'camelCase' diff --git a/packages/purgecss/__tests__/test_examples/chaining_rules/index.css b/packages/purgecss/__tests__/test_examples/chaining-rules/index.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/chaining_rules/index.css rename to packages/purgecss/__tests__/test_examples/chaining-rules/index.css diff --git a/packages/purgecss/__tests__/test_examples/chaining_rules/index.html b/packages/purgecss/__tests__/test_examples/chaining-rules/index.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/chaining_rules/index.html rename to packages/purgecss/__tests__/test_examples/chaining-rules/index.html diff --git a/packages/purgecss/__tests__/test_examples/combined/combined.css b/packages/purgecss/__tests__/test_examples/combined/combined.css deleted file mode 100644 index 84d1f4cc..00000000 --- a/packages/purgecss/__tests__/test_examples/combined/combined.css +++ /dev/null @@ -1,7 +0,0 @@ -.added-together { - color: black; -} - -.array-joined { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/combined/combined.js b/packages/purgecss/__tests__/test_examples/combined/combined.js deleted file mode 100644 index 4bca8745..00000000 --- a/packages/purgecss/__tests__/test_examples/combined/combined.js +++ /dev/null @@ -1,3 +0,0 @@ -const x = "added-" + "together"; - -const y = ["array", "joined"].join("-"); diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.css b/packages/purgecss/__tests__/test_examples/comments/ignore_comment.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.css rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment.css diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.html b/packages/purgecss/__tests__/test_examples/comments/ignore_comment.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.html rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment.html diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment_range/index.css b/packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment_range/index.css rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.css diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment_range/index.html b/packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment_range/index.html rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.html diff --git a/packages/purgecss/__tests__/test_examples/variables/variables.css b/packages/purgecss/__tests__/test_examples/css-variables/variables.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/variables/variables.css rename to packages/purgecss/__tests__/test_examples/css-variables/variables.css diff --git a/packages/purgecss/__tests__/test_examples/variables/variables.html b/packages/purgecss/__tests__/test_examples/css-variables/variables.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/variables/variables.html rename to packages/purgecss/__tests__/test_examples/css-variables/variables.html diff --git a/packages/purgecss/__tests__/test_examples/font_face/font_face.css b/packages/purgecss/__tests__/test_examples/font-faces/font_face.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/font_face/font_face.css rename to packages/purgecss/__tests__/test_examples/font-faces/font_face.css diff --git a/packages/purgecss/__tests__/test_examples/font_face/font_face.html b/packages/purgecss/__tests__/test_examples/font-faces/font_face.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/font_face/font_face.html rename to packages/purgecss/__tests__/test_examples/font-faces/font_face.html diff --git a/packages/purgecss/__tests__/test_examples/media_queries/media_queries.css b/packages/purgecss/__tests__/test_examples/media-queries/media_queries.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/media_queries/media_queries.css rename to packages/purgecss/__tests__/test_examples/media-queries/media_queries.css diff --git a/packages/purgecss/__tests__/test_examples/media_queries/media_queries.html b/packages/purgecss/__tests__/test_examples/media-queries/media_queries.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/media_queries/media_queries.html rename to packages/purgecss/__tests__/test_examples/media-queries/media_queries.html diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.css b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.css deleted file mode 100644 index fc5c1d3c..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.css +++ /dev/null @@ -1,3 +0,0 @@ -.blank-space { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.html b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.html deleted file mode 100644 index 41868f6b..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.js b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.js deleted file mode 100644 index 3d5c18e2..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.js +++ /dev/null @@ -1 +0,0 @@ -'blank-space' diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files2.css b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files2.css deleted file mode 100644 index 9562d03c..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files2.css +++ /dev/null @@ -1,7 +0,0 @@ -.taylor-swift { - color: black; -} - -.shake-it-off { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.css b/packages/purgecss/__tests__/test_examples/others/remove_unused.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.css rename to packages/purgecss/__tests__/test_examples/others/remove_unused.css diff --git a/packages/purgecss/__tests__/test_examples/others/remove_unused.js b/packages/purgecss/__tests__/test_examples/others/remove_unused.js new file mode 100644 index 00000000..7401750c --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/others/remove_unused.js @@ -0,0 +1 @@ +".used-class"; diff --git a/packages/purgecss/__tests__/test_examples/special_characters/special_characters.css b/packages/purgecss/__tests__/test_examples/others/special_characters.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/special_characters/special_characters.css rename to packages/purgecss/__tests__/test_examples/others/special_characters.css diff --git a/packages/purgecss/__tests__/test_examples/others/special_characters.js b/packages/purgecss/__tests__/test_examples/others/special_characters.js new file mode 100644 index 00000000..a3325ede --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/others/special_characters.js @@ -0,0 +1,7 @@ +"@home"; + +"+rounded"; + +"button"; + +".md:w-1/3"; diff --git a/packages/purgecss/__tests__/test_examples/not/not.css b/packages/purgecss/__tests__/test_examples/pseudo-class/not.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/not/not.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/not.css diff --git a/packages/purgecss/__tests__/test_examples/not/not.html b/packages/purgecss/__tests__/test_examples/pseudo-class/not.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/not/not.html rename to packages/purgecss/__tests__/test_examples/pseudo-class/not.html diff --git a/packages/purgecss/__tests__/test_examples/nth_child/nth_child.css b/packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/nth_child/nth_child.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.css diff --git a/packages/purgecss/__tests__/test_examples/nth_child/nth_child.html b/packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/nth_child/nth_child.html rename to packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.html diff --git a/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.css b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.css diff --git a/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js new file mode 100644 index 00000000..14e54281 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js @@ -0,0 +1 @@ +"div"; diff --git a/packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.css b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.css diff --git a/packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.html b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.html rename to packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.html diff --git a/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.js b/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.js deleted file mode 100644 index 99900fbc..00000000 --- a/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.js +++ /dev/null @@ -1 +0,0 @@ -'div' diff --git a/packages/purgecss/__tests__/test_examples/simple/simple.css b/packages/purgecss/__tests__/test_examples/rejected/simple.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/simple/simple.css rename to packages/purgecss/__tests__/test_examples/rejected/simple.css diff --git a/packages/purgecss/__tests__/test_examples/rejected/simple.js b/packages/purgecss/__tests__/test_examples/rejected/simple.js new file mode 100644 index 00000000..74ba283f --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/rejected/simple.js @@ -0,0 +1,5 @@ +"single"; + +"double-class"; + +"triple-simple-class"; diff --git a/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.js b/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.js deleted file mode 100644 index 76539783..00000000 --- a/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.js +++ /dev/null @@ -1 +0,0 @@ -'.used-class' diff --git a/packages/purgecss/__tests__/test_examples/whitelist/whitelist.css b/packages/purgecss/__tests__/test_examples/safelist/whitelist.css similarity index 90% rename from packages/purgecss/__tests__/test_examples/whitelist/whitelist.css rename to packages/purgecss/__tests__/test_examples/safelist/whitelist.css index 2b9fe22b..06554780 100644 --- a/packages/purgecss/__tests__/test_examples/whitelist/whitelist.css +++ b/packages/purgecss/__tests__/test_examples/safelist/whitelist.css @@ -22,6 +22,6 @@ button { background-color: red; } -.random[data-v-test] { +[data-v-test] { color: green; } diff --git a/packages/purgecss/__tests__/test_examples/whitelist/whitelist.html b/packages/purgecss/__tests__/test_examples/safelist/whitelist.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist/whitelist.html rename to packages/purgecss/__tests__/test_examples/safelist/whitelist.html diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.css b/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.css rename to packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.css diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.html b/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.html rename to packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.html diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.css b/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.css rename to packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.css diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.html b/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist_patterns_greedy/whitelist_patterns_greedy.html rename to packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.html diff --git a/packages/purgecss/__tests__/test_examples/simple/simple.js b/packages/purgecss/__tests__/test_examples/simple/simple.js deleted file mode 100644 index 53693f99..00000000 --- a/packages/purgecss/__tests__/test_examples/simple/simple.js +++ /dev/null @@ -1,5 +0,0 @@ -'single' - -'double-class' - -'triple-simple-class' diff --git a/packages/purgecss/__tests__/test_examples/special_characters/special_characters.js b/packages/purgecss/__tests__/test_examples/special_characters/special_characters.js deleted file mode 100644 index 3f7d0d42..00000000 --- a/packages/purgecss/__tests__/test_examples/special_characters/special_characters.js +++ /dev/null @@ -1,7 +0,0 @@ -'@home' - -'+rounded' - -'button' - -'.md:w-1/3' diff --git a/packages/purgecss/__tests__/test_examples/tables/tables.css b/packages/purgecss/__tests__/test_examples/tables/tables.css deleted file mode 100644 index 73417e9b..00000000 --- a/packages/purgecss/__tests__/test_examples/tables/tables.css +++ /dev/null @@ -1,8 +0,0 @@ -.table-responsive > .table > tbody > tr > td, -.table-responsive > .table > tbody > tr > th, -.table-responsive > .table > tfoot > tr > td, -.table-responsive > .table > tfoot > tr > th, -.table-responsive > .table > thead > tr > td, -.table-responsive > .table > thead > tr > th { - white-space: normal; -} \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/tables/tables.html b/packages/purgecss/__tests__/test_examples/tables/tables.html deleted file mode 100644 index 1504bc8f..00000000 --- a/packages/purgecss/__tests__/test_examples/tables/tables.html +++ /dev/null @@ -1,9 +0,0 @@ -
- - - - - - -
Something
-
\ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.css b/packages/purgecss/__tests__/test_examples/wildcard/wildcard.css deleted file mode 100644 index 62362c98..00000000 --- a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.css +++ /dev/null @@ -1,21 +0,0 @@ -*, -::before, -::after { - background: black; -} - -:root { - background: black; -} - -::selection { - background: black; -} - -::-webkit-scrollbar { - background: black; -} - -::-webkit-scrollbar:vertical { - background: black; -} diff --git a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.html b/packages/purgecss/__tests__/test_examples/wildcard/wildcard.html deleted file mode 100644 index 834fdf11..00000000 --- a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - wildcard - - - - - diff --git a/packages/purgecss/__tests__/utils.ts b/packages/purgecss/__tests__/utils.ts new file mode 100644 index 00000000..69663d66 --- /dev/null +++ b/packages/purgecss/__tests__/utils.ts @@ -0,0 +1,22 @@ +export const ROOT_TEST_EXAMPLES = + "./packages/purgecss/__tests__/test_examples/"; + +export function findInCSS( + expect: jest.Expect, + selectors: string[], + css: string +): void { + selectors.forEach((selector) => { + expect(css.includes(selector)).toBe(true); + }); +} + +export function notFindInCSS( + expect: jest.Expect, + selectors: string[], + css: string +): void { + selectors.forEach((selector) => { + expect(css.includes(selector)).toBe(false); + }); +} diff --git a/packages/purgecss/__tests__/whitelist.test.ts b/packages/purgecss/__tests__/whitelist.test.ts deleted file mode 100644 index f62c7390..00000000 --- a/packages/purgecss/__tests__/whitelist.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -import PurgeCSS from "./../src/index"; - -const root = "./packages/purgecss/__tests__/test_examples/"; - -describe("whitelist", () => { - let purgedCSS = ""; - beforeAll(async (done) => { - const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}whitelist/whitelist.html`], - css: [`${root}whitelist/whitelist.css`], - whitelist: ["random", "h1", "yep", "button"], - whitelistPatterns: [/nav-/, /data-v-.*/], - }); - purgedCSS = resultsPurge[0].css; - done(); - }); - - it("finds attr", () => { - expect(purgedCSS.includes("[data-v-test]")).toBe(true); - }); - - it("finds random class", () => { - expect(purgedCSS.includes(".random")).toBe(true); - }); - - it("finds h1", () => { - expect(purgedCSS.includes("h1")).toBe(true); - }); - - it("finds #yep", () => { - expect(purgedCSS.includes("#yep")).toBe(true); - }); - - it("finds button", () => { - expect(purgedCSS.includes("button")).toBe(true); - }); - - it("finds .nav-blue", () => { - expect(purgedCSS.includes(".nav-blue")).toBe(true); - }); - - it("finds .nav-red", () => { - expect(purgedCSS.includes(".nav-red")).toBe(true); - }); -}); - -describe("whitelistPatternsChildren", () => { - let purgedCSS: string; - beforeAll(async () => { - const resultsPurge = await new PurgeCSS().purge({ - content: [ - `${root}whitelist_patterns_children/whitelist_patterns_children.html`, - ], - css: [ - `${root}whitelist_patterns_children/whitelist_patterns_children.css`, - ], - whitelistPatternsChildren: [/^card$/], - }); - purgedCSS = resultsPurge[0].css; - }); - - it("finds card class", () => { - expect(purgedCSS.includes(".card")).toBe(true); - }); - - it("finds card--title", () => { - expect(purgedCSS.includes(".title")).toBe(false); - }); - - it("finds card--content", () => { - expect(purgedCSS.includes(".card .content")).toBe(true); - }); - - it("finds btn", () => { - expect(purgedCSS.includes(".btn")).toBe(true); - }); - - it("finds btn yellow", () => { - expect(purgedCSS.includes(".card .btn .yellow")).toBe(true); - }); - - it("finds btn red", () => { - expect(purgedCSS.includes(".btn .red")).toBe(false); - }); - - it("excludes btn--green", () => { - expect(purgedCSS.includes(".btn__green")).toBe(false); - }); -}); - -describe("whitelistPatternsGreedy", () => { - let purgedCSS: string; - beforeAll(async () => { - const resultsPurge = await new PurgeCSS().purge({ - content: [ - `${root}whitelist_patterns_greedy/whitelist_patterns_greedy.html`, - ], - css: [`${root}whitelist_patterns_greedy/whitelist_patterns_greedy.css`], - whitelistPatternsGreedy: [/data-v-.*/], - }); - purgedCSS = resultsPurge[0].css; - }); - - it("finds card", () => { - expect(purgedCSS.includes(".card")).toBe(true); - }); - - it("finds card with data-v attribute", () => { - expect(purgedCSS.includes(".card[data-v-test]")).toBe(true); - }); - - it("excludes card--large", () => { - expect(purgedCSS.includes(".card.card--large")).toBe(false); - }); - - it("finds card--large with data-v attribute", () => { - expect(purgedCSS.includes(".card[data-v-test].card--large")).toBe(true); - }); - - it("excludes card-content", () => { - expect(purgedCSS.includes(".card .card-content")).toBe(false); - }); - - it("finds card-content inside card with data-v attribute", () => { - expect(purgedCSS.includes(".card[data-v-test] .card-content")).toBe(true); - }); -}); From 86cfff9d7c418d44566e6434a2b764d4e943b6e2 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Thu, 2 Jul 2020 20:33:08 +0100 Subject: [PATCH 06/36] refactor: rename whitelist to safelist - rename whitelist to safelist in the code --- packages/gulp-purgecss/src/types/index.ts | 32 +----- packages/postcss-purgecss/src/index.ts | 7 +- packages/postcss-purgecss/src/types/index.ts | 8 +- .../webpack.config.js | 97 +++++++++--------- .../simple-with-exclusion/webpack.config.js | 98 +++++++++---------- .../__tests__/cases/simple/webpack.config.js | 92 ++++++++--------- packages/purgecss-webpack-plugin/src/index.ts | 18 +--- .../src/types/index.ts | 14 +-- packages/purgecss/src/index.ts | 78 ++++++++------- ...rnal-whitelist.ts => internal-safelist.ts} | 2 +- packages/purgecss/src/options.ts | 11 ++- packages/purgecss/src/types/index.ts | 22 +++-- .../generator/templates/postcss.config.js | 34 ++++--- 13 files changed, 261 insertions(+), 252 deletions(-) rename packages/purgecss/src/{internal-whitelist.ts => internal-safelist.ts} (74%) diff --git a/packages/gulp-purgecss/src/types/index.ts b/packages/gulp-purgecss/src/types/index.ts index 27cc067f..a168b893 100644 --- a/packages/gulp-purgecss/src/types/index.ts +++ b/packages/gulp-purgecss/src/types/index.ts @@ -1,28 +1,6 @@ -export interface RawContent { - extension: string; - raw: T; -} -export interface RawCSS { - raw: string; -} -type ExtractorFunction = (content: T) => string[]; -export interface Extractors { - extensions: string[]; - extractor: ExtractorFunction; -} -export interface UserDefinedOptions { - content: Array; - defaultExtractor?: ExtractorFunction; - extractors?: Array; - fontFace?: boolean; - keyframes?: boolean; - output?: string; - rejected?: boolean; - stdin?: boolean; - stdout?: boolean; - variables?: boolean; - whitelist?: string[]; - whitelistPatterns?: Array; - whitelistPatternsChildren?: Array; - whitelistPatternsGreedy?: Array; +import { UserDefinedOptions as PurgeCSSUserDefinedOptions } from "../../../purgecss/src/types/index"; + +export interface UserDefinedOptions + extends Omit { + content: string[]; } diff --git a/packages/postcss-purgecss/src/index.ts b/packages/postcss-purgecss/src/index.ts index 7342cdb4..d2cf432d 100644 --- a/packages/postcss-purgecss/src/index.ts +++ b/packages/postcss-purgecss/src/index.ts @@ -1,5 +1,9 @@ import postcss from "postcss"; -import PurgeCSS, { defaultOptions, mergeExtractorSelectors } from "purgecss"; +import PurgeCSS, { + defaultOptions, + mergeExtractorSelectors, + standardizeSafelist, +} from "purgecss"; import { RawContent, UserDefinedOptions } from "./types"; @@ -11,6 +15,7 @@ const purgeCSSPlugin = postcss.plugin>( const options = { ...defaultOptions, ...opts, + safelist: standardizeSafelist(opts?.safelist), }; if (opts && typeof opts.contentFunction === "function") { diff --git a/packages/postcss-purgecss/src/types/index.ts b/packages/postcss-purgecss/src/types/index.ts index 6eb83943..490b6d32 100644 --- a/packages/postcss-purgecss/src/types/index.ts +++ b/packages/postcss-purgecss/src/types/index.ts @@ -1,3 +1,5 @@ +import { UserDefinedSafelist } from "../../../purgecss/src/types/index"; + export interface RawContent { extension: string; raw: T; @@ -10,6 +12,7 @@ export interface Extractors { extensions: string[]; extractor: ExtractorFunction; } + export interface UserDefinedOptions { content?: Array; contentFunction?: (sourceFile: string) => Array; @@ -23,8 +26,5 @@ export interface UserDefinedOptions { stdin?: boolean; stdout?: boolean; variables?: boolean; - whitelist?: string[]; - whitelistPatterns?: Array; - whitelistPatternsChildren?: Array; - whitelistPatternsGreedy?: Array; + safelist?: UserDefinedSafelist; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js index 2c18d6a2..c6569d69 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js @@ -1,54 +1,57 @@ -const path = require('path') -const glob = require('glob') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('../../../src/').default +const path = require("path"); +const glob = require("glob"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const PurgecssPlugin = require("../../../src/").default; const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || []; const PATHS = { - src: path.join(__dirname, 'src') -} + src: path.join(__dirname, "src"), +}; module.exports = { - mode: 'development', - entry: './src/index.js', - context: path.resolve(__dirname), - optimization: { - splitChunks: { - cacheGroups: { - styles: { - name: 'styles', - test: /\.css$/, - chunks: 'all', - enforce: true - } - } - } + mode: "development", + entry: "./src/index.js", + context: path.resolve(__dirname), + optimization: { + splitChunks: { + cacheGroups: { + styles: { + name: "styles", + test: /\.css$/, + chunks: "all", + enforce: true, + }, + }, }, - module: { - rules: [ - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] - } - ] - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name].css' - }), - new PurgecssPlugin({ - paths: () => glob.sync(`${PATHS.src}/*`), - whitelist: () => ['whitelisted'], - whitelistPatterns: () => [/^whitelistedPat/], - whitelistPatternsChildren: () => [/^whitelistedPatternChildren/], - whitelistPatternsGreedy: () => [/^whitelistedPatternGreedy/], - extractors: [ - { - extractor: customExtractor, - extensions: ['html', 'js'] - } - ] - }) - ] -} + }, + module: { + rules: [ + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + new PurgecssPlugin({ + paths: () => glob.sync(`${PATHS.src}/*`), + safelist: () => { + return { + standard: ["whitelisted", /^whitelistedPat/], + deep: [/^whitelistedPatternChildren/], + greedy: [/^whitelistedPatternGreedy/], + }; + }, + extractors: [ + { + extractor: customExtractor, + extensions: ["html", "js"], + }, + ], + }), + ], +}; diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js index 6703dcec..8a2c8751 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js @@ -1,55 +1,55 @@ -const path = require('path') -const glob = require('glob') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('../../../src/').default +const path = require("path"); +const glob = require("glob"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const PurgecssPlugin = require("../../../src/").default; -const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || [] +const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || []; const PATHS = { - src: path.join(__dirname, 'src') -} + src: path.join(__dirname, "src"), +}; module.exports = { - mode: 'development', - entry: { - bundle: './src/index.js', - legacy: './src/legacy.js' + mode: "development", + entry: { + bundle: "./src/index.js", + legacy: "./src/legacy.js", + }, + optimization: { + splitChunks: { + cacheGroups: { + styles: { + name: "styles", + test: /\.css$/, + chunks: "all", + enforce: true, + }, + }, }, - optimization: { - splitChunks: { - cacheGroups: { - styles: { - name: 'styles', - test: /\.css$/, - chunks: 'all', - enforce: true - } - } - } - }, - module: { - rules: [ - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] - } - ] - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name].css' - }), - new PurgecssPlugin({ - paths: glob.sync(`${PATHS.src}/*`), - styleExtensions: ['.css'], - whitelist: ['whitelisted'], - only: ['bundle'], - extractors: [ - { - extractor: customExtractor, - extensions: ['html', 'js'] - } - ] - }) - ] -} + }, + module: { + rules: [ + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + new PurgecssPlugin({ + paths: glob.sync(`${PATHS.src}/*`), + styleExtensions: [".css"], + safelist: ["whitelisted"], + only: ["bundle"], + extractors: [ + { + extractor: customExtractor, + extensions: ["html", "js"], + }, + ], + }), + ], +}; diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js index 78bbc824..2a47b7e1 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js @@ -1,54 +1,54 @@ -const path = require('path') -const glob = require('glob') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('../../../src/').default +const path = require("path"); +const glob = require("glob"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const PurgecssPlugin = require("../../../src/").default; const customExtractor = (content) => { - const res = content.match(/[A-z0-9-:/]+/g) || [] - // console.log('hel', res) - return res + const res = content.match(/[A-z0-9-:/]+/g) || []; + // console.log('hel', res) + return res; }; const PATHS = { - src: path.join(__dirname, 'src') -} + src: path.join(__dirname, "src"), +}; module.exports = { - mode: 'development', - optimization: { - splitChunks: { - cacheGroups: { - styles: { - name: 'styles', - test: /\.css$/, - chunks: 'all', - enforce: true - } - } - } - }, - entry: './src/index.js', - module: { - rules: [ - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] - } - ] + mode: "development", + optimization: { + splitChunks: { + cacheGroups: { + styles: { + name: "styles", + test: /\.css$/, + chunks: "all", + enforce: true, + }, + }, }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name].css' - }), - new PurgecssPlugin({ - paths: glob.sync(`${PATHS.src}/*`), - whitelist: ['whitelisted'], - extractors: [ - { - extractor: customExtractor, - extensions: ['html', 'js'] - } - ] - }) - ] -} + }, + entry: "./src/index.js", + module: { + rules: [ + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + new PurgecssPlugin({ + paths: glob.sync(`${PATHS.src}/*`), + safelist: ["whitelisted"], + extractors: [ + { + extractor: customExtractor, + extensions: ["html", "js"], + }, + ], + }), + ], +}; diff --git a/packages/purgecss-webpack-plugin/src/index.ts b/packages/purgecss-webpack-plugin/src/index.ts index 7b4e42d4..8486a381 100644 --- a/packages/purgecss-webpack-plugin/src/index.ts +++ b/packages/purgecss-webpack-plugin/src/index.ts @@ -107,17 +107,8 @@ export default class PurgeCSSPlugin { ], }; - if (typeof options.whitelist === "function") { - options.whitelist = options.whitelist(); - } - if (typeof options.whitelistPatterns === "function") { - options.whitelistPatterns = options.whitelistPatterns(); - } - if (typeof options.whitelistPatternsChildren === "function") { - options.whitelistPatternsChildren = options.whitelistPatternsChildren(); - } - if (typeof options.whitelistPatternsGreedy === "function") { - options.whitelistPatternsGreedy = options.whitelistPatternsGreedy(); + if (typeof options.safelist === "function") { + options.safelist = options.safelist(); } const purgecss = await new PurgeCSS().purge({ @@ -130,10 +121,7 @@ export default class PurgeCSSPlugin { output: options.output, rejected: options.rejected, variables: options.variables, - whitelist: options.whitelist, - whitelistPatterns: options.whitelistPatterns, - whitelistPatternsChildren: options.whitelistPatternsChildren, - whitelistPatternsGreedy: options.whitelistPatternsGreedy, + safelist: options.safelist, }); const purged = purgecss[0]; diff --git a/packages/purgecss-webpack-plugin/src/types/index.ts b/packages/purgecss-webpack-plugin/src/types/index.ts index 74f38302..e6e0b1ea 100644 --- a/packages/purgecss-webpack-plugin/src/types/index.ts +++ b/packages/purgecss-webpack-plugin/src/types/index.ts @@ -1,3 +1,8 @@ +import { + ComplexSafelist, + StringRegExpArray, +} from "./../../../purgecss/src/types/index"; + export interface RawContent { extension: string; raw: T; @@ -12,8 +17,8 @@ export interface Extractors { } type PathFunction = () => string[]; -type WhitelistFunction = () => string[]; -type WhitelistPatternsFunction = () => Array; + +type SafelistFunction = () => ComplexSafelist; export interface UserDefinedOptions { paths: string[] | PathFunction; @@ -28,10 +33,7 @@ export interface UserDefinedOptions { stdout?: boolean; variables?: boolean; verbose?: boolean; - whitelist?: string[] | WhitelistFunction; - whitelistPatterns?: Array | WhitelistPatternsFunction; - whitelistPatternsChildren?: Array | WhitelistPatternsFunction; - whitelistPatternsGreedy?: Array | WhitelistPatternsFunction; + safelist?: StringRegExpArray | ComplexSafelist | SafelistFunction; only?: string[]; } diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index ced09fcd..c395a9f4 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -20,6 +20,8 @@ import { RawCSS, UserDefinedOptions, ExtractorResultDetailed, + UserDefinedSafelist, + ComplexSafelist, } from "./types"; import { @@ -30,7 +32,7 @@ import { IGNORE_ANNOTATION_END, IGNORE_ANNOTATION_CURRENT, } from "./constants"; -import { CSS_WHITELIST } from "./internal-whitelist"; +import { CSS_SAFELIST } from "./internal-safelist"; import VariablesStructure from "./VariablesStructure"; import ExtractorResultSets from "./ExtractorResultSets"; @@ -39,6 +41,21 @@ const asyncFs = { readFile: promisify(fs.readFile), }; +export function standardizeSafelist( + userDefinedSafelist: UserDefinedSafelist = [] +): Required { + if (Array.isArray(userDefinedSafelist)) { + return { + ...defaultOptions.safelist, + standard: userDefinedSafelist, + }; + } + return { + ...defaultOptions.safelist, + ...userDefinedSafelist, + }; +} + /** * Load the configuration file from the path * @param configFile Path of the config file @@ -56,6 +73,7 @@ export async function setOptions( return { ...defaultOptions, ...options, + safelist: standardizeSafelist(options.safelist), }; } @@ -525,42 +543,35 @@ class PurgeCSS { } /** - * Check if the selector is whitelisted by the option whitelist or whitelistPatterns + * Check if the selector is safelisted with the option safelist standard * @param selector css selector */ - private isSelectorWhitelisted(selector: string): boolean { - return ( - CSS_WHITELIST.includes(selector) || - (this.options.whitelist && - this.options.whitelist.some((v: string) => v === selector)) || - (this.options.whitelistPatterns && - this.options.whitelistPatterns.some((v: RegExp) => v.test(selector))) - ); + private isSelectorSafelisted(selector: string): boolean { + const isSafelisted = this.options.safelist.standard.some((safelistItem) => { + return typeof safelistItem === "string" + ? safelistItem === selector + : safelistItem.test(selector); + }); + return CSS_SAFELIST.includes(selector) || isSafelisted; } /** - * Check if the selector is whitelisted by the whitelistPatternsChildren option + * Check if the selector is safelisted with the option safelist deep * @param selector selector */ - private isSelectorWhitelistedChildren(selector: string): boolean { - return ( - this.options.whitelistPatternsChildren && - this.options.whitelistPatternsChildren.some((pattern: RegExp) => - pattern.test(selector) - ) + private isSelectorSafelistedDeep(selector: string): boolean { + return this.options.safelist.deep.some((safelistItem) => + safelistItem.test(selector) ); } /** - * Check if the selector is whitelisted by the whitelistPatternsGreedy option + * Check if the selector is safelisted with the option safelist greedy * @param selector selector */ - private isSelectorWhitelistedGreedy(selector: string): boolean { - return ( - this.options.whitelistPatternsGreedy && - this.options.whitelistPatternsGreedy.some((pattern: RegExp) => - pattern.test(selector) - ) + private isSelectorSafelistedGreedy(selector: string): boolean { + return this.options.safelist.greedy.some((safelistItem) => + safelistItem.test(selector) ); } @@ -577,6 +588,7 @@ class PurgeCSS { : { ...defaultOptions, ...userOptions, + safelist: standardizeSafelist(userOptions.safelist), }; const { content, css, extractors } = this.options; @@ -652,14 +664,14 @@ class PurgeCSS { // ignore the selector if it is inside a pseudo class if (isInPseudoClass(selector)) return true; - // if there is any greedy whitelist pattern, run all the selector parts through them + // if there is any greedy safelist pattern, run all the selector parts through them // if there is any match, return true - if (this.options.whitelistPatternsGreedy?.length) { + if (this.options.safelist.greedy.length > 0) { const selectorParts = selector.nodes.map(this.getSelectorValue); if ( selectorParts.some( (selectorPart) => - selectorPart && this.isSelectorWhitelistedGreedy(selectorPart) + selectorPart && this.isSelectorSafelistedGreedy(selectorPart) ) ) { return true; @@ -671,17 +683,17 @@ class PurgeCSS { for (const selectorNode of selector.nodes) { const selectorValue = this.getSelectorValue(selectorNode); - // if the selector is whitelisted with children + // if the selector is safelisted with children // returns true to keep all children selectors - if (selectorValue && this.isSelectorWhitelistedChildren(selectorValue)) { + if (selectorValue && this.isSelectorSafelistedDeep(selectorValue)) { return true; } - // The selector is found in the internal and user-defined whitelist + // The selector is found in the internal and user-defined safelist if ( selectorValue && - (CSS_WHITELIST.includes(selectorValue) || - this.isSelectorWhitelisted(selectorValue)) + (CSS_SAFELIST.includes(selectorValue) || + this.isSelectorSafelisted(selectorValue)) ) { isPresent = true; continue; @@ -711,7 +723,7 @@ class PurgeCSS { continue; } - // selector is not whitelisted + // selector is not safelisted // and it has not been found as an attribute/class/id/tag if (!isPresent) { return false; diff --git a/packages/purgecss/src/internal-whitelist.ts b/packages/purgecss/src/internal-safelist.ts similarity index 74% rename from packages/purgecss/src/internal-whitelist.ts rename to packages/purgecss/src/internal-safelist.ts index 23db9099..edd91b7d 100644 --- a/packages/purgecss/src/internal-whitelist.ts +++ b/packages/purgecss/src/internal-safelist.ts @@ -1,4 +1,4 @@ -export const CSS_WHITELIST = [ +export const CSS_SAFELIST = [ "*", "::-webkit-scrollbar", "::selection", diff --git a/packages/purgecss/src/options.ts b/packages/purgecss/src/options.ts index b543cf98..e96afa20 100644 --- a/packages/purgecss/src/options.ts +++ b/packages/purgecss/src/options.ts @@ -12,8 +12,11 @@ export const defaultOptions: Options = { stdin: false, stdout: false, variables: false, - whitelist: [], - whitelistPatterns: [], - whitelistPatternsChildren: [], - whitelistPatternsGreedy: [], + safelist: { + standard: [], + deep: [], + greedy: [], + variables: [], + keyframes: [], + }, }; diff --git a/packages/purgecss/src/types/index.ts b/packages/purgecss/src/types/index.ts index 9270c8cb..42adc1cb 100644 --- a/packages/purgecss/src/types/index.ts +++ b/packages/purgecss/src/types/index.ts @@ -40,6 +40,18 @@ export interface Extractors { export type IgnoreType = "end" | "start" | "next"; +export type StringRegExpArray = Array; + +export type ComplexSafelist = { + standard?: StringRegExpArray; + deep?: RegExp[]; + greedy?: RegExp[]; + variables?: StringRegExpArray; + keyframes?: StringRegExpArray; +}; + +export type UserDefinedSafelist = StringRegExpArray | ComplexSafelist; + export interface UserDefinedOptions { content: Array; css: Array; @@ -52,10 +64,7 @@ export interface UserDefinedOptions { stdin?: boolean; stdout?: boolean; variables?: boolean; - whitelist?: string[]; - whitelistPatterns?: Array; - whitelistPatternsChildren?: Array; - whitelistPatternsGreedy?: Array; + safelist?: UserDefinedSafelist; } export interface Options { @@ -70,10 +79,7 @@ export interface Options { stdin: boolean; stdout: boolean; variables: boolean; - whitelist: string[]; - whitelistPatterns: Array; - whitelistPatternsChildren: Array; - whitelistPatternsGreedy: Array; + safelist: Required; } export interface ResultPurge { diff --git a/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js b/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js index 1353779a..1da633c3 100644 --- a/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js +++ b/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js @@ -1,15 +1,27 @@ -const IN_PRODUCTION = process.env.NODE_ENV === 'production' +const IN_PRODUCTION = process.env.NODE_ENV === "production"; module.exports = { plugins: [ - IN_PRODUCTION && require('@fullhuman/postcss-purgecss')({ - content: [ `./public/**/*.html`, `./src/**/*.vue` ], - defaultExtractor (content) { - const contentWithoutStyleBlocks = content.replace(/<\/style>/gi, '') - return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] - }, - whitelist: [], - whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], - }) + IN_PRODUCTION && + require("@fullhuman/postcss-purgecss")({ + content: [`./public/**/*.html`, `./src/**/*.vue`], + defaultExtractor(content) { + const contentWithoutStyleBlocks = content.replace( + /<\/style>/gi, + "" + ); + return ( + contentWithoutStyleBlocks.match( + /[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g + ) || [] + ); + }, + safelist: [ + /-(leave|enter|appear)(|-(to|from|active))$/, + /^(?!(|.*?:)cursor-move).+-move$/, + /^router-link(|-exact)-active$/, + /data-v-.*/, + ], + }), ], -} \ No newline at end of file +}; From cc43d4c0acaa5ddcaf7be2f2507daffd98aa743e Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sat, 4 Jul 2020 13:46:25 +0100 Subject: [PATCH 07/36] refactor: rename whitelist to safelist - CLI option - Documentation #428 #439 --- README.md | 2 +- docs/.vuepress/config.js | 2 +- docs/CLI.md | 8 +- docs/README.md | 9 +- docs/api.md | 5 -- docs/configuration.md | 89 +++++++++++++------ docs/guides/nuxt.md | 2 +- docs/guides/vue.md | 3 +- docs/guides/wordpress.md | 16 ++-- docs/plugins/webpack.md | 36 +++----- docs/{whitelisting.md => safelisting.md} | 30 ++++--- packages/gulp-purgecss/src/types/index.ts | 2 +- packages/purgecss-webpack-plugin/README.md | 85 ++++-------------- .../expected/styles.css | 6 +- .../src/content.html | 0 .../path-and-safelist-functions/src/index.js | 1 + .../src/style.css | 6 +- .../webpack.config.js | 6 +- .../path-and-whitelist-functions/src/index.js | 1 - .../simple-with-exclusion/expected/styles.css | 2 +- .../src/style_that_we_want_to_purge.css | 2 +- .../simple-with-exclusion/webpack.config.js | 2 +- .../cases/simple/expected/styles.css | 2 +- .../__tests__/cases/simple/src/style.css | 2 +- .../__tests__/cases/simple/webpack.config.js | 2 +- .../__tests__/index.test.ts | 2 +- packages/purgecss-with-wordpress/README.md | 14 +-- packages/purgecss-with-wordpress/index.js | 42 +++++---- packages/purgecss/README.md | 2 +- packages/purgecss/__tests__/safelist.test.ts | 20 ++--- .../safelist/{whitelist.css => safelist.css} | 0 .../{whitelist.html => safelist.html} | 0 ...ren.css => safelist_patterns_children.css} | 0 ...n.html => safelist_patterns_children.html} | 0 ...reedy.css => safelist_patterns_greedy.css} | 0 ...edy.html => safelist_patterns_greedy.html} | 0 .../purgecss/bin/{purgecss => purgecss.js} | 63 +++++++------ packages/purgecss/package.json | 2 +- packages/vue-cli-plugin-purgecss/README.md | 3 +- 39 files changed, 218 insertions(+), 251 deletions(-) rename docs/{whitelisting.md => safelisting.md} (66%) rename packages/purgecss-webpack-plugin/__tests__/cases/{path-and-whitelist-functions => path-and-safelist-functions}/expected/styles.css (62%) rename packages/purgecss-webpack-plugin/__tests__/cases/{path-and-whitelist-functions => path-and-safelist-functions}/src/content.html (100%) create mode 100644 packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js rename packages/purgecss-webpack-plugin/__tests__/cases/{path-and-whitelist-functions => path-and-safelist-functions}/src/style.css (67%) rename packages/purgecss-webpack-plugin/__tests__/cases/{path-and-whitelist-functions => path-and-safelist-functions}/webpack.config.js (87%) delete mode 100644 packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/index.js rename packages/purgecss/__tests__/test_examples/safelist/{whitelist.css => safelist.css} (100%) rename packages/purgecss/__tests__/test_examples/safelist/{whitelist.html => safelist.html} (100%) rename packages/purgecss/__tests__/test_examples/safelist/{whitelist_patterns_children.css => safelist_patterns_children.css} (100%) rename packages/purgecss/__tests__/test_examples/safelist/{whitelist_patterns_children.html => safelist_patterns_children.html} (100%) rename packages/purgecss/__tests__/test_examples/safelist/{whitelist_patterns_greedy.css => safelist_patterns_greedy.css} (100%) rename packages/purgecss/__tests__/test_examples/safelist/{whitelist_patterns_greedy.html => safelist_patterns_greedy.html} (100%) rename packages/purgecss/bin/{purgecss => purgecss.js} (53%) diff --git a/README.md b/README.md index d04a94e0..ab7899ef 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ You can find the PurgeCSS documentation on [this website](https://purgecss.com). - [Configuration](https://purgecss.com/configuration.html) - [Command Line Interface](https://purgecss.com/CLI.html) - [Programmatic API](https://purgecss.com/api.html) -- [Whitelisting](https://purgecss.com/whitelisting.html) +- [Whitelisting](https://purgecss.com/safelisting.html) - [Extractors](https://purgecss.com/extractors.html) - [Comparison](https://purgecss.com/comparison.html) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 45cde807..d72c1529 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -99,7 +99,7 @@ module.exports = { ["configuration", "Configuration"], ["CLI", "Command Line Interface"], ["api", "Programmatic API"], - ["whitelisting", "Whitelisting"], + ["safelisting", "Safelisting"], ["extractors", "Extractors"], ], }, diff --git a/docs/CLI.md b/docs/CLI.md index 4c9c471e..c774b113 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -35,7 +35,7 @@ Options: -font, --font-face option to remove unused font-faces -keyframes, --keyframes option to remove unused keyframes -rejected, --rejected option to output rejected selectors - -w, --whitelist list of classes that should not be removed (comma separated) + -s, --safelist list of classes that should not be removed (comma separated) -h, --help display help for command ``` @@ -71,10 +71,10 @@ By default, the CLI outputs the result in the console. If you wish to return the purgecss --css css/app.css --content src/index.html,"src/**/*.js" --output build/css/ ``` -### --whitelist +### --safelist -If you wish to prevent PurgeCSS from removing a specific CSS selector, you can whitelist it. +If you wish to prevent PurgeCSS from removing a specific CSS selector, you can add it to the safelist. ```text -purgecss --css css/app.css --content src/index.html --whitelist classnameToWhitelist +purgecss --css css/app.css --content src/index.html --safelist classnameToSafelist ``` diff --git a/docs/README.md b/docs/README.md index b9cac411..4bba871d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,7 +13,7 @@ meta: PurgeCSS is a tool to remove unused CSS. It can be part of your development workflow. 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. -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. +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. ## Table of Contents @@ -22,7 +22,7 @@ This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your - [Configuration](configuration.md) - [Command Line Interface](CLI.md) - [Programmatic API](api.md) -- [Whitelisting](whitelisting.md) +- [Safelisting](safelisting.md) - [Extractors](extractors.md) - [Comparison](comparison.md) @@ -42,3 +42,8 @@ This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your - [Next.js](guides/next.md) - [Razzle](guides/razzle.md) - [WordPress](guides/wordpress.md) + +### Common Questions + +- [How to use with CSS modules?](css_modules.md) +- [How to use with Ant Design?](ant_design.md) \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index 56b504b6..df9e8d26 100644 --- a/docs/api.md +++ b/docs/api.md @@ -64,8 +64,3 @@ interface ResultPurge { rejected?: string[]; } ``` - -::: tip - Take a look at the type definition file to get the complete information. - [Definition file](https://github.com/FullHuman/purgecss/tree/master/packages/purgecss/lib/purgecss.d.ts) -::: diff --git a/docs/configuration.md b/docs/configuration.md index 03e0fd41..4e9d3986 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -37,21 +37,18 @@ The options are defined by the following types: ```ts interface UserDefinedOptions { - content: Array - css: Array - defaultExtractor?: ExtractorFunction - extractors?: Array - fontFace?: boolean - keyframes?: boolean - output?: string - rejected?: boolean - stdin?: boolean - stdout?: boolean - variables?: boolean - whitelist?: string[] - whitelistPatterns?: Array - whitelistPatternsChildren?: Array - whitelistPatternsGreedy?: Array + content: Array; + css: Array; + defaultExtractor?: ExtractorFunction; + extractors?: Array; + fontFace?: boolean; + keyframes?: boolean; + output?: string; + rejected?: boolean; + stdin?: boolean; + stdout?: boolean; + variables?: boolean; + safelist?: UserDefinedSafelist; } interface RawContent { @@ -62,6 +59,18 @@ interface RawContent { interface RawCSS { raw: string } + +type StringRegExpArray = Array; + +type ComplexSafelist = { + standard?: StringRegExpArray; + deep?: RegExp[]; + greedy?: RegExp[]; + variables?: StringRegExpArray; + keyframes?: StringRegExpArray; +}; + +type UserDefinedSafelist = StringRegExpArray | ComplexSafelist; ``` - **content** @@ -215,57 +224,79 @@ await new PurgeCSS().purge({ }) ``` -- **whitelist** +- **safelist** + +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. + +Two forms are available for this option. + +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. + +The _complex_ form is: + +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` -You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the options `whitelist`, `whitelistPatterns`, `whitelistPatternsChildren`, and `whitelistPatternsGreedy`. +e.g: ```js const purgecss = await new PurgeCSS().purge({ content: [], // content css: [], // css - whitelist: ['random', 'yep', 'button'] + safelist: ['random', 'yep', 'button'] }) ``` In this example, the selectors `.random`, `#yep`, `button` will be left in the final CSS. -- **whitelistPatterns** - -You can whitelist selectors based on a regular expression with `whitelistPatterns`. - ```js const purgecss = await new PurgeCSS().purge({ content: [], // content css: [], // css - whitelistPatterns: [/red$/] + safelist: [/red$/] }) ``` In this example, selectors ending with `red` such as `.bg-red` will be left in the final CSS. -- **whitelistPatternsChildren** +- **safelist.deep** -You can whitelist selectors and their children based on a regular expression with `whitelistPatternsChildren`. +You can safelist selectors and their children based on a regular expression with `safelist.deep`. ```js const purgecss = await new PurgeCSS().purge({ content: [], // content css: [], // css - whitelistPatternsChildren: [/red$/] + safelist: { + deep: [/red$/] + } }) ``` In this example, selectors such as `.bg-red .child-of-bg` will be left in the final CSS, even if `child-of-bg` is not found. -- **whitelistPatternsGreedy** +- **safelist.greedy** -Finally, you can whitelist whole selectors if any part of that selector matches a regular expression with `whitelistPatternsGreedy`. +Finally, you can safelist whole selectors if any part of that selector matches a regular expression with `safelist.greedy`. ```js const purgecss = await new PurgeCSS().purge({ content: [], // content css: [], // css - whitelistPatternsGreedy: [/red$/] + safelist: { + greedy: [/red$/] + } }) ``` diff --git a/docs/guides/nuxt.md b/docs/guides/nuxt.md index fee7537e..71ef0b66 100644 --- a/docs/guides/nuxt.md +++ b/docs/guides/nuxt.md @@ -120,6 +120,6 @@ npm i --save-dev @fullhuman/postcss-purgecss ```javascript '@fullhuman/postcss-purgecss': { content: ['./pages/**/*.vue', './layouts/**/*.vue', './components/**/*.vue'], - whitelist: ['html', 'body'] + safelist: ['html', 'body'] } ``` diff --git a/docs/guides/vue.md b/docs/guides/vue.md index 0ac85564..154e867f 100644 --- a/docs/guides/vue.md +++ b/docs/guides/vue.md @@ -45,7 +45,6 @@ Below are the PurgeCSS options set by this plugin: const contentWithoutStyleBlocks = content.replace(/<\/style>/gi, '') return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] }, - whitelist: [], - whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], + safelist: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], } ``` diff --git a/docs/guides/wordpress.md b/docs/guides/wordpress.md index 08cb319c..804a514c 100644 --- a/docs/guides/wordpress.md +++ b/docs/guides/wordpress.md @@ -3,14 +3,14 @@ title: WordPress | PurgeCSS lang: en-US meta: - name: description - content: PurgeCSS can be used for WordPress development. A module exists to ease the process and provide common whitelist items. + content: PurgeCSS can be used for WordPress development. A module exists to ease the process and provide common safelist items. - name: keywords content: PurgeCSS WordPress purgecss-with-wordpress remove unused css --- # WordPress -If you want to use PurgeCSS with WordPress, you might need to whitelist classes generated by WordPress to avoid them being removed by PurgeCSS. `purgecss-with-wordpress` contains the classes needed to be whitelisted. +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. ## Installation @@ -30,23 +30,19 @@ import purgecssWordpress from 'purgecss-with-wordpress' const purgeCss = new Purgecss({ content: ['**/*.html'], css: ['**/*.css'], - whitelist: purgecssWordpress.whitelist, - whitelistPatterns: purgecssWordpress.whitelistPatterns + safelist: purgecssWordpress.safelist }) const result = purgecss.purge() ``` -If you have additional classes you want to include in either of the `whitelist` or `whitelistPatterns`, you can include them using the spread operator: +If you have additional classes you want to include, you can include them using the spread operator: ```js { - whitelist: [ - ...purgecssWordpress.whitelist, + safelist: [ + ...purgecssWordpress.safelist, 'red', 'blue', - ], - whitelistPatterns: [ - ...purgecssWordpress.whitelistPatterns, /^red/, /blue$/, ] diff --git a/docs/plugins/webpack.md b/docs/plugins/webpack.md index 0bd70614..1f893c1d 100644 --- a/docs/plugins/webpack.md +++ b/docs/plugins/webpack.md @@ -125,40 +125,26 @@ new PurgecssPlugin({ }) ``` -* #### whitelist, whitelistPatterns, whitelistPatternsChildren, and whitelistPatternsGreedy +* #### safelist -Similar as for the `paths` option, you can also define functions for the following options: +Similar as for the `paths` option, you also can define a function for this option: ```js -function collectWhitelist() { - // do something to collect the whitelist - return ['whitelisted']; -} - -function collectWhitelistPatterns() { - // do something to collect the whitelist - return [/^whitelisted-/]; -} - -function collectWhitelistPatternsChildren() { - // do something to collect the whitelist - return [/^whitelisted-/]; -} - -function collectWhitelistPatternsGreedy() { - // do something to collect the whitelist - return [/^whitelisted-/]; +function collectSafelist() { + return { + standard: ['safelisted', /^safelisted-/], + deep: [/^safelisted-deep-/], + greedy: [/^safelisted-greedy/] + } } // In the webpack configuration -new PurgecssPlugin({ - whitelist: collectWhitelist, - whitelistPatterns: collectWhitelistPatterns, - whitelistPatternsChildren: collectWhitelistPatternsChildren, - whitelistPatternsGreedy: collectWhitelistPatternsGreedy +new PurgeCSSPlugin({ + safelist: collectSafelist }) ``` + * #### rejected When this option is set to `true` all removed selectors are added to the [Stats Data](https://webpack.js.org/api/stats/) as `purged`. diff --git a/docs/whitelisting.md b/docs/safelisting.md similarity index 66% rename from docs/whitelisting.md rename to docs/safelisting.md index 4580b56e..b51da7e7 100644 --- a/docs/whitelisting.md +++ b/docs/safelisting.md @@ -1,26 +1,26 @@ --- -title: Whitelisting | PurgeCSS +title: Safelisting | PurgeCSS lang: en-US meta: - name: description - content: To avoid PurgeCSS to remove unused CSS that you want to keep, you can whitelist selectors. + content: To avoid PurgeCSS to remove unused CSS that you want to keep, you can safelist selectors. - name: keywords content: PurgeCSS remove unused CSS optimization web --- -# Whitelisting +# Safelisting -You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the PurgeCSS options `whitelist`, `whitelistPatterns`, `whitelistPatternsChildren`, `whitelistPatternsGreedy`, or directly in your CSS with a special comment. +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the PurgeCSS option `safelist`, or directly in your CSS with a special comment. ## Specific selectors -You can whitelist selectors with `whitelist`. +You can add selectors to the safelist with `safelist`. ```javascript const purgecss = new Purgecss({ content: [], // content css: [], // css - whitelist: ['random', 'yep', 'button'] + safelist: ['random', 'yep', 'button'] }) ``` @@ -28,15 +28,17 @@ In the example, the selectors `.random`, `#yep`, `button` will be left in the fi ## Patterns -You can whitelist selectors based on a regular expression with `whitelistPatterns`, `whitelistPatternsChildren`, and `whitelistPatternsGreedy`. +You can safelist selectors based on a regular expression with `safelist.standard`, `safelist.deep`, and `safelist.greedy`. ```javascript const purgecss = new Purgecss({ content: [], // content css: [], // css - whitelistPatterns: [/red$/], - whitelistPatternsChildren: [/blue$/], - whitelistPatternsGreedy: [/yellow$/] + safelist: { + standard: [/red$/], + deep: [/blue$/], + greedy: [/yellow$/] + } }) ``` @@ -46,8 +48,8 @@ Patterns are regular expressions. You can use [regexr](https://regexr.com) to ve ## In the CSS directly -You can whitelist directly in your CSS with a special comment. -Use `/* purgecss ignore */` to whitelist the next rule. +You can safelist directly in your CSS with a special comment. +Use `/* purgecss ignore */` to safelist the next rule. ```css /* purgecss ignore */ @@ -56,7 +58,7 @@ h1 { } ``` -Use `/* purgecss ignore current */` to whitelist the current rule. +Use `/* purgecss ignore current */` to safelist the current rule. ```css h1 { @@ -65,7 +67,7 @@ h1 { } ``` -You can use `/* purgecss start ignore */` and `/* purgecss end ignore */` to whitelist a range of rules. +You can use `/* purgecss start ignore */` and `/* purgecss end ignore */` to safelist a range of rules. ```css /* purgecss start ignore */ diff --git a/packages/gulp-purgecss/src/types/index.ts b/packages/gulp-purgecss/src/types/index.ts index a168b893..1ae4a6b0 100644 --- a/packages/gulp-purgecss/src/types/index.ts +++ b/packages/gulp-purgecss/src/types/index.ts @@ -1,6 +1,6 @@ import { UserDefinedOptions as PurgeCSSUserDefinedOptions } from "../../../purgecss/src/types/index"; export interface UserDefinedOptions - extends Omit { + extends Omit { content: string[]; } diff --git a/packages/purgecss-webpack-plugin/README.md b/packages/purgecss-webpack-plugin/README.md index 2469825c..fe5ac986 100644 --- a/packages/purgecss-webpack-plugin/README.md +++ b/packages/purgecss-webpack-plugin/README.md @@ -25,7 +25,7 @@ npm i purgecss-webpack-plugin -D const path = require('path') const glob = require('glob') const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('purgecss-webpack-plugin') +const PurgeCSSPlugin = require('purgecss-webpack-plugin') const PATHS = { src: path.join(__dirname, 'src') @@ -64,7 +64,7 @@ module.exports = { new MiniCssExtractPlugin({ filename: "[name].css", }), - new PurgecssPlugin({ + new PurgeCSSPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), }), ] @@ -73,50 +73,13 @@ module.exports = { ### Multiple paths If you need multiple paths use the npm package `glob-all` instead of `glob`, then you can use this syntax: ```javascript -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: glob.sync([ // ... ]) }), ``` to filter out directories see the glob-all documentation [here](https://www.npmjs.com/package/glob-all#filtering-out-directories). - ### Options @@ -127,21 +90,21 @@ The options available in purgecss [Configuration](https://www.purgecss.com/confi With the webpack plugin, you can specified the content that should be analyzed by purgecss with an array of filename. It can be html, pug, blade, ... files. You can use a module like `glob` or `glob-all` to easily get a list of files. ```js -const PurgecssPlugin = require('purgecss-webpack-plugin') +const PurgeCSSPlugin = require('purgecss-webpack-plugin') const glob = require('glob') const PATHS = { src: path.join(__dirname, 'src') } // In the webpack configuration -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }) }) ``` If you want to regenerate the paths list on every compilation (e.g. with `--watch`), then you can also pass a function: ```js -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: () => glob.sync(`${PATHS.src}/**/*`, { nodir: true }) }) ``` @@ -151,42 +114,28 @@ new PurgecssPlugin({ You can specify entrypoints to the purgecss-webpack-plugin with the option only: ```js -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), only: ['bundle', 'vendor'] }) ``` -* #### whitelist, whitelistPatterns, whitelistPatternsChildren, and whitelistPatternsGreedy +* #### safelist -Similar as for the `paths` option, you also can define functions for the these options: +Similar as for the `paths` option, you also can define a function for this option: ```js -function collectWhitelist() { - // do something to collect the whitelist - return ['whitelisted']; -} -function collectWhitelistPatterns() { - // do something to collect the whitelist - return [/^whitelisted-/]; -} - -function collectWhitelistPatternsChildren() { - // do something to collect the whitelist - return [/^whitelisted-/]; -} - -function collectWhitelistPatternsGreedy() { - // do something to collect the whitelist - return [/^whitelisted-/]; +function collectSafelist() { + return { + standard: ['safelisted', /^safelisted-/], + deep: [/^safelisted-deep-/], + greedy: [/^safelisted-greedy/] + } } // In the webpack configuration -new PurgecssPlugin({ - whitelist: collectWhitelist, - whitelistPatterns: collectWhitelistPatterns, - whitelistPatternsChildren: collectWhitelistPatternsChildren, - whitelistPatternsGreedy: collectWhitelistPatternsGreedy +new PurgeCSSPlugin({ + safelist: collectSafelist }) ``` diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/expected/styles.css b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/expected/styles.css similarity index 62% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/expected/styles.css rename to packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/expected/styles.css index 4a1473b6..74f173a3 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/expected/styles.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/expected/styles.css @@ -2,15 +2,15 @@ color: red; } -.whitelisted { +.safelisted { color: green; } -.whitelistedPattern { +.safelistedPattern { color: rebeccapurple; } -.whitelistedPatternChildren > p a { +.safelistedPatternChildren > p a { color: orange; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/content.html b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/content.html similarity index 100% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/content.html rename to packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/content.html diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js new file mode 100644 index 00000000..4fe51c72 --- /dev/null +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js @@ -0,0 +1 @@ +import "./style.css"; diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/style.css b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/style.css similarity index 67% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/style.css rename to packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/style.css index a16dad3e..6eee65a3 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/style.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/style.css @@ -6,15 +6,15 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } -.whitelistedPattern { +.safelistedPattern { color: rebeccapurple; } -.whitelistedPatternChildren > p a { +.safelistedPatternChildren > p a { color: orange; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/webpack.config.js similarity index 87% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js rename to packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/webpack.config.js index c6569d69..b42fd71c 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/webpack.config.js @@ -41,9 +41,9 @@ module.exports = { paths: () => glob.sync(`${PATHS.src}/*`), safelist: () => { return { - standard: ["whitelisted", /^whitelistedPat/], - deep: [/^whitelistedPatternChildren/], - greedy: [/^whitelistedPatternGreedy/], + standard: ["safelisted", /^safelistedPat/], + deep: [/^safelistedPatternChildren/], + greedy: [/^safelistedPatternGreedy/], }; }, extractors: [ diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/index.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/index.js deleted file mode 100644 index cab743ad..00000000 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/index.js +++ /dev/null @@ -1 +0,0 @@ -import './style.css' diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css index 3ca96dce..11fc3e1a 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css @@ -6,7 +6,7 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css index 065927db..e2d156dd 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css @@ -6,7 +6,7 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js index 8a2c8751..0571262c 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js @@ -42,7 +42,7 @@ module.exports = { new PurgecssPlugin({ paths: glob.sync(`${PATHS.src}/*`), styleExtensions: [".css"], - safelist: ["whitelisted"], + safelist: ["safelisted"], only: ["bundle"], extractors: [ { diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css index ffac9135..88f614e7 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css @@ -2,7 +2,7 @@ color: red; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css index 065927db..e2d156dd 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css @@ -6,7 +6,7 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js index 2a47b7e1..965d143d 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js @@ -42,7 +42,7 @@ module.exports = { }), new PurgecssPlugin({ paths: glob.sync(`${PATHS.src}/*`), - safelist: ["whitelisted"], + safelist: ["safelisted"], extractors: [ { extractor: customExtractor, diff --git a/packages/purgecss-webpack-plugin/__tests__/index.test.ts b/packages/purgecss-webpack-plugin/__tests__/index.test.ts index 26482567..50189e0c 100644 --- a/packages/purgecss-webpack-plugin/__tests__/index.test.ts +++ b/packages/purgecss-webpack-plugin/__tests__/index.test.ts @@ -34,7 +34,7 @@ describe("Webpack integration", () => { }); const cases: string[] = [ - "path-and-whitelist-functions", + "path-and-safelist-functions", "simple", "simple-with-exclusion", ]; diff --git a/packages/purgecss-with-wordpress/README.md b/packages/purgecss-with-wordpress/README.md index fc9d9109..11bc47c3 100644 --- a/packages/purgecss-with-wordpress/README.md +++ b/packages/purgecss-with-wordpress/README.md @@ -24,21 +24,21 @@ import purgecssWordpress from 'purgecss-with-wordpress' const purgeCSSResults = await new PurgeCSS().purge({ content: ['**/*.html'], css: ['**/*.css'], - whitelist: purgecssWordpress.whitelist, - whitelistPatterns: purgecssWordpress.whitelistPatterns + safelist: purgecssWordpress.safelist, + safelistPatterns: purgecssWordpress.safelistPatterns }) ``` -If you have additional classes you want to include in either of the `whitelist` or `whitelistPatterns`, you can include them using the spread operator: +If you have additional classes you want to include in either of the `safelist` or `safelistPatterns`, you can include them using the spread operator: ```js -whitelist: [ - ...purgecssWordpress.whitelist, +safelist: [ + ...purgecssWordpress.safelist, 'red', 'blue', ], -whitelistPatterns: [ - ...purgecssWordpress.whitelistPatterns, +safelistPatterns: [ + ...purgecssWordpress.safelistPatterns, /^red/, /blue$/, ] diff --git a/packages/purgecss-with-wordpress/index.js b/packages/purgecss-with-wordpress/index.js index 56933f14..47f40988 100644 --- a/packages/purgecss-with-wordpress/index.js +++ b/packages/purgecss-with-wordpress/index.js @@ -1,26 +1,24 @@ module.exports = { - whitelist: [ - 'rtl', - 'home', - 'blog', - 'archive', - 'date', - 'error404', - 'logged-in', - 'admin-bar', - 'no-customize-support', - 'custom-background', - 'wp-custom-logo', - 'alignnone', - 'alignright', - 'alignleft', - 'wp-caption', - 'wp-caption-text', - 'screen-reader-text', - 'comment-list', - 'wp-social-link', - ], - whitelistPatterns: [ + safelist: [ + "rtl", + "home", + "blog", + "archive", + "date", + "error404", + "logged-in", + "admin-bar", + "no-customize-support", + "custom-background", + "wp-custom-logo", + "alignnone", + "alignright", + "alignleft", + "wp-caption", + "wp-caption-text", + "screen-reader-text", + "comment-list", + "wp-social-link", /^search(-.*)?$/, /^(.*)-template(-.*)?$/, /^(.*)?-?single(-.*)?$/, diff --git a/packages/purgecss/README.md b/packages/purgecss/README.md index 48c10de6..aa7d943b 100644 --- a/packages/purgecss/README.md +++ b/packages/purgecss/README.md @@ -32,7 +32,7 @@ You can find the PurgeCSS documentation on [this website](https://purgecss.com). - [Configuration](https://purgecss.com/configuration.html) - [Command Line Interface](https://purgecss.com/CLI.html) - [Programmatic API](https://purgecss.com/api.html) -- [Whitelisting](https://purgecss.com/whitelisting.html) +- [Whitelisting](https://purgecss.com/safelisting.html) - [Extractors](https://purgecss.com/extractors.html) - [Comparison](https://purgecss.com/comparison.html) diff --git a/packages/purgecss/__tests__/safelist.test.ts b/packages/purgecss/__tests__/safelist.test.ts index 7bf54387..90651e34 100644 --- a/packages/purgecss/__tests__/safelist.test.ts +++ b/packages/purgecss/__tests__/safelist.test.ts @@ -6,8 +6,8 @@ describe("safelist string", () => { let purgedCSS = ""; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.html`], - css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.css`], + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist.css`], safelist: ["random", "h1", "yep", "button"], }); purgedCSS = resultsPurge[0].css; @@ -22,8 +22,8 @@ describe("safelist regular expression", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.html`], - css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.css`], + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist.css`], safelist: [/nav-/, /data-v-.*/], }); purgedCSS = resultsPurge[0].css; @@ -38,8 +38,8 @@ describe("safelist option: standard", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.html`], - css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist.css`], + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist.css`], safelist: { standard: ["random", "h1", "yep", "button", /nav-/, /data-v-.*/], }, @@ -69,9 +69,9 @@ describe("safelist option: deep", () => { beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ content: [ - `${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_children.html`, + `${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_children.html`, ], - css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_children.css`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_children.css`], safelist: { deep: [/^card$/], }, @@ -96,8 +96,8 @@ describe("safelist option: greedy", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_greedy.html`], - css: [`${ROOT_TEST_EXAMPLES}safelist/whitelist_patterns_greedy.css`], + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_greedy.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_greedy.css`], safelist: { greedy: [/data-v-.*/], }, diff --git a/packages/purgecss/__tests__/test_examples/safelist/whitelist.css b/packages/purgecss/__tests__/test_examples/safelist/safelist.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/safelist/whitelist.css rename to packages/purgecss/__tests__/test_examples/safelist/safelist.css diff --git a/packages/purgecss/__tests__/test_examples/safelist/whitelist.html b/packages/purgecss/__tests__/test_examples/safelist/safelist.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/safelist/whitelist.html rename to packages/purgecss/__tests__/test_examples/safelist/safelist.html diff --git a/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.css rename to packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.css diff --git a/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_children.html rename to packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.html diff --git a/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.css rename to packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.css diff --git a/packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/safelist/whitelist_patterns_greedy.html rename to packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.html diff --git a/packages/purgecss/bin/purgecss b/packages/purgecss/bin/purgecss.js similarity index 53% rename from packages/purgecss/bin/purgecss rename to packages/purgecss/bin/purgecss.js index dad60e5e..cd544fd5 100755 --- a/packages/purgecss/bin/purgecss +++ b/packages/purgecss/bin/purgecss.js @@ -1,30 +1,45 @@ #!/usr/bin/env node -const program = require('commander'); -const fs = require('fs'); -const { default: PurgeCSS, defaultOptions, setOptions } = require('../lib/purgecss') +const program = require("commander"); +const fs = require("fs"); +const { + default: PurgeCSS, + defaultOptions, + setOptions, +} = require("../lib/purgecss"); function getList(list) { - return list.split(',') + return list.split(","); } async function writeCSSToFile(filePath, css) { try { await fs.promises.writeFile(filePath, css); - } catch(err) { + } catch (err) { console.error(err.message); } } program - .usage('--css --content [options]') - .option('-con, --content ', 'glob of content files (comma separated)', getList) - .option('-css, --css ', 'glob of css files (comma separated)', getList) - .option('-c, --config ', 'path to the configuration file') - .option('-o, --output ', 'file path directory to write purged css files to') - .option('-font, --font-face', 'option to remove unused font-faces') - .option('-keyframes, --keyframes', 'option to remove unused keyframes') - .option('-rejected, --rejected', 'option to output rejected selectors') - .option('-w, --whitelist ', 'list of classes that should not be removed (comma separated)', getList); + .usage("--css --content [options]") + .option( + "-con, --content ", + "glob of content files (comma separated)", + getList + ) + .option("-css, --css ", "glob of css files (comma separated)", getList) + .option("-c, --config ", "path to the configuration file") + .option( + "-o, --output ", + "file path directory to write purged css files to" + ) + .option("-font, --font-face", "option to remove unused font-faces") + .option("-keyframes, --keyframes", "option to remove unused keyframes") + .option("-rejected, --rejected", "option to output rejected selectors") + .option( + "-s, --safellist ", + "list of classes that should not be removed (comma separated)", + getList + ); program.parse(process.argv); @@ -34,7 +49,7 @@ if (!program.config && !(program.content && program.css)) { program.help(); } -(async() => { +(async () => { // if the config file is present, use it // other options specified will override let options = defaultOptions; @@ -47,22 +62,16 @@ if (!program.config && !(program.content && program.css)) { if (program.keyframes) options.keyframes = program.keyframes; if (program.rejected) options.rejected = program.rejected; if (program.variables) options.variables = program.variables; - if (program.whitelist) options.whitelist = program.whitelist; - - // if (!options.css) { - // options.css = [{ - // raw: stdInCSS - // }] - // } + if (program.safelist) options.safelist = program.safelist; const purged = await new PurgeCSS().purge(options); const output = options.output || program.output; // output results in specified directory if (output) { - if (purged.length === 1 && output.endsWith('.css')) { - await writeCSSToFile(output, purged[0].css) - return + if (purged.length === 1 && output.endsWith(".css")) { + await writeCSSToFile(output, purged[0].css); + return; } for (const purgedResult of purged) { @@ -72,6 +81,4 @@ if (!program.config && !(program.content && program.css)) { } else { console.log(JSON.stringify(purged)); } - -})() - +})(); diff --git a/packages/purgecss/package.json b/packages/purgecss/package.json index 5105389c..fe054b22 100644 --- a/packages/purgecss/package.json +++ b/packages/purgecss/package.json @@ -21,7 +21,7 @@ "module": "./lib/purgecss.esm.js", "types": "./lib/purgecss.d.ts", "bin": { - "purgecss": "bin/purgecss" + "purgecss": "bin/purgecss.js" }, "directories": { "lib": "lib", diff --git a/packages/vue-cli-plugin-purgecss/README.md b/packages/vue-cli-plugin-purgecss/README.md index a0713ab9..12d5ea3a 100644 --- a/packages/vue-cli-plugin-purgecss/README.md +++ b/packages/vue-cli-plugin-purgecss/README.md @@ -33,8 +33,7 @@ Below are the PurgeCSS options set by this plugin: const contentWithoutStyleBlocks = content.replace(/<\/style>/gi, '') return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] }, - whitelist: [], - whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], + safelist: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], } ``` From 21d629553e65f6545acf54c95f4d406b687bd7b8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:06:22 +0000 Subject: [PATCH 08/36] build(deps-dev): bump grunt from 1.1.0 to 1.2.1 Bumps [grunt](https://github.com/gruntjs/grunt) from 1.1.0 to 1.2.1. - [Release notes](https://github.com/gruntjs/grunt/releases) - [Changelog](https://github.com/gruntjs/grunt/blob/master/CHANGELOG) - [Commits](https://github.com/gruntjs/grunt/compare/v1.1.0...v1.2.1) Signed-off-by: dependabot-preview[bot] --- packages/grunt-purgecss/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grunt-purgecss/package.json b/packages/grunt-purgecss/package.json index 86e47c70..ecd43037 100644 --- a/packages/grunt-purgecss/package.json +++ b/packages/grunt-purgecss/package.json @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/grunt": "^0.4.25", - "grunt": "~1.1.0" + "grunt": "~1.2.1" }, "bugs": { "url": "https://github.com/FullHuman/purgecss/issues" From d2e8212148ee2d2ff552651d7012ad2cdd81a426 Mon Sep 17 00:00:00 2001 From: Knox Jarvis <45022934+knoxj1@users.noreply.github.com> Date: Tue, 14 Jul 2020 10:47:00 -0400 Subject: [PATCH 09/36] Update README.md Grammar --- packages/purgecss-webpack-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purgecss-webpack-plugin/README.md b/packages/purgecss-webpack-plugin/README.md index fe5ac986..70a7a40a 100644 --- a/packages/purgecss-webpack-plugin/README.md +++ b/packages/purgecss-webpack-plugin/README.md @@ -87,7 +87,7 @@ The options available in purgecss [Configuration](https://www.purgecss.com/confi * #### paths -With the webpack plugin, you can specified the content that should be analyzed by purgecss with an array of filename. It can be html, pug, blade, ... files. You can use a module like `glob` or `glob-all` to easily get a list of files. +With the webpack plugin, you can specify the content that should be analyzed by purgecss with an array of filename. It can be html, pug, blade, ... files. You can use a module like `glob` or `glob-all` to easily get a list of files. ```js const PurgeCSSPlugin = require('purgecss-webpack-plugin') From 0bb32b6613ae1a81cf67048a8494a218aa20d1fb Mon Sep 17 00:00:00 2001 From: Amir Hossein Abdolkhalegh Date: Fri, 17 Jul 2020 23:58:39 +0430 Subject: [PATCH 10/36] change modules property to buildModules If you are using [nuxt > 2.9.0], modules property doesn't work. --- docs/guides/nuxt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/nuxt.md b/docs/guides/nuxt.md index 71ef0b66..47e26d89 100644 --- a/docs/guides/nuxt.md +++ b/docs/guides/nuxt.md @@ -26,7 +26,7 @@ to the configuration. ```js { - modules: [ + buildModules: [ // if you are using nuxt < 2.9.0, use modules property instead. 'nuxt-purgecss', ], From dc59d309a4a4be9845c40966a19f9705c42a33a1 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sat, 18 Jul 2020 22:19:44 +0100 Subject: [PATCH 11/36] feat: add safelist keyframes and css variables - add safelist for keyframes (#418, #439) - add safelist for css variables --- packages/purgecss/__tests__/safelist.test.ts | 50 +++++++++++++++ .../safelist/safelist_css_variables.css | 22 +++++++ .../safelist/safelist_css_variables.html | 1 + .../safelist/safelist_keyframes.css | 61 +++++++++++++++++++ .../safelist/safelist_keyframes.html | 2 + packages/purgecss/src/VariablesStructure.ts | 14 ++++- packages/purgecss/src/index.ts | 23 ++++++- 7 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css create mode 100644 packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html create mode 100644 packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.css create mode 100644 packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html diff --git a/packages/purgecss/__tests__/safelist.test.ts b/packages/purgecss/__tests__/safelist.test.ts index 90651e34..71a4a8d4 100644 --- a/packages/purgecss/__tests__/safelist.test.ts +++ b/packages/purgecss/__tests__/safelist.test.ts @@ -126,3 +126,53 @@ describe("safelist option: greedy", () => { ); }); }); + +describe("safelist option: keyframes", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist_keyframes.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_keyframes.css`], + safelist: { + keyframes: [/^scale/, "spin"], + }, + keyframes: true, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted keyframes", () => { + findInCSS( + expect, + ["@keyframes scale", "@keyframes scale-down", "@keyframes spin"], + purgedCSS + ); + }); + + it("excludes non-safelisted keyframes", () => { + notFindInCSS(expect, ["flash"], purgedCSS); + }); +}); + +describe("safelist option: variables", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist_css_variables.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_css_variables.css`], + safelist: { + variables: [/^--b/, "--unused-color"], + }, + variables: true, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted css variables", () => { + findInCSS(expect, ["--unused-color", "--button-color"], purgedCSS); + }); + + it("excludes non-safelisted css variables", () => { + notFindInCSS(expect, ["--tertiary-color:"], purgedCSS); + }); +}); diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css new file mode 100644 index 00000000..1956037d --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css @@ -0,0 +1,22 @@ +:root { + --primary-color: blue; + --secondary-color: indigo; + --tertiary-color: aqua; + --unused-color: violet; + --used-color: rebeccapurple; + --accent-color: orange; + } + + .button { + --button-color: var(--tertiary-color); + --border-color: linear-gradient(to top, var(--secondary-color), var(--used-color, white)); + + background-color: var(--primary-color); + color: var(--accent-color); + border-color: var(--border-color); + } + + .button:focus { + background-color: var(--accent-color); + color: var(--primary-color); + } \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html new file mode 100644 index 00000000..65214131 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.css new file mode 100644 index 00000000..3be12e29 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.css @@ -0,0 +1,61 @@ +@keyframes bounce { + from, 20%, 53%, 80%, to { + animation-timing-function: cubic-bezier(0.3, 0.1, 0.9, 1.000); + transform: translate3d(1, 1, 0); + } +} + +.bounce { + -webkit-animation-name: bounce; + animation-name: bounce; + -webkit-transform-origin: center bottom; + transform-origin: center bottom; +} + +@keyframes flash { + from, 50%, to { + opacity: 1; + } + + 25%, 75% { + opacity: 0.5; + } +} + +.flash { + animation: flash +} + +@keyframes scale { + from { + transform: scale(1); + } + + to { + transform: scale(2); + } +} + +@keyframes scale-down { + from { + transform: scale(2); + } + + to { + transform: scale(1); + } +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +.scale-spin { + animation: spin 300ms linear infinite forwards,scale 300ms linear infinite alternate; +} diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html new file mode 100644 index 00000000..7b1f750a --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html @@ -0,0 +1,2 @@ +
+
diff --git a/packages/purgecss/src/VariablesStructure.ts b/packages/purgecss/src/VariablesStructure.ts index 06f9b666..af5a9221 100644 --- a/packages/purgecss/src/VariablesStructure.ts +++ b/packages/purgecss/src/VariablesStructure.ts @@ -1,4 +1,5 @@ import postcss from "postcss"; +import { StringRegExpArray } from "./types"; class VariableNode { public nodes: VariableNode[] = []; @@ -13,6 +14,7 @@ class VariableNode { class VariablesStructure { public nodes: Map = new Map(); public usedVariables: Set = new Set(); + public safelist: StringRegExpArray = []; addVariable(declaration: postcss.Declaration): void { const { prop } = declaration; @@ -63,12 +65,20 @@ class VariablesStructure { for (const used of this.usedVariables) { this.setAsUsed(used); } - for (const [, declaration] of this.nodes) { - if (!declaration.isUsed) { + for (const [name, declaration] of this.nodes) { + if (!declaration.isUsed && !this.isVariablesSafelisted(name)) { declaration.value.remove(); } } } + + isVariablesSafelisted(variable: string): boolean { + return this.safelist.some((safelistItem) => { + return typeof safelistItem === "string" + ? safelistItem === variable + : safelistItem.test(variable); + }); + } } export default VariablesStructure; diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index c395a9f4..7b76fe02 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -542,6 +542,18 @@ class PurgeCSS { return sources; } + /** + * Check if the keyframe is safelisted with the option safelist keyframes + * @param keyframesName name of the keyframe animation + */ + private isKeyframesSafelisted(keyframesName: string): boolean { + return this.options.safelist.keyframes.some((safelistItem) => { + return typeof safelistItem === "string" + ? safelistItem === keyframesName + : safelistItem.test(keyframesName); + }); + } + /** * Check if the selector is safelisted with the option safelist standard * @param selector css selector @@ -590,7 +602,11 @@ class PurgeCSS { ...userOptions, safelist: standardizeSafelist(userOptions.safelist), }; - const { content, css, extractors } = this.options; + const { content, css, extractors, safelist } = this.options; + + if (this.options.variables) { + this.variablesStructure.safelist = safelist.variables || []; + } const fileFormatContents = content.filter( (o) => typeof o === "string" @@ -637,7 +653,10 @@ class PurgeCSS { */ public removeUnusedKeyframes(): void { for (const node of this.atRules.keyframes) { - if (!this.usedAnimations.has(node.params)) { + if ( + !this.usedAnimations.has(node.params) && + !this.isKeyframesSafelisted(node.params) + ) { node.remove(); } } From e6074ed1cd28aa2b1bacb567e30fbed2dc59fb3b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2020 09:16:40 +0000 Subject: [PATCH 12/36] build(deps): bump commander from 5.1.0 to 6.0.0 Bumps [commander](https://github.com/tj/commander.js) from 5.1.0 to 6.0.0. - [Release notes](https://github.com/tj/commander.js/releases) - [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/tj/commander.js/compare/v5.1.0...v6.0.0) Signed-off-by: dependabot-preview[bot] --- packages/purgecss/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purgecss/package.json b/packages/purgecss/package.json index fe054b22..ce0bacea 100644 --- a/packages/purgecss/package.json +++ b/packages/purgecss/package.json @@ -39,7 +39,7 @@ "test": "echo \"Error: run tests from root\" && exit 1" }, "dependencies": { - "commander": "^5.0.0", + "commander": "^6.0.0", "glob": "^7.0.0", "postcss": "7.0.32", "postcss-selector-parser": "^6.0.2" From cb11770df6dc8a5aa53409fb2d2b58638a1c722a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2020 09:33:33 +0000 Subject: [PATCH 13/36] build(deps-dev): bump css-loader from 3.6.0 to 4.0.0 Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.6.0 to 4.0.0. - [Release notes](https://github.com/webpack-contrib/css-loader/releases) - [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.6.0...v4.0.0) Signed-off-by: dependabot-preview[bot] --- packages/purgecss-webpack-plugin/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purgecss-webpack-plugin/package.json b/packages/purgecss-webpack-plugin/package.json index e394cd0b..2896cee7 100644 --- a/packages/purgecss-webpack-plugin/package.json +++ b/packages/purgecss-webpack-plugin/package.json @@ -46,7 +46,7 @@ "devDependencies": { "@types/webpack": "^4.41.12", "@types/webpack-sources": "^1.4.0", - "css-loader": "^3.5.2", + "css-loader": "^4.0.0", "mini-css-extract-plugin": "^0.9.0" }, "peerDependencies": { From 04223f7fe27f8d818961a53900a7c5293d2322b6 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Wed, 29 Jul 2020 23:43:20 +0100 Subject: [PATCH 14/36] feat: add blocklist option --- packages/purgecss/__tests__/safelist.test.ts | 24 ++++++++++++++++ .../test_examples/safelist/blocklist.css | 28 +++++++++++++++++++ .../test_examples/safelist/blocklist.html | 11 ++++++++ packages/purgecss/src/index.ts | 18 ++++++++++++ packages/purgecss/src/options.ts | 1 + packages/purgecss/src/types/index.ts | 2 ++ 6 files changed, 84 insertions(+) create mode 100644 packages/purgecss/__tests__/test_examples/safelist/blocklist.css create mode 100644 packages/purgecss/__tests__/test_examples/safelist/blocklist.html diff --git a/packages/purgecss/__tests__/safelist.test.ts b/packages/purgecss/__tests__/safelist.test.ts index 71a4a8d4..6b669cac 100644 --- a/packages/purgecss/__tests__/safelist.test.ts +++ b/packages/purgecss/__tests__/safelist.test.ts @@ -176,3 +176,27 @@ describe("safelist option: variables", () => { notFindInCSS(expect, ["--tertiary-color:"], purgedCSS); }); }); + +describe("blocklist option", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/blocklist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/blocklist.css`], + blocklist: ["h1", "yep", "button", /nav-/], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("excludes blocklisted selectors", () => { + notFindInCSS( + expect, + ["h1", "yep", "button", "nav-blue", "nav-red"], + purgedCSS + ); + }); + + it("includes non-blocklisted selectors", () => { + findInCSS(expect, ["data-v-test", ".random"], purgedCSS); + }); +}); diff --git a/packages/purgecss/__tests__/test_examples/safelist/blocklist.css b/packages/purgecss/__tests__/test_examples/safelist/blocklist.css new file mode 100644 index 00000000..f28ae760 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/blocklist.css @@ -0,0 +1,28 @@ +h1 { + color: blue; + } + + .random { + color: green; + } + + #yep { + color: red; + } + + button { + color: rebeccapurple; + } + + .nav-blue { + background-color: blue; + } + + .nav-red { + background-color: red; + } + + [data-v-test] { + color: green; + } + \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/safelist/blocklist.html b/packages/purgecss/__tests__/test_examples/safelist/blocklist.html new file mode 100644 index 00000000..985eb619 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/blocklist.html @@ -0,0 +1,11 @@ + + + + + +

Title

+
random
+ + + + \ No newline at end of file diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index 7b76fe02..f922f69c 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -554,6 +554,18 @@ class PurgeCSS { }); } + /** + * Check if the selector is blocklisted with the option blocklist + * @param selector css selector + */ + private isSelectorBlocklisted(selector: string): boolean { + return this.options.blocklist.some((blocklistItem) => { + return typeof blocklistItem === "string" + ? blocklistItem === selector + : blocklistItem.test(selector); + }); + } + /** * Check if the selector is safelisted with the option safelist standard * @param selector css selector @@ -718,6 +730,12 @@ class PurgeCSS { continue; } + // The selector is present in the blocklist + if (selectorValue && this.isSelectorBlocklisted(selectorValue)) { + isPresent = false; + continue; + } + switch (selectorNode.type) { case "attribute": // `value` is a dynamic attribute, highly used in input element diff --git a/packages/purgecss/src/options.ts b/packages/purgecss/src/options.ts index e96afa20..1d416bd8 100644 --- a/packages/purgecss/src/options.ts +++ b/packages/purgecss/src/options.ts @@ -19,4 +19,5 @@ export const defaultOptions: Options = { variables: [], keyframes: [], }, + blocklist: [], }; diff --git a/packages/purgecss/src/types/index.ts b/packages/purgecss/src/types/index.ts index 42adc1cb..5b52ae1e 100644 --- a/packages/purgecss/src/types/index.ts +++ b/packages/purgecss/src/types/index.ts @@ -65,6 +65,7 @@ export interface UserDefinedOptions { stdout?: boolean; variables?: boolean; safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } export interface Options { @@ -80,6 +81,7 @@ export interface Options { stdout: boolean; variables: boolean; safelist: Required; + blocklist: StringRegExpArray; } export interface ResultPurge { From d289366f5e0bd899fde7423e2b4766bf36bf8ae2 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Thu, 6 Aug 2020 00:45:04 +0100 Subject: [PATCH 15/36] docs: SEO improvment - add remove unused CSS to the title - add open graph --- docs/README.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index 4bba871d..0462e13b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,11 +1,25 @@ --- -title: Introduction | PurgeCSS +title: PurgeCSS - Remove unused CSS lang: en-US meta: - name: description content: PurgeCSS is a tool for removing CSS that you're not actually using in 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. - - name: keywords - content: PurgeCSS remove unused CSS optimization web + - itemprop: description + content: PurgeCSS is a tool for removing CSS that you're not actually using in 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. + - property: og:url + content: https://purgecss.com + - property: og:site_name + content: purgecss.com + - property: og:type + content: website + - property: og:locale + content: en_US + - property: og:title + content: Remove unused CSS - PurgeCSS + - property: og:description + content: PurgeCSS is a tool for removing CSS that you're not actually using in 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. + + --- # About PurgeCSS From 008172925d9f958570e361d5ee932b22313dcafd Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Thu, 6 Aug 2020 00:54:52 +0100 Subject: [PATCH 16/36] docs: update next.js guide to the last version - update version of next.js guide to be compatible with next 9.3 and above --- docs/guides/next.md | 68 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/docs/guides/next.md b/docs/guides/next.md index 74e30a5f..ff1d73b7 100644 --- a/docs/guides/next.md +++ b/docs/guides/next.md @@ -12,9 +12,45 @@ meta: > Next.js is a React framework for production grade applications that scale. The world's leading companies use Next.js to build server-rendered applications, static websites, and more. -You can use PurgeCSS with Next.js by using the [Next.js plugin](https://github.com/lucleray/next-purgecss) or the PostCSS plugin. +You can use PurgeCSS with Next.js by using the postCSS plugin in the Next.js configuration -## Next.js plugin +## Customize PostCSS configuration (Next.js >= 9.3) + +To customize the PostCSS configuration, create a postcss.config.json file in the root of your project. + +Add PurgeCSS to the default configuration: + +```js +{ + "plugins": [ + "postcss-flexbugs-fixes", + [ + "postcss-preset-env", + { + "autoprefixer": { + "flexbox": "no-2009" + }, + "stage": 3, + "features": { + "custom-properties": false + } + } + ], + [ + '@fullhuman/postcss-purgecss', + { + content: [ + './pages/**/*.{js,jsx,ts,tsx}', + './components/**/*.{js,jsx,ts,tsx}' + ], + defaultExtractor: content => content.match(/[\w-/:]+(? !dev && !isServer // Only enable PurgeCSS for client-side production builds + purgeCssEnabled: ({ dev, isServer }) => !dev && !isServer, // Only enable PurgeCSS for client-side production builds }) -) +); ``` #### `purgeCssPaths` @@ -78,12 +114,12 @@ directories for classnames. You can change that by defining `purgeCssPaths`. module.exports = withCss( withPurgeCss({ purgeCssPaths: [ - 'pages/**/*', - 'components/**/*', - 'other-components/**/*' // also scan other-components folder - ] + "pages/**/*", + "components/**/*", + "other-components/**/*", // also scan other-components folder + ], }) -) +); ``` #### `purgeCss` @@ -97,10 +133,10 @@ You can pass custom options to module.exports = withCss( withPurgeCss({ purgeCss: { - whitelist: () => ['my-custom-class'] - } + whitelist: () => ["my-custom-class"], + }, }) -) +); ``` The list of available options are documented in [`purgecss-webpack-plugin` @@ -108,4 +144,4 @@ docs](https://github.com/FullHuman/purgecss-webpack-plugin#options). ::: warning `purgeCss.paths` will overwrite `purgeCssPaths` -::: \ No newline at end of file +::: From 5c603ff82b942cbcaf7ebca1615de5cb5a585211 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sun, 9 Aug 2020 00:04:05 +0100 Subject: [PATCH 17/36] docs: `safelist` for plugins postCSS and grunt - add safelist to the documentation for postCSS and grunt plugins - remove `css` from postcss plugin options --- README.md | 2 +- docs/plugins/grunt.md | 27 ++++++++++++++------ docs/plugins/postcss.md | 25 +++++++++--------- packages/grunt-purgecss/README.md | 27 ++++++++++++++------ packages/postcss-purgecss/README.md | 26 ++++++++++++++----- packages/postcss-purgecss/src/types/index.ts | 1 - 6 files changed, 72 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index ab7899ef..6468795d 100644 --- a/README.md +++ b/README.md @@ -80,5 +80,5 @@ This repository is a monorepo that we manage using [Lerna](https://github.com/le | [purgecss-from-html](/packages/purgecss-from-html) | ![npm](https://img.shields.io/npm/v/purgecss-from-html?style=flat-square) | Html extractor for PurgeCSS | | [purgecss-from-js](/packages/purgecss-from-js) | ![npm](https://img.shields.io/npm/v/purgecss-from-js?style=flat-square) | Js extractor for PurgeCSS | | [purgecss-from-pug](/packages/purgecss-from-pug) | ![npm](https://img.shields.io/npm/v/purgecss-from-pug?style=flat-square) | Pug extractor for PurgeCSS | -| [purgecss-with-wordpress](/packages/purgecss-with-wordpress) | ![npm](https://img.shields.io/npm/v/purgecss-with-wordpress?style=flat-square) | Collection of whitelist items for WordPress | +| [purgecss-with-wordpress](/packages/purgecss-with-wordpress) | ![npm](https://img.shields.io/npm/v/purgecss-with-wordpress?style=flat-square) | Collection of safelist items for WordPress | | [vue-cli-plugin-purgecss](/packages/vue-cli-plugin-purgecss) | ![npm](https://img.shields.io/npm/v/@fullhuman/vue-cli-plugin-purgecss?style=flat-square) | Vue CLI Plugin for PurgeCSS | diff --git a/docs/plugins/grunt.md b/docs/plugins/grunt.md index 45d537fd..2b5521cd 100644 --- a/docs/plugins/grunt.md +++ b/docs/plugins/grunt.md @@ -62,17 +62,29 @@ Type: `Array` Purgecss can be adapted to suit your needs. If you notice a lot of unused CSS is not being removed, you might want to use a custom extractor. More information about extractors [here](https://www.purgecss.com/extractors.html). -#### options.whitelist +#### options.safelist -Type: `Array` +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. -You can whitelist selectors to stop Purgecss from removing them from your CSS. This can be accomplished with the options `whitelist` and `whitelistPatterns`. +Two forms are available for this option. -#### options.whitelistPatterns +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. -Type: `Array` +The _complex_ form is: -You can whitelist selectors based on a regular expression with `whitelistPatterns`. +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` #### options.keyframes @@ -107,8 +119,7 @@ grunt.initConfig({ }, extension: ['html', 'blade'] }, - whitelist: ['random', 'yep', 'button'], - whitelistPatterns: [/red$/], + safelist: ['random', 'yep', 'button', /red$/], keyframes: true, fontFace: true }, diff --git a/docs/plugins/postcss.md b/docs/plugins/postcss.md index cc23d70d..e82c65cf 100644 --- a/docs/plugins/postcss.md +++ b/docs/plugins/postcss.md @@ -51,18 +51,19 @@ All of the options of PurgeCSS are available to use with the plugins. You will find below the type definition of the main options available. For the complete list, go to the [PurgeCSS documentation website](https://www.purgecss.com/configuration.html#options). ```ts -interface UserDefinedOptions { - content: Array - css: Array - defaultExtractor?: ExtractorFunction - extractors?: Array - fontFace?: boolean - keyframes?: boolean - output?: string - variables?: boolean - whitelist?: string[] - whitelistPatterns?: Array - whitelistPatternsChildren?: Array +export interface UserDefinedOptions { + content?: Array; + contentFunction?: (sourceFile: string) => Array; + defaultExtractor?: ExtractorFunction; + extractors?: Array; + fontFace?: boolean; + keyframes?: boolean; + output?: string; + rejected?: boolean; + stdin?: boolean; + stdout?: boolean; + variables?: boolean; + safelist?: UserDefinedSafelist; } interface RawContent { diff --git a/packages/grunt-purgecss/README.md b/packages/grunt-purgecss/README.md index 489fea7c..f770fe7e 100644 --- a/packages/grunt-purgecss/README.md +++ b/packages/grunt-purgecss/README.md @@ -67,17 +67,29 @@ Type: `Array` Purgecss can be adapted to suit your needs. If you notice a lot of unused CSS is not being removed, you might want to use a custom extractor. More information about extractors [here](https://www.purgecss.com/extractors.html). -#### options.whitelist +#### options.safelist -Type: `Array` +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. -You can whitelist selectors to stop Purgecss from removing them from your CSS. This can be accomplished with the options `whitelist` and `whitelistPatterns`. +Two forms are available for this option. -#### options.whitelistPatterns +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. -Type: `Array` +The _complex_ form is: -You can whitelist selectors based on a regular expression with `whitelistPatterns`. +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` #### options.keyframes @@ -112,8 +124,7 @@ grunt.initConfig({ }, extension: ['html', 'blade'] }, - whitelist: ['random', 'yep', 'button'], - whitelistPatterns: [/red$/], + safelist: ['random', 'yep', 'button', /red$/] keyframes: true, fontFace: true }, diff --git a/packages/postcss-purgecss/README.md b/packages/postcss-purgecss/README.md index 8a4a8ec7..32d5b027 100644 --- a/packages/postcss-purgecss/README.md +++ b/packages/postcss-purgecss/README.md @@ -61,15 +61,29 @@ Type: `Array` Purgecss can be adapted to suit your needs. If you notice a lot of unused CSS is not being removed, you might want to use a custom extractor. More information about extractors [here](https://www.purgecss.com/extractors.html). -### `whitelist` -Type: `Array` +### `safelist` + +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. + +Two forms are available for this option. -You can whitelist selectors to stop Purgecss from removing them from your CSS. This can be accomplished with the options whitelist and whitelistPatterns. +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. -### `whitelistPatterns` -Type: `Array` +The _complex_ form is: -You can whitelist selectors based on a regular expression with whitelistPatterns. +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` ### `rejected` Type: `boolean` diff --git a/packages/postcss-purgecss/src/types/index.ts b/packages/postcss-purgecss/src/types/index.ts index 490b6d32..447c89ab 100644 --- a/packages/postcss-purgecss/src/types/index.ts +++ b/packages/postcss-purgecss/src/types/index.ts @@ -16,7 +16,6 @@ export interface Extractors { export interface UserDefinedOptions { content?: Array; contentFunction?: (sourceFile: string) => Array; - css: Array; defaultExtractor?: ExtractorFunction; extractors?: Array; fontFace?: boolean; From 72cf2b751dddd9beb55ebfc33045433155e29244 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Mon, 10 Aug 2020 07:10:58 +0200 Subject: [PATCH 18/36] fix typo: encourage -> encouraged --- docs/extractors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extractors.md b/docs/extractors.md index 99dcd163..7dcbf0f2 100644 --- a/docs/extractors.md +++ b/docs/extractors.md @@ -65,7 +65,7 @@ const purgeFromJs = (content) => { ::: warning Those extractors are still a work in progress. -It is not encourage to use them in production yet. +It is not encouraged to use them in production yet. ::: - [purgecss-from-html](https://github.com/FullHuman/purgecss/blob/master/packages/purgecss-from-html): HTML files (.html) From 6f0cab7d9a72d99c99d5edefaa67a1e566626384 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2020 07:15:53 +0000 Subject: [PATCH 19/36] build(deps-dev): bump grunt from 1.2.1 to 1.3.0 Bumps [grunt](https://github.com/gruntjs/grunt) from 1.2.1 to 1.3.0. - [Release notes](https://github.com/gruntjs/grunt/releases) - [Changelog](https://github.com/gruntjs/grunt/blob/master/CHANGELOG) - [Commits](https://github.com/gruntjs/grunt/compare/v1.2.1...v1.3.0) Signed-off-by: dependabot-preview[bot] --- packages/grunt-purgecss/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grunt-purgecss/package.json b/packages/grunt-purgecss/package.json index ecd43037..d7ee0206 100644 --- a/packages/grunt-purgecss/package.json +++ b/packages/grunt-purgecss/package.json @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/grunt": "^0.4.25", - "grunt": "~1.2.1" + "grunt": "~1.3.0" }, "bugs": { "url": "https://github.com/FullHuman/purgecss/issues" From 10d1efc935c597b0f61f3957a37627facba38207 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2020 07:13:59 +0000 Subject: [PATCH 20/36] build(deps-dev): bump mini-css-extract-plugin from 0.9.0 to 0.11.0 Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 0.9.0 to 0.11.0. - [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases) - [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v0.9.0...v0.11.0) Signed-off-by: dependabot-preview[bot] --- packages/purgecss-webpack-plugin/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purgecss-webpack-plugin/package.json b/packages/purgecss-webpack-plugin/package.json index 2896cee7..67f30aad 100644 --- a/packages/purgecss-webpack-plugin/package.json +++ b/packages/purgecss-webpack-plugin/package.json @@ -47,7 +47,7 @@ "@types/webpack": "^4.41.12", "@types/webpack-sources": "^1.4.0", "css-loader": "^4.0.0", - "mini-css-extract-plugin": "^0.9.0" + "mini-css-extract-plugin": "^0.11.0" }, "peerDependencies": { "webpack": "*" From 18a617e9ca567c40c2161a0e0c93052fd581fac5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2020 07:15:55 +0000 Subject: [PATCH 21/36] build(deps-dev): bump rollup-plugin-terser from 6.1.0 to 7.0.1 Bumps [rollup-plugin-terser](https://github.com/TrySound/rollup-plugin-terser) from 6.1.0 to 7.0.1. - [Release notes](https://github.com/TrySound/rollup-plugin-terser/releases) - [Commits](https://github.com/TrySound/rollup-plugin-terser/commits) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abc65bb0..a4fc492c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "prettier": "^2.0.4", "rollup": "^2.6.1", "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-terser": "^6.1.0", + "rollup-plugin-terser": "^7.0.1", "ts-jest": "^24.3.0", "ts-node": "^8.8.2", "typescript": "^3.8.3", From aed746201fe5d600f49a0b8f96fe2ae7d022a8b2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 5 Sep 2020 10:05:58 +0000 Subject: [PATCH 22/36] build(deps-dev): bump typescript from 3.9.7 to 4.0.2 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 3.9.7 to 4.0.2. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v3.9.7...v4.0.2) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a4fc492c..da75bef5 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "rollup-plugin-terser": "^7.0.1", "ts-jest": "^24.3.0", "ts-node": "^8.8.2", - "typescript": "^3.8.3", + "typescript": "^4.0.2", "vuepress": "^1.4.1" }, "scripts": { From 2d24c842ba1137c306f4cf41e18cc6693b62049d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 5 Sep 2020 10:12:45 +0000 Subject: [PATCH 23/36] build(deps-dev): bump ts-node from 8.10.2 to 9.0.0 Bumps [ts-node](https://github.com/TypeStrong/ts-node) from 8.10.2 to 9.0.0. - [Release notes](https://github.com/TypeStrong/ts-node/releases) - [Commits](https://github.com/TypeStrong/ts-node/compare/v8.10.2...v9.0.0) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da75bef5..50e6708e 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-terser": "^7.0.1", "ts-jest": "^24.3.0", - "ts-node": "^8.8.2", + "ts-node": "^9.0.0", "typescript": "^4.0.2", "vuepress": "^1.4.1" }, From 2c3d700ee6190692403e0e5f82b9ac5aa77cf7c7 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sat, 5 Sep 2020 19:30:13 +0100 Subject: [PATCH 24/36] build(dependencies): update dependencies - update monorepo level dependencies with npm update --- package.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 50e6708e..b96718ba 100644 --- a/package.json +++ b/package.json @@ -15,25 +15,25 @@ "devDependencies": { "@types/jest": "^26.0.3", "@types/node": "^14.0.5", - "@typescript-eslint/eslint-plugin": "^2.28.0", - "@typescript-eslint/parser": "^2.28.0", - "@vuepress/plugin-google-analytics": "^1.4.1", - "@vuepress/theme-default": "^1.4.1", - "@wessberg/rollup-plugin-ts": "^1.2.24", - "conventional-changelog-cli": "^2.0.31", + "@typescript-eslint/eslint-plugin": "^2.34.0", + "@typescript-eslint/parser": "^2.34.0", + "@vuepress/plugin-google-analytics": "^1.5.4", + "@vuepress/theme-default": "^1.5.4", + "@wessberg/rollup-plugin-ts": "^1.3.4", + "conventional-changelog-cli": "^2.1.0", "eslint": "^6.8.0", "husky": "^4.2.5", "jest": "^24.9.0", - "lerna": "^3.20.2", - "lint-staged": "^10.1.6", - "prettier": "^2.0.4", - "rollup": "^2.6.1", + "lerna": "^3.22.1", + "lint-staged": "^10.3.0", + "prettier": "^2.1.1", + "rollup": "^2.26.10", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-terser": "^7.0.1", "ts-jest": "^24.3.0", "ts-node": "^9.0.0", "typescript": "^4.0.2", - "vuepress": "^1.4.1" + "vuepress": "^1.5.4" }, "scripts": { "build": "ts-node scripts/build.ts", From 3961afbc6d90eae83fe4862a4498857fa7ba3ff6 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sat, 5 Sep 2020 19:31:56 +0100 Subject: [PATCH 25/36] feat(CLI): add blocklist option --- packages/purgecss/bin/purgecss.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/purgecss/bin/purgecss.js b/packages/purgecss/bin/purgecss.js index cd544fd5..1aba27d4 100755 --- a/packages/purgecss/bin/purgecss.js +++ b/packages/purgecss/bin/purgecss.js @@ -39,6 +39,11 @@ program "-s, --safellist ", "list of classes that should not be removed (comma separated)", getList + ) + .option( + "-b, --blocklist ", + "list of selectors that should be removed", + getList ); program.parse(process.argv); @@ -63,6 +68,7 @@ if (!program.config && !(program.content && program.css)) { if (program.rejected) options.rejected = program.rejected; if (program.variables) options.variables = program.variables; if (program.safelist) options.safelist = program.safelist; + if (program.blocklist) options.blocklist = program.blocklist; const purged = await new PurgeCSS().purge(options); From d4fb6fa0952c078595ba42be1815fec5aa023a31 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sat, 5 Sep 2020 19:36:11 +0100 Subject: [PATCH 26/36] docs: add blocklist option - add blocklist option to types plugins - add blocklist option to docs --- docs/configuration.md | 10 ++++++++++ docs/plugins/postcss.md | 3 +++ packages/postcss-purgecss/README.md | 10 ++++++++++ packages/postcss-purgecss/src/types/index.ts | 6 +++++- packages/purgecss-webpack-plugin/src/types/index.ts | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 4e9d3986..b76a0a3e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -49,6 +49,7 @@ interface UserDefinedOptions { stdout?: boolean; variables?: boolean; safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } interface RawContent { @@ -301,3 +302,12 @@ const purgecss = await new PurgeCSS().purge({ ``` In this example, selectors such as `button.bg-red.nonexistent-class` will be left in the final CSS, even if `button` and `nonexistent-class` are not found. + +- **blocklist** + +Blocklist will block the CSS selectors from appearing in the final output CSS. The selectors will be removed even when they are seen as used by PurgeCSS. + +```ts +blocklist: ['usedClass', /^nav-/] +``` +Even if nav-links and usedClass are found by an extractor, they will be removed. diff --git a/docs/plugins/postcss.md b/docs/plugins/postcss.md index e82c65cf..21708b7e 100644 --- a/docs/plugins/postcss.md +++ b/docs/plugins/postcss.md @@ -64,6 +64,7 @@ export interface UserDefinedOptions { stdout?: boolean; variables?: boolean; safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } interface RawContent { @@ -74,4 +75,6 @@ interface RawContent { interface RawCSS { raw: string } + +type StringRegExpArray = Array; ``` diff --git a/packages/postcss-purgecss/README.md b/packages/postcss-purgecss/README.md index 32d5b027..b9a18829 100644 --- a/packages/postcss-purgecss/README.md +++ b/packages/postcss-purgecss/README.md @@ -85,6 +85,16 @@ safelist: { } ``` +### `blocklist` + +Blocklist will block the CSS selectors from appearing in the final output CSS. The selectors will be removed even when they are seen as used by PurgeCSS. + +```ts +blocklist: ['usedClass', /^nav-/] +``` +Even if nav-links and usedClass are found by an extractor, they will be removed. + + ### `rejected` Type: `boolean` Default value: `false` diff --git a/packages/postcss-purgecss/src/types/index.ts b/packages/postcss-purgecss/src/types/index.ts index 447c89ab..20a5352b 100644 --- a/packages/postcss-purgecss/src/types/index.ts +++ b/packages/postcss-purgecss/src/types/index.ts @@ -1,4 +1,7 @@ -import { UserDefinedSafelist } from "../../../purgecss/src/types/index"; +import { + UserDefinedSafelist, + StringRegExpArray, +} from "../../../purgecss/src/types/index"; export interface RawContent { extension: string; @@ -26,4 +29,5 @@ export interface UserDefinedOptions { stdout?: boolean; variables?: boolean; safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } diff --git a/packages/purgecss-webpack-plugin/src/types/index.ts b/packages/purgecss-webpack-plugin/src/types/index.ts index e6e0b1ea..9b84687a 100644 --- a/packages/purgecss-webpack-plugin/src/types/index.ts +++ b/packages/purgecss-webpack-plugin/src/types/index.ts @@ -34,6 +34,7 @@ export interface UserDefinedOptions { variables?: boolean; verbose?: boolean; safelist?: StringRegExpArray | ComplexSafelist | SafelistFunction; + blocklist?: StringRegExpArray; only?: string[]; } From 35e5c45f1f0954696457792fa2e58930977fe385 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Thu, 10 Sep 2020 22:26:10 +0100 Subject: [PATCH 27/36] feat(CLI): use variadic options for css, content - use commander variadic options for css, content, safelist, blocklist - add basic integration test for the CLI - add cmd.ts file, helper to run test --- package.json | 14 +++--- packages/purgecss/__tests__/cli.test.ts | 17 +++++++ packages/purgecss/__tests__/cmd.ts | 50 +++++++++++++++++++ .../cli/simple/expected/styles.css | 12 +++++ .../test_examples/cli/simple/src/content.html | 13 +++++ .../test_examples/cli/simple/src/index.js | 1 + .../test_examples/cli/simple/src/style.css | 15 ++++++ packages/purgecss/bin/purgecss.js | 42 +++++++--------- 8 files changed, 133 insertions(+), 31 deletions(-) create mode 100644 packages/purgecss/__tests__/cli.test.ts create mode 100644 packages/purgecss/__tests__/cmd.ts create mode 100644 packages/purgecss/__tests__/test_examples/cli/simple/expected/styles.css create mode 100644 packages/purgecss/__tests__/test_examples/cli/simple/src/content.html create mode 100644 packages/purgecss/__tests__/test_examples/cli/simple/src/index.js create mode 100644 packages/purgecss/__tests__/test_examples/cli/simple/src/style.css diff --git a/package.json b/package.json index b96718ba..8020daee 100644 --- a/package.json +++ b/package.json @@ -15,22 +15,22 @@ "devDependencies": { "@types/jest": "^26.0.3", "@types/node": "^14.0.5", - "@typescript-eslint/eslint-plugin": "^2.34.0", - "@typescript-eslint/parser": "^2.34.0", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", "@vuepress/plugin-google-analytics": "^1.5.4", "@vuepress/theme-default": "^1.5.4", "@wessberg/rollup-plugin-ts": "^1.3.4", "conventional-changelog-cli": "^2.1.0", - "eslint": "^6.8.0", - "husky": "^4.2.5", - "jest": "^24.9.0", + "eslint": "^7.8.0", + "husky": "^4.3.0", + "jest": "^26.4.0", "lerna": "^3.22.1", "lint-staged": "^10.3.0", "prettier": "^2.1.1", - "rollup": "^2.26.10", + "rollup": "^2.26.11", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-terser": "^7.0.1", - "ts-jest": "^24.3.0", + "ts-jest": "^26.3.0", "ts-node": "^9.0.0", "typescript": "^4.0.2", "vuepress": "^1.5.4" diff --git a/packages/purgecss/__tests__/cli.test.ts b/packages/purgecss/__tests__/cli.test.ts new file mode 100644 index 00000000..cbf15dc4 --- /dev/null +++ b/packages/purgecss/__tests__/cli.test.ts @@ -0,0 +1,17 @@ +import { promisify } from "util"; +import { exec } from "child_process"; +import path from "path"; + +const asyncExec = promisify(exec); + +describe("PurgeCSS CLI", () => { + const purgeCSSExecutable = path.resolve(__dirname, "./../bin/purgecss.js"); + const testFolder = path.resolve(__dirname, "./test_examples/cli/simple/"); + it("should print the correct output", async () => { + const response = await asyncExec( + `${purgeCSSExecutable} --content ${testFolder}/src/content.html ${testFolder}/src/*.js --css ${testFolder}/src/style.css` + ); + const result = JSON.parse(response.stdout) + expect(result[0].css).toBe('.hello {\n color: red;\n}\n') + }); +}); diff --git a/packages/purgecss/__tests__/cmd.ts b/packages/purgecss/__tests__/cmd.ts new file mode 100644 index 00000000..8fc07403 --- /dev/null +++ b/packages/purgecss/__tests__/cmd.ts @@ -0,0 +1,50 @@ +import path from 'path' +import { spawn, SpawnOptionsWithoutStdio } from 'child_process' + +const purgeCSSExecutable = path.resolve(__dirname, "./../bin/purgecss.js"); + +function spawnCommand(command: string, options?: SpawnOptionsWithoutStdio) { + let file = '/bin/sh' + let args: string[] = ['-c', command] + if (process.platform === 'win32') { + file = 'cmd.exe'; + args = ['/s', '/c', '"' + command + '"']; + // options = util._extend({}, options); + if (options) options.windowsVerbatimArguments = true; + } + return spawn(file, args, options); +} + +export function runCLI(args = '', cwd = process.cwd()): Promise { + const isRelative = cwd[0] !== '/' + if (isRelative) { + cwd = path.resolve(__dirname, cwd) + } + + return new Promise((resolve, reject) => { + let stdout = '' + let stderr = '' + const command = `${purgeCSSExecutable} ${args}` + const child = spawnCommand(command, {cwd}) + + child.on('error', error => { + reject(error) + }) + + child.stdout.on('data', data => { + stdout += data.toString() + }) + + child.stderr.on('data', data => { + stderr += data.toString() + }) + + child.on('close', () => { + if (stderr) { + reject(stderr) + } else { + resolve(stdout) + } + }) + }) + } \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/cli/simple/expected/styles.css b/packages/purgecss/__tests__/test_examples/cli/simple/expected/styles.css new file mode 100644 index 00000000..88f614e7 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/cli/simple/expected/styles.css @@ -0,0 +1,12 @@ +.hello { + color: red; +} + +.safelisted { + color: green; +} + +md\:w-2\/3 { + color: red; +} + diff --git a/packages/purgecss/__tests__/test_examples/cli/simple/src/content.html b/packages/purgecss/__tests__/test_examples/cli/simple/src/content.html new file mode 100644 index 00000000..8fbbb60f --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/cli/simple/src/content.html @@ -0,0 +1,13 @@ + + + + Purgecss webpack test + + + +
+
+
+ + + \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/cli/simple/src/index.js b/packages/purgecss/__tests__/test_examples/cli/simple/src/index.js new file mode 100644 index 00000000..cab743ad --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/cli/simple/src/index.js @@ -0,0 +1 @@ +import './style.css' diff --git a/packages/purgecss/__tests__/test_examples/cli/simple/src/style.css b/packages/purgecss/__tests__/test_examples/cli/simple/src/style.css new file mode 100644 index 00000000..e2d156dd --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/cli/simple/src/style.css @@ -0,0 +1,15 @@ +.hello { + color: red; +} + +.unused { + color: blue; +} + +.safelisted { + color: green; +} + +md\:w-2\/3 { + color: red; +} diff --git a/packages/purgecss/bin/purgecss.js b/packages/purgecss/bin/purgecss.js index 1aba27d4..1fcdea3c 100755 --- a/packages/purgecss/bin/purgecss.js +++ b/packages/purgecss/bin/purgecss.js @@ -7,10 +7,6 @@ const { setOptions, } = require("../lib/purgecss"); -function getList(list) { - return list.split(","); -} - async function writeCSSToFile(filePath, css) { try { await fs.promises.writeFile(filePath, css); @@ -20,13 +16,12 @@ async function writeCSSToFile(filePath, css) { } program - .usage("--css --content [options]") + .usage("--css --content [options]") .option( - "-con, --content ", - "glob of content files (comma separated)", - getList + "-con, --content ", + "glob of content files" ) - .option("-css, --css ", "glob of css files (comma separated)", getList) + .option("-css, --css ", "glob of css files") .option("-c, --config ", "path to the configuration file") .option( "-o, --output ", @@ -36,25 +31,23 @@ program .option("-keyframes, --keyframes", "option to remove unused keyframes") .option("-rejected, --rejected", "option to output rejected selectors") .option( - "-s, --safellist ", - "list of classes that should not be removed (comma separated)", - getList + "-s, --safelist ", + "list of classes that should not be removed" ) .option( - "-b, --blocklist ", - "list of selectors that should be removed", - getList + "-b, --blocklist ", + "list of selectors that should be removed" ); -program.parse(process.argv); +program.parse(process.argv); -// config file is not specified or the content and css are not, -// PurgeCSS will not run -if (!program.config && !(program.content && program.css)) { - program.help(); -} +const run = async () => { + // config file is not specified or the content and css are not, + // PurgeCSS will not run + if (!program.config && !(program.content && program.css)) { + program.help(); + } -(async () => { // if the config file is present, use it // other options specified will override let options = defaultOptions; @@ -71,7 +64,6 @@ if (!program.config && !(program.content && program.css)) { if (program.blocklist) options.blocklist = program.blocklist; const purged = await new PurgeCSS().purge(options); - const output = options.output || program.output; // output results in specified directory if (output) { @@ -87,4 +79,6 @@ if (!program.config && !(program.content && program.css)) { } else { console.log(JSON.stringify(purged)); } -})(); +}; + +run(); From d60f7e092b125115efbe73e4bc87f4144602b675 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Thu, 10 Sep 2020 22:27:02 +0100 Subject: [PATCH 28/36] docs: change CLI option no longer require to be comma separated - update CLI documentation with variadic option --- docs/CLI.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index c774b113..db510969 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -28,14 +28,14 @@ To see the available options for the CLI: `purgecss --help` Usage: purgecss --css --content [options] Options: - -con, --content glob of content files (comma separated) - -css, --css glob of css files (comma separated) + -con, --content glob of content files + -css, --css glob of css files -c, --config path to the configuration file -o, --output file path directory to write purged css files to -font, --font-face option to remove unused font-faces -keyframes, --keyframes option to remove unused keyframes -rejected, --rejected option to output rejected selectors - -s, --safelist list of classes that should not be removed (comma separated) + -s, --safelist list of classes that should not be removed -h, --help display help for command ``` @@ -44,15 +44,15 @@ The options available through the CLI are similar to the ones available with a c ### --css ```text -purgecss --css css/app.css,css/palette.css --content src/index.html +purgecss --css css/app.css css/palette.css --content src/index.html ``` ### --content -You can specify content that should be analyzed by PurgeCSS with an array of filenames or [globs](https://github.com/isaacs/node-glob/blob/master/README.md#glob-primer). These files can be HTML, Pug, Blade, etc. The files should be comma separated. +You can specify content that should be analyzed by PurgeCSS with an array of filenames or [globs](https://github.com/isaacs/node-glob/blob/master/README.md#glob-primer). These files can be HTML, Pug, Blade, etc. ```text -purgecss --css css/app.css --content src/index.html,src/**/*.js +purgecss --css css/app.css --content src/index.html src/**/*.js ``` ### --config @@ -68,7 +68,7 @@ purgecss --config ./purgecss.config.js By default, the CLI outputs the result in the console. If you wish to return the CSS as files, specify the directory to write the purified CSS files to. ```text -purgecss --css css/app.css --content src/index.html,"src/**/*.js" --output build/css/ +purgecss --css css/app.css --content src/index.html "src/**/*.js" --output build/css/ ``` ### --safelist From 9cb763edbbaa3c4953733cfa4bc0261c0d8c0ca9 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Thu, 10 Sep 2020 22:28:23 +0100 Subject: [PATCH 29/36] refactor: remove unused file cmd.ts - remove unused test helper --- packages/purgecss/__tests__/cmd.ts | 50 ------------------------------ 1 file changed, 50 deletions(-) delete mode 100644 packages/purgecss/__tests__/cmd.ts diff --git a/packages/purgecss/__tests__/cmd.ts b/packages/purgecss/__tests__/cmd.ts deleted file mode 100644 index 8fc07403..00000000 --- a/packages/purgecss/__tests__/cmd.ts +++ /dev/null @@ -1,50 +0,0 @@ -import path from 'path' -import { spawn, SpawnOptionsWithoutStdio } from 'child_process' - -const purgeCSSExecutable = path.resolve(__dirname, "./../bin/purgecss.js"); - -function spawnCommand(command: string, options?: SpawnOptionsWithoutStdio) { - let file = '/bin/sh' - let args: string[] = ['-c', command] - if (process.platform === 'win32') { - file = 'cmd.exe'; - args = ['/s', '/c', '"' + command + '"']; - // options = util._extend({}, options); - if (options) options.windowsVerbatimArguments = true; - } - return spawn(file, args, options); -} - -export function runCLI(args = '', cwd = process.cwd()): Promise { - const isRelative = cwd[0] !== '/' - if (isRelative) { - cwd = path.resolve(__dirname, cwd) - } - - return new Promise((resolve, reject) => { - let stdout = '' - let stderr = '' - const command = `${purgeCSSExecutable} ${args}` - const child = spawnCommand(command, {cwd}) - - child.on('error', error => { - reject(error) - }) - - child.stdout.on('data', data => { - stdout += data.toString() - }) - - child.stderr.on('data', data => { - stderr += data.toString() - }) - - child.on('close', () => { - if (stderr) { - reject(stderr) - } else { - resolve(stdout) - } - }) - }) - } \ No newline at end of file From 1f192e8f8d772db1a980a0d669fa79a7f2f81020 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Fri, 11 Sep 2020 13:12:28 +0100 Subject: [PATCH 30/36] build: fix eslint errors - replace ban-ts-ignore with ban-ts-comment - add pseudo type for webpack Chunk --- packages/gulp-purgecss/src/index.ts | 2 +- packages/purgecss-webpack-plugin/src/index.ts | 2 +- packages/purgecss-webpack-plugin/src/search.ts | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/gulp-purgecss/src/index.ts b/packages/gulp-purgecss/src/index.ts index acb977fe..c536e4e2 100644 --- a/packages/gulp-purgecss/src/index.ts +++ b/packages/gulp-purgecss/src/index.ts @@ -60,7 +60,7 @@ function gulpPurgeCSS(options: UserDefinedOptions): internal.Transform { optionsGulp.rejected && purge.rejected ? purge.rejected.join(" {}\n") + " {}" : purge.css; - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore streamFile.contents = Buffer.from(result, "utf-8"); callback(null, file); diff --git a/packages/purgecss-webpack-plugin/src/index.ts b/packages/purgecss-webpack-plugin/src/index.ts index 8486a381..f6b59943 100644 --- a/packages/purgecss-webpack-plugin/src/index.ts +++ b/packages/purgecss-webpack-plugin/src/index.ts @@ -35,7 +35,7 @@ export default class PurgeCSSPlugin { } if (this.options.rejected) { - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore stats["purged"] = this.purgedStats; } diff --git a/packages/purgecss-webpack-plugin/src/search.ts b/packages/purgecss-webpack-plugin/src/search.ts index 933ae184..3e61d205 100644 --- a/packages/purgecss-webpack-plugin/src/search.ts +++ b/packages/purgecss-webpack-plugin/src/search.ts @@ -49,11 +49,14 @@ export function getAssets( return purgeAssets; } +type Chunk = { + modulesIterable: File[] +} + export function files( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - chunk: any, + chunk: Chunk, extensions: string[], - getter: Function + getter: (file: File) => string | undefined ): string[] { const mods = []; for (const module of Array.from(chunk.modulesIterable || [])) { From c871293af8f219d48f88b9657ddd28733554c695 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Fri, 11 Sep 2020 14:54:29 +0100 Subject: [PATCH 31/36] build: fix build errors - fix build errors - change prettier ignore file - remove webpack test for search with strings --- .eslintignore | 2 -- package.json | 2 +- packages/grunt-purgecss/tasks/purgecss.js | 31 +------------------ .../__tests__/search.test.ts | 26 +++------------- .../purgecss-webpack-plugin/src/search.ts | 6 +--- .../src/types/index.ts | 4 +++ packages/purgecss/__tests__/cli.test.ts | 4 +-- 7 files changed, 13 insertions(+), 62 deletions(-) delete mode 100644 .eslintignore diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 94f7bae0..00000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -packages/*/lib/* -packages/grunt-purgecss/tasks/purgecss.js diff --git a/package.json b/package.json index 8020daee..97e3d55f 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ }, "lint-staged": { "*.js": [ - "prettier --write", + "prettier --write --ignore-path .gitignore", "git add" ], "*.ts": [ diff --git a/packages/grunt-purgecss/tasks/purgecss.js b/packages/grunt-purgecss/tasks/purgecss.js index 55333df4..e85fd7e8 100644 --- a/packages/grunt-purgecss/tasks/purgecss.js +++ b/packages/grunt-purgecss/tasks/purgecss.js @@ -1,30 +1 @@ -"use strict"; -var t, - e = require("purgecss"), - s = (t = e) && "object" == typeof t && "default" in t ? t.default : t; -function i(t, e = []) { - return e.filter( - (e) => - !!t.file.exists(e) || (t.log.warn(`Source file "${e}" not found.`), !1) - ); -} -module.exports = function (t) { - t.registerMultiTask("purgecss", "Grunt plugin for PurgeCSS", function () { - const n = this.async(), - o = this.options(e.defaultOptions); - for (const e of this.files) { - const r = i(t, e.src); - new s() - .purge({ ...o, css: r }) - .then((s) => { - if (void 0 === e.dest) throw new Error("Destination file not found"); - t.file.write(e.dest, s[0].css), - t.log.writeln(`File "${e.dest}" created.`), - n(); - }) - .catch(() => { - n(!1); - }); - } - }); -}; +"use strict";var t=require("purgecss");function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t);function r(t,e=[]){return e.filter(e=>!!t.file.exists(e)||(t.log.warn(`Source file "${e}" not found.`),!1))}module.exports=function(e){e.registerMultiTask("purgecss","Grunt plugin for PurgeCSS",(function(){const s=this.async(),i=this.options(t.defaultOptions);for(const t of this.files){const o=r(e,t.src);(new n.default).purge({...i,css:o}).then(n=>{if(void 0===t.dest)throw new Error("Destination file not found");e.file.write(t.dest,n[0].css),e.log.writeln(`File "${t.dest}" created.`),s()}).catch(()=>{s(!1)})}}))}; diff --git a/packages/purgecss-webpack-plugin/__tests__/search.test.ts b/packages/purgecss-webpack-plugin/__tests__/search.test.ts index 16a2592e..03799128 100644 --- a/packages/purgecss-webpack-plugin/__tests__/search.test.ts +++ b/packages/purgecss-webpack-plugin/__tests__/search.test.ts @@ -1,5 +1,5 @@ import { getAssets, files } from "../src/search"; -import { File } from "../src/types"; +import { Chunk, File } from "../src/types"; describe("Search assets", () => { it("returns matches based on a pattern", () => { @@ -46,30 +46,12 @@ describe("Search assets", () => { }); }); -interface Chunk { - modulesIterable?: string[] | File[]; -} - describe("Search files", () => { let chunk: Chunk; beforeEach(() => { - chunk = {}; - }); - - it("returns matches based on extension", () => { - chunk.modulesIterable = ["foobar.txt", "barbar.css"]; - const extensions = [".txt"]; - const matches = ["foobar.txt"]; - - expect(files(chunk, extensions, (a: string) => a)).toEqual(matches); - }); - - it("does not fail with missing modules", () => { - chunk.modulesIterable = ["foobar.txt", "", "barbar.css"]; - const extensions = [".txt"]; - const matches = ["foobar.txt"]; - - expect(files(chunk, extensions, (a: string) => a)).toEqual(matches); + chunk = { + modulesIterable: [], + }; }); it("returns matches based on extension with a customized getter", () => { diff --git a/packages/purgecss-webpack-plugin/src/search.ts b/packages/purgecss-webpack-plugin/src/search.ts index 3e61d205..c4091730 100644 --- a/packages/purgecss-webpack-plugin/src/search.ts +++ b/packages/purgecss-webpack-plugin/src/search.ts @@ -1,5 +1,5 @@ import path from "path"; -import { PurgeAsset } from "./types"; +import { Chunk, File, PurgeAsset } from "./types"; /** * Get the filename without ?hash @@ -49,10 +49,6 @@ export function getAssets( return purgeAssets; } -type Chunk = { - modulesIterable: File[] -} - export function files( chunk: Chunk, extensions: string[], diff --git a/packages/purgecss-webpack-plugin/src/types/index.ts b/packages/purgecss-webpack-plugin/src/types/index.ts index 9b84687a..c23072b2 100644 --- a/packages/purgecss-webpack-plugin/src/types/index.ts +++ b/packages/purgecss-webpack-plugin/src/types/index.ts @@ -52,3 +52,7 @@ export interface PurgeAsset { export interface File { resource?: string; } + +export type Chunk = { + modulesIterable: File[]; +}; diff --git a/packages/purgecss/__tests__/cli.test.ts b/packages/purgecss/__tests__/cli.test.ts index cbf15dc4..d1b6132e 100644 --- a/packages/purgecss/__tests__/cli.test.ts +++ b/packages/purgecss/__tests__/cli.test.ts @@ -11,7 +11,7 @@ describe("PurgeCSS CLI", () => { const response = await asyncExec( `${purgeCSSExecutable} --content ${testFolder}/src/content.html ${testFolder}/src/*.js --css ${testFolder}/src/style.css` ); - const result = JSON.parse(response.stdout) - expect(result[0].css).toBe('.hello {\n color: red;\n}\n') + const result = JSON.parse(response.stdout); + expect(result[0].css).toBe(".hello {\n color: red;\n}\n"); }); }); From 761eed87c623ee87338ef999ea755099b36bd7ae Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sun, 20 Sep 2020 14:53:37 +0100 Subject: [PATCH 32/36] fix(CLI): exit process on error with code 1 - print error message if CLI fails - exit with code 1 #477 --- packages/purgecss/bin/purgecss.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/purgecss/bin/purgecss.js b/packages/purgecss/bin/purgecss.js index 1fcdea3c..23d408e0 100755 --- a/packages/purgecss/bin/purgecss.js +++ b/packages/purgecss/bin/purgecss.js @@ -17,10 +17,7 @@ async function writeCSSToFile(filePath, css) { program .usage("--css --content [options]") - .option( - "-con, --content ", - "glob of content files" - ) + .option("-con, --content ", "glob of content files") .option("-css, --css ", "glob of css files") .option("-c, --config ", "path to the configuration file") .option( @@ -39,7 +36,7 @@ program "list of selectors that should be removed" ); -program.parse(process.argv); +program.parse(process.argv); const run = async () => { // config file is not specified or the content and css are not, @@ -81,4 +78,9 @@ const run = async () => { } }; -run(); +try { + run(); +} catch (error) { + console.error(error.message); + process.exit(1); +} From 0f177056c14fe43a7b83c6b03deeabddab25ce5b Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sun, 20 Sep 2020 15:00:25 +0100 Subject: [PATCH 33/36] docs: add sponsor - add sponsor to the Readmes and documentation - replace whitelisting with safelisting --- README.md | 6 +++++- docs/README.md | 5 +++++ packages/purgecss/README.md | 6 +++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6468795d..a04a9447 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,10 @@ When you are building a website, chances are that you are using a css framework 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. +## Sponsors 🥰 + +[](tailwindcss.com) + ## Documentation You can find the PurgeCSS documentation on [this website](https://purgecss.com). @@ -28,7 +32,7 @@ You can find the PurgeCSS documentation on [this website](https://purgecss.com). - [Configuration](https://purgecss.com/configuration.html) - [Command Line Interface](https://purgecss.com/CLI.html) - [Programmatic API](https://purgecss.com/api.html) -- [Whitelisting](https://purgecss.com/safelisting.html) +- [Safelisting](https://purgecss.com/safelisting.html) - [Extractors](https://purgecss.com/extractors.html) - [Comparison](https://purgecss.com/comparison.html) diff --git a/docs/README.md b/docs/README.md index 0462e13b..5174a595 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,6 +29,11 @@ When you are building a website, you might decide to use a CSS framework like Ta 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. +## Sponsors 🥰 + +[](tailwindcss.com) + + ## Table of Contents ### PurgeCSS diff --git a/packages/purgecss/README.md b/packages/purgecss/README.md index aa7d943b..a685a5bd 100644 --- a/packages/purgecss/README.md +++ b/packages/purgecss/README.md @@ -17,9 +17,9 @@ When you are building a website, chances are that you are using a css framework 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. -## Version 1 +## Sponsors 🥰 -If you are looking for [PurgeCSS 1](https://github.com/FullHuman/purgecss/tree/v1), the documentation is [here](https://v1.purgecss.com) +[](tailwindcss.com) ## Documentation @@ -32,7 +32,7 @@ You can find the PurgeCSS documentation on [this website](https://purgecss.com). - [Configuration](https://purgecss.com/configuration.html) - [Command Line Interface](https://purgecss.com/CLI.html) - [Programmatic API](https://purgecss.com/api.html) -- [Whitelisting](https://purgecss.com/safelisting.html) +- [Safelisting](https://purgecss.com/safelisting.html) - [Extractors](https://purgecss.com/extractors.html) - [Comparison](https://purgecss.com/comparison.html) From 7af721599cd28a66d84732d2ade8ffc15996b423 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sun, 20 Sep 2020 15:05:38 +0100 Subject: [PATCH 34/36] build: add exports auto to rollup build script - exports: auto for commonjs format ouputs --- scripts/build.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/build.ts b/scripts/build.ts index e3082d3c..2bce4278 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -57,6 +57,7 @@ async function build(): Promise { }); await bundle.write({ + exports: "auto", file: path.resolve(packagesDirectory, pkg.name, `./lib/${pkg.name}.js`), format: "cjs", }); @@ -69,6 +70,7 @@ async function build(): Promise { external: ["purgecss"], }); await gruntBundle.write({ + exports: "auto", file: path.resolve( packagesDirectory, "grunt-purgecss", From 10315a80a2b0bf5ea7a93c74e74cf9590ada328e Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sun, 20 Sep 2020 15:06:16 +0100 Subject: [PATCH 35/36] build: changelog 3.0 - add changelog for PurgeCSS 3.0 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9559bd8c..d3a15f55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# [3.0](https://github.com/FullHuman/purgecss/compare/v2.3.0...v) (2020-09-06) + + +### Features + +* **CLI:** add blocklist option ([3961afb](https://github.com/FullHuman/purgecss/commit/3961afbc6d90eae83fe4862a4498857fa7ba3ff6)) +* add blocklist option ([04223f7](https://github.com/FullHuman/purgecss/commit/04223f7fe27f8d818961a53900a7c5293d2322b6)) +* add safelist option, replace whitelist +* add safelist keyframes and css variables ([dc59d30](https://github.com/FullHuman/purgecss/commit/dc59d309a4a4be9845c40966a19f9705c42a33a1)), closes [#418](https://github.com/FullHuman/purgecss/issues/418) [#439](https://github.com/FullHuman/purgecss/issues/439) + + # [](https://github.com/FullHuman/purgecss/compare/v2.0.1-alpha.0...v) (2019-11-23) From 4a2a9504a41fe361f5aa36f3a2e50eabee84aec0 Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sun, 20 Sep 2020 20:36:16 +0100 Subject: [PATCH 36/36] build: 3.0.0 --- lerna.json | 2 +- packages/grunt-purgecss/package.json | 4 ++-- packages/gulp-purgecss/package.json | 4 ++-- packages/postcss-purgecss/package.json | 4 ++-- packages/purgecss-from-html/package.json | 2 +- packages/purgecss-from-pug/package.json | 2 +- packages/purgecss-from-twig/package.json | 2 +- packages/purgecss-webpack-plugin/package.json | 4 ++-- packages/purgecss-with-wordpress/package.json | 2 +- packages/purgecss/package.json | 2 +- packages/vue-cli-plugin-purgecss/package.json | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lerna.json b/lerna.json index 75e90219..5c0f9b24 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "packages": [ "packages/*" ], - "version": "2.3.0" + "version": "3.0.0" } diff --git a/packages/grunt-purgecss/package.json b/packages/grunt-purgecss/package.json index d7ee0206..930b5aab 100644 --- a/packages/grunt-purgecss/package.json +++ b/packages/grunt-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "grunt-purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "Grunt plugin for PurgeCSS", "author": "Ffloriel", "homepage": "https://purgecss.com", @@ -30,7 +30,7 @@ "test": "echo \"Error: run tests from root\" && exit 1" }, "dependencies": { - "purgecss": "^2.3.0" + "purgecss": "^3.0.0" }, "devDependencies": { "@types/grunt": "^0.4.25", diff --git a/packages/gulp-purgecss/package.json b/packages/gulp-purgecss/package.json index 232bd7c7..f48d2a2e 100644 --- a/packages/gulp-purgecss/package.json +++ b/packages/gulp-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "gulp-purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "Gulp plugin for purgecss", "author": "Ffloriel", "homepage": "https://purgecss.com", @@ -37,7 +37,7 @@ "dependencies": { "glob": "^7.1.6", "plugin-error": "^1.0.1", - "purgecss": "^2.3.0", + "purgecss": "^3.0.0", "through2": "^4.0.1" }, "devDependencies": { diff --git a/packages/postcss-purgecss/package.json b/packages/postcss-purgecss/package.json index 503b3d29..2b86706c 100644 --- a/packages/postcss-purgecss/package.json +++ b/packages/postcss-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "@fullhuman/postcss-purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "PostCSS plugin for PurgeCSS", "author": "FoundrySH ", "homepage": "https://github.com/FullHuman/purgecss#readme", @@ -27,6 +27,6 @@ }, "dependencies": { "postcss": "7.0.32", - "purgecss": "^2.3.0" + "purgecss": "^3.0.0" } } diff --git a/packages/purgecss-from-html/package.json b/packages/purgecss-from-html/package.json index 120a241a..d6a24de5 100644 --- a/packages/purgecss-from-html/package.json +++ b/packages/purgecss-from-html/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-from-html", - "version": "2.2.0", + "version": "3.0.0", "description": "HTML extractor for PurgeCSS", "author": "Ffloriel", "homepage": "https://github.com/FullHuman/purgecss#readme", diff --git a/packages/purgecss-from-pug/package.json b/packages/purgecss-from-pug/package.json index 6ab5acb0..40e0d407 100644 --- a/packages/purgecss-from-pug/package.json +++ b/packages/purgecss-from-pug/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-from-pug", - "version": "2.3.0", + "version": "3.0.0", "description": "Pug extractor for PurgeCSS", "author": "Ffloriel", "homepage": "https://github.com/FullHuman/purgecss#readme", diff --git a/packages/purgecss-from-twig/package.json b/packages/purgecss-from-twig/package.json index 48663ff2..3093c5ac 100644 --- a/packages/purgecss-from-twig/package.json +++ b/packages/purgecss-from-twig/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-from-twig", - "version": "2.3.0", + "version": "3.0.0", "description": "Twig extractor for PurgeCSS", "author": "Ffloriel", "homepage": "https://github.com/FullHuman/purgecss#readme", diff --git a/packages/purgecss-webpack-plugin/package.json b/packages/purgecss-webpack-plugin/package.json index 67f30aad..ec591cd4 100644 --- a/packages/purgecss-webpack-plugin/package.json +++ b/packages/purgecss-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-webpack-plugin", - "version": "2.3.0", + "version": "3.0.0", "description": "PurgeCSS plugin for webpack - Remove unused css", "author": "Ffloriel", "homepage": "https://purgecss.com", @@ -36,7 +36,7 @@ "test": "echo \"Error: run tests from root\" && exit 1" }, "dependencies": { - "purgecss": "^2.3.0", + "purgecss": "^3.0.0", "webpack": "^4.42.1", "webpack-sources": "^1.4.3" }, diff --git a/packages/purgecss-with-wordpress/package.json b/packages/purgecss-with-wordpress/package.json index 3831dce8..dce82ba4 100644 --- a/packages/purgecss-with-wordpress/package.json +++ b/packages/purgecss-with-wordpress/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-with-wordpress", - "version": "2.3.0", + "version": "3.0.0", "description": "PurgeCSS with wordpress", "author": "Ffloriel", "homepage": "https://purgecss.com", diff --git a/packages/purgecss/package.json b/packages/purgecss/package.json index ce0bacea..62861b15 100644 --- a/packages/purgecss/package.json +++ b/packages/purgecss/package.json @@ -1,6 +1,6 @@ { "name": "purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "Remove unused css selectors", "author": "Ffloriel", "homepage": "https://purgecss.com", diff --git a/packages/vue-cli-plugin-purgecss/package.json b/packages/vue-cli-plugin-purgecss/package.json index 2f945f13..4455be34 100644 --- a/packages/vue-cli-plugin-purgecss/package.json +++ b/packages/vue-cli-plugin-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "@fullhuman/vue-cli-plugin-purgecss", - "version": "2.2.0", + "version": "3.0.0", "description": "vue-cli plugin to add PurgeCSS", "author": "Ffloriel", "homepage": "https://purgecss.com",