Devex: Faster linting (#5611)

* devex: Keep the presubmit from wiping the lint cache

* devex: typescript for eslint config

* devex: upgrade lint plugins and dedupe lockfile

* lint: Fix autofixable rules with updated vue linter

* lint: Remove default for required prop

* lint: temporarily disable warnings for missing defaults

* deps: Update vue-tsc

* lint: use the config convenience utility, switch to using projectService

* lint: Fix redundant eslint config blocks and misplaced parser

* lint: Split up parsing options for typescript vs vue files
This commit is contained in:
Alexander Brown
2025-09-16 19:23:03 -07:00
committed by GitHub
parent 6a01b08ebf
commit 6b59f839e0
22 changed files with 391 additions and 574 deletions

View File

@@ -5,13 +5,14 @@ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
import storybook from 'eslint-plugin-storybook' import storybook from 'eslint-plugin-storybook'
import unusedImports from 'eslint-plugin-unused-imports' import unusedImports from 'eslint-plugin-unused-imports'
import pluginVue from 'eslint-plugin-vue' import pluginVue from 'eslint-plugin-vue'
import { defineConfig } from 'eslint/config'
import globals from 'globals' import globals from 'globals'
import tseslint from 'typescript-eslint' import tseslint from 'typescript-eslint'
import vueParser from 'vue-eslint-parser'
export default [ const extraFileExtensions = ['.vue']
{
files: ['src/**/*.{js,mjs,cjs,ts,vue}'] export default defineConfig([
},
{ {
ignores: [ ignores: [
'src/scripts/*', 'src/scripts/*',
@@ -24,35 +25,49 @@ export default [
] ]
}, },
{ {
files: ['./**/*.{ts,mts}'],
languageOptions: { languageOptions: {
globals: { globals: {
...globals.browser, ...globals.browser,
__COMFYUI_FRONTEND_VERSION__: 'readonly' __COMFYUI_FRONTEND_VERSION__: 'readonly'
}, },
parser: tseslint.parser,
parserOptions: { parserOptions: {
project: ['./tsconfig.json', './tsconfig.eslint.json'], parser: tseslint.parser,
projectService: true,
tsConfigRootDir: import.meta.dirname,
ecmaVersion: 2020, ecmaVersion: 2020,
sourceType: 'module', sourceType: 'module',
extraFileExtensions: ['.vue'] extraFileExtensions
}
}
},
{
files: ['./**/*.vue'],
languageOptions: {
globals: {
...globals.browser,
__COMFYUI_FRONTEND_VERSION__: 'readonly'
},
parser: vueParser,
parserOptions: {
parser: tseslint.parser,
projectService: true,
tsConfigRootDir: import.meta.dirname,
ecmaVersion: 2020,
sourceType: 'module',
extraFileExtensions
} }
} }
}, },
pluginJs.configs.recommended, pluginJs.configs.recommended,
...tseslint.configs.recommended, tseslint.configs.recommended,
...pluginVue.configs['flat/recommended'], pluginVue.configs['flat/recommended'],
eslintPluginPrettierRecommended, eslintPluginPrettierRecommended,
{ storybook.configs['flat/recommended'],
files: ['src/**/*.vue'],
languageOptions: {
parserOptions: {
parser: tseslint.parser
}
}
},
{ {
plugins: { plugins: {
'unused-imports': unusedImports, 'unused-imports': unusedImports,
// @ts-expect-error Bad types in the plugin
'@intlify/vue-i18n': pluginI18n '@intlify/vue-i18n': pluginI18n
}, },
rules: { rules: {
@@ -67,6 +82,7 @@ export default [
'vue/multi-word-component-names': 'off', // TODO: fix 'vue/multi-word-component-names': 'off', // TODO: fix
'vue/no-template-shadow': 'off', // TODO: fix 'vue/no-template-shadow': 'off', // TODO: fix
'vue/one-component-per-file': 'off', // TODO: fix 'vue/one-component-per-file': 'off', // TODO: fix
'vue/require-default-prop': 'off', // TODO: fix -- this one is very worthwhile
// Restrict deprecated PrimeVue components // Restrict deprecated PrimeVue components
'no-restricted-imports': [ 'no-restricted-imports': [
'error', 'error',
@@ -135,6 +151,5 @@ export default [
} }
] ]
} }
}, }
...storybook.configs['flat/recommended'] ])
]

View File

@@ -3,13 +3,13 @@ export default {
'./**/*.{ts,tsx,vue,mts}': (stagedFiles) => [ './**/*.{ts,tsx,vue,mts}': (stagedFiles) => [
...formatAndEslint(stagedFiles), ...formatAndEslint(stagedFiles),
'vue-tsc --noEmit' 'pnpm typecheck'
] ]
} }
function formatAndEslint(fileNames) { function formatAndEslint(fileNames) {
return [ return [
`eslint --fix ${fileNames.join(' ')}`, `pnpm exec eslint --cache --fix ${fileNames.join(' ')}`,
`prettier --write ${fileNames.join(' ')}` `pnpm exec prettier --cache --write ${fileNames.join(' ')}`
] ]
} }

View File

@@ -38,10 +38,10 @@
"build-storybook": "storybook build" "build-storybook": "storybook build"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.8.0", "@eslint/js": "^9.35.0",
"@iconify-json/lucide": "^1.2.66", "@iconify-json/lucide": "^1.2.66",
"@iconify/tailwind": "^1.2.0", "@iconify/tailwind": "^1.2.0",
"@intlify/eslint-plugin-vue-i18n": "^3.2.0", "@intlify/eslint-plugin-vue-i18n": "^4.1.0",
"@lobehub/i18n-cli": "^1.25.1", "@lobehub/i18n-cli": "^1.25.1",
"@nx/eslint": "21.4.1", "@nx/eslint": "21.4.1",
"@nx/playwright": "21.4.1", "@nx/playwright": "21.4.1",
@@ -64,11 +64,11 @@
"@vitest/ui": "^3.0.0", "@vitest/ui": "^3.0.0",
"@vue/test-utils": "^2.4.6", "@vue/test-utils": "^2.4.6",
"eslint": "^9.34.0", "eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.2", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.2.6", "eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-storybook": "^9.1.1", "eslint-plugin-storybook": "^9.1.6",
"eslint-plugin-unused-imports": "^4.1.4", "eslint-plugin-unused-imports": "^4.2.0",
"eslint-plugin-vue": "^9.27.0", "eslint-plugin-vue": "^10.4.0",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"globals": "^15.9.0", "globals": "^15.9.0",
"happy-dom": "^15.11.0", "happy-dom": "^15.11.0",
@@ -79,13 +79,13 @@
"lint-staged": "^15.2.7", "lint-staged": "^15.2.7",
"nx": "21.4.1", "nx": "21.4.1",
"prettier": "^3.3.2", "prettier": "^3.3.2",
"storybook": "^9.1.1", "storybook": "^9.1.6",
"tailwindcss": "^4.1.12", "tailwindcss": "^4.1.12",
"tailwindcss-primeui": "^0.6.1", "tailwindcss-primeui": "^0.6.1",
"tsx": "^4.15.6", "tsx": "^4.15.6",
"tw-animate-css": "^1.3.8", "tw-animate-css": "^1.3.8",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"typescript-eslint": "^8.42.0", "typescript-eslint": "^8.44.0",
"unplugin-icons": "^0.22.0", "unplugin-icons": "^0.22.0",
"unplugin-vue-components": "^0.28.0", "unplugin-vue-components": "^0.28.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
@@ -94,7 +94,8 @@
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",
"vite-plugin-vue-devtools": "^7.7.6", "vite-plugin-vue-devtools": "^7.7.6",
"vitest": "^3.2.4", "vitest": "^3.2.4",
"vue-tsc": "^2.1.10", "vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.0.7",
"zip-dir": "^2.0.0", "zip-dir": "^2.0.0",
"zod-to-json-schema": "^3.24.1" "zod-to-json-schema": "^3.24.1"
}, },

