Socket
Socket
Sign inDemoInstall

eslint-config-lostfictions

Package Overview
Dependencies
15
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    eslint-config-lostfictions


Version published
Weekly downloads
9
increased by200%
Maintainers
1
Created
Weekly downloads
 

Readme

Source

eslint-config-lostfictions

NPM version

eslint-config-lostfictions is a shareable, opinionated, batteries-included configuration for ESLint.

Features

  • Intended for use with TypeScript, using typescript-eslint's parser and type-aware rules. (Also supports JavaScript in projects with a tsconfig.json configured.)
  • Includes an alternate React config that adds additional rules from eslint-plugin-react and eslint-plugin-react-hooks to catch errors and uphold best practices in React code.
  • Delegates all formatting to Prettier. No more noisy warnings in your editor shouting that you forgot to indent your code!
  • Includes rules for best practices when writing tests with Jest. (But use of Jest is not required to use this config!)
  • Prefers warnings to errors for most lints, which helps distinguish between genuine problems (like TypeScript typechecking errors) and lesser code smells. (Read more.)
  • Deviations and disabled rules relative to base ESLint, React and Typescript recommended configs are documented and include a rationale.
  • Adds warnings about deprecations for both internal and external code (by checking JSDoc annotations), best practices around imports, Node.js-specific lints, and select additional rules from the wonderful eslint-plugin-unicorn.
  • Batteries included: just add ESLint and this package to your existing project and start linting! Unlike many other shareable configs, eslint-config-lostfictions doesn't declare any peerDependencies besides ESLint and TypeScript, so there's no extra ESLint plugins and parsers cluttering your package.json that need to be audited for compatibility on version bumps. (See below for why this is now possible.)

Read on for the rationale, or jump to the Usage section below to get started.

Why?

TypeScript and ESLint might seem like they're used for the exact same thing — catching errors in JavaScript — but they have different, complementary uses. TypeScript generally limits itself to typechecking, while ESLint can catch a broad variety of other errors, from simple oversights like expressions that do nothing or comparing a value to itself, to technically legal but dangerous syntax, to analysis of possible race conditions. The typescript-eslint project not only enables ESLint to parse and validate TypeScript code directly, it adds support for a wide range of TypeScript-specific lints and error-checking not covered by the TypeScript compiler. In fact, the TypeScript developers use ESLint and typescript-eslint on their own codebase! (Another tool called TSLint formerly fulfilled a similar role for TypeScript, but it's been deprecated for a while now in favour of ESLint.)

eslint-config-lostfictions is based on a few key principles:

Linters are for linting.

After untold numbers of code reviews nitpicking whitespace or semicolons, programmers are finally coming around to the idea that it's usually easier to let the computer fix those things for you. A consistent team-wide code style helps to improve readability and avoid bikeshedding that arises from minor differences in opinion, but time and experience have shown that the ideal way to enforce style is via editor tooling and runnning checks in continuous integration. (Who cares about "tabs versus spaces" if you can treat leading whitespace as tabstops on your machine but save them to disk as spaces?)

Languages like Go and Rust make things easy here by integrating opinionated formatters in their toolchain. In JavaScript-land, there are two main options for formatting: ESLint and Prettier. Both are viable, but I've found that Prettier is much easier to configure, more consistent, and allows for a better separation of concerns.

