How to set up reusable ESLint configs for SharePoint Framework (SPFx) projects

When Microsoft swapped out TSLint in favor of ESLint in SPFx v1.15, I think they went overboard with their default rules. In this episode, learn how to set up a reusable ESLint configuration & modify your SharePoint Framework projects to use your rules.

By Last Updated: November 2, 2024 5 minutes read

In a recent post, I talked about some of the challenges developers ran into with ESLint rules that Microsoft introduced in the SharePoint Framework v1.15 release. That video covered some common rules you’re likely to run into and how to address the warning messages.

I also showed how you can selectively disable rules within files, blocks of code, on individual lines, or how you can make global changes to a project in the ESLint configuration file.

But, you have to make these changes in every single project. What if you want to have a single set of ESLint rules for your own coding style that are applied to every SPFx project?

Or better yet, what if you need to follow your company’s coding style guide? Or what if you’re a consultant with multiple clients, each having their own coding style guides?

Making these changes to each SPFx project is a pain and error prone. Wouldn’t it be cool if there was a better way?

There is… and in this post I’ll show you how!

Before I explain & demonstrate my solution, let me first restate an opinion I expressed in my last post.

I don’t think a vendor should be defaulting projects a lot of opinionated coding style rules. That’s not their place. It forces individual developers, consultants, or in-house development teams to modify every single project to follow their established coding style guide.

Andrew Connell
Andrew Connell
Microsoft MVP, Full-Stack Developer & Chief Course Artisan - Voitanos LLC.

So, what I’m going to show you is something I proposed as a solution to address this challenge.

GitHub issue #8290: SPFx v1.15+ Consider dialing back ESLint rules

GitHub issue #8290: SPFx v1.15+ Consider dialing back ESLint rules

I encourage you to check the proposal and offer your reaction or comments as Microsoft is monitoring feedback for possible changes to future SPFx releases.

Creating reusable ESLint configurations shared across projects

This proposal is inspired by how the SPFx build toolchain enables developers to modify the webpack configuration.

Here’s how it works: let’s say the eslintrc.js file is changed to look for the presence of a file in well-known path. If that file exists, it calls an expected method and passes in the ESLint configuration. That method returns a new configuration. If the file doesn’t exist, it uses the default SPFx configuration.

Let’s see this in action…

Convert .eslintrc.js to be dynamic

The default .eslintrc.js file pulls in a library and sets the configuration as shown in the following code. All the rules are being set by a nested hierarchy of dependencies buried in the node_modules folder:

require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
  extends: ['@microsoft/eslint-config-spfx/lib/profiles/default'],
  parserOptions: { tsconfigRootDir: __dirname }
};

I proposed to Microsoft to change the default eslintrc.js file added to new SPFx projects to the following. This does the following things:

  • instead of exporting their configuration, we store it in a temp variable defaultEslintConfig
  • check to see if a well-known file, .spfx-eslintrc.js, exists on the user’s machine in their home directory
  • if that file exists, call that files exported method mergeEslintConfig and export the returned value back
  • otherwise if the file doesn’t exist, export the default configuration
require('@rushstack/eslint-config/patch/modern-module-resolution');
const defaultEslintConfig = {
  extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
  parserOptions: { tsconfigRootDir: __dirname }
};

const fs = require('fs');
const os = require('os');
const path = require('path');
const userEslintConfig = path.join(os.homedir(),'.spfx-eslintrc.js');
module.exports = (fs.existsSync(userEslintConfig))
  // user-defined config
  ? require(userEslintConfig).mergeEslintConfig(defaultEslintConfig)
  // Microsoft default esLintConfig
  : defaultEslintConfig;

Let’s see how that impacts a project. I have an SPFx v1.15 project that contains a web part that uses React. I’ve got a method that uses the async keyword and uses an arrow function in a React property. Both of these trigger ESLint warning messages in the default settings:

Linting warnings using the default SPFx ESLint configuration when your project uses the `async` keyword

Linting warnings using the default SPFx ESLint configuration when your project uses the `async` keyword

Linting warnings using the default SPFx ESLint configuration when your project uses the async keyword

I don’t agree with those warning messages. The async keyword is perfectly fine to use as it never touches the browser… the JavaScript generated by the TypeScript compiler doesn’t include that keyword, so that rule is irrelevant.

So… let’s say I want to override the rules that come with every SPFx project to turn this warning off across all my projects.

Define your own global rules in your profile

To do this, I’ll create a new file, .spfx-eslintrc.js, in the root of my profile and add the following code to it:

exports.mergeEslintConfig = (esLintConfig) => {
  esLintConfig.rules = {
    "@microsoft/spfx/no-async-await": "off",
  };

  return esLintConfig;
};

Notice that by adding the well known file, SPFx projects will now call my JavaScript file which has a chance to modify the ESLint config before ESLint is run on my project. Now I can have a global configuration on my dev environment!

So now when I run the same project, no errors!

Linting message eliminated using our local ESLint configuration

Linting message eliminated using our local ESLint configuration

Linting message eliminated using our local ESLint configuration

I could even take this further, by having my JavaScript file figure out which set of rules I should be using and dynamically load them. Maybe using differently named rules for different clients, or dynamically pulling in rules from a central location for my company coding standard.

The point is, now, we’re in control of our rules.

The one caveat to this approach is that it allows a developer to change the linting process on their workstation, but not in a project that multiple developers can share. While that’s true, I think the flexibility of this solution is best for developers. But to address that concern, if the linting rules in the project’s default configuration stay with the project, then they’ll be applied when the project goes through the continuous integration process and the builds are run.