This commit is contained in:
silverwind 2025-12-14 12:04:01 +01:00 committed by GitHub
commit f5d65e0edb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 399 additions and 391 deletions

View File

@ -339,12 +339,12 @@ lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backen
.PHONY: lint-js .PHONY: lint-js
lint-js: node_modules ## lint js files lint-js: node_modules ## lint js files
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) $(NODE_VARS) pnpm exec eslint --color --max-warnings=0 $(ESLINT_FILES)
$(NODE_VARS) pnpm exec vue-tsc $(NODE_VARS) pnpm exec vue-tsc
.PHONY: lint-js-fix .PHONY: lint-js-fix
lint-js-fix: node_modules ## lint js files and fix issues lint-js-fix: node_modules ## lint js files and fix issues
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) --fix $(NODE_VARS) pnpm exec eslint --color --max-warnings=0 $(ESLINT_FILES) --fix
$(NODE_VARS) pnpm exec vue-tsc $(NODE_VARS) pnpm exec vue-tsc
.PHONY: lint-css .PHONY: lint-css

View File

@ -147,7 +147,7 @@ export default defineConfig([
'@stylistic/wrap-regex': [0], '@stylistic/wrap-regex': [0],
'@stylistic/yield-star-spacing': [2, 'after'], '@stylistic/yield-star-spacing': [2, 'after'],
'@typescript-eslint/adjacent-overload-signatures': [0], '@typescript-eslint/adjacent-overload-signatures': [0],
'@typescript-eslint/array-type': [0], '@typescript-eslint/array-type': [2, {default: 'generic'}],
'@typescript-eslint/await-thenable': [2], '@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': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}],
'@typescript-eslint/ban-tslint-comment': [0], '@typescript-eslint/ban-tslint-comment': [0],
@ -215,7 +215,7 @@ export default defineConfig([
'@typescript-eslint/no-unnecessary-condition': [0], '@typescript-eslint/no-unnecessary-condition': [0],
'@typescript-eslint/no-unnecessary-qualifier': [0], '@typescript-eslint/no-unnecessary-qualifier': [0],
'@typescript-eslint/no-unnecessary-template-expression': [0], '@typescript-eslint/no-unnecessary-template-expression': [0],
'@typescript-eslint/no-unnecessary-type-arguments': [0], '@typescript-eslint/no-unnecessary-type-arguments': [2],
'@typescript-eslint/no-unnecessary-type-assertion': [2], '@typescript-eslint/no-unnecessary-type-assertion': [2],
'@typescript-eslint/no-unnecessary-type-constraint': [2], '@typescript-eslint/no-unnecessary-type-constraint': [2],
'@typescript-eslint/no-unnecessary-type-conversion': [2], '@typescript-eslint/no-unnecessary-type-conversion': [2],
@ -228,7 +228,7 @@ export default defineConfig([
'@typescript-eslint/no-unsafe-member-access': [0], '@typescript-eslint/no-unsafe-member-access': [0],
'@typescript-eslint/no-unsafe-return': [0], '@typescript-eslint/no-unsafe-return': [0],
'@typescript-eslint/no-unsafe-unary-minus': [2], '@typescript-eslint/no-unsafe-unary-minus': [2],
'@typescript-eslint/no-unused-expressions': [0], '@typescript-eslint/no-unused-expressions': [2],
'@typescript-eslint/no-unused-private-class-members': [2], '@typescript-eslint/no-unused-private-class-members': [2],
'@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'all', caughtErrors: 'all', ignoreRestSiblings: false, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_'}], '@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'all', caughtErrors: 'all', ignoreRestSiblings: false, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_'}],
'@typescript-eslint/no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}], '@typescript-eslint/no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}],
@ -584,7 +584,7 @@ export default defineConfig([
'no-unreachable': [2], 'no-unreachable': [2],
'no-unsafe-finally': [2], 'no-unsafe-finally': [2],
'no-unsafe-negation': [2], 'no-unsafe-negation': [2],
'no-unused-expressions': [2], 'no-unused-expressions': [0], // handled by @typescript-eslint/no-unused-expressions
'no-unused-labels': [2], 'no-unused-labels': [2],
'no-unused-private-class-members': [0], // handled by @typescript-eslint/no-unused-private-class-members 'no-unused-private-class-members': [0], // handled by @typescript-eslint/no-unused-private-class-members
'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars 'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars

View File

@ -1,6 +1,6 @@
{ {
"type": "module", "type": "module",
"packageManager": "pnpm@10.24.0", "packageManager": "pnpm@10.25.0",
"engines": { "engines": {
"node": ">= 22.6.0", "node": ">= 22.6.0",
"pnpm": ">= 10.0.0" "pnpm": ">= 10.0.0"
@ -12,7 +12,7 @@
"@citation-js/plugin-software-formats": "0.6.1", "@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3", "@github/markdown-toolbar-element": "2.2.3",
"@github/paste-markdown": "1.5.3", "@github/paste-markdown": "1.5.3",
"@github/relative-time-element": "4.5.1", "@github/relative-time-element": "5.0.0",
"@github/text-expander-element": "2.9.2", "@github/text-expander-element": "2.9.2",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.21.1", "@primer/octicons": "19.21.1",
@ -21,7 +21,7 @@
"@techknowlogick/license-checker-webpack-plugin": "0.3.0", "@techknowlogick/license-checker-webpack-plugin": "0.3.0",
"add-asset-webpack-plugin": "3.1.1", "add-asset-webpack-plugin": "3.1.1",
"ansi_up": "6.0.6", "ansi_up": "6.0.6",
"asciinema-player": "3.12.1", "asciinema-player": "3.13.2",
"chart.js": "4.5.1", "chart.js": "4.5.1",
"chartjs-adapter-dayjs-4": "1.0.4", "chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0", "chartjs-plugin-zoom": "2.2.0",
@ -35,25 +35,24 @@
"htmx.org": "2.0.8", "htmx.org": "2.0.8",
"idiomorph": "0.7.4", "idiomorph": "0.7.4",
"jquery": "3.7.1", "jquery": "3.7.1",
"katex": "0.16.25", "katex": "0.16.27",
"mermaid": "11.12.2", "mermaid": "11.12.2",
"mini-css-extract-plugin": "2.9.4", "mini-css-extract-plugin": "2.9.4",
"monaco-editor": "0.55.1", "monaco-editor": "0.55.1",
"monaco-editor-webpack-plugin": "7.1.1", "monaco-editor-webpack-plugin": "7.1.1",
"online-3d-viewer": "0.16.0", "online-3d-viewer": "0.17.0",
"pdfobject": "2.3.1", "pdfobject": "2.3.1",
"perfect-debounce": "2.0.0", "perfect-debounce": "2.0.0",
"postcss": "8.5.6", "postcss": "8.5.6",
"postcss-loader": "8.2.0", "postcss-loader": "8.2.0",
"sortablejs": "1.15.6", "sortablejs": "1.15.6",
"swagger-ui-dist": "5.30.3", "swagger-ui-dist": "5.31.0",
"tailwindcss": "3.4.17", "tailwindcss": "3.4.17",
"throttle-debounce": "5.0.2", "throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tippy.js": "6.3.7", "tippy.js": "6.3.7",
"toastify-js": "1.12.0", "toastify-js": "1.12.0",
"tributejs": "5.1.3", "tributejs": "5.1.3",
"typescript": "5.9.3",
"uint8-to-base64": "0.2.1", "uint8-to-base64": "0.2.1",
"vanilla-colorful": "0.7.2", "vanilla-colorful": "0.7.2",
"vue": "3.5.25", "vue": "3.5.25",
@ -79,10 +78,10 @@
"@types/throttle-debounce": "5.0.2", "@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/toastify-js": "1.12.4", "@types/toastify-js": "1.12.4",
"@typescript-eslint/parser": "8.48.1", "@typescript-eslint/parser": "8.49.0",
"@vitejs/plugin-vue": "6.0.2", "@vitejs/plugin-vue": "6.0.3",
"@vitest/eslint-plugin": "1.5.1", "@vitest/eslint-plugin": "1.5.2",
"eslint": "9.39.1", "eslint": "9.39.2",
"eslint-import-resolver-typescript": "4.4.4", "eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-array-func": "5.1.0", "eslint-plugin-array-func": "5.1.0",
"eslint-plugin-github": "6.0.0", "eslint-plugin-github": "6.0.0",
@ -96,7 +95,8 @@
"eslint-plugin-wc": "3.0.2", "eslint-plugin-wc": "3.0.2",
"globals": "16.5.0", "globals": "16.5.0",
"happy-dom": "20.0.11", "happy-dom": "20.0.11",
"markdownlint-cli": "0.46.0", "jiti": "2.6.1",
"markdownlint-cli": "0.47.0",
"material-icon-theme": "5.29.0", "material-icon-theme": "5.29.0",
"nolyfill": "1.0.44", "nolyfill": "1.0.44",
"postcss-html": "1.8.0", "postcss-html": "1.8.0",
@ -107,11 +107,12 @@
"stylelint-declaration-strict-value": "1.10.11", "stylelint-declaration-strict-value": "1.10.11",
"stylelint-value-no-unknown-custom-properties": "6.0.1", "stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "4.0.0", "svgo": "4.0.0",
"typescript-eslint": "8.48.1", "typescript": "5.9.3",
"updates": "17.0.4", "typescript-eslint": "8.49.0",
"updates": "17.0.7",
"vite-string-plugin": "1.4.9", "vite-string-plugin": "1.4.9",
"vitest": "4.0.15", "vitest": "4.0.15",
"vue-tsc": "3.1.5" "vue-tsc": "3.1.8"
}, },
"browserslist": [ "browserslist": [
"defaults" "defaults"

File diff suppressed because it is too large Load Diff

View File

@ -75,7 +75,7 @@ function getLanguage(filename: string): string {
return languagesByFilename[filename] || languagesByExt[extname(filename)] || 'plaintext'; return languagesByFilename[filename] || languagesByExt[extname(filename)] || 'plaintext';
} }
function updateEditor(monaco: Monaco, editor: IStandaloneCodeEditor, filename: string, lineWrapExts: string[]): void { function updateEditor(monaco: Monaco, editor: IStandaloneCodeEditor, filename: string, lineWrapExts: Array<string>): void {
editor.updateOptions(getFileBasedOptions(filename, lineWrapExts)); editor.updateOptions(getFileBasedOptions(filename, lineWrapExts));
const model = editor.getModel(); const model = editor.getModel();
if (!model) return; if (!model) return;
@ -175,7 +175,7 @@ export async function createMonaco(textarea: HTMLTextAreaElement, filename: stri
return {monaco, editor}; return {monaco, editor};
} }
function getFileBasedOptions(filename: string, lineWrapExts: string[]): MonacoOpts { function getFileBasedOptions(filename: string, lineWrapExts: Array<string>): MonacoOpts {
return { return {
wordWrap: (lineWrapExts || []).includes(extname(filename)) ? 'on' : 'off', wordWrap: (lineWrapExts || []).includes(extname(filename)) ? 'on' : 'off',
}; };

View File

@ -78,7 +78,7 @@ type MarkdownHandleIndentionResult = {
}; };
type TextLinesBuffer = { type TextLinesBuffer = {
lines: string[]; lines: Array<string>;
lengthBeforePosLine: number; lengthBeforePosLine: number;
posLineIndex: number; posLineIndex: number;
inlinePos: number inlinePos: number

View File

@ -6,7 +6,7 @@ import {createElementFromHTML, showElem, toggleElemClass} from '../utils/dom.ts'
import {html} from '../utils/html.ts'; import {html} from '../utils/html.ts';
import {basename} from '../utils.ts'; import {basename} from '../utils.ts';
const plugins: FileRenderPlugin[] = []; const plugins: Array<FileRenderPlugin> = [];
function initPluginsOnce(): void { function initPluginsOnce(): void {
if (plugins.length) return; if (plugins.length) return;

View File

@ -57,7 +57,7 @@ function initRepoIssueListCheckboxes() {
const url = el.getAttribute('data-url')!; const url = el.getAttribute('data-url')!;
let action = el.getAttribute('data-action')!; let action = el.getAttribute('data-action')!;
let elementId = el.getAttribute('data-element-id')!; let elementId = el.getAttribute('data-element-id')!;
const issueIDList: string[] = []; const issueIDList: Array<string> = [];
for (const el of document.querySelectorAll('.issue-checkbox:checked')) { for (const el of document.querySelectorAll('.issue-checkbox:checked')) {
issueIDList.push(el.getAttribute('data-issue-id')!); issueIDList.push(el.getAttribute('data-issue-id')!);
} }
@ -116,7 +116,7 @@ function initDropdownUserRemoteSearch(el: Element) {
}; };
type ProcessedResult = {value: string, name: string}; type ProcessedResult = {value: string, name: string};
const processedResults: ProcessedResult[] = []; // to be used by dropdown to generate menu items const processedResults: Array<ProcessedResult> = []; // to be used by dropdown to generate menu items
const syncItemFromInput = () => { const syncItemFromInput = () => {
const inputVal = elSearchInput.value.trim(); const inputVal = elSearchInput.value.trim();
elItemFromInput.setAttribute('data-value', inputVal); elItemFromInput.setAttribute('data-value', inputVal);

View File

@ -29,7 +29,7 @@ export class IssueSidebarComboList {
elDropdown: HTMLElement; elDropdown: HTMLElement;
elList: HTMLElement; elList: HTMLElement;
elComboValue: HTMLInputElement; elComboValue: HTMLInputElement;
initialValues: string[]; initialValues: Array<string>;
container: HTMLElement; container: HTMLElement;
constructor(container: HTMLElement) { constructor(container: HTMLElement) {

View File

@ -67,7 +67,7 @@ function initRepoIssueLabelFilter(elDropdown: HTMLElement) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const labelId = item.getAttribute('data-label-id')!; const labelId = item.getAttribute('data-label-id')!;
let labelIds: string[] = queryLabels ? queryLabels.split(',') : []; let labelIds: Array<string> = queryLabels ? queryLabels.split(',') : [];
labelIds = labelIds.filter((id) => Math.abs(parseInt(id)) !== Math.abs(parseInt(labelId))); labelIds = labelIds.filter((id) => Math.abs(parseInt(id)) !== Math.abs(parseInt(labelId)));
labelIds.push(`-${labelId}`); labelIds.push(`-${labelId}`);
url.searchParams.set('labels', labelIds.join(',')); url.searchParams.set('labels', labelIds.join(','));

View File

@ -60,7 +60,7 @@ interface Window {
_inited: boolean, _inited: boolean,
push: (e: ErrorEvent & PromiseRejectionEvent) => void | number, push: (e: ErrorEvent & PromiseRejectionEvent) => void | number,
}, },
codeEditors: any[], // export editor for customization codeEditors: Array<any>, // export editor for customization
// various captcha plugins // various captcha plugins
grecaptcha: any, grecaptcha: any,

View File

@ -12,7 +12,7 @@ export type DiffTreeEntry = {
DiffStatus: DiffStatus, DiffStatus: DiffStatus,
EntryMode: string, EntryMode: string,
IsViewed: boolean, IsViewed: boolean,
Children: DiffTreeEntry[] | null, Children: Array<DiffTreeEntry> | null,
FileIcon: string, FileIcon: string,
ParentEntry?: DiffTreeEntry, ParentEntry?: DiffTreeEntry,
}; };

View File

@ -291,8 +291,8 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT
// * z-label // * z-label
// when the "scope/*" are filtered out, we'd like to see "a-label" and "z-label" without the divider. // when the "scope/*" are filtered out, we'd like to see "a-label" and "z-label" without the divider.
export function hideScopedEmptyDividers(container: Element) { export function hideScopedEmptyDividers(container: Element) {
const visibleItems: Element[] = []; const visibleItems: Array<Element> = [];
const curScopeVisibleItems: Element[] = []; const curScopeVisibleItems: Array<Element> = [];
let curScope: string = '', lastVisibleScope: string = ''; let curScope: string = '', lastVisibleScope: string = '';
const isDivider = (item: Element) => item.classList.contains('divider'); const isDivider = (item: Element) => item.classList.contains('divider');
const isScopedDivider = (item: Element) => isDivider(item) && item.hasAttribute('data-scope'); const isScopedDivider = (item: Element) => isDivider(item) && item.hasAttribute('data-scope');

View File

@ -1,5 +1,5 @@
export class InitPerformanceTracer { export class InitPerformanceTracer {
results: {name: string, dur: number}[] = []; results: Array<{name: string, dur: number}> = [];
recordCall(name: string, func: () => void) { recordCall(name: string, func: () => void) {
const start = performance.now(); const start = performance.now();
func(); func();
@ -13,7 +13,7 @@ export class InitPerformanceTracer {
} }
} }
export function callInitFunctions(functions: (() => any)[]): InitPerformanceTracer | null { export function callInitFunctions(functions: Array<() => any>): InitPerformanceTracer | null {
// Start performance trace by accessing a URL by "https://localhost/?_ui_performance_trace=1" or "https://localhost/?key=value&_ui_performance_trace=1" // Start performance trace by accessing a URL by "https://localhost/?_ui_performance_trace=1" or "https://localhost/?key=value&_ui_performance_trace=1"
// It is a quick check, no side effect so no need to do slow URL parsing. // It is a quick check, no side effect so no need to do slow URL parsing.
const perfTracer = !window.location.search.includes('_ui_performance_trace=1') ? null : new InitPerformanceTracer(); const perfTracer = !window.location.search.includes('_ui_performance_trace=1') ? null : new InitPerformanceTracer();

View File

@ -5,7 +5,7 @@ import type {InitPerformanceTracer} from './init.ts';
let globalSelectorObserverInited = false; let globalSelectorObserverInited = false;
type SelectorHandler = {selector: string, handler: (el: HTMLElement) => void}; type SelectorHandler = {selector: string, handler: (el: HTMLElement) => void};
const selectorHandlers: SelectorHandler[] = []; const selectorHandlers: Array<SelectorHandler> = [];
type GlobalEventFunc<T extends HTMLElement, E extends Event> = (el: T, e: E) => Promisable<void>; type GlobalEventFunc<T extends HTMLElement, E extends Event> = (el: T, e: E) => Promisable<void>;
const globalEventFuncs: Record<string, GlobalEventFunc<HTMLElement, Event>> = {}; const globalEventFuncs: Record<string, GlobalEventFunc<HTMLElement, Event>> = {};

View File

@ -170,7 +170,7 @@ export type SvgName = keyof typeof svgs;
// most of the SVG icons in assets couldn't be used directly. // most of the SVG icons in assets couldn't be used directly.
// retrieve an HTML string for given SVG icon name, size and additional classes // retrieve an HTML string for given SVG icon name, size and additional classes
export function svg(name: SvgName, size = 16, classNames?: string | string[]): string { export function svg(name: SvgName, size = 16, classNames?: string | Array<string>): string {
const className = Array.isArray(classNames) ? classNames.join(' ') : classNames; const className = Array.isArray(classNames) ? classNames.join(' ') : classNames;
if (!(name in svgs)) throw new Error(`Unknown SVG icon: ${name}`); if (!(name in svgs)) throw new Error(`Unknown SVG icon: ${name}`);
if (size === 16 && !className) return svgs[name]; if (size === 16 && !className) return svgs[name];

View File

@ -17,7 +17,7 @@ export type Config = {
pageData: Record<string, any>, pageData: Record<string, any>,
notificationSettings: Record<string, any>, notificationSettings: Record<string, any>,
enableTimeTracking: boolean, enableTimeTracking: boolean,
mentionValues?: MentionValue[], mentionValues?: Array<MentionValue>,
mermaidMaxSourceCharacters: number, mermaidMaxSourceCharacters: number,
i18n: Record<string, string>, i18n: Record<string, string>,
}; };
@ -70,7 +70,7 @@ export type Issue = {
export type FomanticInitFunction = { export type FomanticInitFunction = {
settings?: Record<string, any>, settings?: Record<string, any>,
(...args: any[]): any, (...args: Array<any>): any,
}; };
export type GitRefType = 'branch' | 'tag'; export type GitRefType = 'branch' | 'tag';

View File

@ -6,9 +6,9 @@ import {isInFrontendUnitTest} from './testhelper.ts';
type ArrayLikeIterable<T> = ArrayLike<T> & Iterable<T>; // for NodeListOf and Array type ArrayLikeIterable<T> = ArrayLike<T> & Iterable<T>; // for NodeListOf and Array
type ElementArg = Element | string | ArrayLikeIterable<Element> | ReturnType<typeof $>; type ElementArg = Element | string | ArrayLikeIterable<Element> | ReturnType<typeof $>;
type ElementsCallback<T extends Element> = (el: T) => Promisable<any>; type ElementsCallback<T extends Element> = (el: T) => Promisable<any>;
type ElementsCallbackWithArgs = (el: Element, ...args: any[]) => Promisable<any>; type ElementsCallbackWithArgs = (el: Element, ...args: Array<any>) => Promisable<any>;
function elementsCall(el: ElementArg, func: ElementsCallbackWithArgs, ...args: any[]): ArrayLikeIterable<Element> { function elementsCall(el: ElementArg, func: ElementsCallbackWithArgs, ...args: Array<any>): ArrayLikeIterable<Element> {
if (typeof el === 'string' || el instanceof String) { if (typeof el === 'string' || el instanceof String) {
el = document.querySelectorAll(el as string); el = document.querySelectorAll(el as string);
} }
@ -65,7 +65,7 @@ function applyElemsCallback<T extends Element>(elems: ArrayLikeIterable<T>, fn?:
export function queryElemSiblings<T extends Element>(el: Element, selector = '*', fn?: ElementsCallback<T>): ArrayLikeIterable<T> { export function queryElemSiblings<T extends Element>(el: Element, selector = '*', fn?: ElementsCallback<T>): ArrayLikeIterable<T> {
if (!el.parentNode) return []; if (!el.parentNode) return [];
const elems = Array.from(el.parentNode.children) as T[]; const elems = Array.from(el.parentNode.children) as Array<T>;
return applyElemsCallback<T>(elems.filter((child: Element) => { return applyElemsCallback<T>(elems.filter((child: Element) => {
return child !== el && child.matches(selector); return child !== el && child.matches(selector);
}), fn); }), fn);
@ -301,7 +301,7 @@ export function createElementFromHTML<T extends HTMLElement>(htmlString: string)
return div.firstChild as T; return div.firstChild as T;
} }
export function createElementFromAttrs(tagName: string, attrs: Record<string, any> | null, ...children: (Node | string)[]): HTMLElement { export function createElementFromAttrs(tagName: string, attrs: Record<string, any> | null, ...children: Array<Node | string>): HTMLElement {
const el = document.createElement(tagName); const el = document.createElement(tagName);
for (const [key, value] of Object.entries(attrs || {})) { for (const [key, value] of Object.entries(attrs || {})) {
if (value === undefined || value === null) continue; if (value === undefined || value === null) continue;

View File

@ -2,7 +2,7 @@ import {readFile} from 'node:fs/promises';
import * as path from 'node:path'; import * as path from 'node:path';
import {globCompile} from './glob.ts'; import {globCompile} from './glob.ts';
async function loadGlobTestData(): Promise<{caseNames: string[], caseDataMap: Record<string, string>}> { async function loadGlobTestData(): Promise<{caseNames: Array<string>, caseDataMap: Record<string, string>}> {
const fileContent = await readFile(path.join(import.meta.dirname, 'glob.test.txt'), 'utf8'); const fileContent = await readFile(path.join(import.meta.dirname, 'glob.test.txt'), 'utf8');
const fileLines = fileContent.split('\n'); const fileLines = fileContent.split('\n');
const caseDataMap: Record<string, string> = {}; const caseDataMap: Record<string, string> = {};

View File

@ -3,9 +3,9 @@ type PngChunk = {
data: Uint8Array, data: Uint8Array,
}; };
export async function pngChunks(blob: Blob): Promise<PngChunk[]> { export async function pngChunks(blob: Blob): Promise<Array<PngChunk>> {
const uint8arr = new Uint8Array(await blob.arrayBuffer()); const uint8arr = new Uint8Array(await blob.arrayBuffer());
const chunks: PngChunk[] = []; const chunks: Array<PngChunk> = [];
if (uint8arr.length < 12) return chunks; if (uint8arr.length < 12) return chunks;
const view = new DataView(uint8arr.buffer); const view = new DataView(uint8arr.buffer);
if (view.getBigUint64(0) !== 9894494448401390090n) return chunks; if (view.getBigUint64(0) !== 9894494448401390090n) return chunks;

View File

@ -4,12 +4,12 @@ import type {Issue} from '../types.ts';
const maxMatches = 6; const maxMatches = 6;
function sortAndReduce<T>(map: Map<T, number>): T[] { function sortAndReduce<T>(map: Map<T, number>): Array<T> {
const sortedMap = new Map(Array.from(map.entries()).sort((a, b) => a[1] - b[1])); const sortedMap = new Map(Array.from(map.entries()).sort((a, b) => a[1] - b[1]));
return Array.from(sortedMap.keys()).slice(0, maxMatches); return Array.from(sortedMap.keys()).slice(0, maxMatches);
} }
export function matchEmoji(queryText: string): string[] { export function matchEmoji(queryText: string): Array<string> {
const query = queryText.toLowerCase().replaceAll('_', ' '); const query = queryText.toLowerCase().replaceAll('_', ' ');
if (!query) return emojis.slice(0, maxMatches).map((e) => e.aliases[0]); if (!query) return emojis.slice(0, maxMatches).map((e) => e.aliases[0]);
@ -30,7 +30,7 @@ export function matchEmoji(queryText: string): string[] {
} }
type MentionSuggestion = {value: string; name: string; fullname: string; avatar: string}; type MentionSuggestion = {value: string; name: string; fullname: string; avatar: string};
export function matchMention(queryText: string): MentionSuggestion[] { export function matchMention(queryText: string): Array<MentionSuggestion> {
const query = queryText.toLowerCase(); const query = queryText.toLowerCase();
// results is a map of weights, lower is better // results is a map of weights, lower is better
@ -45,10 +45,10 @@ export function matchMention(queryText: string): MentionSuggestion[] {
return sortAndReduce(results); return sortAndReduce(results);
} }
export async function matchIssue(owner: string, repo: string, issueIndexStr: string, query: string): Promise<Issue[]> { export async function matchIssue(owner: string, repo: string, issueIndexStr: string, query: string): Promise<Array<Issue>> {
const res = await GET(`${window.config.appSubUrl}/${owner}/${repo}/issues/suggestions?q=${encodeURIComponent(query)}`); const res = await GET(`${window.config.appSubUrl}/${owner}/${repo}/issues/suggestions?q=${encodeURIComponent(query)}`);
const issues: Issue[] = await res.json(); const issues: Array<Issue> = await res.json();
const issueNumber = parseInt(issueIndexStr); const issueNumber = parseInt(issueIndexStr);
// filter out issue with same id // filter out issue with same id

View File

@ -11,7 +11,7 @@ dayjs.extend(utc);
* @param startDate The start date. Can take any type that dayjs accepts. * @param startDate The start date. Can take any type that dayjs accepts.
* @param endDate The end date. Can take any type that dayjs accepts. * @param endDate The end date. Can take any type that dayjs accepts.
*/ */
export function startDaysBetween(startDate: ConfigType, endDate: ConfigType): number[] { export function startDaysBetween(startDate: ConfigType, endDate: ConfigType): Array<number> {
const start = dayjs.utc(startDate); const start = dayjs.utc(startDate);
const end = dayjs.utc(endDate); const end = dayjs.utc(endDate);
@ -22,7 +22,7 @@ export function startDaysBetween(startDate: ConfigType, endDate: ConfigType): nu
current = current.add(1, 'day'); current = current.add(1, 'day');
} }
const startDays: number[] = []; const startDays: Array<number> = [];
while (current.isBefore(end)) { while (current.isBefore(end)) {
startDays.push(current.valueOf()); startDays.push(current.valueOf());
current = current.add(1, 'week'); current = current.add(1, 'week');
@ -53,7 +53,7 @@ export type DayDataObject = {
[timestamp: string]: DayData, [timestamp: string]: DayData,
}; };
export function fillEmptyStartDaysWithZeroes(startDays: number[], data: DayDataObject): DayData[] { export function fillEmptyStartDaysWithZeroes(startDays: Array<number>, data: DayDataObject): Array<DayData> {
const result: Record<string, any> = {}; const result: Record<string, any> = {};
for (const startDay of startDays) { for (const startDay of startDays) {

View File

@ -5,7 +5,7 @@ try {
} catch { } catch {
const intlNumberFormat = Intl.NumberFormat; const intlNumberFormat = Intl.NumberFormat;
// @ts-expect-error - polyfill is incomplete // @ts-expect-error - polyfill is incomplete
Intl.NumberFormat = function(locales: string | string[], options: Intl.NumberFormatOptions) { Intl.NumberFormat = function(locales: string | Array<string>, options: Intl.NumberFormatOptions) {
if (options.style === 'unit') { if (options.style === 'unit') {
return { return {
format(value: number | bigint | string) { format(value: number | bigint | string) {