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 1/5] 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() {
-
-