chore: configure project wide linter (#577)

* chore(lint): configure project-wide linter

* chore(lint): improve linting in files

* chore(lint): hide warnings in CI
This commit is contained in:
Carlos Valente
2023-11-07 22:30:39 +01:00
committed by GitHub
parent 943dd19c09
commit 2c47d90f34
38 changed files with 138 additions and 171 deletions
+1
View File
@@ -0,0 +1 @@
"ONTIME_VERSION.js"
+31 -2
View File
@@ -6,8 +6,16 @@
"es6": true,
"jest": true
},
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended"
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"eslint-config-prettier"
],
"plugins": [
"@typescript-eslint",
"prettier"
],
"overrides": [
{
@@ -21,6 +29,27 @@
}
],
"rules": {
"no-console": ["warn", { "allow": ["warn", "error"]}]
"no-useless-concat": "warn",
"prefer-template": "warn",
"no-console": [
"warn",
{
"allow": [
"warn",
"error"
]
}
],
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
],
"prettier/prettier": "warn"
}
}
+17
View File
@@ -27,6 +27,23 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
# Run code quality per package
- name: React - Run linter
run: pnpm lint
working-directory: ./apps/client
- name: Server - Run linter
run: pnpm lint
working-directory: ./apps/server
- name: Utils - Run linter
run: pnpm lint
working-directory: ./packages/utils
- name: Types - Run linter
run: pnpm lint
working-directory: ./packages/types
# We choose to run tests separately
- name: React - Run unit tests
run: pnpm test:pipeline
+2 -18
View File
@@ -8,34 +8,18 @@
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:@typescript-eslint/recommended",
"eslint-config-prettier",
"plugin:@tanstack/eslint-plugin-query/recommended",
"prettier"
"plugin:@tanstack/eslint-plugin-query/recommended"
],
"plugins": [
"react",
"testing-library",
"simple-import-sort",
"@tanstack/query",
"@typescript-eslint",
"prettier"
"@tanstack/query"
],
"rules": {
"@typescript-eslint/no-non-null-assertion": "warn",
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"no-useless-concat": "warn",
"prefer-template": "warn",
"react/jsx-no-bind": [
"error",
{
+1 -1
View File
@@ -39,7 +39,7 @@
"build": "vite build",
"build:local": "cross-env NODE_ENV=local vite build",
"build:docker": "vite build",
"lint": "eslint .",
"lint": "eslint . --quiet",
"test": "vitest",
"test:pipeline": "vitest run",
"cleanup": "rm -rf .turbo && rm -rf node_modules && rm -rf build"
@@ -25,8 +25,14 @@ export default function MultiPartProgressBar(props: MultiPartProgressBar) {
return (
<div className={`multiprogress-bar ${hidden ? 'multiprogress-bar--hidden' : ''} ${className}`}>
<div className='multiprogress-bar__bg-normal' style={{ backgroundColor: normalColor }} />
<div className='multiprogress-bar__bg-warning' style={{ width: `${warningWidth}%`, backgroundColor: warningColor }} />
<div className='multiprogress-bar__bg-danger' style={{ width: `${dangerWidth}%`, backgroundColor: dangerColor }} />
<div
className='multiprogress-bar__bg-warning'
style={{ width: `${warningWidth}%`, backgroundColor: warningColor }}
/>
<div
className='multiprogress-bar__bg-danger'
style={{ width: `${dangerWidth}%`, backgroundColor: dangerColor }}
/>
<div className='multiprogress-bar__indicator' style={{ width: `${percentComplete}%` }} />
</div>
);
@@ -14,16 +14,7 @@ interface ScheduleItemProps {
}
export default function ScheduleItem(props: ScheduleItemProps) {
const {
selected,
timeStart,
timeEnd,
title,
presenter,
backstageEvent,
colour,
skip,
} = props;
const { selected, timeStart, timeEnd, title, presenter, backstageEvent, colour, skip } = props;
const start = formatTime(timeStart, { format: 'hh:mm' });
const end = formatTime(timeEnd, { format: 'hh:mm' });
@@ -37,9 +28,7 @@ export default function ScheduleItem(props: ScheduleItemProps) {
{`${start}${end} ${backstageEvent ? '*' : ''}`}
</div>
<div className='entry-title'>{title}</div>
{presenter && (
<div className='entry-presenter'>{presenter}</div>
)}
{presenter && <div className='entry-presenter'>{presenter}</div>}
</li>
);
}
@@ -13,12 +13,11 @@ export default function ScheduleNav({ className }: ScheduleNavProps) {
<div className={`schedule-nav ${className}`}>
{numPages > 1 &&
[...Array(numPages).keys()].map((i) => (
<div
key={i}
className={i === visiblePage ? 'schedule-nav__item schedule-nav__item--selected' : 'schedule-nav__item'}
/>
),
)}
<div
key={i}
className={i === visiblePage ? 'schedule-nav__item schedule-nav__item--selected' : 'schedule-nav__item'}
/>
))}
</div>
);
}
@@ -6,7 +6,6 @@ export default function useClickOutside<T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,
callback: ClickOutsideEventHandler,
) {
useEffect(() => {
function handleClick(event: MouseEvent) {
const element = ref?.current;
+11 -29
View File
@@ -1,10 +1,4 @@
import {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
export type TLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
@@ -26,13 +20,13 @@ const LOG_LEVEL: Record<TLogLevel, number> = {
};
const useFitText = ({
logLevel: logLevelOption = 'info',
maxFontSize = 100,
minFontSize = 20,
onFinish,
onStart,
resolution = 5,
}: TOptions = {}) => {
logLevel: logLevelOption = 'info',
maxFontSize = 100,
minFontSize = 20,
onFinish,
onStart,
resolution = 5,
}: TOptions = {}) => {
const logLevel = LOG_LEVEL[logLevelOption];
const initState = useCallback(() => {
@@ -112,8 +106,7 @@ const useFitText = ({
const isWithinResolution = Math.abs(fontSize - fontSizePrev) <= resolution;
const isOverflow =
!!ref.current &&
(ref.current.scrollHeight > ref.current.offsetHeight ||
ref.current.scrollWidth > ref.current.offsetWidth);
(ref.current.scrollHeight > ref.current.offsetHeight || ref.current.scrollWidth > ref.current.offsetWidth);
const isFailed = isOverflow && fontSize === fontSizePrev;
const isAsc = fontSize > fontSizePrev;
@@ -123,9 +116,7 @@ const useFitText = ({
if (isFailed) {
isCalculatingRef.current = false;
if (logLevel <= LOG_LEVEL.info) {
console.info(
`[use-fit-text] reached \`minFontSize = ${minFontSize}\` without fitting text`,
);
console.info(`[use-fit-text] reached \`minFontSize = ${minFontSize}\` without fitting text`);
}
} else if (isOverflow) {
setState({
@@ -160,16 +151,7 @@ const useFitText = ({
fontSizeMin: newMin,
fontSizePrev: fontSize,
});
}, [
calcKey,
fontSize,
fontSizeMax,
fontSizeMin,
fontSizePrev,
onFinish,
ref,
resolution,
]);
}, [calcKey, fontSize, fontSizeMax, fontSizeMin, fontSizePrev, onFinish, ref, resolution]);
return { fontSize: `${fontSize}%`, ref };
};
+1 -1
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef } from 'react';
/**
* @description utility hook to around setInterval
@@ -46,7 +46,7 @@ export const useLocalStorage = <T>(key: string, initialValue: T): [T, (value: T
setStoredValue(valueToStore);
window.localStorage.setItem(`ontime-${key}`, JSON.stringify(valueToStore));
} catch (error) {
console.log(error);
console.error(error);
}
};
return [storedValue, setValue];
@@ -1,5 +1,6 @@
import { resolvePath } from 'react-router-dom';
import { validateAlias, generateURLFromAlias, getAliasRoute } from '../aliases';
import { generateURLFromAlias, getAliasRoute, validateAlias } from '../aliases';
describe('An alias fails if incorrect', () => {
const testsToFail = [
@@ -12,7 +12,7 @@ test('Clamps a set of numbers correctly', () => {
{ num: -50, min: 0, max: 0, result: 0 },
{ num: 50.5, min: 0, max: 100, result: 50.5 },
{ num: 50, min: 0, max: 20.32, result: 20.32 },
{ num: 10, min: 20.32, max: 40, result: 20.32 }
{ num: 10, min: 20.32, max: 40, result: 20.32 },
];
testCases.forEach((t) => expect(clamp(t.num, t.min, t.max)).toBe(t.result));
+2 -3
View File
@@ -13,9 +13,8 @@ declare global {
};
process: {
type: string;
}
};
}
}
// eslint-disable-next-line import/no-anonymous-default-export
export default {}
export default {};
+1 -1
View File
@@ -6,6 +6,6 @@ import 'vitest';
// https://github.com/testing-library/jest-dom/issues/123
declare global {
namespace Vi {
interface Assertion<T = any> extends TestingLibraryMatchers<T, void> {}
type Assertion<T = any> = TestingLibraryMatchers<T, void>;
}
}
@@ -1,6 +0,0 @@
interface CuesheetRowProps {
row: OntimeRundownEntry;
isSelected: boolean;
}
function CuesheetRow() {}
@@ -21,7 +21,9 @@ import style from './SettingsModal.module.scss';
const aliasesDocsUrl = 'https://ontime.gitbook.io/v2/features/url-aliases';
// we wrap the array in an object to be simplify react-hook-form
type Aliases = { aliases: Alias[] };
type Aliases = {
aliases: Alias[];
};
export default function AliasesForm() {
const { data, status, isFetching, refetch } = useAliases();
@@ -93,8 +95,7 @@ export default function AliasesForm() {
<AlertDescription>
Custom aliases allow providing a short name for any ontime URL. <br />
It serves two primary purposes: <br />
- Providing dynamic URLs for automation or unattended screens <br />
- Simplifying complex URLs
- Providing dynamic URLs for automation or unattended screens <br />- Simplifying complex URLs
<ModalLink href={aliasesDocsUrl}>For more information, see the docs</ModalLink>
</AlertDescription>
</div>
+1
View File
@@ -20,6 +20,7 @@
},
"scripts": {
"postinstall": "",
"lint": "eslint . --quiet",
"dev:electron": "cross-env NODE_ENV=development electron .",
"dist-win": "electron-builder --publish=never --x64 --win",
"dist-mac": "electron-builder --publish=never --mac",
+27 -27
View File
@@ -8,23 +8,23 @@ function getApplicationMenu(isMac, askToQuit) {
return [
...(isMac
? [
{
label: 'Ontime',
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{
label: 'quit',
click: () => askToQuit(),
accelerator: 'Cmd+Q',
},
],
},
]
{
label: 'Ontime',
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{
label: 'quit',
click: () => askToQuit(),
accelerator: 'Cmd+Q',
},
],
},
]
: []),
{
label: 'File',
@@ -41,15 +41,15 @@ function getApplicationMenu(isMac, askToQuit) {
{ role: 'paste' },
...(isMac
? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [{ role: 'startSpeaking' }, { role: 'stopSpeaking' }],
},
]
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [{ role: 'startSpeaking' }, { role: 'stopSpeaking' }],
},
]
: [{ role: 'delete' }, { type: 'separator' }, { role: 'selectAll' }]),
],
},
@@ -161,4 +161,4 @@ function getApplicationMenu(isMac, askToQuit) {
];
}
module.exports = { getApplicationMenu }
module.exports = { getApplicationMenu };
+2 -2
View File
@@ -13,7 +13,7 @@ function getTrayMenu(showApp, askToQuit) {
label: 'Shutdown',
click: () => askToQuit(),
},
]
];
}
module.exports = { getTrayMenu }
module.exports = { getTrayMenu };
-15
View File
@@ -2,23 +2,8 @@
"env": {
"node": true
},
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"plugins": ["@typescript-eslint", "prettier"],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"rules": {
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
]
}
}
+1 -1
View File
@@ -52,7 +52,7 @@
"build:local": "pnpm prebuild && esbuild src/index.ts --log-level=error --platform=node --format=cjs --bundle --minify --outfile=dist/index.cjs",
"build:docker": "pnpm prebuild && esbuild src/index.ts --log-level=error --platform=node --format=cjs --minify --bundle --outfile=dist/docker.cjs",
"build:debug": "pnpm prebuild && esbuild src/app.ts --platform=node --format=cjs --bundle --outfile=dist/index.cjs",
"lint": "eslint .",
"lint": "eslint . --quiet",
"test": "cross-env IS_TEST=true vitest",
"test:pipeline": "cross-env IS_TEST=true vitest run",
"typecheck": "tsc --noEmit",
@@ -30,7 +30,7 @@ const whitelistedPayload = {
};
export function parse(field: string, value: unknown) {
if (!whitelistedPayload.hasOwnProperty(field)) {
if (!Object.hasOwn(whitelistedPayload, field)) {
throw new Error(`Field ${field} not permitted`);
}
const parserFn = whitelistedPayload[field];
@@ -51,7 +51,7 @@ export function updateEvent(
const event = EventLoader.getEventWithId(eventId);
if (event) {
let propertiesToUpdate = { [propertyName]: newValue };
const propertiesToUpdate = { [propertyName]: newValue };
// Handles the special case for duration
// needs to be converted to milliseconds
@@ -14,7 +14,7 @@ export function dispatchFromAdapter(
payload: unknown;
params?: Array<string>;
},
source?: 'osc' | 'ws',
_source?: 'osc' | 'ws',
) {
const payload = args.payload;
const typeComponents = type.toLowerCase().split('/');
@@ -1,6 +1,7 @@
/* eslint-disable no-console -- we are mocking the console */
import { vi } from 'vitest';
import { EndAction, TimerType } from 'ontime-types';
import { EndAction, OntimeEvent, TimerType } from 'ontime-types';
import { dbModel } from '../../models/dataModel.js';
import { parseExcel, parseJson, validateEvent } from '../parser.js';
+1 -6
View File
@@ -4,10 +4,5 @@
},
"env": {
"node": true
},
"extends": [
"eslint:recommended"
],
"plugins": [],
"rules": {}
}
}
+1
View File
@@ -25,6 +25,7 @@
"dev": "turbo run dev",
"dev:electron": "turbo run dev --filter=ontime",
"dev:server": "turbo run dev --filter=ontime-server",
"lint": "turbo run lint",
"build": "turbo run build",
"build:local": "turbo run build:local",
"dist-win": "turbo run dist-win",
-5
View File
@@ -3,11 +3,6 @@
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
+2 -1
View File
@@ -6,7 +6,8 @@
"private": true,
"description": "shared typings for ontime",
"scripts": {
"cleanup": "rm -rf .turbo && rm -rf node_modules"
"cleanup": "rm -rf .turbo && rm -rf node_modules",
"lint": "eslint ."
},
"keywords": [],
"author": "",
@@ -2,4 +2,4 @@ export type Alias = {
enabled: boolean;
alias: string;
pathAndParams: string;
}
};
@@ -9,5 +9,4 @@ export type UserFields = {
user7: string;
user8: string;
user9: string;
}
};
@@ -6,4 +6,4 @@ export type ViewSettings = {
warningThreshold: number;
dangerColor: string;
dangerThreshold: number;
}
};
@@ -23,5 +23,5 @@ export enum LogOrigin {
Rx = 'RX',
Server = 'SERVER',
Tx = 'TX',
User = 'USER'
User = 'USER',
}
@@ -6,4 +6,4 @@ export type Message = {
export type TimerMessage = Message & {
timerBlink: boolean;
timerBlackout: boolean;
}
};
+1 -14
View File
@@ -3,27 +3,14 @@
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": [
"simple-import-sort",
"prettier"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
"simple-import-sort"
],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"rules": {
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"simple-import-sort/exports": "error",
"simple-import-sort/imports": "error"
}
+1 -1
View File
@@ -5,7 +5,7 @@
"private": true,
"description": "shared logic for ontime",
"scripts": {
"lint": "eslint .",
"lint": "eslint . --quiet",
"test": "vitest",
"test:pipeline": "vitest run",
"cleanup": "rm -rf .turbo && rm -rf node_modules"
+1
View File
@@ -17,6 +17,7 @@ export function getIncrement(input: string): string {
const match = regex.exec(input);
if (match) {
// If a number is found, extract the non-numeric prefix, integer part, and decimal part
// eslint-disable-next-line prefer-const -- some items in the destructuring are modified
let [, prefix, integerPart, decimalPart] = match;
if (decimalPart) {