npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

eslint-config-lostfictions

v7.0.0-alpha.7

Published

<p align="center"> <img height="50" src="logos.svg" /> </p>

Downloads

425

Readme

eslint-config-lostfictions

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~~ Vitest. (But use of Vitest 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.

    Admittedly this is less of a concern in the shiny new era of "flat" ESLint configs, whose new plugin resolution allows configs to bundle plugins without difficulty.

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!

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 (formerly) 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
# or
pnpm i -D eslint eslint-config-lostfictions

eslint-config-lostfictions bundles all its plugins and parsers as dependencies. ~~ESLint doesn't yet directly support bundling plugins in a config in this way, eslint-config-lostfictions also re-exports @rushstack/eslint-patch.~~ eslint-config-lostfictions is now based on ESLint 9, no ESLint patching needed!

eslint.config.mjs

export { default } from "eslint-config-lostfictions";

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

export { react as default } from "eslint-config-lostfictions";

Customizing the config

If you need to change enabled rules or configure linter settings, that looks something like this:

import { react } from "../index.js";

export default [
  ...react,
  {
    rules: {
      "comments/require-description": "off",
    },
  },
];

eslint-config-lostfictions also includes eslint-plugin-vitest 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 Vitest rules are enabled for files with a .test.{js,jsx,ts,tsx} suffix, as well as files under a __tests__ folder — which should match Jest's default rules for finding tests.


Additional info about specific rules

Warnings about Object.hasOwn(), Array#at() and String#at()

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

The respective functions they recommend are cleaner and less error-prone than their older alternatives. 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). Since eslint-config-lostfictions itself requires Node 18+ as of v7.0.0, these functions should be available in all environments where you'd use the config.

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-n'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.