Add ESLint config and update code style

Introduced a new eslint.config.js using neostandard and added related devDependencies. Updated codebase for consistent formatting, spacing, and function declarations. Minor refactoring and cleanup across multiple files to improve readability and maintain code style compliance.
This commit is contained in:
手瓜一十雪 2025-11-15 16:21:59 +08:00
parent 1990761ad6
commit fa80441e36
42 changed files with 628 additions and 580 deletions

52
eslint.config.js Normal file
View File

@ -0,0 +1,52 @@
import neostandard from 'neostandard';
/** 尾随逗号 */
const commaDangle = val => {
if (val?.rules?.['@stylistic/comma-dangle']?.[0] === 'warn') {
const rule = val?.rules?.['@stylistic/comma-dangle']?.[1];
Object.keys(rule).forEach(key => {
rule[key] = 'always-multiline';
});
val.rules['@stylistic/comma-dangle'][1] = rule;
}
/** 三元表达式 */
if (val?.rules?.['@stylistic/indent']) {
val.rules['@stylistic/indent'][2] = {
...val.rules?.['@stylistic/indent']?.[2],
flatTernaryExpressions: true,
offsetTernaryExpressions: false,
};
}
/** 支持下划线 - 禁用 camelcase 规则 */
if (val?.rules?.camelcase) {
val.rules.camelcase = 'off';
}
/** 未使用的变量强制报错 */
if (val?.rules?.['@typescript-eslint/no-unused-vars']) {
val.rules['@typescript-eslint/no-unused-vars'] = ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
}];
}
return val;
};
/** 忽略的文件 */
const ignores = [
'node_modules',
'**/dist/**',
'launcher',
];
const options = neostandard({
ts: true,
ignores,
semi: true, // 强制使用分号
}).map(commaDangle);
export default options;

View File

@ -11,13 +11,17 @@
"dev:shell": "pnpm --filter napcat-develop run dev || exit 1",
"typecheck": "pnpm -r --if-present run typecheck",
"test": "pnpm --filter napcat-test run test",
"test:ui": "pnpm --filter napcat-test run test:ui"
"test:ui": "pnpm --filter napcat-test run test:ui",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.3",
"@vitejs/plugin-react-swc": "^4.2.2",
"@vitest/ui": "^4.0.9",
"eslint": "^9.39.1",
"inversify": "^7.10.4",
"neostandard": "^0.12.2",
"reflect-metadata": "^0.2.2",
"typescript": "^5.3.0",
"vite": "^6.4.1",

View File

@ -1,3 +1,2 @@
// @ts-ignore
export const napCatVersion = (typeof import.meta?.env !== 'undefined' && import.meta.env.VITE_NAPCAT_VERSION) || 'alpha';

View File

