Skip to content

Commit

Permalink
Rewrite README
Browse files Browse the repository at this point in the history
  • Loading branch information
mantoni committed Jul 28, 2024
1 parent 71b35f9 commit 7ce116f
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 174 deletions.
289 changes: 116 additions & 173 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,211 +1,158 @@
# eslint\_d

![Build Status](https://github.com/mantoni/eslint_d.js/workflows/Build/badge.svg)
[![SemVer]](http://semver.org)
[![License]](https://github.com/mantoni/eslint\_d.js/blob/master/LICENSE)

Makes [eslint][] the fastest linter on the planet.

## "But eslint is pretty fast already, right?"

Yes, it's really fast. But the node.js startup time and loading all the
required modules slows down linting times for a single file to ~700
milliseconds. `eslint_d` reduces this overhead by running a server in the
background. It brings the linting time down to ~160 milliseconds. If you want
to lint from within your editor whenever you save a file, `eslint_d` is for
you.

## Install

This will install the `eslint_d` command globally:
<h1 align="center">
eslint_d
</h1>
<p align="center">
<b>🪄 Speed up eslint to accelerate your development workflow</b>
</p>
<div align="center">
<a href="https://www.npmjs.com/package/eslint_d">
<img src="https://img.shields.io/npm/v/eslint_d.svg" alt="npm Version">
</a>
<a href="https://semver.org">
<img src="https://img.shields.io/:semver-%E2%9C%93-blue.svg" alt="SemVer">
</a>
<a href="https://github.com/mantoni/eslint_d.js/actions">
<img src="https://github.com/mantoni/eslint_d.js/workflows/Build/badge.svg" alt="Build Status">
</a>
<a href="https://opensource.org/licenses/MIT">
<img src="https://img.shields.io/npm/l/eslint_d.svg" alt="License">
</a>
<br>
<br>
<br>
</div>

> Runs [eslint][] in a background process to improve linting time while editing.
> On a MacBook Air M1 with node.js v22.2.0 and eslint v9.8.0:
```bash
$ npm install -g eslint_d
❯ eslint file.js # ~500ms
❯ eslint_d file.js # ~115ms
```

## Usage
## Features

To start the server and lint a file, just run:
- Supports all eslint versions from v4 to v9.
- Supports all LTS versions of node.js.
- Automatically starts, stops and restarts the background server.
- Binds to parent process, editor process or exits after IDLE time.
- Falls back to bundled eslint if local eslint is missing.

## Setup

```bash
$ eslint_d file.js
❯ npm i -g eslint_d
```

On the initial call, the `eslint_d` server is launched and then the given file
is linted. Subsequent invocations are super fast.

## How does this work?

The first time you use `eslint_d`, a little server is started in the background
and bound to a random port. You can then run `eslint_d` commands the
same way you would use `eslint` and it will delegate to the background server.
It will load a [separate instance][change220] of eslint for each working
directory to make sure settings are kept local. If eslint is found in the
current working directories `node_modules` folder, then this version of eslint
is going to be used. Otherwise, the version of eslint that ships with
`eslint_d` is used as a fallback.

It's possible to force `eslint_d` to only resolve local `eslint` by setting the
`ESLINT_D_LOCAL_ESLINT_ONLY` environment variable to a truthy value (ie. `true` or `1`).
### Vim

To keep the memory footprint low, `eslint_d` keeps only the last 10 used
instances in the internal [nanolru][] cache.
- With [ale][]:

## What if eslint or a plugin is updated?

The cached version of eslint and the Node `require` cache for the current
working directory are cleared whenever a change to one of these files is
detected: `package.json`, `package-lock.json`, `npm-shrinkwrap.json`,
`yarn.lock` and `pnpm-lock.yaml`. If changes are not automatically detected,
remember to run `eslint_d restart` to bounce the background server.

**Note:** Change detection was switched from mtime to content hash with v12.

## Which versions of eslint are supported?
```vim
let $ESLINT_D_PPID = getpid()
let g:ale_javascript_eslint_executable = 'eslint_d'
let g:ale_javascript_eslint_use_global = 1
```

You can use `eslint_d` with multiple projects depending on different versions
of eslint. If no local eslint is found, `eslint_d` falls back to the eslint
version that ships with `eslint_d`.
- With [syntastic][]:

Experimental support for eslint flat config is available since `v13.1.0` if
the `ESLINT_USE_FLAT_CONFIG` environment variable is defined.
```vim
let $ESLINT_D_PPID = getpid()
let g:syntastic_javascript_checkers = ['eslint']
let g:syntastic_javascript_eslint_exec = 'eslint_d'
```

Known flat config limitations:
### WebStorm

- [#281](https://github.com/mantoni/eslint_d.js/issues/281) - The background server process only evaluates the config format used at start time and not per invocation of eslint. Multiple projects sharing the same background server but using different config formats will result in config errors for the projects that don't match the background server's set config format. Editors that automatically start `eslint_d` will also result in config errors if the background server was not started with the correct config format for the project. Restarting `eslint_d` with the desired config format can be used to work around the limitation: `ESLINT_USE_FLAT_CONFIG=true eslint_d restart` to enable flat configs or `ESLINT_USE_FLAT_CONFIG= eslint_d restart` to disable flat configs.
Configure your IDE to point to the `eslint_d` package instead of `eslint`. In
the ESLint configuration dialog, under 'ESLint package', select your `eslint_d`
package.

## Commands
### Emacs

Control the server like this:
Use [flycheck][] with the `javascript-eslint` checker:

```bash
$ eslint_d <command>
```elisp
(setq flycheck-javascript-eslint-executable "eslint_d")
```

Available commands:

- `start`: start the server
- `stop`: stop the server
- `status`: print out whether the server is currently running
- `restart`: restart the server
- `[options] file.js [file.js] [dir]`: invoke `eslint` with the given options.
The `eslint` engine will be created in the current directory. If the server
is not yet running, it is started.

Type `eslint_d --help` to see the supported `eslint` options.

When the server starts, `eslint_d` selects a free port automatically
and decides on a random access token. Both the port and token are
written to an `.eslint_d` file so that future usages of `eslint_d` can
connect to the already running server. The `.eslint_d` file is stored
under the `XDG_RUNTIME_DIR` directory if this environment variable is
defined. If the variable is not defined then the file is stored in the
user's home directory.

## Editor integration

### Linting

- __Vim__:
- With [syntastic][]:
```vim
let g:syntastic_javascript_checkers = ['eslint']
let g:syntastic_javascript_eslint_exec = 'eslint_d'
```

- With [ale][]:
```vim
let g:ale_javascript_eslint_executable = 'eslint_d'
let g:ale_javascript_eslint_use_global = 1
```

- __WebStorm__: Configure your IDE to point to the `eslint_d` package instead
of `eslint`. In the ESLint configuration dialog, under 'ESLint package',
select your `eslint_d` package.
- __Emacs__: Use [flycheck](http://www.flycheck.org/) with the
`javascript-eslint` checker:

```elisp
(setq flycheck-javascript-eslint-executable "eslint_d")
```
- __Sublime__: The official [SublimeLinter-eslint](https://github.com/SublimeLinter/SublimeLinter-eslint)
plugin automatically prefers `eslint_d` if it finds one.
- __Atom__, __VSCode__: You will not gain any performance from this module as
these editors already cache eslint instances for you.
### Sublime

If you're using `eslint_d` in any other editor, please let us know!
The official [SublimeLinter-eslint][] plugin automatically prefers `eslint_d`
if it finds one.

### Automatic Fixing
### Atom, VSCode

`eslint_d` has an additional flag that `eslint` does not have,
`--fix-to-stdout` which prints the fixed file to stdout. This allows editors to
add before save hooks to automatically fix a file prior to saving. It must be
used with `--stdin`.
You will not gain any performance from this module as these editors already
cache eslint instances for you.

- __Vim__: Add this to your `.vimrc` to lint the current buffer or visual
selection on `<leader>f`:
---

```vim
" Autofix entire buffer with eslint_d:
nnoremap <leader>f mF:%!eslint_d --stdin --fix-to-stdout<CR>`F
" Autofix visual selection with eslint_d:
vnoremap <leader>f :!eslint_d --stdin --fix-to-stdout<CR>gv
```

- __Emacs__: See [eslintd-fix](https://github.com/aaronjensen/eslintd-fix)
- __If the above doesn't autofix__: [This can happen with .vue files](https://github.com/mantoni/eslint_d.js/issues/145#issuecomment-787119881)
Change `eslint_d --stdin --fix-to-stdout` to `eslint_d --stdin --fix-to-stdout --stdin-filename %` (% = path to file you want to autofix)
In Vim, the above mapping should be replaced with:
```vim
nnoremap <leader>f mF:%!eslint_d --stdin --fix-to-stdout --stdin-filename %<CR>`F
```


## Moar speed
If you're using `eslint_d` in any other editor, please let us know!

If you're really into performance and want the lowest possible latency, talk to
the `eslint_d` server with netcat. This will also eliminate the node.js startup
time.
## Commands

You first need to extract the port and access token from the
`.eslint_d` file. The location of this file may change depending on
your system (see above). For example, if `XDG_RUNTIME_DIR` is
specified, you can do this:
`eslint_d` is a drop-in replacement for `eslint`. It forwards all arguments to
`eslint` and starts the background server if necessary:

```bash
$ PORT=`cat $XDG_RUNTIME_DIR/.eslint_d | cut -d" " -f1`
$ TOKEN=`cat $XDG_RUNTIME_DIR/.eslint_d | cut -d" " -f2`
```
eslint_d [options] file.js [file.js] [dir]
```

Then, you can do the following to run eslint on `file.js`:
All arguments are passed to eslint, except for the following commands:

```session
$ echo "$TOKEN $PWD file.js" | nc localhost $PORT
```
start Start the daemon
stop Stop the daemon
restart Restart the daemon
status Show daemon status, process id and resolved eslint version
--help, -h Show this help
--version, -v Show version number of eslint_d and bundled eslint
```

Or if you want to work with stdin:
## Environment variables

```bash
$ echo "$TOKEN $PWD --stdin" | cat - file.js | nc localhost $PORT
```
- `ESLINT_D_PPID` Parent process id to monitor. If the parent process dies, the
daemon exits as well. "0" disables monitoring (default), and "auto" monitors
the parent process that started eslint_d.
- `ESLINT_D_IDLE` Number of minutes of inactivity before the daemon exits.
Defaults to "0" if `ESLINT_D_PPID` is set, otherwise "15".
- `ESLINT_D_MISS` How to behave if local eslint is missing. "fallback" uses the
bundled eslint (default). "fail" logs an error and exits with code 1.
"ignore" silently exits with code 0.

## How does this work?

This runs `eslint` in under `50ms`!
`eslint_d` starts a background server that runs `eslint` in a separate process.
It communicates with the server over a Unix domain socket. When you run
`eslint_d`, it forwards all arguments to the server and prints the result. This
is faster because node.js doesn't have to load all the required modules every
time.

**Tip** For additional speed, did you know that you can lint only files that
have changed? This is a feature of normal `eslint`, but it also works from
`eslint_d`. Run:
By default, `eslint_d` uses the local `eslint` package if available. If the
local `eslint` package is missing, `eslint_d` falls back to the bundled
`eslint` package. You can change this behavior with the `ESLINT_D_MISS`
environment variable. To see which `eslint` package is used, run `eslint_d
status`.

```bash
$ eslint_d . --cache
```
A `.eslint_d` file is stored in the resolved eslint installation directory
which stores a security token, the server port and process id, and the hash of
the monitored files. If the file is removed, the server exits.

## References
The server automatically stops after 15 minutes of inactivity. You can change
this with the `ESLINT_D_IDLE` environment variable. Alternatively, you can bind
the lifetime of the server to a parent process by setting `ESLINT_D_PPID` to
"auto" or a specific parent process id. The server will exit when the parent
process dies. Note that "auto" uses the parent process that started `eslint_d`,
which may not be the editor process.

If you're interested in building something similar to this: Most of the logic
was extracted to [core_d][], a library that manages the background server.
The server is also automatically restarted if one of the following files
changed: `package.json`, `package-lock.json`, `npm-shrinkwrap.json`,
`yarn.lock`, `pnpm-lock.yaml`.

## Compatibility

- `14.0.0`: eslint 4 - 8, node 18 - 22 (ships with eslint 9)
- `13.0.0`: eslint 4 - 8, node 12 - 20 (ships with eslint 8)
- `12.0.0`: eslint 4 - 8, node 12 - 16 (ships with eslint 8)
- `11.0.0`: eslint 4 - 8, node 12 - 16 (ships with eslint 7)
Expand All @@ -224,13 +171,9 @@ was extracted to [core_d][], a library that manages the background server.

MIT

[SemVer]: https://img.shields.io/:semver-%E2%9C%93-brightgreen.svg
[License]: https://img.shields.io/npm/l/eslint_d.svg
[eslint]: https://eslint.org
[SublimeLinter]: https://github.com/roadhump/SublimeLinter-contrib-eslint_d
[syntastic]: https://github.com/scrooloose/syntastic
[ale]: https://github.com/dense-analysis/ale
[change220]: https://github.com/mantoni/eslint_d.js/blob/master/CHANGES.md#220
[change401]: https://github.com/mantoni/eslint_d.js/blob/master/CHANGES.md#401
[nanolru]: https://github.com/s3ththompson/nanolru
[core_d]: https://github.com/mantoni/core_d.js
[syntastic]: https://github.com/scrooloose/syntastic
[flycheck]: http://www.flycheck.org/
[SublimeLinter-eslint]: https://github.com/SublimeLinter/SublimeLinter-eslint
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "eslint_d",
"version": "13.1.2",
"description": "Makes eslint the fastest linter on the planet",
"description": "Speed up eslint to accelerate your development workflow",
"type": "module",
"bin": {
"eslint_d": "./bin/eslint_d.js"
Expand Down

0 comments on commit 7ce116f

Please sign in to comment.