810
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -46,7 +46,7 @@ const hasSelection = ref(false)
const isHovered = useElementHover(rootEl) const isHovered = useElementHover(rootEl)
const terminalData = useTerminal(terminalEl) const terminalData = useTerminal(terminalEl)
emit('created', terminalData, rootEl) emit('created', terminalData, ref(rootEl))
const { terminal } = terminalData const { terminal } = terminalData
let selectionDisposable: IDisposable | undefined let selectionDisposable: IDisposable | undefined

View File

@@ -7,7 +7,7 @@
<InputText <InputText
v-else v-else
ref="inputRef" ref="inputRef"
v-model:modelValue="inputValue" v-model:model-value="inputValue"
v-focus v-focus
type="text" type="text"
size="small" size="small"

View File

@@ -21,7 +21,7 @@
<component <component
:is="markRaw(getFormComponent(props.item))" :is="markRaw(getFormComponent(props.item))"
:id="props.id" :id="props.id"
v-model:modelValue="formValue" v-model:model-value="formValue"
:aria-labelledby="`${props.id}-label`" :aria-labelledby="`${props.id}-label`"
v-bind="getFormAttrs(props.item)" v-bind="getFormAttrs(props.item)"
/> />

View File

@@ -1,7 +1,7 @@
<template> <template>
<Tree <Tree
v-model:expandedKeys="expandedKeys" v-model:expanded-keys="expandedKeys"
v-model:selectionKeys="selectionKeys" v-model:selection-keys="selectionKeys"
class="tree-explorer py-0 px-2 2xl:px-4" class="tree-explorer py-0 px-2 2xl:px-4"
:class="props.class" :class="props.class"
:value="renderedRoot.children" :value="renderedRoot.children"

