├── .gitignore ├── bin ├── eslint.js └── eslint_d.js ├── Makefile ├── lib ├── portfile.js ├── launcher.js ├── server.js ├── client.js ├── linter.js └── options.js ├── LICENSE ├── package.json ├── .eslintrc ├── CHANGES.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /bin/eslint.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | require('./eslint_d'); 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | version = $(shell node -p "require('./package.json').version") 4 | 5 | default: 6 | npm test 7 | 8 | install: 9 | rm -rf node_modules 10 | npm install 11 | 12 | release: default 13 | git tag -a -m "Release ${version}" v${version} 14 | git push --follow-tags 15 | npm publish 16 | -------------------------------------------------------------------------------- /lib/portfile.js: -------------------------------------------------------------------------------- 1 | /*eslint no-sync: 0*/ 2 | 'use strict'; 3 | 4 | var fs = require('fs'); 5 | 6 | var homeEnv = process.platform === 'win32' ? 'USERPROFILE' : 'HOME'; 7 | var dataFile = process.env[homeEnv] + '/.eslint_d'; 8 | 9 | exports.write = function (port, token) { 10 | fs.writeFileSync(dataFile, port + ' ' + token); 11 | }; 12 | 13 | exports.read = function () { 14 | if (fs.existsSync(dataFile)) { 15 | var data = fs.readFileSync(dataFile, 'utf8').split(' '); 16 | return { 17 | port: Number(data[0]), 18 | token: data[1] 19 | }; 20 | } 21 | return null; 22 | }; 23 | 24 | exports.unlink = function () { 25 | if (fs.existsSync(dataFile)) { 26 | fs.unlinkSync(dataFile); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Maximilian Antoni 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint_d", 3 | "version": "4.2.5", 4 | "description": "Makes eslint the fastest linter on the planet", 5 | "bin": "bin/eslint_d.js", 6 | "keywords": [ 7 | "eslint", 8 | "lint" 9 | ], 10 | "author": "Maximilian Antoni (http://maxantoni.de/)", 11 | "contributors": [ 12 | "Richard Herrera ", 13 | "Kevin Yue ", 14 | "Roger Zurawicki ", 15 | "jpsc ", 16 | "ruanyl ", 17 | "Nick Hwang ", 18 | "Aaron Jensen " 19 | ], 20 | "homepage": "https://github.com/mantoni/eslint_d.js", 21 | "scripts": { 22 | "test": "eslint ." 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/mantoni/eslint_d.js.git" 27 | }, 28 | "dependencies": { 29 | "chalk": "^1.1.1", 30 | "eslint": "^3.0.0", 31 | "optionator": "^0.8.1", 32 | "resolve": "^1.1.7", 33 | "supports-color": "^3.1.2" 34 | }, 35 | "files": [ 36 | "bin", 37 | "lib", 38 | "LICENSE", 39 | "README.md" 40 | ], 41 | "license": "MIT" 42 | } 43 | -------------------------------------------------------------------------------- /lib/launcher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var net = require('net'); 4 | var portfile = require('../lib/portfile'); 5 | 6 | function check(callback) { 7 | var data = portfile.read(); 8 | if (!data) { 9 | callback(false); 10 | return; 11 | } 12 | var socket = net.connect(data.port, function () { 13 | socket.end(); 14 | callback(true); 15 | }); 16 | socket.on('error', function () { 17 | callback(false); 18 | }); 19 | } 20 | 21 | function wait(callback) { 22 | check(function (running) { 23 | if (running) { 24 | if (typeof callback === 'function') { 25 | callback(null); 26 | } 27 | } else { 28 | setTimeout(function () { 29 | wait(callback); 30 | }, 100); 31 | } 32 | }); 33 | } 34 | 35 | function launch(callback) { 36 | var spawn = require('child_process').spawn; 37 | var server = require.resolve('../lib/server'); 38 | var child = spawn('node', [server], { 39 | detached: true, 40 | stdio: ['ignore', 'ignore', 'ignore'] 41 | }); 42 | child.unref(); 43 | setTimeout(function () { 44 | wait(callback); 45 | }, 100); 46 | } 47 | 48 | module.exports = function (callback) { 49 | check(function (running) { 50 | if (running) { 51 | process.stdout.write('Already running\n'); 52 | } else { 53 | launch(callback); 54 | } 55 | }); 56 | }; 57 | -------------------------------------------------------------------------------- /bin/eslint_d.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | function start() { 5 | require('../lib/launcher')(); 6 | } 7 | 8 | var cmd = process.argv[2]; 9 | if (cmd === 'start') { 10 | 11 | start(); 12 | 13 | } else if (cmd === '-v' || cmd === '--version') { 14 | 15 | console.log('v%s (eslint_d v%s)', 16 | require('eslint/package.json').version, 17 | require('../package.json').version); 18 | 19 | } else if (cmd === '-h' || cmd === '--help') { 20 | 21 | var options = require('../lib/options'); 22 | console.log(options.generateHelp()); 23 | 24 | } else { 25 | 26 | var client = require('../lib/client'); 27 | if (cmd === 'restart') { 28 | client.stop(function () { 29 | process.nextTick(start); 30 | }); 31 | } else { 32 | var commands = ['stop', 'status', 'restart']; 33 | if (commands.indexOf(cmd) === -1) { 34 | var useStdIn = (process.argv.indexOf('--stdin') > -1); 35 | var args = process.argv.slice(2); 36 | 37 | if (!require('supports-color')) { 38 | args.unshift('--no-color'); 39 | } 40 | 41 | if (useStdIn) { 42 | var text = ''; 43 | process.stdin.setEncoding('utf8'); 44 | 45 | process.stdin.on('data', function (chunk) { 46 | text += chunk; 47 | }); 48 | 49 | process.stdin.on('end', function () { 50 | client.lint(args, text); 51 | }); 52 | } else { 53 | client.lint(args); 54 | } 55 | } else { 56 | client[cmd](process.argv.slice(3)); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var crypto = require('crypto'); 4 | var net = require('net'); 5 | var portfile = require('./portfile'); 6 | var linter = require('./linter'); 7 | 8 | var token = crypto.randomBytes(8).toString('hex'); 9 | 10 | 11 | var server = net.createServer({ 12 | allowHalfOpen: true 13 | }, function (con) { 14 | var data = ''; 15 | con.on('data', function (chunk) { 16 | data += chunk; 17 | }); 18 | con.on('end', function () { 19 | if (!data) { 20 | con.end(); 21 | return; 22 | } 23 | var p = data.indexOf(' '); 24 | if (p === -1 || data.substring(0, p) !== token) { 25 | con.end(); 26 | return; 27 | } 28 | data = data.substring(p + 1); 29 | if (data === 'stop') { 30 | con.end(); 31 | server.close(); 32 | return; 33 | } 34 | var cwd, args, text; 35 | if (data.substring(0, 1) === '{') { 36 | var json = JSON.parse(data); 37 | cwd = json.cwd; 38 | args = json.args; 39 | text = json.text; 40 | } else { 41 | var parts = data.split(' '); 42 | cwd = parts[0]; 43 | args = parts.slice(1); 44 | } 45 | try { 46 | con.write(linter(cwd, args, text)); 47 | } catch (e) { 48 | con.write(e.toString() + '\n# exit 1'); 49 | } 50 | con.end(); 51 | }); 52 | }); 53 | 54 | server.listen(0, '127.0.0.1', function () { 55 | var port = server.address().port; 56 | portfile.write(port, token); 57 | }); 58 | 59 | process.on('exit', function () { 60 | portfile.unlink(); 61 | }); 62 | process.on('SIGTERM', function () { 63 | process.exit(); 64 | }); 65 | process.on('SIGINT', function () { 66 | process.exit(); 67 | }); 68 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | root: true 2 | 3 | env: 4 | node: true 5 | 6 | extends: "eslint:recommended" 7 | 8 | rules: 9 | array-bracket-spacing: [2, "never"] 10 | block-scoped-var: 2 11 | block-spacing: 2 12 | brace-style: [2, "1tbs", { allowSingleLine: true }] 13 | comma-spacing: [2, { before: false, after: true }] 14 | comma-style: [2, "last"] 15 | consistent-return: 2 16 | curly: 2 17 | dot-notation: 2 18 | dot-location: [2, "property"] 19 | eqeqeq: 2 20 | handle-callback-err: 2 21 | guard-for-in: 2 22 | indent: [2, 2] 23 | linebreak-style: [2, "unix"] 24 | max-len: [2, 80, 4] 25 | new-cap: 2 26 | new-parens: 2 27 | no-alert: 2 28 | no-caller: 2 29 | no-catch-shadow: 2 30 | no-console: 0 31 | no-else-return: 2 32 | no-eq-null: 2 33 | no-eval: 2 34 | no-extend-native: [2] 35 | no-extra-bind: 2 36 | no-implied-eval: 2 37 | no-invalid-this: 2 38 | no-lonely-if: 2 39 | no-loop-func: 2 40 | no-multi-spaces: 2 41 | no-multiple-empty-lines: [2, { max: 2 }] 42 | no-new-func: 2 43 | no-new-wrappers: 2 44 | no-new: 2 45 | no-return-assign: 2 46 | no-self-compare: 2 47 | no-spaced-func: 2 48 | no-sync: 2 49 | no-throw-literal: 2 50 | no-trailing-spaces: 2 51 | no-undef-init: 2 52 | no-unneeded-ternary: 2 53 | no-unused-expressions: 2 54 | no-use-before-define: 2 55 | no-useless-call: 2 56 | no-useless-concat: 2 57 | no-void: 2 58 | object-curly-spacing: [2, "always"] 59 | operator-linebreak: [2, "before"] 60 | quotes: [2, "single"] 61 | quote-props: [2, "as-needed"] 62 | radix: 2 63 | semi: [2, "always"] 64 | semi-spacing: 2 65 | keyword-spacing: 2 66 | space-before-function-paren: [2, { anonymous: "always", named: "never" }] 67 | space-in-parens: [2, "never"] 68 | space-infix-ops: 2 69 | space-unary-ops: 2 70 | strict: [2, "global"] 71 | wrap-iife: [2, "outside"] 72 | yoda: 2 73 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var net = require('net'); 4 | var portfile = require('./portfile'); 5 | 6 | function connect(callback) { 7 | var data = portfile.read(); 8 | if (data) { 9 | var socket = net.connect(data.port, function () { 10 | callback(null, socket, data.token); 11 | }); 12 | socket.on('error', function () { 13 | callback('Could not connect'); 14 | }); 15 | } else { 16 | callback('Not running'); 17 | } 18 | } 19 | 20 | exports.stop = function (callback) { 21 | connect(function (err, socket, token) { 22 | if (err) { 23 | process.stdout.write(err + '\n'); 24 | return; 25 | } 26 | socket.end(token + ' stop', function () { 27 | if (typeof callback === 'function') { 28 | callback(); 29 | } 30 | }); 31 | }); 32 | }; 33 | 34 | exports.status = function () { 35 | connect(function (err, socket) { 36 | if (err) { 37 | process.stdout.write(err + '\n'); 38 | return; 39 | } 40 | socket.end(function () { 41 | process.stdout.write('Running\n'); 42 | }); 43 | }); 44 | }; 45 | 46 | exports.lint = function (args, text) { 47 | if (!args.length && !text) { 48 | process.stdout.write('No files specified\n'); 49 | return; 50 | } 51 | 52 | function lint(socket, token) { 53 | var buf = ''; 54 | socket.on('data', function (chunk) { 55 | buf += chunk; 56 | var p = buf.lastIndexOf('\n'); 57 | if (p !== -1) { 58 | process.stdout.write(buf.substring(0, p + 1)); 59 | buf = buf.substring(p + 1); 60 | } 61 | }); 62 | socket.on('end', function () { 63 | if (buf) { 64 | if (buf === '# exit 1') { 65 | process.exitCode = 1; 66 | } else { 67 | process.stdout.write(buf); 68 | } 69 | } 70 | }); 71 | socket.end(token + ' ' + JSON.stringify({ 72 | cwd: process.cwd(), 73 | args: args, 74 | text: text 75 | })); 76 | } 77 | connect(function (err, socket, token) { 78 | if (err) { 79 | require('./launcher')(function () { 80 | connect(function (err, socket, token) { 81 | if (err) { 82 | process.stdout.write(err + '\n'); 83 | } else { 84 | lint(socket, token); 85 | } 86 | }); 87 | }); 88 | } else { 89 | lint(socket, token); 90 | } 91 | }); 92 | }; 93 | -------------------------------------------------------------------------------- /lib/linter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var resolve = require('resolve'); 4 | var options = require('./options'); 5 | var path = require('path'); 6 | 7 | function translateOptions(cliOptions, cwd) { 8 | return { 9 | envs: cliOptions.env, 10 | extensions: cliOptions.ext, 11 | rules: cliOptions.rule, 12 | plugins: cliOptions.plugin, 13 | globals: cliOptions.global, 14 | ignore: cliOptions.ignore, 15 | ignorePath: cliOptions.ignorePath, 16 | ignorePattern: cliOptions.ignorePattern, 17 | configFile: cliOptions.config, 18 | rulePaths: cliOptions.rulesdir, 19 | useEslintrc: cliOptions.eslintrc, 20 | parser: cliOptions.parser, 21 | cache: cliOptions.cache, 22 | cacheFile: cliOptions.cacheFile, 23 | cacheLocation: cliOptions.cacheLocation, 24 | fix: cliOptions.fix || cliOptions.fixToStdout, 25 | allowInlineConfig: cliOptions.inlineConfig, 26 | printConfig: cliOptions.printConfig, 27 | cwd: cwd 28 | }; 29 | } 30 | 31 | var eslintMap = {}; 32 | 33 | module.exports = function (cwd, args, text) { 34 | process.chdir(cwd); 35 | var cwdDeps = eslintMap[cwd]; 36 | if (!cwdDeps) { 37 | var eslintPath; 38 | try { 39 | eslintPath = resolve.sync('eslint', { basedir: cwd }); 40 | } catch (e) { 41 | // module not found 42 | eslintPath = resolve.sync('eslint'); 43 | } 44 | cwdDeps = eslintMap[cwd] = { 45 | eslint: require(eslintPath), 46 | // use chalk from eslint 47 | chalk: require(resolve.sync('chalk', { 48 | basedir: path.dirname(eslintPath) 49 | })) 50 | }; 51 | } 52 | var currentOptions; 53 | try { 54 | currentOptions = options.parse([0, 0].concat(args)); 55 | } catch (e) { 56 | return e.message + '\n# exit 1'; 57 | } 58 | cwdDeps.chalk.enabled = currentOptions.color; 59 | var files = currentOptions._; 60 | var stdin = currentOptions.stdin; 61 | if (!files.length && (!stdin || typeof text !== 'string')) { 62 | return options.generateHelp() + '\n'; 63 | } 64 | var engine = new cwdDeps.eslint.CLIEngine( 65 | translateOptions(currentOptions, cwd) 66 | ); 67 | if (currentOptions.printConfig) { 68 | if (files.length !== 1) { 69 | return 'The --print-config option requires a "' 70 | + 'single file as positional argument.\n# exit 1'; 71 | } 72 | 73 | if (text) { 74 | return 'The --print-config option is not available for piped-in code.' 75 | + '\n# exit 1'; 76 | } 77 | 78 | var fileConfig = engine.getConfigForFile(files[0]); 79 | 80 | return JSON.stringify(fileConfig, null, ' '); 81 | } 82 | if (currentOptions.fixToStdout && !stdin) { 83 | return 'The --fix-to-stdout option must be used with --stdin.' 84 | + '\n# exit 1'; 85 | } 86 | var report; 87 | if (stdin) { 88 | report = engine.executeOnText(text, currentOptions.stdinFilename); 89 | } else { 90 | report = engine.executeOnFiles(files); 91 | } 92 | if (currentOptions.fixToStdout) { 93 | // No results will be returned if the file is ignored 94 | // No output will be returned if there are no fixes 95 | return (report.results[0] && report.results[0].output) || text; 96 | } 97 | if (currentOptions.fix) { 98 | cwdDeps.eslint.CLIEngine.outputFixes(report); 99 | } 100 | if (currentOptions.quiet) { 101 | report.results = cwdDeps.eslint.CLIEngine.getErrorResults(report.results); 102 | } 103 | var format = currentOptions.format; 104 | var formatter = engine.getFormatter(format); 105 | if (!formatter) { 106 | return 'Could not find formatter \'' + format + '\'.\n'; 107 | } 108 | var output = formatter(report.results); 109 | var tooManyWarnings = currentOptions.maxWarnings >= 0 110 | && report.warningCount > currentOptions.maxWarnings; 111 | if (!report.errorCount && tooManyWarnings) { 112 | output += 'ESLint found too many warnings (maximum: ' 113 | + currentOptions.maxWarnings + ').\n'; 114 | } 115 | if (report.errorCount) { 116 | output += '\n# exit 1'; 117 | } 118 | return output; 119 | }; 120 | -------------------------------------------------------------------------------- /lib/options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var optionator = require('optionator'); 4 | 5 | module.exports = optionator({ 6 | prepend: 'eslint_d [options] file.js [file.js] [dir]', 7 | defaults: { 8 | concatRepeatedArrays: true, 9 | mergeRepeatedObjects: true 10 | }, 11 | options: [ 12 | { 13 | heading: 'Basic configuration' 14 | }, 15 | { 16 | option: 'config', 17 | alias: 'c', 18 | type: 'path::String', 19 | description: 'Use configuration from this file or shareable config' 20 | }, 21 | { 22 | option: 'eslintrc', 23 | type: 'Boolean', 24 | default: 'true', 25 | description: 'Disable use of configuration from .eslintrc' 26 | }, 27 | { 28 | option: 'env', 29 | type: '[String]', 30 | description: 'Specify environments' 31 | }, 32 | { 33 | option: 'ext', 34 | type: '[String]', 35 | default: '.js', 36 | description: 'Specify JavaScript file extensions' 37 | }, 38 | { 39 | option: 'global', 40 | type: '[String]', 41 | description: 'Define global variables' 42 | }, 43 | { 44 | option: 'parser', 45 | type: 'String', 46 | default: '', 47 | description: 'Specify the parser to be used' 48 | }, 49 | { 50 | heading: 'Caching' 51 | }, 52 | { 53 | option: 'cache', 54 | type: 'Boolean', 55 | default: 'false', 56 | description: 'Only check changed files' 57 | }, 58 | { 59 | option: 'cache-file', 60 | type: 'String', 61 | default: '.eslintcache', 62 | description: 'Path to the cache file' 63 | }, 64 | { 65 | option: 'cache-location', 66 | type: 'path::String', 67 | description: 'Path to the cache file or directory' 68 | }, 69 | { 70 | heading: 'Specifying rules and plugins' 71 | }, 72 | { 73 | option: 'rulesdir', 74 | type: '[path::String]', 75 | description: 'Use additional rules from this directory' 76 | }, 77 | { 78 | option: 'plugin', 79 | type: '[String]', 80 | description: 'Specify plugins' 81 | }, 82 | { 83 | option: 'rule', 84 | type: 'Object', 85 | description: 'Specify rules' 86 | }, 87 | { 88 | heading: 'Ignoring files' 89 | }, 90 | { 91 | option: 'ignore-path', 92 | type: 'path::String', 93 | description: 'Specify path of ignore file' 94 | }, 95 | { 96 | option: 'ignore', 97 | type: 'Boolean', 98 | default: 'true', 99 | description: 'Disable use of .eslintignore' 100 | }, 101 | { 102 | option: 'ignore-pattern', 103 | type: 'String', 104 | description: 105 | 'Pattern of files to ignore (in addition to those in .eslintignore)' 106 | }, 107 | { 108 | heading: 'Using stdin' 109 | }, 110 | { 111 | option: 'stdin', 112 | type: 'Boolean', 113 | default: 'false', 114 | description: 'Lint code provided on ' 115 | }, 116 | { 117 | option: 'stdin-filename', 118 | type: 'String', 119 | description: 'Specify filename to process STDIN as' 120 | }, 121 | { 122 | heading: 'Handling warnings' 123 | }, 124 | { 125 | option: 'quiet', 126 | type: 'Boolean', 127 | default: 'false', 128 | description: 'Report errors only' 129 | }, 130 | { 131 | option: 'max-warnings', 132 | type: 'Number', 133 | default: '-1', 134 | description: 'Number of warnings to trigger nonzero exit code' 135 | }, 136 | { 137 | heading: 'Output' 138 | }, 139 | { 140 | option: 'format', 141 | alias: 'f', 142 | type: 'String', 143 | default: 'stylish', 144 | description: 'Use a specific output format' 145 | }, 146 | { 147 | option: 'color', 148 | type: 'Boolean', 149 | default: 'true', 150 | description: 'Disable color in piped output' 151 | }, 152 | { 153 | heading: 'Miscellaneous' 154 | }, 155 | { 156 | option: 'fix', 157 | type: 'Boolean', 158 | default: false, 159 | description: 'Automatically fix problems' 160 | }, 161 | { 162 | option: 'fix-to-stdout', 163 | type: 'Boolean', 164 | description: 'Print the fix results to stdout instead of regular output, ' 165 | + 'must be used with --stdin' 166 | }, 167 | { 168 | option: 'debug', 169 | type: 'Boolean', 170 | default: false, 171 | description: 'Output debugging information' 172 | }, 173 | { 174 | option: 'help', 175 | alias: 'h', 176 | type: 'Boolean', 177 | description: 'Show help' 178 | }, 179 | { 180 | option: 'version', 181 | alias: 'v', 182 | type: 'Boolean', 183 | description: 'Outputs the eslint and eslint_d version numbers' 184 | }, 185 | { 186 | option: 'inline-config', 187 | type: 'Boolean', 188 | default: 'true', 189 | description: 'Allow comments to change eslint config/rules' 190 | }, 191 | { 192 | option: 'print-config', 193 | type: 'Boolean', 194 | description: 'Print the configuration to be used' 195 | } 196 | ] 197 | }); 198 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## 4.2.5 4 | 5 | Add `.vimrc` example for buffer auto-fixing to README. 6 | 7 | ## 4.2.4 8 | 9 | Exit with status 1 when an error occurs. Fixes [#63][issue 63]. 10 | 11 | [issue 63]: https://github.com/mantoni/eslint_d.js/issues/63 12 | 13 | ## 4.2.2 14 | 15 | Fix `--fix-to-stdout` when used with an ignored file. 16 | 17 | ## 4.2.1 18 | 19 | Fix [`--fix-to-stdout` when used with an empty file][pull 59]. 20 | 21 | [pull 59]: https://github.com/mantoni/eslint_d.js/pull/59 22 | 23 | ## 4.2.0 24 | 25 | An exciting new feature comes to eslint_d, the first one that is not part of 26 | eslint itself. [Aaron Jensen implemented][pull 53] `--fix-to-stdout` which 27 | allows to integrated `eslint --fix` into your editor as a save action 🎉 28 | 29 | Currently, this feature only works with `--stdin` and you can test it like this: 30 | 31 | ``` 32 | $ cat ./some/file.js | eslint_d --fix-to-stdout --stdin 33 | ``` 34 | 35 | [pull 53]: https://github.com/mantoni/eslint_d.js/pull/53 36 | 37 | ## 4.1.0 38 | 39 | Support for `--print-config` was [added by Aaron Jensen][pull 51]. He also 40 | added instructions for Emacs users. 41 | 42 | [pull 51]: https://github.com/mantoni/eslint_d.js/pull/51 43 | 44 | ## 4.0.1 45 | 46 | Fixes a security issue that was [noticed by Andri Möll][issue 45]. Thanks for 47 | reporting! To avoid CSRF attacks, this [introduces a security token][pull 46] 48 | that must be sent by clients on each request. This change also binds the daemon 49 | explicitly to `127.0.0.1` instead of implicitly listening on all network 50 | interfaces. 51 | 52 | [issue 45]: https://github.com/mantoni/eslint_d.js/issues/45 53 | [pull 46]: https://github.com/mantoni/eslint_d.js/pull/46 54 | 55 | ## 4.0.0 56 | 57 | Use ESLint 3. 58 | 59 | ## 3.1.2 60 | 61 | Back-ported the security fix from `v4.0.1`. 62 | 63 | ## 3.1.1 64 | 65 | As per a [recent change in eslint][bda5de5] the default parser `espree` [was 66 | removed][pull 43]. The `eslint` dependency was bumped to `2.10.2` which 67 | introduced the change. 68 | 69 | [bda5de5]: https://github.com/eslint/eslint/commit/bda5de5 70 | [pull 43]: https://github.com/mantoni/eslint_d.js/pull/43 71 | 72 | ## 3.1.0 73 | 74 | The `eslint_d` command will now exit with code 1 if errors where reported. 75 | 76 | ## 3.0.1 77 | 78 | A [fix was provided by ruanyl][pull #33] to resolve `chalk` relative from the 79 | actually resolved eslint module. 80 | 81 | [pull #33]: https://github.com/mantoni/eslint_d.js/pull/33 82 | 83 | ## 3.0.0 84 | 85 | jpsc got the [eslint 2 upgrade][pull #30] started. `eslint_d` will now use 86 | eslint 2.2+ if no local install of eslint is found. 87 | 88 | Also in this release: 89 | 90 | - Support `--inline-config` and `--cache-location` options 91 | - Pass `cwd` through to eslint. 92 | 93 | [pull #30]: https://github.com/mantoni/eslint_d.js/pull/30 94 | 95 | ## 2.5.1 96 | 97 | - Fix `--fix` 98 | - Fix color for local eslint 99 | 100 | ## 2.5.0 101 | 102 | - Support color and the `--no-color` option (fixes [issue #7][]) 103 | - Improve formatting in "Editor integration" documentation 104 | 105 | [issue #7]: https://github.com/mantoni/eslint_d.js/issues/7 106 | 107 | ## 2.4.0 108 | 109 | Roger Zurawicki [figured out][pull #24] how to make `eslint_d` work in WebStorm. 110 | 111 | - Add information about `--cache` in the readme (netei) 112 | - Add symlink to `eslint.js` for WebStorm compat (Roger Zurawicki) 113 | 114 | [pull #24]: https://github.com/mantoni/eslint_d.js/pull/24 115 | 116 | ## 2.3.2 117 | 118 | Fixes an error in case no local eslint module can be found (Kevin Yue) 119 | 120 | - [Issue #17](https://github.com/mantoni/eslint_d.js/issues/17) 121 | - [Pull request #18](https://github.com/mantoni/eslint_d.js/pull/18) 122 | 123 | ## 2.3.1 124 | 125 | - Remove `concat-stream` dependency and micro optimizations (Richard Herrera) 126 | 127 | ## 2.3.0 128 | 129 | Richard Herrera implemented a missing eslint feature to [lint text provided via 130 | stdin][]. This also fixes [issue #13][]. 131 | 132 | [lint text provided via stdin]: https://github.com/mantoni/eslint_d.js/pull/15 133 | [issue #13]: https://github.com/mantoni/eslint_d.js/issues/13 134 | 135 | ## 2.2.0 136 | 137 | Resolves the `eslint` module for each working directory separately. This allows 138 | multiple versions of eslint to co-exist. This is required to support local 139 | plugins like the `babel-eslint` parser (see [issue #10][]). If no local eslint 140 | install is found, the one that was installed with `eslint_d` is used. 141 | 142 | [issue #10]: https://github.com/mantoni/eslint_d.js/issues/10 143 | 144 | ## 2.1.2 145 | 146 | Fixes [issue #9][] with space-containing config path or other shell parameters 147 | that need escaping. 148 | 149 | [issue #9]: https://github.com/mantoni/eslint_d.js/issues/9 150 | 151 | ## 2.1.1 152 | 153 | Fixes [issue #8][] on Windows when launching in a `cmd` shell where `eslint_d` 154 | was hanging indefinitely. 155 | 156 | - Update Sublime linter URL to it's new home 157 | - Add note for Atom users 158 | 159 | [issue #8]: https://github.com/mantoni/eslint_d.js/issues/8 160 | 161 | ## 2.1.0 162 | 163 | Make `eslint_d` work out of the box in vim with the syntastic eslint checker. 164 | 165 | - Add `--version` and `-v` options 166 | - Do not start server when called with `-h` or `--help` 167 | - Downgrade `optionator` to align with eslint 168 | - Update instructions for vim integration 169 | 170 | ## 2.0.0 171 | 172 | This realease support (almost) all `eslint` options. Check `eslint_d --help`. 173 | 174 | Breaking that API already: The `lint` command was removed and in case you're 175 | not passing a control command like `start`, `stop`, `restart` or `status`, the 176 | given options are passed to the linter. 177 | 178 | Also, the default output format was changed from `compact` to `stylish` to 179 | align with `eslint`. 180 | 181 | - Document vim syntastic javascript checkers (Chris Gaudreau) 182 | - invokations -> invocations (Juho Vepsäläinen) 183 | - Document Sublime editor integration 184 | - Handle linter exceptions 185 | 186 | ## 1.0.0 187 | 188 | - Initial release 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint\_d 2 | 3 | [![SemVer]](http://semver.org) 4 | [![License]](https://github.com/mantoni/eslint\_d.js/blob/master/LICENSE) 5 | 6 | Makes [eslint][] the fastest linter on the planet. 7 | 8 | ## "But eslint is pretty fast already, right?" 9 | 10 | Yes, it's actually super fast. But the node.js startup time and loading all the 11 | required modules slows down linting times for a single file to ~700 12 | milliseconds. `eslint_d` reduces this overhead by running a server in the 13 | background. It brings the linting time down to ~160 milliseconds. If you want 14 | to lint from within your editor whenever you save a file, `eslint_d` is for 15 | you. 16 | 17 | ## Install 18 | 19 | This will install the `eslint_d` command globally: 20 | 21 | ```bash 22 | $ npm install -g eslint_d 23 | ``` 24 | 25 | ## Usage 26 | 27 | To start the server and lint a file, just run: 28 | 29 | ```bash 30 | $ eslint_d file.js 31 | ``` 32 | 33 | On the initial call, the `eslint_d` server is launched and then the given file 34 | is linted. Subsequent invocations are super fast. 35 | 36 | ## How does this work? 37 | 38 | The first time you use `eslint_d`, a little server is started in the background 39 | and bound to a random port. The port number is stored along with [a 40 | token][change401] in `~/.eslint_d`. You can then run `eslint_d` commands the 41 | same way you would use `eslint` and it will delegate to the background server. 42 | It will load a [separate instance][change220] of eslint for each working 43 | directory to make sure settings are kept local. If eslint is found in the 44 | current working directories `node_modules` folder, then this version of eslint 45 | is going to be used. Otherwise, the version of eslint that ships with 46 | `eslint_d` is used as a fallback. 47 | 48 | However, the performance gain comes at a small price: Changes in the eslint 49 | settings are only picked up after a server restart, so you will have to 50 | remember to run `eslint_d restart` after tweaking this rule or installing that 51 | plugin. Also, when you have a lot of projects that use eslint, it might use 52 | quite a bit of ram for cached instances. All memory can be freed up by running 53 | `eslint_d stop` or `eslint_d restart`. 54 | 55 | ## Commands 56 | 57 | Control the server like this: 58 | 59 | ```bash 60 | $ eslint_d 61 | ``` 62 | 63 | Available commands: 64 | 65 | - `start`: start the server 66 | - `stop`: stop the server 67 | - `status`: print out whether the server is currently running 68 | - `restart`: restart the server 69 | - `[options] file.js [file.js] [dir]`: invoke `eslint` with the given options. 70 | The `eslint` engine will be created in the current directory. If the server 71 | is not yet running, it is started. 72 | 73 | Type `eslint_d --help` to see the supported `eslint` options. 74 | 75 | `eslint_d` will select a free port automatically and store the port number 76 | along with an access token in `~/.eslint_d`. 77 | 78 | ## Editor integration 79 | 80 | ### Linting 81 | 82 | - __Sublime__: Check out [SublimeLinter-contrib-eslint\_d][SublimeLinter]. 83 | - __Vim__: Install the [syntastic][] plugin, then make sure this is in your 84 | `.vimrc`: 85 | 86 | ```vim 87 | let g:syntastic_javascript_checkers = ['eslint'] 88 | let g:syntastic_javascript_eslint_exec = 'eslint_d' 89 | ``` 90 | 91 | - __WebStorm__: Configure your IDE to point to the `eslint_d` package instead 92 | of `eslint`. In the ESLint configuration dialog, under 'ESLint package', 93 | select your `eslint_d` package. 94 | - __Atom__: You will not gain any performance from this module as it already 95 | avoids starting a new node instance and uses the API directly (see [this 96 | AtomLinter issue](https://github.com/AtomLinter/linter-eslint/issues/215)). 97 | - __Emacs__: Use [flycheck](http://www.flycheck.org/) with the 98 | `javascript-eslint` checker: 99 | 100 | ```elisp 101 | (setq flycheck-javascript-eslint-executable "eslint_d") 102 | ``` 103 | 104 | If you're using `eslint_d` in any other editor, please tell me! 105 | 106 | ### Automatic Fixing 107 | 108 | `eslint_d` has an additional flag that `eslint` does not have, 109 | `--fix-to-stdout` which prints the fixed file to stdout. This allows editors to 110 | add before save hooks to automatically fix a file prior to saving. It must be 111 | used with `--stdin`. 112 | 113 | - __Vim__: Add this to your `.vimrc` to lint the current buffer or visual 114 | selection on `f`: 115 | 116 | ```vim 117 | " Autofix entire buffer with eslint_d: 118 | nnoremap f mF:%!eslint_d --stdin --fix-to-stdout`F 119 | " Autofix visual selection with eslint_d: 120 | vnoremap f :!eslint_d --stdin --fix-to-stdoutgv 121 | ``` 122 | 123 | - __Emacs__: See [eslintd-fix](https://github.com/aaronjensen/eslintd-fix) 124 | 125 | ## Moar speed 126 | 127 | If you're really into performance and want the lowest possible latency, talk to 128 | the `eslint_d` server with netcat. This will also eliminate the node.js startup 129 | time. 130 | 131 | ```bash 132 | $ PORT=`cat ~/.eslint_d | cut -d" " -f1` 133 | $ TOKEN=`cat ~/.eslint_d | cut -d" " -f2` 134 | $ echo "$TOKEN $PWD file.js" | nc localhost $PORT 135 | ``` 136 | 137 | This runs `eslint` in under `50ms`! 138 | 139 | **Tip** For additional speed, did you know that you can lint only files that 140 | have changed? This is a feature of normal `eslint`, but it also works from 141 | `eslint_d`. Run: 142 | 143 | ```bash 144 | $ eslint_d . --cache 145 | ``` 146 | 147 | ## Compatibility 148 | 149 | - `4.0.0`: eslint 3.0+ 150 | - `3.0.0`: eslint 2.2+ 151 | - `1.0.0`, `2.0.0`: eslint 1.4+ 152 | 153 | ## License 154 | 155 | MIT 156 | 157 | [SemVer]: http://img.shields.io/:semver-%E2%9C%93-brightgreen.svg 158 | [License]: http://img.shields.io/npm/l/eslint_d.svg 159 | [eslint]: http://eslint.org 160 | [SublimeLinter]: https://github.com/roadhump/SublimeLinter-contrib-eslint_d 161 | [syntastic]: https://github.com/scrooloose/syntastic 162 | [change220]: https://github.com/mantoni/eslint_d.js/blob/master/CHANGES.md#220 163 | [change401]: https://github.com/mantoni/eslint_d.js/blob/master/CHANGES.md#401 164 | --------------------------------------------------------------------------------