From 0f705a566c2608af4d174fadb5e6df5432334a20 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 2 Apr 2026 05:29:27 +0800 Subject: [PATCH 01/53] style: use oxlint-plugin-eslint for unimplemented eslint rules --- .github/dependabot.yml | 1 + .oxlintrc.json | 55 +++++++++--- eslint.config.mjs | 161 +----------------------------------- lib/routes/8kcos/utils.ts | 2 +- lib/routes/qq/news/user.ts | 10 ++- lib/utils/wechat-mp.test.ts | 2 +- package.json | 1 + pnpm-lock.yaml | 9 ++ 8 files changed, 65 insertions(+), 176 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c9a819637e83..20b8cd6d002d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -35,6 +35,7 @@ updates: - '@oxlint/*' - 'oxfmt' - 'oxlint' + - 'oxlint-plugin-eslint' - 'oxlint-tsgolint' proxy-agent: patterns: diff --git a/.oxlintrc.json b/.oxlintrc.json index 10927bb882bb..c6800c9e53a1 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -12,11 +12,12 @@ }, "plugins": ["eslint", "typescript", "node", "unicorn", "import"], "jsPlugins": [ - "@stylistic/eslint-plugin", { "name": "import-x-js", "specifier": "eslint-plugin-import-x" }, { "name": "n", "specifier": "eslint-plugin-n" }, { "name": "unicorn-js", "specifier": "eslint-plugin-unicorn" }, + "@stylistic/eslint-plugin", "eslint-plugin-simple-import-sort", + "oxlint-plugin-eslint", "./eslint-plugins/no-then.js", "./eslint-plugins/nsfw-flag.js" ], @@ -325,27 +326,60 @@ } ], - // "no-implicit-globals": "error", // not yet implemented + "eslint-js/no-implicit-globals": "error", // use jsPlugins "no-labels": "error", "no-lonely-if": "error", "no-multi-str": "error", "no-new-func": "error", + + "eslint-js/no-restricted-syntax": [ + "error", // use jsPlugins + { + "selector": "CallExpression[callee.property.name='get'][arguments.length=0]", + "message": "Please use .toArray() instead." + }, + { + "selector": "CallExpression[callee.property.name='toArray'] MemberExpression[object.callee.property.name='map']", + "message": "Please use .toArray() before .map()." + }, + { + "selector": "CallExpression[callee.property.name=\"catch\"] > ArrowFunctionExpression[params.length=0][body.value=null]", + "message": "Usage of .catch(() => null) is not allowed. Please handle the error appropriately." + }, + { + "selector": "CallExpression[callee.property.name=\"catch\"] > ArrowFunctionExpression[params.length=0][body.type=\"Identifier\"][body.name=\"undefined\"]", + "message": "Usage of .catch(() => undefined) is not allowed. Please handle the error appropriately." + }, + { + "selector": "CallExpression[callee.property.name=\"catch\"] > ArrowFunctionExpression[params.length=0] > ArrayExpression[elements.length=0]", + "message": "Usage of .catch(() => []) is not allowed. Please handle the error appropriately." + }, + { + "selector": "CallExpression[callee.property.name=\"catch\"] > ArrowFunctionExpression[params.length=0] > BlockStatement[body.length=0]", + "message": "Usage of .catch(() => {}) is not allowed. Please handle the error appropriately." + }, + { + "selector": "CallExpression[callee.name=\"load\"] AwaitExpression > CallExpression", + "message": "Do not use await in call expressions. Extract the result into a variable first." + } + ], + "no-unneeded-ternary": "error", "no-useless-computed-key": "error", "no-useless-concat": "warn", "no-useless-rename": "error", "no-var": "error", - // "object-shorthand": "error", // not yet implemented - // "prefer-arrow-callback'": "error", // not yet implemented + "eslint-js/object-shorthand": "error", // use jsPlugins + "eslint-js/prefer-arrow-callback": "error", // use jsPlugins "prefer-const": "error", "prefer-object-has-own": "error", + "eslint-js/prefer-regex-literals": [ + "error", // use jsPlugins + { + "disallowRedundantWrapping": true + } + ], "require-await": "error", - // "prefer-regex-literals": [ // not yet implemented - // "error", - // { - // "disallowRedundantWrapping": true - // } - // ], // #endregion // #region --- TypeScript --- @@ -354,6 +388,7 @@ "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/consistent-indexed-object-style": "off", // stylistic "@typescript-eslint/consistent-type-definitions": "off", // stylistic + "@typescript-eslint/dot-notation": "error", // type-aware "@typescript-eslint/no-empty-function": "off", // stylistic && tests "@typescript-eslint/no-explicit-any": "off", diff --git a/eslint.config.mjs b/eslint.config.mjs index 76f7be82c274..e1637cb97180 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,41 +1,26 @@ import js from '@eslint/js'; -import stylistic from '@stylistic/eslint-plugin'; import typescriptEslint from '@typescript-eslint/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; // import { importX } from 'eslint-plugin-import-x'; -import n from 'eslint-plugin-n'; // import simpleImportSort from 'eslint-plugin-simple-import-sort'; import eslintPluginYml from 'eslint-plugin-yml'; import { defineConfig } from 'eslint/config'; import globals from 'globals'; -// import github from './eslint-plugins/no-then.js'; -// import nsfwFlagPlugin from './eslint-plugins/nsfw-flag.js'; - const SOURCE_FILES_GLOB = '**/*.?([cm])[jt]s?(x)'; export default defineConfig([ - // { - // plugins: { - // '@rsshub/nsfw-flag': nsfwFlagPlugin, - // }, - // rules: { - // '@rsshub/nsfw-flag/add-nsfw-flag': 'error', - // }, - // }, { ignores: ['**/coverage', '**/.vscode', '**/docker-compose.yml', '!.github', 'assets/build', 'lib/routes-deprecated', 'lib/router.js', 'dist', 'dist-lib', 'dist-worker'], }, { files: [SOURCE_FILES_GLOB], plugins: { - '@stylistic': stylistic, '@typescript-eslint': typescriptEslint, // github, js, - n, }, - // extends: [js.configs.recommended, typescriptEslint.configs['flat/recommended'], typescriptEslint.configs['flat/stylistic'], n.configs['flat/recommended-script']], + // extends: [typescriptEslint.configs['flat/recommended'], typescriptEslint.configs['flat/stylistic'], n.configs['flat/recommended-script']], languageOptions: { globals: { @@ -53,101 +38,6 @@ export default defineConfig([ }, rules: { - // #region possible problems - /* - 'array-callback-return': ['error', { allowImplicit: true }], - - 'no-await-in-loop': 'error', - 'no-control-regex': 'off', - 'no-prototype-builtins': 'off', - */ - // #endregion - - // #region suggestions - /* - 'arrow-body-style': 'error', - 'block-scoped-var': 'error', - curly: 'error', - 'dot-notation': 'error', - eqeqeq: 'error', - - 'default-case': ['warn', { commentPattern: '^no default$' }], - - 'default-case-last': 'error', - 'no-console': 'error', - 'no-eval': 'error', - 'no-extend-native': 'error', - 'no-extra-label': 'error', - - 'no-implicit-coercion': [ - 'error', - { - boolean: false, - number: false, - string: false, - disallowTemplateShorthand: true, - }, - ], - - 'no-implicit-globals': 'error', - 'no-labels': 'error', - 'no-lonely-if': 'error', - 'no-multi-str': 'error', - 'no-new-func': 'error', - */ - 'no-restricted-syntax': [ - 'error', - { - selector: "CallExpression[callee.property.name='get'][arguments.length=0]", - message: 'Please use .toArray() instead.', - }, - { - selector: "CallExpression[callee.property.name='toArray'] MemberExpression[object.callee.property.name='map']", - message: 'Please use .toArray() before .map().', - }, - { - selector: 'CallExpression[callee.property.name="catch"] > ArrowFunctionExpression[params.length=0][body.value=null]', - message: 'Usage of .catch(() => null) is not allowed. Please handle the error appropriately.', - }, - { - selector: 'CallExpression[callee.property.name="catch"] > ArrowFunctionExpression[params.length=0][body.type="Identifier"][body.name="undefined"]', - message: 'Usage of .catch(() => undefined) is not allowed. Please handle the error appropriately.', - }, - { - selector: 'CallExpression[callee.property.name="catch"] > ArrowFunctionExpression[params.length=0] > ArrayExpression[elements.length=0]', - message: 'Usage of .catch(() => []) is not allowed. Please handle the error appropriately.', - }, - { - selector: 'CallExpression[callee.property.name="catch"] > ArrowFunctionExpression[params.length=0] > BlockStatement[body.length=0]', - message: 'Usage of .catch(() => {}) is not allowed. Please handle the error appropriately.', - }, - { - selector: 'CallExpression[callee.name="load"] AwaitExpression > CallExpression', - message: 'Do not use await in call expressions. Extract the result into a variable first.', - }, - ], - /* - 'no-unneeded-ternary': 'error', - 'no-useless-computed-key': 'error', - 'no-useless-concat': 'warn', - 'no-useless-rename': 'error', - 'no-var': 'error', - 'object-shorthand': 'error', - 'prefer-arrow-callback': 'error', - 'prefer-const': 'error', - 'prefer-object-has-own': 'error', - - 'prefer-regex-literals': [ - 'error', - { - disallowRedundantWrapping: true, - }, - ], - - 'require-await': 'error', - */ - // #endregion - // #region typescript /* '@typescript-eslint/array-type': ['error', { default: 'array-simple' }], @@ -163,55 +53,6 @@ export default defineConfig([ '@typescript-eslint/no-unused-vars': ['error', { args: 'after-used', argsIgnorePattern: '^_' }], */ // #endregion - - // #region stylistic - /* - '@stylistic/arrow-parens': 'error', - '@stylistic/arrow-spacing': 'error', - '@stylistic/comma-spacing': 'error', - '@stylistic/comma-style': 'error', - '@stylistic/function-call-spacing': 'error', - '@stylistic/keyword-spacing': 'off', - '@stylistic/linebreak-style': 'error', - - '@stylistic/lines-around-comment': ['error', { beforeBlockComment: false }], - - '@stylistic/no-multiple-empty-lines': 'error', - '@stylistic/no-trailing-spaces': 'error', - '@stylistic/rest-spread-spacing': 'error', - '@stylistic/semi': 'error', - '@stylistic/space-before-blocks': 'error', - '@stylistic/space-in-parens': 'error', - '@stylistic/space-infix-ops': 'error', - '@stylistic/space-unary-ops': 'error', - '@stylistic/spaced-comment': 'error', - */ - // #endregion - - // #region node specific rules - /* - 'n/no-extraneous-require': 'error', - 'n/no-deprecated-api': 'warn', - 'n/no-missing-import': 'off', - 'n/no-missing-require': 'off', - 'n/no-process-exit': 'off', - 'n/no-unpublished-import': 'off', - - 'n/no-unpublished-require': ['error', { allowModules: ['tosource'] }], - - 'n/no-unsupported-features/node-builtins': [ - 'error', - { - version: '^22.20.0 || ^24', - allowExperimental: true, - ignores: [], - }, - ], - */ - // #endregion - - // github - // 'github/no-then': 'warn', }, }, { diff --git a/lib/routes/8kcos/utils.ts b/lib/routes/8kcos/utils.ts index 9bb08bc5b04c..237317eaec81 100644 --- a/lib/routes/8kcos/utils.ts +++ b/lib/routes/8kcos/utils.ts @@ -19,7 +19,7 @@ export const getPosts = async (limit: number, options?: { categories?: number; t description: item.content.rendered, link: item.link, pubDate: parseDate(item.date_gmt), - author: item._embedded?.['author']?.map((a) => a.name).join(', '), + author: item._embedded?.author?.map((a) => a.name).join(', '), category: item._embedded?.['wp:term']?.flatMap((terms) => terms.map((t) => t.name)), })) satisfies DataItem[]; }; diff --git a/lib/routes/qq/news/user.ts b/lib/routes/qq/news/user.ts index 191542514e2a..aad163a1c138 100644 --- a/lib/routes/qq/news/user.ts +++ b/lib/routes/qq/news/user.ts @@ -64,10 +64,12 @@ async function handler(ctx) { news = await Promise.all( response.newslist.map((item) => cache.tryGet(item.id, async () => { - const description = - item.articletype === '0' - ? load(await ofetch(`https://news.qq.com/rain/a/${item.id}`))('.rich_media_content').html()! - : `

${item.abstract}

文章包含非文本内容,请在浏览器中打开查看

`; + let description = `

${item.abstract}

文章包含非文本内容,请在浏览器中打开查看

`; + if (item.articletype === '0') { + const article = await ofetch(`https://news.qq.com/rain/a/${item.id}`); + const $ = load(article); + description = $('.rich_media_content').html()!; + } return { title: item.longtitle, description, diff --git a/lib/utils/wechat-mp.test.ts b/lib/utils/wechat-mp.test.ts index e5e39325d70a..4e59d2b051f3 100644 --- a/lib/utils/wechat-mp.test.ts +++ b/lib/utils/wechat-mp.test.ts @@ -139,7 +139,7 @@ describe('wechat-mp', () => { // item_show_type in a separate script tag from real_item_show_type expect( ExtractMetadata.common( - load(/* HTML */ ` + load(`