View File

@@ -16,7 +16,7 @@
<div class="flex flex-1 relative overflow-hidden"> <div class="flex flex-1 relative overflow-hidden">
<ManagerNavSidebar <ManagerNavSidebar
v-if="isSideNavOpen" v-if="isSideNavOpen"
v-model:selectedTab="selectedTab" v-model:selected-tab="selectedTab"
:tabs="tabs" :tabs="tabs"
/> />
<div <div
@@ -57,9 +57,9 @@
</IconButton> </IconButton>
</div> </div>
<RegistrySearchBar <RegistrySearchBar
v-model:searchQuery="searchQuery" v-model:search-query="searchQuery"
v-model:searchMode="searchMode" v-model:search-mode="searchMode"
v-model:sortField="sortField" v-model:sort-field="sortField"
:search-results="searchResults" :search-results="searchResults"
:suggestions="suggestions" :suggestions="suggestions"
:is-missing-tab="isMissingTab" :is-missing-tab="isMissingTab"

View File

@@ -8,7 +8,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const { value = 'N/A', label = 'N/A' } = defineProps<{ const { value = 'N/A', label } = defineProps<{
label: string label: string
value?: string | number value?: string | number
}>() }>()

View File

@@ -41,12 +41,12 @@
<div class="flex mt-3 text-sm"> <div class="flex mt-3 text-sm">
<div class="flex gap-6 ml-1"> <div class="flex gap-6 ml-1">
<SearchFilterDropdown <SearchFilterDropdown
v-model:modelValue="searchMode" v-model:model-value="searchMode"
:options="filterOptions" :options="filterOptions"
:label="$t('g.filter')" :label="$t('g.filter')"
/> />
<SearchFilterDropdown <SearchFilterDropdown
v-model:modelValue="sortField" v-model:model-value="sortField"
:options="availableSortOptions" :options="availableSortOptions"
:label="$t('g.sort')" :label="$t('g.sort')"
/> />

View File