@ -169,7 +169,6 @@ export class NTQQFileApi {
};
}
async downloadFileForModelId (peer: Peer, modelId: string, unknown: string, timeout = 1000 * 60 * 2) {
const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelRichMediaService/downloadFileForModelId',

View File

@ -68,13 +68,13 @@ export class FFmpegAddonAdapter implements IFFmpegAdapter {
const addon = this.ensureAddon();
const info = await addon.getVideoInfo(videoPath);
let format = info.format.includes(',') ? info.format.split(',')[0] ?? info.format : info.format;
const format = info.format.includes(',') ? info.format.split(',')[0] ?? info.format : info.format;
console.log('[FFmpegAddonAdapter] Detected format:', format);
return {
width: info.width,
height: info.height,
duration: info.duration,
format: format,
format,
thumbnail: info.image,
};
}

View File

@ -53,7 +53,6 @@ export class FFmpegService {
throw new Error('FFmpeg service not initialized. Please call FFmpegService.init() first.');
}
return this.adapter.name;
}
/**

View File

@ -1,4 +1,3 @@
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
import { LogWrapper, LogLevel } from '@/napcat-core/helper/log';

View File

@ -50,7 +50,6 @@ export class NativePacketHandler {
this.logger.logError('NativePacketClient 加载出错:', error);
this.loaded = false;
}
}
/**

View File

@ -1,4 +1,4 @@
import { TypedEventEmitter } from "./typeEvent";
import { TypedEventEmitter } from './typeEvent';
export interface AppEvents {
'event:emoji_like': { groupId: string; senderUin: string; emojiId: string, msgSeq: string, isAdd: boolean, count: number };

View File

@ -1,7 +1,7 @@
import "reflect-metadata";
import { Container, injectable } from "inversify";
import { NapCatCore } from "../..";
import { TypedEventEmitter } from "./typeEvent";
import 'reflect-metadata';
import { Container, injectable } from 'inversify';
import { NapCatCore } from '../..';
import { TypedEventEmitter } from './typeEvent';
export const container = new Container();
@ -11,6 +11,7 @@ export abstract class ServiceBase {
get core (): NapCatCore {
return container.get(NapCatCore);
}
get event () {
return container.get(TypedEventEmitter);
}
@ -25,4 +26,3 @@ export function ReceiveService(serviceName: string) {
return constructor;
};
}

View File

@ -1,6 +1,6 @@
import { NapProtoMsg } from "napcat-protobuf";
import { ReceiveService, ServiceBase } from "../packet/handler/serviceRegister";
import { GroupReactNotify, PushMsg } from "../packet/transformer/proto";
import { NapProtoMsg } from 'napcat-protobuf';
import { ReceiveService, ServiceBase } from '../packet/handler/serviceRegister';
import { GroupReactNotify, PushMsg } from '../packet/transformer/proto';
@ReceiveService('trpc.msg.olpush.OlPushService.MsgPush')
export class OlPushService extends ServiceBase {
@ -24,14 +24,13 @@ export class OlPushService extends ServiceBase {
const senderUin = await this.core.apis.UserApi.getUinByUidV2(operatorUid);
this.event.emit('event:emoji_like', {
groupId: groupCode,
senderUin: senderUin,
senderUin,
emojiId: code,
msgSeq: seq,
isAdd: type === 1,
count: count
count,
});
}
}
}
}

View File

@ -24,7 +24,6 @@ export interface NodeIKernelBuddyService {
}>;
}>;
getBuddyListFromCache (reqType: BuddyListReqType): Promise<Array<
{
categoryId: number, // 9999为特别关心

View File

@ -45,15 +45,15 @@ const itemsToCopy = [
'opencv.dll',
'package.json',
'QBar.dll',
'wrapper.node'
'wrapper.node',
];
async function copyAll () {
const qqntDllPath = path.join(TARGET_DIR, 'QQNT.dll');
const configPath = path.join(TARGET_DIR, 'config.json');
const allItemsExist = await fs.pathExists(qqntDllPath)
&& await fs.pathExists(configPath)
&& (await Promise.all(itemsToCopy.map(item => fs.pathExists(path.join(TARGET_DIR, item))))).every(exists => exists);
const allItemsExist = await fs.pathExists(qqntDllPath) &&
await fs.pathExists(configPath) &&
(await Promise.all(itemsToCopy.map(item => fs.pathExists(path.join(TARGET_DIR, item))))).every(exists => exists);
if (!allItemsExist) {
console.log('Copying required files...');

View File

@ -2,23 +2,23 @@ import cp from 'vite-plugin-cp';
import { defineConfig, PluginOption, UserConfig } from 'vite';
import path, { resolve } from 'path';
import nodeResolve from '@rollup/plugin-node-resolve';
import { autoIncludeTSPlugin } from "napcat-vite/vite-auto-include.js";
import { autoIncludeTSPlugin } from 'napcat-vite/vite-auto-include.js';
import { builtinModules } from 'module';
import react from '@vitejs/plugin-react-swc';
import napcatVersion from "napcat-vite/vite-plugin-version.js";
import napcatVersion from 'napcat-vite/vite-plugin-version.js';
// 依赖排除
const external = [
'silk-wasm',
'ws',
'express'
'express',
];
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
const FrameworkBaseConfigPlugin: PluginOption[] = [
autoIncludeTSPlugin({
entries: [
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-core/protocol') },
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') }
]
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') },
],
}),
react({ tsDecorators: true }),
cp({

View File

@ -1,5 +1,3 @@
import { NapCatOneBot11Adapter } from '@/napcat-onebot/index';
import { encodeSilk } from '@/napcat-core/helper/audio';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
@ -20,6 +18,7 @@ export class OneBotFileApi {
this.obContext = obContext;
this.core = core;
}
async createValidSendFileElement (context: SendMessageContext, filePath: string, fileName: string = '', folderId: string = ''): Promise<SendFileElement> {
const {
fileName: _fileName,
@ -178,5 +177,4 @@ export class OneBotFileApi {
},
};
}
}

View File

@ -188,6 +188,7 @@ export class OneBotGroupApi {
}
});
}
async parsePaiYiPai (msg: RawMessage, jsonStr: string) {
const json = JSON.parse(jsonStr);
// 判断业务类型

View File

@ -7,7 +7,7 @@ type LowerCamelCase<S extends string> = CamelCaseHelper<S, false, true>;
type CamelCaseHelper<
S extends string,
CapNext extends boolean,
IsFirstChar extends boolean,
IsFirstChar extends boolean
> = S extends `${infer F}${infer R}`
? F extends '_'
? CamelCaseHelper<R, true, false>
@ -55,7 +55,7 @@ export interface ScalarProtoFieldType<T extends ScalarType, O extends boolean, R
export interface MessageProtoFieldType<
T extends () => ProtoMessageType,
O extends boolean,
R extends O extends true ? false : boolean,
R extends O extends true ? false : boolean
> extends BaseProtoFieldType<T, O, R> {
kind: 'message';
}
@ -71,12 +71,12 @@ type ProtoMessageType = {
export function ProtoField<
T extends ScalarType,
O extends boolean = false,
R extends O extends true ? false : boolean = false,
R extends O extends true ? false : boolean = false
> (no: number, type: T, optional?: O, repeat?: R): ScalarProtoFieldType<T, O, R>;
export function ProtoField<
T extends () => ProtoMessageType,
O extends boolean = false,
R extends O extends true ? false : boolean = false,
R extends O extends true ? false : boolean = false
> (no: number, type: T, optional?: O, repeat?: R): MessageProtoFieldType<T, O, R>;
export function ProtoField (
no: number,
@ -85,16 +85,16 @@ export function ProtoField(
repeat?: boolean
): ProtoFieldType {
if (typeof type === 'function') {
return { kind: 'message', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false };
return { kind: 'message', no, type, optional: optional ?? false, repeat: repeat ?? false };
} else {
return { kind: 'scalar', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false };
return { kind: 'scalar', no, type, optional: optional ?? false, repeat: repeat ?? false };
}
}
type ProtoFieldReturnType<T, E extends boolean> =
NonNullable<T> extends ScalarProtoFieldType<infer S, infer O, infer R>
NonNullable<T> extends ScalarProtoFieldType<infer S, infer _O, infer _R>
? ScalarTypeToTsType<S>
: T extends NonNullable<MessageProtoFieldType<infer S, infer O, infer R>>
: T extends NonNullable<MessageProtoFieldType<infer S, infer _O, infer _R>>
? NonNullable<NapProtoStructType<ReturnType<S>, E>>
: never;
@ -158,6 +158,8 @@ class NapProtoRealMsg<T extends ProtoMessageType> {
repeat: field.repeat ? RepeatType.PACKED : RepeatType.NO,
T: () => NapProtoRealMsg.getInstance(field.type())._proto_msg,
};
} else {
throw new Error(`Unknown field kind: ${field.kind}`);
}
}) as PartialFieldInfo[];
this._proto_msg = new MessageType<NapProtoStructType<T, boolean>>('nya', this._field);

View File

@ -1,5 +1,5 @@
const path = require('path');
const CurrentPath = path.dirname(__filename);
(async () => {
await import("file://" + path.join(CurrentPath, './napcat/napcat.mjs'));
await import('file://' + path.join(CurrentPath, './napcat/napcat.mjs'));
})();

View File

@ -364,7 +364,7 @@ export async function NCoreInitShell () {
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
handleProxy(session, logger);
let quickLoginUin: string | undefined = undefined;
let quickLoginUin: string | undefined;
try {
const args = process.argv;
const qIndex = args.findIndex(arg => arg === '-q' || arg === '--qq');

View File

@ -3,15 +3,15 @@ import { defineConfig, PluginOption, UserConfig } from 'vite';
import path, { resolve } from 'path';
import nodeResolve from '@rollup/plugin-node-resolve';
import { builtinModules } from 'module';
import napcatVersion from "napcat-vite/vite-plugin-version.js";
import { autoIncludeTSPlugin } from "napcat-vite/vite-auto-include.js";
import napcatVersion from 'napcat-vite/vite-plugin-version.js';
import { autoIncludeTSPlugin } from 'napcat-vite/vite-auto-include.js';
import react from '@vitejs/plugin-react-swc';
// 依赖排除
const external = [
'silk-wasm',
'ws',
'express'
'express',
];
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
@ -20,8 +20,8 @@ const ShellBaseConfigPlugin: PluginOption[] = [
autoIncludeTSPlugin({
entries: [
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-core/protocol') },
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') }
]
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') },
],
}),
cp({
targets: [
@ -29,7 +29,7 @@ const ShellBaseConfigPlugin: PluginOption[] = [
{ src: '../napcat-webui-frontend/dist/', dest: 'dist/static/', flatten: false },
{ src: '../napcat-core/external/napcat.json', dest: 'dist/config/' },
{ src: '../../package.json', dest: 'dist' },
{ src: '../napcat-shell-loader', dest: 'dist' }
{ src: '../napcat-shell-loader', dest: 'dist' },
],
}),
nodeResolve(),
@ -62,7 +62,7 @@ const ShellBaseConfig = (source_map: boolean = false) =>
fileName: (_, entryName) => `${entryName}.mjs`,
},
rollupOptions: {
external: [...nodeModules, ...external]
external: [...nodeModules, ...external],
},
},
});

View File

@ -49,7 +49,7 @@ export function autoIncludeTSPlugin(options) {
return {
code: transformedCode,
map: { mappings: '' } // 空映射即可VSCode 可以在 TS 上断点
map: { mappings: '' }, // 空映射即可VSCode 可以在 TS 上断点
};
}
}
@ -65,7 +65,7 @@ async function findTSFiles(dir) {
const files = [];
const items = await fs.promises.readdir(dir, { withFileTypes: true });
for (let item of items) {
for (const item of items) {
const fullPath = path.join(dir, item.name);
if (item.isDirectory()) {
files.push(...await findTSFiles(fullPath)); // 递归查找子目录

View File

@ -1,7 +1,7 @@
import fs from "fs";
import path from "path";
import https from "https";
import { fileURLToPath } from "url";
import fs from 'fs';
import path from 'path';
import https from 'https';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@ -10,10 +10,10 @@ const __dirname = path.dirname(__filename);
* NapCat Vite Plugin: fetches latest GitHub tag (not release) and injects into import.meta.env
*/
export default function vitePluginNapcatVersion () {
const pluginDir = path.resolve(__dirname, "dist");
const cacheFile = path.join(pluginDir, ".napcat-version.json");
const owner = "NapNeko";
const repo = "NapCatQQ";
const pluginDir = path.resolve(__dirname, 'dist');
const cacheFile = path.join(pluginDir, '.napcat-version.json');
const owner = 'NapNeko';
const repo = 'NapCatQQ';
const maxAgeMs = 24 * 60 * 60 * 1000; // cache 1 day
const githubToken = process.env.GITHUB_TOKEN;
@ -23,7 +23,7 @@ export default function vitePluginNapcatVersion() {
try {
const stat = fs.statSync(cacheFile);
if (Date.now() - stat.mtimeMs < maxAgeMs) {
const data = JSON.parse(fs.readFileSync(cacheFile, "utf8"));
const data = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
if (data?.tag) return data.tag;
}
} catch {}
@ -46,27 +46,27 @@ export default function vitePluginNapcatVersion() {
url,
{
headers: {
"User-Agent": "vite-plugin-napcat-version",
Accept: "application/vnd.github.v3+json",
'User-Agent': 'vite-plugin-napcat-version',
Accept: 'application/vnd.github.v3+json',
...(githubToken ? { Authorization: `token ${githubToken}` } : {}),
},
},
(res) => {
let data = "";
res.on("data", (c) => (data += c));
res.on("end", () => {
let data = '';
res.on('data', (c) => (data += c));
res.on('end', () => {
try {
const json = JSON.parse(data);
if (Array.isArray(json) && json[0]?.name) {
resolve(json[0].name);
} else reject(new Error("Invalid GitHub tag response"));
} else reject(new Error('Invalid GitHub tag response'));
} catch (e) {
reject(e);
}
});
}
);
req.on("error", reject);
req.on('error', reject);
});
}
@ -78,16 +78,16 @@ export default function vitePluginNapcatVersion() {
writeCache(tag);
return tag;
} catch (e) {
console.warn("[vite-plugin-napcat-version] Failed to fetch tag:", e.message);
return cached ?? "v0.0.0";
console.warn('[vite-plugin-napcat-version] Failed to fetch tag:', e.message);
return cached ?? 'v0.0.0';
}
}
let lastTag = null;
return {
name: "vite-plugin-napcat-version",
enforce: "pre",
name: 'vite-plugin-napcat-version',
enforce: 'pre',
async config (userConfig) {
const tag = await getVersion();
@ -96,7 +96,7 @@ export default function vitePluginNapcatVersion() {
return {
define: {
...(userConfig.define || {}),
"import.meta.env.VITE_NAPCAT_VERSION": JSON.stringify(tag),
'import.meta.env.VITE_NAPCAT_VERSION': JSON.stringify(tag),
},
};
},
@ -104,11 +104,11 @@ export default function vitePluginNapcatVersion() {
handleHotUpdate (ctx) {
if (path.resolve(ctx.file) === cacheFile) {
try {
const json = JSON.parse(fs.readFileSync(cacheFile, "utf8"));
const json = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
const tag = json?.tag;
if (tag && tag !== lastTag) {
lastTag = tag;
ctx.server?.ws.send({ type: "full-reload" });
ctx.server?.ws.send({ type: 'full-reload' });
}
} catch {}
}

View File

@ -1,4 +1,4 @@
import { webUiPathWrapper, getInitialWebUiToken } from '@/napcat-webui-backend/index'
import { webUiPathWrapper, getInitialWebUiToken } from '@/napcat-webui-backend/index';
import { Type, Static } from '@sinclair/typebox';
import Ajv from 'ajv';
import fs, { constants } from 'node:fs/promises';

View File

@ -1,2 +0,0 @@
import eslintConfig from '../eslint.config.mjs';
export default eslintConfig;

View File

@ -1,6 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}
autoprefixer: {},
},
};

View File

@ -23,6 +23,6 @@ declare module 'react-color' {
export const CirclePicker: any;
export default {
SketchPicker: SketchPicker,
SketchPicker,
} as any;
}

View File

@ -1,4 +1,4 @@
import { heroui } from '@heroui/theme'
import { heroui } from '@heroui/theme';
/** @type {import('tailwindcss').Config} */
export default {
@ -7,16 +7,16 @@ export default {
'./src/layouts/**/*.{js,ts,jsx,tsx,mdx}',
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}'
'./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}',
],
safelist: [
{
pattern:
/bg-(primary|secondary|success|danger|warning|default)-(50|100|200|300|400|500|600|700|800|900)/
}
/bg-(primary|secondary|success|danger|warning|default)-(50|100|200|300|400|500|600|700|800|900)/,
},
],
theme: {
extend: {}
extend: {},
},
darkMode: 'class',
plugins: [
@ -36,7 +36,7 @@ export default {
600: '#c20e4d',
700: '#920b3a',
800: '#610726',
900: '#310413'
900: '#310413',
},
danger: {
DEFAULT: '#DB3694',
@ -50,9 +50,9 @@ export default {
600: '#BC278B',
700: '#9D1B7F',
800: '#7F1170',
900: '#690A66'
}
}
900: '#690A66',
},
},
},
dark: {
colors: {
@ -68,7 +68,7 @@ export default {
600: '#f871a0',
700: '#faa0bf',
800: '#fdd0df',
900: '#fee7ef'
900: '#fee7ef',
},
danger: {
DEFAULT: '#DB3694',
@ -82,11 +82,11 @@ export default {
600: '#F485AE',
700: '#FBAFC4',
800: '#FDD7DD',
900: '#FEEAF6'
}
}
}
}
})
]
}
900: '#FEEAF6',
},
},
},
},
}),
],
};