(For example, when saving a file in your editor you may want to automatically reformat your code, but you may not want to simultaneously "auto-fix" other linter warnings, since these "fixes" occasionally change the semantics of your code or erase some intermediate work you've done. This is a lot harder when using ESLint as both a formatter and linter. ESLint also tends to give you angry messages if a line of text is too long or it detects some other whitespace or formatting issue, which can mask other more important lints.)

eslint-config-lostfictions assumes that you're using Prettier. All ESLint whitespace and formatting rules are turned off via eslint-config-prettier.

Red is for errors.

Some popular ESLint configs like to scream at you at maximum volume for every possible variety of included lint. Again, in my experience, this can result in a lot of noise that masks other more important problems, such as typechecker failures, semantic issues, and parsing errors.

By contrast, eslint-config-lostfictions tries to reserve the "error" lint level for genuine suspected errors and prefers warnings for lesser code smells and minor issues.

For example, no-lonely-if is a warning. A lonely if may be somewhat unidiomatic JavaScript, but by itself it does not represent a semantic issue. On the other hand, no-self-compare is an error. There is almost no imaginable circumstance where you would want to compare a value to itself, so it very likely represents an oversight that will lead to unintended behaviour in your code.

All that said, for the same reason you should use a formatter, it's a good idea to treat warnings as errors in your CI to ensure warnings get fixed before a pull request can be merged. You can use the ESLint CLI option --max-warnings=0 to enforce this.


Usage

npm i -D eslint eslint-config-lostfictions
# or
yarn add -D eslint eslint-config-lostfictions

eslint-config-lostfictions bundles all its required plugins and parsers as dependencies. Since ESLint doesn't yet directly support bundling plugins in a config in this way (the feature request for it is by far the most upvoted open issue in the ESLint repo), eslint-config-lostfictions also re-exports @rushstack/eslint-patch. You'll need to require it in your ESLint config like this:

.eslintrc.js
require("eslint-config-lostfictions/patch");

/** @type {import("eslint-config-lostfictions").Config} */
module.exports = {
  extends: ["lostfictions"],
  parserOptions: { tsconfigRootDir: __dirname },
};

For React projects, use the lostfictions/react config, which adds additional React-specific plugins and rules:

require("eslint-config-lostfictions/patch");

/** @type {import("eslint-config-lostfictions").Config} */
module.exports = {
---  extends: ["lostfictions"],
+++  extends: ["lostfictions/react"],
  parserOptions: { tsconfigRootDir: __dirname }
};

Since we're requireing a module in the config, this obviously means you need to use the .eslintrc.js config format rather than JSON, YAML, etc.

eslint-config-lostfictions also includes eslint-plugin-jest for linting your tests (including helpful rules like expect-expect, which ensures you haven't forgotten to make assertions in your test cases). By default the extra Jest rules are enabled for files with a .test.{js,jsx,ts,tsx} suffix, as well as files under a test folder — which should match Jest's default rules for finding tests.


IDE warnings about unlintable files

If you're using an editor integration like vscode-eslint, typescript-eslint may complain about any files not listed in your tsconfig.json being unlintable (including .config.js files, etc). This is not the fault of eslint-config-lostfictions; it's a quirk of typescript-eslint's type-aware linting. There are a few solutions to this:

  • You can safely ignore these warnings and (at least in VS Code) they'll go away when you close the file. Often you don't care too much about linting a config file in your project root.

  • If you don't want to lint the files in question but are annoyed by the warning, add them to a .eslintignore file, to an ignorePatterns field in .eslintrc.js, or via any other method ESLint offers for ignoring code. (eslint-config-lostfictions already excludes .eslintrc.js itself from linting.)

  • If you do want to lint these files, add them to your tsconfig.json via the include or files field. If you do this, note that TypeScript may include these files for transpilation if you're using TypeScript as a transpiler rather than purely as a typechecker (typecheck-only mode is typical for Babel/SWC/esbuild-based setups, including create-react-app and Next.js).

    If transpiling them isn't what you want, you may need to introduce an additional ESLint-specific tsconfig.json to your project. It can be named something like tsconfig.eslint.json and can inherit from your existing tsconfig.json. You should only need to additionally specify noEmit: true and the extra files to lint. You can then point the typescript-eslint parser at the new tsconfig file via the project field under parserOptions in your .eslintrc.js.

See typescript-eslint's documentation for further explanation about this warning and example configurations that fix it.

Additional info about specific rules

Warnings about Array#at() and String#at()

unicorn/prefer-at and prefer-object-has-own are both enabled in this config.

(EDIT: Object.hasOwn doesn't have support yet in TypeScript's lib.d.ts, so we're waiting for that. See the tracking issue.)

The respective functions they recommend are cleaner and less error-prone than their older alternatives, but they're both pretty fresh at the moment. Object.hasOwn() shipped in Node 16.9.0 (2021-09-07). String#at() and Array#at() shipped in Node 16.6.0 (2021-07-29). If you're stuck on an older version of Node, you may prefer to disable these rules. These functions have shipped in all evergreen browsers and should be polyfilled by frontend tools that incorporate core-js polyfills (Next.js, CRA) if your browserslist config indicates that support is required.

The in operator

The in operator has a number of pitfalls that can make it tricky to use. Using an object with arbitrary string keys can be a code smell if there's any possibility the keys are user-provided — this can be a source of prototype poisoning, and even solutions like Object.create(null) aren't foolproof (and create new pitfalls of their own). For this reason, it's recommended to use a Map or Set when working with arbitrary keys.

However, the in operator is often more ergonomic than the more "correct" alternatives and works as a type guard in TypeScript where other forms of membership checking do not. For example, given this type declaration:

type XorY = { x: string } | { y: number };
let thing: XorY;

This code will typecheck correctly:

if ("x" in thing) {
  // narrowed to { x: string } in this block
  console.log(thing.x);
} else {
  // narrowed to { y: number } in this block
  console.log(thing.y + 3);
}

But this will not:

if (Object.prototype.hasOwnProperty.call(thing, "x")) {
  // ERROR: Property 'x' does not exist on type 'XorY'.
  // Property 'x' does not exist on type '{ y: number; }'.
  console.log(thing.x);
}

For these reasons, eslint-config-lostfictions warns when using the in operator, unless the left-hand operand is a string literal. This should catch a majority of code-smell cases while still permitting the relatively safer case of using in to narrow a union of TypeScript types.

Keep in mind that the preferred way to narrow non-literal union types is the use of a discriminant property. (For example, the kind property in type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number } is a discriminant.) Discriminants sidestep the need to reach for something like in in the first place.

Warnings about process.env

As noted in eslint-plugin-node's no-process-env rule, Node's process.env is effectively a kind of global mutable variable. But even if you don't mutate it, spreading it around your codebase can make it difficult to reason about your application's configuration. Instead, it's recommended to declare and validate all your configuration up-front in a single file, and then export those validated bindings for use across your application. (Something like znv could help you with this.)

no-process-env is thus enabled by default, with the expectation that you'll disable it in the file that handles your app configuration. This might be a bit noisy for brownfield projects that don't already follow the single-point-of-declaration convention, but in my experience it's worth it to clean up those stray uses of process.env.

for-of vs. forEach()

forEach() methods — mainly Array#forEach(), though equivalents also exist for Maps, Sets, TypedArrays, and some "array-like" DOM entities — are a holdover from an earlier age of JavaScript. Before the block-scoped declarations let and const became widely supported, iteration blocks were either clunky or risky to use. Even a simple for(var i = 0; i < x; i++) often needed its body wrapped in an IIFE. In these dire circumstances, forEach() emerged as a more elegant and humane alternative for iteration.

Fortunately, things are better for JavaScript these days, and for-of loops are generally a simpler and more readable construct. Every built-in with a forEach() method also supports iteration via for-of.

Digging a bit deeper, forEach() has some issues in that it's imperative but looks functional; it can sometimes be tempting to add it at the end of a chain of array .map() and .filter() calls. Unfortunately, the resulting code is generally the worst of both worlds, with none of the benefits of truly functional code but all of the drawbacks.

In this config, the rule forbidding forEach() comes by way of eslint-plugin-unicorn, but Github's ESLint docs about forEach() go into more detail and outline some further compelling reasons to avoid it.

FAQs

Last updated on 14 Dec 2023

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc