Attention TypeScript Developers: Are You Linting Your Files All Wrong?
Transform Your TypeScript Projects Overnight with This Mind-Blowing Linting Secret!
Do you lint your TypeScript code?
If you have worked in a large-scale React/Node TypeScript project, you’ve likely had to follow the linting rules defined in your project. For example, I’m sure you’ve run into linting errors like this:
Sometimes, you run into these errors even though you want to define your code using such syntax for a specific set of files. For example, I’ve used Snake Case to define types for a React Context when my ESLint configuration requires Type definitions in Pascal Case. However, if you navigate the Redux documentation, you’ll see a best practice recommending using Snake Case to define types for a particular Action.
But, when working in a monolithic environment such as a single React or Node.js application, you’re bound to a single linting configuration. In this case, I was bound to a single ESLint configuration as all the files I managed belonged to a single repository. This approach works fine when you’re adhering to your linting config. However, there can be a time when you define a file that does not comply with a linting standard, just like the example we discussed earlier.
In these cases, you’re left with the following options:
- You can turn off certain linting rules in your ESLint configuration file. However, by doing so, you turn it off globally across your project.
- You can turn off the linting rule by adding
@ts-ignore
oreslint-disable-<<rule>>
onto your TypeScript file. However, by doing so, you add unnecessary lines of code onto your file, hindering readability and maintainability. - Finally, you can add an
overrides
array in your ESLint configuration and specify a set of rules that are applied only to a specific subset of files, as shown below:
{
"extends": "eslint:recommended",
"rules": {
// Your default rules
},
"overrides": [
{
"files": ["*.test.js", "*.spec.js"],
"rules": {
"no-unused-expressions": "off"
}
}
]
}
However, as you can see, you have to manually update the files
array and specify the files to which you want the rules to apply. This can be hard to maintain as there are chances where you forget to add the file to this array, or it can even become more burdensome to read if you add around 200 entries to this array.
So, this problem ultimately leads to poor code being shipped to production while hindering developer productivity. Rather than a global ESLint configuration, we require a solution that lets us maintain a single ESLint configuration per TypeScript file.
And that’s exactly what tools like Bit let us achieve.
Independent Component Development with Bit
Think of Bit as a build system for composable software.
You can design and build anything in terms of simple modular components that are completely independent of each other. It does this by ensuring that a single component has its own isolated runtime (environment) that lets you design, develop, test, and build a component in complete isolation. If this sounds confusing, look at the diagram below:
The diagram above showcases four components, each having its own version. All of these components were designed, and built in complete isolation. Doing so lets us handle each component’s configuration information independently.
Yes, you thought right. You can use independent linting configurations per Bit component.
So, during build time, each component would be linted using its linting configuration. This lets you address all the issues we discussed at the start without any additional configuration!
Building Custom With Custom ESLint Configurations with Bit
Well, how do we do this?
It’s easy! In fact, it’ll take less than 5 minutes to create a component with a custom ESLint configuration.
Step 01: Pre-requisites
First things first. You’ll need to install Bit. You can do this by running the command:
npx @teambit/bvm install
To confirm the installation, run the command bit --version
and you should see the output I've shown below.
Step 02: Initializing an empty workspace
After that, you’ll need to set up a workspace in Bit. This lets you build your components locally and work on them! What’s interesting about this is that a Bit workspace does not have any runtime built into it. It’s not the same thing as running npx create-react-app
. Instead, you must utilize Bit Environment Components to provide a runtime for your component.
Yes, every Bit component has an environment component
As you can see above, all three React components use the my-react-env
component that provides the React runtime for each component to work. Okay, now that we have a basic understanding of an environment, let's create the Bit workspace using the command:
bit init
After you run the command, you should see the following files:
Step 03: Creating a reusable ESLint Component
Next, we’ll have to create a reusable linter component that we can apply to one or group of Bit components. This component will hold an instance of ESLint that we can directly apply to a set of components or simply one!
To create a Linter component, run the commands:
bit install @teambit/defender.eslint-linter
This will install the Linter libraries onto your workspace so that you can start configuring it. Next, you’ll need to create a custom environment. In this demo, let’s create a custom React environment:
bit fork teambit.react/react-env
This will create a component as shown:
As you can see, your React Environment has a config
directory that holds the eslintrc.js
file. In this file, you can configure all the ESLint configurations you want running in the group of components you're using the environment with. For example, let's include the linting configuration:
// force bit to recognize this as dependency
require('@teambit/react.eslint-config-bit-react');
module.exports = {
extends: [require.resolve('@teambit/react.eslint-config-bit-react')],
settings: {
'mdx/code-blocks': true,
jest: {
version: 29,
},
react: {
version: '18.0',
},
},
rules: {
'no-console': 'off'
},
};
As show above, we’re extending the pre-build ESLint configuration and disabling error flag that ESLint throws for the usage of console.log()
.
To test this rule out, let’s create a React component that uses the environment we just created.
Step 04: Creating a React Component
To create the React component that uses the custom env, run the command:
bit create react sample/button --env react-env
Next, let’s open the button.tsx
file and include a console.log
to test out the linter:
import type { ReactNode } from 'react';
export type ButtonProps = {
children?: ReactNode;
};
export function Button({ children }: ButtonProps) {
console.log('Hello');
return (
<div>
{children}
</div>
);
}
Next, lint your component using the command:
bit lint sample/button
You’ll notice that there’s no error popping up:
Next, go ahead and uncomment the rule and re-lint your component. You should see the error pop up:
This showcases that our custom ESLint configuration is now up and running. Likewise, you can customize this as you need and use environments to isolate your components that use different linting configurations.
Step 05 — Customizing Component Environment Files In an Independent Space
Okay, now, after you’ve created a custom base ESLint for all your components, you’ll likely want to proceed and have custom ESLint variations per component, right? I mean, that’s what we’re trying to solve?
Let’s create another React component:
bit create react sample/typography --env react-env
Afterward, run the following command:
bit ws-config write
This command will create ESLint configurations for all components in your workspace:
Figure: Viewing the generated Linter configuration
So, what this means is that you now have the capability of customizing ESLint configurations on a per-component basis.
How independent is that?
Okay, now, let’s add a rule that will only apply to this Typography component:
// bit-generated-eslint-config
{
"extends": [
"./../../../node_modules/.cache/.eslintrc.bit.586ffdd814fb4c3cfe69ed772f82ab3aa4bb7382.json"
],
"rules": {
"no-alert": "off",
}
}
Let’s update the typography.tsx
to add an alert:
import type { ReactNode } from 'react';
export type TypographyProps = {
children?: ReactNode;
};
export function Typography({ children }: TypographyProps) {
alert('Hello');
return (
<div>
{children}
</div>
);
}
Next, let’s lint the component using:
bit lint sample/typography
You’ll see the output below:
Wrapping up
Well, there we have it!
It didn’t even take 5 minutes to set up a custom linter and adapt it to independent components. Likewise, you can manage your components with independent ESLint configurations and ensure your development productivity is as optimal as ever!
If you want to explore the code we built, check out my Bit Cloud Scope!
Thank you for reading.