Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8848b84
Initial plan
Copilot Feb 1, 2026
3e8eb05
Remove all @ts-expect-error comments and move module stubs to types.d.ts
Copilot Feb 1, 2026
6c8477f
Address review feedback: revert runtime changes, keep type-only changes
Copilot Feb 1, 2026
0b7b818
Simplify bootstrap.ts error handler loop back to original form
Copilot Feb 1, 2026
a6cc467
fixes
silverwind Feb 1, 2026
9faafaf
remove comment
silverwind Feb 1, 2026
981d557
spacing
silverwind Feb 1, 2026
dc74909
Update repo-diff.ts
wxiaoguang Feb 1, 2026
2173abf
forbid `@ts-expect-error`
silverwind Feb 1, 2026
b217db4
fix type of self
silverwind Feb 1, 2026
cf49595
fix tribute types
silverwind Feb 1, 2026
b133c4a
add StepContainerElement type
silverwind Feb 1, 2026
8a6affa
fix form issue
silverwind Feb 1, 2026
e188005
fix sortable type
silverwind Feb 1, 2026
d41eb5e
revert lookup
silverwind Feb 1, 2026
52835c5
use HTMLElement
silverwind Feb 1, 2026
46acce5
re-add link
silverwind Feb 1, 2026
5458eb9
comment
silverwind Feb 1, 2026
ab4935f
fix type
silverwind Feb 1, 2026
cc7270d
re-add comment
silverwind Feb 1, 2026
dd2775e
simplify vue-bar-graph types
silverwind Feb 1, 2026
7fc018b
remove unused MessageEvent
silverwind Feb 1, 2026
c6ea491
improve tribute types
silverwind Feb 1, 2026
b9dafdf
Update web_src/js/components/RepoBranchTagSelector.vue
wxiaoguang Feb 1, 2026
eab7a6c
Update web_src/js/features/eventsource.sharedworker.ts
wxiaoguang Feb 1, 2026
4e2d1fa
Merge branch 'main' into copilot/remove-ts-expect-error
wxiaoguang Feb 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import vue from 'eslint-plugin-vue';
import vueScopedCss from 'eslint-plugin-vue-scoped-css';
import wc from 'eslint-plugin-wc';
import {defineConfig, globalIgnores} from 'eslint/config';
import type {ESLint} from 'eslint';

