refactor: 优化eslint配置,提升代码质量 (#1341)

* feat: 统一并标准化eslint

* lint: napcat.webui

* lint: napcat.webui

* lint: napcat.core

* build: fix

* lint: napcat.webui

* refactor: 重构eslint

* Update README.md
This commit is contained in:
时瑾
2025-11-03 16:30:45 +08:00
committed by GitHub
parent c9b1d3fafa
commit 2185a884b4
651 changed files with 37981 additions and 37587 deletions

View File

@@ -1,11 +1,11 @@
import { PlayMode } from '@/const/enum'
import { PlayMode } from '@/const/enum';
import WebUIManager from '@/controllers/webui_manager'
import WebUIManager from '@/controllers/webui_manager';
import type {
FinalMusic,
Music163ListResponse,
Music163URLResponse
} from '@/types/music'
Music163URLResponse,
} from '@/types/music';
/**
* 获取网易云音乐歌单
@@ -13,17 +13,17 @@ import type {
* @returns 歌单信息
*/
export const get163MusicList = async (id: string) => {
let res = await WebUIManager.proxy<Music163ListResponse>(
const res = await WebUIManager.proxy<Music163ListResponse>(
'https://wavesgame.top/playlist/track/all?id=' + id
)
);
// const res = await request.get<Music163ListResponse>(
// `https://wavesgame.top/playlist/track/all?id=${id}`
// )
if (res?.data?.code !== 200) {
throw new Error('获取歌曲列表失败')
throw new Error('获取歌曲列表失败');
}
return res.data
}
return res.data;
};
/**
* 获取歌曲地址
@@ -32,29 +32,29 @@ export const get163MusicList = async (id: string) => {
*/
export const getSongsURL = async (ids: number[]) => {
const _ids = ids.reduce((prev, cur, index) => {
const groupIndex = Math.floor(index / 10)
const groupIndex = Math.floor(index / 10);
if (!prev[groupIndex]) {
prev[groupIndex] = []
prev[groupIndex] = [];
}
prev[groupIndex].push(cur)
return prev
}, [] as number[][])
prev[groupIndex].push(cur);
return prev;
}, [] as number[][]);
const res = await Promise.all(
_ids.map(async (id) => {
const res = await WebUIManager.proxy<Music163URLResponse>(
`https://wavesgame.top/song/url?id=${id.join(',')}`
)
);
if (res?.data?.code !== 200) {
throw new Error('获取歌曲地址失败')
throw new Error('获取歌曲地址失败');
}
return res.data.data
return res.data.data;
})
)
);
const result = res.reduce((prev, cur) => {
return prev.concat(...cur)
}, [])
return result
}
return prev.concat(...cur);
}, []);
return result;
};
/**
* 获取网易云音乐歌单歌曲
@@ -62,26 +62,26 @@ export const getSongsURL = async (ids: number[]) => {
* @returns 歌曲信息
*/
export const get163MusicListSongs = async (id: string) => {
const listRes = await get163MusicList(id)
const songs = listRes.songs.map((song) => song.id)
const songsRes = await getSongsURL(songs)
const finalMusic: FinalMusic[] = []
const listRes = await get163MusicList(id);
const songs = listRes.songs.map((song) => song.id);
const songsRes = await getSongsURL(songs);
const finalMusic: FinalMusic[] = [];
for (let i = 0; i < listRes.songs.length; i++) {
const song = listRes.songs[i]
const music = songsRes.find((s) => s.id === song.id)
const songURL = music?.url
const song = listRes.songs[i];
const music = songsRes.find((s) => s.id === song.id);
const songURL = music?.url;
if (songURL) {
finalMusic.push({
id: song.id,
url: songURL.replace(/http:\/\//, '//').replace(/https:\/\//, '//'),
title: song.name,
artist: song.ar.map((p) => p.name).join('/'),
cover: song.al.picUrl
})
cover: song.al.picUrl,
});
}
}
return finalMusic
}
return finalMusic;
};
/**
* 获取随机音乐
@@ -90,13 +90,13 @@ export const get163MusicListSongs = async (id: string) => {
* @returns 随机音乐id
*/
export const getRandomMusic = (ids: number[], currentId: number): number => {
const randomIndex = Math.floor(Math.random() * ids.length)
const randomId = ids[randomIndex]
const randomIndex = Math.floor(Math.random() * ids.length);
const randomId = ids[randomIndex];
if (randomId === currentId) {
return getRandomMusic(ids, currentId)
return getRandomMusic(ids, currentId);
}
return randomId
}
return randomId;
};
/**
* 获取下一首音乐id
@@ -109,14 +109,14 @@ export const getNextMusic = (
currentId: number,
mode: PlayMode
): number => {
const ids = musics.map((music) => music.id)
const ids = musics.map((music) => music.id);
if (mode === PlayMode.Loop) {
const currentIndex = ids.findIndex((id) => id === currentId)
const nextIndex = currentIndex + 1
return ids[nextIndex] || ids[0]
const currentIndex = ids.findIndex((id) => id === currentId);
const nextIndex = currentIndex + 1;
return ids[nextIndex] || ids[0];
}
if (mode === PlayMode.Random) {
return getRandomMusic(ids, currentId)
return getRandomMusic(ids, currentId);
}
return currentId
}
return currentId;
};

View File

@@ -5,7 +5,7 @@
*/
export const deepClone = <T>(obj: T): T => {
// 我们这里只只用于配置项,所以直接使用字符串就行
const newObj = JSON.parse(JSON.stringify(obj))
const newObj = JSON.parse(JSON.stringify(obj));
return newObj
}
return newObj;
};

