From cda3a8f855b7df295f73019f76b63df2363c5ccb Mon Sep 17 00:00:00 2001 From: undefined Date: Mon, 10 Mar 2025 16:16:34 +0800 Subject: [PATCH] workspace: add oxlint --- .gitignore | 1 + framework/eslint-config/build.ts | 72 +++++++++++++++++++ package.json | 2 + packages/blog/index.ts | 2 +- packages/hydrojudge/src/sandbox.ts | 2 +- packages/hydrooj/src/handler/user.ts | 2 +- packages/hydrooj/src/model/builtin.ts | 2 +- packages/hydrooj/src/model/document.ts | 22 +++--- packages/hydrooj/src/plugin-api.ts | 2 +- packages/hydrooj/src/service/hmr.ts | 1 + packages/hydrooj/src/upgrade.ts | 2 +- .../onsite-toolkit/frontend/resolver.page.tsx | 41 ++++++----- packages/prom-client/metrics.ts | 2 +- packages/ui-default/backendlib/template.ts | 3 +- .../messagepad/reducers/dialogues.js | 2 +- .../ui-default/pages/problem_detail.page.tsx | 2 +- packages/ui-default/service-worker.ts | 2 +- packages/ui-next/index.ts | 2 +- 18 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 framework/eslint-config/build.ts diff --git a/.gitignore b/.gitignore index 69b50713b..d3a937f56 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ yarn.lock pnpm-lock.yaml tsconfig.json tsconfig.*.json +.oxlintrc.json # Project Rules ## Core diff --git a/framework/eslint-config/build.ts b/framework/eslint-config/build.ts new file mode 100644 index 000000000..006632ee2 --- /dev/null +++ b/framework/eslint-config/build.ts @@ -0,0 +1,72 @@ +import fs from 'fs'; +import { rules as baseBestPracticesRules } from 'eslint-config-airbnb-base/rules/best-practices'; +import { rules as baseErrorsRules } from 'eslint-config-airbnb-base/rules/errors'; +import { rules as baseES6Rules } from 'eslint-config-airbnb-base/rules/es6'; +import { rules as baseStyleRules } from 'eslint-config-airbnb-base/rules/style'; +import { rules as baseVariablesRules } from 'eslint-config-airbnb-base/rules/variables'; +import yaml from 'js-yaml'; + +const base = yaml.load(fs.readFileSync('base.yaml', 'utf8')) as any; +const react = yaml.load(fs.readFileSync('react.yaml', 'utf8')) as any; + +function sort(obj: Record): Record { + const clone = { ...obj }; + Object.keys(clone).sort().reduce( + (acc, key) => { + acc[key] = clone[key]; + return acc; + }, + ); + return clone; +} + +// NOTE: oxlint doesn't support most stylistic rules + +const rules = sort({ + ...baseBestPracticesRules, + ...baseErrorsRules, + ...baseES6Rules, + ...baseStyleRules, + ...baseVariablesRules, + ...base.rules, + ...react.rules, + 'no-unused-vars': [ + 'off', + // 'error', + // { + // caughtErrorsIgnorePattern: '^_|e', + // argsIgnorePattern: '^_', + // }, + ], + + 'no-await-in-loop': 'off', // temporary disable the file until oxlint support extending config + + // Let typescript to the thing + 'no-undef': 'off', + 'no-empty-function': 'off', + 'no-lonely-if': 'off', + 'no-unsafe-declaration-merging': 'off', + 'no-this-alias': 'off', + 'no-new-array': 'off', // It breaks [...new Array(n)].map() + 'no-useless-spread': 'off', // It breaks [...new Array(n)].map() + 'no-script-url': 'off', // It breaks createElement('a').setAttribute('href', 'javascript:;') +}); + +delete rules['@typescript-eslint/no-unused-vars']; + +fs.writeFileSync('.oxlintrc.json', JSON.stringify({ + $schema: './node_modules/oxlint/configuration_schema.json', + rules, + env: { + browser: true, + es6: true, + jquery: true, + node: true, + }, + globals: { + UiContext: 'readonly', + UserContext: 'readonly', + externalModules: 'readonly', + LOCALES: 'readonly', + }, +}, null, 2)); diff --git a/package.json b/package.json index 61f89a881..bcee74a92 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production", "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", @@ -53,6 +54,7 @@ "mongodb-memory-server": "10.1.4", "nyc": "^17.1.0", "ora": "^8.2.0", + "oxlint": "^0.15.13", "package-json": "^10.0.1", "semver": "^7.7.1", "simple-git": "^3.27.0", diff --git a/packages/blog/index.ts b/packages/blog/index.ts index 20c04a5b3..f58e3db75 100644 --- a/packages/blog/index.ts +++ b/packages/blog/index.ts @@ -4,7 +4,7 @@ import { param, PRIV, Types, UserModel, } from 'hydrooj'; -export const TYPE_BLOG: 70 = 70; +export const TYPE_BLOG = 70 as const; export interface BlogDoc { docType: 70; docId: ObjectId; diff --git a/packages/hydrojudge/src/sandbox.ts b/packages/hydrojudge/src/sandbox.ts index d0f1f8d28..b0083b7d1 100644 --- a/packages/hydrojudge/src/sandbox.ts +++ b/packages/hydrojudge/src/sandbox.ts @@ -85,7 +85,7 @@ function proc(params: Parameter): Cmd { copyOutCached.push('stdout', 'stderr'); if (params.filename) copyOutCached.push(`${params.filename}.out?`); } else if (params.filename) copyOut.push(`${params.filename}.out${supportOptional ? '?' : ''}`); - const copyIn = { ...(params.copyIn || {}) }; + const copyIn = { ...params.copyIn }; const stdin = params.stdin || { content: '' }; if (params.filename) copyIn[`${params.filename}.in`] = stdin; const time = params.time || 16000; diff --git a/packages/hydrooj/src/handler/user.ts b/packages/hydrooj/src/handler/user.ts index 5e352be2c..d3260f9e9 100644 --- a/packages/hydrooj/src/handler/user.ts +++ b/packages/hydrooj/src/handler/user.ts @@ -464,7 +464,7 @@ class OauthCallbackHandler extends Handler { break; } } - const set: Partial = { ...(r.set || {}), oauth: args.type }; + const set: Partial = { ...r.set, oauth: args.type }; if (r.bio) set.bio = r.bio; if (r.viewLang) set.viewLang = r.viewLang; if (r.avatar) set.avatar = r.avatar; diff --git a/packages/hydrooj/src/model/builtin.ts b/packages/hydrooj/src/model/builtin.ts index 0abc6efe9..f6028a515 100644 --- a/packages/hydrooj/src/model/builtin.ts +++ b/packages/hydrooj/src/model/builtin.ts @@ -101,7 +101,7 @@ export const PERM = { PERM_NEVER: 1n << 60n, }; -export const Permission = (family: string, key: BigInt, desc: string) => ({ family, key, desc }); +export const Permission = (family: string, key: bigint, desc: string) => ({ family, key, desc }); export const PERMS = [ Permission('perm_general', PERM.PERM_VIEW, 'View this domain'), diff --git a/packages/hydrooj/src/model/document.ts b/packages/hydrooj/src/model/document.ts index f252481c6..e052bd8c9 100644 --- a/packages/hydrooj/src/model/document.ts +++ b/packages/hydrooj/src/model/document.ts @@ -15,22 +15,22 @@ import { ArrayKeys, MaybeArray, NumberKeys, Projection } from '../typeutils'; import { buildProjection } from '../utils'; type DocID = ObjectId | string | number; -type NormalArrayKeys = Exclude, Symbol>; +type NormalArrayKeys = Exclude, symbol>; export const coll = db.collection('document'); export const collStatus = db.collection('document.status'); -export const TYPE_PROBLEM: 10 = 10; -export const TYPE_PROBLEM_SOLUTION: 11 = 11; -export const TYPE_PROBLEM_LIST: 12 = 12; -export const TYPE_DISCUSSION_NODE: 20 = 20; -export const TYPE_DISCUSSION: 21 = 21; -export const TYPE_DISCUSSION_REPLY: 22 = 22; -export const TYPE_CONTEST: 30 = 30; -export const TYPE_CONTEST_CLARIFICATION: 31 = 31; -export const TYPE_TRAINING: 40 = 40; +export const TYPE_PROBLEM = 10 as const; +export const TYPE_PROBLEM_SOLUTION = 11 as const; +export const TYPE_PROBLEM_LIST = 12 as const; +export const TYPE_DISCUSSION_NODE = 20 as const; +export const TYPE_DISCUSSION = 21 as const; +export const TYPE_DISCUSSION_REPLY = 22 as const; +export const TYPE_CONTEST = 30 as const; +export const TYPE_CONTEST_CLARIFICATION = 31 as const; +export const TYPE_TRAINING = 40 as const; /** @deprecated use `TYPE_CONTEST` with rule `homework` instead. */ -export const TYPE_HOMEWORK: 60 = 60; +export const TYPE_HOMEWORK = 60 as const; export interface DocType { [TYPE_PROBLEM]: ProblemDoc; diff --git a/packages/hydrooj/src/plugin-api.ts b/packages/hydrooj/src/plugin-api.ts index 775d3ed51..9d791b5de 100644 --- a/packages/hydrooj/src/plugin-api.ts +++ b/packages/hydrooj/src/plugin-api.ts @@ -58,4 +58,4 @@ export { EventMap } from './service/bus'; export { db, pwsh }; // to load ctx.scoreboard -export { } from './handler/contest'; +export { } from './handler/contest'; // eslint-disable-line @typescript-eslint/no-useless-empty-export diff --git a/packages/hydrooj/src/service/hmr.ts b/packages/hydrooj/src/service/hmr.ts index b87962169..c60f5b0aa 100644 --- a/packages/hydrooj/src/service/hmr.ts +++ b/packages/hydrooj/src/service/hmr.ts @@ -242,6 +242,7 @@ export default class HMR extends Service { for (const [plugin, { filename, runtime }] of reloads) { try { this.ctx.registry.delete(attempts[filename]); + // eslint-disable-next-line no-await-in-loop await reload(plugin, runtime); } catch (err) { logger.warn(err); diff --git a/packages/hydrooj/src/upgrade.ts b/packages/hydrooj/src/upgrade.ts index 5a4dca4c5..e0eb53a10 100644 --- a/packages/hydrooj/src/upgrade.ts +++ b/packages/hydrooj/src/upgrade.ts @@ -51,7 +51,7 @@ export const coreScripts: MigrationScript[] = [ return true; }, // Init - ...new Array(28).fill(unsupportedUpgrade), + ...new Array(28).fill(unsupportedUpgrade), // oxlint-disable-line no-new-array async function _29_30() { return await iterateAllDomain((ddoc) => RecordModel.coll.updateMany({ domainId: ddoc._id }, { $set: { pdomain: ddoc._id } })); }, diff --git a/packages/onsite-toolkit/frontend/resolver.page.tsx b/packages/onsite-toolkit/frontend/resolver.page.tsx index cf6212ac9..ae5e017aa 100644 --- a/packages/onsite-toolkit/frontend/resolver.page.tsx +++ b/packages/onsite-toolkit/frontend/resolver.page.tsx @@ -259,28 +259,27 @@ export function start(data: ResolverInput, options: DisplaySettings) { background: 'transparent', }), }} - children={<> -
{team.rank === -1 ? '⭐' : team.rank}
- {props.showAvatar && } -
-
- {props.showSchool ? `${teamInfo.institution} - ` : ''}{teamInfo.name} -
-
- {data.problems.map((v) => { - const uncover = team.id === selectedTeam && selectedProblem === v.id; - const problemStatus = team.problems.find((idx) => idx.id === v.id); - if (!problemStatus) return ERR; - return - {submissions(problemStatus)} - ; - })} -
+ > +
{team.rank === -1 ? '⭐' : team.rank}
+ {props.showAvatar && } +
+
+ {props.showSchool ? `${teamInfo.institution} - ` : ''}{teamInfo.name}
-
{team.score}
-
{team.penalty}
- } - />; +
+ {data.problems.map((v) => { + const uncover = team.id === selectedTeam && selectedProblem === v.id; + const problemStatus = team.problems.find((idx) => idx.id === v.id); + if (!problemStatus) return ERR; + return + {submissions(problemStatus)} + ; + })} +
+
+
{team.score}
+
{team.penalty}
+ ; })} ); } diff --git a/packages/prom-client/metrics.ts b/packages/prom-client/metrics.ts index 417c7afd6..31fc07e4d 100644 --- a/packages/prom-client/metrics.ts +++ b/packages/prom-client/metrics.ts @@ -16,7 +16,7 @@ export function createRegistry(ctx: Context) { function createMetric Metric)>( C: T, name: string, help: string, extra?: T extends new (a: infer R) => any ? Partial : never, ): T extends (new (a) => Gauge) ? Gauge : T extends (new (a) => Counter) ? Counter : Metric { - const metric = new C({ name, help, ...(extra || {}) }); + const metric = new C({ name, help, ...extra }); registry.registerMetric(metric); return metric as any; } diff --git a/packages/ui-default/backendlib/template.ts b/packages/ui-default/backendlib/template.ts index cc4ec2106..b2f4730a5 100644 --- a/packages/ui-default/backendlib/template.ts +++ b/packages/ui-default/backendlib/template.ts @@ -2,7 +2,7 @@ import * as status from '@hydrooj/common/status'; import { findFileSync } from '@hydrooj/utils/lib/utils'; import { convert } from 'html-to-text'; import { - avatar, Context, fs, PERM, PRIV, Service, STATUS, yaml, + avatar, Context, difficultyAlgorithm, fs, PERM, PRIV, Service, STATUS, yaml, } from 'hydrooj'; import jsesc from 'jsesc'; import nunjucks from 'nunjucks'; @@ -134,6 +134,7 @@ class Nunjucks extends nunjucks.Environment { this.addGlobal('avatarUrl', avatar); this.addGlobal('formatSeconds', misc.formatSeconds); this.addGlobal('model', global.Hydro.model); + this.addGlobal('lib', { difficulty: difficultyAlgorithm }); this.addGlobal('ui', global.Hydro.ui); this.addGlobal('isIE', (str) => { if (!str) return false; diff --git a/packages/ui-default/components/messagepad/reducers/dialogues.js b/packages/ui-default/components/messagepad/reducers/dialogues.js index 190b1b12c..f33ab4fc8 100644 --- a/packages/ui-default/components/messagepad/reducers/dialogues.js +++ b/packages/ui-default/components/messagepad/reducers/dialogues.js @@ -38,7 +38,7 @@ export default function reducer(state = {}, action = {}) { return { ...state, [to]: { - ...state[to] || {}, + ...state[to], udoc, messages: [ ...state[to]?.messages || [], diff --git a/packages/ui-default/pages/problem_detail.page.tsx b/packages/ui-default/pages/problem_detail.page.tsx index 8c29258bc..19e04eeb3 100644 --- a/packages/ui-default/pages/problem_detail.page.tsx +++ b/packages/ui-default/pages/problem_detail.page.tsx @@ -242,7 +242,7 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor function ProblemNavigation() { [, setUpdate] = React.useState(0); return
- {pids.map((i) => + {pids.map((i) => {i} )}
; diff --git a/packages/ui-default/service-worker.ts b/packages/ui-default/service-worker.ts index e5af3ad5c..9de66a695 100644 --- a/packages/ui-default/service-worker.ts +++ b/packages/ui-default/service-worker.ts @@ -160,12 +160,12 @@ async function get(request: Request) { method: request.method, credentials: isResource ? 'same-origin' : 'include', headers: request.headers, - body: request.body, redirect: request.redirect, keepalive: request.keepalive, referrer: request.referrer, referrerPolicy: request.referrerPolicy, signal: request.signal, + ...(request.method.toLowerCase() === 'get' ? {} : { body: request.body }), }); if (r.ok) { console.log('Load success from ', source.toString()); diff --git a/packages/ui-next/index.ts b/packages/ui-next/index.ts index 45011385f..c6e8210cb 100644 --- a/packages/ui-next/index.ts +++ b/packages/ui-next/index.ts @@ -1,8 +1,8 @@ +import fs from 'fs'; import path from 'path'; import importMetaUrlPlugin from '@codingame/esbuild-import-meta-url-plugin'; import react from '@vitejs/plugin-react-swc'; import c2k from 'koa2-connect'; -import fs from 'fs'; import { createServer } from 'vite'; import { } from '@hydrooj/framework'; import { Context } from 'hydrooj';