From 8b9dabe011b133f7edfc84e59ddd58db57872d38 Mon Sep 17 00:00:00 2001 From: sarayourfriend <24264157+sarayourfriend@users.noreply.github.com> Date: Mon, 18 Sep 2023 12:58:32 +1000 Subject: [PATCH] Deduplicate checks between ESLint and TypeScript (#3008) * Deduplicate TypeScript and ESLint features * Separate TS ESLint config into separate sub-config * Fix TypeScript typo Co-authored-by: Olga Bulat --------- Co-authored-by: Olga Bulat --- frontend/src/modules/prometheus.ts | 8 ++--- frontend/src/plugins/ua-parse.ts | 4 +-- packages/eslint-plugin/src/configs/import.ts | 28 ++++++++++++++- packages/eslint-plugin/src/configs/index.ts | 36 ++----------------- .../eslint-plugin/src/configs/typescript.ts | 24 +++++++++++++ tsconfig.base.json | 12 +++++-- 6 files changed, 68 insertions(+), 44 deletions(-) create mode 100644 packages/eslint-plugin/src/configs/typescript.ts diff --git a/frontend/src/modules/prometheus.ts b/frontend/src/modules/prometheus.ts index 4984d23bfbd..fdfcb107c55 100644 --- a/frontend/src/modules/prometheus.ts +++ b/frontend/src/modules/prometheus.ts @@ -1,6 +1,6 @@ import http from "http" -import Prometheus from "prom-client" +import { register } from "prom-client" import promBundle from "express-prom-bundle" import { searchTypes } from "../../src/constants/media" @@ -57,7 +57,7 @@ const PrometheusModule: Module = function () { this.nuxt.hook("close", () => { metricsServer?.close() // Clear registry so that metrics can re-register when the server restarts in development - Prometheus.register.clear() + register.clear() }) this.nuxt.hook("listen", () => { @@ -66,9 +66,9 @@ const PrometheusModule: Module = function () { metricsServer = http .createServer(async (_, res) => { res.writeHead(200, { - "Content-Type": Prometheus.register.contentType, + "Content-Type": register.contentType, }) - res.end(await Prometheus.register.metrics()) + res.end(await register.metrics()) }) .listen(parseFloat(process.env.METRICS_PORT || "54641"), "0.0.0.0") }) diff --git a/frontend/src/plugins/ua-parse.ts b/frontend/src/plugins/ua-parse.ts index 44b71ef7769..00275d54f7a 100644 --- a/frontend/src/plugins/ua-parse.ts +++ b/frontend/src/plugins/ua-parse.ts @@ -1,4 +1,4 @@ -import useragent, { Details as UADetails } from "express-useragent" +import { parse, Details as UADetails } from "express-useragent" import type { Plugin } from "@nuxt/types" @@ -12,7 +12,7 @@ const uaParsePlugin: Plugin = (context, inject) => { } let ua: UADetails | null if (typeof userAgent == "string") { - ua = useragent.parse(userAgent) + ua = parse(userAgent) } else { ua = null } diff --git a/packages/eslint-plugin/src/configs/import.ts b/packages/eslint-plugin/src/configs/import.ts index 4bbcc90d0e1..bcab2723100 100644 --- a/packages/eslint-plugin/src/configs/import.ts +++ b/packages/eslint-plugin/src/configs/import.ts @@ -4,9 +4,14 @@ import type { TSESLint } from "@typescript-eslint/utils" * ESLint `import` plugin configuration. */ export = { - extends: ["plugin:import/recommended"], + extends: ["plugin:import/recommended", "plugin:import/typescript"], plugins: ["import"], rules: { + // `namespace` and `default` are handled by TypeScript + // There's no need to rely on ESLint for this + // https://github.com/import-js/eslint-plugin-import/issues/2878 + "import/namespace": "off", + "import/default": "off", "import/newline-after-import": ["error"], "import/order": [ "error", @@ -65,6 +70,27 @@ export = { "import/extensions": ["error", "always", { js: "never", ts: "never" }], }, overrides: [ + { + files: ["frontend/**"], + settings: { + "import/resolver": { + typescript: { + project: "frontend/tsconfig.json", + extensions: [".js", ".ts", ".vue", ".png"], + }, + }, + }, + }, + { + files: ["packages/**"], + settings: { + "import/resolver": { + typescript: { + project: "packages/*/tsconfig.json", + }, + }, + }, + }, { files: ["frontend/.storybook/**"], rules: { diff --git a/packages/eslint-plugin/src/configs/index.ts b/packages/eslint-plugin/src/configs/index.ts index 5ae7da0abd7..13363b2e92a 100644 --- a/packages/eslint-plugin/src/configs/index.ts +++ b/packages/eslint-plugin/src/configs/index.ts @@ -12,20 +12,17 @@ export const project: TSESLint.Linter.Config = { node: true, }, parser: "vue-eslint-parser", - parserOptions: { - parser: "@typescript-eslint/parser", - }, extends: [ "eslint:recommended", - "plugin:@typescript-eslint/recommended", "plugin:eslint-comments/recommended", "plugin:jsonc/recommended-with-jsonc", require.resolve("./custom"), require.resolve("./vue"), require.resolve("./import"), + require.resolve("./typescript"), "prettier", ], - plugins: ["@typescript-eslint", "tsdoc", "unicorn"], + plugins: ["unicorn"], settings: { "vue-i18n": { localeDir: "./frontend/src/locales/*.{json}", @@ -36,40 +33,12 @@ export const project: TSESLint.Linter.Config = { semi: ["error", "never"], "no-console": "off", "unicorn/filename-case": ["error", { case: "kebabCase" }], - "@typescript-eslint/no-var-requires": ["off"], }, overrides: [ - { - files: ["*.ts"], - rules: { - "tsdoc/syntax": "error", - }, - }, { files: ["*.json", "*.json5", "*.jsonc"], parser: "jsonc-eslint-parser", }, - { - files: ["frontend/**"], - settings: { - "import/resolver": { - typescript: { - project: "frontend/tsconfig.json", - extensions: [".js", ".ts", ".vue", ".png"], - }, - }, - }, - }, - { - files: ["packages/**"], - settings: { - "import/resolver": { - typescript: { - project: "packages/*/tsconfig.json", - }, - }, - }, - }, { env: { jest: true }, files: ["packages/**/*/test", "frontend/test/unit/**"], @@ -128,7 +97,6 @@ export const project: TSESLint.Linter.Config = { "unicorn/filename-case": "off", }, }, - { files: ["frontend/src/components/**"], rules: { diff --git a/packages/eslint-plugin/src/configs/typescript.ts b/packages/eslint-plugin/src/configs/typescript.ts new file mode 100644 index 00000000000..85b6be75cce --- /dev/null +++ b/packages/eslint-plugin/src/configs/typescript.ts @@ -0,0 +1,24 @@ +import type { TSESLint } from "@typescript-eslint/utils" + +export = { + parserOptions: { + parser: "@typescript-eslint/parser", + }, + plugins: ["@typescript-eslint", "tsdoc"], + extends: ["plugin:@typescript-eslint/recommended"], + rules: { + "@typescript-eslint/no-var-requires": ["off"], + }, + overrides: [ + { + files: ["*.ts"], + rules: { + "tsdoc/syntax": "error", + // This rule is disabled above to avoid forcing ESM syntax on regular JS files + // that aren't ready for it yet. We do want to enforce this for TypeScript, + // however, so we re-enable it here. + "@typescript-eslint/no-var-requires": ["error"], + }, + }, + ], +} satisfies TSESLint.Linter.Config diff --git a/tsconfig.base.json b/tsconfig.base.json index 3b70618c123..cc5c9d24ff1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,12 +10,18 @@ "esModuleInterop": true, /* Strict Type-Checking Options */ "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "paths": { "@openverse/*": ["./packages/*/src"] - } + }, + + /** + * Disable these in favour of more flexible ESLint rule. + * + * https://typescript-eslint.io/rules/no-unused-vars/#benefits-over-typescript + */ + "noUnusedLocals": false, + "noUnusedParameters": false } }