View File

@@ -1,8 +1,8 @@
import type { Op } from 'quill'
import type { Op } from 'quill';
import type { EmojiValue } from '@/components/chat_input/formats/emoji_blot'
import type { ImageValue } from '@/components/chat_input/formats/image_blot'
import type { ReplyBlockValue } from '@/components/chat_input/formats/reply_blot'
import type { EmojiValue } from '@/components/chat_input/formats/emoji_blot';
import type { ImageValue } from '@/components/chat_input/formats/image_blot';
import type { ReplyBlockValue } from '@/components/chat_input/formats/reply_blot';
import {
type AllOB11WsResponse,
@@ -14,8 +14,8 @@ import {
type OB11PrivateMessage,
type OB11Segment,
type OneBot11Lifecycle,
type RequestResponse
} from '../types/onebot'
type RequestResponse,
} from '../types/onebot';
/**
* 获取事件名称
@@ -26,19 +26,19 @@ import {
export const getEventName = (event: OB11AllEvent['post_type']): string => {
switch (event) {
case 'message':
return '消息'
return '消息';
case 'notice':
return '通知'
return '通知';
case 'request':
return '请求'
return '请求';
case 'meta_event':
return '元事件'
return '元事件';
case 'message_sent':
return '消息上报'
return '消息上报';
default:
return '未知'
return '未知';
}
}
};
/**
* 获取生命周期事件名称
@@ -51,15 +51,15 @@ export const getLifecycleName = (
): string => {
switch (event) {
case 'enable':
return '启用'
return '启用';
case 'disable':
return '停用'
return '停用';
case 'connect':
return '连接'
return '连接';
default:
return '未知'
return '未知';
}
}
};
/**
* 获取生命周期事件Chip颜色
@@ -72,15 +72,15 @@ export const getLifecycleColor = (
): 'success' | 'danger' | 'default' => {
switch (event) {
case 'enable':
return 'success'
return 'success';
case 'disable':
return 'danger'
return 'danger';
case 'connect':
return 'success'
return 'success';
default:
return 'default'
return 'default';
}
}
};
/**
* 判断 OneBot WS 返回值是否为事件
@@ -89,8 +89,8 @@ export const getLifecycleColor = (
* @description 用于判断 OneBot WS 返回值是否为事件
*/
export const isOB11Event = (data: AllOB11WsResponse): data is OB11AllEvent => {
return 'post_type' in data
}
return 'post_type' in data;
};
/**
* 判断 OneBot WS 返回值是否为请求响应
@@ -101,8 +101,8 @@ export const isOB11Event = (data: AllOB11WsResponse): data is OB11AllEvent => {
export const isOB11RequestResponse = (
data: AllOB11WsResponse
): data is RequestResponse => {
return 'status' in data && 'retcode' in data
}
return 'status' in data && 'retcode' in data;
};
/**
* 获取请求响应状态文本
@@ -115,15 +115,15 @@ export const getResponseStatusText = (
): string => {
switch (status) {
case 'ok':
return '成功'
return '成功';
case 'failed':
return '失败'
return '失败';
case 'async':
return '异步'
return '异步';
default:
return '未知'
return '未知';
}
}
};
/**
* 获取请求响应状态颜色
@@ -136,15 +136,15 @@ export const getResponseStatusColor = (
): 'success' | 'danger' | 'warning' | 'default' => {
switch (status) {
case 'ok':
return 'success'
return 'success';
case 'failed':
return 'danger'
return 'danger';
case 'async':
return 'warning'
return 'warning';
default:
return 'default'
return 'default';
}
}
};
/**
* 获取通知类型名称
@@ -155,33 +155,33 @@ export const getResponseStatusColor = (
export const getNoticeTypeName = (type: OB11Notice['notice_type']): string => {
switch (type) {
case OB11NoticeType.GroupUpload:
return '群文件上传'
return '群文件上传';
case OB11NoticeType.GroupAdmin:
return '群管理员变动'
return '群管理员变动';
case OB11NoticeType.GroupDecrease:
return '群成员减少'
return '群成员减少';
case OB11NoticeType.GroupIncrease:
return '群成员增加'
return '群成员增加';
case OB11NoticeType.GroupBan:
return '群禁言'
return '群禁言';
case OB11NoticeType.FriendAdd:
return '好友添加'
return '好友添加';
case OB11NoticeType.GroupRecall:
return '群消息撤回'
return '群消息撤回';
case OB11NoticeType.FriendRecall:
return '好友消息撤回'
return '好友消息撤回';
case OB11NoticeType.Notify:
return '通知'
return '通知';
case OB11NoticeType.GroupMsgEmojiLike:
return '群消息表情回应'
return '群消息表情回应';
case OB11NoticeType.GroupEssence:
return '群消息精华'
return '群消息精华';
case OB11NoticeType.GroupCard:
return '群名片更新'
return '群名片更新';
default:
return '未知'
return '未知';
}
}
};
/**
* 判断 OneBot 消息是否为私聊消息
@@ -192,8 +192,8 @@ export const getNoticeTypeName = (type: OB11Notice['notice_type']): string => {
export const isOB11PrivateMessage = (
data: OB11Message
): data is OB11PrivateMessage => {
return data.message_type === 'private'
}
return data.message_type === 'private';
};
/**
* 判断 OneBot 消息是否为群消息
@@ -204,8 +204,8 @@ export const isOB11PrivateMessage = (
export const isOB11GroupMessage = (
data: OB11Message
): data is OB11GroupMessage => {
return data.message_type === 'group'
}
return data.message_type === 'group';
};
/**
* 将 Quill Delta 转换为 OneBot 消息
@@ -217,32 +217,32 @@ export const quillToMessage = (op: Op) => {
let message: OB11Segment = {
type: 'text',
data: {
text: op.insert as string
}
}
text: op.insert as string,
},
};
if (typeof op.insert !== 'string') {
if (op.insert?.image) {
message = {
type: 'image',
data: {
file: (op.insert.image as ImageValue).src
}
}
file: (op.insert.image as ImageValue).src,
},
};
} else if (op.insert?.emoji) {
message = {
type: 'face',
data: {
id: (op.insert.emoji as EmojiValue).id
}
}
id: (op.insert.emoji as EmojiValue).id,
},
};
} else if (op.insert?.reply) {
message = {
type: 'reply',
data: {
id: (op.insert.reply as ReplyBlockValue).messageId
}
}
id: (op.insert.reply as ReplyBlockValue).messageId,
},
};
}
}
return message
}
return message;
};

View File

@@ -1,4 +1,4 @@
import type { QQItem } from '@/components/quick_login'
import type { QQItem } from '@/components/quick_login';
/**
* 判断 QQ 快速登录列表项是否为 QQ 信息
@@ -9,5 +9,5 @@ import type { QQItem } from '@/components/quick_login'
export const isQQQuickNewItem = (
data?: QQItem | LoginListItem | null
): data is LoginListItem => {
return !!data && 'nickName' in data && 'faceUrl' in data
}
return !!data && 'nickName' in data && 'faceUrl' in data;
};

View File

@@ -1,64 +1,64 @@
import axios from 'axios'
import axios from 'axios';
import key from '@/const/key'
import key from '@/const/key';
export const serverRequest = axios.create({
timeout: 5000
})
timeout: 5000,
});
export const request = axios.create({
timeout: 10000
})
timeout: 10000,
});
export const requestServerWithFetch = async (
url: string,
options: RequestInit
) => {
const token = localStorage.getItem(key.token)
const token = localStorage.getItem(key.token);
if (token) {
options.headers = {
...options.headers,
Authorization: `Bearer ${JSON.parse(token)}`
}
Authorization: `Bearer ${JSON.parse(token)}`,
};
}
const baseURL = '/api'
const baseURL = '/api';
const response = await fetch(baseURL + url, options)
const response = await fetch(baseURL + url, options);
return response
}
return response;
};
serverRequest.interceptors.request.use((config) => {
const baseURL = '/api'
const baseURL = '/api';
config.baseURL = baseURL
config.baseURL = baseURL;
const token = localStorage.getItem(key.token)
const token = localStorage.getItem(key.token);
if (token) {
config.headers['Authorization'] = `Bearer ${JSON.parse(token)}`
config.headers['Authorization'] = `Bearer ${JSON.parse(token)}`;
}
return config
})
return config;
});
serverRequest.interceptors.response.use((response) => {
// 如果是流式传输的文件
if (response.headers['content-type'] === 'application/octet-stream') {
return response
return response;
}
if (response.data.code !== 0) {
if (response.data.message === 'Unauthorized') {
const token = localStorage.getItem(key.token)
const token = localStorage.getItem(key.token);
if (token && JSON.parse(token)) {
localStorage.removeItem(key.token)
window.location.reload()
localStorage.removeItem(key.token);
window.location.reload();
}
}
throw new Error(response.data.message)
throw new Error(response.data.message);
}
return response
})
return response;
});

View File

@@ -1,4 +1,4 @@
import { LogLevel } from '@/const/enum'
import { LogLevel } from '@/const/enum';
export const gradientText = (
text: string,
@@ -8,90 +8,90 @@ export const gradientText = (
italic: boolean = false,
underline: boolean = false
) => {
const steps = text.length
const steps = text.length;
const colorStep = startColor.map(
(start, index) => (endColor[index] - start) / steps
)
);
let coloredText = ''
let coloredText = '';
for (let i = 0; i < steps; i++) {
const color = startColor.map((start, index) =>
Math.round(start + colorStep[index] * i)
)
coloredText += `\x1b[38;2;${color[0]};${color[1]};${color[2]}m${text[i]}`
);
coloredText += `\x1b[38;2;${color[0]};${color[1]};${color[2]}m${text[i]}`;
}
// 添加样式
if (bold) {
coloredText = `\x1b[1m${coloredText}`
coloredText = `\x1b[1m${coloredText}`;
}
if (italic) {
coloredText = `\x1b[3m${coloredText}`
coloredText = `\x1b[3m${coloredText}`;
}
if (underline) {
coloredText = `\x1b[4m${coloredText}`
coloredText = `\x1b[4m${coloredText}`;
}
return coloredText + '\x1b[0m' // 重置颜色和样式
}
return coloredText + '\x1b[0m'; // 重置颜色和样式
};
export const logColor = {
[LogLevel.DEBUG]: 'green',
[LogLevel.INFO]: 'black',
[LogLevel.WARN]: 'yellow',
[LogLevel.ERROR]: 'red',
[LogLevel.FATAL]: 'red'
} as const
[LogLevel.FATAL]: 'red',
} as const;
export const colorizeLogLevel = (content: string) => {
const logLevel = content.match(/\[[a-zA-Z]+\]/) || []
let _content = content
const logLevel = content.match(/\[[a-zA-Z]+\]/) || [];
let _content = content;
const level =
(logLevel?.[0]?.replace('[', '').replace(']', '') as LogLevel) ??
LogLevel.INFO
const color = logColor[level]
LogLevel.INFO;
const color = logColor[level];
switch (color) {
case 'green':
_content = `\x1b[32m${_content}\x1b[0m`
break
_content = `\x1b[32m${_content}\x1b[0m`;
break;
case 'black':
_content = `\x1b[30m${_content}\x1b[0m`
break
_content = `\x1b[30m${_content}\x1b[0m`;
break;
case 'yellow':
_content = `\x1b[33m${_content}\x1b[0m`
break
_content = `\x1b[33m${_content}\x1b[0m`;
break;
case 'red':
_content = `\x1b[31m${_content}\x1b[0m`
break
_content = `\x1b[31m${_content}\x1b[0m`;
break;
default:
_content = `\x1b[30m${_content}\x1b[0m`
_content = `\x1b[30m${_content}\x1b[0m`;
}
return {
content: _content,
level
}
}
level,
};
};
export const colorizeLogLevelWithTag = (content: string, level: LogLevel) => {
let _content = content
let _content = content;
switch (level) {
case LogLevel.DEBUG:
_content = `\x1b[32m[DEBUG] ${content}\x1b[0m`
break
_content = `\x1b[32m[DEBUG] ${content}\x1b[0m`;
break;
case LogLevel.INFO:
_content = `\x1b[30m[INFO] ${content}\x1b[0m`
break
_content = `\x1b[30m[INFO] ${content}\x1b[0m`;
break;
case LogLevel.WARN:
_content = `\x1b[33m[WARN] ${content}\x1b[0m`
break
_content = `\x1b[33m[WARN] ${content}\x1b[0m`;
break;
case LogLevel.ERROR:
_content = `\x1b[31m[ERROR] ${content}\x1b[0m`
break
_content = `\x1b[31m[ERROR] ${content}\x1b[0m`;
break;
case LogLevel.FATAL:
_content = `\x1b[31m[FATAL] ${content}\x1b[0m`
break
_content = `\x1b[31m[FATAL] ${content}\x1b[0m`;
break;
default:
_content = `\x1b[30m${content}\x1b[0m`
_content = `\x1b[30m${content}\x1b[0m`;
}
return _content
}
return _content;
};

View File

@@ -1,17 +1,17 @@
import { request } from './request'
import { request } from './request';
const style = document.createElement('style')
document.head.appendChild(style)
const style = document.createElement('style');
document.head.appendChild(style);
export function loadTheme() {
export function loadTheme () {
request('/files/theme.css?_t=' + Date.now())
.then((res) => res.data)
.then((css) => {
style.innerHTML = css
style.innerHTML = css;
})
.catch(() => {
console.error('Failed to load theme.css')
})
console.error('Failed to load theme.css');
});
}
export const colorKeys = [
@@ -121,21 +121,21 @@ export const colorKeys = [
'--heroui-divider',
'--heroui-code-background',
'--heroui-strong',
'--heroui-code-mdx'
] as const
'--heroui-code-mdx',
] as const;
export const generateTheme = (theme: ThemeConfig, validField?: string) => {
let css = `:root ${validField ? `.${validField}` : ''}, .light ${validField ? `.${validField}` : ''}, [data-theme="light"] ${validField ? `.${validField}` : ''} {`
let css = `:root ${validField ? `.${validField}` : ''}, .light ${validField ? `.${validField}` : ''}, [data-theme="light"] ${validField ? `.${validField}` : ''} {`;
for (const key in theme.light) {
const _key = key as keyof ThemeConfigItem
css += `${_key}: ${theme.light[_key]};`
const _key = key as keyof ThemeConfigItem;
css += `${_key}: ${theme.light[_key]};`;
}
css += `}`
css += `.dark ${validField ? `.${validField}` : ''}, [data-theme="dark"] ${validField ? `.${validField}` : ''} {`
css += '}';
css += `.dark ${validField ? `.${validField}` : ''}, [data-theme="dark"] ${validField ? `.${validField}` : ''} {`;
for (const key in theme.dark) {
const _key = key as keyof ThemeConfigItem
css += `${_key}: ${theme.dark[_key]};`
const _key = key as keyof ThemeConfigItem;
css += `${_key}: ${theme.dark[_key]};`;
}
css += `}`
return css
}
css += '}';
return css;
};

View File

@@ -4,7 +4,7 @@
* @returns Promise<void>
*/
export const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms))
new Promise((resolve) => setTimeout(resolve, ms));
/**
* 获取距离当前时间的发布时间
@@ -13,26 +13,26 @@ export const sleep = (ms: number) =>
* @example getReleaseTime("2021-10-10T10:10:10Z") => "1天前"
*/
export const getReleaseTime = (time: string) => {
const releaseTime = new Date(time).getTime()
const nowTime = new Date().getTime()
const diffTime = nowTime - releaseTime
const diffDays = Math.floor(diffTime / (24 * 3600 * 1000))
const diffHours = Math.floor((diffTime % (24 * 3600 * 1000)) / (3600 * 1000))
const diffMinutes = Math.floor((diffTime % (3600 * 1000)) / (60 * 1000))
const diffSeconds = Math.floor((diffTime % (60 * 1000)) / 1000)
const releaseTime = new Date(time).getTime();
const nowTime = new Date().getTime();
const diffTime = nowTime - releaseTime;
const diffDays = Math.floor(diffTime / (24 * 3600 * 1000));
const diffHours = Math.floor((diffTime % (24 * 3600 * 1000)) / (3600 * 1000));
const diffMinutes = Math.floor((diffTime % (3600 * 1000)) / (60 * 1000));
const diffSeconds = Math.floor((diffTime % (60 * 1000)) / 1000);
if (diffDays > 0) {
return `${diffDays}天前`
return `${diffDays}天前`;
} else if (diffHours > 0) {
return `${diffHours}小时前`
return `${diffHours}小时前`;
} else if (diffMinutes > 0) {
return `${diffMinutes}分钟前`
return `${diffMinutes}分钟前`;
} else {
return `${diffSeconds}秒前`
return `${diffSeconds}秒前`;
}
}
};
const formatNumber = (n: number) => n.toString().padStart(2, '0')
const formatNumber = (n: number) => n.toString().padStart(2, '0');
/**
* 将时间戳转换为日期字符串
* @param timestamp 时间戳
@@ -40,13 +40,13 @@ const formatNumber = (n: number) => n.toString().padStart(2, '0')
* @example timestampToDateString(163383301) => "2021-10-10 10:10:10"
*/
export const timestampToDateString = (timestamp: number) => {
timestamp = timestamp * 1000
const date = new Date(timestamp)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return `${year}-${formatNumber(month)}-${formatNumber(day)} ${formatNumber(hour)}:${formatNumber(minute)}:${formatNumber(second)}`
}
timestamp = timestamp * 1000;
const date = new Date(timestamp);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
return `${year}-${formatNumber(month)}-${formatNumber(day)} ${formatNumber(hour)}:${formatNumber(minute)}:${formatNumber(second)}`;
};