const jsExts = ['js', 'mjs', 'cjs'] as const;
const tsExts = ['ts', 'mts', 'cts'] as const;
Expand Down Expand Up @@ -62,8 +63,7 @@ export default defineConfig([
'@stylistic': stylistic,
'@typescript-eslint': typescriptPlugin.plugin,
'array-func': arrayFunc,
// @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/203
'import-x': importPlugin,
'import-x': importPlugin as unknown as ESLint.Plugin, // https://github.com/un-ts/eslint-plugin-import-x/issues/203
regexp,
sonarjs,
unicorn,
Expand Down Expand Up @@ -156,7 +156,7 @@ export default defineConfig([
'@typescript-eslint/adjacent-overload-signatures': [0],
'@typescript-eslint/array-type': [0],
'@typescript-eslint/await-thenable': [2],
'@typescript-eslint/ban-ts-comment': [2, {'ts-expect-error': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}],
'@typescript-eslint/ban-ts-comment': [2, {'ts-expect-error': true, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}],
'@typescript-eslint/ban-tslint-comment': [0],
'@typescript-eslint/class-literal-property-style': [0],
'@typescript-eslint/class-methods-use-this': [0],
Expand Down Expand Up @@ -924,8 +924,7 @@ export default defineConfig([
},
extends: [
vue.configs['flat/recommended'],
// @ts-expect-error
vueScopedCss.configs['flat/recommended'],
vueScopedCss.configs['flat/recommended'] as any,
],
rules: {
'vue/attributes-order': [0],
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"target": "es2020",
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["dom", "dom.iterable", "dom.asynciterable", "esnext"],
"lib": ["dom", "dom.iterable", "dom.asynciterable", "esnext", "webworker"],
"allowImportingTsExtensions": true,
"allowJs": true,
"allowSyntheticDefaultImports": true,
Expand Down
87 changes: 87 additions & 0 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,105 @@ declare module '@techknowlogick/license-checker-webpack-plugin' {
const plugin: any;
export = plugin;
}

declare module 'eslint-plugin-no-use-extend-native' {
import type {Eslint} from 'eslint';
const plugin: Eslint.Plugin;
export = plugin;
}

declare module 'eslint-plugin-array-func' {
import type {Eslint} from 'eslint';
const plugin: Eslint.Plugin;
export = plugin;
}

declare module 'eslint-plugin-github' {
import type {Eslint} from 'eslint';
const plugin: Eslint.Plugin;
export = plugin;
}

declare module '*.svg' {
const value: string;
export default value;
}

declare module '*.css' {
const value: string;
export default value;
}

declare module '*.vue' {
import type {DefineComponent} from 'vue';
const component: DefineComponent<unknown, unknown, any>;
export default component;
// Here we declare all exports from vue files so `tsc` or `tsgo` can work for
// non-vue files. To lint .vue files, `vue-tsc` must be used.
export function initDashboardRepoList(): void;
export function initRepositoryActionView(): void;
}

declare module 'htmx.org/dist/htmx.esm.js' {
const value = await import('htmx.org');
export default value;
}

declare module 'swagger-ui-dist/swagger-ui-es-bundle.js' {
const value = await import('swagger-ui-dist');
export default value.SwaggerUIBundle;
}

declare module 'asciinema-player' {
interface AsciinemaPlayer {
create(src: string, element: HTMLElement, options?: Record<string, unknown>): void;
}
const exports: AsciinemaPlayer;
export = exports;
}

declare module '@citation-js/core' {
export class Cite {
constructor(data: string);
format(format: string, options?: Record<string, any>): string;
}
export const plugins: {
config: {
get(name: string): any;
};
};
}

declare module '@citation-js/plugin-software-formats' {}
declare module '@citation-js/plugin-bibtex' {}
declare module '@citation-js/plugin-csl' {}

declare module 'vue-bar-graph' {
import type {DefineComponent} from 'vue';

interface BarGraphPoint {
value: number;
label: string;
}

export const VueBarGraph: DefineComponent<{
points?: Array<BarGraphPoint>;
barColor?: string;
textColor?: string;
textAltColor?: string;
height?: number;
labelHeight?: number;
}>;
}

declare module '@mcaptcha/vanilla-glue' {
export let INPUT_NAME: string;
export default class Widget {
constructor(options: {
siteKey: {
instanceUrl: URL;
key: string;
};
});
}
}
5 changes: 2 additions & 3 deletions web_src/js/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,12 @@ function initGlobalErrorHandler() {
// we added an event handler for window error at the very beginning of <script> of page head the
// handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before
// this init then in this init, we can collect all error events and show them.
for (const e of window._globalHandlerErrors || []) {
for (const e of (window._globalHandlerErrors as Iterable<ErrorEvent & PromiseRejectionEvent>) || []) {
processWindowErrorEvent(e);
}
// then, change _globalHandlerErrors to an object with push method, to process further error
// events directly
// @ts-expect-error -- this should be refactored to not use a fake array
window._globalHandlerErrors = {_inited: true, push: (e: ErrorEvent & PromiseRejectionEvent) => processWindowErrorEvent(e)};
window._globalHandlerErrors = {_inited: true, push: (e: ErrorEvent & PromiseRejectionEvent) => processWindowErrorEvent(e)} as any;
}

initGlobalErrorHandler();
11 changes: 6 additions & 5 deletions web_src/js/components/RepoActionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {localUserSettings} from '../modules/user-settings.ts';
// see "models/actions/status.go", if it needs to be used somewhere else, move it to a shared file like "types/actions.ts"
type RunStatus = 'unknown' | 'waiting' | 'running' | 'success' | 'failure' | 'cancelled' | 'skipped' | 'blocked';

type StepContainerElement = HTMLElement & {_stepLogsActiveContainer?: HTMLElement}

type LogLine = {
index: number;
timestamp: number;
Expand Down Expand Up @@ -221,19 +223,18 @@ export default defineComponent({
},

// get the job step logs container ('.job-step-logs')
getJobStepLogsContainer(stepIndex: number): HTMLElement {
getJobStepLogsContainer(stepIndex: number): StepContainerElement {
return (this.$refs.logs as any)[stepIndex];
},

// get the active logs container element, either the `job-step-logs` or the `job-log-list` in the `job-log-group`
getActiveLogsContainer(stepIndex: number): HTMLElement {
getActiveLogsContainer(stepIndex: number): StepContainerElement {
const el = this.getJobStepLogsContainer(stepIndex);
// @ts-expect-error - _stepLogsActiveContainer is a custom property
return el._stepLogsActiveContainer ?? el;
},
// begin a log group
beginLogGroup(stepIndex: number, startTime: number, line: LogLine, cmd: LogLineCommand) {
const el = (this.$refs.logs as any)[stepIndex];
const el = (this.$refs.logs as any)[stepIndex] as StepContainerElement;
const elJobLogGroupSummary = createElementFromAttrs('summary', {class: 'job-log-group-summary'},
this.createLogLine(stepIndex, startTime, {
index: line.index,
Expand Down Expand Up @@ -395,7 +396,7 @@ export default defineComponent({
}

// auto-scroll to the last log line of the last step
let autoScrollJobStepElement: HTMLElement | undefined;
let autoScrollJobStepElement: StepContainerElement | undefined;
for (let stepIndex = 0; stepIndex < this.currentJob.steps.length; stepIndex++) {
if (!autoScrollStepIndexes.get(stepIndex)) continue;
autoScrollJobStepElement = this.getJobStepLogsContainer(stepIndex);
Expand Down
1 change: 0 additions & 1 deletion web_src/js/components/RepoActivityTopAuthors.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script lang="ts" setup>
// @ts-expect-error - module exports no types
import {VueBarGraph} from 'vue-bar-graph';
import {computed, onMounted, shallowRef, useTemplateRef, type ShallowRef} from 'vue';

Expand Down
7 changes: 3 additions & 4 deletions web_src/js/components/RepoBranchTagSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,8 @@ export default defineComponent({
return -1;
},
getActiveItem() {
const el = this.$refs[`listItem${this.activeItemIndex}`];
// @ts-expect-error - el is unknown type
return (el && el.length) ? el[0] : null;
const el = this.$refs[`listItem${this.activeItemIndex}`] as Array<HTMLDivElement>;
return el?.length ? el[0] : null;
},
keydown(e: KeyboardEvent) {
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
Expand All @@ -174,7 +173,7 @@ export default defineComponent({
return;
}
this.activeItemIndex = nextIndex;
this.getActiveItem().scrollIntoView({block: 'nearest'});
this.getActiveItem()!.scrollIntoView({block: 'nearest'});
} else if (e.key === 'Enter') {
e.preventDefault();
this.getActiveItem()?.click();
Expand Down
12 changes: 10 additions & 2 deletions web_src/js/components/RepoContributors.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ const customEventListener: Plugin = {
},
};

type LineOptions = ChartOptions<'line'> & {
plugins?: {
customEventListener?: {
chartType: string;
instance: unknown;
};
};
}

Chart.defaults.color = chartJsColors.text;
Chart.defaults.borderColor = chartJsColors.border;

Expand Down Expand Up @@ -251,7 +260,7 @@ export default defineComponent({
}
},

getOptions(type: string): ChartOptions<'line'> {
getOptions(type: string): LineOptions {
return {
responsive: true,
maintainAspectRatio: false,
Expand All @@ -264,7 +273,6 @@ export default defineComponent({
position: 'top',
align: 'center',
},
// @ts-expect-error: bug in chart.js types
customEventListener: {
chartType: type,
instance: this,
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/components/RepoRecentCommits.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
TimeScale,
type ChartOptions,
type ChartData,
type ChartDataset,
} from 'chart.js';
import {GET} from '../modules/fetch.ts';
import {Bar} from 'vue-chartjs';
Expand Down Expand Up @@ -83,13 +84,12 @@ function toGraphData(data: DayData[]): ChartData<'bar'> {
return {
datasets: [
{
// @ts-expect-error -- bar chart expects one-dimensional data, but apparently x/y still works
data: data.map((i) => ({x: i.week, y: i.commits})),
label: 'Commits',
backgroundColor: chartJsColors['commits'],
borderWidth: 0,
tension: 0.3,
},
} as unknown as ChartDataset<'bar'>,
],
};
}
Expand Down
1 change: 0 additions & 1 deletion web_src/js/features/captcha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export async function initCaptcha() {
// * the INPUT_NAME is a "const", it should not be changed.
// * the "mCaptcha.default" is actually the "Widget".

// @ts-expect-error TS2540: Cannot assign to 'INPUT_NAME' because it is a read-only property.
mCaptcha.INPUT_NAME = 'm-captcha-response';
const instanceURL = captchaEl.getAttribute('data-instance-url')!;

Expand Down
4 changes: 0 additions & 4 deletions web_src/js/features/citation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@ const {pageData} = window.config;

async function initInputCitationValue(citationCopyApa: HTMLButtonElement, citationCopyBibtex: HTMLButtonElement) {
const [{Cite, plugins}] = await Promise.all([
// @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-core" */'@citation-js/core'),
// @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-formats" */'@citation-js/plugin-software-formats'),
// @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-bibtex" */'@citation-js/plugin-bibtex'),
// @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-csl" */'@citation-js/plugin-csl'),
]);
const {citationFileContent} = pageData;
Expand Down
5 changes: 2 additions & 3 deletions web_src/js/features/eventsource.sharedworker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,9 @@ class Source {
const sourcesByUrl = new Map<string, Source | null>();
const sourcesByPort = new Map<MessagePort, Source | null>();

// @ts-expect-error: typescript bug?
self.addEventListener('connect', (e: MessageEvent) => {
(self as unknown as SharedWorkerGlobalScope).addEventListener('connect', (e: MessageEvent) => {
for (const port of e.ports) {
port.addEventListener('message', (event) => {
port.addEventListener('message', (event: MessageEvent) => {
if (!self.EventSource) {
// some browsers (like PaleMoon, Firefox<53) don't support EventSource in SharedWorkerGlobalScope.
// this event handler needs EventSource when doing "new Source(url)", so just post a message back to the caller,
Expand Down
3 changes: 1 addition & 2 deletions web_src/js/features/repo-diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ function initRepoDiffConversationForm() {
const idx = newConversationHolder.getAttribute('data-idx');

form.closest('.conversation-holder')!.replaceWith(newConversationHolder);
// @ts-expect-error -- prevent further usage of the form because it should have been replaced
form = null;
(form as any) = null; // prevent further usage of the form because it should have been replaced

if (trLineType) {
// if there is a line-type for the "tr", it means the form is on the diff page
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/features/repo-issue-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ async function pinMoveEnd(e: SortableEvent) {
}

async function initIssuePinSort() {
const pinDiv = document.querySelector('#issue-pins');
const pinDiv = document.querySelector<HTMLElement>('#issue-pins');

if (pinDiv === null) return;

Expand Down
4 changes: 2 additions & 2 deletions web_src/js/features/repo-projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ async function moveIssue({item, from, to, oldIndex}: SortableEvent): Promise<voi

async function initRepoProjectSortable(): Promise<void> {
// the HTML layout is: #project-board.board > .project-column .cards > .issue-card
const mainBoard = document.querySelector('#project-board')!;
const mainBoard = document.querySelector<HTMLElement>('#project-board')!;
let boardColumns = mainBoard.querySelectorAll<HTMLElement>('.project-column');
createSortable(mainBoard, {
group: 'project-column',
Expand Down Expand Up @@ -67,7 +67,7 @@ async function initRepoProjectSortable(): Promise<void> {
});

for (const boardColumn of boardColumns) {
const boardCardList = boardColumn.querySelector('.cards')!;
const boardCardList = boardColumn.querySelector<HTMLElement>('.cards')!;
createSortable(boardCardList, {
group: 'shared',
onAdd: moveIssue, // eslint-disable-line @typescript-eslint/no-misused-promises
Expand Down
5 changes: 2 additions & 3 deletions web_src/js/features/repo-settings-branches.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,11 @@ describe('Repository Branch Settings', () => {
vi.mocked(POST).mockResolvedValue({ok: true} as Response);

// Mock createSortable to capture and execute the onEnd callback
vi.mocked(createSortable).mockImplementation(async (_el: Element, options: SortableOptions | undefined) => {
vi.mocked(createSortable).mockImplementation(async (_el: HTMLElement, options: SortableOptions | undefined) => {
if (options?.onEnd) {
options.onEnd(new Event('SortableEvent') as SortableEvent);
}
// @ts-expect-error: mock is incomplete
return {destroy: vi.fn()} as Sortable;
return {destroy: vi.fn()} as unknown as Sortable;
});

initRepoSettingsBranchesDrag();
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/features/repo-settings-branches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {showErrorToast} from '../modules/toast.ts';
import {queryElemChildren} from '../utils/dom.ts';

export function initRepoSettingsBranchesDrag() {
const protectedBranchesList = document.querySelector('#protected-branches-list');
const protectedBranchesList = document.querySelector<HTMLElement>('#protected-branches-list');
if (!protectedBranchesList) return;

createSortable(protectedBranchesList, {
Expand Down
Loading