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-nimbus-tech

v2.0.6

Published

Welcome to the config! All Nimbus Tech projects should use this eslint config to ensure a consistent coding style.

Downloads

479

Readme

Nimbus Tech Flat ESLint Config

Welcome to the config! All Nimbus Tech projects should use this eslint config to ensure a consistent coding style.

How to add it your project

Install it via:

yarn add -D eslint-config-nimbus-tech@latest

Note: You don't need eslint as a dependency because it's already included.

And then add it to your eslint.config.mjs file:

import nimbusConfig from 'eslint-config-nimbus-tech';

export default [
  ...nimbusConfig,
  {
    /** your custom config goes here **/
  },
];

Then you can lint via

ESLINT_USE_FLAT_CONFIG=true eslint -c eslint.config.mjs .

We use the .mjs extension so that we can use modules regardless of the "type" property in our package.json file.

Setup with VS Code

Add a .vscode directory inside of the project if you don't have one already.

Then add a settings.json file if you don't have one already.

Inside of it, add the following:

{
  "eslint.experimental.useFlatConfig": true,
  "eslint.options": {
    "overrideConfigFile": "eslint.config.mjs"
  }
}

Note: You need to have the VS Code Eslint plugin for this to be relevant.

Next.js project example

import nimbusTechConfig from 'eslint-config-nimbus-tech';
import nextPlugin from '@next/eslint-plugin-next';
import reactPlugin from 'eslint-plugin-react';
import hooksPlugin from 'eslint-plugin-react-hooks';

export default [
  {
    plugins: {
      react: reactPlugin,
    },
    rules: {
      ...reactPlugin.configs['jsx-runtime'].rules,
    },
    settings: {
      react: {
        version: 'detect',
      },
    },
  },
  {
    plugins: {
      'react-hooks': hooksPlugin,
    },
    rules: hooksPlugin.configs.recommended.rules,
  },
  {
    plugins: {
      '@next/next': nextPlugin,
    },
    rules: {
      ...nextPlugin.configs.recommended.rules,
      ...nextPlugin.configs['core-web-vitals'].rules,
      '@next/next/no-img-element': 'error',
    },
  },
  ...nimbusTechConfig,
  {
    ignores: ['.next/*', '*.cjs'],
  },
];

You need to install @next/eslint-plugin-next, eslint-plugin-react and eslint-plugin-react-hooks as dev dependencies.

Node.js with jest projects

import nimbusConfig from 'eslint-config-nimbus-tech';
import jestPlugin from 'eslint-plugin-jest';

export default [
    {
        plugins: {
            jest: jestPlugin,
        },
        rules: {
            ...jestPlugin.configs.recommended.rules,
        },
    },
    ...nimbusConfig,
    {
        ignores: ['**/dist/*'],
    },
];

You need to install eslint-plugin-jest as dev dependency.

Monorepo projects

Place the file in the root of the directory, and then define a lint command for each of the submodules like this

"lint": "ESLINT_USE_FLAT_CONFIG=true eslint -c ./eslint.config.mjs .",

And in the root package.json, have a command that runs all the lint commands of the submodules.

export default [
  ...nimbusConfig,
  {
    ignores: ['**/.turbo/*', '**/cdk.out/*'],
  },
];

How to add new rules?

Open a pull request with new updates and wait for a new version to be released.

For more info, contact NimbusTech.

Conventions

Interface names

Interfaces should not be prefixed by an I since they are the ones that should be imported by most consumers, therefore, their names should be natural.

Let's say we have a user repository: The interface could be named UserRepository, and the implementing class could be a TypeOrmUserRepository. The fact that we're using TypeORM as the underlying implementation for our repository is something that should be irrelevant to the consumers, they should only use UserRepository.

In conclusion, the name of the interface should be about what contracts its providing (i.e. handling User data access), and the name of the implementing class should signify what implementation details we are using in this particular implementation.

Enum names

Enums are basically spruced up objects that define constants. However, since they are a construct such as classes, it makes sense to give them PascalCase names. Since the values inside of the enum are constants, it makes sense to give them UPPER_CASE names, just like you would name constants outside of enums. An example:

enum ExampleEnum {
  EXAMPLE_VALUE,
  ANOTHER_EXAMPLE_VALUE,
}

Usage of any

Philosophy

Using any is dangerous and detrimental in the long run. What helps you today will come back to bite you later down the road.

Keeping the code typesafe should come down to checking the edges of your application, in other words:

  • outputs from untyped or badly typed libraries
  • outputs from external services

These two edges should be, ideally, unit and/or integration tested and made sure that they conform to the interfaces that have been defined for them.

Keeping the code typesafe inside of the app is then trivial, because you always can expect all the types to be exactly what they state to be, since they are either:

  • external and we have covered them with the above
  • internal and in our absolute control

Alternatives

Use unknown. If you get a type error for an unknown, that means that, when using any, you would be completely unsafely accessing a property of an object that you have no idea whether it exists or not.

If you get a type error for an unknown, that means that it's time to look up or define a type. Since TypeScript supports duck typing, you can at least define an interface with the set of fields you are using until you find a better type or you don't start maintaining your type more seriously and you move it out into its own file.

Example

Using any

function convertBackendResponseToUiModel(response: any) {
  return {
    // You have no idea whether this exists
    // or not without running the code.
    // You have no way of defending
    // yourself against this type
    // changing in the future other
    // than running the app and it
    // failing in runtime
    name: response.user.name
  }
}

Using unknown

function convertBackendResponseToUiModel(response: unknown) {
  return {
    // Using unknown here throws an
    // error, and rightfully so
    // You said that you have no
    // idea what the type is, and yet
    // you're accessing fields on
    // it - how can you be sure they exist?
    name: response.user.name
  }
}

So, if no other type exists, you can at least do this.


interface Response {
  user: {
    name: string | undefined;
  }
}

function convertBackendResponseToUiModel(response: Response) {
  return {
    // This now works, although it
    // still misses tests to
    // confirm behavior
    name: response.user.name
  }
}

We should still be able to define any in extreme situations, by disabling the eslint rule for that line and leaving a comment as to why we defined the type that way.

You'll notice that most of the time the comments won't be as justfied as just finding out a type or making one.

Do keep in mind that if you have a lot of instances in your code where you're not sure what type something is - that means that the boundary isolation is not well-defined. Insecure types should be pushed out to the edges of the code, making things such as these a less frequent occurence.

Using type predicates

You can infer types from unknown or any values by using type predicates. For example:

interface Response {
  veryImportantData: string;
}

/**
 * Checkes whether the object passed
 * in is not null, then whether it's
 * an object and then whether it has
 * a property called `veryImportantData`
 */
function isResponse(someObject: unknown): someObject is Response {
  return !!someObject && typeof someObject === 'object' && 'veryImportantData' in someObject;
}

Later in code, you can do:

const someFunction = () => {
  // untyped API
  const unknownResponse = getData();

  if (isResponse(unknownResponse)) {
    // unknownResponse now has
    // the type of Response and
    // you can be sure that it contains
    // the field `veryImportantData`
  }
};