├── .babelrc ├── .eslintrc ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmrc ├── .travis.yml ├── LICENSE ├── README.md ├── __tests__ ├── buffer.test.js └── test.html ├── circle.yml ├── example └── simple │ ├── build │ └── css │ │ ├── main.css │ │ └── navigation.css │ ├── gulpfile.js │ ├── package.json │ └── src │ ├── css │ ├── main.css │ └── navigation.css │ └── index.html ├── greenkeeper.json ├── jest.config.js ├── lib ├── gulpPurgecss.es.js └── gulpPurgecss.js ├── package.json ├── rollup.config.js └── src └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "presets": [ 5 | "@babel/preset-env" 6 | ] 7 | }, 8 | "development": { 9 | "presets": [ 10 | [ 11 | "@babel/preset-env", 12 | { 13 | "targets": { 14 | "browsers": [ 15 | "last 2 versions" 16 | ] 17 | }, 18 | "modules": false 19 | } 20 | ] 21 | ] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | }, 7 | "env": { 8 | "browser": true, 9 | "commonjs": true, 10 | "node": true, 11 | "es6": true 12 | } 13 | } -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at florielfedry@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Before you open an issue, please check if a similar issue already exists or has been closed before. 2 | 3 | ### When reporting a bug, please be sure to include the following: 4 | - [ ] A descriptive title 5 | - [ ] An *isolated* way to reproduce the behavior (example: GitHub repository with code isolated to the issue that anyone can clone to observe the problem) 6 | - [ ] What version of `gulp-purgecss` you're using, and the platform(s) you're running it on 7 | - [ ] The behavior you expect to see, and the actual behavior 8 | 9 | ### When you open an issue for a feature request, please add as much detail as possible: 10 | - [ ] A descriptive title 11 | - [ ] A description of the problem you're trying to solve, including *why* you think this is a problem 12 | - [ ] An overview of the suggested solution 13 | - [ ] If the feature changes current behavior, reasons why your solution is better 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Proposed changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 4 | 5 | ## Types of changes 6 | 7 | What types of changes does your code introduce to gulp-purgecss? 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] Bugfix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | 14 | ## Checklist 15 | 16 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ 17 | 18 | - [ ] Lint and unit tests pass locally with my changes 19 | - [ ] I have added tests that prove my fix is effective or that my feature works 20 | - [ ] I have added necessary documentation (if appropriate) 21 | 22 | ## Further comments 23 | 24 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (http://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Typescript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | before_install: 5 | - npm i -g npm@5.3.0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Full Human 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gulp Purgecss 2 | 3 | The contents of this repository were moved into the 4 | [PurgeCSS](https://github.com/FullHuman/purgecss) repository. The source code can be 5 | found in [packages/gulp-purgecss](https://github.com/FullHuman/purgecss/tree/master/packages/gulp-purgecss). 6 | 7 | ``` 8 | npm i -D gulp-purgecss 9 | ``` 10 | -------------------------------------------------------------------------------- /__tests__/buffer.test.js: -------------------------------------------------------------------------------- 1 | import gulpPurgecss from "./../src/index.js" 2 | import File from "vinyl" 3 | 4 | 5 | describe('gulp-purgecss with buffer', () => { 6 | let myGulpPurgecss 7 | let fileTest 8 | beforeEach(() => { 9 | fileTest = new File({ 10 | contents: new Buffer('.unused, .used, a { color: blue; }') 11 | }) 12 | 13 | myGulpPurgecss = gulpPurgecss({ 14 | content: ["./__tests__/test.html"] 15 | }) 16 | }) 17 | 18 | it('returns a buffer', done => { 19 | myGulpPurgecss.write(fileTest) 20 | myGulpPurgecss.once('data', file => { 21 | expect(file.isBuffer()).toBe(true) 22 | done() 23 | }) 24 | }) 25 | 26 | it('returns a purified css buffer', done => { 27 | myGulpPurgecss.write(fileTest) 28 | myGulpPurgecss.once('data', file => { 29 | const result = file.contents.toString('utf8') 30 | expect(result.includes('used')).toBe(true) 31 | expect(result.includes('unused')).toBe(false) 32 | expect(result.includes('a')).toBe(true) 33 | done() 34 | }) 35 | }) 36 | 37 | }) -------------------------------------------------------------------------------- /__tests__/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 6.11.0 4 | dependencies: 5 | override: 6 | - npm i npm 7 | - npm i 8 | -------------------------------------------------------------------------------- /example/simple/build/css/main.css: -------------------------------------------------------------------------------- 1 | .title-page { 2 | color: rebeccapurple; 3 | } 4 | -------------------------------------------------------------------------------- /example/simple/build/css/navigation.css: -------------------------------------------------------------------------------- 1 | .bg-blue { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /example/simple/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp') 2 | const purgecss = require('gulp-purgecss') 3 | 4 | gulp.task('default', () => { 5 | return gulp 6 | .src('src/**/*.css') 7 | .pipe( 8 | purgecss({ 9 | content: ['src/*.html'] 10 | }) 11 | ) 12 | .pipe(gulp.dest('build')) 13 | }) 14 | -------------------------------------------------------------------------------- /example/simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-gulp-purgecss", 3 | "version": "1.0.0", 4 | "description": "example gulp-purgecss", 5 | "scripts": { 6 | "build": "npx gulp", 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "gulp": "^4.0.2", 13 | "gulp-purgecss": "^1.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/simple/src/css/main.css: -------------------------------------------------------------------------------- 1 | .title-page { 2 | color: rebeccapurple; 3 | } 4 | -------------------------------------------------------------------------------- /example/simple/src/css/navigation.css: -------------------------------------------------------------------------------- 1 | .bg-blue { 2 | background: blue; 3 | } 4 | 5 | .unused { 6 | color: green; 7 | } 8 | 9 | .shouldberemoved { 10 | width: 45px; 11 | height: 60px; 12 | } 13 | 14 | .shouldberemoved p { 15 | font-size: 2rem; 16 | } 17 | -------------------------------------------------------------------------------- /example/simple/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | The HTML5 Herald 9 | 10 | 11 | 12 | 13 | 14 | 15 |

