upgraded eslint, defined new rules & added ignore comments

This commit is contained in:
2026-02-23 20:27:09 +01:00
parent b30c068f9e
commit c5c1cf1c8d
25 changed files with 2696 additions and 108 deletions

View File

@@ -1,31 +0,0 @@
{
"root": true,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"@vue/eslint-config-airbnb",
"plugin:vue/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
"rules": {
"vue/no-v-model-argument": "off",
"no-underscore-dangle": "off",
"vue/multi-word-component-names": "off",
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"],
},
"settings": {
"import/resolver": {
webpack: {
config: "./webpack.config.js"
}
}
}
}

67
eslint.config.mjs Normal file
View File

@@ -0,0 +1,67 @@
import path from "node:path";
import { includeIgnoreFile } from "@eslint/compat";
import js from "@eslint/js";
import { defineConfig } from "eslint/config";
import { configs, plugins } from "eslint-config-airbnb-extended";
import { rules as prettierConfigRules } from "eslint-config-prettier";
import prettierPlugin from "eslint-plugin-prettier";
const CUSTOM_RULES = {
"vue/no-v-model-argument": "off",
"no-underscore-dangle": "off",
"vue/multi-word-component-names": "off",
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"]
};
const gitignorePath = path.resolve(".", ".gitignore");
// ESLint recommended config
const jsConfig = defineConfig([
{
name: "js/config",
...js.configs.recommended
},
plugins.stylistic,
plugins.importX,
...configs.base.recommended // Airbnb base recommended config
]);
// Node & Airbnb recommended config
const nodeConfig = defineConfig([plugins.node, ...configs.node.recommended]);
// Typescript & Airbnb base TS config
const typescriptConfig = defineConfig([
plugins.typescriptEslint,
...configs.base.typescript
]);
// Prettier config
const prettierConfig = defineConfig([
{
name: "prettier/plugin/config",
plugins: {
prettier: prettierPlugin
}
},
{
name: "prettier/config",
rules: {
...prettierConfigRules,
"prettier/prettier": "error"
}
}
]);
export default defineConfig([
// Ignore files and folders listed in .gitignore
includeIgnoreFile(gitignorePath),
...jsConfig,
...nodeConfig,
...typescriptConfig,
...prettierConfig,
{
rules: CUSTOM_RULES
}
]);

View File

@@ -7,8 +7,7 @@
"scripts": { "scripts": {
"dev": "NODE_ENV=development vite", "dev": "NODE_ENV=development vite",
"build": "yarn vite build", "build": "yarn vite build",
"postbuild": "cp public/dist/index.html public/index.html", "clean": "rm -r dist 2> /dev/null; rm public/index.html 2> /dev/null; rm -r lib 2> /dev/null",
"clean": "rm -r public/dist 2> /dev/null; rm public/index.html 2> /dev/null; rm -r lib 2> /dev/null",
"start": "echo 'Start using docker, consult README'", "start": "echo 'Start using docker, consult README'",
"lint": "eslint src --ext .ts,.vue", "lint": "eslint src --ext .ts,.vue",
"docs": "documentation build src/api.ts -f html -o docs/api && documentation build src/api.ts -f md -o docs/api.md" "docs": "documentation build src/api.ts -f html -o docs/api && documentation build src/api.ts -f md -o docs/api.md"
@@ -20,10 +19,17 @@
"vuex": "4.1.0" "vuex": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/compat": "^2.0.2",
"@eslint/js": "^10.0.1",
"@types/node": "^25.3.0",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
"prettier": "2.7.1", "eslint": "^10.0.1",
"eslint-config-airbnb-extended": "^3.0.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
"prettier": "^3.8.1",
"sass": "1.54.3", "sass": "1.54.3",
"typescript": "4.7.4", "typescript": "5.9.3",
"vite": "^6.0.3" "vite": "^6.0.3"
} }
} }

View File

