├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── bin ├── cli ├── cli.js ├── fileTypes.js └── fixme.js ├── index.js ├── package.json ├── test └── annotation_test.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 John Postlethwait 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fixme 2 | 3 | _**NOTE:** I no longer actively maintain this package. I'd love to get PRs to keep it going though!_ 4 | 5 | ![Fixme](https://nodei.co/npm/fixme.png "Fixme on NPM") 6 | 7 | Scan for NOTE, OPTIMIZE, TODO, HACK, XXX, FIXME, and BUG comments within your source, and print them to stdout so you can deal with them. This is similar to the `rake notes` task from Rails. 8 | 9 | It ends up giving you an output like this: 10 | 11 | ![](http://i.imgur.com/OXsTtCZ.png) 12 | 13 | The color formatting is currently done using the excellent terminal coloring library [chalk](https://www.npmjs.org/package/chalk). 14 | 15 | _Fixme currently scans your matching files line-by-line looking for annotations in the code. As such; multi-line annotation capturing is currently not supported. All annotations must be on the same line._ 16 | 17 | 18 | 19 | ## Supported Filetypes 20 | 21 | Fixme supports scanning for annotations in a variety of file types. The default file patterns it looks for are: 22 | 23 | - **JavaScript**: `**/*.js` 24 | - **Makefile**: `Makefile` 25 | - **Shell scripts**: `**/*.sh` 26 | - **HTML**: `**/*.html` 27 | - **CSS**: `**/*.css` 28 | - **JSON**: `**/*.json` 29 | - **Markdown**: `**/*.md` 30 | - **Text files**: `**/*.txt` 31 | - **YAML**: `**/*.yaml`, `**/*.yml` 32 | - **SASS**: `**/*.scss` 33 | - **LESS**: `**/*.less` 34 | - **PHP**: `**/*.php` 35 | - **Java**: `**/*.java` 36 | - **Python**: `**/*.py` 37 | - **Ruby**: `**/*.rb` 38 | - **C++**: `**/*.cpp` 39 | - **C#**: `**/*.cs` 40 | - **Swift**: `**/*.swift` 41 | - **Rust**: `**/*.rs` 42 | - **Go**: `**/*.go` 43 | - **Header files**: `**/*.h`, `**/*.hpp` 44 | - **TypeScript**: `**/*.ts` 45 | - **JSX**: `**/*.jsx` 46 | - **TSX**: `**/*.tsx` 47 | - **Vue.js**: `**/*.vue` 48 | - **Perl**: `**/*.pl` 49 | - **Lua**: `**/*.lua` 50 | - **Groovy**: `**/*.groovy` 51 | - **Scala**: `**/*.scala` 52 | - **Kotlin**: `**/*.kt` 53 | - **Dart**: `**/*.dart` 54 | 55 | ## Usage 56 | 57 | In order to use Fixme all you need to do is install it: 58 | 59 | > npm install -g fixme 60 | 61 | _Note: There really shouldn't be much reason to globally install it..._ 62 | 63 | Require it: 64 | 65 | ```javascript 66 | var fixme = require("fixme"); 67 | ``` 68 | 69 | And finally; configure it when you call it: 70 | 71 | ```javascript 72 | // All values below are Fixme default values unless otherwise overridden here. 73 | fixme({ 74 | path: process.cwd(), 75 | ignored_directories: ["node_modules/**", ".git/**", ".hg/**"], 76 | file_patterns: ["**/*.js", "Makefile", "**/*.sh"], 77 | file_encoding: "utf8", 78 | line_length_limit: 1000, 79 | skip: [], 80 | }); 81 | ``` 82 | 83 | You should then see some nice output when this is run: 84 | 85 | ``` 86 | • path/to/your/directory/file.js [4 messages]: 87 | [Line 1] ✐ NOTE: This is here because sometimes an intermittent issue appears. 88 | [Line 7] ↻ OPTIMIZE: This could be reworked to not do a O(N2) lookup. 89 | [Line 9] ✓ TODO from John: Add a check here to ensure these are always strings. 90 | [Line 24] ✄ HACK: I am doing something here that is horrible, but it works for now... 91 | [Line 89] ✗ XXX: Let's do this better next time? It's bad. 92 | [Line 136] ☠ FIXME: We sometimes get an undefined index in this array. 93 | [Line 211] ☢ BUG: If the user inputs "Easter" we always output "Egg", even if they wanted a "Bunny". 94 | ``` 95 | 96 | ### Configure Options (In More Detail) 97 | 98 | - **path:** The path to scan through for notes, defaults to process.cwd() 99 | - **ignored_directories:** Glob patterns for directories to ignore. Passes these straight to [minimatch](https://www.npmjs.org/package/minimatch) so check there for more information on proper syntax. 100 | - **file_patterns:** Glob patterns for files to scan. Also uses [minimatch](https://www.npmjs.org/package/minimatch). 101 | - **file_encoding:** The encoding the files scanned will be opened as. 102 | - **line_length_limit:** The number of max characters a line can be before Fixme gives up and doen not scan it for matches. If a line is too long, the regular expression will take an extremely long time to finish. _You have been warned!_ 103 | - **skip:** List of check names to skip. Valid options: `note`, `optimize`, `todo`, `hack`, `xxx`, `fixme`, `bug`, `line`. `line` will disable the line length warning. 104 | 105 | ### CLI Usage 106 | 107 | ```sh 108 | fixme --help 109 | ``` 110 | 111 | ### Using With [GulpJS](http://gulpjs.com/) 112 | 113 | Using this as a GulpJS task is pretty simple, here is a very straight-forward "notes" task: 114 | 115 | ```javascript 116 | gulp.task("notes", fixme); 117 | ``` 118 | 119 | That, of course, assumes all of the defaults in Fixme are ok with you. If not, this is still pretty simple to configure and run as a Gulp task: 120 | 121 | ```javascript 122 | gulp.task("notes", function () { 123 | fixme({ 124 | path: process.cwd(), 125 | ignored_directories: ["node_modules/**", ".git/**", ".hg/**"], 126 | file_patterns: ["**/*.js", "Makefile", "**/*.sh"], 127 | file_encoding: "utf8", 128 | line_length_limit: 1000, 129 | }); 130 | }); 131 | ``` 132 | 133 | ### Writing Comments for Use With Fixme 134 | 135 | A code annotation needs to follow these rules to be picked up by Fixme: 136 | 137 | - Can be preceeded by 0 to n number of characters, this includes the comment characters //, /\*, ", "#}}", "*/", "--}}", "}}", "#}"], 139 | "", 140 | ); 141 | 142 | Object.keys(messageChecks).forEach(function (checkName) { 143 | var matchResults = lineString.match(messageChecks[checkName].regex), 144 | checker = messageChecks[checkName], 145 | thisMessage; 146 | 147 | if (matchResults && matchResults.length) { 148 | thisMessage = JSON.parse(JSON.stringify(messageFormat)); // Clone the above structure. 149 | 150 | thisMessage.label = checker.label; 151 | thisMessage.colorer = checker.colorer; 152 | 153 | if (matchResults[3] && matchResults[3].length) { 154 | thisMessage.author = matchResults[3].trim(); 155 | } 156 | 157 | if (matchResults[4] && matchResults[4].length) { 158 | thisMessage.message = matchResults[4].trim(); 159 | } 160 | } 161 | 162 | if (thisMessage) messages.push(thisMessage); 163 | }); 164 | 165 | return messages; 166 | } 167 | 168 | /** 169 | * Removes the end of html, twig and handlebar comments. EG: -->, --}}, etc. 170 | * 171 | * @param {String} str 172 | * @param {Array} find 173 | * @param {String} replace 174 | * 175 | * @return {String} 176 | */ 177 | function removeCommentEnd(str, find, replace) { 178 | var replaceString = str; 179 | for (var i = 0; i < find.length; i++) { 180 | replaceString = replaceString.replace(find[i], replace); 181 | } 182 | return replaceString; 183 | } 184 | 185 | /** 186 | * Takes a line number and returns a padded string matching the total number of 187 | * characters in totalLinesNumber. EG: A lineNumber of 12 and a 188 | * totalLinesNumber of 1323 will return the string ' 12'. 189 | * 190 | * @param {Number} lineNumber 191 | * @param {Number} totalLinesNumber 192 | * 193 | * @return {String} 194 | */ 195 | function getPaddedLineNumber(lineNumber, totalLinesNumber) { 196 | var paddedLineNumberString = "" + lineNumber; 197 | 198 | while (paddedLineNumberString.length < ("" + totalLinesNumber).length) { 199 | paddedLineNumberString = " " + paddedLineNumberString; 200 | } 201 | 202 | return paddedLineNumberString; 203 | } 204 | 205 | /** 206 | * Takes an individual message object, as output from retrieveMessagesFromLine 207 | * and formats it for output. 208 | * 209 | * @param {Object} individualMessage 210 | * @property {String} individualMessage.author 211 | * @property {String} individualMessage.message 212 | * @property {String} individualMessage.label 213 | * @property {Function} individualMessage.colorer 214 | * @property {Number} individualMessage.line_number 215 | * @param {Number} totalNumberOfLines 216 | * 217 | * @return {String} The formatted message string. 218 | */ 219 | function formatMessageOutput(individualMessage, totalNumberOfLines) { 220 | var paddedLineNumber = getPaddedLineNumber( 221 | individualMessage.line_number, 222 | totalNumberOfLines, 223 | ), 224 | finalLabelString, 225 | finalNoteString; 226 | 227 | finalNoteString = chalk.gray(" [Line " + paddedLineNumber + "] "); 228 | 229 | finalLabelString = individualMessage.label; 230 | 231 | if (individualMessage.author) { 232 | finalLabelString += " from " + individualMessage.author + ": "; 233 | } else { 234 | finalLabelString += ": "; 235 | } 236 | 237 | finalLabelString = chalk.bold(individualMessage.colorer(finalLabelString)); 238 | 239 | finalNoteString += finalLabelString; 240 | 241 | if (individualMessage.message && individualMessage.message.length) { 242 | finalNoteString += individualMessage.colorer(individualMessage.message); 243 | } else { 244 | finalNoteString += chalk.grey("[[no message to display]]"); 245 | } 246 | 247 | return finalNoteString; 248 | } 249 | 250 | /** 251 | * Formatter function for the file name. Takes a file path, and the total 252 | * number of messages in the file, and formats this information for display as 253 | * the heading for the file messages. 254 | * 255 | * @param {String} filePath 256 | * @param {Number} numberOfMessages 257 | * 258 | * @return {String} 259 | */ 260 | function formatFilePathOutput(filePath, numberOfMessages) { 261 | var filePathOutput = chalk.bold.white("\n* " + filePath + " "), 262 | messagesString = "messages"; 263 | 264 | if (numberOfMessages === 1) { 265 | messagesString = "message"; 266 | } 267 | 268 | filePathOutput += chalk.grey( 269 | "[" + numberOfMessages + " " + messagesString + "]:", 270 | ); 271 | 272 | return filePathOutput; 273 | } 274 | 275 | /** 276 | * Takes an object representing the messages and other meta-info for the file 277 | * and calls off to the formatters for the messages, as well as logs the 278 | * formatted result. 279 | * 280 | * @param {Object} messagesInfo 281 | * @property {String} messagesInfo.path The file path 282 | * @property {Array} messagesInfo.messages All of the message objects for the file. 283 | * @property {String} messagesInfo.total_lines Total number of lines in the file. 284 | */ 285 | function logMessages(messagesInfo) { 286 | if (messagesInfo.messages.length) { 287 | console.log( 288 | formatFilePathOutput(messagesInfo.path, messagesInfo.messages.length), 289 | ); 290 | 291 | messagesInfo.messages.forEach(function (message) { 292 | var formattedMessage = formatMessageOutput( 293 | message, 294 | messagesInfo.total_lines, 295 | ); 296 | 297 | console.log(formattedMessage); 298 | }); 299 | } 300 | } 301 | 302 | /** 303 | * Reads through the configured path scans the matching files for messages. 304 | */ 305 | function scanAndProcessMessages() { 306 | var stream = readdirp(scanPath, { 307 | fileFilter: fileFilterer, 308 | }); 309 | 310 | // Remove skipped checks for our mapping 311 | skipChecks.forEach(function (checkName) { 312 | delete messageChecks[checkName]; 313 | }); 314 | 315 | // TODO: Actually do something meaningful/useful with these handlers. 316 | stream.on("warn", console.warn).on("error", console.error); 317 | 318 | stream.pipe( 319 | eventStream.map(function (fileInformation, callback) { 320 | var input = fs.createReadStream(fileInformation.fullPath, { 321 | encoding: fileEncoding, 322 | }), 323 | // lineStream = byline.createStream(input, { encoding: fileEncoding }), 324 | fileMessages = { path: null, total_lines: 0, messages: [] }, 325 | currentFileLineNumber = 1; 326 | 327 | fileMessages.path = fileInformation.path; 328 | 329 | input.pipe(eventStream.split()).pipe( 330 | eventStream.map(function (fileLineString, cb) { 331 | var messages, lengthError; 332 | 333 | if (fileLineString.length < lineLengthLimit) { 334 | messages = retrieveMessagesFromLine( 335 | fileLineString, 336 | currentFileLineNumber, 337 | ); 338 | 339 | messages.forEach(function (message) { 340 | fileMessages.messages.push(message); 341 | }); 342 | } else if (skipChecks.indexOf("line") === -1) { 343 | lengthError = 344 | "Fixme is skipping this line because its length is " + 345 | "greater than the maximum line-length of " + 346 | lineLengthLimit + 347 | "."; 348 | 349 | fileMessages.messages.push({ 350 | message: lengthError, 351 | line_number: currentFileLineNumber, 352 | label: " ⚠ SKIPPING CHECK", 353 | colorer: chalk.underline.red, 354 | }); 355 | } 356 | 357 | currentFileLineNumber += 1; 358 | }), 359 | ); 360 | 361 | input.on("end", function () { 362 | fileMessages.total_lines = currentFileLineNumber; 363 | 364 | logMessages(fileMessages); 365 | }); 366 | 367 | callback(); 368 | }), 369 | ); 370 | } 371 | 372 | /** 373 | * Takes an options object and over-writes the defaults, then calls off to the 374 | * scanner to scan the files for messages. 375 | * 376 | * @param {Object} options 377 | * @property {String} options.path The base directory to recursively scan for messages. Defaults to process.cwd() 378 | * @property {Array} options.ignored_directories An array of minimatch glob patterns for directories to ignore scanning entirely. 379 | * @property {Array} options.file_patterns An array of minimatch glob patterns for files to scan for messages. 380 | * @property {String} options.file_encoding The encoding the files scanned will be opened with, defaults to 'utf8'. 381 | * @property {Number} options.line_length_limit The number of characters a line can be before it is ignored. Defaults to 1000. 382 | * @property {Array} options.skip An array of names of checks to skip. 383 | */ 384 | // TODO(johnp): Allow custom messageChecks to be added via options. 385 | function parseUserOptionsAndScan(options) { 386 | if (options) { 387 | if (options.path) { 388 | scanPath = options.path; 389 | } 390 | 391 | if ( 392 | options.ignored_directories && 393 | Array.isArray(options.ignored_directories) && 394 | options.ignored_directories.length 395 | ) { 396 | ignoredDirectories = options.ignored_directories; 397 | } 398 | 399 | if ( 400 | options.file_patterns && 401 | Array.isArray(options.file_patterns) && 402 | options.file_patterns.length 403 | ) { 404 | filesToScan = options.file_patterns; 405 | } 406 | 407 | if (options.file_encoding) { 408 | fileEncoding = options.file_encoding; 409 | } 410 | 411 | if (options.line_length_limit) { 412 | lineLengthLimit = options.line_length_limit; 413 | } 414 | 415 | if (options.skip && Array.isArray(options.skip) && options.skip.length) { 416 | skipChecks = options.skip; 417 | } 418 | } 419 | 420 | scanAndProcessMessages(); 421 | } 422 | 423 | module.exports = parseUserOptionsAndScan; 424 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./bin/fixme'); 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixme", 3 | "description": "Scan for NOTE, OPTIMIZE, TODO, HACK, XXX, FIXME, and BUG comments within your source, and print them to stdout so you can deal with them.", 4 | "version": "0.6.0", 5 | "author": "John Postlethwait ", 6 | "website": "http://johnpostlethwait.github.io/fixme", 7 | "contributors": [], 8 | "keywords": [ 9 | "notes", 10 | "hack", 11 | "todo", 12 | "fixme", 13 | "annotate", 14 | "optimize" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/JohnPostlethwait/fixme.git" 19 | }, 20 | "main": "./index.js", 21 | "scripts": { 22 | "test": "node test/annotation_test.js" 23 | }, 24 | "bin": { 25 | "fixme": "bin/cli" 26 | }, 27 | "engines": { 28 | "node": ">= v0.10.26" 29 | }, 30 | "licenses": [ 31 | { 32 | "name": "MIT", 33 | "url": "http://opensource.org/licenses/mit-license.php" 34 | } 35 | ], 36 | "dependencies": { 37 | "byline": "5.0.0", 38 | "chalk": "4.0.0", 39 | "event-stream": "4.0.1", 40 | "isbinaryfile": "4.0.6", 41 | "minimatch": "3.0.5", 42 | "minimist": "1.2.6", 43 | "readdirp": "3.4.0" 44 | }, 45 | "devDependencies": {} 46 | } 47 | -------------------------------------------------------------------------------- /test/annotation_test.js: -------------------------------------------------------------------------------- 1 | // NOTE: This is the sample output for a note! 2 | // OPTIMIZE (John Postlethwait): This is the sample output for an optimize with an author! 3 | // TODO: This is the sample output for a todo! 4 | // HACK: This is the sample output for a hack! Don't commit hacks! 5 | // XXX: This is the sample output for a XXX! XXX's need attention too! 6 | // FIXME (John Postlethwait): This is the sample output for a fixme! Seriously fix this... 7 | // BUG: This is the sample output for a bug! Who checked in a bug?! 8 | /* BUG: This is the sample output for a bug! Who checked in a bug?! */ 9 | 10 | // The next line is just a URL 11 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1399196 12 | 13 | require('../bin/fixme')({ // NOTE: It doesn't need to be placed at line start! 14 | path: process.cwd(), 15 | ignored_directories: ['node_modules/**', '.git/**'], 16 | file_patterns: ['**/annotation_test.js'], 17 | file_encoding: 'utf8', 18 | line_length_limit: 1000 19 | }); 20 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/color-name@^1.1.1": 6 | version "1.1.1" 7 | resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" 8 | integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== 9 | 10 | ansi-styles@^4.1.0: 11 | version "4.2.1" 12 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" 13 | integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== 14 | dependencies: 15 | "@types/color-name" "^1.1.1" 16 | color-convert "^2.0.1" 17 | 18 | balanced-match@^1.0.0: 19 | version "1.0.0" 20 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 21 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 22 | 23 | brace-expansion@^1.1.7: 24 | version "1.1.11" 25 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 26 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 27 | dependencies: 28 | balanced-match "^1.0.0" 29 | concat-map "0.0.1" 30 | 31 | byline@5.0.0: 32 | version "5.0.0" 33 | resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" 34 | integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= 35 | 36 | chalk@4.0.0: 37 | version "4.0.0" 38 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" 39 | integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== 40 | dependencies: 41 | ansi-styles "^4.1.0" 42 | supports-color "^7.1.0" 43 | 44 | color-convert@^2.0.1: 45 | version "2.0.1" 46 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 47 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 48 | dependencies: 49 | color-name "~1.1.4" 50 | 51 | color-name@~1.1.4: 52 | version "1.1.4" 53 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 54 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 55 | 56 | concat-map@0.0.1: 57 | version "0.0.1" 58 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 59 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 60 | 61 | duplexer@^0.1.1, duplexer@~0.1.1: 62 | version "0.1.1" 63 | resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" 64 | integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= 65 | 66 | event-stream@4.0.1: 67 | version "4.0.1" 68 | resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-4.0.1.tgz#4092808ec995d0dd75ea4580c1df6a74db2cde65" 69 | integrity sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA== 70 | dependencies: 71 | duplexer "^0.1.1" 72 | from "^0.1.7" 73 | map-stream "0.0.7" 74 | pause-stream "^0.0.11" 75 | split "^1.0.1" 76 | stream-combiner "^0.2.2" 77 | through "^2.3.8" 78 | 79 | from@^0.1.7: 80 | version "0.1.7" 81 | resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" 82 | integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= 83 | 84 | has-flag@^4.0.0: 85 | version "4.0.0" 86 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 87 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 88 | 89 | isbinaryfile@4.0.6: 90 | version "4.0.6" 91 | resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" 92 | integrity sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg== 93 | 94 | map-stream@0.0.7: 95 | version "0.0.7" 96 | resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" 97 | integrity sha1-ih8HiW2CsQkmvTdEokIACfiJdKg= 98 | 99 | minimatch@3.0.5: 100 | version "3.0.5" 101 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" 102 | integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== 103 | dependencies: 104 | brace-expansion "^1.1.7" 105 | 106 | minimist@1.2.6: 107 | version "1.2.6" 108 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" 109 | integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== 110 | 111 | pause-stream@^0.0.11: 112 | version "0.0.11" 113 | resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" 114 | integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= 115 | dependencies: 116 | through "~2.3" 117 | 118 | picomatch@^2.2.1: 119 | version "2.2.2" 120 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" 121 | integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== 122 | 123 | readdirp@3.4.0: 124 | version "3.4.0" 125 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" 126 | integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== 127 | dependencies: 128 | picomatch "^2.2.1" 129 | 130 | split@^1.0.1: 131 | version "1.0.1" 132 | resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" 133 | integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== 134 | dependencies: 135 | through "2" 136 | 137 | stream-combiner@^0.2.2: 138 | version "0.2.2" 139 | resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" 140 | integrity sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg= 141 | dependencies: 142 | duplexer "~0.1.1" 143 | through "~2.3.4" 144 | 145 | supports-color@^7.1.0: 146 | version "7.1.0" 147 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" 148 | integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== 149 | dependencies: 150 | has-flag "^4.0.0" 151 | 152 | through@2, through@^2.3.8, through@~2.3, through@~2.3.4: 153 | version "2.3.8" 154 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 155 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 156 | --------------------------------------------------------------------------------