Hello World

16 |
17 | Hover over this element 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /greenkeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": { 3 | "default": { 4 | "packages": [ 5 | "example/simple/package.json", 6 | "package.json" 7 | ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | 8 | // Stop running tests after the first failure 9 | // bail: false, 10 | 11 | // Respect "browser" field in package.json when resolving modules 12 | // browser: false, 13 | 14 | // The directory where Jest should store its cached dependency information 15 | // cacheDirectory: "/var/folders/sk/zystdgkn6bzdyp6f8kcvjdnr0000gn/T/jest_dx", 16 | 17 | // Automatically clear mock calls and instances between every test 18 | clearMocks: true, 19 | 20 | // Indicates whether the coverage information should be collected while executing the test 21 | // collectCoverage: false, 22 | 23 | // An array of glob patterns indicating a set of files for which coverage information should be collected 24 | // collectCoverageFrom: null, 25 | 26 | // The directory where Jest should output its coverage files 27 | coverageDirectory: "coverage", 28 | 29 | // An array of regexp pattern strings used to skip coverage collection 30 | // coveragePathIgnorePatterns: [ 31 | // "/node_modules/" 32 | // ], 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: null, 44 | 45 | // Make calling deprecated APIs throw helpful error messages 46 | // errorOnDeprecated: false, 47 | 48 | // Force coverage collection from ignored files usin a array of glob patterns 49 | // forceCoverageMatch: [], 50 | 51 | // A path to a module which exports an async function that is triggered once before all test suites 52 | // globalSetup: null, 53 | 54 | // A path to a module which exports an async function that is triggered once after all test suites 55 | // globalTeardown: null, 56 | 57 | // A set of global variables that need to be available in all test environments 58 | // globals: {}, 59 | 60 | // An array of directory names to be searched recursively up from the requiring module's location 61 | // moduleDirectories: [ 62 | // "node_modules" 63 | // ], 64 | 65 | // An array of file extensions your modules use 66 | // moduleFileExtensions: [ 67 | // "js", 68 | // "json", 69 | // "jsx", 70 | // "node" 71 | // ], 72 | 73 | // A map from regular expressions to module names that allow to stub out resources with a single module 74 | // moduleNameMapper: {}, 75 | 76 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 77 | // modulePathIgnorePatterns: [], 78 | 79 | // Activates notifications for test results 80 | // notify: false, 81 | 82 | // An enum that specifies notification mode. Requires { notify: true } 83 | // notifyMode: "always", 84 | 85 | // A preset that is used as a base for Jest's configuration 86 | // preset: null, 87 | 88 | // Run tests from one or more projects 89 | // projects: null, 90 | 91 | // Use this configuration option to add custom reporters to Jest 92 | // reporters: undefined, 93 | 94 | // Automatically reset mock state between every test 95 | // resetMocks: false, 96 | 97 | // Reset the module registry before running each individual test 98 | // resetModules: false, 99 | 100 | // A path to a custom resolver 101 | // resolver: null, 102 | 103 | // Automatically restore mock state between every test 104 | // restoreMocks: false, 105 | 106 | // The root directory that Jest should scan for tests and modules within 107 | // rootDir: null, 108 | 109 | // A list of paths to directories that Jest should use to search for files in 110 | // roots: [ 111 | // "" 112 | // ], 113 | 114 | // Allows you to use a custom runner instead of Jest's default test runner 115 | // runner: "jest-runner", 116 | 117 | // The paths to modules that run some code to configure or set up the testing environment before each test 118 | // setupFiles: [], 119 | 120 | // The path to a module that runs some code to configure or set up the testing framework before each test 121 | // setupTestFrameworkScriptFile: null, 122 | 123 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 124 | // snapshotSerializers: [], 125 | 126 | // The test environment that will be used for testing 127 | testEnvironment: "node", 128 | 129 | // Options that will be passed to the testEnvironment 130 | // testEnvironmentOptions: {}, 131 | 132 | // Adds a location field to test results 133 | // testLocationInResults: false, 134 | 135 | // The glob patterns Jest uses to detect test files 136 | // testMatch: [ 137 | // "**/__tests__/**/*.js?(x)", 138 | // "**/?(*.)+(spec|test).js?(x)" 139 | // ], 140 | 141 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 142 | // testPathIgnorePatterns: [ 143 | // "/node_modules/" 144 | // ], 145 | 146 | // The regexp pattern Jest uses to detect test files 147 | // testRegex: "", 148 | 149 | // This option allows the use of a custom results processor 150 | // testResultsProcessor: null, 151 | 152 | // This option allows use of a custom test runner 153 | // testRunner: "jasmine2", 154 | 155 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 156 | // testURL: "about:blank", 157 | 158 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 159 | // timers: "real", 160 | 161 | // A map from regular expressions to paths to transformers 162 | // transform: null, 163 | 164 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 165 | // transformIgnorePatterns: [ 166 | // "/node_modules/" 167 | // ], 168 | 169 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 170 | // unmockedModulePathPatterns: undefined, 171 | 172 | // Indicates whether each individual test should be reported during the run 173 | // verbose: null, 174 | 175 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 176 | // watchPathIgnorePatterns: [], 177 | 178 | // Whether to use watchman for file crawling 179 | // watchman: true, 180 | }; 181 | -------------------------------------------------------------------------------- /lib/gulpPurgecss.es.js: -------------------------------------------------------------------------------- 1 | import through from 'through2'; 2 | import PluginError from 'plugin-error'; 3 | import Purgecss from 'purgecss'; 4 | import glob from 'glob'; 5 | 6 | function _toConsumableArray(arr) { 7 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); 8 | } 9 | 10 | function _arrayWithoutHoles(arr) { 11 | if (Array.isArray(arr)) { 12 | for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; 13 | 14 | return arr2; 15 | } 16 | } 17 | 18 | function _iterableToArray(iter) { 19 | if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); 20 | } 21 | 22 | function _nonIterableSpread() { 23 | throw new TypeError("Invalid attempt to spread non-iterable instance"); 24 | } 25 | 26 | var PLUGIN_NAME = 'gulp-purgecss'; 27 | 28 | var getFiles = function getFiles(contentArray) { 29 | return contentArray.reduce(function (acc, content) { 30 | return [].concat(_toConsumableArray(acc), _toConsumableArray(glob.sync(content))); 31 | }, []); 32 | }; 33 | 34 | var gulpPurgecss = function gulpPurgecss(options) { 35 | return through.obj(function (file, encoding, callback) { 36 | var _this = this; 37 | 38 | // empty 39 | if (file.isNull()) return callback(null, file); // buffer 40 | 41 | if (file.isBuffer()) { 42 | try { 43 | var optionsGulp = Object.assign(options, { 44 | content: getFiles(options.content), 45 | css: [{ 46 | raw: file.contents.toString() 47 | }], 48 | stdin: true 49 | }); 50 | var purge = new Purgecss(optionsGulp).purge()[0]; 51 | var result = optionsGulp.rejected ? purge.rejected.join(' {}\n') + ' {}' : purge.css; 52 | file.contents = new Buffer(result); 53 | callback(null, file); 54 | } catch (e) { 55 | this.emit('error', new PluginError(PLUGIN_NAME, e.message)); 56 | } 57 | } // stream 58 | 59 | 60 | if (file.isStream()) { 61 | var css = ''; 62 | file.on('readable', function (buffer) { 63 | css += buffer.read().toString(); 64 | }).on('end', function () { 65 | try { 66 | var _optionsGulp = Object.assign(options, { 67 | css: [css] 68 | }); 69 | 70 | var _purge = new Purgecss(_optionsGulp).purge()[0]; 71 | 72 | var _result = _optionsGulp.rejected ? _purge.rejected.join(' {}\n') + ' {}' : _purge.css; 73 | 74 | file.contents = new Buffer(_result); 75 | callback(null, file); 76 | } catch (e) { 77 | _this.emit('error', new PluginError(PLUGIN_NAME, e.message)); 78 | } 79 | }); 80 | } 81 | }); 82 | }; 83 | 84 | export default gulpPurgecss; 85 | -------------------------------------------------------------------------------- /lib/gulpPurgecss.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 4 | 5 | var through = _interopDefault(require('through2')); 6 | var PluginError = _interopDefault(require('plugin-error')); 7 | var Purgecss = _interopDefault(require('purgecss')); 8 | var glob = _interopDefault(require('glob')); 9 | 10 | function _toConsumableArray(arr) { 11 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); 12 | } 13 | 14 | function _arrayWithoutHoles(arr) { 15 | if (Array.isArray(arr)) { 16 | for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; 17 | 18 | return arr2; 19 | } 20 | } 21 | 22 | function _iterableToArray(iter) { 23 | if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); 24 | } 25 | 26 | function _nonIterableSpread() { 27 | throw new TypeError("Invalid attempt to spread non-iterable instance"); 28 | } 29 | 30 | var PLUGIN_NAME = 'gulp-purgecss'; 31 | 32 | var getFiles = function getFiles(contentArray) { 33 | return contentArray.reduce(function (acc, content) { 34 | return [].concat(_toConsumableArray(acc), _toConsumableArray(glob.sync(content))); 35 | }, []); 36 | }; 37 | 38 | var gulpPurgecss = function gulpPurgecss(options) { 39 | return through.obj(function (file, encoding, callback) { 40 | var _this = this; 41 | 42 | // empty 43 | if (file.isNull()) return callback(null, file); // buffer 44 | 45 | if (file.isBuffer()) { 46 | try { 47 | var optionsGulp = Object.assign(options, { 48 | content: getFiles(options.content), 49 | css: [{ 50 | raw: file.contents.toString() 51 | }], 52 | stdin: true 53 | }); 54 | var purge = new Purgecss(optionsGulp).purge()[0]; 55 | var result = optionsGulp.rejected ? purge.rejected.join(' {}\n') + ' {}' : purge.css; 56 | file.contents = new Buffer(result); 57 | callback(null, file); 58 | } catch (e) { 59 | this.emit('error', new PluginError(PLUGIN_NAME, e.message)); 60 | } 61 | } // stream 62 | 63 | 64 | if (file.isStream()) { 65 | var css = ''; 66 | file.on('readable', function (buffer) { 67 | css += buffer.read().toString(); 68 | }).on('end', function () { 69 | try { 70 | var _optionsGulp = Object.assign(options, { 71 | css: [css] 72 | }); 73 | 74 | var _purge = new Purgecss(_optionsGulp).purge()[0]; 75 | 76 | var _result = _optionsGulp.rejected ? _purge.rejected.join(' {}\n') + ' {}' : _purge.css; 77 | 78 | file.contents = new Buffer(_result); 79 | callback(null, file); 80 | } catch (e) { 81 | _this.emit('error', new PluginError(PLUGIN_NAME, e.message)); 82 | } 83 | }); 84 | } 85 | }); 86 | }; 87 | 88 | module.exports = gulpPurgecss; 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-purgecss", 3 | "version": "1.2.0", 4 | "description": "Gulp plugin for purgecss", 5 | "main": "./lib/gulpPurgecss.js", 6 | "module": "./lib/gulpPurgecss.es.js", 7 | "jsnext:main": "./lib/gulpPurgecss.es.js", 8 | "directories": { 9 | "lib": "lib", 10 | "test": "__tests__", 11 | "example": "example" 12 | }, 13 | "files": [ 14 | "/lib" 15 | ], 16 | "scripts": { 17 | "test": "npx jest", 18 | "build": "rollup -c rollup.config.js", 19 | "lint": "npx eslint -c .eslintrc src/", 20 | "prettier": "npx prettier --write --print-width 100 --tab-width 2 --no-semi --single-quote src/index.js" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/FullHuman/gulp-purgecss.git" 25 | }, 26 | "keywords": [ 27 | "gulpplugin", 28 | "purgecss", 29 | "optimize", 30 | "remove", 31 | "css", 32 | "unused" 33 | ], 34 | "author": "Ffloriel", 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/FullHuman/gulp-purgecss/issues" 38 | }, 39 | "homepage": "https://github.com/FullHuman/gulp-purgecss#readme", 40 | "devDependencies": { 41 | "@babel/core": "^7.4.4", 42 | "@babel/preset-env": "^7.4.4", 43 | "babel-core": "^7.0.0-bridge.0", 44 | "babel-jest": "^24.7.1", 45 | "eslint": "^6.0.0", 46 | "jest": "^24.7.1", 47 | "prettier": "^1.17.0", 48 | "rollup": "^1.10.1", 49 | "rollup-plugin-babel": "^4.3.2", 50 | "rollup-plugin-commonjs": "^9.3.4", 51 | "rollup-plugin-node-resolve": "^5.0.0", 52 | "vinyl": "^2.2.0" 53 | }, 54 | "dependencies": { 55 | "glob": "^7.1.2", 56 | "plugin-error": "^1.0.1", 57 | "purgecss": "^1.4.1", 58 | "through2": "^2.0.3" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | import commonjs from 'rollup-plugin-commonjs' 3 | import resolve from 'rollup-plugin-node-resolve' 4 | 5 | export default { 6 | input: 'src/index.js', 7 | output: [ 8 | { 9 | file: 'lib/gulpPurgecss.es.js', 10 | format: 'es' 11 | }, 12 | { 13 | file: 'lib/gulpPurgecss.js', 14 | format: 'cjs' 15 | } 16 | ], 17 | plugins: [resolve(), commonjs(), babel()], 18 | external: ['through2', 'plugin-error', 'purgecss', 'glob'] 19 | } 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import through from 'through2' 2 | import PluginError from 'plugin-error' 3 | import Purgecss from 'purgecss' 4 | import glob from 'glob' 5 | 6 | const PLUGIN_NAME = 'gulp-purgecss' 7 | 8 | const getFiles = contentArray => 9 | contentArray.reduce((acc, content) => { 10 | return [...acc, ...glob.sync(content)] 11 | }, []) 12 | 13 | const gulpPurgecss = options => { 14 | return through.obj(function(file, encoding, callback) { 15 | // empty 16 | if (file.isNull()) return callback(null, file) 17 | // buffer 18 | if (file.isBuffer()) { 19 | try { 20 | const optionsGulp = Object.assign(options, { 21 | content: getFiles(options.content), 22 | css: [ 23 | { 24 | raw: file.contents.toString() 25 | } 26 | ], 27 | stdin: true 28 | }) 29 | const purge = new Purgecss(optionsGulp).purge()[0] 30 | const result = optionsGulp.rejected ? purge.rejected.join(' {}\n') + ' {}' : purge.css 31 | file.contents = new Buffer(result) 32 | callback(null, file) 33 | } catch (e) { 34 | this.emit('error', new PluginError(PLUGIN_NAME, e.message)) 35 | } 36 | } 37 | // stream 38 | if (file.isStream()) { 39 | var css = '' 40 | file 41 | .on('readable', buffer => { 42 | css += buffer.read().toString() 43 | }) 44 | .on('end', () => { 45 | try { 46 | const optionsGulp = Object.assign(options, { css: [css] }) 47 | const purge = new Purgecss(optionsGulp).purge()[0] 48 | const result = optionsGulp.rejected ? purge.rejected.join(' {}\n') + ' {}' : purge.css 49 | file.contents = new Buffer(result) 50 | callback(null, file) 51 | } catch (e) { 52 | this.emit('error', new PluginError(PLUGIN_NAME, e.message)) 53 | } 54 | }) 55 | } 56 | }) 57 | } 58 | 59 | export default gulpPurgecss 60 | --------------------------------------------------------------------------------