View File

@@ -1,15 +1,15 @@
import { AxiosResponse } from 'axios'
import { AxiosResponse } from 'axios';
/**
* 打开链接
* @param url 链接地址
* @param newTab 是否在新标签页打开
*/
export function openUrl(url: string, newTab = true) {
export function openUrl (url: string, newTab = true) {
if (newTab) {
window.open(url)
window.open(url);
} else {
window.location.href = url
window.location.href = url;
}
}
@@ -18,21 +18,21 @@ export function openUrl(url: string, newTab = true) {
* @param response Axios响应
* @returns 标准的HTTP报文
*/
export function parseAxiosResponse(
export function parseAxiosResponse (
response: AxiosResponse,
http_version: string = 'HTTP/1.1'
) {
if (!response?.status) {
return 'No response'
return 'No response';
}
const statusLine = `${http_version} ${response.status} ${response.statusText}`
const statusLine = `${http_version} ${response.status} ${response.statusText}`;
const headers = Object.entries(response.headers)
.map(([key, value]) => `${key}: ${value}`)
.join('\r\n')
.join('\r\n');
const body = response.data
? `\r\n\r\n${typeof response.data === 'string' ? JSON.stringify(JSON.parse(response.data), null, 2) : JSON.stringify(response.data, null, 2)}`
: ''
return `${statusLine}\r\n${headers}${body}`
: '';
return `${statusLine}\r\n${headers}${body}`;
}
/**
@@ -41,5 +41,5 @@ export function parseAxiosResponse(
* @returns 是否为URI
*/
export const isURI = (uri: string) => {
return /^(http|https|file):\/\/.*/.test(uri)
}
return /^(http|https|file):\/\/.*/.test(uri);
};

View File

@@ -4,16 +4,16 @@
* @returns 版本号数字
*/
export const versionToNumber = (version: string): number => {
const finalVersionString = version.replace(/^v/, '')
const finalVersionString = version.replace(/^v/, '');
const versionArray = finalVersionString.split('.')
const versionArray = finalVersionString.split('.');
const versionNumber =
parseInt(versionArray[2]) +
parseInt(versionArray[1]) * 100 +
parseInt(versionArray[0]) * 10000
parseInt(versionArray[0]) * 10000;
return versionNumber
}
return versionNumber;
};
/**
* 比较版本号
@@ -25,12 +25,12 @@ export const versionToNumber = (version: string): number => {
* -1: version1 < version2
*/
export const compareVersion = (version1: string, version2: string): number => {
const versionNumber1 = versionToNumber(version1)
const versionNumber2 = versionToNumber(version2)
const versionNumber1 = versionToNumber(version1);
const versionNumber2 = versionToNumber(version2);
if (versionNumber1 === versionNumber2) {
return 0
return 0;
}
return versionNumber1 > versionNumber2 ? 1 : -1
}
return versionNumber1 > versionNumber2 ? 1 : -1;
};

