chore(lint): switch to eslint

This commit is contained in:
Julian Tölle 2021-05-25 18:12:42 +02:00
parent f56548e432
commit 9b96d0fab4
29 changed files with 1609 additions and 113 deletions

43
.eslintrc.js Normal file
View file

@ -0,0 +1,43 @@
module.exports = {
extends: ["airbnb-typescript", "prettier"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "tsconfig.json",
},
plugins: [
"eslint-plugin-import",
"eslint-plugin-jsdoc",
"eslint-plugin-prefer-arrow",
"eslint-plugin-react",
"@typescript-eslint",
],
rules: {
"import/prefer-default-export": "off",
"class-methods-use-this": "off",
"@typescript-eslint/lines-between-class-members": [
"error",
"always",
{ exceptAfterSingleLine: true },
],
"@typescript-eslint/return-await": "off",
"import/no-cycle": "off",
"no-restricted-syntax": [
"error",
{
selector: "ForInStatement",
message:
"for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.",
},
{
selector: "LabeledStatement",
message:
"Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.",
},
{
selector: "WithStatement",
message:
"`with` is disallowed in strict mode because it makes code impossible to predict and optimize.",
},
],
},
};

View file

@ -54,6 +54,7 @@ function useProvideAuth(): AuthContext {
useEffect(() => {
refreshAccessToken().catch(() => {
// eslint-disable-next-line no-console
console.log("Unable to refresh access token");
});
}, [refreshAccessToken]);

View file

@ -1,3 +1,4 @@
/* eslint-disable no-console */
// This optional code is used to register a service worker.
// register() is not called by default.

1550
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,7 +15,7 @@
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "tslint -p tsconfig.json -c tslint.json",
"lint": "eslint --ext .js,.jsx,.ts,.tsx src/ frontend/src/",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
@ -64,6 +64,17 @@
"@types/node": "15.6.0",
"@types/passport-jwt": "3.0.5",
"@types/supertest": "2.0.11",
"@typescript-eslint/eslint-plugin": "4.25.0",
"@typescript-eslint/parser": "4.25.0",
"eslint": "7.27.0",
"eslint-config-airbnb-typescript": "12.3.1",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-import": "2.23.3",
"eslint-plugin-jsdoc": "35.0.0",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-prefer-arrow": "1.2.3",
"eslint-plugin-react": "7.23.2",
"eslint-plugin-react-hooks": "4.2.0",
"jest": "26.6.3",
"prettier": "2.3.0",
"supertest": "6.1.3",
@ -71,9 +82,6 @@
"ts-loader": "9.2.2",
"ts-node": "10.0.0",
"tsconfig-paths": "3.9.0",
"tslint": "6.1.3",
"tslint-config-prettier": "1.18.0",
"tslint-plugin-prettier": "2.3.0",
"typescript": "4.2.4"
},
"jest": {

View file

@ -12,7 +12,7 @@ export class AuthSession {
@PrimaryGeneratedColumn("uuid")
id: string;
@ManyToOne((type) => User, { eager: true })
@ManyToOne(() => User, { eager: true })
user: User;
@CreateDateColumn()

View file

@ -1,4 +1,4 @@
// tslint:disable: max-classes-per-file
/* eslint-disable max-classes-per-file */
import { EntityRepository, Repository, SelectQueryBuilder } from "typeorm";
import { User } from "../users/user.entity";
import { AuthSession } from "./auth-session.entity";

View file

@ -7,7 +7,7 @@ import {
UseGuards,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Response } from "express";
import type { Response } from "express";
import { User } from "../users/user.entity";
import { AuthSession } from "./auth-session.entity";
import { AuthService } from "./auth.service";

View file

@ -4,7 +4,7 @@ import {
ExceptionFilter,
ForbiddenException,
} from "@nestjs/common";
import { Response } from "express";
import type { Response } from "express";
import { Logger } from "../logger/logger.service";
@Catch()

View file

@ -1,11 +1,12 @@
import { Controller, Get } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import {
DNSHealthIndicator,
HealthCheck,
HealthCheckResult,
HealthCheckService,
TypeOrmHealthIndicator,
} from "@nestjs/terminus";
import { ConfigService } from "@nestjs/config";
@Controller("api/v1/health")
export class HealthCheckController {
@ -18,7 +19,7 @@ export class HealthCheckController {
@Get()
@HealthCheck()
check() {
check(): Promise<HealthCheckResult> {
return this.health.check([
() =>
this.dns.pingCheck(

View file

@ -1,9 +1,8 @@
/* eslint-disable max-classes-per-file */
import { Track } from "../../music-library/track.entity";
import { User } from "../../users/user.entity";
import { Listen } from "../listen.entity";
// tslint:disable max-classes-per-file
export class CreateListenRequestDto {
track: Track;
user: User;

View file

@ -1,23 +1,22 @@
/* eslint-disable max-classes-per-file */
import { IsDate, IsOptional, ValidateNested } from "class-validator";
import { Interval } from "date-fns";
import { User } from "../../users/user.entity";
// tslint:disable-next-line: max-classes-per-file
export class GetListensFilterTimeDto implements Interval {
@IsDate()
start: Date;
@IsDate()
end: Date;
}
// tslint:disable-next-line: max-classes-per-file
export class GetListensFilterDto {
@IsOptional()
@ValidateNested()
time?: GetListensFilterTimeDto;
}
// tslint:disable-next-line: max-classes-per-file
export class GetListensDto {
user: User;

View file

@ -14,10 +14,10 @@ export class Listen {
@PrimaryGeneratedColumn("uuid")
id: string;
@ManyToOne((type) => Track)
@ManyToOne(() => Track)
track: Track;
@ManyToOne((type) => User)
@ManyToOne(() => User)
user: User;
@Column({ type: "timestamp" })

View file

@ -1,4 +1,4 @@
// tslint:disable: max-classes-per-file
/* eslint-disable max-classes-per-file */
import { EntityRepository, Repository, SelectQueryBuilder } from "typeorm";
import { Interval } from "../reports/interval";
import { User } from "../users/user.entity";

View file

@ -19,8 +19,13 @@ export class ListensController {
@Query("filter") filter: GetListensFilterDto,
@ReqUser() user: User
): Promise<Pagination<Listen>> {
limit = limit > 100 ? 100 : limit;
const clampedLimit = limit > 100 ? 100 : limit;
return this.listensService.getListens({ page, limit, user, filter });
return this.listensService.getListens({
page,
limit: clampedLimit,
user,
filter,
});
}
}

View file

@ -3,9 +3,9 @@ import { ConfigService } from "@nestjs/config";
import { NestFactory } from "@nestjs/core";
import { NestExpressApplication } from "@nestjs/platform-express";
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
import { AppModule } from "./app.module";
import * as Sentry from "@sentry/node";
import { RavenInterceptor } from "nest-raven";
import { AppModule } from "./app.module";
function setupSentry(
app: NestExpressApplication,

View file

@ -18,13 +18,13 @@ export class Album {
@Column()
name: string;
@ManyToMany((type) => Artist, (artist) => artist.albums)
@ManyToMany(() => Artist, (artist) => artist.albums)
@JoinTable({ name: "album_artists" })
artists?: Artist[];
@OneToMany((type) => Track, (track) => track.album)
@OneToMany(() => Track, (track) => track.album)
tracks?: Track[];
@Column((type) => SpotifyLibraryDetails)
@Column(() => SpotifyLibraryDetails)
spotify: SpotifyLibraryDetails;
}

View file

@ -10,9 +10,9 @@ export class Artist {
@Column()
name: string;
@ManyToMany((type) => Album, (album) => album.artists)
@ManyToMany(() => Album, (album) => album.artists)
albums?: Album[];
@Column((type) => SpotifyLibraryDetails)
@Column(() => SpotifyLibraryDetails)
spotify: SpotifyLibraryDetails;
}

View file

@ -18,13 +18,13 @@ export class Track {
@Column()
name: string;
@ManyToOne((type) => Album, (album) => album.tracks)
@ManyToOne(() => Album, (album) => album.tracks)
album?: Album;
@ManyToMany((type) => Artist)
@ManyToMany(() => Artist)
@JoinTable({ name: "track_artists" })
artists?: Artist[];
@Column((type) => SpotifyLibraryDetails)
@Column(() => SpotifyLibraryDetails)
spotify?: SpotifyLibraryDetails;
}

View file

@ -1,5 +1,3 @@
import { Timeframe } from "../timeframe.enum";
export class ListenReportDto {
items: {
date: string;

View file

@ -264,6 +264,13 @@ export class ReportsService {
break;
}
default: {
interval = this.getIntervalFromPreset({
timePreset: TimePreset.LAST_7_DAYS,
});
break;
}
}
return interval;

View file

@ -1,8 +1,4 @@
import {
Injectable,
OnApplicationBootstrap,
OnModuleInit,
} from "@nestjs/common";
import { Injectable, OnApplicationBootstrap } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { SchedulerRegistry } from "@nestjs/schedule";
import { captureException } from "@sentry/node";

View file

@ -1,5 +1,4 @@
import { Module } from "@nestjs/common";
import { ConfigModule } from "../config/config.module";
import { SchedulerService } from "./scheduler.service";
import { SpotifyModule } from "./spotify/spotify.module";

View file

@ -12,7 +12,6 @@ export class SpotifyApiService {
async getRecentlyPlayedTracks({
accessToken,
lastRefreshTime,
}: SpotifyConnection): Promise<PlayHistoryObject[]> {
const parameters: { limit: number; after?: number } = {
limit: 50,

View file

@ -35,6 +35,8 @@ export class SpotifyService {
const users = await this.usersService.findAll();
for (const user of users) {
// We want to run this sequentially to avoid rate limits
// eslint-disable-next-line no-await-in-loop
await this.crawlListensForUser(user);
}
}
@ -142,7 +144,7 @@ export class SpotifyService {
spotifyID
);
} catch (err) {
if (err.response && err.response.status === 401) {
if (err.response && err.response.status === 401 && retryOnExpiredToken) {
await this.refreshAppAccessToken();
return this.importTrack(spotifyID, false);
@ -192,7 +194,7 @@ export class SpotifyService {
spotifyID
);
} catch (err) {
if (err.response && err.response.status === 401) {
if (err.response && err.response.status === 401 && retryOnExpiredToken) {
await this.refreshAppAccessToken();
return this.importAlbum(spotifyID, false);
@ -238,7 +240,7 @@ export class SpotifyService {
spotifyID
);
} catch (err) {
if (err.response && err.response.status === 401) {
if (err.response && err.response.status === 401 && retryOnExpiredToken) {
await this.refreshAppAccessToken();
return this.importArtist(spotifyID, false);
@ -261,6 +263,8 @@ export class SpotifyService {
private async refreshAppAccessToken(): Promise<void> {
if (!this.appAccessTokenInProgress) {
this.logger.debug("refreshing spotify app access token");
/* eslint-disable no-async-promise-executor */
this.appAccessTokenInProgress = new Promise(async (resolve, reject) => {
try {
const newAccessToken =

View file

@ -1,4 +1,4 @@
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from "typeorm";
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import { SpotifyConnection } from "../sources/spotify/spotify-connection.entity";
@Entity()
@ -12,6 +12,6 @@ export class User {
@Column({ nullable: true })
photo?: string;
@Column((type) => SpotifyConnection)
@Column(() => SpotifyConnection)
spotify: SpotifyConnection;
}

View file

@ -49,6 +49,7 @@ export class UsersService {
user: User,
spotify: SpotifyConnection
): Promise<void> {
// eslint-disable-next-line no-param-reassign
user.spotify = spotify;
await this.userRepository.save(user);
}

View file

@ -8,7 +8,7 @@ describe("AppController (e2e)", () => {
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();

View file

@ -1,23 +0,0 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended",
"tslint-config-prettier",
"tslint-plugin-prettier"
],
"jsRules": {
"no-unused-expression": true
},
"rules": {
"member-access": [false],
"ordered-imports": [false],
"max-line-length": [true, 150],
"member-ordering": [false],
"interface-name": [false],
"arrow-parens": false,
"object-literal-sort-keys": false,
"trailing-comma": false,
"prettier": "true"
},
"rulesDirectory": []
}