@@ -4,7 +4,7 @@
class="px-4 py-2 flex items-center" class="px-4 py-2 flex items-center"
> >
<TabMenu <TabMenu
v-model:activeIndex="activeTabIndex" v-model:active-index="activeTabIndex"
:model="tabs" :model="tabs"
class="w-full border-none" class="w-full border-none"
:pt="{ :pt="{

View File

@@ -59,7 +59,7 @@ import ResponseBlurb from '@/components/graph/widgets/chatHistory/ResponseBlurb.
import { ComponentWidget } from '@/scripts/domWidget' import { ComponentWidget } from '@/scripts/domWidget'
import { linkifyHtml, nl2br } from '@/utils/formatUtil' import { linkifyHtml, nl2br } from '@/utils/formatUtil'
const { widget, history = '[]' } = defineProps<{ const { widget, history } = defineProps<{
widget?: ComponentWidget<string> widget?: ComponentWidget<string>
history: string history: string
}>() }>()

View File

@@ -21,7 +21,7 @@
</template> </template>
<template #header> <template #header>
<SearchBox <SearchBox
v-model:modelValue="searchQuery" v-model:model-value="searchQuery"
class="model-lib-search-box p-2 2xl:p-4" class="model-lib-search-box p-2 2xl:p-4"
:placeholder="$t('g.searchModels') + '...'" :placeholder="$t('g.searchModels') + '...'"
@search="handleSearch" @search="handleSearch"
@@ -31,7 +31,7 @@
<ElectronDownloadItems v-if="isElectron()" /> <ElectronDownloadItems v-if="isElectron()" />
<TreeExplorer <TreeExplorer
v-model:expandedKeys="expandedKeys" v-model:expanded-keys="expandedKeys"
class="model-lib-tree-explorer" class="model-lib-tree-explorer"
:root="renderedRoot" :root="renderedRoot"
> >

View File

@@ -78,7 +78,7 @@
<template #header> <template #header>
<div> <div>
<SearchBox <SearchBox
v-model:modelValue="searchQuery" v-model:model-value="searchQuery"
class="node-lib-search-box p-2 2xl:p-4" class="node-lib-search-box p-2 2xl:p-4"
:placeholder="$t('g.searchNodes') + '...'" :placeholder="$t('g.searchNodes') + '...'"
filter-icon="pi pi-filter" filter-icon="pi pi-filter"
@@ -106,7 +106,7 @@
class="m-2" class="m-2"
/> />
<TreeExplorer <TreeExplorer
v-model:expandedKeys="expandedKeys" v-model:expanded-keys="expandedKeys"
class="node-lib-tree-explorer" class="node-lib-tree-explorer"
:root="renderedRoot" :root="renderedRoot"
> >

View File

@@ -86,7 +86,7 @@
<ConfirmPopup /> <ConfirmPopup />
<ContextMenu ref="menu" :model="menuItems" /> <ContextMenu ref="menu" :model="menuItems" />
<ResultGallery <ResultGallery
v-model:activeIndex="galleryActiveIndex" v-model:active-index="galleryActiveIndex"
:all-gallery-items="allGalleryItems" :all-gallery-items="allGalleryItems"
/> />
</template> </template>

View File

@@ -14,7 +14,7 @@
</template> </template>
<template #header> <template #header>
<SearchBox <SearchBox
v-model:modelValue="searchQuery" v-model:model-value="searchQuery"
class="workflows-search-box p-2 2xl:p-4" class="workflows-search-box p-2 2xl:p-4"
:placeholder="$t('g.searchWorkflows') + '...'" :placeholder="$t('g.searchWorkflows') + '...'"
@search="handleSearch" @search="handleSearch"
@@ -32,7 +32,7 @@
class="ml-2" class="ml-2"
/> />
<TreeExplorer <TreeExplorer
v-model:expandedKeys="dummyExpandedKeys" v-model:expanded-keys="dummyExpandedKeys"
:root="renderTreeNode(openWorkflowsTree, WorkflowTreeType.Open)" :root="renderTreeNode(openWorkflowsTree, WorkflowTreeType.Open)"
:selection-keys="selectionKeys" :selection-keys="selectionKeys"
> >
@@ -74,7 +74,7 @@
class="ml-2" class="ml-2"
/> />
<TreeExplorer <TreeExplorer
v-model:expandedKeys="dummyExpandedKeys" v-model:expanded-keys="dummyExpandedKeys"
:root=" :root="
renderTreeNode( renderTreeNode(
bookmarkedWorkflowsTree, bookmarkedWorkflowsTree,
@@ -96,7 +96,7 @@
/> />
<TreeExplorer <TreeExplorer
v-if="workflowStore.persistedWorkflows.length > 0" v-if="workflowStore.persistedWorkflows.length > 0"
v-model:expandedKeys="expandedKeys" v-model:expanded-keys="expandedKeys"
:root="renderTreeNode(workflowsTree, WorkflowTreeType.Browse)" :root="renderTreeNode(workflowsTree, WorkflowTreeType.Browse)"
:selection-keys="selectionKeys" :selection-keys="selectionKeys"
> >
@@ -114,7 +114,7 @@
</div> </div>
<div v-else class="comfyui-workflows-search-panel"> <div v-else class="comfyui-workflows-search-panel">
<TreeExplorer <TreeExplorer
v-model:expandedKeys="expandedKeys" v-model:expanded-keys="expandedKeys"
:root="renderTreeNode(filteredRoot, WorkflowTreeType.Browse)" :root="renderTreeNode(filteredRoot, WorkflowTreeType.Browse)"
> >
<template #node="{ node }"> <template #node="{ node }">

View File

@@ -54,7 +54,7 @@
<div v-for="item in items" :key="item.name" class="mb-4"> <div v-for="item in items" :key="item.name" class="mb-4">
<FormItem <FormItem
:id="item.id" :id="item.id"
v-model:formValue="item.value" v-model:form-value="item.value"
:item="translateItem(item)" :item="translateItem(item)"
:label-class="{ :label-class="{
'text-highlight': item.initialValue !== item.value 'text-highlight': item.initialValue !== item.value

View File

@@ -2,7 +2,7 @@
<div class="settings-container"> <div class="settings-container">
<ScrollPanel class="settings-sidebar shrink-0 p-2 w-48 2xl:w-64"> <ScrollPanel class="settings-sidebar shrink-0 p-2 w-48 2xl:w-64">
<SearchBox <SearchBox
v-model:modelValue="searchQuery" v-model:model-value="searchQuery"
class="settings-search-box w-full mb-2" class="settings-search-box w-full mb-2"
:placeholder="$t('g.searchSettings') + '...'" :placeholder="$t('g.searchSettings') + '...'"
:debounce-time="128" :debounce-time="128"

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<Galleria <Galleria
v-model:activeIndex="activeIndex" v-model:active-index="activeIndex"
:value="galleryImages" :value="galleryImages"
v-bind="filteredProps" v-bind="filteredProps"
:show-thumbnails="showThumbnails" :show-thumbnails="showThumbnails"

View File

@@ -37,8 +37,8 @@
</StepPanel> </StepPanel>
<StepPanel v-slot="{ activateCallback }" value="1"> <StepPanel v-slot="{ activateCallback }" value="1">
<InstallLocationPicker <InstallLocationPicker
v-model:installPath="installPath" v-model:install-path="installPath"
v-model:pathError="pathError" v-model:path-error="pathError"
/> />
<div class="flex pt-6 justify-between"> <div class="flex pt-6 justify-between">
<Button <Button
@@ -58,8 +58,8 @@
</StepPanel> </StepPanel>
<StepPanel v-slot="{ activateCallback }" value="2"> <StepPanel v-slot="{ activateCallback }" value="2">
<MigrationPicker <MigrationPicker
v-model:sourcePath="migrationSourcePath" v-model:source-path="migrationSourcePath"
v-model:migrationItemIds="migrationItemIds" v-model:migration-item-ids="migrationItemIds"
/> />
<div class="flex pt-6 justify-between"> <div class="flex pt-6 justify-between">
<Button <Button
@@ -78,13 +78,13 @@
</StepPanel> </StepPanel>
<StepPanel v-slot="{ activateCallback }" value="3"> <StepPanel v-slot="{ activateCallback }" value="3">
<DesktopSettingsConfiguration <DesktopSettingsConfiguration
v-model:autoUpdate="autoUpdate" v-model:auto-update="autoUpdate"
v-model:allowMetrics="allowMetrics" v-model:allow-metrics="allowMetrics"
/> />
<MirrorsConfiguration <MirrorsConfiguration
v-model:pythonMirror="pythonMirror" v-model:python-mirror="pythonMirror"
v-model:pypiMirror="pypiMirror" v-model:pypi-mirror="pypiMirror"
v-model:torchMirror="torchMirror" v-model:torch-mirror="torchMirror"
:device="device" :device="device"
class="mt-6" class="mt-6"
/> />

View File

@@ -33,6 +33,7 @@
"src/types/**/*.d.ts", "src/types/**/*.d.ts",
"tests-ui/**/*", "tests-ui/**/*",
"global.d.ts", "global.d.ts",
"eslint.config.ts",
"vite.config.mts", "vite.config.mts",
".storybook/**/*" ".storybook/**/*"
] ]