> **alex** — Catch insensitive, inconsiderate writing.
[![Build][build-badge]][build]
[![Coverage][coverage-badge]][coverage]
[![First timers friendly][first-timers-badge]][first-timers]
Whether your own or someone else’s writing, **alex** helps you find gender
favoring, polarizing, race related, or other **unequal** phrasing in text.
For example, when `We’ve confirmed his identity` is given, **alex** will warn
you and suggest using `their` instead of `his`.
Give **alex** a spin on the [Online demo »][demo].
## Why
* [x] Helps to get better at considerate writing
* [x] Catches many possible offences
* [x] Suggests helpful alternatives
* [x] Reads plain text, HTML, MDX, or markdown as input
* [x] Stylish
## Install
Using [npm][] (with [Node.js][node]):
```sh
$ npm install alex --global
```
Using [yarn][]:
```sh
$ yarn global add alex
```
Or you can follow this step-by-step tutorial:
[Setting up alex in your project][setup-tutorial]
## Contents
* [Checks](#checks)
* [Integrations](#integrations)
* [Ignoring files](#ignoring-files)
* [`.alexignore`](#alexignore)
* [Control](#control)
* [Configuration](#configuration)
* [CLI](#cli)
* [API](#api)
* [`markdown(value, config)`](#markdownvalue-config)
* [`mdx(value, config)`](#mdxvalue-config)
* [`html(value, config)`](#htmlvalue-config)
* [`text(value, config)`](#textvalue-config)
* [Workflow](#workflow)
* [FAQ](#faq)
* [This is stupid!](#this-is-stupid)
* [alex didn’t check “X”!](#alex-didnt-check-x)
* [Why is this named alex?](#why-is-this-named-alex)
* [Further reading](#further-reading)
* [Contribute](#contribute)
* [Origin story](#origin-story)
* [Acknowledgments](#acknowledgments)
* [License](#license)
## Checks
**alex** checks things such as:
* Gendered work-titles (if you write `garbageman` alex suggests `garbage
collector`; if you write `landlord` alex suggests `proprietor`)
* Gendered proverbs (if you write `like a man` alex suggests `bravely`; if you
write `ladylike` alex suggests `courteous`)
* Ableist language (if you write `learning disabled` alex suggests `person
with learning disabilities`)
* Condescending language (if you write `obviously` or `everyone knows` alex
warns about it)
* Intolerant phrasing (if you write `master` and `slave` alex suggests
`primary` and `replica`)
* Profanities (if you write `butt` alex warns about it)
…and much more!
Note: alex assumes good intent: that you don’t mean to offend!
See [`retext-equality`][equality] and [`retext-profanities`][profanities] for
all rules.
**alex** ignores words meant literally, so `“he”`, `He — ...`, and [the
like][literals] are not warned about.
## Integrations
* Sublime — [`sindresorhus/SublimeLinter-contrib-alex`](https://github.com/sindresorhus/SublimeLinter-contrib-alex)
* Gulp — [`dustinspecker/gulp-alex`](https://github.com/dustinspecker/gulp-alex)
* Slack — [`keoghpe/alex-slack`](https://github.com/keoghpe/alex-slack)
* Ember — [`yohanmishkin/ember-cli-alex`](https://github.com/yohanmishkin/ember-cli-alex)
* Probot — [`swinton/linter-alex`](https://github.com/swinton/linter-alex)
* GitHub Actions — [`brown-ccv/alex-recommends`](https://github.com/marketplace/actions/alex-recommends)
* GitHub Actions (reviewdog) — [`reviewdog/action-alex`](https://github.com/marketplace/actions/run-alex-with-reviewdog)
* Vim — [`w0rp/ale`](https://github.com/w0rp/ale),
[`Freed-Wu/coc-alex`](https://github.com/Freed-Wu/coc-alex)
* Browser extension — [`skn0tt/alex-browser-extension`](https://github.com/skn0tt/alex-browser-extension)
* Contentful - [`stefanjudis/alex-js-contentful-ui-extension`](https://github.com/stefanjudis/alex-js-contentful-ui-extension)
* Figma - [`nickradford/figma-plugin-alex`](https://github.com/nickradford/figma-plugin-alex)
* VSCode - [`tlahmann/vscode-alex`](https://github.com/tlahmann/vscode-alex)
## Ignoring files
The CLI searches for files with a markdown or text extension when given
directories (so `$ alex .` will find `readme.md` and `path/to/file.txt`).
To prevent files from being found, create an [`.alexignore`][alexignore] file.
### `.alexignore`
The CLI will sometimes [search for files][ignoring-files].
To prevent files from being found, add a file named `.alexignore` in one of the
directories above the current working directory (the place you run `alex` from).
The format of these files is similar to [`.eslintignore`][eslintignore] (which
in turn is similar to `.gitignore` files).
For example, when working in `~/path/to/place`, the ignore file can be in
`to`, `place`, or `~`.
The ignore file for [this project itself][.alexignore] looks like this:
```txt
# `node_modules` is ignored by default.
example.md
```
## Control
Sometimes **alex** makes mistakes:
```markdown
A message for this sentence will pop up.
```
Yields:
```txt
readme.md
1:15-1:18 warning `pop` may be insensitive, use `parent` instead dad-mom retext-equality
⚠ 1 warning
```
HTML comments in Markdown can be used to ignore them:
```markdown
A message for this sentence will **not** pop up.
```
Yields:
```txt
readme.md: no issues found
```
`ignore` turns off messages for the thing after the comment (in this case, the
paragraph).
It’s also possible to turn off messages after a comment by using `disable`, and,
turn those messages back on using `enable`:
```markdown
A message for this sentence will **not** pop up.
A message for this sentence will also **not** pop up.
Yet another sentence where a message will **not** pop up.
A message for this sentence will pop up.
```
Yields:
```txt
readme.md
9:15-9:18 warning `pop` may be insensitive, use `parent` instead dad-mom retext-equality
⚠ 1 warning
```
Multiple messages can be controlled in one go:
```md
```
…and all messages can be controlled by omitting all rule identifiers:
```md
```
## Configuration
You can control **alex** through `.alexrc` configuration files:
```json
{
"allow": ["boogeyman-boogeywoman"]
}
```
…you can use YAML if the file is named `.alexrc.yml` or `.alexrc.yaml`:
```yml
allow:
- dad-mom
```
…you can also use JavaScript if the file is named `.alexrc.js`:
```js
// But making it random like this is a bad idea!
exports.profanitySureness = Math.floor(Math.random() * 3)
```
…and finally it is possible to use an `alex` field in `package.json`:
```txt
{
…
"alex": {
"noBinary": true
},
…
}
```
The `allow` field should be an array of rules or `undefined` (the default is
`undefined`). When provided, the rules specified are skipped and not reported.
The `deny` field should be an array of rules or `undefined` (the default is
`undefined`). When provided, *only* the rules specified are reported.
You cannot use both `allow` and `deny` at the same time.
The `noBinary` field should be a boolean (the default is `false`).
When turned on (`true`), pairs such as `he and she` and `garbageman or
garbagewoman` are seen as errors.
When turned off (`false`, the default), such pairs are okay.
The `profanitySureness` field is a number (the default is `0`).
We use [`cuss`][cuss], which has a dictionary of words that have a rating
between 0 and 2 of how likely it is that a word or phrase is a profanity (not
how “bad” it is):
| Rating | Use as a profanity | Use in clean text | Example |
| ------ | ------------------ | ----------------- | -------- |
| 2 | likely | unlikely | `asshat` |
| 1 | maybe | maybe | `addict` |
| 0 | unlikely | likely | `beaver` |
The `profanitySureness` field is the minimum rating (including) that you want to
check for.
If you set it to `1` (maybe) then it will warn for level `1` *and* `2` (likely)
profanities, but not for level `0` (unlikely).
## CLI
![][screenshot]
Let’s say `example.md` looks as follows:
```markdown
The boogeyman wrote all changes to the **master server**. Thus, the slaves
were read-only copies of master. But not to worry, he was a cripple.
```
Now, run **alex** on `example.md`:
```sh
$ alex example.md
```
Yields:
```
…
```
See `$ alex --help` for more information.
> When no input files are given to **alex**, it searches for files in the
> current directory, `doc`, and `docs`.
> If `--mdx` is given, it searches for `mdx` extensions.
> If `--html` is given, it searches for `htm` and `html` extensions.
> Otherwise, it searches for `txt`, `text`, `md`, `mkd`, `mkdn`, `mkdown`,
> `ron`, and `markdown` extensions.
## API
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c):
Node 14+ is needed to use it and it must be `import`ed instead of `require`d.
[npm][]:
```sh
$ npm install alex --save
```
This package exports the identifiers `markdown`, `mdx`, `html`, and `text`.
The default export is `markdown`.
### `markdown(value, config)`
Check Markdown (ignoring syntax).
###### Parameters
* `value` ([`VFile`][vfile] or `string`) — Markdown document
* `config` (`Object`, optional) — See the [Configuration][] section
###### Returns
[`VFile`][vfile].
You are probably interested in its [`messages`][vfile-message] property, as
shown in the example below, because it holds the possible violations.
###### Example
```js
import alex from 'alex'
alex('We’ve confirmed his identity.').messages
```
Yields:
```js
[
[1:17-1:20: `his` may be insensitive, when referring to a person, use `their`, `theirs`, `them` instead] {
message: '`his` may be insensitive, when referring to a ' +
'person, use `their`, `theirs`, `them` instead',
name: '1:17-1:20',
reason: '`his` may be insensitive, when referring to a ' +
'person, use `their`, `theirs`, `them` instead',
line: 1,
column: 17,
location: { start: [Object], end: [Object] },
source: 'retext-equality',
ruleId: 'her-him',
fatal: false,
actual: 'his',
expected: [ 'their', 'theirs', 'them' ]
}
]
```
### `mdx(value, config)`
Check [MDX][] (ignoring syntax).
> Note: the syntax for [MDX@2][mdx-next], while currently in beta, is used in
> alex.
###### Parameters
* `value` ([`VFile`][vfile] or `string`) — MDX document
* `config` (`Object`, optional) — See the [Configuration][] section
###### Returns
[`VFile`][vfile].
###### Example
```js
import {mdx} from 'alex'
mdx('He walked to class.').messages
```
Yields:
```js
[
[1:12-1:14: `He` may be insensitive, use `They`, `It` instead] {
reason: '`He` may be insensitive, use `They`, `It` instead',
line: 1,
column: 12,
location: { start: [Object], end: [Object] },
source: 'retext-equality',
ruleId: 'he-she',
fatal: false,
actual: 'He',
expected: [ 'They', 'It' ]
}
]
```
### `html(value, config)`
Check HTML (ignoring syntax).
###### Parameters
* `value` ([`VFile`][vfile] or `string`) — HTML document
* `config` (`Object`, optional) — See the [Configuration][] section
###### Returns
[`VFile`][vfile].
###### Example
```js
import {html} from 'alex'
html('
He walked to class.
').messages
```
Yields:
```js
[
[1:18-1:20: `He` may be insensitive, use `They`, `It` instead] {
message: '`He` may be insensitive, use `They`, `It` instead',
name: '1:18-1:20',
reason: '`He` may be insensitive, use `They`, `It` instead',
line: 1,
column: 18,
location: { start: [Object], end: [Object] },
source: 'retext-equality',
ruleId: 'he-she',
fatal: false,
actual: 'He',
expected: [ 'They', 'It' ]
}
]
```
### `text(value, config)`
Check plain text (as in, syntax is checked).
###### Parameters
* `value` ([`VFile`][vfile] or `string`) — Text document
* `config` (`Object`, optional) — See the [Configuration][] section
###### Returns
[`VFile`][vfile].
###### Example
```js
import {markdown, text} from 'ale