mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #5320 from jellyfin/renovate/major-linters
This commit is contained in:
commit
c516534ce3
34 changed files with 1765 additions and 920 deletions
|
@ -1,5 +0,0 @@
|
||||||
node_modules
|
|
||||||
coverage
|
|
||||||
dist
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
325
.eslintrc.js
325
.eslintrc.js
|
@ -1,325 +0,0 @@
|
||||||
const restrictedGlobals = require('confusing-browser-globals');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: [
|
|
||||||
'@stylistic',
|
|
||||||
'@typescript-eslint',
|
|
||||||
'react',
|
|
||||||
'import',
|
|
||||||
'sonarjs'
|
|
||||||
],
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
es2017: true,
|
|
||||||
es2020: true
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:react/recommended',
|
|
||||||
'plugin:import/errors',
|
|
||||||
'plugin:@eslint-community/eslint-comments/recommended',
|
|
||||||
'plugin:compat/recommended',
|
|
||||||
'plugin:sonarjs/recommended'
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'array-callback-return': ['error', { 'checkForEach': true }],
|
|
||||||
'curly': ['error', 'multi-line', 'consistent'],
|
|
||||||
'default-case-last': ['error'],
|
|
||||||
'max-params': ['error', 7],
|
|
||||||
'new-cap': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
'capIsNewExceptions': ['jQuery.Deferred'],
|
|
||||||
'newIsCapExceptionPattern': '\\.default$'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-duplicate-imports': ['error'],
|
|
||||||
'no-empty-function': ['error'],
|
|
||||||
'no-extend-native': ['error'],
|
|
||||||
'no-lonely-if': ['error'],
|
|
||||||
'no-nested-ternary': ['error'],
|
|
||||||
'no-redeclare': ['off'],
|
|
||||||
'@typescript-eslint/no-redeclare': ['error', { builtinGlobals: false }],
|
|
||||||
'no-restricted-globals': ['error'].concat(restrictedGlobals),
|
|
||||||
'no-return-assign': ['error'],
|
|
||||||
'no-return-await': ['error'],
|
|
||||||
'no-sequences': ['error', { 'allowInParentheses': false }],
|
|
||||||
'no-shadow': ['off'],
|
|
||||||
'@typescript-eslint/no-shadow': ['error'],
|
|
||||||
'no-throw-literal': ['error'],
|
|
||||||
'no-undef-init': ['error'],
|
|
||||||
'no-unneeded-ternary': ['error'],
|
|
||||||
'no-unused-expressions': ['off'],
|
|
||||||
'@typescript-eslint/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
|
||||||
'no-unused-private-class-members': ['error'],
|
|
||||||
'no-useless-rename': ['error'],
|
|
||||||
'no-useless-constructor': ['off'],
|
|
||||||
'@typescript-eslint/no-useless-constructor': ['error'],
|
|
||||||
'no-var': ['error'],
|
|
||||||
'no-void': ['error', { 'allowAsStatement': true }],
|
|
||||||
'no-warning-comments': ['warn', { 'terms': ['fixme', 'hack', 'xxx'] }],
|
|
||||||
'one-var': ['error', 'never'],
|
|
||||||
'prefer-const': ['error', { 'destructuring': 'all' }],
|
|
||||||
'prefer-promise-reject-errors': ['warn', { 'allowEmptyReject': true }],
|
|
||||||
'@typescript-eslint/prefer-for-of': ['error'],
|
|
||||||
'radix': ['error'],
|
|
||||||
'yoda': 'error',
|
|
||||||
|
|
||||||
'react/jsx-filename-extension': ['error', { 'extensions': ['.jsx', '.tsx'] }],
|
|
||||||
'react/jsx-no-bind': ['error'],
|
|
||||||
'react/jsx-no-useless-fragment': ['error'],
|
|
||||||
'react/jsx-no-constructed-context-values': ['error'],
|
|
||||||
'react/no-array-index-key': ['error'],
|
|
||||||
|
|
||||||
'sonarjs/no-inverted-boolean-check': ['error'],
|
|
||||||
// TODO: Enable the following rules and fix issues
|
|
||||||
'sonarjs/cognitive-complexity': ['off'],
|
|
||||||
'sonarjs/no-duplicate-string': ['off'],
|
|
||||||
|
|
||||||
'@stylistic/block-spacing': ['error'],
|
|
||||||
'@stylistic/brace-style': ['error', '1tbs', { 'allowSingleLine': true }],
|
|
||||||
'@stylistic/comma-dangle': ['error', 'never'],
|
|
||||||
'@stylistic/comma-spacing': ['error'],
|
|
||||||
'@stylistic/eol-last': ['error'],
|
|
||||||
'@stylistic/indent': ['error', 4, { 'SwitchCase': 1 }],
|
|
||||||
'@stylistic/jsx-quotes': ['error', 'prefer-single'],
|
|
||||||
'@stylistic/keyword-spacing': ['error'],
|
|
||||||
'@stylistic/max-statements-per-line': ['error'],
|
|
||||||
'@stylistic/no-floating-decimal': ['error'],
|
|
||||||
'@stylistic/no-multi-spaces': ['error'],
|
|
||||||
'@stylistic/no-multiple-empty-lines': ['error', { 'max': 1 }],
|
|
||||||
'@stylistic/no-trailing-spaces': ['error'],
|
|
||||||
'@stylistic/object-curly-spacing': ['error', 'always'],
|
|
||||||
'@stylistic/operator-linebreak': ['error', 'before', { overrides: { '?': 'after', ':': 'after', '=': 'after' } }],
|
|
||||||
'@stylistic/padded-blocks': ['error', 'never'],
|
|
||||||
'@stylistic/quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
|
|
||||||
'@stylistic/semi': ['error'],
|
|
||||||
'@stylistic/space-before-blocks': ['error'],
|
|
||||||
'@stylistic/space-infix-ops': ['error']
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
react: {
|
|
||||||
version: 'detect'
|
|
||||||
},
|
|
||||||
'import/parsers': {
|
|
||||||
'@typescript-eslint/parser': [ '.ts', '.tsx' ]
|
|
||||||
},
|
|
||||||
'import/resolver': {
|
|
||||||
node: {
|
|
||||||
extensions: [
|
|
||||||
'.js',
|
|
||||||
'.ts',
|
|
||||||
'.jsx',
|
|
||||||
'.tsx'
|
|
||||||
],
|
|
||||||
moduleDirectory: [
|
|
||||||
'node_modules',
|
|
||||||
'src'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
polyfills: [
|
|
||||||
// Native Promises Only
|
|
||||||
'Promise',
|
|
||||||
// whatwg-fetch
|
|
||||||
'fetch',
|
|
||||||
// document-register-element
|
|
||||||
'document.registerElement',
|
|
||||||
// resize-observer-polyfill
|
|
||||||
'ResizeObserver',
|
|
||||||
// fast-text-encoding
|
|
||||||
'TextEncoder',
|
|
||||||
// intersection-observer
|
|
||||||
'IntersectionObserver',
|
|
||||||
// Core-js
|
|
||||||
'Object.assign',
|
|
||||||
'Object.is',
|
|
||||||
'Object.setPrototypeOf',
|
|
||||||
'Object.toString',
|
|
||||||
'Object.freeze',
|
|
||||||
'Object.seal',
|
|
||||||
'Object.preventExtensions',
|
|
||||||
'Object.isFrozen',
|
|
||||||
'Object.isSealed',
|
|
||||||
'Object.isExtensible',
|
|
||||||
'Object.getOwnPropertyDescriptor',
|
|
||||||
'Object.getPrototypeOf',
|
|
||||||
'Object.keys',
|
|
||||||
'Object.entries',
|
|
||||||
'Object.getOwnPropertyNames',
|
|
||||||
'Function.name',
|
|
||||||
'Function.hasInstance',
|
|
||||||
'Array.from',
|
|
||||||
'Array.arrayOf',
|
|
||||||
'Array.copyWithin',
|
|
||||||
'Array.fill',
|
|
||||||
'Array.find',
|
|
||||||
'Array.findIndex',
|
|
||||||
'Array.iterator',
|
|
||||||
'String.fromCodePoint',
|
|
||||||
'String.raw',
|
|
||||||
'String.iterator',
|
|
||||||
'String.codePointAt',
|
|
||||||
'String.endsWith',
|
|
||||||
'String.includes',
|
|
||||||
'String.repeat',
|
|
||||||
'String.startsWith',
|
|
||||||
'String.trim',
|
|
||||||
'String.anchor',
|
|
||||||
'String.big',
|
|
||||||
'String.blink',
|
|
||||||
'String.bold',
|
|
||||||
'String.fixed',
|
|
||||||
'String.fontcolor',
|
|
||||||
'String.fontsize',
|
|
||||||
'String.italics',
|
|
||||||
'String.link',
|
|
||||||
'String.small',
|
|
||||||
'String.strike',
|
|
||||||
'String.sub',
|
|
||||||
'String.sup',
|
|
||||||
'RegExp',
|
|
||||||
'Number',
|
|
||||||
'Math',
|
|
||||||
'Date',
|
|
||||||
'async',
|
|
||||||
'Symbol',
|
|
||||||
'Map',
|
|
||||||
'Set',
|
|
||||||
'WeakMap',
|
|
||||||
'WeakSet',
|
|
||||||
'ArrayBuffer',
|
|
||||||
'DataView',
|
|
||||||
'Int8Array',
|
|
||||||
'Uint8Array',
|
|
||||||
'Uint8ClampedArray',
|
|
||||||
'Int16Array',
|
|
||||||
'Uint16Array',
|
|
||||||
'Int32Array',
|
|
||||||
'Uint32Array',
|
|
||||||
'Float32Array',
|
|
||||||
'Float64Array',
|
|
||||||
'Reflect',
|
|
||||||
// Temporary while eslint-compat-plugin is buggy
|
|
||||||
'document.querySelector'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
// Config files and development scripts
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'./babel.config.js',
|
|
||||||
'./.eslintrc.js',
|
|
||||||
'./postcss.config.js',
|
|
||||||
'./webpack.*.js',
|
|
||||||
'./scripts/**/*.js'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// JavaScript source files
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'./src/**/*.{js,jsx,ts,tsx}'
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
project: ['./tsconfig.json']
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
node: false,
|
|
||||||
browser: true,
|
|
||||||
es6: true,
|
|
||||||
es2017: true,
|
|
||||||
es2020: true
|
|
||||||
},
|
|
||||||
globals: {
|
|
||||||
// Tizen globals
|
|
||||||
'tizen': 'readonly',
|
|
||||||
'webapis': 'readonly',
|
|
||||||
// WebOS globals
|
|
||||||
'webOS': 'readonly',
|
|
||||||
// Dependency globals
|
|
||||||
'$': 'readonly',
|
|
||||||
'jQuery': 'readonly',
|
|
||||||
// Jellyfin globals
|
|
||||||
'ApiClient': 'writable',
|
|
||||||
'Events': 'writable',
|
|
||||||
'chrome': 'writable',
|
|
||||||
'Emby': 'readonly',
|
|
||||||
'Hls': 'writable',
|
|
||||||
'LibraryMenu': 'writable',
|
|
||||||
'Windows': 'readonly',
|
|
||||||
// Build time definitions
|
|
||||||
__COMMIT_SHA__: 'readonly',
|
|
||||||
__JF_BUILD_VERSION__: 'readonly',
|
|
||||||
__PACKAGE_JSON_NAME__: 'readonly',
|
|
||||||
__PACKAGE_JSON_VERSION__: 'readonly',
|
|
||||||
__USE_SYSTEM_FONTS__: 'readonly',
|
|
||||||
__WEBPACK_SERVE__: 'readonly'
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/naming-convention': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
selector: 'default',
|
|
||||||
format: [ 'camelCase', 'PascalCase' ],
|
|
||||||
leadingUnderscore: 'allow'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'variable',
|
|
||||||
format: [ 'camelCase', 'PascalCase', 'UPPER_CASE' ],
|
|
||||||
leadingUnderscore: 'allowSingleOrDouble',
|
|
||||||
trailingUnderscore: 'allowSingleOrDouble'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'typeLike',
|
|
||||||
format: [ 'PascalCase' ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'enumMember',
|
|
||||||
format: [ 'PascalCase', 'UPPER_CASE' ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: [ 'objectLiteralProperty', 'typeProperty' ],
|
|
||||||
format: [ 'camelCase', 'PascalCase' ],
|
|
||||||
leadingUnderscore: 'allowSingleOrDouble',
|
|
||||||
trailingUnderscore: 'allowSingleOrDouble'
|
|
||||||
},
|
|
||||||
// Ignore numbers, locale strings (en-us), aria/data attributes, CSS selectors,
|
|
||||||
// and api_key parameter
|
|
||||||
{
|
|
||||||
selector: [ 'objectLiteralProperty', 'typeProperty' ],
|
|
||||||
format: null,
|
|
||||||
filter: {
|
|
||||||
regex: '[ &\\-]|^([0-9]+)$|^api_key$',
|
|
||||||
match: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@typescript-eslint/prefer-string-starts-ends-with': ['error']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// TypeScript source files
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'./src/**/*.{ts,tsx}'
|
|
||||||
],
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:import/typescript',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:@eslint-community/eslint-comments/recommended',
|
|
||||||
'plugin:react/recommended',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
'plugin:jsx-a11y/recommended'
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-floating-promises': ['error'],
|
|
||||||
'@typescript-eslint/no-unused-vars': ['error'],
|
|
||||||
|
|
||||||
'sonarjs/cognitive-complexity': ['error']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
385
eslint.config.mjs
Normal file
385
eslint.config.mjs
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import eslint from '@eslint/js';
|
||||||
|
import comments from '@eslint-community/eslint-plugin-eslint-comments/configs';
|
||||||
|
import compat from 'eslint-plugin-compat';
|
||||||
|
import globals from 'globals';
|
||||||
|
// @ts-expect-error Missing type definition
|
||||||
|
import importPlugin from 'eslint-plugin-import';
|
||||||
|
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
||||||
|
import reactPlugin from 'eslint-plugin-react';
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks';
|
||||||
|
import restrictedGlobals from 'confusing-browser-globals';
|
||||||
|
import sonarjs from 'eslint-plugin-sonarjs';
|
||||||
|
import stylistic from '@stylistic/eslint-plugin';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
// @ts-expect-error Harmless type mismatch in dependency
|
||||||
|
comments.recommended,
|
||||||
|
compat.configs['flat/recommended'],
|
||||||
|
importPlugin.flatConfigs.errors,
|
||||||
|
sonarjs.configs.recommended,
|
||||||
|
|
||||||
|
reactPlugin.configs.flat.recommended,
|
||||||
|
{
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
jsxA11y.flatConfigs.recommended,
|
||||||
|
|
||||||
|
// Global ignores
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
'node_modules',
|
||||||
|
'coverage',
|
||||||
|
'dist',
|
||||||
|
'.idea',
|
||||||
|
'.vscode'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Global style rules
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
'@stylistic': stylistic
|
||||||
|
},
|
||||||
|
extends: [ importPlugin.flatConfigs.typescript ],
|
||||||
|
rules: {
|
||||||
|
'array-callback-return': ['error', { 'checkForEach': true }],
|
||||||
|
'curly': ['error', 'multi-line', 'consistent'],
|
||||||
|
'default-case-last': 'error',
|
||||||
|
'max-params': ['error', 7],
|
||||||
|
'new-cap': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'capIsNewExceptions': ['jQuery.Deferred'],
|
||||||
|
'newIsCapExceptionPattern': '\\.default$'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-duplicate-imports': 'error',
|
||||||
|
'no-empty-function': 'error',
|
||||||
|
'no-extend-native': 'error',
|
||||||
|
'no-lonely-if': 'error',
|
||||||
|
'no-nested-ternary': 'error',
|
||||||
|
'no-redeclare': 'off',
|
||||||
|
'@typescript-eslint/no-redeclare': ['error', { builtinGlobals: false }],
|
||||||
|
'no-restricted-globals': ['error'].concat(restrictedGlobals),
|
||||||
|
'no-return-assign': 'error',
|
||||||
|
'no-return-await': 'error',
|
||||||
|
'no-sequences': ['error', { 'allowInParentheses': false }],
|
||||||
|
'no-shadow': 'off',
|
||||||
|
'@typescript-eslint/no-shadow': 'error',
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
'no-undef-init': 'error',
|
||||||
|
'no-unneeded-ternary': 'error',
|
||||||
|
'no-unused-expressions': 'off',
|
||||||
|
'@typescript-eslint/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
||||||
|
'no-unused-private-class-members': 'error',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
|
'no-useless-rename': 'error',
|
||||||
|
'no-useless-constructor': 'off',
|
||||||
|
'@typescript-eslint/no-useless-constructor': 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
'no-void': ['error', { 'allowAsStatement': true }],
|
||||||
|
'no-warning-comments': ['warn', { 'terms': ['hack', 'xxx'] }],
|
||||||
|
'one-var': ['error', 'never'],
|
||||||
|
'prefer-const': ['error', { 'destructuring': 'all' }],
|
||||||
|
'prefer-promise-reject-errors': ['warn', { 'allowEmptyReject': true }],
|
||||||
|
'@typescript-eslint/prefer-for-of': 'error',
|
||||||
|
'radix': 'error',
|
||||||
|
'yoda': 'error',
|
||||||
|
|
||||||
|
'sonarjs/fixme-tag': 'warn',
|
||||||
|
'sonarjs/todo-tag': 'off',
|
||||||
|
'sonarjs/deprecation': 'warn',
|
||||||
|
'sonarjs/no-alphabetical-sort': 'warn',
|
||||||
|
'sonarjs/no-inverted-boolean-check': 'error',
|
||||||
|
'sonarjs/no-selector-parameter': 'off',
|
||||||
|
'sonarjs/pseudo-random': 'warn',
|
||||||
|
// TODO: Enable the following sonarjs rules and fix issues
|
||||||
|
'sonarjs/no-duplicate-string': 'off',
|
||||||
|
'sonarjs/no-nested-functions': 'warn',
|
||||||
|
|
||||||
|
// TODO: Replace with stylistic.configs.customize()
|
||||||
|
'@stylistic/block-spacing': 'error',
|
||||||
|
'@stylistic/brace-style': ['error', '1tbs', { 'allowSingleLine': true }],
|
||||||
|
'@stylistic/comma-dangle': ['error', 'never'],
|
||||||
|
'@stylistic/comma-spacing': 'error',
|
||||||
|
'@stylistic/eol-last': 'error',
|
||||||
|
'@stylistic/indent': ['error', 4, { 'SwitchCase': 1 }],
|
||||||
|
'@stylistic/jsx-quotes': ['error', 'prefer-single'],
|
||||||
|
'@stylistic/keyword-spacing': 'error',
|
||||||
|
'@stylistic/max-statements-per-line': 'error',
|
||||||
|
'@stylistic/no-floating-decimal': 'error',
|
||||||
|
'@stylistic/no-mixed-spaces-and-tabs': 'error',
|
||||||
|
'@stylistic/no-multi-spaces': 'error',
|
||||||
|
'@stylistic/no-multiple-empty-lines': ['error', { 'max': 1 }],
|
||||||
|
'@stylistic/no-trailing-spaces': 'error',
|
||||||
|
'@stylistic/object-curly-spacing': ['error', 'always'],
|
||||||
|
'@stylistic/operator-linebreak': ['error', 'before', { overrides: { '?': 'after', ':': 'after', '=': 'after' } }],
|
||||||
|
'@stylistic/padded-blocks': ['error', 'never'],
|
||||||
|
'@stylistic/quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
|
||||||
|
'@stylistic/semi': 'error',
|
||||||
|
'@stylistic/space-before-blocks': 'error',
|
||||||
|
'@stylistic/space-infix-ops': 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Config files use node globals
|
||||||
|
{
|
||||||
|
ignores: [ 'src' ],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Config files are commonjs by default
|
||||||
|
{
|
||||||
|
files: [ '**/*.{cjs,js}' ],
|
||||||
|
ignores: [ 'src' ],
|
||||||
|
languageOptions: {
|
||||||
|
sourceType: 'commonjs'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-require-imports': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// App files
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'src/**/*.{js,jsx,ts,tsx}'
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
// Tizen globals
|
||||||
|
'tizen': false,
|
||||||
|
'webapis': false,
|
||||||
|
// WebOS globals
|
||||||
|
'webOS': false,
|
||||||
|
// Dependency globals
|
||||||
|
'$': false,
|
||||||
|
'jQuery': false,
|
||||||
|
// Jellyfin globals
|
||||||
|
'ApiClient': true,
|
||||||
|
'Events': true,
|
||||||
|
'chrome': true,
|
||||||
|
'Emby': false,
|
||||||
|
'Hls': true,
|
||||||
|
'LibraryMenu': true,
|
||||||
|
'Windows': false,
|
||||||
|
// Build time definitions
|
||||||
|
__COMMIT_SHA__: false,
|
||||||
|
__JF_BUILD_VERSION__: false,
|
||||||
|
__PACKAGE_JSON_NAME__: false,
|
||||||
|
__PACKAGE_JSON_VERSION__: false,
|
||||||
|
__USE_SYSTEM_FONTS__: false,
|
||||||
|
__WEBPACK_SERVE__: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import/resolver': {
|
||||||
|
node: {
|
||||||
|
extensions: [
|
||||||
|
'.js',
|
||||||
|
'.ts',
|
||||||
|
'.jsx',
|
||||||
|
'.tsx'
|
||||||
|
],
|
||||||
|
moduleDirectory: [
|
||||||
|
'node_modules',
|
||||||
|
'src'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
polyfills: [
|
||||||
|
'Promise',
|
||||||
|
// whatwg-fetch
|
||||||
|
'fetch',
|
||||||
|
// document-register-element
|
||||||
|
'document.registerElement',
|
||||||
|
// resize-observer-polyfill
|
||||||
|
'ResizeObserver',
|
||||||
|
// fast-text-encoding
|
||||||
|
'TextEncoder',
|
||||||
|
// intersection-observer
|
||||||
|
'IntersectionObserver',
|
||||||
|
// Core-js
|
||||||
|
'Object.assign',
|
||||||
|
'Object.is',
|
||||||
|
'Object.setPrototypeOf',
|
||||||
|
'Object.toString',
|
||||||
|
'Object.freeze',
|
||||||
|
'Object.seal',
|
||||||
|
'Object.preventExtensions',
|
||||||
|
'Object.isFrozen',
|
||||||
|
'Object.isSealed',
|
||||||
|
'Object.isExtensible',
|
||||||
|
'Object.getOwnPropertyDescriptor',
|
||||||
|
'Object.getPrototypeOf',
|
||||||
|
'Object.keys',
|
||||||
|
'Object.entries',
|
||||||
|
'Object.getOwnPropertyNames',
|
||||||
|
'Function.name',
|
||||||
|
'Function.hasInstance',
|
||||||
|
'Array.from',
|
||||||
|
'Array.arrayOf',
|
||||||
|
'Array.copyWithin',
|
||||||
|
'Array.fill',
|
||||||
|
'Array.find',
|
||||||
|
'Array.findIndex',
|
||||||
|
'Array.iterator',
|
||||||
|
'String.fromCodePoint',
|
||||||
|
'String.raw',
|
||||||
|
'String.iterator',
|
||||||
|
'String.codePointAt',
|
||||||
|
'String.endsWith',
|
||||||
|
'String.includes',
|
||||||
|
'String.repeat',
|
||||||
|
'String.startsWith',
|
||||||
|
'String.trim',
|
||||||
|
'String.anchor',
|
||||||
|
'String.big',
|
||||||
|
'String.blink',
|
||||||
|
'String.bold',
|
||||||
|
'String.fixed',
|
||||||
|
'String.fontcolor',
|
||||||
|
'String.fontsize',
|
||||||
|
'String.italics',
|
||||||
|
'String.link',
|
||||||
|
'String.small',
|
||||||
|
'String.strike',
|
||||||
|
'String.sub',
|
||||||
|
'String.sup',
|
||||||
|
'RegExp',
|
||||||
|
'Number',
|
||||||
|
'Math',
|
||||||
|
'Date',
|
||||||
|
'async',
|
||||||
|
'Symbol',
|
||||||
|
'Map',
|
||||||
|
'Set',
|
||||||
|
'WeakMap',
|
||||||
|
'WeakSet',
|
||||||
|
'ArrayBuffer',
|
||||||
|
'DataView',
|
||||||
|
'Int8Array',
|
||||||
|
'Uint8Array',
|
||||||
|
'Uint8ClampedArray',
|
||||||
|
'Int16Array',
|
||||||
|
'Uint16Array',
|
||||||
|
'Int32Array',
|
||||||
|
'Uint32Array',
|
||||||
|
'Float32Array',
|
||||||
|
'Float64Array',
|
||||||
|
'Reflect'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// TODO: Add typescript recommended typed rules
|
||||||
|
'@typescript-eslint/naming-convention': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
selector: 'default',
|
||||||
|
format: [ 'camelCase', 'PascalCase' ],
|
||||||
|
leadingUnderscore: 'allow'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'variable',
|
||||||
|
format: [ 'camelCase', 'PascalCase', 'UPPER_CASE' ],
|
||||||
|
leadingUnderscore: 'allowSingleOrDouble',
|
||||||
|
trailingUnderscore: 'allowSingleOrDouble'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'typeLike',
|
||||||
|
format: [ 'PascalCase' ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'enumMember',
|
||||||
|
format: [ 'PascalCase', 'UPPER_CASE' ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: [ 'objectLiteralProperty', 'typeProperty' ],
|
||||||
|
format: [ 'camelCase', 'PascalCase' ],
|
||||||
|
leadingUnderscore: 'allowSingleOrDouble',
|
||||||
|
trailingUnderscore: 'allowSingleOrDouble'
|
||||||
|
},
|
||||||
|
// Ignore numbers, locale strings (en-us), aria/data attributes, CSS selectors,
|
||||||
|
// and api_key parameter
|
||||||
|
{
|
||||||
|
selector: [ 'objectLiteralProperty', 'typeProperty' ],
|
||||||
|
format: null,
|
||||||
|
filter: {
|
||||||
|
regex: '[ &\\-]|^([0-9]+)$|^api_key$',
|
||||||
|
match: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-floating-promises': 'error',
|
||||||
|
'@typescript-eslint/prefer-string-starts-ends-with': 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// React files
|
||||||
|
{
|
||||||
|
files: [ 'src/**/*.{jsx,tsx}' ],
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': reactHooks
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'react/jsx-filename-extension': ['error', { 'extensions': ['.jsx', '.tsx'] }],
|
||||||
|
'react/jsx-no-bind': 'error',
|
||||||
|
'react/jsx-no-useless-fragment': 'error',
|
||||||
|
'react/no-array-index-key': 'error',
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Service worker
|
||||||
|
{
|
||||||
|
files: [ 'src/serviceworker.js' ],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.serviceworker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Legacy JS (less strict)
|
||||||
|
{
|
||||||
|
files: [ 'src/**/*.{js,jsx}' ],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-floating-promises': 'off',
|
||||||
|
'@typescript-eslint/no-this-alias': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
|
||||||
|
'sonarjs/public-static-readonly': 'off',
|
||||||
|
|
||||||
|
// TODO: Enable the following rules and fix issues
|
||||||
|
'sonarjs/cognitive-complexity': 'off',
|
||||||
|
'sonarjs/constructor-for-side-effects': 'off',
|
||||||
|
'sonarjs/function-return-type': 'off',
|
||||||
|
'sonarjs/no-async-constructor': 'off',
|
||||||
|
'sonarjs/no-duplicate-string': 'off',
|
||||||
|
'sonarjs/no-ignored-exceptions': 'off',
|
||||||
|
'sonarjs/no-invariant-returns': 'warn',
|
||||||
|
'sonarjs/no-nested-functions': 'off',
|
||||||
|
'sonarjs/void-use': 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
1878
package-lock.json
generated
1878
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
@ -10,7 +10,8 @@
|
||||||
"@babel/preset-env": "7.25.8",
|
"@babel/preset-env": "7.25.8",
|
||||||
"@babel/preset-react": "7.25.7",
|
"@babel/preset-react": "7.25.7",
|
||||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||||
"@stylistic/eslint-plugin": "2.13.0",
|
"@eslint/js": "9.20.0",
|
||||||
|
"@stylistic/eslint-plugin": "3.1.0",
|
||||||
"@stylistic/stylelint-plugin": "3.1.1",
|
"@stylistic/stylelint-plugin": "3.1.1",
|
||||||
"@types/dompurify": "3.0.5",
|
"@types/dompurify": "3.0.5",
|
||||||
"@types/escape-html": "1.0.4",
|
"@types/escape-html": "1.0.4",
|
||||||
|
@ -21,8 +22,7 @@
|
||||||
"@types/react-dom": "18.3.1",
|
"@types/react-dom": "18.3.1",
|
||||||
"@types/react-lazy-load-image-component": "1.6.4",
|
"@types/react-lazy-load-image-component": "1.6.4",
|
||||||
"@types/sortablejs": "1.15.8",
|
"@types/sortablejs": "1.15.8",
|
||||||
"@typescript-eslint/eslint-plugin": "8.21.0",
|
"@typescript-eslint/parser": "8.24.1",
|
||||||
"@typescript-eslint/parser": "8.21.0",
|
|
||||||
"@uupaa/dynamic-import-polyfill": "1.0.2",
|
"@uupaa/dynamic-import-polyfill": "1.0.2",
|
||||||
"@vitest/coverage-v8": "3.0.4",
|
"@vitest/coverage-v8": "3.0.4",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.20",
|
||||||
|
@ -34,15 +34,16 @@
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"cssnano": "7.0.6",
|
"cssnano": "7.0.6",
|
||||||
"es-check": "7.2.1",
|
"es-check": "7.2.1",
|
||||||
"eslint": "8.57.1",
|
"eslint": "9.20.1",
|
||||||
"eslint-plugin-compat": "4.2.0",
|
"eslint-plugin-compat": "6.0.2",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"eslint-plugin-jsx-a11y": "6.10.2",
|
"eslint-plugin-jsx-a11y": "6.10.2",
|
||||||
"eslint-plugin-react": "7.37.4",
|
"eslint-plugin-react": "7.37.4",
|
||||||
"eslint-plugin-react-hooks": "4.6.2",
|
"eslint-plugin-react-hooks": "5.1.0",
|
||||||
"eslint-plugin-sonarjs": "0.25.1",
|
"eslint-plugin-sonarjs": "3.0.2",
|
||||||
"expose-loader": "5.0.0",
|
"expose-loader": "5.0.0",
|
||||||
"fork-ts-checker-webpack-plugin": "9.0.2",
|
"fork-ts-checker-webpack-plugin": "9.0.2",
|
||||||
|
"globals": "15.15.0",
|
||||||
"html-loader": "5.1.0",
|
"html-loader": "5.1.0",
|
||||||
"html-webpack-plugin": "5.6.3",
|
"html-webpack-plugin": "5.6.3",
|
||||||
"jsdom": "25.0.1",
|
"jsdom": "25.0.1",
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
"stylelint-scss": "6.10.1",
|
"stylelint-scss": "6.10.1",
|
||||||
"ts-loader": "9.5.2",
|
"ts-loader": "9.5.2",
|
||||||
"typescript": "5.7.3",
|
"typescript": "5.7.3",
|
||||||
|
"typescript-eslint": "8.24.1",
|
||||||
"vitest": "3.0.4",
|
"vitest": "3.0.4",
|
||||||
"webpack": "5.97.1",
|
"webpack": "5.97.1",
|
||||||
"webpack-bundle-analyzer": "4.10.2",
|
"webpack-bundle-analyzer": "4.10.2",
|
||||||
|
|
|
@ -17,6 +17,7 @@ const GenresItemsContainer: FC<GenresItemsContainerProps> = ({
|
||||||
parentId,
|
parentId,
|
||||||
collectionType,
|
collectionType,
|
||||||
itemType
|
itemType
|
||||||
|
// eslint-disable-next-line sonarjs/function-return-type
|
||||||
}) => {
|
}) => {
|
||||||
const { isLoading, data: genresResult } = useGetGenres(itemType, parentId);
|
const { isLoading, data: genresResult } = useGetGenres(itemType, parentId);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import SectionContainer from 'components/common/SectionContainer';
|
||||||
import { CardShape } from 'utils/card';
|
import { CardShape } from 'utils/card';
|
||||||
import type { LibraryViewProps } from 'types/library';
|
import type { LibraryViewProps } from 'types/library';
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/function-return-type
|
||||||
const UpcomingView: FC<LibraryViewProps> = ({ parentId }) => {
|
const UpcomingView: FC<LibraryViewProps> = ({ parentId }) => {
|
||||||
const { isLoading, data: groupsUpcomingEpisodes } =
|
const { isLoading, data: groupsUpcomingEpisodes } =
|
||||||
useGetGroupsUpcomingEpisodes(parentId);
|
useGetGroupsUpcomingEpisodes(parentId);
|
||||||
|
|
|
@ -17,6 +17,7 @@ export interface MovedItem {
|
||||||
playlistItemId: string
|
playlistItemId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/redundant-type-aliases
|
||||||
export type PlayerErrorCode = string;
|
export type PlayerErrorCode = string;
|
||||||
|
|
||||||
export interface PlayerStopInfo {
|
export interface PlayerStopInfo {
|
||||||
|
|
|
@ -16,6 +16,7 @@ const MarkdownBox: FC<MarkdownBoxProps> = ({
|
||||||
<Box
|
<Box
|
||||||
dangerouslySetInnerHTML={
|
dangerouslySetInnerHTML={
|
||||||
markdown ?
|
markdown ?
|
||||||
|
// eslint-disable-next-line sonarjs/disabled-auto-escaping
|
||||||
{ __html: DOMPurify.sanitize(markdownIt({ html: true }).render(markdown)) } :
|
{ __html: DOMPurify.sanitize(markdownIt({ html: true }).render(markdown)) } :
|
||||||
undefined
|
undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ interface CardTextProps {
|
||||||
|
|
||||||
const CardText: FC<CardTextProps> = ({ className, textLine }) => {
|
const CardText: FC<CardTextProps> = ({ className, textLine }) => {
|
||||||
const { title, titleAction } = textLine;
|
const { title, titleAction } = textLine;
|
||||||
|
// eslint-disable-next-line sonarjs/function-return-type
|
||||||
const renderCardText = () => {
|
const renderCardText = () => {
|
||||||
if (titleAction) {
|
if (titleAction) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -323,7 +323,7 @@ function shouldShowMediaTitle(
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldShowExtraType(itemExtraType: NullableString) {
|
function shouldShowExtraType(itemExtraType: NullableString) {
|
||||||
return itemExtraType && itemExtraType !== 'Unknown';
|
return !!(itemExtraType && itemExtraType !== 'Unknown');
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldShowSeriesYearOrYear(
|
function shouldShowSeriesYearOrYear(
|
||||||
|
@ -351,7 +351,7 @@ function shouldShowPersonRoleOrType(
|
||||||
showPersonRoleOrType: boolean | undefined,
|
showPersonRoleOrType: boolean | undefined,
|
||||||
item: ItemDto
|
item: ItemDto
|
||||||
) {
|
) {
|
||||||
return showPersonRoleOrType && (item as BaseItemPerson).Role;
|
return !!(showPersonRoleOrType && (item as BaseItemPerson).Role);
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldShowParentTitle(
|
function shouldShowParentTitle(
|
||||||
|
|
|
@ -195,6 +195,7 @@ function buildCardsHtmlInternal(items, options) {
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line sonarjs/no-dead-store
|
||||||
hasOpenSection = false;
|
hasOpenSection = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +216,7 @@ function buildCardsHtmlInternal(items, options) {
|
||||||
if (options.rows && itemsInRow === 0) {
|
if (options.rows && itemsInRow === 0) {
|
||||||
if (hasOpenRow) {
|
if (hasOpenRow) {
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
// eslint-disable-next-line sonarjs/no-dead-store
|
||||||
hasOpenRow = false;
|
hasOpenRow = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import globalize from '../../../lib/globalize';
|
||||||
import IconButtonElement from '../../../elements/IconButtonElement';
|
import IconButtonElement from '../../../elements/IconButtonElement';
|
||||||
|
|
||||||
type AccessScheduleListProps = {
|
type AccessScheduleListProps = {
|
||||||
index: number;
|
index?: number;
|
||||||
DayOfWeek?: string;
|
DayOfWeek?: string;
|
||||||
StartHour?: number ;
|
StartHour?: number ;
|
||||||
EndHour?: number;
|
EndHour?: number;
|
||||||
|
|
|
@ -3,7 +3,6 @@ import globalize from 'lib/globalize';
|
||||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||||
import { getParameterByName } from 'utils/url';
|
import { getParameterByName } from 'utils/url';
|
||||||
|
|
||||||
import { appHost } from './apphost';
|
|
||||||
import cardBuilder from './cardbuilder/cardBuilder';
|
import cardBuilder from './cardbuilder/cardBuilder';
|
||||||
import imageLoader from './images/imageLoader';
|
import imageLoader from './images/imageLoader';
|
||||||
import layoutManager from './layoutManager';
|
import layoutManager from './layoutManager';
|
||||||
|
@ -160,8 +159,10 @@ function loadSection(elem, userId, topParentId, section, isSingleSection) {
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right">';
|
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right">';
|
||||||
}
|
}
|
||||||
|
|
||||||
let cardLayout = appHost.preferVisualCards && section.autoCardLayout && section.showTitle;
|
// NOTE: Why is card layout always disabled?
|
||||||
cardLayout = false;
|
// let cardLayout = appHost.preferVisualCards && section.autoCardLayout && section.showTitle;
|
||||||
|
const cardLayout = false;
|
||||||
|
|
||||||
html += cardBuilder.getCardsHtml(result.Items, {
|
html += cardBuilder.getCardsHtml(result.Items, {
|
||||||
preferThumb: section.preferThumb,
|
preferThumb: section.preferThumb,
|
||||||
shape: section.shape,
|
shape: section.shape,
|
||||||
|
|
|
@ -74,6 +74,7 @@ function fetchWithTimeout(url, options, timeoutMs) {
|
||||||
*/
|
*/
|
||||||
function paramsToString(params) {
|
function paramsToString(params) {
|
||||||
return Object.entries(params)
|
return Object.entries(params)
|
||||||
|
// eslint-disable-next-line sonarjs/different-types-comparison
|
||||||
.filter(([, v]) => v !== null && v !== undefined && v !== '')
|
.filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||||
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
||||||
.join('&');
|
.join('&');
|
||||||
|
|
|
@ -389,6 +389,7 @@ function intersectsInternal(a1, a2, b1, b2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function intersects(a1, a2, b1, b2) {
|
function intersects(a1, a2, b1, b2) {
|
||||||
|
// eslint-disable-next-line sonarjs/arguments-order
|
||||||
return intersectsInternal(a1, a2, b1, b2) || intersectsInternal(b1, b2, a1, a2);
|
return intersectsInternal(a1, a2, b1, b2) || intersectsInternal(b1, b2, a1, a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ export function handleHlsJsMediaError(instance, reject) {
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
|
|
||||||
if (window.performance?.now) {
|
if (window.performance?.now) {
|
||||||
now = performance.now(); // eslint-disable-line compat/compat
|
now = performance.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!recoverDecodingErrorDate || (now - recoverDecodingErrorDate) > 3000) {
|
if (!recoverDecodingErrorDate || (now - recoverDecodingErrorDate) > 3000) {
|
||||||
|
@ -373,6 +373,7 @@ export function getBufferedRanges(instance, elem) {
|
||||||
start = 0;
|
start = 0;
|
||||||
}
|
}
|
||||||
if (!isValidDuration(end)) {
|
if (!isValidDuration(end)) {
|
||||||
|
// eslint-disable-next-line sonarjs/no-dead-store
|
||||||
end = 0;
|
end = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -417,6 +417,7 @@ export function getListViewHtml(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableOverview && item.Overview) {
|
if (enableOverview && item.Overview) {
|
||||||
|
// eslint-disable-next-line sonarjs/disabled-auto-escaping
|
||||||
const overview = DOMPurify.sanitize(markdownIt({ html: true }).render(item.Overview || ''));
|
const overview = DOMPurify.sanitize(markdownIt({ html: true }).render(item.Overview || ''));
|
||||||
html += '<div class="secondary listItem-overview listItemBodyText">';
|
html += '<div class="secondary listItem-overview listItemBodyText">';
|
||||||
html += '<bdi>' + overview + '</bdi>';
|
html += '<bdi>' + overview + '</bdi>';
|
||||||
|
|
|
@ -13,6 +13,7 @@ interface MediaInfoItemProps {
|
||||||
const MediaInfoItem: FC<MediaInfoItemProps> = ({ className, miscInfo }) => {
|
const MediaInfoItem: FC<MediaInfoItemProps> = ({ className, miscInfo }) => {
|
||||||
const { text, textAction, cssClass, type } = miscInfo;
|
const { text, textAction, cssClass, type } = miscInfo;
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/function-return-type
|
||||||
const renderText = () => {
|
const renderText = () => {
|
||||||
if (textAction) {
|
if (textAction) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -208,6 +208,7 @@ function getMimeType(type, container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getParam(name, url) {
|
function getParam(name, url) {
|
||||||
|
// eslint-disable-next-line sonarjs/single-char-in-character-classes
|
||||||
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
|
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
|
||||||
const regexS = '[\\?&]' + name + '=([^&#]*)';
|
const regexS = '[\\?&]' + name + '=([^&#]*)';
|
||||||
const regex = new RegExp(regexS, 'i');
|
const regex = new RegExp(regexS, 'i');
|
||||||
|
@ -2115,6 +2116,7 @@ export class PlaybackManager {
|
||||||
|
|
||||||
if (!state) {
|
if (!state) {
|
||||||
playerStates[player.name] = {};
|
playerStates[player.name] = {};
|
||||||
|
// eslint-disable-next-line sonarjs/no-dead-store
|
||||||
state = playerStates[player.name];
|
state = playerStates[player.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ class SkipSegment extends PlaybackSubscriber {
|
||||||
elem.classList.remove('no-transition');
|
elem.classList.remove('no-transition');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/void-use
|
||||||
void elem.offsetWidth;
|
void elem.offsetWidth;
|
||||||
|
|
||||||
const hasFocus = document.activeElement && focusManager.isCurrentlyFocusable(document.activeElement);
|
const hasFocus = document.activeElement && focusManager.isCurrentlyFocusable(document.activeElement);
|
||||||
|
@ -111,6 +112,7 @@ class SkipSegment extends PlaybackSubscriber {
|
||||||
const elem = this.skipElement;
|
const elem = this.skipElement;
|
||||||
if (elem) {
|
if (elem) {
|
||||||
elem.classList.remove('no-transition');
|
elem.classList.remove('no-transition');
|
||||||
|
// eslint-disable-next-line sonarjs/void-use
|
||||||
void elem.offsetWidth;
|
void elem.offsetWidth;
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
// TODO: Check if needed and move to external dependency
|
// TODO: Check if needed and move to external dependency
|
||||||
// From https://github.com/parshap/node-sanitize-filename
|
// From https://github.com/parshap/node-sanitize-filename
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/duplicates-in-character-class
|
||||||
const illegalRe = /[/?<>\\:*|":]/g;
|
const illegalRe = /[/?<>\\:*|":]/g;
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex, sonarjs/no-control-regex
|
||||||
const controlRe = /[\x00-\x1f\x80-\x9f]/g;
|
const controlRe = /[\x00-\x1f\x80-\x9f]/g;
|
||||||
const reservedRe = /^\.+$/;
|
const reservedRe = /^\.+$/;
|
||||||
|
// eslint-disable-next-line sonarjs/concise-regex
|
||||||
const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
||||||
|
// eslint-disable-next-line sonarjs/slow-regex
|
||||||
const windowsTrailingRe = /[. ]+$/;
|
const windowsTrailingRe = /[. ]+$/;
|
||||||
|
|
||||||
function isHighSurrogate(codePoint) {
|
function isHighSurrogate(codePoint) {
|
||||||
|
@ -64,6 +67,7 @@ function truncate(string, byteLength) {
|
||||||
segment = string[i];
|
segment = string[i];
|
||||||
|
|
||||||
if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
|
if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
|
||||||
|
// eslint-disable-next-line sonarjs/updated-loop-counter
|
||||||
i += 1;
|
i += 1;
|
||||||
segment += string[i];
|
segment += string[i];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { appHost } from 'components/apphost';
|
|
||||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||||
import focusManager from 'components/focusManager';
|
import focusManager from 'components/focusManager';
|
||||||
import layoutManager from 'components/layoutManager';
|
import layoutManager from 'components/layoutManager';
|
||||||
|
@ -202,8 +201,9 @@ function getRouteUrl(section, serverId) {
|
||||||
|
|
||||||
function getItemsHtmlFn(section) {
|
function getItemsHtmlFn(section) {
|
||||||
return function (items) {
|
return function (items) {
|
||||||
let cardLayout = appHost.preferVisualCards && section.autoCardLayout && section.showTitle;
|
// NOTE: Why is card layout always disabled?
|
||||||
cardLayout = false;
|
// let cardLayout = appHost.preferVisualCards && section.autoCardLayout && section.showTitle;
|
||||||
|
const cardLayout = false;
|
||||||
const serverId = this.apiClient.serverId();
|
const serverId = this.apiClient.serverId();
|
||||||
const leadingButtons = layoutManager.tv ? [{
|
const leadingButtons = layoutManager.tv ? [{
|
||||||
name: globalize.translate('All'),
|
name: globalize.translate('All'),
|
||||||
|
|
|
@ -877,6 +877,7 @@ function renderOverview(page, item) {
|
||||||
const overviewElements = page.querySelectorAll('.overview');
|
const overviewElements = page.querySelectorAll('.overview');
|
||||||
|
|
||||||
if (overviewElements.length > 0) {
|
if (overviewElements.length > 0) {
|
||||||
|
// eslint-disable-next-line sonarjs/disabled-auto-escaping
|
||||||
const overview = DOMPurify.sanitize(markdownIt({ html: true }).render(item.Overview || ''));
|
const overview = DOMPurify.sanitize(markdownIt({ html: true }).render(item.Overview || ''));
|
||||||
|
|
||||||
if (overview) {
|
if (overview) {
|
||||||
|
@ -1378,6 +1379,7 @@ function renderChildren(page, item) {
|
||||||
if (item.Type == 'MusicAlbum') {
|
if (item.Type == 'MusicAlbum') {
|
||||||
let showArtist = false;
|
let showArtist = false;
|
||||||
for (const track of result.Items) {
|
for (const track of result.Items) {
|
||||||
|
// eslint-disable-next-line sonarjs/no-alphabetical-sort
|
||||||
if (!isEqual(track.ArtistItems.map(x => x.Id).sort(), track.AlbumArtists.map(x => x.Id).sort())) {
|
if (!isEqual(track.ArtistItems.map(x => x.Id).sort(), track.AlbumArtists.map(x => x.Id).sort())) {
|
||||||
showArtist = true;
|
showArtist = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -36,6 +36,7 @@ function handleConnectionResult(page, result) {
|
||||||
|
|
||||||
function submitServer(page) {
|
function submitServer(page) {
|
||||||
loading.show();
|
loading.show();
|
||||||
|
// eslint-disable-next-line sonarjs/slow-regex
|
||||||
const host = page.querySelector('#txtServerHost').value.replace(/\/+$/, '');
|
const host = page.querySelector('#txtServerHost').value.replace(/\/+$/, '');
|
||||||
ServerConnections.connectToAddress(host, {
|
ServerConnections.connectToAddress(host, {
|
||||||
enableAutoLogin: appSettings.enableAutoLogin()
|
enableAutoLogin: appSettings.enableAutoLogin()
|
||||||
|
|
|
@ -292,6 +292,7 @@ export default function (view, params) {
|
||||||
apiClient.getJSON(apiClient.getUrl('Branding/Configuration')).then(function (options) {
|
apiClient.getJSON(apiClient.getUrl('Branding/Configuration')).then(function (options) {
|
||||||
const loginDisclaimer = view.querySelector('.loginDisclaimer');
|
const loginDisclaimer = view.querySelector('.loginDisclaimer');
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/disabled-auto-escaping
|
||||||
loginDisclaimer.innerHTML = DOMPurify.sanitize(markdownIt({ html: true }).render(options.LoginDisclaimer || ''));
|
loginDisclaimer.innerHTML = DOMPurify.sanitize(markdownIt({ html: true }).render(options.LoginDisclaimer || ''));
|
||||||
|
|
||||||
for (const elem of loginDisclaimer.querySelectorAll('a')) {
|
for (const elem of loginDisclaimer.querySelectorAll('a')) {
|
||||||
|
|
|
@ -13,7 +13,8 @@ function slideDownToShow(button, elem) {
|
||||||
elem.style.height = '0';
|
elem.style.height = '0';
|
||||||
// trigger reflow
|
// trigger reflow
|
||||||
// TODO: Find a better way to do this
|
// TODO: Find a better way to do this
|
||||||
const newHeight = elem.offsetHeight; /* eslint-disable-line no-unused-vars */
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars, sonarjs/no-unused-vars, sonarjs/no-dead-store
|
||||||
|
const newHeight = elem.offsetHeight;
|
||||||
elem.style.height = height;
|
elem.style.height = height;
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
@ -35,7 +36,8 @@ function slideUpToHide(button, elem) {
|
||||||
elem.style.height = elem.offsetHeight + 'px';
|
elem.style.height = elem.offsetHeight + 'px';
|
||||||
// trigger reflow
|
// trigger reflow
|
||||||
// TODO: Find a better way to do this
|
// TODO: Find a better way to do this
|
||||||
const newHeight = elem.offsetHeight; /* eslint-disable-line no-unused-vars */
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars, sonarjs/no-unused-vars, sonarjs/no-dead-store
|
||||||
|
const newHeight = elem.offsetHeight;
|
||||||
elem.classList.remove('expanded');
|
elem.classList.remove('expanded');
|
||||||
elem.style.height = '0';
|
elem.style.height = '0';
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,7 @@ function updateValues(isValueSet) {
|
||||||
if (!!isValueSet && !supportsValueAutoSnap) {
|
if (!!isValueSet && !supportsValueAutoSnap) {
|
||||||
const value = snapValue(this, parseFloat(this.value)).toString();
|
const value = snapValue(this, parseFloat(this.value)).toString();
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/different-types-comparison
|
||||||
if (this.value !== value) {
|
if (this.value !== value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
const realPlay = HTMLMediaElementPrototype.play;
|
const realPlay = HTMLMediaElementPrototype.play;
|
||||||
|
|
||||||
HTMLMediaElementPrototype.play = function () {
|
HTMLMediaElementPrototype.play = function () {
|
||||||
|
// eslint-disable-next-line sonarjs/no-try-promise
|
||||||
try {
|
try {
|
||||||
const promise = realPlay.apply(this, arguments);
|
const promise = realPlay.apply(this, arguments);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ function type(value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof value === 'object' || typeof value === 'function') {
|
if (typeof value === 'object' || typeof value === 'function') {
|
||||||
|
// eslint-disable-next-line sonarjs/prefer-regexp-exec
|
||||||
return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object';
|
return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ function iOSversion() {
|
||||||
/Version\/(\d+)/
|
/Version\/(\d+)/
|
||||||
];
|
];
|
||||||
for (const test of tests) {
|
for (const test of tests) {
|
||||||
const matches = (navigator.appVersion).match(test);
|
const matches = RegExp(test).exec(navigator.appVersion);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
return [
|
return [
|
||||||
parseInt(matches[1], 10),
|
parseInt(matches[1], 10),
|
||||||
|
@ -163,6 +163,7 @@ function supportsCssAnimation(allowPrefix) {
|
||||||
const domPrefixes = ['Webkit', 'O', 'Moz'];
|
const domPrefixes = ['Webkit', 'O', 'Moz'];
|
||||||
const elm = document.createElement('div');
|
const elm = document.createElement('div');
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/different-types-comparison
|
||||||
if (elm.style.animationName !== undefined) {
|
if (elm.style.animationName !== undefined) {
|
||||||
animation = true;
|
animation = true;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +299,7 @@ if (browser.web0s) {
|
||||||
delete browser.chrome;
|
delete browser.chrome;
|
||||||
delete browser.safari;
|
delete browser.safari;
|
||||||
} else if (browser.tizen) {
|
} else if (browser.tizen) {
|
||||||
const v = (navigator.appVersion).match(/Tizen (\d+).(\d+)/);
|
const v = RegExp(/Tizen (\d+).(\d+)/).exec(navigator.appVersion);
|
||||||
browser.tizenVersion = parseInt(v[1], 10);
|
browser.tizenVersion = parseInt(v[1], 10);
|
||||||
|
|
||||||
// UserAgent string contains 'Chrome' and 'Safari', but we only want 'tizen' to be true
|
// UserAgent string contains 'Chrome' and 'Safari', but we only want 'tizen' to be true
|
||||||
|
@ -319,7 +320,6 @@ if (browser.mobile || browser.tv) {
|
||||||
browser.slow = true;
|
browser.slow = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable-next-line compat/compat */
|
|
||||||
if (typeof document !== 'undefined' && ('ontouchstart' in window) || (navigator.maxTouchPoints > 0)) {
|
if (typeof document !== 'undefined' && ('ontouchstart' in window) || (navigator.maxTouchPoints > 0)) {
|
||||||
browser.touch = true;
|
browser.touch = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,7 +230,8 @@ function supportsVc1(videoTestElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function supportsHdr10(options) {
|
function supportsHdr10(options) {
|
||||||
return options.supportsHdr10 ?? (false // eslint-disable-line sonarjs/no-redundant-boolean
|
// eslint-disable-next-line no-constant-binary-expression, sonarjs/no-redundant-boolean
|
||||||
|
return options.supportsHdr10 ?? (false
|
||||||
|| browser.vidaa
|
|| browser.vidaa
|
||||||
|| browser.tizen
|
|| browser.tizen
|
||||||
|| browser.web0s
|
|| browser.web0s
|
||||||
|
@ -253,7 +254,8 @@ function supportsHlg(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function supportsDolbyVision(options) {
|
function supportsDolbyVision(options) {
|
||||||
return options.supportsDolbyVision ?? (false // eslint-disable-line sonarjs/no-redundant-boolean
|
// eslint-disable-next-line no-constant-binary-expression, sonarjs/no-redundant-boolean
|
||||||
|
return options.supportsDolbyVision ?? (false
|
||||||
|| browser.safari && ((browser.iOS && browser.iOSVersion >= 13) || browser.osx)
|
|| browser.safari && ((browser.iOS && browser.iOSVersion >= 13) || browser.osx)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -512,10 +514,8 @@ export default function (options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable compat/compat */
|
|
||||||
let maxVideoWidth = browser.xboxOne ? window.screen?.width : null;
|
let maxVideoWidth = browser.xboxOne ? window.screen?.width : null;
|
||||||
|
|
||||||
/* eslint-enable compat/compat */
|
|
||||||
if (options.maxVideoWidth) {
|
if (options.maxVideoWidth) {
|
||||||
maxVideoWidth = options.maxVideoWidth;
|
maxVideoWidth = options.maxVideoWidth;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ function textAreaCopy(text) {
|
||||||
} else {
|
} else {
|
||||||
ret = Promise.reject();
|
ret = Promise.reject();
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch {
|
||||||
ret = Promise.reject();
|
ret = Promise.reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,11 +48,10 @@ function textAreaCopy(text) {
|
||||||
* @returns {Promise<void>} Promise.
|
* @returns {Promise<void>} Promise.
|
||||||
*/
|
*/
|
||||||
export function copy(text) {
|
export function copy(text) {
|
||||||
/* eslint-disable-next-line compat/compat */
|
// eslint-disable-next-line sonarjs/different-types-comparison
|
||||||
if (navigator.clipboard === undefined) {
|
if (navigator.clipboard === undefined) {
|
||||||
return textAreaCopy(text);
|
return textAreaCopy(text);
|
||||||
} else {
|
} else {
|
||||||
/* eslint-disable-next-line compat/compat */
|
|
||||||
return navigator.clipboard.writeText(text).catch(() => {
|
return navigator.clipboard.writeText(text).catch(() => {
|
||||||
return textAreaCopy(text);
|
return textAreaCopy(text);
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,6 +23,7 @@ const DEV_MODE = process.env.NODE_ENV !== 'production';
|
||||||
let COMMIT_SHA = '';
|
let COMMIT_SHA = '';
|
||||||
try {
|
try {
|
||||||
COMMIT_SHA = require('child_process')
|
COMMIT_SHA = require('child_process')
|
||||||
|
// eslint-disable-next-line sonarjs/no-os-command-from-path
|
||||||
.execSync('git describe --always --dirty')
|
.execSync('git describe --always --dirty')
|
||||||
.toString()
|
.toString()
|
||||||
.trim();
|
.trim();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue