├── .gitattributes ├── .gitignore ├── .jshintrc ├── .editorconfig ├── index.js ├── cli.js ├── package.json ├── README.md ├── .jscsrc └── lib └── engine.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "immed": true, 8 | "newcap": true, 9 | "noarg": true, 10 | "undef": true, 11 | "unused": "vars", 12 | "strict": true, 13 | "predef": [ 14 | "-Promise" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | indent_size = 2 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * keepfast-cli 3 | * 4 | * Copyright (c) 2016 Davidson Fellipe, contributors 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var engine = require('./lib/engine'); 11 | 12 | var tests = { 13 | plugins: [ 14 | 'keepfast-contrib-psi', 15 | 'keepfast-contrib-phantomas' 16 | ], 17 | profiles: [] 18 | }; 19 | 20 | // engine.runURL(tests, 'http://fellipe.com/'); 21 | // engine.runURL(tests, 'https://loadsmart.com'); 22 | engine.runURL(tests, 'https://github.com/'); 23 | // engine.runURL(tests, 'http://globoesporte.globo.com/'); 24 | // engine.runURL(tests, 'http://julianamalta.com/'); 25 | // engine.runURL(tests, 'http://www.nytimes.com/'); 26 | // engine.runURL(tests, 'https://www.google.com/'); 27 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | /* 2 | * keepfast-cli 3 | * 4 | * Copyright (c) 2016 Davidson Fellipe, contributors 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var meow = require('meow'); 11 | var updateNotifier = require('update-notifier'); 12 | var Promise = require('bluebird'); 13 | var engine = require('./lib/engine'); 14 | 15 | var cli = meow([ 16 | 'Usage', 17 | ' $ keepfast ', 18 | '', 19 | 'Example', 20 | ' $ keepfast fellipe.com', 21 | '' 22 | ]); 23 | 24 | updateNotifier({ 25 | pkg: cli.pkg 26 | }).notify(); 27 | 28 | if (!cli.input[0]) { 29 | throw new Error('Please supply a valid URL'); 30 | } 31 | 32 | var tests = { 33 | plugins: [ 34 | 'keepfast-contrib-phantomas', 35 | 'keepfast-contrib-psi' 36 | ], 37 | profiles: [] 38 | }; 39 | 40 | engine.runURL(tests, cli.input[0]); 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keepfast-cli", 3 | "version": "0.0.13", 4 | "description": "", 5 | "author": { 6 | "name": "Davidson Fellipe", 7 | "email": "davidsonfellipe@gmail.com", 8 | "url": "fellipe.com" 9 | }, 10 | "bin": { 11 | "keepfast": "cli.js" 12 | }, 13 | "preferGlobal": true, 14 | "engines": { 15 | "node": ">=0.10.0" 16 | }, 17 | "repository": "keepfast/keepfast-cli", 18 | "scripts": { 19 | "test": "" 20 | }, 21 | "files": [ 22 | "cli.js", 23 | "index.js", 24 | "lib" 25 | ], 26 | "keywords": [ 27 | "speed", 28 | "wpo", 29 | "keepfast", 30 | "insights", 31 | "performance", 32 | "measure", 33 | "optimize", 34 | "website", 35 | "pagespeed" 36 | ], 37 | "dependencies": { 38 | "bluebird": "^3.2.1", 39 | "chalk": "^1.1.1", 40 | "httpinvoke": "^1.4.0", 41 | "keepfast-contrib-phantomas": "^1.0.9", 42 | "keepfast-contrib-psi": "^1.0.7", 43 | "keepfast-contrib-yslow": "0.0.3", 44 | "meow": "^3.7.0", 45 | "update-notifier": "^0.5.0" 46 | }, 47 | "devDependencies": { 48 | "jshint": "^2.9.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keepfast CLI 👾 2 | 3 | Keepfast in your command line with reporting, this tool helps front-end developers to keep a good performance scores. 4 | 5 | 6 | 7 | [![npm version](https://badge.fury.io/js/keepfast-cli.svg)](https://badge.fury.io/js/keepfast-cli) 8 | [![npm](https://img.shields.io/npm/dt/keepfast-cli.svg)](https://www.npmjs.com/package/keepfast-cli) 9 | [![npm version](https://david-dm.org/keepfast/keepfast-cli.svg)](https://david-dm.org/keepfast/keepfast-cli.svg) 10 | [![Code Climate](https://codeclimate.com/github/keepfast/keepfast-cli/badges/gpa.svg)](https://codeclimate.com/github/keepfast/keepfast-cli) 11 | [![MIT license](https://img.shields.io/github/license/mashape/apistatus.svg?style=flat)](https://davidsonfellipe.mit-license.org/) 12 | 13 | ## Install 💾 14 | ``` 15 | $ npm i keepfast-cli -g 16 | ``` 17 | 18 | ## Run 🚀 19 | ```shell 20 | $ keepfast https://www.fellipe.com/ 21 | ``` 22 | ![](https://cloud.githubusercontent.com/assets/381179/15797115/a5328a6a-29db-11e6-8fbf-948555f554d9.png) 23 | 24 | 25 | ## Dependencies ✔︎ 26 | 27 | [![NPM](https://nodei.co/npm/keepfast-cli.png)](https://npmjs.org/package/keepfast-cli) 28 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fkeepfast%2Fkeepfast-cli.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fkeepfast%2Fkeepfast-cli?ref=badge_shield) 29 | 30 | 31 | ## Contributing 👣 32 | 33 | 1. Fork it! 34 | 2. Create your feature branch: `git checkout -b my-awesome-new-feature` 35 | 3. Commit your changes: `git commit -m 'Add some awesome feature'` 36 | 4. Push to the branch: `git push origin my-awesome-new-feature` 37 | 5. Submit a pull request :] 38 | 39 | 40 | ## License 📖 41 | 42 | Code is under [MIT](http://davidsonfellipe.mit-license.org) license 43 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "excludeFiles": ["node_modules/**", "bower_components/**"], 3 | "safeContextKeyword": "self", 4 | "requireCurlyBraces": [ 5 | "if", 6 | "else", 7 | "for", 8 | "while", 9 | "do", 10 | "try", 11 | "catch" 12 | ], 13 | "requireOperatorBeforeLineBreak": true, 14 | "requireCamelCaseOrUpperCaseIdentifiers": true, 15 | "maximumLineLength": { 16 | "value": 120, 17 | "allowComments": true, 18 | "allowRegex": true 19 | }, 20 | "validateIndentation": 2, 21 | "validateQuoteMarks": "'", 22 | 23 | "disallowMultipleLineStrings": true, 24 | "disallowMixedSpacesAndTabs": true, 25 | "disallowTrailingWhitespace": true, 26 | "disallowSpaceAfterPrefixUnaryOperators": true, 27 | "disallowMultipleVarDecl": null, 28 | 29 | "requireSpaceAfterKeywords": [ 30 | "if", 31 | "else", 32 | "for", 33 | "while", 34 | "do", 35 | "switch", 36 | "return", 37 | "try", 38 | "catch" 39 | ], 40 | "requireSpaceBeforeBinaryOperators": [ 41 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", 42 | "&=", "|=", "^=", "+=", 43 | 44 | "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", 45 | "|", "^", "&&", "||", "===", "==", ">=", 46 | "<=", "<", ">", "!=", "!==" 47 | ], 48 | "requireSpaceAfterBinaryOperators": true, 49 | "requireSpacesInConditionalExpression": true, 50 | "requireSpaceBeforeBlockStatements": true, 51 | "requireLineFeedAtFileEnd": true, 52 | "disallowSpacesInsideObjectBrackets": "all", 53 | "disallowSpacesInsideArrayBrackets": "all", 54 | "disallowSpacesInsideParentheses": true, 55 | 56 | "jsDoc": { 57 | "checkAnnotations": true, 58 | "checkParamNames": true, 59 | "requireParamTypes": true, 60 | "checkReturnTypes": true, 61 | "checkTypes": true 62 | }, 63 | 64 | "disallowMultipleLineBreaks": true, 65 | "disallowCommaBeforeLineBreak": null, 66 | "disallowDanglingUnderscores": null, 67 | "disallowEmptyBlocks": null, 68 | "disallowTrailingComma": null, 69 | "requireCommaBeforeLineBreak": null, 70 | "requireDotNotation": null, 71 | "requireMultipleVarDecl": null, 72 | "requireParenthesesAroundIIFE": true 73 | } 74 | -------------------------------------------------------------------------------- /lib/engine.js: -------------------------------------------------------------------------------- 1 | /* 2 | * keepfast-cli 3 | * 4 | * Copyright (c) 2016 Davidson Fellipe, contributors 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | // var Promise = require('bluebird'); 11 | var chalk = require('chalk'); 12 | var httpinvoke = require('httpinvoke'); 13 | 14 | var getScore = function(value, total, reference, condition, unit) { 15 | if (!total) { 16 | return '(should be ' + condition + ' ' + reference + unit + ')'; 17 | } else { 18 | var percentual = Math.round(((value / total) * 100)) - 100; 19 | return '🏁 ' + (percentual > 0 ? '+' : '') + percentual + '% of average'; 20 | } 21 | }; 22 | 23 | // var getReference = function(name, plugins) { 24 | // for (var i = 0; i < plugins.length; i++) { 25 | // if (plugins[i].plugin === name) { 26 | // return plugins[i].sensors; 27 | // } 28 | // } 29 | // 30 | // return {}; 31 | // }; 32 | 33 | var compare = function(a, b, operator, result) { 34 | a = parseInt(a, 10); 35 | b = parseInt(b, 10); 36 | 37 | if (operator === '<') { 38 | return a < b; 39 | } else if (operator === '>') { 40 | return a > b; 41 | } else if (operator === '=') { 42 | return a === b; 43 | } 44 | }; 45 | 46 | var getExpectedMessage = function(current, reference) { 47 | var msg = ''; 48 | 49 | if (current && reference) { 50 | var percentual = Math.round(((current / reference) - 1) * 100); 51 | 52 | percentual = (percentual > 0) ? '+' + percentual : percentual; 53 | 54 | msg = '(' + percentual + '% better than average)'; 55 | } 56 | 57 | return msg; 58 | }; 59 | 60 | var getMessage = function(current, reference) { 61 | var criteria = reference.criteria[0]; 62 | 63 | if (!compare(current, criteria.expected, criteria.condition, criteria.result)) { 64 | return { 65 | type: 'warning', 66 | text:( 67 | chalk.white(' 🐢 ' + reference.label + ':') + ' ' + 68 | chalk.green(current + ' ' + criteria.unit) + ' ' + 69 | chalk.gray(getScore(current, reference.desktop, criteria.expected, criteria.condition, criteria.unit)) 70 | )}; 71 | } else { 72 | return { 73 | type: 'success', 74 | text:( 75 | chalk.white(' 😀 ' + reference.label + ':') + ' ' + 76 | chalk.green(current + ' ' + criteria.unit) + ' ' + 77 | chalk.gray(getExpectedMessage(current, criteria.expected)) 78 | )}; 79 | } 80 | }; 81 | 82 | var print = function(msgs) { 83 | for (var i = 0; i < msgs.length; i++) { 84 | console.log(msgs[i].text); 85 | } 86 | }; 87 | 88 | var evaluating = function(result, reference) { 89 | var warnings = []; 90 | var success = []; 91 | var msg; 92 | 93 | for (var item in reference) { 94 | if (reference.hasOwnProperty(item)) { 95 | msg = getMessage(result[item], reference[item]); 96 | 97 | if (msg.type === 'warning') { 98 | warnings.push(msg); 99 | } else { 100 | success.push(msg); 101 | } 102 | } 103 | } 104 | 105 | if (warnings.length) { 106 | console.log(' ⚠️ ', 107 | chalk.yellow('Warnings'), 108 | chalk.gray('(average is related to alexa top 1000 sites)', '\n') 109 | ); 110 | 111 | print(warnings); 112 | console.log(''); 113 | } 114 | 115 | if (success.length === Object.keys(reference).length) { 116 | console.log(' 👍 ', 117 | chalk.green('All right, congratulations!', '\n') 118 | ); 119 | } else { 120 | console.log(' 👍 ', 121 | chalk.green('Already done!', '\n') 122 | ); 123 | } 124 | 125 | print(success); 126 | }; 127 | 128 | var getHeaders = function(url) { 129 | httpinvoke(url, 'GET', function(err, body, statusCode, headers) { 130 | if (err) { 131 | console.log('Failure', err); 132 | } 133 | 134 | console.log(chalk.white('\n📡 Request:', url ,'\n')); 135 | console.log(chalk.white(' status code:'), chalk.green(statusCode)); 136 | for (var item in headers) { 137 | if (headers.hasOwnProperty(item)) { 138 | console.log(' ', item + ':', chalk.green(headers[item])); 139 | } 140 | } 141 | console.log(''); 142 | }); 143 | }; 144 | 145 | exports.runURL = function(tests, url) { 146 | var plugin = {}; 147 | var pluginName = ''; 148 | 149 | getHeaders(url); 150 | 151 | for (var i = 0; i < tests.plugins.length; i++) { 152 | pluginName = tests.plugins[i]; 153 | plugin[pluginName] = require(pluginName); 154 | 155 | plugin[pluginName] 156 | .output(url) 157 | .then(function(result) { 158 | console.log(chalk.gray('------------------------------------------------------------------------')); 159 | console.log(chalk.white('\n🚀 Plugin:', result.plugin, '🌎 URL:', url, '\n')); 160 | evaluating(result.sensors, plugin[result.plugin].getReference().sensors); 161 | console.log(' '); 162 | }); 163 | } 164 | }; 165 | // 166 | // exports.run = function(tests) { 167 | // return new Promise(function (resolve, reject) { 168 | // resolve() 169 | // }).then(function() { 170 | // for (var item in tests.profiles) { 171 | // var pluginName = tests.profiles[item].plugins[0]; 172 | // var plugin = Promise.promisifyAll(require(pluginName)); 173 | // 174 | // plugin 175 | // .extract(tests.profiles[item].domain.url) 176 | // .then(function(result) { 177 | // evaluating(result, getReference(pluginName, tests.plugins)); 178 | // }); 179 | // } 180 | // }); 181 | // } 182 | --------------------------------------------------------------------------------