View File

@ -1,18 +1,18 @@
import react from '@vitejs/plugin-react'
import path from 'node:path'
import { defineConfig, loadEnv, normalizePath } from 'vite'
import { viteStaticCopy } from 'vite-plugin-static-copy'
import tsconfigPaths from 'vite-tsconfig-paths'
import react from '@vitejs/plugin-react';
import path from 'node:path';
import { defineConfig, loadEnv, normalizePath } from 'vite';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import tsconfigPaths from 'vite-tsconfig-paths';
const monacoEditorPath = normalizePath(
path.resolve(__dirname, 'node_modules/monaco-editor/min/vs')
)
);
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
const backendDebugUrl = env.VITE_DEBUG_BACKEND_URL
console.log('backendDebugUrl', backendDebugUrl)
const env = loadEnv(mode, process.cwd());
const backendDebugUrl = env.VITE_DEBUG_BACKEND_URL;
console.log('backendDebugUrl', backendDebugUrl);
return {
plugins: [
react(),
@ -21,10 +21,10 @@ export default defineConfig(({ mode }) => {
targets: [
{
src: monacoEditorPath,
dest: 'monaco-editor/min'
}
]
})
dest: 'monaco-editor/min',
},
],
}),
],
base: '/webui/',
server: {
@ -32,11 +32,11 @@ export default defineConfig(({ mode }) => {
'/api/ws/terminal': {
target: backendDebugUrl,
ws: true,
changeOrigin: true
changeOrigin: true,
},
'/api': backendDebugUrl,
'/files': backendDebugUrl
}
'/files': backendDebugUrl,
},
},
build: {
assetsInlineLimit: 0,
@ -49,10 +49,10 @@ export default defineConfig(({ mode }) => {
'react-hook-form': ['react-hook-form'],
'react-icons': ['react-icons'],
'react-hot-toast': ['react-hot-toast'],
qface: ['qface']
}
}
}
}
}
})
qface: ['qface'],
},
},
},
},
};
});