Stylelint is a useful tool to maintain styles code quality and lint CSS as well as its preprocessors. It already has a vast amount of rules and plugins that will cover most of the cases. But from time to time, you may need something specific to your project that has little or no value for other teams and therefore isn’t worth a place in npm.
In the case of my project, global CSS imports were used multiple times across different components. This mistake, without proper attention, resulted in an additional 700kb of useless CSS. However, it could have been easily avoided with the help of a pre-commit hook and a custom Stylelint plugin.
But this article is not about the plugin itself (it is well described in a doc), but about using its TypeScript version.
What’s the problem with that?
Since Stylelint is written in JS, it is not possible to just add a reference to a plugin written in TS, meaning there can be several possible solutions:
- precompile the TS before launching Stylelint
- use ts-node
ts-node – TS execution engine that lets you run TS code without compiling it.
The easiest way to use a plugin
Just register ts-node in a JS config. That being said styleling.config.js
needs only a few lines of code besides including the plugin in the config.
require('ts-node').register({ project: require('node:path').join(__dirname, 'tsconfig.json')
});
But this ease of use comes with a price. It may be suitable for smaller projects with a small number of styling files to lint. Or when Stylelint is used in a pre-commit hook (which means there will not be that many files in most cases).
Several dozen files already cause a noticeable delay of several seconds. Linting all files for rather large projects results in a Node exception since a new instance of ts-node will be created for every linted file.
Fix performance penalty
The solution for the problem described above is to register ts-node once. This can be achieved with a modified command that launches Stylelint.
node --require ts-node/register ./node_modules/stylelint/bin/stylelint.js
Stylelint API
If you don’t want to mix TS and JS in a config file making, it invalid, it is also possible to create a script that will use the Stylelint API. The benefit is that it is possible to run linting without a custom plugin, and it is also slightly faster.
package.json
"stylelint": "node ts-node --project tsconfig.json stylelint.ts"
stylelint.ts
import stylelint from 'stylelint';
import config from './stylelint.config'; const [n, cmd, ...files] = process.argv; async function lint() { try { const report = await stylelint.lint({ files: !files.length ? ['**/*.less'] : files, config: { ...config, plugins: [ // path to plugin ], rules: { ...config.rules 'my-custom-rule': true } } }); // check if the report has errors and exit if any } catch { process.exit(1); }
}; lint();
Performance comparison
1 file | 50 files | 800 files | |
---|---|---|---|
Vanilla JS Plugin | 0.55s | 1.8s | 16.1s |
Stylelint API | 0.93s | 1.8s | 14.6s |
ts-node singleton | 2s | 3.5s | 16.5s |
ts-node in config file | 2s | 4.9s | max call stack error |