@@ -1,3 +1,4 @@
/* eslint-disable n/no-unsupported-features/node-builtins */
import { IList, IMediaCredits, IPersonCredits } from "./interfaces/IList"; import { IList, IMediaCredits, IPersonCredits } from "./interfaces/IList";
const API_HOSTNAME = import.meta.env.VITE_SEASONED_API; const API_HOSTNAME = import.meta.env.VITE_SEASONED_API;
@@ -336,7 +337,11 @@ const register = (username, password) => {
}); });
}; };
const login = (username, password, throwError = false) => { const login = async (
username: string,
password: string,
throwError = false
) => {
const url = new URL("/api/v1/user/login", API_HOSTNAME); const url = new URL("/api/v1/user/login", API_HOSTNAME);
const options = { const options = {
method: "POST", method: "POST",
@@ -353,7 +358,7 @@ const login = (username, password, throwError = false) => {
}); });
}; };
const logout = (throwError = false) => { const logout = async (throwError = false) => {
const url = new URL("/api/v1/user/logout", API_HOSTNAME); const url = new URL("/api/v1/user/logout", API_HOSTNAME);
const options = { method: "POST" }; const options = { method: "POST" };

View File

@@ -46,7 +46,7 @@
let _type: MediaTypes; let _type: MediaTypes;
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
params.forEach((value, key) => { params.forEach((_, key) => {
if ( if (
key !== MediaTypes.Movie && key !== MediaTypes.Movie &&
key !== MediaTypes.Show && key !== MediaTypes.Show &&

View File

@@ -27,7 +27,6 @@
const overflow: Ref<boolean> = ref(false); const overflow: Ref<boolean> = ref(false);
const descriptionElement: Ref<HTMLElement> = ref(null); const descriptionElement: Ref<HTMLElement> = ref(null);
// eslint-disable-next-line no-undef
function removeElements(elems: NodeListOf<Element>) { function removeElements(elems: NodeListOf<Element>) {
elems.forEach(el => el.remove()); elems.forEach(el => el.remove());
} }

View File

@@ -85,7 +85,7 @@
try { try {
validate(); validate();
} catch (error) { } catch (error) {
console.log("not valid!"); // eslint-disable-line no-console console.log("not valid! error:", error); // eslint-disable-line no-console
} }
// const body: ResetPasswordPayload = { // const body: ResetPasswordPayload = {

View File

@@ -1,3 +1,4 @@
/*
let setValue = function(el, binding) { let setValue = function(el, binding) {
let value = binding.value; let value = binding.value;
let dateArray = value.split('-'); let dateArray = value.split('-');
@@ -13,3 +14,4 @@ module.exports = {
setValue(el, binding); setValue(el, binding);
} }
} }
*/

View File

@@ -1,5 +1,6 @@
let setValue = function(el, binding) { /*
let img = new Image(); const setValue = function(el, binding) {
const img = new Image();
img.src = binding.value; img.src = binding.value;
img.onload = function() { img.onload = function() {
@@ -10,10 +11,11 @@ let setValue = function(el, binding) {
module.exports = { module.exports = {
isLiteral: true, isLiteral: true,
bind(el, binding){ bind(el, binding) {
setValue(el, binding); setValue(el, binding);
}, },
update(el, binding){ update(el, binding) {
setValue(el, binding); setValue(el, binding);
} }
} };
*/

View File

@@ -1,4 +1,3 @@
/* eslint-disable no-use-before-define */
import { MediaTypes } from "./IList"; import { MediaTypes } from "./IList";
export interface IAutocompleteResult { export interface IAutocompleteResult {

View File

@@ -1,5 +1,3 @@
/* eslint-disable no-use-before-define */
export enum GraphTypes { export enum GraphTypes {
Plays = "plays", Plays = "plays",
Duration = "duration" Duration = "duration"
@@ -12,12 +10,12 @@ export enum GraphValueTypes {
export interface IGraphDataset { export interface IGraphDataset {
name: string; name: string;
data: Array<number>; data: number[];
} }
export interface IGraphData { export interface IGraphData {
labels: Array<string>; labels: string[];
series: Array<IGraphDataset>; series: IGraphDataset[];
} }
export interface IGraphResponse { export interface IGraphResponse {

View File

@@ -67,7 +67,7 @@ export interface IMovie {
backdrop: string; backdrop: string;
release_date: string | Date; release_date: string | Date;
rating: number; rating: number;
genres: Array<MovieGenres>; genres: MovieGenres[];
production_status: MovieProductionStatus; production_status: MovieProductionStatus;
tagline: string; tagline: string;
runtime: number; runtime: number;
@@ -88,9 +88,9 @@ export interface IShow {
seasons?: number; seasons?: number;
episodes?: number; episodes?: number;
popularity?: number; popularity?: number;
genres?: Array<ShowGenres>; genres?: ShowGenres[];
production_status?: string; production_status?: string;
runtime?: Array<number>; runtime?: number[];
exists_in_plex?: boolean; exists_in_plex?: boolean;
type: MediaTypes.Show; type: MediaTypes.Show;
} }
@@ -135,19 +135,19 @@ export interface ICrew {
} }
export interface IMediaCredits { export interface IMediaCredits {
cast: Array<ICast>; cast: ICast[];
crew: Array<ICrew>; crew: ICrew[];
id: number; id: number;
} }
export interface IPersonCredits { export interface IPersonCredits {
cast: Array<IMovie | IShow>; cast: (IMovie | IShow)[];
crew: Array<ICrew>; crew: ICrew[];
id: number; id: number;
type?: string; type?: string;
} }
export type ListResults = Array<IMovie | IShow | IPerson | IRequest>; export type ListResults = (IMovie | IShow | IPerson | IRequest)[];
export interface IList { export interface IList {
results: ListResults; results: ListResults;

View File

@@ -1,7 +1,7 @@
export default interface INavigationIcon { export default interface INavigationIcon {
title: string; title: string;
route: string; route: string;
icon: any; // eslint-disable-line @typescript-eslint/no-explicit-any icon: any;
requiresAuth?: boolean; requiresAuth?: boolean;
useStroke?: boolean; useStroke?: boolean;
} }

View File

@@ -1,6 +1,6 @@
import type ITorrent from "./ITorrent"; import type ITorrent from "./ITorrent";
export default interface IStateTorrent { export default interface IStateTorrent {
results: Array<ITorrent>; results: ITorrent[];
resultCount: number | null; resultCount: number | null;
} }

View File

@@ -7,5 +7,5 @@ export default interface ITorrent {
seed: string; seed: string;
leech: string; leech: string;
url: string | null; url: string | null;
release_type: Array<string>; release_type: string[];
} }

View File

@@ -3,7 +3,6 @@ import router from "./routes";
import store from "./store"; import store from "./store";
import Toast from "./plugins/Toast"; import Toast from "./plugins/Toast";
// eslint-disable-next-line @typescript-eslint/no-var-requires
import App from "./App.vue"; import App from "./App.vue";
store.dispatch("darkmodeModule/findAndSetDarkmodeSupported"); store.dispatch("darkmodeModule/findAndSetDarkmodeSupported");

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-param-reassign */
import IStateDarkmode from "../interfaces/IStateDarkmode"; import IStateDarkmode from "../interfaces/IStateDarkmode";
const state: IStateDarkmode = { const state: IStateDarkmode = {
@@ -10,9 +11,7 @@ export default {
namespaced: true, namespaced: true,
state, state,
getters: { getters: {
darkmodeSupported: (state: IStateDarkmode) => { darkmodeSupported: (state: IStateDarkmode) => state.darkmodeSupported
return state.darkmodeSupported;
}
}, },
mutations: { mutations: {
SET_DARKMODE_SUPPORT: ( SET_DARKMODE_SUPPORT: (

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-param-reassign */
import type IStateDocumentTitle from "../interfaces/IStateDocumentTitle"; import type IStateDocumentTitle from "../interfaces/IStateDocumentTitle";
const capitalize = (string: string) => { const capitalize = (string: string) => {
@@ -26,7 +27,7 @@ const state: IStateDocumentTitle = {
title: undefined title: undefined
}; };
/* eslint-disable @typescript-eslint/no-shadow, no-return-assign */ /* eslint-disable @typescript-eslint/no-shadow */
export default { export default {
namespaced: true, namespaced: true,
state, state,

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-param-reassign */
import type IStateHamburger from "../interfaces/IStateHamburger"; import type IStateHamburger from "../interfaces/IStateHamburger";
const state: IStateHamburger = { const state: IStateHamburger = {

View File

@@ -1,15 +1,16 @@
/* eslint-disable no-param-reassign */
import { MediaTypes } from "../interfaces/IList"; import { MediaTypes } from "../interfaces/IList";
import type { IStatePopup, IPopupQuery } from "../interfaces/IStatePopup"; import type { IStatePopup, IPopupQuery } from "../interfaces/IStatePopup";
/* eslint-disable-next-line import/no-cycle */ /* eslint-disable-next-line import-x/no-cycle */
import router from "../routes"; import router from "../routes";
const removeIncludedQueryParams = (params, key) => { const removeIncludedQueryParams = (params: URLSearchParams, key: string) => {
if (params.has(key)) params.delete(key); if (params.has(key)) params.delete(key);
return params; return params;
}; };
function paramsToObject(entries) { function paramsToObject(entries: Iterator<[string, string]>) {
const result = {}; const result = {};
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const [key, value] of entries) { for (const [key, value] of entries) {
@@ -65,7 +66,7 @@ export default {
actions: { actions: {
open: ({ commit }, { id, type }: { id: number; type: MediaTypes }) => { open: ({ commit }, { id, type }: { id: number; type: MediaTypes }) => {
if (!Number.isNaN(id)) { if (!Number.isNaN(id)) {
id = Number(id); /* eslint-disable-line no-param-reassign */ id = Number(id);
} }
commit("SET_OPEN", { id, type }); commit("SET_OPEN", { id, type });

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-param-reassign */
import type ITorrent from "../interfaces/ITorrent"; import type ITorrent from "../interfaces/ITorrent";
import type IStateTorrent from "../interfaces/IStateTorrent"; import type IStateTorrent from "../interfaces/IStateTorrent";
@@ -11,16 +12,12 @@ export default {
namespaced: true, namespaced: true,
state, state,
getters: { getters: {
results: (state: IStateTorrent) => { results: (state: IStateTorrent) => state.results,
return state.results; resultCount: (state: IStateTorrent) => state.resultCount
},
resultCount: (state: IStateTorrent) => {
return state.resultCount;
}
}, },
mutations: { mutations: {
SET_RESULTS: (state: IStateTorrent, results: Array<ITorrent>) => { SET_RESULTS: (state: IStateTorrent, results: ITorrent[]) => {
state.results = results; state.results = results;
}, },
SET_RESULT_COUNT: (state: IStateTorrent, count: number) => { SET_RESULT_COUNT: (state: IStateTorrent, count: number) => {
@@ -32,7 +29,7 @@ export default {
} }
}, },
actions: { actions: {
setResults({ commit }, results: Array<ITorrent>) { setResults({ commit }, results: ITorrent[]) {
commit("SET_RESULTS", results); commit("SET_RESULTS", results);
}, },
setResultCount({ commit }, count: number) { setResultCount({ commit }, count: number) {

View File

@@ -1,7 +1,7 @@
import { createRouter, createWebHistory } from "vue-router"; import { createRouter, createWebHistory } from "vue-router";
import type { RouteRecordRaw, RouteLocationNormalized } from "vue-router"; import type { RouteRecordRaw, RouteLocationNormalized } from "vue-router";
/* eslint-disable-next-line import/no-cycle */ /* eslint-disable-next-line import-x/no-cycle */
import store from "./store"; import store from "./store";
declare global { declare global {
@@ -10,7 +10,7 @@ declare global {
} }
} }
const routes: Array<RouteRecordRaw> = [ const routes: RouteRecordRaw[] = [
{ {
name: "home", name: "home",
path: "/", path: "/",
@@ -99,7 +99,6 @@ const loggedIn = () => store.getters["user/loggedIn"];
const hasPlexAccount = () => store.getters["user/plexUserId"] !== null; const hasPlexAccount = () => store.getters["user/plexUserId"] !== null;
const hamburgerIsOpen = () => store.getters["hamburger/isOpen"]; const hamburgerIsOpen = () => store.getters["hamburger/isOpen"];
/* eslint-disable @typescript-eslint/no-explicit-any */
router.beforeEach( router.beforeEach(
(to: RouteLocationNormalized, from: RouteLocationNormalized, next: any) => { (to: RouteLocationNormalized, from: RouteLocationNormalized, next: any) => {
store.dispatch("documentTitle/updateTitle", to.name); store.dispatch("documentTitle/updateTitle", to.name);

View File

@@ -6,7 +6,7 @@ import torrentModule from "./modules/torrentModule";
import user from "./modules/user"; import user from "./modules/user";
import hamburger from "./modules/hamburger"; import hamburger from "./modules/hamburger";
/* eslint-disable-next-line import/no-cycle */ /* eslint-disable-next-line import-x/no-cycle */
import popup from "./modules/popup"; import popup from "./modules/popup";
const store = createStore({ const store = createStore({

View File

@@ -14,9 +14,7 @@ export const parseJwt = (token: string) => {
const jsonPayload = decodeURIComponent( const jsonPayload = decodeURIComponent(
atob(base64) atob(base64)
.split("") .split("")
.map(c => { .map(c => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
return `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`;
})
.join("") .join("")
); );
@@ -62,10 +60,10 @@ export function focusOnNextElement(elementEvent: KeyboardEvent): void {
} }
} }
export function humanMinutes(minutes) { export function humanMinutes(minutes: number[] | number) {
if (minutes instanceof Array) { if (minutes instanceof Array) {
/* eslint-disable-next-line prefer-destructuring, no-param-reassign */ /* eslint-disable-next-line no-param-reassign */
minutes = minutes[0]; [minutes] = minutes;
} }
const hours = Math.floor(minutes / 60); const hours = Math.floor(minutes / 60);
@@ -93,7 +91,7 @@ export function setUrlQueryParameter(parameter: string, value: string): void {
const url = `${window.location.protocol}//${window.location.hostname}${ const url = `${window.location.protocol}//${window.location.hostname}${
window.location.port ? `:${window.location.port}` : "" window.location.port ? `:${window.location.port}` : ""
}${window.location.pathname}${params.toString().length ? `?${params}` : ""}`; }${ndow.location.pathname}${params.toString().length ? `?${params}` : ""}`;
window.history.pushState({}, "search", url); window.history.pushState({}, "search", url);
} }

2582
yarn.lock

File diff suppressed because it is too large Load Diff