View File

@@ -19,10 +19,10 @@ import {
ZodTuple,
ZodTypeAny,
ZodUnion,
ZodUnknown
} from 'zod'
ZodUnknown,
} from 'zod';
export type LiteralValue = string | number | boolean | null
export type LiteralValue = string | number | boolean | null;
export type ParsedSchema = {
name?: string
@@ -32,37 +32,37 @@ export type ParsedSchema = {
enum?: LiteralValue[]
children?: ParsedSchema[]
description?: string
}
};
export function parse(
export function parse (
schema: ZodTypeAny,
name?: string,
isRoot = true
): ParsedSchema | ParsedSchema[] {
const optional = schema.isOptional ? schema.isOptional() : false
const description = schema.description
const optional = schema.isOptional ? schema.isOptional() : false;
const description = schema.description;
if (schema instanceof ZodString) {
return { name, type: 'string', optional, description }
return { name, type: 'string', optional, description };
}
if (schema instanceof ZodNumber) {
return { name, type: 'number', optional, description }
return { name, type: 'number', optional, description };
}
if (schema instanceof ZodBoolean) {
return { name, type: 'boolean', optional, description }
return { name, type: 'boolean', optional, description };
}
if (schema instanceof ZodBigInt) {
return { name, type: 'bigint', optional, description }
return { name, type: 'bigint', optional, description };
}
if (schema instanceof ZodDate) {
return { name, type: 'date', optional, description }
return { name, type: 'date', optional, description };
}
if (schema instanceof ZodUnknown) {
return { name, type: 'unknown', optional, description }
return { name, type: 'unknown', optional, description };
}
if (schema instanceof ZodLiteral) {
@@ -71,8 +71,8 @@ export function parse(
type: 'value',
optional,
value: schema._def.value as LiteralValue,
description
}
description,
};
}
if (schema instanceof ZodEnum) {
@@ -81,16 +81,16 @@ export function parse(
type: 'enum',
optional,
enum: schema._def.values as LiteralValue[],
description
}
return data
description,
};
return data;
}
if (schema instanceof ZodUnion) {
const options = schema._def.options
const options = schema._def.options;
const parsedOptions = options.map(
(option: ZodTypeAny) => parse(option, undefined, false) as ParsedSchema
)
);
const basicTypes = [
'string',
@@ -100,42 +100,42 @@ export function parse(
'date',
'unknown',
'value',
'enum'
]
'enum',
];
const optionTypes: (string | string[])[] = parsedOptions.map(
(option: ParsedSchema) => option.type
)
);
const isAllBasicTypes = optionTypes.every((type) =>
basicTypes.includes(Array.isArray(type) ? type[0] : type)
)
);
if (isAllBasicTypes) {
const types = [
...new Set(
optionTypes.flatMap((type) => (Array.isArray(type) ? type[0] : type))
)
]
return { name, type: types, optional, description }
),
];
return { name, type: types, optional, description };
} else {
return {
name,
type: 'union',
optional,
children: parsedOptions,
description
}
description,
};
}
}
if (schema instanceof ZodObject) {
const shape = schema._def.shape()
const shape = schema._def.shape();
const children = Object.keys(shape).map((key) =>
parse(shape[key], key, false)
) as ParsedSchema[]
) as ParsedSchema[];
if (isRoot) {
return children
return children;
} else {
return { name, type: 'object', optional, children, description }
return { name, type: 'object', optional, children, description };
}
}
@@ -144,120 +144,120 @@ export function parse(
schema._def.type,
undefined,
false
) as ParsedSchema
) as ParsedSchema;
return {
name,
type: 'array',
optional,
children: Array.isArray(childSchema) ? childSchema : [childSchema],
description
}
description,
};
}
if (schema instanceof ZodNullable || schema instanceof ZodDefault) {
return parse(schema._def.innerType, name)
return parse(schema._def.innerType, name);
}
if (schema instanceof ZodOptional) {
const data = parse(schema._def.innerType, name)
const data = parse(schema._def.innerType, name);
if (Array.isArray(data)) {
data.forEach((item) => {
item.optional = true
item.description = description
})
item.optional = true;
item.description = description;
});
} else {
data.optional = true
data.description = description
data.optional = true;
data.description = description;
}
return data
return data;
}
if (schema instanceof ZodRecord) {
const valueType = parse(schema._def.valueType) as ParsedSchema
const valueType = parse(schema._def.valueType) as ParsedSchema;
return {
name,
type: 'record',
optional,
children: [valueType],
description
}
description,
};
}
if (schema instanceof ZodTuple) {
const items: ParsedSchema[] = schema._def.items.map((item: ZodTypeAny) =>
parse(item)
)
return { name, type: 'tuple', optional, children: items, description }
);
return { name, type: 'tuple', optional, children: items, description };
}
if (schema instanceof ZodNull) {
return { name, type: 'null', optional, description }
return { name, type: 'null', optional, description };
}
if (schema instanceof ZodLazy) {
return parse(schema._def.getter(), name)
return parse(schema._def.getter(), name);
}
if (schema instanceof ZodEffects) {
return parse(schema._def.schema, name)
return parse(schema._def.schema, name);
}
return { name, type: 'unknown', optional, description }
return { name, type: 'unknown', optional, description };
}
const generateDefault = (schema: ZodSchema): unknown => {
if (schema instanceof ZodObject) {
const obj: Record<string, unknown> = {}
const obj: Record<string, unknown> = {};
for (const key in schema.shape) {
obj[key] = generateDefault(schema.shape[key])
obj[key] = generateDefault(schema.shape[key]);
}
return obj
return obj;
}
if (schema instanceof ZodString) {
return 'textValue'
return 'textValue';
}
if (schema instanceof ZodNumber) {
return 0
return 0;
}
if (schema instanceof ZodBoolean) {
return false
return false;
}
if (schema instanceof ZodArray) {
return []
return [];
}
if (schema instanceof ZodUnion) {
return generateDefault(schema._def.options[0])
return generateDefault(schema._def.options[0]);
}
if (schema instanceof ZodEnum) {
return schema._def.values[0]
return schema._def.values[0];
}
if (schema instanceof ZodLiteral) {
return schema._def.value
return schema._def.value;
}
if (schema instanceof ZodNullable) {
return null
return null;
}
if (schema instanceof ZodOptional) {
return generateDefault(schema._def.innerType)
return generateDefault(schema._def.innerType);
}
if (schema instanceof ZodRecord) {
return {}
return {};
}
if (schema instanceof ZodTuple) {
return schema._def.items.map((item: ZodTypeAny) => generateDefault(item))
return schema._def.items.map((item: ZodTypeAny) => generateDefault(item));
}
if (schema instanceof ZodNull) {
return null
return null;
}
if (schema instanceof ZodLazy) {
return generateDefault(schema._def.getter())
return generateDefault(schema._def.getter());
}
if (schema instanceof ZodEffects) {
return generateDefault(schema._def.schema)
return generateDefault(schema._def.schema);
}
return null
}
return null;
};
export const generateDefaultJson = (schema: ZodSchema) => {
return JSON.stringify(generateDefault(schema), null, 2)
}
return JSON.stringify(generateDefault(schema), null, 2);
};