Linting Typescript Projects
This page will discuss common topics around maintaining the code quality of Typescript codebases using the Mill build tool
Linting and AutoFormatting with Eslint and Prettier
Eslint and Prettier are tools that analyzes your Typescript source code, performing intelligent analyses and code quality checks, and is often able to automatically fix the issues that it discovers. It can also perform automated refactoring.
package build
import mill._, javascriptlib._
object foo extends TypeScriptModule with TsLintModule
Mill supports code linting via eslint
https://eslint.org out of the box.
You can lint your projects code by providing a configuration for eslint and running mill _.checkFormatAll
.
You can define npmLintDeps
field to add dependencies specific to linting to your module.
The task npmLintDeps
works the same way as npmDeps
or npmDevDeps
.
> cat foo/src/foo.ts # initial code with lint errors.
export class Foo{
static main(
args: string[
]) {
console.log(`Hello World`);
}
}
> mill foo.checkFormatAll # run eslint
...
foo/src/foo.ts
3:5 error 'args' is defined but never used @typescript-eslint/no-unused-vars
...
...1 problem (1 error, 0 warnings)
> sed -i.bak 's/Hello World/Hello World ${args.join('\'' '\'')}/' foo/src/foo.ts # fix lint error.
> cat foo/src/foo.ts # initial code with lint errors.
export class Foo{
static main(
args: string[
]) {
console.log(`Hello World ${args.join(' ')}`);
}
}
> mill foo.checkFormatAll # run eslint
All matched files use Eslint code style!
package build
import mill._, javascriptlib._
object foo extends TypeScriptModule with TsLintModule
Mill supports code auto formatting via eslint
https://eslint.org and prettier
https://prettier.io/docs/en.
You can reformat your projects code by providing a configuration for your preferred formtter then running
mill _.reformatAll
to reformat your code. You can also check for formatting errors by running
mill _.checkFormatAll
.
If both configuration files are present, the command mill _.checkFormatAll
and
mill _.reformatAll
will default to eslint. If neither files are present,
the command will exit with an error, you must include at least one configuration file.
When using prettier you can specify the path to check format or reformat via command line argument,
mill _.checkFormatAll "//.ts"
just as you would when running prettier --check
or
mill _.reformatAll "//.ts"
just as you would when running prettier --write
,
if no path is provided mill will default to using "/*/*.ts".
Also if a .prettierignore
is not provided, mill will generate one ignoring "node_modules" and ".git".
> cat foo/src/foo.ts # initial poorly formatted source code
export class Foo{
static main(
args: string[
])
{console.log("Hello World!")
}
}
> mill foo.checkFormatAll # run linter - since both eslint and prettier configurations are present, mill will opt to use eslint.
...
foo/src/foo.ts
2:1 error Expected indentation of 2 spaces but found 0 indent
3:1 error Expected indentation of 4 spaces but found 0 indent
5:1 error Opening curly brace does not appear on the same line as controlling statement brace-style
5:1 error Statement inside of curly braces should be on next line brace-style
5:1 error Requires a space after '{' block-spacing
5:1 error Expected indentation of 2 spaces but found 0 indent
5:14 error Strings must use singlequote quotes
5:29 error Missing semicolon semi
6:1 error Expected indentation of 2 spaces but found 0 indent
7:2 error Newline required at end of file but not found eol-last
...
...10 problems (10 errors, 0 warnings)
...10 errors and 0 warnings potentially fixable with running foo.reformatAll.
> mill foo.reformatAll
...
All matched files have been reformatted!
> cat foo/src/foo.ts # code formatted with eslint configuration.
export class Foo{
static main(
args: string[
]) {
console.log('Hello World!');
}
}
> rm -rf eslint.config.mjs # since there is no eslint config file `eslint.config.(js|mjs|cjs)`, mill will use the prettier configuration available.
> mill foo.checkFormatAll # run lint with prettier configuration.
Checking formatting...
[warn] foo/src/foo.ts
[warn] Code style issues found. Run foo.reformatAll to fix.
> mill foo.reformatAll
...
All matched files have been reformatted!
> cat foo/src/foo.ts # code formatted with prettier configuration.
export class Foo {
static main(args: string[]) {
console.log('Hello World!');
}
}
Code Coverage with Jest, Mocha, Vitest and Jasmine
Mill supports code coverage analysis with multiple Typescript testing frameworks.
package build
import mill._, javascriptlib._
object foo extends TypeScriptModule {
object test extends TypeScriptTests with TestModule.Jest
}
object bar extends TypeScriptModule {
object test extends TypeScriptTests with TestModule.Mocha
}
object baz extends TypeScriptModule {
object test extends TypeScriptTests with TestModule.Vitest
}
object qux extends TypeScriptModule {
object test extends TypeScriptTests with TestModule.Jasmine
}
Mill supports code coverage with Jest
, Mocha
, Vitest
and Jasmine
out of the box.
To run a test with coverage, run the command mill _.test.coverage
.
The path to generated coverage data can be retrieved with mill _.test.coverageFiles
,
The path to generated html file can be retrieved with mill _.test.htmlReport
.
To use custom configuations for test suites, you can simply include matching test suite config file in your project root.
For custom configurations:
Jest suite expects a jest.config.ts
file.
Jasmine suite expects a jasmine.json
file.
Mocha suite expects a test-runner.js
file.
Vitest suite expects a vitest.config.ts
file.
Mocha & Jasmine both rely on nyc
https://www.npmjs.com/package/nyc and @istanbuljs/nyc-config-typescript
https://www.npmjs.com/package/@istanbuljs/nyc-config-typescript
for coverage, when using either, to use custom coverage configurations you must include a .nycrc
file in your project root.
Example .nycrc
configuration
{
"extends": "@istanbuljs/nyc-config-typescript",
"require": ["ts-node/register", "tsconfig-paths/register"],
"exclude": ["node_modules", "*/**/*.test.ts"],
"reporter": ["text", "html"],
...
}
As always for most use cases you will never need to define a custom test configurtion file.
> mill foo.test.coverage
...Calculator
...
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...62.5 |...50 |...66.66 |...62.5 |...
...calculator.ts...|...62.5 |...50 |...66.66 |...62.5 | 14-17...
---------------|---------|----------|---------|---------|-------------------
...
Test Suites:...1 passed, 1 total...
Tests:...4 passed, 4 total...
...
> mill bar.test.coverage
...
...2 passing...
...
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...66.66 |...0 |...66.66 |...62.5 |...
...calculator.ts...|...66.66 |...0 |...66.66 |...62.5 | 7-10...
---------------|---------|----------|---------|---------|-------------------
> mill baz.test.coverage
.../calculator.test.ts...
...Test Files 1 passed...
...Tests 4 passed...
...
...Coverage report from v8
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...100 |...100 |...100 |...100 |...
...calculator.ts...|...100 |...100 |...100 |...100 |...
---------------|---------|----------|---------|---------|-------------------
> mill qux.test.coverage
...
4 specs, 0 failures
...
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...100 |...100 |...100 |...100 |...
...calculator.ts...|...100 |...100 |...100 |...100 |...
---------------|---------|----------|---------|---------|-------------------