diff --git a/.eslintrc.yaml b/.eslintrc.yaml deleted file mode 100644 index 3eb37b15f..000000000 --- a/.eslintrc.yaml +++ /dev/null @@ -1,68 +0,0 @@ -root: true -extends: - - '@hydrooj/eslint-config' -ignorePatterns: - - public/**/*.js - - packages/ui-default - - dist - - '*.d.ts' - - node_modules -overrides: - - files: - - '**/{public,frontend}/**/*.{ts,tsx,page.js}' - - '**/plugins/**/*.page.{ts,js,tsx,jsx}' - rules: - github/array-foreach: 0 - '@stylistic/indent': - - warn - - 2 - '@typescript-eslint/no-invalid-this': 0 - env: - browser: true - es6: true - jquery: true - globals: - UiContext: true - UserContext: true - externalModules: true - LOCALES: true - LANGS: true - - files: - - 'packages/ui-next/src/**/*.{ts,tsx}' - rules: - '@stylistic/indent': - - warn - - 2 - env: - browser: true - es6: true -rules: - '@typescript-eslint/no-invalid-this': 1 - simple-import-sort/imports: - - warn - - groups: - - ["^\\u0000"] - - - - "^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)" - - "^(?!@?hydrooj)(@?\\w.+)" - - "^@?hydrooj" - - "^" - - "^\\." -settings: - import/parsers: - '@typescript-eslint/parser': - - .ts - - .js - - .jsx - - .tsx - import/resolver: - webpack: - config: - resolve: - extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'] - -parserOptions: - sourceType: module -# project: -# - './tsconfig.json' -# - './packages/**/tsconfig.json' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4789e9e88..e145a3578 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,7 @@ jobs: if [[ ${{ steps.ui-changed-files.outputs.any_changed }} == true ]] || [[ $(npm info @hydrooj/ui-default version) != $(node -e 'console.log(require("./packages/ui-default/package.json").version)') ]] then yarn build:ui:iconfont - parallel --tty -j+0 yarn ::: lint:ci lint:ui:ci build build:ui:production:webpack test + parallel --tty -j+0 yarn ::: lint:ci build build:ui:production:webpack test else parallel --tty -j+0 yarn ::: lint:ci build test fi diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..3df38c2da --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,149 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/naming-convention */ +import path from 'node:path'; +import { defineConfig, globalIgnores } from 'eslint/config'; +import globals from 'globals'; +import react from '@hydrooj/eslint-config'; + +export default defineConfig([globalIgnores([ + '**/{public,files}/**/*.js', + '**/dist', + '**/*.d.ts', + '**/node_modules', + '**/.*.js', + 'packages/ui-default/public', +]), { + extends: [react], + + languageOptions: { + ecmaVersion: 5, + sourceType: 'module', + }, + + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.js', '.jsx', '.tsx'], + }, + + 'import/resolver': { + webpack: { + config: { + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'], + }, + }, + }, + }, + }, + + rules: { + '@typescript-eslint/no-invalid-this': 1, + + 'simple-import-sort/imports': ['warn', { + groups: [ + ['^\\u0000'], + [ + '^(node:)?(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)', + '^(?!@?hydrooj)(@?\\w.+)', + '^@?hydrooj', + '^', + '^\\.', + ]], + }], + }, +}, { + files: [ + '**/{public,frontend}/**/*.{ts,tsx,page.js}', + '**/plugins/**/*.page.{ts,js,tsx,jsx}', + 'packages/ui-default/**/*.{ts,tsx,js,jsx}', + ], + + languageOptions: { + globals: { + ...globals.browser, + ...globals.jquery, + ...globals.commonjs, + UiContext: true, + UserContext: true, + externalModules: true, + LOCALES: true, + LANGS: true, + __webpack_public_path__: true, + __webpack_require__: true, + }, + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + ecmaFeatures: { + impliedStrict: true, + experimentalObjectRestSpread: true, + jsx: true, + defaultParams: true, + legacyDecorators: true, + allowImportExportEverywhere: true, + }, + }, + }, + + settings: { + 'import/resolver': { + webpack: { + config: { + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + alias: { + vj: path.resolve('./packages/ui-default'), + }, + }, + }, + }, + }, + }, + + rules: { + 'github/array-foreach': 0, + '@typescript-eslint/no-invalid-this': 0, + + // FIXME A bug with eslint-parser + // 'template-curly-spacing': 'off', + + '@stylistic/indent': [ + 'warn', + 2, + { SwitchCase: 1 }, + ], + 'function-paren-newline': 'off', + 'no-mixed-operators': 'off', + 'no-await-in-loop': 'off', + 'no-lonely-if': 'off', + 'no-script-url': 'off', + + 'simple-import-sort/imports': [ + 'warn', + { + groups: [ + ['^\\u0000'], + [ + '^(node:)?(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)', + '^@?hydrooj', + '^', + '^(@|vj)\\/', + '^\\.', + ], + ], + }, + ], + }, +}, { + files: ['packages/ui-next/src/**/*.{ts,tsx}'], + + languageOptions: { + globals: { + ...globals.browser, + }, + }, + + rules: { + '@stylistic/indent': ['warn', 2], + }, +}]); diff --git a/framework/eslint-config/base.mjs b/framework/eslint-config/base.mjs new file mode 100644 index 000000000..eab17edb8 --- /dev/null +++ b/framework/eslint-config/base.mjs @@ -0,0 +1,482 @@ +import { FlatCompat } from '@eslint/eslintrc'; +import eslintReact from '@eslint-react/eslint-plugin'; +import stylistic from '@stylistic/eslint-plugin'; +import typescriptEslint from '@typescript-eslint/eslint-plugin'; +import tsParser from '@typescript-eslint/parser'; +import { defineConfig, globalIgnores } from 'eslint/config'; +import deMorgan from 'eslint-plugin-de-morgan'; +import github from 'eslint-plugin-github'; +import jsxA11Y from 'eslint-plugin-jsx-a11y'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; + +const compat = new FlatCompat({}); + +export default defineConfig([ + globalIgnores([ + '**/*.d.ts', + '**/node_modules', + '**/.git', + ]), + { + extends: [ + compat.extends('airbnb-base'), + compat.extends('airbnb/hooks'), + github.getFlatConfigs().react, + deMorgan.configs.recommended, + eslintReact.configs['recommended-typescript'], + ], + + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx', '.d.ts'], + }, + + 'import/resolver': { + node: { + extensions: ['.mjs', '.js', '.json', '.ts', '.d.ts'], + }, + }, + + 'import/extensions': ['.js', '.mjs', '.jsx', '.ts', '.tsx', '.d.ts'], + 'import/external-module-folders': ['node_modules', 'node_modules/@types'], + }, + + plugins: { + '@typescript-eslint': typescriptEslint, + 'simple-import-sort': simpleImportSort, + // import: fixupPluginRules(_import), + 'react-refresh': reactRefresh, + 'jsx-a11y': jsxA11Y, + '@stylistic': stylistic, + }, + + languageOptions: { + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', + BigInt: 'readonly', + }, + + parser: tsParser, + }, + + rules: { + 'brace-style': 'off', + + '@stylistic/brace-style': ['error', '1tbs', { + allowSingleLine: true, + }], + + camelcase: 'off', + 'comma-dangle': 'off', + + '@stylistic/comma-dangle': ['error', { + arrays: 'always-multiline', + objects: 'always-multiline', + imports: 'always-multiline', + exports: 'always-multiline', + functions: 'always-multiline', + enums: 'always-multiline', + generics: 'always-multiline', + tuples: 'always-multiline', + }], + + 'comma-spacing': 'off', + + '@stylistic/comma-spacing': ['error', { + before: false, + after: true, + }], + + 'default-param-last': 'off', + + 'func-call-spacing': 'off', + '@stylistic/func-call-spacing': ['error', 'never'], + indent: 'off', + + '@stylistic/indent': ['warn', 4, { + SwitchCase: 1, + VariableDeclarator: 1, + outerIIFEBody: 1, + + FunctionDeclaration: { + parameters: 1, + body: 1, + }, + + FunctionExpression: { + parameters: 1, + body: 1, + }, + + CallExpression: { + arguments: 1, + }, + + ArrayExpression: 1, + ObjectExpression: 1, + ImportDeclaration: 1, + flatTernaryExpressions: false, + + ignoredNodes: [ + 'JSXElement', + 'JSXElement > *', + 'JSXAttribute', + 'JSXIdentifier', + 'JSXNamespacedName', + 'JSXMemberExpression', + 'JSXSpreadAttribute', + 'JSXExpressionContainer', + 'JSXOpeningElement', + 'JSXClosingElement', + 'JSXFragment', + 'JSXOpeningFragment', + 'JSXClosingFragment', + 'JSXText', + 'JSXEmptyExpression', + 'JSXSpreadChild', + ], + + ignoreComments: false, + }], + + 'keyword-spacing': 'off', + + '@stylistic/keyword-spacing': ['error', { + before: true, + after: true, + + overrides: { + return: { + after: true, + }, + + throw: { + after: true, + }, + + case: { + after: true, + }, + }, + }], + + 'lines-between-class-members': 'off', + + '@stylistic/lines-between-class-members': ['error', 'always', { + exceptAfterSingleLine: true, + }], + + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + 'no-dupe-class-members': 'off', + '@typescript-eslint/no-dupe-class-members': 'error', + 'no-empty-function': 'off', + + '@typescript-eslint/no-empty-function': ['error', { + allow: ['arrowFunctions', 'functions', 'methods'], + }], + + 'no-extra-parens': 'off', + + '@typescript-eslint/no-extra-parens': ['off', 'all', { + conditionalAssign: true, + nestedBinaryExpressions: false, + returnAssign: false, + ignoreJSX: 'all', + enforceForArrowConditionals: false, + }], + + 'no-extra-semi': 'off', + '@stylistic/no-extra-semi': 'error', + 'no-new-func': 'off', + 'no-loss-of-precision': 'off', + '@typescript-eslint/no-loss-of-precision': 'error', + 'no-loop-func': 'off', + '@typescript-eslint/no-loop-func': 'error', + 'no-magic-numbers': 'off', + + '@typescript-eslint/no-magic-numbers': ['off', { + ignore: [], + ignoreArrayIndexes: true, + enforceConst: true, + detectObjects: false, + }], + + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': 'error', + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': 'error', + 'space-before-blocks': 'off', + '@stylistic/space-before-blocks': 'error', + 'no-unused-expressions': 'off', + + '@typescript-eslint/no-unused-expressions': ['error', { + allowShortCircuit: false, + allowTernary: false, + allowTaggedTemplates: false, + }], + + 'no-unused-vars': 'off', + + '@typescript-eslint/no-unused-vars': ['error', { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: true, + caughtErrorsIgnorePattern: '^_|e', + argsIgnorePattern: '^_', + }], + + 'no-use-before-define': 'off', + + '@typescript-eslint/no-use-before-define': ['error', { + functions: true, + classes: true, + variables: true, + }], + + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + quotes: 'off', + + '@stylistic/quotes': ['warn', 'single', { + avoidEscape: true, + }], + + semi: 'off', + '@stylistic/semi': ['error', 'always'], + 'space-before-function-paren': 'off', + + '@stylistic/space-before-function-paren': ['error', { + anonymous: 'always', + named: 'never', + asyncArrow: 'always', + }], + + 'require-await': 'off', + '@typescript-eslint/require-await': 'off', + 'space-infix-ops': 'off', + '@stylistic/space-infix-ops': 'error', + 'object-curly-spacing': 'off', + '@stylistic/object-curly-spacing': ['error', 'always'], + + 'function-call-argument-newline': 0, + + 'no-implicit-coercion': ['warn', { + allow: ['!!', '+'], + }], + + 'no-extra-boolean-cast': ['warn', { + enforceForLogicalOperands: true, + }], + + 'no-empty-pattern': 0, + 'no-multi-str': 0, + + 'dot-notation': 0, + + '@typescript-eslint/naming-convention': [ + 'warn', + { + selector: 'default', + format: ['camelCase'], + leadingUnderscore: 'allowSingleOrDouble', + }, { + selector: 'default', + + filter: { + regex: '^([A-Z]|_+id|__call__)$', + match: true, + }, + + format: null, + }, { + selector: 'variable', + modifiers: ['destructured'], + format: null, + }, { + selector: 'import', + format: null, + }, { + selector: 'variable', + format: ['camelCase', 'PascalCase', 'UPPER_CASE'], + modifiers: ['const'], + leadingUnderscore: 'allowSingleOrDouble', + }, { + selector: 'property', + format: ['camelCase', 'PascalCase', 'UPPER_CASE', 'snake_case'], + leadingUnderscore: 'allowSingleOrDouble', + }, { + selector: ['function', 'parameter', 'parameterProperty', 'objectLiteralMethod'], + + filter: { + regex: '__call__', + match: false, + }, + + format: ['camelCase', 'PascalCase'], + leadingUnderscore: 'allowSingleOrDouble', + }, { + selector: ['objectLiteralProperty'], + + filter: { + regex: '^[\\d]+$', + match: true, + }, + + format: null, + }, { + selector: ['typeProperty', 'typeMethod', 'objectLiteralProperty'], + modifiers: ['requiresQuotes'], + format: null, + }, { + selector: ['typeMethod'], + format: ['camelCase', 'PascalCase'], + }, { + selector: ['enum', 'class', 'interface'], + format: ['PascalCase'], + }, { + selector: ['typeAlias', 'enumMember', 'typeParameter'], + format: ['PascalCase', 'camelCase', 'UPPER_CASE'], + }, + ], + + '@typescript-eslint/no-invalid-this': 0, + + 'class-methods-use-this': 0, + 'global-require': 0, + 'guard-for-in': 0, + 'implicit-arrow-linebreak': 0, + 'import/extensions': 0, + 'import/order': 0, + 'consistent-return': 1, + 'func-names': 0, + 'import/newline-after-import': 0, + 'import/no-cycle': 0, + 'import/no-extraneous-dependencies': 0, + 'import/no-named-as-default': 0, + 'import/prefer-default-export': 0, + + 'logical-assignment-operators': ['warn', 'always', { + enforceForIfStatements: true, + }], + + 'max-classes-per-file': 0, + 'max-len': ['warn', 150], + 'newline-per-chained-call': 0, + 'no-bitwise': 0, + 'no-console': 0, + 'no-continue': 0, + 'no-extend-native': 0, + + 'no-empty': ['warn', { + allowEmptyCatch: true, + }], + + 'no-await-in-loop': 1, + 'no-multi-assign': 0, + 'no-return-await': 0, + 'no-nested-ternary': 0, + 'no-param-reassign': 0, + 'no-plusplus': 0, + 'no-underscore-dangle': 0, + 'no-unmodified-loop-condition': 1, + 'prefer-destructuring': 0, + 'function-paren-newline': 0, + 'simple-import-sort/exports': 0, + + 'no-restricted-syntax': [ + 'error', { + 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.', + }, + ], + + 'generator-star-spacing': 0, + + 'github/array-foreach': 1, + 'github/a11y-svg-has-accessible-name': 0, + 'quote-props': 0, + '@stylistic/quote-props': [0, 'as-needed'], + + 'simple-import-sort/imports': ['warn', { + groups: [ + ['^\\u0000'], + [ + // eslint-disable-next-line max-len + '^(node:)?(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)', + '^', + '^\\.', + ], + ], + }], + + // ui-default is currently on react 18 + '@eslint-react/no-prop-types': 0, + '@eslint-react/no-string-refs': 0, + + // to allow `javascript:;` + '@eslint-react/dom/no-script-url': 0, + + '@eslint-react/no-array-index-key': 0, + '@eslint-react/dom/no-missing-button-type': 0, + '@eslint-react/dom/no-dangerously-set-innerhtml': 0, + '@eslint-react/hooks-extra/no-direct-set-state-in-use-effect': 0, + '@eslint-react/hooks-extra/no-unnecessary-use-prefix': 0, + 'react-hooks/exhaustive-deps': 0, + // 'react/prefer-stateless-function': 0, + // 'react/function-component-definition': 0, + // 'react/static-property-placement': 0, + // 'react/self-closing-comp': 0, + // 'react/prop-types': 0, + // 'react/jsx-filename-extension': 0, + // 'react/jsx-one-expression-per-line': 0, + // 'react/jsx-props-no-spreading': 0, + // 'react/no-string-refs': 0, + // 'react/require-default-props': 0, + // 'react/react-in-jsx-scope': 0, + // 'react/destructuring-assignment': 0, + // 'react/button-has-type': 0, + // 'react/forbid-prop-types': 0, + 'jsx-a11y/no-static-element-interactions': 0, + 'jsx-a11y/anchor-is-valid': 0, + 'jsx-a11y/click-events-have-key-events': 0, + 'jsx-a11y/label-has-associated-control': 0, + + 'react-refresh/only-export-components': ['warn', { + allowConstantExport: true, + }], + }, + }, + { + files: ['**/*.ts', '**/*.tsx'], + + // Hand over to tsc + rules: { + 'constructor-super': 'off', + 'getter-return': 'off', + 'no-const-assign': 'off', + 'no-dupe-args': 'off', + 'no-dupe-class-members': 'off', + 'no-dupe-keys': 'off', + 'no-func-assign': 'off', + 'no-import-assign': 'off', + 'no-new-symbol': 'off', + 'no-obj-calls': 'off', + 'no-redeclare': 'off', + 'no-setter-return': 'off', + 'no-this-before-super': 'off', + 'no-undef': 'off', + 'no-unreachable': 'off', + 'no-unsafe-negation': 'off', + 'valid-typeof': 'off', + 'import/named': 'off', + 'import/no-named-as-default-member': 'off', + 'import/no-unresolved': 'off', + }, + }, +]); diff --git a/framework/eslint-config/base.yaml b/framework/eslint-config/base.yaml deleted file mode 100644 index 68840c5d1..000000000 --- a/framework/eslint-config/base.yaml +++ /dev/null @@ -1,164 +0,0 @@ -extends: - - airbnb-base - - ./typescript - - plugin:de-morgan/recommended-legacy -env: - es6: true -globals: - Atomics: readonly - SharedArrayBuffer: readonly - BigInt: readonly -parser: '@typescript-eslint/parser' -plugins: - - '@typescript-eslint' - - simple-import-sort - - eslint-plugin-import - - eslint-plugin-github - - '@stylistic' - - eslint-plugin-de-morgan -rules: - '@typescript-eslint/no-shadow': 1 - - function-call-argument-newline: 0 - '@typescript-eslint/dot-notation': 0 - '@typescript-eslint/no-implied-eval': 0 - '@typescript-eslint/no-throw-literal': 0 - '@typescript-eslint/return-await': 0 - - no-implicit-coercion: - - warn - - allow: ["!!", "+"] - no-extra-boolean-cast: - - warn - - enforceForLogicalOperands: true - no-empty-pattern: 0 - no-multi-str: 0 - '@typescript-eslint/naming-convention': - - warn - - selector: default - format: [camelCase] - leadingUnderscore: allowSingleOrDouble - - selector: default - filter: - regex: '^([A-Z]|_+id|__call__)$' - match: true - format: null - - selector: variable - modifiers: [destructured] - format: null - - selector: import - format: null - - selector: variable - format: [camelCase, PascalCase, UPPER_CASE] - modifiers: [const] - leadingUnderscore: allowSingleOrDouble - - selector: property - format: [camelCase, PascalCase, UPPER_CASE, snake_case] - leadingUnderscore: allowSingleOrDouble - - selector: [function, parameter, parameterProperty, objectLiteralMethod] - filter: - regex: __call__ - match: false - format: [camelCase, PascalCase] - leadingUnderscore: allowSingleOrDouble - - selector: [objectLiteralProperty] - filter: - regex: '^[\d]+$' - match: true - format: null - - selector: [typeProperty, typeMethod, objectLiteralProperty] - modifiers: [requiresQuotes] - format: null - - selector: [typeMethod] - format: [camelCase, PascalCase] - - selector: [enum, class, interface] - format: [PascalCase] - - selector: [typeAlias, enumMember, typeParameter] - format: [PascalCase, camelCase, UPPER_CASE] - - '@typescript-eslint/no-redeclare': 0 - '@typescript-eslint/default-param-last': 0 - default-param-last: 0 - '@typescript-eslint/no-invalid-this': 0 - - '@stylistic/indent': - - warn - - 4 - '@stylistic/comma-dangle': - - 1 - - always-multiline - '@stylistic/lines-between-class-members': - - error - - always - - exceptAfterSingleLine: true - - '@stylistic/quotes': ['warn', 'single', { avoidEscape: true }] - - class-methods-use-this: 0 - global-require: 0 - guard-for-in: 0 - implicit-arrow-linebreak: 0 - import/extensions: 0 - import/order: 0 - consistent-return: 1 - func-names: 0 - import/newline-after-import: 0 - import/no-cycle: 0 - import/no-extraneous-dependencies: 0 - import/no-named-as-default: 0 - import/prefer-default-export: 0 - - logical-assignment-operators: - - warn - - always - - enforceForIfStatements: true - max-classes-per-file: 0 - max-len: - - warn - - 150 - newline-per-chained-call: 0 - no-bitwise: 0 - no-console: 0 - no-continue: 0 - no-extend-native: 0 - no-empty: - - warn - - allowEmptyCatch: true - no-multi-assign: 0 - no-nested-ternary: 0 - no-param-reassign: 0 - no-plusplus: 0 - no-return-await: 0 - no-shadow: 0 - no-underscore-dangle: 0 - no-unmodified-loop-condition: 1 - prefer-destructuring: 0 - function-paren-newline: 0 - simple-import-sort/exports: 0 - - '@typescript-eslint/no-use-before-define': 1 - '@typescript-eslint/no-unused-vars': - - error - - caughtErrorsIgnorePattern: "^_|e" - argsIgnorePattern: "^_" - - no-restricted-syntax: - - error - - 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.' - - github/array-foreach: 1 - - quote-props: 0 - '@stylistic/quote-props': [0, as-needed] - - simple-import-sort/imports: - - warn - - groups: - - ["^\\u0000"] - - - - "^(node:)?(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)" - - "^" - - "^\\." diff --git a/framework/eslint-config/package.json b/framework/eslint-config/package.json index e600ed723..e5b48d516 100644 --- a/framework/eslint-config/package.json +++ b/framework/eslint-config/package.json @@ -2,23 +2,26 @@ "name": "@hydrooj/eslint-config", "version": "1.1.2", "license": "MIT", - "main": "react.yaml", + "main": "base.mjs", "dependencies": { - "@stylistic/eslint-plugin": "^3.1.0", - "@typescript-eslint/eslint-plugin": "^8.26.1", - "@typescript-eslint/parser": "^8.26.1", + "@eslint-react/eslint-plugin": "^1.37.0", + "@eslint/compat": "^1.2.7", + "@eslint/eslintrc": "^3.3.0", + "@stylistic/eslint-plugin": "^4.2.0", + "@typescript-eslint/eslint-plugin": "^8.27.0", + "@typescript-eslint/parser": "^8.27.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-de-morgan": "^1.2.1", - "eslint-plugin-github": "^5.1.8", + "eslint-plugin-github": "^6.0.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", - "eslint-plugin-simple-import-sort": "^12.1.1" + "eslint-plugin-simple-import-sort": "^12.1.1", + "globals": "^16.0.0" }, "peerDependencies": { - "eslint": ">= 8" + "eslint": ">= 9" }, "optionalDependencies": { "typescript": "^5.8.2" diff --git a/framework/eslint-config/react.yaml b/framework/eslint-config/react.yaml deleted file mode 100644 index 97ca98637..000000000 --- a/framework/eslint-config/react.yaml +++ /dev/null @@ -1,30 +0,0 @@ -extends: -- ./base.yaml -- airbnb/hooks -plugins: -- react -- react-refresh -- react-hooks -- jsx-a11y -rules: - react-hooks/exhaustive-deps: 0 - react/prefer-stateless-function: 0 - react/function-component-definition: 0 - react/static-property-placement: 0 - react/self-closing-comp: 0 - react/prop-types: 0 - react/jsx-filename-extension: 0 - react/jsx-one-expression-per-line: 0 - react/jsx-props-no-spreading: 0 - react/no-string-refs: 0 - react/require-default-props: 0 - react/react-in-jsx-scope: 0 - react/destructuring-assignment: 0 - react/button-has-type: 0 - react/forbid-prop-types: 0 - jsx-a11y/no-static-element-interactions: 0 - jsx-a11y/anchor-is-valid: 0 - jsx-a11y/click-events-have-key-events: 0 - jsx-a11y/label-has-associated-control: 0 - react-refresh/only-export-components: ['warn', { allowConstantExport: true }] - # 'react/no-unknown-property': ['error', { ignore: ['key'] }], \ No newline at end of file diff --git a/framework/eslint-config/typescript.js b/framework/eslint-config/typescript.js deleted file mode 100644 index 42736bc01..000000000 --- a/framework/eslint-config/typescript.js +++ /dev/null @@ -1,240 +0,0 @@ -// Modified from eslint-config-airbnb-typescript for @typescript/eslint-plugin@9 -/* eslint-disable import/no-extraneous-dependencies */ -const { rules: baseBestPracticesRules } = require('eslint-config-airbnb-base/rules/best-practices'); -const { rules: baseErrorsRules } = require('eslint-config-airbnb-base/rules/errors'); -const { rules: baseES6Rules } = require('eslint-config-airbnb-base/rules/es6'); -const { rules: baseStyleRules } = require('eslint-config-airbnb-base/rules/style'); -const { rules: baseVariablesRules } = require('eslint-config-airbnb-base/rules/variables'); -/* eslint-enable import/no-extraneous-dependencies */ - -module.exports = { - plugins: ['@typescript-eslint'], - parser: '@typescript-eslint/parser', - settings: { - // Apply special parsing for TypeScript files - 'import/parsers': { - '@typescript-eslint/parser': ['.ts', '.tsx', '.d.ts'], - }, - // Append 'ts' extensions to Airbnb 'import/resolver' setting - // Original: ['.mjs', '.js', '.json'] - 'import/resolver': { - node: { - extensions: ['.mjs', '.js', '.json', '.ts', '.d.ts'], - }, - }, - // Append 'ts' extensions to Airbnb 'import/extensions' setting - // Original: ['.js', '.mjs', '.jsx'] - 'import/extensions': ['.js', '.mjs', '.jsx', '.ts', '.tsx', '.d.ts'], - // Resolve type definition packages - 'import/external-module-folders': ['node_modules', 'node_modules/@types'], - }, - rules: { - // Replace Airbnb 'brace-style' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/brace-style.md - 'brace-style': 'off', - '@stylistic/brace-style': baseStyleRules['brace-style'], - - // Replace Airbnb 'camelcase' rule with '@typescript-eslint/naming-convention' - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/naming-convention.md - camelcase: 'off', - - // Replace Airbnb 'comma-dangle' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/comma-dangle.md - // The TypeScript version also adds 3 new options, all of which should be set to the same value as the base config - 'comma-dangle': 'off', - '@stylistic/comma-dangle': [ - baseStyleRules['comma-dangle'][0], - { - ...baseStyleRules['comma-dangle'][1], - enums: baseStyleRules['comma-dangle'][1].arrays, - generics: baseStyleRules['comma-dangle'][1].arrays, - tuples: baseStyleRules['comma-dangle'][1].arrays, - }, - ], - - // Replace Airbnb 'comma-spacing' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/comma-spacing.md - 'comma-spacing': 'off', - '@stylistic/comma-spacing': baseStyleRules['comma-spacing'], - - // Replace Airbnb 'default-param-last' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/default-param-last.md - 'default-param-last': 'off', - '@typescript-eslint/default-param-last': baseBestPracticesRules['default-param-last'], - - // Replace Airbnb 'dot-notation' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/dot-notation.md - 'dot-notation': 'off', - '@typescript-eslint/dot-notation': baseBestPracticesRules['dot-notation'], - - // Replace Airbnb 'func-call-spacing' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/func-call-spacing.md - 'func-call-spacing': 'off', - '@stylistic/func-call-spacing': baseStyleRules['func-call-spacing'], - - // Replace Airbnb 'indent' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/indent.md - indent: 'off', - '@stylistic/indent': baseStyleRules.indent, - - // Replace Airbnb 'keyword-spacing' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/keyword-spacing.md - 'keyword-spacing': 'off', - '@stylistic/keyword-spacing': baseStyleRules['keyword-spacing'], - - // Replace Airbnb 'lines-between-class-members' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/lines-between-class-members.md - 'lines-between-class-members': 'off', - '@stylistic/lines-between-class-members': baseStyleRules['lines-between-class-members'], - - // Replace Airbnb 'no-array-constructor' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-array-constructor.md - 'no-array-constructor': 'off', - '@typescript-eslint/no-array-constructor': baseStyleRules['no-array-constructor'], - - // Replace Airbnb 'no-dupe-class-members' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-dupe-class-members.md - 'no-dupe-class-members': 'off', - '@typescript-eslint/no-dupe-class-members': baseES6Rules['no-dupe-class-members'], - - // Replace Airbnb 'no-empty-function' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-empty-function.md - 'no-empty-function': 'off', - '@typescript-eslint/no-empty-function': baseBestPracticesRules['no-empty-function'], - - // Replace Airbnb 'no-extra-parens' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-extra-parens.md - 'no-extra-parens': 'off', - '@typescript-eslint/no-extra-parens': baseErrorsRules['no-extra-parens'], - - // Replace Airbnb 'no-extra-semi' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-extra-semi.md - 'no-extra-semi': 'off', - '@stylistic/no-extra-semi': baseErrorsRules['no-extra-semi'], - - // Replace Airbnb 'no-implied-eval' and 'no-new-func' rules with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-implied-eval.md - 'no-implied-eval': 'off', - 'no-new-func': 'off', - '@typescript-eslint/no-implied-eval': baseBestPracticesRules['no-implied-eval'], - - // Replace Airbnb 'no-loss-of-precision' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-loss-of-precision.md - 'no-loss-of-precision': 'off', - '@typescript-eslint/no-loss-of-precision': baseErrorsRules['no-loss-of-precision'], - - // Replace Airbnb 'no-loop-func' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-loop-func.md - 'no-loop-func': 'off', - '@typescript-eslint/no-loop-func': baseBestPracticesRules['no-loop-func'], - - // Replace Airbnb 'no-magic-numbers' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-magic-numbers.md - 'no-magic-numbers': 'off', - '@typescript-eslint/no-magic-numbers': baseBestPracticesRules['no-magic-numbers'], - - // Replace Airbnb 'no-redeclare' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-redeclare.md - 'no-redeclare': 'off', - '@typescript-eslint/no-redeclare': baseBestPracticesRules['no-redeclare'], - - // Replace Airbnb 'no-shadow' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-shadow.md - 'no-shadow': 'off', - '@typescript-eslint/no-shadow': baseVariablesRules['no-shadow'], - - // Replace Airbnb 'space-before-blocks' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/space-before-blocks.md - 'space-before-blocks': 'off', - '@stylistic/space-before-blocks': baseStyleRules['space-before-blocks'], - - // Replace Airbnb 'no-throw-literal' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-throw-literal.md - 'no-throw-literal': 'off', - '@typescript-eslint/no-throw-literal': baseBestPracticesRules['no-throw-literal'], - - // Replace Airbnb 'no-unused-expressions' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unused-expressions.md - 'no-unused-expressions': 'off', - '@typescript-eslint/no-unused-expressions': baseBestPracticesRules['no-unused-expressions'], - - // Replace Airbnb 'no-unused-vars' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unused-vars.md - 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': baseVariablesRules['no-unused-vars'], - - // Replace Airbnb 'no-use-before-define' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md - 'no-use-before-define': 'off', - '@typescript-eslint/no-use-before-define': baseVariablesRules['no-use-before-define'], - - // Replace Airbnb 'no-useless-constructor' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-useless-constructor.md - 'no-useless-constructor': 'off', - '@typescript-eslint/no-useless-constructor': baseES6Rules['no-useless-constructor'], - - // Replace Airbnb 'quotes' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/quotes.md - quotes: 'off', - '@stylistic/quotes': baseStyleRules.quotes, - - // Replace Airbnb 'semi' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/semi.md - semi: 'off', - '@stylistic/semi': baseStyleRules.semi, - - // Replace Airbnb 'space-before-function-paren' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/space-before-function-paren.md - 'space-before-function-paren': 'off', - '@stylistic/space-before-function-paren': baseStyleRules['space-before-function-paren'], - - // Replace Airbnb 'require-await' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/require-await.md - 'require-await': 'off', - '@typescript-eslint/require-await': baseBestPracticesRules['require-await'], - - // Replace Airbnb 'no-return-await' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/return-await.md - 'no-return-await': 'off', - '@typescript-eslint/return-await': [baseBestPracticesRules['no-return-await'], 'in-try-catch'], - - // Replace Airbnb 'space-infix-ops' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/space-infix-ops.md - 'space-infix-ops': 'off', - '@stylistic/space-infix-ops': baseStyleRules['space-infix-ops'], - - // Replace Airbnb 'object-curly-spacing' rule with '@typescript-eslint' version - // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/object-curly-spacing.md - 'object-curly-spacing': 'off', - '@stylistic/object-curly-spacing': baseStyleRules['object-curly-spacing'], - }, - overrides: [ - { - files: ['*.ts', '*.tsx'], - rules: { - // The following rules are enabled in Airbnb config, but are already checked (more thoroughly) by the TypeScript compiler - 'constructor-super': 'off', - 'getter-return': 'off', - 'no-const-assign': 'off', - 'no-dupe-args': 'off', - 'no-dupe-class-members': 'off', - 'no-dupe-keys': 'off', - 'no-func-assign': 'off', - 'no-import-assign': 'off', - 'no-new-symbol': 'off', - 'no-obj-calls': 'off', - 'no-redeclare': 'off', - 'no-setter-return': 'off', - 'no-this-before-super': 'off', - 'no-undef': 'off', - 'no-unreachable': 'off', - 'no-unsafe-negation': 'off', - 'valid-typeof': 'off', - 'import/named': 'off', - 'import/no-named-as-default-member': 'off', - // Disable `import/no-unresolved`, see README.md for details - 'import/no-unresolved': 'off', - }, - }, - ], -}; diff --git a/package.json b/package.json index 5a93a28fc..e3f4e3d32 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,8 @@ "test": "node -r @hydrooj/register packages/common/tests/subtask.spec.ts && node test/entry.js", "benchmark": "cross-env BENCHMARK=true node test/entry.js", "oxlint": "cd framework/eslint-config && node -r @hydrooj/register build.ts && cd ../../ && oxlint --config framework/eslint-config/.oxlintrc.json", - "lint": "eslint packages plugins modules framework --ext .ts,.tsx,.jsx --fix", - "lint:ci": "eslint packages plugins modules framework --ext .ts,.tsx,.jsx --max-warnings=0", - "lint:ui": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --fix", - "lint:ui:ci": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --max-warnings=0", + "lint": "eslint --flag unstable_config_lookup_from_file packages plugins modules framework --ext .ts,.tsx,.jsx --fix", + "lint:ci": "eslint --flag unstable_config_lookup_from_file packages plugins modules framework --ext .ts,.tsx,.jsx --max-warnings=0", "debug": "node --trace-warnings --async-stack-traces --trace-deprecation packages/hydrooj/bin/hydrooj --debug --template", "start": "packages/hydrooj/bin/hydrooj.js", "postinstall": "node build/prepare.js", @@ -45,20 +43,21 @@ "cross-env": "^7.0.3", "cross-spawn": "^7.0.6", "esbuild": "0.25.0", - "eslint": "^8.57.1", - "eslint-import-resolver-typescript": "^4.1.1", + "eslint": "^9.22.0", + "eslint-import-resolver-typescript": "^4.2.2", "eslint-import-resolver-webpack": "^0.13.10", "fs-extra": "^11.3.0", + "globals": "^16.0.0", "globby": "~14.0.2", "inspectpack": "^4.7.1", "mongodb-memory-server": "10.1.4", "nyc": "^17.1.0", "ora": "^8.2.0", - "oxlint": "^0.16.0", + "oxlint": "^0.16.1", "package-json": "^10.0.1", "semver": "^7.7.1", "simple-git": "^3.27.0", - "supertest": "^7.0.0", + "supertest": "^7.1.0", "supports-hyperlinks": "^3.2.0", "typescript": "5.8.2", "wtfnode": "^0.10.0" diff --git a/packages/ui-default/.eslintignore b/packages/ui-default/.eslintignore deleted file mode 100644 index e4ee7a460..000000000 --- a/packages/ui-default/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -backendlib -build -public \ No newline at end of file diff --git a/packages/ui-default/.eslintrc.js b/packages/ui-default/.eslintrc.js deleted file mode 100644 index b35debbac..000000000 --- a/packages/ui-default/.eslintrc.js +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -const path = require('path'); - -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - env: { - browser: true, - es6: true, - jquery: true, - commonjs: true, - }, - extends: [ - '@hydrooj/eslint-config', - ], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020, - ecmaFeatures: { - impliedStrict: true, - experimentalObjectRestSpread: true, - jsx: true, - defaultParams: true, - legacyDecorators: true, - allowImportExportEverywhere: true, - }, - }, - settings: { - 'import/resolver': { - webpack: { - config: { - resolve: { - extensions: ['.js', '.jsx', '.ts', '.tsx'], - alias: { - vj: path.resolve(__dirname), - }, - }, - }, - }, - }, - }, - globals: { - __webpack_public_path__: true, - __webpack_require__: true, - UiContext: true, - UserContext: true, - externalModules: true, - LOCALES: true, - LANGS: true, - window: true, - JQuery: true, - }, - rules: { - // FIXME A bug with eslint-parser - // 'template-curly-spacing': 'off', - - '@stylistic/indent': [ - 'warn', - 2, - { SwitchCase: 1 }, - ], - 'github/array-foreach': 'off', - 'function-paren-newline': 'off', - 'no-mixed-operators': 'off', - 'no-await-in-loop': 'off', - 'no-lonely-if': 'off', - 'no-script-url': 'off', - - 'simple-import-sort/imports': [ - 'warn', - { - groups: [ - ['^\\u0000'], - ['^(?!vj)(@?\\w.+)', '^vj\\/', '^', '^\\.'], - ], - }, - ], - }, -};