From b3a6393c910d34a067cd7eb1d108dc094c6cd323 Mon Sep 17 00:00:00 2001 From: Sasha Sorokin <10401817+brawaru@users.noreply.github.com> Date: Sun, 8 Sep 2024 08:40:40 +0200 Subject: [PATCH] Add TypeScript to app-frontend (#2364) * Add TypeScript to app-frontend Co-authored-by: Evan Song <52982404+ferothefox@users.noreply.github.com> * Switch app-frontend to ESLint 9 & Nuxt config * Fix ESLint issues after config change in app-frontend --------- Co-authored-by: Evan Song <52982404+ferothefox@users.noreply.github.com> --- apps/app-frontend/.eslintrc.cjs | 4 - apps/app-frontend/eslint.config.mjs | 22 + apps/app-frontend/package.json | 20 +- .../src/components/ui/AccountsCard.vue | 6 +- .../components/ui/InstanceCreationModal.vue | 2 +- .../src/components/ui/JavaSelector.vue | 4 +- .../src/components/ui/SplashScreen.vue | 2 - .../IncompatibilityWarningModal.vue | 2 +- .../ui/install_flow/InstallConfirmModal.vue | 2 +- .../ui/install_flow/ModInstallModal.vue | 6 +- apps/app-frontend/src/helpers/utils.js | 2 +- apps/app-frontend/src/pages/Browse.vue | 12 +- apps/app-frontend/src/pages/Index.vue | 2 +- apps/app-frontend/src/pages/Settings.vue | 12 +- apps/app-frontend/src/pages/instance/Logs.vue | 2 +- apps/app-frontend/src/pages/instance/Mods.vue | 4 +- .../src/pages/instance/Options.vue | 2 +- .../src/pages/project/Gallery.vue | 6 +- apps/app-frontend/src/pages/project/Index.vue | 12 +- apps/app-frontend/tsconfig.app.json | 24 + apps/app-frontend/tsconfig.json | 12 +- apps/app-frontend/tsconfig.node.json | 17 + pnpm-lock.yaml | 1131 ++++++++++++++--- 23 files changed, 1053 insertions(+), 255 deletions(-) delete mode 100644 apps/app-frontend/.eslintrc.cjs create mode 100644 apps/app-frontend/eslint.config.mjs create mode 100644 apps/app-frontend/tsconfig.app.json create mode 100644 apps/app-frontend/tsconfig.node.json diff --git a/apps/app-frontend/.eslintrc.cjs b/apps/app-frontend/.eslintrc.cjs deleted file mode 100644 index 1f85730cc..000000000 --- a/apps/app-frontend/.eslintrc.cjs +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - root: true, - extends: ['custom/vue'], -} diff --git a/apps/app-frontend/eslint.config.mjs b/apps/app-frontend/eslint.config.mjs new file mode 100644 index 000000000..05f559424 --- /dev/null +++ b/apps/app-frontend/eslint.config.mjs @@ -0,0 +1,22 @@ +import { createConfigForNuxt } from '@nuxt/eslint-config/flat' +import { fixupPluginRules } from '@eslint/compat' +import turboPlugin from 'eslint-plugin-turbo' + +export default createConfigForNuxt().append([ + { + name: 'turbo', + plugins: { + turbo: fixupPluginRules(turboPlugin), + }, + rules: { + 'turbo/no-undeclared-env-vars': 'error', + }, + }, + { + name: 'modrinth', + rules: { + 'vue/html-self-closing': 'off', + 'vue/multi-word-component-names': 'off', + }, + }, +]) diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index 5fbc35e92..654182f89 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "vue-tsc --noEmit && vite build", + "tsc:check": "vue-tsc --noEmit", "lint": "eslint . && prettier --check .", "fix": "eslint . --fix && prettier --write ." }, @@ -13,37 +14,42 @@ "@modrinth/assets": "workspace:*", "@modrinth/ui": "workspace:*", "@modrinth/utils": "workspace:*", + "@sentry/vue": "^8.27.0", "@tauri-apps/api": "^2.0.0-rc.3", "@tauri-apps/plugin-dialog": "^2.0.0-rc.0", "@tauri-apps/plugin-os": "^2.0.0-rc.0", - "@tauri-apps/plugin-window-state": "^2.0.0-rc.0", "@tauri-apps/plugin-shell": "^2.0.0-rc.0", "@tauri-apps/plugin-updater": "^2.0.0-rc.0", + "@tauri-apps/plugin-window-state": "^2.0.0-rc.0", "@vintl/vintl": "^4.4.1", "dayjs": "^1.11.10", "floating-vue": "^5.2.2", "ofetch": "^1.3.4", "pinia": "^2.1.7", + "posthog-js": "^1.158.2", "vite-svg-loader": "^5.1.0", "vue": "^3.4.21", "vue-multiselect": "3.0.0", "vue-router": "4.3.0", - "vue-virtual-scroller": "v2.0.0-beta.8", - "posthog-js": "^1.158.2", - "@sentry/vue": "^8.27.0" + "vue-virtual-scroller": "v2.0.0-beta.8" }, "devDependencies": { + "@eslint/compat": "^1.1.1", + "@nuxt/eslint-config": "^0.5.6", "@tauri-apps/cli": "^2.0.0-rc", "@vitejs/plugin-vue": "^5.0.4", "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", + "eslint": "^9.9.1", "eslint-config-custom": "workspace:*", + "eslint-plugin-turbo": "^2.1.1", "postcss": "^8.4.39", "prettier": "^3.2.5", "sass": "^1.74.1", "tailwindcss": "^3.4.4", "tsconfig": "workspace:*", - "vite": "^5.2.8" + "typescript": "^5.5.4", + "vite": "^5.2.8", + "vue-tsc": "^2.1.6" }, "packageManager": "pnpm@9.4.0" } diff --git a/apps/app-frontend/src/components/ui/AccountsCard.vue b/apps/app-frontend/src/components/ui/AccountsCard.vue index 4a0a05a7b..50080490d 100644 --- a/apps/app-frontend/src/components/ui/AccountsCard.vue +++ b/apps/app-frontend/src/components/ui/AccountsCard.vue @@ -134,9 +134,9 @@ const logout = async (id) => { trackEvent('AccountLogOut') } -let showCard = ref(false) -let card = ref(null) -let button = ref(null) +const showCard = ref(false) +const card = ref(null) +const button = ref(null) const handleClickOutside = (event) => { const elements = document.elementsFromPoint(event.clientX, event.clientY) if ( diff --git a/apps/app-frontend/src/components/ui/InstanceCreationModal.vue b/apps/app-frontend/src/components/ui/InstanceCreationModal.vue index 9333a75af..c4068271c 100644 --- a/apps/app-frontend/src/components/ui/InstanceCreationModal.vue +++ b/apps/app-frontend/src/components/ui/InstanceCreationModal.vue @@ -462,7 +462,7 @@ const promises = profileOptions.value.map(async (option) => { option.name, instances.map((name) => ({ name, selected: false })), ) - } catch (error) { + } catch { // Allow failure silently } }) diff --git a/apps/app-frontend/src/components/ui/JavaSelector.vue b/apps/app-frontend/src/components/ui/JavaSelector.vue index f321f814c..1a255f21c 100644 --- a/apps/app-frontend/src/components/ui/JavaSelector.vue +++ b/apps/app-frontend/src/components/ui/JavaSelector.vue @@ -124,7 +124,7 @@ async function testJava() { } async function handleJavaFileInput() { - let filePath = await open() + const filePath = await open() if (filePath) { let result = await get_jre(filePath.path) @@ -150,7 +150,7 @@ async function autoDetect() { if (!props.compact) { detectJavaModal.value.show(props.version, props.modelValue) } else { - let versions = await find_filtered_jres(props.version).catch(handleError) + const versions = await find_filtered_jres(props.version).catch(handleError) if (versions.length > 0) { emit('update:modelValue', versions[0]) } diff --git a/apps/app-frontend/src/components/ui/SplashScreen.vue b/apps/app-frontend/src/components/ui/SplashScreen.vue index bb3ac1aa8..393899376 100644 --- a/apps/app-frontend/src/components/ui/SplashScreen.vue +++ b/apps/app-frontend/src/components/ui/SplashScreen.vue @@ -88,8 +88,6 @@ import { loading_listener } from '@/helpers/events.js' import { getCurrentWindow } from '@tauri-apps/api/window' import { XIcon } from '@modrinth/assets' import { MaximizeIcon, MinimizeIcon } from '@/assets/icons/index.js' -import { TauriEvent } from '@tauri-apps/api/event' -import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state' import { getOS } from '@/helpers/utils.js' import { useLoading } from '@/store/loading.js' diff --git a/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue b/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue index 81bdf1077..f75f7946b 100644 --- a/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue @@ -66,7 +66,7 @@ const selectedVersion = ref(null) const incompatibleModal = ref(null) const installing = ref(false) -let onInstall = ref(() => {}) +const onInstall = ref(() => {}) defineExpose({ show: (instanceVal, projectVal, projectVersions, callback) => { diff --git a/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue b/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue index 014cf63b1..9bb7ec0d2 100644 --- a/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue @@ -12,7 +12,7 @@ const project = ref() const confirmModal = ref(null) const installing = ref(false) -let onInstall = ref(() => {}) +const onInstall = ref(() => {}) defineExpose({ show: (projectVal, versionIdVal, callback) => { diff --git a/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue b/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue index e4e342dc9..212a658eb 100644 --- a/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue @@ -48,7 +48,7 @@ const shownProfiles = computed(() => return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase()) }) .filter((profile) => { - let loaders = versions.value.flatMap((v) => v.loaders) + const loaders = versions.value.flatMap((v) => v.loaders) return ( versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) && @@ -59,7 +59,7 @@ const shownProfiles = computed(() => }), ) -let onInstall = ref(() => {}) +const onInstall = ref(() => {}) defineExpose({ show: async (projectVal, versionsVal, callback) => { @@ -77,7 +77,7 @@ defineExpose({ onInstall.value = callback const profilesVal = await list().catch(handleError) - for (let profile of profilesVal) { + for (const profile of profilesVal) { profile.installing = false profile.installedMod = await check_installed(profile.path, project.value.id).catch( handleError, diff --git a/apps/app-frontend/src/helpers/utils.js b/apps/app-frontend/src/helpers/utils.js index 55dd8b646..69cd5a8c4 100644 --- a/apps/app-frontend/src/helpers/utils.js +++ b/apps/app-frontend/src/helpers/utils.js @@ -56,7 +56,7 @@ export function debounce(fn, wait) { if (timer) { clearTimeout(timer) // clear any pre-existing timer } - // eslint-disable-next-line @typescript-eslint/no-this-alias + const context = this // get the current context timer = setTimeout(() => { fn.apply(context, args) // call the function if time expires diff --git a/apps/app-frontend/src/pages/Browse.vue b/apps/app-frontend/src/pages/Browse.vue index 6b9e9f6a4..793029908 100644 --- a/apps/app-frontend/src/pages/Browse.vue +++ b/apps/app-frontend/src/pages/Browse.vue @@ -381,20 +381,20 @@ const sortedCategories = computed(() => { // identifier[0], then if it ties, identifier[1], etc async function sortByNameOrNumber(sortable, identifiers) { sortable.sort((a, b) => { - for (let identifier of identifiers) { - let aNum = parseFloat(a[identifier]) - let bNum = parseFloat(b[identifier]) + for (const identifier of identifiers) { + const aNum = parseFloat(a[identifier]) + const bNum = parseFloat(b[identifier]) if (isNaN(aNum) && isNaN(bNum)) { // Both are strings, sort alphabetically - let stringComp = a[identifier].localeCompare(b[identifier]) + const stringComp = a[identifier].localeCompare(b[identifier]) if (stringComp != 0) return stringComp } else if (!isNaN(aNum) && !isNaN(bNum)) { // Both are numbers, sort numerically - let numComp = aNum - bNum + const numComp = aNum - bNum if (numComp != 0) return numComp } else { // One is a number and one is a string, numbers go first - let numStringComp = isNaN(aNum) ? 1 : -1 + const numStringComp = isNaN(aNum) ? 1 : -1 if (numStringComp != 0) return numStringComp } } diff --git a/apps/app-frontend/src/pages/Index.vue b/apps/app-frontend/src/pages/Index.vue index dc066a522..a134463ef 100644 --- a/apps/app-frontend/src/pages/Index.vue +++ b/apps/app-frontend/src/pages/Index.vue @@ -47,7 +47,7 @@ const getInstances = async () => { return dateB - dateA }) - let filters = [] + const filters = [] for (const instance of recentInstances.value) { if (instance.linked_data && instance.linked_data.project_id) { filters.push(`NOT"project_id"="${instance.linked_data.project_id}"`) diff --git a/apps/app-frontend/src/pages/Settings.vue b/apps/app-frontend/src/pages/Settings.vue index 1e14efc08..9d717e08b 100644 --- a/apps/app-frontend/src/pages/Settings.vue +++ b/apps/app-frontend/src/pages/Settings.vue @@ -406,14 +406,14 @@ async function purgeCache() { Java settings -