migrate lint stack to Oxlint + Oxfmt

This commit is contained in:
Simek
2026-01-21 13:14:41 +01:00
parent 95c2aa3d3e
commit 9d3820b312
75 changed files with 642 additions and 2542 deletions
+3 -2
View File
@@ -79,7 +79,7 @@ pnpm test
# Watch mode for tests
pnpm test:watch
# Check code formatting (Prettier)
# Check code formatting (Oxfmt)
pnpm check-format
# Auto-format code
@@ -187,7 +187,8 @@ pnpm lint [path]
- **pnpm-workspace.yaml**: Defines workspace packages
- **ui/.build/**: Custom frontend build system
- **.scalafmt.conf**: Scala formatting rules
- **ui/.prettierrc.json**: TypeScript/CSS formatting rules
- **ui/.oxlint.json**: TypeScript formatting rules (Oxlint)
- **ui/.oxfmt.json**: TypeScript/CSS/JSON/MD formatting rules (Oxfmt)
- **conf/routes**: HTTP route definitions
- **conf/application.conf.default**: Main application configuration template
-3
View File
@@ -60,8 +60,5 @@ instance.lock
/docker-compose.yml
/?
# eslint cache
/ui/.eslintcache
# deploy bearer
.lila-cli
+10 -11
View File
@@ -19,37 +19,36 @@
"@lichess-org/pgn-viewer": "^2.5.6",
"@types/lichess": "workspace:*",
"@types/node": "^24.10.0",
"@typescript-eslint/eslint-plugin": "^8.46.3",
"@typescript-eslint/parser": "^8.46.3",
"ab": "github:lichess-org/ab-stub",
"chessops": "^0.15",
"eslint": "^9.39.1",
"eslint-plugin-compat": "^6.0.2",
"eslint-plugin-import": "^2.32.0",
"jsdom": "^27.1.0",
"lint-staged": "^16.2.6",
"prettier": "^3.6.2",
"oxfmt": "^0.26.0",
"oxlint": "^1.41.0",
"oxlint-tsgolint": "^0.11.1",
"snabbdom": "3.5.1",
"tsx": "^4.20.6",
"typescript": "^5.9.3"
},
"//": [
"snabbdom pinned to 3.5.1 until https://github.com/snabbdom/snabbdom/issues/1114 is resolved",
"typescript above just to allow manual tsc. ui/.build/package.json's typesript version is the truth"
"typescript above just to allow manual tsc. ui/.build/package.json's typescript version is the truth"
],
"scripts": {
"format": "prettier --config=ui/.prettierrc.json --cache --write --log-level=warn .",
"check-format": "prettier --config=ui/.prettierrc.json --cache --check --log-level=warn .",
"format": "oxfmt --config=ui/.oxfmtrc.json ui",
"check-format": "oxfmt --config=ui/.oxfmtrc.json --check ui",
"add-hooks": "git config get --all core.hooksPath | grep -Fxq bin/git-hooks || git config set --append core.hooksPath bin/git-hooks",
"remove-hooks": "git config unset --value=bin/git-hooks core.hooksPath || true",
"lint": "eslint --config=ui/eslint.config.mjs --cache --cache-location=ui/.eslintcache",
"lint-staged": "lint-staged --config=ui/lint-staged.config.mjs",
"lint": "oxlint --config=ui/.oxlintrc.json --tsconfig=ui/tsconfig.base.json --type-aware ui",
"journal": "journalctl --user -fu lila -o cat",
"metals": "tail -F .metals/metals.log | stdbuf -oL cut -c 21- | rg -v '(notification for request|handleCancellation)'",
"serverlog": "pnpm journal & pnpm metals",
"i18n-file-gen": "pnpx tsx bin/i18n-file-gen.ts",
"multilog": "pnpm serverlog & ui/build -w"
},
"lint-staged": {
"*.{json,scss,ts,mts,mjs}": "pnpm lint --fix && pnpm format"
},
"browserslist": [
"defaults",
"not op_mini all",
+234 -2151
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -81,7 +81,9 @@ export async function subfolders(folder: string, depth = 1): Promise<string[]> {
if (depth <= 0) return [];
return (
await Promise.all(
(await fs.promises.readdir(folder).catch(() => [])).map(async f => {
(
await fs.promises.readdir(folder).catch(() => [])
).map(async f => {
const fullpath = join(folder, f);
return (await isFolder(fullpath)) ? [fullpath, ...(await subfolders(fullpath, depth - 1))] : [];
}),
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"singleQuote": true,
"printWidth": 110,
"arrowParens": "avoid",
"endOfLine": "lf",
"ignorePatterns": ["package.json"]
}
+221
View File
@@ -0,0 +1,221 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript"],
"env": {
"builtin": true
},
"ignorePatterns": ["**/bin/", "**/dist/"],
"rules": {
"prefer-spread": "off",
"prefer-rest-params": "off",
"no-useless-escape": "off",
"no-control-regex": "off",
"no-var": "warn",
"no-async-promise-executor": "warn",
"no-constant-condition": [
"warn",
{
"checkLoops": false
}
],
"no-duplicate-imports": "error",
"no-restricted-globals": [
"error",
"addEventListener",
"blur",
"captureEvents",
"chrome",
"clientInformation",
"close",
"closed",
"createImageBitmap",
"crypto",
"customElements",
"defaultstatus",
"defaultStatus",
"devicePixelRatio",
"external",
"find",
"focus",
"frameElement",
"frames",
"getComputedStyle",
"getSelection",
"indexedDB",
"innerHeight",
"innerWidth",
"isSecureContext",
"length",
"locationbar",
"matchMedia",
"menubar",
"moveBy",
"moveTo",
"name",
"onabort",
"onafterprint",
"onanimationend",
"onanimationiteration",
"onanimationstart",
"onappinstalled",
"onauxclick",
"onbeforeinstallprompt",
"onbeforeprint",
"onbeforeunload",
"onblur",
"oncancel",
"oncanplay",
"oncanplaythrough",
"onchange",
"onclick",
"onclose",
"oncontextmenu",
"oncuechange",
"ondblclick",
"ondevicemotion",
"ondeviceorientation",
"ondeviceorientationabsolute",
"ondrag",
"ondragend",
"ondragenter",
"ondragleave",
"ondragover",
"ondragstart",
"ondrop",
"ondurationchange",
"onemptied",
"onended",
"onerror",
"onfocus",
"ongotpointercapture",
"onhashchange",
"oninput",
"oninvalid",
"onkeydown",
"onkeypress",
"onkeyup",
"onlanguagechange",
"onload",
"onloadeddata",
"onloadedmetadata",
"onloadstart",
"onlostpointercapture",
"onmessage",
"onmessageerror",
"onmousedown",
"onmouseenter",
"onmouseleave",
"onmousemove",
"onmouseout",
"onmouseover",
"onmouseup",
"onmousewheel",
"onoffline",
"ononline",
"onpagehide",
"onpageshow",
"onpause",
"onplay",
"onplaying",
"onpointercancel",
"onpointerdown",
"onpointerenter",
"onpointerleave",
"onpointermove",
"onpointerout",
"onpointerover",
"onpointerup",
"onpopstate",
"onprogress",
"onratechange",
"onrejectionhandled",
"onreset",
"onresize",
"onscroll",
"onsearch",
"onseeked",
"onseeking",
"onselect",
"onstalled",
"onstorage",
"onsubmit",
"onsuspend",
"ontimeupdate",
"ontoggle",
"ontransitionend",
"onunhandledrejection",
"onunload",
"onvolumechange",
"onwaiting",
"onwebkitanimationend",
"onwebkitanimationiteration",
"onwebkitanimationstart",
"onwebkittransitionend",
"onwheel",
"open",
"openDatabase",
"opener",
"origin",
"outerHeight",
"outerWidth",
"pageXOffset",
"pageYOffset",
"parent",
"personalbar",
"postMessage",
"print",
"releaseEvents",
"removeEventListener",
"resizeBy",
"resizeTo",
"screen",
"screenLeft",
"screenTop",
"screenX",
"screenY",
"scroll",
"scrollbars",
"scrollBy",
"scrollTo",
"scrollX",
"scrollY",
"status",
"statusbar",
"stop",
"styleMedia",
"toolbar",
"top",
"visualViewport",
"webkitRequestFileSystem",
"webkitResolveLocalFileSystemURL",
"webkitStorageInfo"
],
"no-empty-function": "off",
"no-extra-boolean-cast": "off",
"no-unused-expressions": "off",
"no-unused-vars": [
"warn",
{
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
],
"typescript/explicit-module-boundary-types": "off",
"typescript/no-base-to-string": "off",
"typescript/no-duplicate-type-constituents": "off",
"typescript/no-empty-interface": "off",
"typescript/no-explicit-any": "off",
"typescript/no-floating-promises": "off",
"typescript/no-for-in-array": "off",
"typescript/no-misused-spread": "off",
"typescript/no-non-null-assertion": "off",
"typescript/no-redundant-type-constituents": "off",
"typescript/no-this-alias": "off",
"typescript/triple-slash-reference": "off",
"typescript/no-wrapper-object-types": "off",
"typescript/require-array-sort-compare": "off",
"typescript/restrict-template-expressions": "off",
"typescript/unbound-method": "off"
}
}
-5
View File
@@ -1,5 +0,0 @@
{
"singleQuote": true,
"printWidth": 110,
"arrowParens": "avoid"
}
+22 -5
View File
@@ -10,11 +10,26 @@ import { Autoplay, type AutoplayDelay } from './autoplay';
import { makeTree, treePath, treeOps, type TreeWrapper } from 'lib/tree';
import { compute as computeAutoShapes } from './autoShape';
import type { Config as ChessgroundConfig } from '@lichess-org/chessground/config';
import type { CevalHandler, EvalMeta, CevalOpts } from 'lib/ceval';
import { CevalCtrl, isEvalBetter, sanIrreversible } from 'lib/ceval';
import {
CevalCtrl,
isEvalBetter,
sanIrreversible,
type CevalHandler,
type EvalMeta,
type CevalOpts,
} from 'lib/ceval';
import { TreeView } from './treeView/treeView';
import type { Prop, Toggle } from 'lib';
import { defined, prop, toggle, debounce, throttle, requestIdleCallback, propWithEffect } from 'lib';
import {
defined,
prop,
toggle,
debounce,
throttle,
requestIdleCallback,
propWithEffect,
type Prop,
type Toggle,
} from 'lib';
import { pubsub } from 'lib/pubsub';
import type { DrawShape } from '@lichess-org/chessground/draw';
import EvalCache from './evalCache';
@@ -1029,7 +1044,9 @@ export default class AnalyseCtrl implements CevalHandler {
toggleVariationArrows = () => {
const trueValue = this.variationArrowOpacity(false);
this.variationArrowOpacity(trueValue === 0 ? 0.6 : -trueValue);
if (typeof trueValue === 'number') {
this.variationArrowOpacity(trueValue === 0 ? 0.6 : -trueValue);
}
};
outcome = () => this.node.outcome(); // LT BC
+5 -5
View File
@@ -154,11 +154,11 @@ export class IdbTree {
const [first, second, third] = node.children.filter(n => this.ctrl.showFishnetAnalysis() || !n.comp);
return Boolean(
first?.forceVariation ||
third ||
(second && treeOps.hasBranching(second, 6)) ||
(isMainline &&
this.ctrl.treeView.mode === 'column' &&
(second || first?.comments?.filter(Boolean).length)),
third ||
(second && treeOps.hasBranching(second, 6)) ||
(isMainline &&
this.ctrl.treeView.mode === 'column' &&
(second || first?.comments?.filter(Boolean).length)),
);
}
@@ -104,7 +104,6 @@ const retroStateView = {
i18n.site.viewTheSolution,
),
hl('button.retro-skip', focusFriendlyHook(ctrl.retro.skip), i18n.site.skipThisMove),
,
];
},
eval({ ctrl }: RetroContext) {
+12 -2
View File
@@ -1,7 +1,17 @@
import { parseFen } from 'chessops/fen';
import { defined, prop, type Prop, toggle } from 'lib';
import type { Dialog, VNode } from 'lib/view';
import { snabDialog, alert, bind, bindSubmit, onInsert, hl, dataIcon, spinnerVdom } from 'lib/view';
import {
snabDialog,
alert,
bind,
bindSubmit,
onInsert,
hl,
dataIcon,
spinnerVdom,
type Dialog,
type VNode,
} from 'lib/view';
import * as licon from 'lib/licon';
import { storedProp } from 'lib/storage';
import { json as xhrJson, text as xhrText } from 'lib/xhr';
+1 -2
View File
@@ -1,8 +1,7 @@
import * as licon from 'lib/licon';
import { type VNode, bind, onInsert, hl } from 'lib/view';
import { type VNode, bind, onInsert, hl, confirm } from 'lib/view';
import { richHTML } from 'lib/richText';
import type StudyCtrl from './studyCtrl';
import { confirm } from 'lib/view';
export type Save = (t: string) => void;
@@ -1,6 +1,5 @@
import * as licon from 'lib/licon';
import { bind, bindNonPassive, type MaybeVNodes } from 'lib/view';
import { spinnerVdom as spinner, toggle } from 'lib/view';
import { bind, bindNonPassive, type MaybeVNodes, spinnerVdom as spinner, toggle } from 'lib/view';
import { h, thunk, type VNode } from 'snabbdom';
import { richHTML } from 'lib/richText';
import { option, plural } from '@/view/util';
@@ -1,5 +1,4 @@
import { hl, type VNode } from 'lib/view';
import { getChessground, initMiniBoardWith, spinnerVdom } from 'lib/view';
import { hl, type VNode, getChessground, initMiniBoardWith, spinnerVdom } from 'lib/view';
import { fenColor, uciToMove } from 'lib/game/chess';
import { type ChatPlugin } from 'lib/chat/interfaces';
import type AnalyseCtrl from '@/ctrl';
+1 -2
View File
@@ -1,7 +1,6 @@
import { type VNode, dataIcon, hl, onInsert, type MaybeVNodes } from 'lib/view';
import { type VNode, dataIcon, hl, onInsert, type MaybeVNodes, spinnerVdom as spinner } from 'lib/view';
import { json as xhrJson } from 'lib/xhr';
import * as licon from 'lib/licon';
import { spinnerVdom as spinner } from 'lib/view';
import type { Photo, RelayRound, RelayTour, RoundId, TourId } from './interfaces';
import { playerColoredResult } from './customScoreStatus';
import { playerFedFlag } from '../playerBars';
+1 -2
View File
@@ -1,9 +1,8 @@
import { type MaybeVNodes, type VNode, onInsert, hl } from 'lib/view';
import { type MaybeVNodes, type VNode, onInsert, hl, spinnerVdom as spinner } from 'lib/view';
import { json as xhrJson } from 'lib/xhr';
import type { RelayRound } from './interfaces';
import type { ChapterId, ChapterPreview, StudyPlayer, ChapterSelect } from '../interfaces';
import { type MultiCloudEval, renderScore } from '../multiCloudEval';
import { spinnerVdom as spinner } from 'lib/view';
import { playerFedFlag } from '../playerBars';
import { gameLinkAttrs, gameLinksListener, StudyChapters } from '../studyChapters';
import { userTitle } from 'lib/view/userLink';
+1 -2
View File
@@ -1,7 +1,7 @@
import type AnalyseCtrl from '@/ctrl';
import RelayCtrl, { type RelayTab } from './relayCtrl';
import * as licon from 'lib/licon';
import { bind, dataIcon, onInsert, hl, type LooseVNode } from 'lib/view';
import { bind, dataIcon, onInsert, hl, type LooseVNode, toggle, copyMeInput } from 'lib/view';
import type { VNode } from 'snabbdom';
import { innerHTML, richHTML } from 'lib/richText';
import type {
@@ -15,7 +15,6 @@ import type {
import { view as multiBoardView } from '../multiBoard';
import { defined, memoize } from 'lib';
import type StudyCtrl from '../studyCtrl';
import { toggle, copyMeInput } from 'lib/view';
import { text as xhrText } from 'lib/xhr';
import { teamsView } from './relayTeams';
import { statsView } from './relayStats';
+1 -2
View File
@@ -1,6 +1,6 @@
import { defined, prop, type Prop, scrollToInnerSelector } from 'lib';
import * as licon from 'lib/licon';
import { type VNode, bind, dataIcon, iconTag, hl } from 'lib/view';
import { type VNode, bind, dataIcon, iconTag, hl, alert } from 'lib/view';
import type AnalyseCtrl from '../ctrl';
import type { StudySocketSend } from '../socket';
import { StudyChapterEditForm } from './chapterEditForm';
@@ -24,7 +24,6 @@ import type StudyCtrl from './studyCtrl';
import { opposite } from 'chessops/util';
import { fenColor } from 'lib/game/chess';
import type Sortable from 'sortablejs';
import { alert } from 'lib/view';
import { INITIAL_FEN } from 'chessops/fen';
/* read-only interface for external use */
+1 -1
View File
@@ -412,7 +412,7 @@ export default class StudyCtrl {
this.practice ? 'practice/load' : 'study',
this.data.id,
this.vm.mode.sticky ? undefined : this.vm.chapterId,
(withChapters = withChapters),
withChapters,
)
.then(this.onReload, site.reload)
.then(callback);
+1 -2
View File
@@ -1,8 +1,7 @@
import * as licon from 'lib/licon';
import { prop } from 'lib';
import { snabDialog, confirm, prompt } from 'lib/view';
import { snabDialog, confirm, prompt, type VNode, bindSubmit, bindNonPassive, onInsert, hl } from 'lib/view';
import flairPickerLoader from 'bits/flairPicker';
import { type VNode, bindSubmit, bindNonPassive, onInsert, hl } from 'lib/view';
import { emptyRedButton } from '../view/util';
import type { StudyData } from './interfaces';
import type RelayCtrl from './relay/relayCtrl';
+1 -2
View File
@@ -1,7 +1,6 @@
import { prop } from 'lib';
import * as licon from 'lib/licon';
import { type VNode, bind, dataIcon, hl } from 'lib/view';
import { copyMeInput } from 'lib/view';
import { type VNode, bind, dataIcon, hl, copyMeInput } from 'lib/view';
import { writeTextClipboard, url as xhrUrl } from 'lib/xhr';
import { renderIndexAndMove } from '../view/components';
import { baseUrl } from '../view/util';
+11 -3
View File
@@ -1,8 +1,17 @@
import { isEmpty } from 'lib';
import * as licon from 'lib/licon';
import { displayColumns } from 'lib/device';
import type { VNode, LooseVNodes, MaybeVNodes, ToggleSettings } from 'lib/view';
import { domDialog, bind, dataIcon, hl, toggle } from 'lib/view';
import {
domDialog,
bind,
dataIcon,
hl,
toggle,
type VNode,
type LooseVNodes,
type MaybeVNodes,
type ToggleSettings,
} from 'lib/view';
import type { AutoplayDelay } from '../autoplay';
import type AnalyseCtrl from '../ctrl';
import { cont as contRoute } from 'lib/game/router';
@@ -134,7 +143,6 @@ export function view(ctrl: AnalyseCtrl): VNode {
{ hook: bind('click', ctrl.toggleRetro, ctrl.redraw), attrs: dataIcon(licon.GraduateCap) },
'Learn from your mistakes',
),
,
canContinue &&
hl(
'a',
+2 -1
View File
@@ -11,6 +11,8 @@ import {
onInsert,
dataIcon,
hl,
spinnerVdom as spinner,
stepwiseScroll,
} from 'lib/view';
import { playable } from 'lib/game';
import { isMobile } from 'lib/device';
@@ -27,7 +29,6 @@ import * as chessground from '../ground';
import type AnalyseCtrl from '../ctrl';
import type { ConcealOf } from '../interfaces';
import * as pgnExport from '../pgnExport';
import { spinnerVdom as spinner, stepwiseScroll } from 'lib/view';
import * as Prefs from 'lib/prefs';
import statusView from 'lib/game/view/status';
import { renderNextChapter } from '../study/nextChapter';
+1 -2
View File
@@ -1,12 +1,11 @@
import { renderEval, view as cevalView } from 'lib/ceval';
import { repeater, myUserId } from 'lib';
import * as licon from 'lib/licon';
import { type VNode, type LooseVNode, onInsert, hl } from 'lib/view';
import { type VNode, type LooseVNode, onInsert, hl, domDialog } from 'lib/view';
import { displayColumns, isTouchDevice } from 'lib/device';
import { addPointerListeners } from 'lib/pointer';
import * as control from '../control';
import type AnalyseCtrl from '../ctrl';
import { domDialog } from 'lib/view';
type Action =
| 'first'
+5 -3
View File
@@ -580,9 +580,11 @@ function renderStudyPlayer(ctrl: AnalyseCtrl, color: Color): VNode | undefined {
hl(
'span',
keys
.reduce<
string[]
>((strs, [key, i18n]) => (player[key] ? strs.concat(`${i18n}: ${key === 'fed' ? player[key].name : player[key]}`) : strs), [])
.reduce<string[]>(
(strs, [key, i18n]) =>
player[key] ? strs.concat(`${i18n}: ${key === 'fed' ? player[key].name : player[key]}`) : strs,
[],
)
.join(' '),
)
);
-1
View File
@@ -20,7 +20,6 @@ export async function initModule(opts?: DiagnosticOpts): Promise<void> {
`Browser: ${navigator.userAgent}\n` +
('userAgentData' in navigator
? // @ts-ignore userAgentData not documented in TypeScript https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
// eslint-disable-next-line compat/compat
`Brand: "${navigator.userAgentData.brands.map(b => `${b.brand} ${b.version}`).join('; ')}", `
: '') +
`Cores: ${navigator.hardwareConcurrency}, ` +
+1 -2
View File
@@ -1,5 +1,4 @@
import { domDialog, type Dialog } from 'lib/view';
import { alert, confirm } from 'lib/view';
import { domDialog, type Dialog, alert, confirm } from 'lib/view';
import { frag } from 'lib';
import * as licon from 'lib/licon';
import { renderRemoveButton } from './devUtil';
+1 -2
View File
@@ -1,8 +1,7 @@
import * as co from 'chessops';
import { type VNode, hl, onInsert, bind } from 'lib/view';
import { type VNode, hl, onInsert, bind, domDialog } from 'lib/view';
import * as licon from 'lib/licon';
import { storedBooleanProp, storedIntProp } from 'lib/storage';
import { domDialog } from 'lib/view';
import { EditDialog } from './editDialog';
import { Bot } from 'lib/bot/bot';
import { resultsString, playersWithResults, rangeTicks } from './devUtil';
+1 -2
View File
@@ -4,8 +4,7 @@ import { frag } from 'lib';
import { deepFreeze, definedMap } from 'lib/algo';
import { buildFromSchema, Panes } from './panes';
import { deadStrip } from './devUtil';
import { domDialog, type Dialog, type Action } from 'lib/view';
import { confirm, alert } from 'lib/view';
import { domDialog, type Dialog, type Action, confirm, alert } from 'lib/view';
import type { BotInfo } from 'lib/bot/types';
import { Bot } from 'lib/bot/bot';
import { AssetDialog, type AssetType } from './assetDialog';
+10 -2
View File
@@ -1,7 +1,15 @@
import { Pane } from './pane';
import { Chart, PointElement, LinearScale, LineController, LineElement } from 'chart.js';
import type { Filter, FilterBy, FilterFacetKey } from 'lib/bot/filter';
import { addPoint, asData, filterFacetKeys, filterFacets, filterBys } from 'lib/bot/filter';
import {
addPoint,
asData,
filterFacetKeys,
filterFacets,
filterBys,
type Filter,
type FilterBy,
type FilterFacetKey,
} from 'lib/bot/filter';
import { frag } from 'lib';
import { clamp } from 'lib/algo';
import type { PaneArgs, FilterInfo } from './devTypes';
+1 -2
View File
@@ -1,7 +1,6 @@
import { h } from 'snabbdom';
import type { LooseVNode } from 'lib/view';
import { type LooseVNode, boardMenu as menuDropdown } from 'lib/view';
import type PlayCtrl from '../playCtrl';
import { boardMenu as menuDropdown } from 'lib/view';
// import { toggle } from 'lib';
// import { boolPrefXhrToggle } from 'lib/view';
+10 -2
View File
@@ -1,7 +1,15 @@
import * as licon from 'lib/licon';
import { bind, hl, onInsert, type LooseVNodes, dataIcon, type VNode } from 'lib/view';
import {
bind,
hl,
onInsert,
type LooseVNodes,
dataIcon,
type VNode,
stepwiseScroll,
toggleButton as boardMenuToggleButton,
} from 'lib/view';
import { Chessground } from '@lichess-org/chessground';
import { stepwiseScroll, toggleButton as boardMenuToggleButton } from 'lib/view';
import type PlayCtrl from '../playCtrl';
import { initialGround } from '@/ground';
import { botAssetUrl } from 'lib/bot/botLoader';
+1 -1
View File
@@ -47,6 +47,6 @@ export const setupDialog = (ctrl: SetupCtrl) => {
};
const settingsPreview = (ctrl: SetupCtrl) => {
const color = colors.find(c => c.key === ctrl.color())?.name!;
const color = colors.find(c => c.key === ctrl.color())?.name ?? 'random';
return [color, ctrl.timeControl.isRealTime() ? ctrl.timeControl.clockStr() : 'No clock'].join(' | ');
};
+1 -1
View File
@@ -13,7 +13,7 @@ import { pubsub } from 'lib/pubsub';
import { wsSend, wsAverageLag } from 'lib/socket';
declare module 'chart.js' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// oxlint-disable-next-line no-unused-vars
interface PluginOptionsByType<TType extends ChartType> {
needle?: {
value: number;
-249
View File
@@ -1,249 +0,0 @@
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import compat from 'eslint-plugin-compat';
import imports from 'eslint-plugin-import';
const barrelHint = 'This script must remain side-effect free.';
function sideEffects(action) {
return {
'no-restricted-syntax': [
action,
{
selector: 'Program > ImportDeclaration[importKind!="type"][specifiers.length=0]',
message: `${barrelHint} No side-effect imports`,
},
{
selector: 'Program > ExpressionStatement:not([directive])',
message: `${barrelHint} No top-level expressions in index.ts`,
},
{
selector: 'Program > ExpressionStatement > CallExpression',
message: `${barrelHint} No top-level calls in index.ts`,
},
{
selector: 'Program > ExpressionStatement > NewExpression',
message: `${barrelHint} No top-level new in index.ts`,
},
{
selector: 'Program > ExpressionStatement > AwaitExpression',
message: `${barrelHint} No top-level await in index.ts`,
},
{
selector:
'Program > VariableDeclaration > VariableDeclarator[init.type=/^(CallExpression|NewExpression|UpdateExpression|AssignmentExpression|AwaitExpression|TaggedTemplateExpression)$/]',
message: `${barrelHint} No side-effectful initializers in index.ts`,
},
],
};
}
export default [
{ ignores: ['*', '!ui/', '!bin/', '**/dist/'] },
{
files: ['**/*.{ts,mts,mjs}'],
plugins: { '@typescript-eslint': typescriptEslint, compat, imports },
languageOptions: { parser: tsParser, ecmaVersion: 5, sourceType: 'module' },
rules: {
...sideEffects('off'),
'compat/compat': 'warn',
'@typescript-eslint/no-empty-interface': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-this-alias': 'off',
'imports/no-duplicates': ['error'],
'@typescript-eslint/no-unused-vars': [
'warn',
{ varsIgnorePattern: '^_', argsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
],
'linebreak-style': ['error', 'unix'],
'prefer-const': 'error',
'prefer-spread': 'off',
'prefer-rest-params': 'off',
'no-useless-escape': 'off',
'no-var': 'warn',
'no-async-promise-executor': 'warn',
'no-constant-condition': ['warn', { checkLoops: false }],
'no-restricted-globals': [
'error',
'addEventListener',
'blur',
'captureEvents',
'chrome',
'clientInformation',
'close',
'closed',
'createImageBitmap',
'crypto',
'customElements',
'defaultstatus',
'defaultStatus',
'devicePixelRatio',
'external',
'find',
'focus',
'frameElement',
'frames',
'getComputedStyle',
'getSelection',
'indexedDB',
'innerHeight',
'innerWidth',
'isSecureContext',
'length',
'locationbar',
'matchMedia',
'menubar',
'moveBy',
'moveTo',
'name',
'onabort',
'onafterprint',
'onanimationend',
'onanimationiteration',
'onanimationstart',
'onappinstalled',
'onauxclick',
'onbeforeinstallprompt',
'onbeforeprint',
'onbeforeunload',
'onblur',
'oncancel',
'oncanplay',
'oncanplaythrough',
'onchange',
'onclick',
'onclose',
'oncontextmenu',
'oncuechange',
'ondblclick',
'ondevicemotion',
'ondeviceorientation',
'ondeviceorientationabsolute',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'ondurationchange',
'onemptied',
'onended',
'onerror',
'onfocus',
'ongotpointercapture',
'onhashchange',
'oninput',
'oninvalid',
'onkeydown',
'onkeypress',
'onkeyup',
'onlanguagechange',
'onload',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onlostpointercapture',
'onmessage',
'onmessageerror',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onoffline',
'ononline',
'onpagehide',
'onpageshow',
'onpause',
'onplay',
'onplaying',
'onpointercancel',
'onpointerdown',
'onpointerenter',
'onpointerleave',
'onpointermove',
'onpointerout',
'onpointerover',
'onpointerup',
'onpopstate',
'onprogress',
'onratechange',
'onrejectionhandled',
'onreset',
'onresize',
'onscroll',
'onsearch',
'onseeked',
'onseeking',
'onselect',
'onstalled',
'onstorage',
'onsubmit',
'onsuspend',
'ontimeupdate',
'ontoggle',
'ontransitionend',
'onunhandledrejection',
'onunload',
'onvolumechange',
'onwaiting',
'onwebkitanimationend',
'onwebkitanimationiteration',
'onwebkitanimationstart',
'onwebkittransitionend',
'onwheel',
'open',
'openDatabase',
'opener',
'origin',
'outerHeight',
'outerWidth',
'pageXOffset',
'pageYOffset',
'parent',
'personalbar',
'postMessage',
'print',
'releaseEvents',
'removeEventListener',
'resizeBy',
'resizeTo',
'screen',
'screenLeft',
'screenTop',
'screenX',
'screenY',
'scroll',
'scrollbars',
'scrollBy',
'scrollTo',
'scrollX',
'scrollY',
//'self',
'status',
'statusbar',
'stop',
'styleMedia',
'toolbar',
'top',
'visualViewport',
'webkitRequestFileSystem',
'webkitResolveLocalFileSystemURL',
'webkitStorageInfo',
],
},
},
{
files: ['**/index.ts'],
rules: {
...sideEffects('error'),
},
},
];
+1 -1
View File
@@ -3,7 +3,7 @@
* flight. Any extra calls are dropped, except the last one, which waits for
* the previous call to complete.
*/
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
export function throttlePromiseWithResult<R, T extends (...args: any) => Promise<R>>(
wrapped: T,
+10 -2
View File
@@ -1,8 +1,16 @@
import * as co from 'chessops';
import { zip } from '../algo';
import { clockToSpeed } from '@/game';
import type { FilterFacetValue, FilterSpec, FilterName, Filters } from './filter';
import { quantizeFilter, evaluateFilter, filterFacetKeys, combine } from './filter';
import {
type FilterFacetValue,
type FilterSpec,
type FilterName,
type Filters,
quantizeFilter,
evaluateFilter,
filterFacetKeys,
combine,
} from './filter';
import type { SearchResult } from '@lichess-org/zerofish';
import type { OpeningBook } from '../game/polyglot';
import { movetime as getMovetime } from './movetime';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { throttle } from '../async';
import { Engines } from './engines/engines';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import type { ClientEval } from '@/tree/types';
import { isMobile } from '../device';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { povChances } from '../winningChances';
import * as licon from '@/licon';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import type { WinningChances } from './types';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
export const defined = <T>(value: T | undefined): value is T => value !== undefined;
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { shuffle } from '../algo';
import { normalizeMove } from 'chessops/chess';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import type { Status } from './status';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { charToRole, makeSquare, type Square } from 'chessops';
import { fixCrazySan } from './chess';
-1
View File
@@ -24,7 +24,6 @@ export const highlightSearchTerm = (search: string, selector: string): void => {
// create a CSS highlight that can be styled with the ::highlight(search) pseudo-element
if (typeof Highlight !== 'function') throw 'no Highlight support';
// eslint-disable-next-line compat/compat
const highlight = new Highlight(...ranges);
CSS.highlights.set(highlightName, highlight);
};
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
/** promisify [indexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) and add nothing
* ### basic usage:
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
// Rich Text helper functions
// Refactored for https://github.com/lichess-org/lila/issues/7342 request
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { defined, notNull, type Prop, withEffect } from './common';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import type { TreeNode, TreePath } from './types';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import type { TreeNode, TreePath } from './types';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { h } from 'snabbdom';
import { type Toggle, myUserId, onClickAway } from '@/index';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { h, type Hooks, type VNode, type Attrs } from 'snabbdom';
import { bind } from './snabbdom';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { onInsert, hl, type VNode, type Attrs, type LooseVNodes } from './snabbdom';
import { isTouchDevice } from '@/device';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { type Dialog, domDialog } from './dialog';
import { escapeHtml } from '../index';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import { h, type VNode } from 'snabbdom';
import * as domData from '@/data';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint no-restricted-syntax:"error" */ // no side effects allowed due to re-export by index.ts
// no side effects allowed due to re-export by index.ts
import {
type VNode,
-11
View File
@@ -1,11 +0,0 @@
import { lstatSync } from 'fs';
export default {
// NOTE: these patterns must stay in sync with bin/git-hooks/pre-commit!
'*.{json,scss,ts}': files => {
const regularFiles = files.filter(f => !lstatSync(f).isSymbolicLink());
return regularFiles.length
? `prettier --config=ui/.prettierrc.json --write ${regularFiles.join(' ')}`
: 'true';
},
};
+1 -2
View File
@@ -1,10 +1,9 @@
import { h, type VNode } from 'snabbdom';
import { bind, type MaybeVNodes } from 'lib/view';
import { bind, type MaybeVNodes, confirm } from 'lib/view';
import { tds, perfNames } from './util';
import type LobbyController from '../ctrl';
import type { Seek } from '../interfaces';
import perfIcons from 'lib/game/perfIcons';
import { confirm } from 'lib/view';
function renderSeek(ctrl: LobbyController, seek: Seek): VNode {
const klass = seek.action === 'joinSeek' ? 'join' : 'cancel';
+1 -2
View File
@@ -1,5 +1,4 @@
import { hl, type VNode, type LooseVNodes } from 'lib/view';
import { snabDialog, spinnerVdom } from 'lib/view';
import { hl, type VNode, type LooseVNodes, snabDialog, spinnerVdom } from 'lib/view';
import type LobbyController from '@/ctrl';
import { variantPicker } from './components/variantPicker';
import { gameModeButtons } from './components/gameModeButtons';
+1 -2
View File
@@ -1,7 +1,6 @@
import type { Ctrl, NotifyData, Notification } from './interfaces';
import { hl, type VNode, type LooseVNodes } from 'lib/view';
import { hl, type VNode, type LooseVNodes, spinnerVdom as spinner } from 'lib/view';
import * as licon from 'lib/licon';
import { spinnerVdom as spinner } from 'lib/view';
import makeRenderers from './renderers';
import { pubsub } from 'lib/pubsub';
+1 -2
View File
@@ -3,8 +3,7 @@ import type RacerCtrl from '../ctrl';
import renderClock from 'lib/puz/view/clock';
import renderHistory from 'lib/puz/view/history';
import * as licon from 'lib/licon';
import { copyMeInput } from 'lib/view';
import { type VNode, type MaybeVNodes, bind, hl } from 'lib/view';
import { copyMeInput, type VNode, type MaybeVNodes, bind, hl } from 'lib/view';
import { playModifiers, renderCombo } from 'lib/puz/view/util';
import { renderRace } from './race';
import { renderBoard } from './board';
+1 -2
View File
@@ -1,10 +1,9 @@
import { pieceGrams, totalGames } from './constants';
import type { Counted, Opening, Recap, Sources, RecapPerf, Opts } from './interfaces';
import { onInsert, hl, type LooseVNodes, type VNode, dataIcon } from 'lib/view';
import { onInsert, hl, type LooseVNodes, type VNode, dataIcon, spinnerVdom } from 'lib/view';
import { loadOpeningLpv } from './ui';
import { shuffle } from 'lib/algo';
import { fullName, userFlair, userTitle } from 'lib/view/userLink';
import { spinnerVdom } from 'lib/view';
import { formatDuration, perfIsSpeed, perfLabel } from './util';
import perfIcons from 'lib/game/perfIcons';
import * as licon from 'lib/licon';
+7 -2
View File
@@ -1,6 +1,11 @@
import { hl, type LooseVNode } from 'lib/view';
import {
hl,
type LooseVNode,
boardMenu as menuDropdown,
toggle as cmnToggle,
boolPrefXhrToggle,
} from 'lib/view';
import type RoundController from '../ctrl';
import { boardMenu as menuDropdown, toggle as cmnToggle, boolPrefXhrToggle } from 'lib/view';
import { toggle } from 'lib';
import { displayColumns, isTouchDevice } from 'lib/device';
import { storage } from 'lib/storage';
+1 -2
View File
@@ -1,13 +1,12 @@
import type { VNode, Hooks } from 'snabbdom';
import * as licon from 'lib/licon';
import { spinnerVdom as spinner } from 'lib/view';
import { spinnerVdom as spinner, type LooseVNodes, type LooseVNode, hl, bind, onInsert } from 'lib/view';
import { justIcon } from '../util';
import { finished, aborted, replayable, rematchable, moretimeable, type PlayerUser } from 'lib/game';
import { game as gameRoute } from 'lib/game/router';
import type { EventsWithoutPayload, RoundData } from '../interfaces';
import type { ClockData } from 'lib/game/clock/clockCtrl';
import type RoundController from '../ctrl';
import { type LooseVNodes, type LooseVNode, hl, bind, onInsert } from 'lib/view';
import { pubsub } from 'lib/pubsub';
export interface ButtonState {
+1 -2
View File
@@ -1,8 +1,7 @@
import { next, prev, view } from '../keyboard';
import crazyView from '../crazy/crazyView';
import type RoundController from '../ctrl';
import { stepwiseScroll } from 'lib/view';
import { type VNode, hl, bind } from 'lib/view';
import { stepwiseScroll, type VNode, hl, bind } from 'lib/view';
import { render as renderKeyboardMove } from 'keyboardMove';
import { render as renderGround } from '../ground';
import { renderTable } from './table';
+8 -2
View File
@@ -7,8 +7,14 @@ import { throttle } from 'lib/async';
import viewStatus from 'lib/game/view/status';
import { game as gameRoute } from 'lib/game/router';
import type { Step } from '../interfaces';
import { toggleButton as boardMenuToggleButton } from 'lib/view';
import { type VNode, type LooseVNodes, type LooseVNode, hl, onInsert } from 'lib/view';
import {
toggleButton as boardMenuToggleButton,
type VNode,
type LooseVNodes,
type LooseVNode,
hl,
onInsert,
} from 'lib/view';
import boardMenu from './boardMenu';
import { repeater } from 'lib';
import { addPointerListeners } from 'lib/pointer';
+1 -2
View File
@@ -5,8 +5,7 @@ import renderExpiration from './expiration';
import { userHtml } from './user';
import * as button from './button';
import type RoundController from '../ctrl';
import { type LooseVNodes, hl, bind } from 'lib/view';
import { toggleButton as boardMenuToggleButton } from 'lib/view';
import { type LooseVNodes, hl, bind, toggleButton as boardMenuToggleButton } from 'lib/view';
import { anyClockView } from './clock';
function renderPlayer(ctrl: RoundController, position: TopOrBottom) {
-1
View File
@@ -8,7 +8,6 @@ export default async function () {
workerUrl.searchParams.set('asset-url', document.body.getAttribute('data-asset-url')!);
let newSub: PushSubscription | undefined = undefined;
try {
// eslint-disable-next-line compat/compat
const reg = await navigator.serviceWorker.register(workerUrl.href, { scope: '/', updateViaCache: 'all' });
const store = storage.make('push-subscribed');
+11 -2
View File
@@ -1,6 +1,15 @@
import * as licon from 'lib/licon';
import { spinnerVdom, initMiniGames, prompt } from 'lib/view';
import { type VNode, dataIcon, bind, onInsert, type LooseVNodes, hl } from 'lib/view';
import {
spinnerVdom,
initMiniGames,
prompt,
type VNode,
dataIcon,
bind,
onInsert,
type LooseVNodes,
hl,
} from 'lib/view';
import { numberRow } from 'lib/view/util';
import type SwissCtrl from '../ctrl';
import { players, renderPager } from '../pagination';
+1 -2
View File
@@ -1,7 +1,6 @@
import type TournamentController from '../ctrl';
import { bind, type MaybeVNode } from 'lib/view';
import { bind, type MaybeVNode, snabDialog } from 'lib/view';
import { fullName, userFlair } from 'lib/view/userLink';
import { snabDialog } from 'lib/view';
import { h, type VNode } from 'snabbdom';
import type { TeamBattle, RankedTeam, LightTeam } from '../interfaces';
+1 -2
View File
@@ -1,6 +1,5 @@
import * as licon from 'lib/licon';
import { spinnerVdom as spinner } from 'lib/view';
import { type VNode, bind, dataIcon, hl } from 'lib/view';
import { spinnerVdom as spinner, type VNode, bind, dataIcon, hl } from 'lib/view';
import { player as renderPlayer } from './util';
import { fullName } from 'lib/view/userLink';
import { numberRow } from 'lib/view/util';
+1 -2
View File
@@ -1,11 +1,10 @@
import { opposite } from '@lichess-org/chessground/util';
import * as licon from 'lib/licon';
import { type VNode, bind, onInsert, hl } from 'lib/view';
import { type VNode, bind, onInsert, hl, initMiniGames } from 'lib/view';
import { player as renderPlayer } from './util';
import type { Duel, DuelPlayer, FeaturedGame, TournamentOpts } from '../interfaces';
import { teamName } from './battle';
import type TournamentController from '../ctrl';
import { initMiniGames } from 'lib/view';
function featuredPlayer(game: FeaturedGame, color: Color, opts: TournamentOpts) {
const player = game[color];
+2 -1
View File
@@ -9,7 +9,6 @@ import type { VoiceMove, VoiceCtrl, Entry, Match } from '../voice';
import { coloredArrows, numberedArrows, brushes } from './arrows';
import { settingNodes } from './view';
import type { MsgType } from '../interfaces';
import type { Transform, SparseMap } from '../util';
import {
spread,
spreadMap,
@@ -22,6 +21,8 @@ import {
src,
dest,
promo,
type Transform,
type SparseMap,
} from '../util';
export function initModule({