logo: troll picture

Loki • dev

Linters & Stylecheck

Beitrag vom: 02.12.2024 (updated: 03.12.2024 06:32)

Stylecheck

Let's start with the simpler issue: Stylechecks. Basically it all boils down to maintaining the same style over the full application. It doesn't really matter WHICH style, but it must be the same!

Why?

  1. Pattern matching: Disregard of developer level it makes it easier to recognize patterns if they all are in the same structure
  2. Git Diffs: Having multiple styles might result in a back-and-forth of changes each time different styles collide
  3. Tools: Having a tool which can format your code and auto apply it's rules may help a lot!

Tools

Examples

Trivial: Spacing
// FROM
@Injectable()
export class JwtService {
    
    decodeToken (token: string): any {
        return jwt.decode(token);
    }
}

// TO
@Injectable()
export class JwtService {
    decodeToken(token: string): any {
        return jwt.decode(token);
    }
}

or tabs vs spaces

// FROM 2 spaces
describe('ReportsController', () => {
  let controller: ReportsController;

  beforeEach(async () => {

// TO tabs
describe('ReportsController', () => {
	let controller: ReportsController;

	beforeEach(async () => {

* Btw I'm a spaces guy, but convention over sympathy!

Trivial: Newlines
// FROM
@Module({
  controllers: [IntegrationController],
  providers: [IntegrationService, PrismaService, ScrapingService, CustomerIoService, JwtService]
})

// TO
@Module({
	controllers: [IntegrationController],
	providers: [
		IntegrationService,
		PrismaService,
		ScrapingService,
		CustomerIoService,
		JwtService,
	],
})

There is also the analyzer which sorts imports in a predictable manner. This is optional, but gives each header of a file the same order. For details about the order you can look here.

Linting

This is actually giving us a headstart on customers finding bugs. Each linter obviously has a small false positive rate, but the ruleset itself makes sure to exclude some classes of errors before they even happen!

The full ruleset of biome is here.

It has some very trivial checks like no distracting elements which prohibits marquee and blink, but also far more advanced checks.

My favorite category is nursery which contains legacy JS features which are surpassed by advanced Syntax.

Linter warnings examples

// 1. no banned types
code-block.ts:1:10 lint/complexity/noBannedTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dont use ’{}’ as a type.
  > 1const n: {} = 0
      │          ^^

// 2. complexity
code-block.js:1:10 lint/complexity/noExcessiveCognitiveComplexity ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Excessive complexity of 21 detected (max: 15).
  > 1function tooComplex() {
      │          ^^^^^^^^^^
    2for (let x = 0; x < 10; x++) {
    3for (let y = 0; y < 10; y++) {

// 3. for .. of  vs  Array.forEach
code-block.js:1:1 lint/complexity/noForEach ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Prefer for…of instead of forEach.
  > 1els.forEach((el) => {
      │ ^^^^^^^^^^^^^^^^^^^^^
  > 2f(el);
  > 3 │ })
      │ ^^

// 4. no useless catch
code-block.js:4:5 lint/complexity/noUselessCatch ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The catch clause that only rethrows the original error is useless.
    2doSomething();
    3 │ } catch(e) {
  > 4throw e;
      │     ^^^^^^^^

// 5. no uselss ternary
const a = foo === 1 ? false : true; // just use foo !== 1

code-block.js:1:9 lint/complexity/noUselessTernary  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Unnecessary use of boolean literals in conditional expression.
  > 1const a = foo === 1 ? false : true;
      │           ^^^^^^^^^^^^^^^^^^^^^^^^
  1. Is trivial, but don't use useless/harmful types
  2. Is using the Cyclomatic Complexity to give upper boundaries of complex code. Is code too complex -> make it simpler. Google for McCabe Metric too find more details. Depending on the language we should try to be <10 (10 possible ways of data routes through a function) and MUST be below 15, as this would be hard to maintain.
  3. Language features: for .. of .. is easier to read than Array.forEach(...). Also a performance issue!
  4. obviously almost dead code which doesn't serve any function/purpose
  5. Is a great example of FIXABLE lints: This one can be auto corrected!

... Aaaaand a lot more! We should use linters at every stage of our development process. The earlier the better and we WILL be using themsoon in our processes.

Biome in your editor

For the huge amount of reasons we should use biome whereever possible.

For the most common editors here the extension/plugin so that you can benefit directly. Just make sure the red squiggly lines disappear and autoformatting is active

Afterwards be sure to:

  1. Set autoformat on
  2. Actually read and see the errors supplied by the linter