diff --git a/jshint/README.md b/jshint/README.md
new file mode 100644
index 0000000..7b43ed0
--- /dev/null
+++ b/jshint/README.md
@@ -0,0 +1,154 @@
+---
+title: JSHint
+homepage: https://jshint.com/about/
+tagline: |
+ JSHint: A Static Code Analysis Tool for JavaScript
+---
+
+To update or switch versions, run `npm install -g jshint@latest` (or `@v2`,
+etc).
+
+### Files
+
+These are the files / directories that are created and/or modified with this
+install:
+
+```txt
+~/.config/envman/PATH.env
+~/.local/opt/node
+~/.jshintrc.defaults.json5
+~/.jshintrc.webi.json5
+```
+
+## Cheat Sheet
+
+> JSHint is a community-driven tool that detects errors and potential problems
+> in JavaScript code. The project aims to help JavaScript developers write
+> complex programs without worrying about typos and language gotchas. -
+> [jshint.com/about/](https://jshint.com/about/)
+
+[vim-ale]: https://webinstall.dev/vim-ale
+[vs-code-jshint]:
+ https://marketplace.visualstudio.com/items?itemName=dbaeumer.jshint
+[jshint-cli]: https://jshint.com/docs/cli/
+[jshint-defaults]:
+ https://github.com/jshint/jshint/blob/master/examples/.jshintrc
+
+`jshint` works best when it's integrated with your editor - such as `vim` (with
+[vim-ale][vim-ale]) or [_VS Code_][vs-code-jshint]. However, you can also use it
+from the CLI.
+
+Here we'll cover how to:
+
+- set defaults
+- pick the best settings
+- check all files in a project
+- ignore certain file patterns
+- apply overrides for specific files
+
+Check out the [official docs][jshint-cli] at for
+more info.
+
+### How to set JSHint's defaults
+
+JSHint is meant to be configure _per-project_.
+
+You should put a `.jshintrc` in the root of the repository of each of your
+projects.
+
+You can copy our recommended settings into your project directory by running
+this command:
+
+```bash
+# convert from JSON5 (with comments) to JSON and copy into current directory
+sed -e ~/.jshintrc.webi.json5 \
+ 's://.*::g' \
+ > .jshintrc
+```
+
+The `.jshintrc` will be read by code tools such as _[`vim-ale`][vim-ale]_ and
+[_VS Code_][vs-code-jshint]
+
+### What are the best settings?
+
+The primary value of tools like JSHint is that they allow you to restrict what
+you use in the language from "everything that could every be useful" down to
+just "safe features that don't cause bugs".
+
+Given that, JSHint is perhaps a little too "flexible" - whereas its primary
+competitor (JSLint) is perhaps a little too inflexible - but if you follow that
+general methodology, you'll do well.
+
+These are the settings we think strike the right balance for _Software
+Engineering_ (as opposed to just _Code Monkey_-ing around):
+
+```json5
+// ~/.jshintrc.webi.json5
+// Recommended config from https://webinstall.dev/jshint
+//
+// To copy this file into your project without comments, run this:
+// sed -e ~/.jshintrc.webi.json5 's://.*::g' > .jshintrc
+
+{
+ browser: true,
+ node: true,
+ esversion: 11,
+ curly: true,
+ sub: true,
+
+ // More strict
+ bitwise: true,
+ eqeqeq: true,
+ forin: true,
+ freeze: true,
+ immed: true,
+ latedef: 'nofunc',
+ nonbsp: true,
+ nonew: true,
+ plusplus: true,
+ undef: true,
+ unused: 'vars',
+ strict: true,
+ maxdepth: 4,
+ maxstatements: 100,
+ maxcomplexity: 20
+}
+```
+
+That file is installed to `~/.jshintrc.webi.json5`, and should look pretty
+similar to the above, assuming that we've kept it in sync with this README.
+
+The list of JSHint's default options can be found here:
+
+
+### How to check project files with jshint
+
+Give `jshint` a list of files and/or directories to check `.js` files:
+
+```bash
+jshint ./
+```
+
+### How to make jshint ignore certain files
+
+Create a `.jshintignore` to tell JSHint which files to ignore every time
+
+```bash
+echo "dist/" >> .jshintignore
+```
+
+### How to apply different settings to different files
+
+You can use the `overrides` directive to specify different rules to apply to
+certain file patterns and directories.
+
+```json5
+{
+ esversion: 11,
+ overrides: {
+ './browser/*.js': {
+ esversion: 7
+ }
+ }
+}
+```
diff --git a/jshint/install.sh b/jshint/install.sh
new file mode 100644
index 0000000..87f0d84
--- /dev/null
+++ b/jshint/install.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+set -e
+set -u
+
+function __init_jshint() {
+ OLD_PATH="${PATH}"
+ PATH="${HOME}/.local/opt/node/bin:${PATH}"
+ if [ -z "$(npm --version 2> /dev/null)" ]; then
+ export PATH="${OLD_PATH}"
+ webi node
+ export PATH="${HOME}/.local/opt/node/bin:${PATH}"
+ fi
+ npm install -g jshint@latest
+
+ curl -fsS \
+ -o ~/.jshintrc.defaults.json5 \
+ 'https://raw.githubusercontent.com/jshint/jshint/master/examples/.jshintrc' || true
+
+ curl -fsS \
+ -o ~/.jshintrc.webi.json5 \
+ "${WEBI_HOST}/packages/jshint/jshintrc.webi.json5" || true
+}
+
+__init_jshint
diff --git a/jshint/jshintrc.defaults.json5 b/jshint/jshintrc.defaults.json5
new file mode 100644
index 0000000..944daed
--- /dev/null
+++ b/jshint/jshintrc.defaults.json5
@@ -0,0 +1,94 @@
+// 2021-09-11
+// https://github.com/jshint/jshint/blob/master/examples/.jshintrc
+{
+ // JSHint Default Configuration File (as on JSHint website)
+ // See http://jshint.com/docs/ for more details
+
+ "maxerr" : 50, // {int} Maximum error before stopping
+
+ // Enforcing
+ "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
+ "camelcase" : false, // true: Identifiers must be in camelCase
+ "curly" : true, // true: Require {} for every new block or scope
+ "eqeqeq" : true, // true: Require triple equals (===) for comparison
+ "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
+ "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
+ "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
+ "latedef" : false, // true: Require variables/functions to be defined before being used
+ "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
+ "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
+ "noempty" : true, // true: Prohibit use of empty blocks
+ "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
+ "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
+ "plusplus" : false, // true: Prohibit use of `++` and `--`
+ "quotmark" : false, // Quotation mark consistency:
+ // false : do nothing (default)
+ // true : ensure whatever is used is consistent
+ // "single" : require single quotes
+ // "double" : require double quotes
+ "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
+ "unused" : true, // Unused variables:
+ // true : all variables, last function parameter
+ // "vars" : all variables only
+ // "strict" : all variables, all function parameters
+ "strict" : true, // true: Requires all functions run in ES5 Strict Mode
+ "maxparams" : false, // {int} Max number of formal params allowed per function
+ "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
+ "maxstatements" : false, // {int} Max number statements per function
+ "maxcomplexity" : false, // {int} Max cyclomatic complexity per function
+ "maxlen" : false, // {int} Max number of characters per line
+ "varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed.
+
+ // Relaxing
+ "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
+ "boss" : false, // true: Tolerate assignments where comparisons would be expected
+ "debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
+ "eqnull" : false, // true: Tolerate use of `== null`
+ "esversion" : 5, // {int} Specify the ECMAScript version to which the code must adhere.
+ "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
+ // (ex: `for each`, multiple try/catch, function expression…)
+ "evil" : false, // true: Tolerate use of `eval` and `new Function()`
+ "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
+ "funcscope" : false, // true: Tolerate defining variables inside control statements
+ "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
+ "iterator" : false, // true: Tolerate using the `__iterator__` property
+ "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
+ "laxbreak" : false, // true: Tolerate possibly unsafe line breakings
+ "laxcomma" : false, // true: Tolerate comma-first style coding
+ "loopfunc" : false, // true: Tolerate functions being defined in loops
+ "multistr" : false, // true: Tolerate multi-line strings
+ "noyield" : false, // true: Tolerate generator functions with no yield statement in them.
+ "notypeof" : false, // true: Tolerate invalid typeof operator values
+ "proto" : false, // true: Tolerate using the `__proto__` property
+ "scripturl" : false, // true: Tolerate script-targeted URLs
+ "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
+ "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
+ "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
+ "validthis" : false, // true: Tolerate using this in a non-constructor function
+
+ // Environments
+ "browser" : true, // Web Browser (window, document, etc)
+ "browserify" : false, // Browserify (node.js code in the browser)
+ "couch" : false, // CouchDB
+ "devel" : true, // Development/debugging (alert, confirm, etc)
+ "dojo" : false, // Dojo Toolkit
+ "jasmine" : false, // Jasmine
+ "jquery" : false, // jQuery
+ "mocha" : true, // Mocha
+ "mootools" : false, // MooTools
+ "node" : false, // Node.js
+ "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
+ "phantom" : false, // PhantomJS
+ "prototypejs" : false, // Prototype and Scriptaculous
+ "qunit" : false, // QUnit
+ "rhino" : false, // Rhino
+ "shelljs" : false, // ShellJS
+ "typed" : false, // Globals for typed array constructions
+ "worker" : false, // Web Workers
+ "wsh" : false, // Windows Scripting Host
+ "yui" : false, // Yahoo User Interface
+
+ // Custom Globals
+ "globals" : {} // additional predefined global variables
+}
+
diff --git a/jshint/jshintrc.webi.json5 b/jshint/jshintrc.webi.json5
new file mode 100644
index 0000000..b826f13
--- /dev/null
+++ b/jshint/jshintrc.webi.json5
@@ -0,0 +1,30 @@
+// ~/.jshintrc.webi.json5
+// Recommended config from https://webinstall.dev/jshint
+//
+// To copy this file into your project without comments, run this:
+// sed -e ~/.jshintrc.webi.json5 's://.*::g' > .jshintrc
+
+{
+ "browser": true,
+ "node": true,
+ "esversion": 11,
+ "curly": true,
+ "sub": true,
+
+ // More strict
+ "bitwise": true,
+ "eqeqeq": true,
+ "forin": true,
+ "freeze": true,
+ "immed": true,
+ "latedef": "nofunc",
+ "nonbsp": true,
+ "nonew": true,
+ "plusplus": true,
+ "undef": true,
+ "unused": "vars",
+ "strict": true,
+ "maxdepth": 4,
+ "maxstatements": 100,
+ "maxcomplexity": 20
+}