Linting MDX using remark-lint
In the previous part of the tutorial, we added linting for our code, in this part we will add linting for our (MDX / markdown) content
Adding and configuring remark-lint
In the previous part we added the .eslintrc.js
ESLint configuration file, in which we have added the MDX plugin
The next step is to install and then configure remark-lint, but first, we need to install remark-lint as well as 3 other packages that contain presets for remark-lint, those presets will install a bunch of rules
Use the following command to install all 4 packages:
Remark-lint rules don't get added to .eslintrc.js
, but instead, we create a new file called .remarkrc.mjs
(in the root of our project) and add the following content:
The first 3 imports are presets with recommended rules, we then use them in the plugins config
The other imports are single rules we want to configure, we then use them in the plugins config, the configuration for each rule consists of an array where the first value is the rule and the second value is the configuration we want to apply, here is an explanation for the ones I suggest adding (but feel free to configure them differently, to match your use case):
- for the maximum headings length rule, we use 1,100, which tells remark-lint that headings should not have a length lower than 1 character and not greater than 100
- for the unordered lists marker style, we set it to consistent, when set to consistent, the rule will check which marker is the most used and then enforce it everywhere, this is nice because it means it is flexible and will adapt to what you use, do you often define lists by using an asterisk (*), then this is what the rule will enforce or do you prefer using a hyphen-minus (-) then that is what will get enforced, consistent is nice setting to ensure that your styling is consistent based on what you use the most
- the no undefined references rule adds a list of references that should get excluded (hence not be considered undefined), we add
!NOTE
and some others as those will be used at some point when we start to use GitHub, like alerts for markdown - images and links can have a title, the title needs to be enclosed by two symbols, here we define that we want to use a single quote (') for titles, so an image with a title would look like this
![IMAGE_ALT_TEXT](IMAGE_PATH 'IMAGE_TITLE')
, if you prefer, you can enforce double quotes or even set it toconsistent
to let it choose the one you use most - the maximum line length rule I disabled it, some people like to stick to 80 characters, I try to keep my lines short, but if a line is bigger, that's ok (for me), too
- finally, the list item spacing I disabled it because I had a lot of false positives when using that rule (I might re-enable it in the future and check if the problems I encountered got fixed)
To get more information about what each rule does as well as more information about the 3 presets I use here, I recommend visiting the rules and presets GitHub repository and then check out the README of each package
There are a lot more rules than the ones listed here that get used, but for all other rules, we keep the default configuration, so we don't need to install and import them individually
package.json lint script update
The next thing we need to do (as I explained in the "Why are we changing the Next.js linting setup?" chapter) is to change the lint script in our package.json
Instead of using the next lint (the Next.js CLI linting command), we will create our own command using the ESLint CLI
When using the next lint CLI command, several things were done for us in the background, however, as we want to use the ESLint CLI, we need to set those flags ourselves
For example, to enable ESLint cache, we need to use the --cache
flag, and then we tell ESLint to use the same .next/cache/eslint
cache folder that Next.js would use, using the --cache-location
flag
To test our command in the terminal (or your preferred command line tool) we are going to use npx:
To specify what files (extensions) get linted, we need to use the --ext
flag followed by a list of extensions like so:
Now that we have tested the command and know how the flags work, we need to put our command into our package.json
(when using the command inside of the package.json
, we don't need to use npx
, as npm does this for us):
We change the lint script in the package.json
to this:
Line 5: I replaced the original alias of the lint command that Next.js had created by next-lint, this way we keep the original command in the scripts, just in case we need it in the future
Line 6: I added our new custom linting command
Now, when we use the npm run lint
command in our terminal, it will use our new script, it will read the ESLint configuration from our .eslintrc.js
configuration file and then do the linting based on what we set in the flags
I have 3 more scripts that can be useful that you might want to add, too:
The lint-nocache script is the same linting command, but it does NOT use the ESLint cache, this is useful when tweaking the ESLint configuration and not wanting ESLint to use the cache but instead always do a fresh start
The lint-debug script is again the same linting command, but this time, we add the --debug
flag (which you can't use when using the next lint CLI, but as we now use the ESLint CLI, we can), this will print a lot of helpful information about what ESLint does in the OUTPUT tab in VSCode, which is again beneficial if you tweak your configuration and want to verify the things ESLint is doing or to debug a problem with your setup, like checking if your plugins get loaded correctly and much more
If you don't know (yet) how to open the output channel, check out the "VSCode (ESLint) output channel" chapter in the VSCode post
The lint-fix script will attempt to fix linting problems for you automatically, however this can be tricky on big repositories with a lot of code, so if you run this later and have potentially a lot of fixes that get applied, then I recommend first to create a new branch, run the command and then use the commit list to check the changes manually for each file, then do some testing to ensure nothing is broken and then finally merge the changes into your main branch
Btw the --fix flag works when using the ESLint CLI, but it can also be used when using the next lint CLI
One more thing that will help us NOT forget about those 3 new commands is to document them in our README.md
file:
Finally, save the README.md
file and commit/sync to changes
Clearing the ESLint cache
Because by default, we use a cache to speed up the linting process when using the npm run lint
command, we need to delete the cache manually after making changes to the .eslintrc.js
ESLint configuration file or the .remarkrc.mjs
remark-lint configuration file
To delete the cache manually, open the .next
folder in the root of your project, then go into the cache
folder and finally delete the eslint
file
If you do a lot of tests and don't want to delete the cache manually after every change, use the npm run lint-nocache
instead, until you are done testing, then delete the cache once and use the regular npm run lint command
one last time to do final test
package.json build script update
Another script we need to change is the build script (as I explained in the "Why are we changing the Next.js linting setup?" chapter)
We need to ensure the build script will NOT use the Next lint CLI by default by editing our next.config.mjs
, like so:
Lines 41 to 43: we add the ignoreDuringBuilds option and set it to true to ensure Next.js does not automatically run the lint command during builds
Now, as we still want linting to happen before a build, we need to add the lint script we did in the previous chapter to the build script in our package.json,
like so:
When we now use our npm run build
command (or when our deployment script uses it), it will first execute npm run lint
, and if there are NO linting errors, it will then execute next build
. This means that if you a deployment tool like Vercel and linting fails, then the build process will abort, so the same behavior as we had when using the default next lint
and next build
commands
Testing our new lint command
After all the coding let's finally make a linting test, by using the following command in our terminal:
You probably notice that the linting command will output some errors and warnings in the terminal, those are because of files from packages we inslled earlier like Sentry, but this is a good as it we can now also test our lint-fix command, that should automatically fix those errors for us
To fix the linting errors, use the following command:
And then run the lint command one more time and you should have no more warnings and errors as they got all fixed:
We had to do a lot of changes to get this new linting setup up and running, but I hope you agree with me that it was worth it, we have greatly improved our DX by automating both the linting of code and content, which will ensure we write cleaner code and ensure our markdown content is well formatted
Congratulations 🎉 you just added linting for all your MDX (markdown) content in your project's MDX pages
If you liked this post, please consider making a donation ❤️ as it will help me create more content and keep it free for everyone