mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-06 08:00:05 +00:00
refactor: address PR review comments for semver migration
- Use named imports instead of wildcard imports for semver - Add memoized computed properties for coerced versions - Implement explicit Git hash detection function - Standardize coercion fallback pattern with helper functions - Create versionUtil.ts with reusable version validation functions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
Christian Byrne
parent
9da2a5b6de
commit
b92369493a
@@ -44,11 +44,12 @@
|
||||
<script setup lang="ts">
|
||||
import { whenever } from '@vueuse/core'
|
||||
import Message from 'primevue/message'
|
||||
import * as semver from 'semver'
|
||||
import { rcompare } from 'semver'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import { coerceVersion } from '@/utils/versionUtil'
|
||||
|
||||
const props = defineProps<{
|
||||
missingCoreNodes: Record<string, LGraphNode[]>
|
||||
@@ -78,10 +79,10 @@ whenever(
|
||||
const sortedMissingCoreNodes = computed(() => {
|
||||
return Object.entries(props.missingCoreNodes).sort(([a], [b]) => {
|
||||
// Sort by version in descending order (newest first)
|
||||
const versionA = semver.coerce(a)
|
||||
const versionB = semver.coerce(b)
|
||||
const versionA = coerceVersion(a, '')
|
||||
const versionB = coerceVersion(b, '')
|
||||
if (!versionA || !versionB) return 0
|
||||
return semver.rcompare(versionA, versionB)
|
||||
return rcompare(versionA, versionB)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import Popover from 'primevue/popover'
|
||||
import * as semver from 'semver'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import PackVersionSelectorPopover from '@/components/dialog/content/manager/PackVersionSelectorPopover.vue'
|
||||
@@ -45,6 +44,7 @@ import { usePackUpdateStatus } from '@/composables/nodePack/usePackUpdateStatus'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import { SelectedVersion } from '@/types/comfyManagerTypes'
|
||||
import { components } from '@/types/comfyRegistryTypes'
|
||||
import { isValidSemver } from '@/utils/versionUtil'
|
||||
|
||||
const TRUNCATED_HASH_LENGTH = 7
|
||||
|
||||
@@ -70,8 +70,7 @@ const installedVersion = computed(() => {
|
||||
nodePack.latest_version?.version ??
|
||||
SelectedVersion.NIGHTLY
|
||||
|
||||
// If Git hash, truncate to 7 characters
|
||||
return semver.valid(version)
|
||||
return isValidSemver(version)
|
||||
? version
|
||||
: version.slice(0, TRUNCATED_HASH_LENGTH)
|
||||
})
|
||||
|
||||
@@ -62,7 +62,6 @@ import { whenever } from '@vueuse/core'
|
||||
import Button from 'primevue/button'
|
||||
import Listbox from 'primevue/listbox'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import * as semver from 'semver'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -76,6 +75,7 @@ import {
|
||||
SelectedVersion
|
||||
} from '@/types/comfyManagerTypes'
|
||||
import { components } from '@/types/comfyRegistryTypes'
|
||||
import { isValidSemver } from '@/utils/versionUtil'
|
||||
|
||||
const { nodePack } = defineProps<{
|
||||
nodePack: components['schemas']['Node']
|
||||
@@ -95,9 +95,9 @@ const isQueueing = ref(false)
|
||||
const selectedVersion = ref<string>(SelectedVersion.LATEST)
|
||||
onMounted(() => {
|
||||
const initialVersion = getInitialSelectedVersion() ?? SelectedVersion.LATEST
|
||||
selectedVersion.value =
|
||||
// Use NIGHTLY when version is a Git hash
|
||||
semver.valid(initialVersion) ? initialVersion : SelectedVersion.NIGHTLY
|
||||
selectedVersion.value = isValidSemver(initialVersion)
|
||||
? initialVersion
|
||||
: SelectedVersion.NIGHTLY
|
||||
})
|
||||
|
||||
const getInitialSelectedVersion = () => {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as semver from 'semver'
|
||||
import { gt } from 'semver'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { coerceVersion, isNightlyVersion } from '@/utils/versionUtil'
|
||||
|
||||
export const usePackUpdateStatus = (
|
||||
nodePack: components['schemas']['Node']
|
||||
@@ -16,17 +17,25 @@ export const usePackUpdateStatus = (
|
||||
const latestVersion = computed(() => nodePack.latest_version?.version)
|
||||
|
||||
const isNightlyPack = computed(
|
||||
() => !!installedVersion.value && !semver.valid(installedVersion.value)
|
||||
() => !!installedVersion.value && isNightlyVersion(installedVersion.value)
|
||||
)
|
||||
|
||||
const coercedInstalledVersion = computed(() =>
|
||||
installedVersion.value ? coerceVersion(installedVersion.value) : null
|
||||
)
|
||||
|
||||
const coercedLatestVersion = computed(() =>
|
||||
latestVersion.value ? coerceVersion(latestVersion.value) : null
|
||||
)
|
||||
|
||||
const isUpdateAvailable = computed(() => {
|
||||
if (!isInstalled.value || isNightlyPack.value || !latestVersion.value) {
|
||||
return false
|
||||
}
|
||||
const installedSemver = semver.coerce(installedVersion.value)
|
||||
const latestSemver = semver.coerce(latestVersion.value)
|
||||
if (!installedSemver || !latestSemver) return false
|
||||
return semver.gt(latestSemver, installedSemver)
|
||||
if (!coercedInstalledVersion.value || !coercedLatestVersion.value) {
|
||||
return false
|
||||
}
|
||||
return gt(coercedLatestVersion.value, coercedInstalledVersion.value)
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import * as semver from 'semver'
|
||||
import { eq, gt } from 'semver'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { type ReleaseNote, useReleaseService } from '@/services/releaseService'
|
||||
@@ -7,6 +7,7 @@ import { useSettingStore } from '@/stores/settingStore'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
import { stringToLocale } from '@/utils/formatUtil'
|
||||
import { coerceVersion } from '@/utils/versionUtil'
|
||||
|
||||
// Store for managing release notes
|
||||
export const useReleaseStore = defineStore('release', () => {
|
||||
@@ -20,11 +21,18 @@ export const useReleaseStore = defineStore('release', () => {
|
||||
const systemStatsStore = useSystemStatsStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
// Current ComfyUI version
|
||||
const currentComfyUIVersion = computed(
|
||||
() => systemStatsStore?.systemStats?.system?.comfyui_version ?? ''
|
||||
)
|
||||
|
||||
const coercedCurrentVersion = computed(() =>
|
||||
coerceVersion(currentComfyUIVersion.value)
|
||||
)
|
||||
|
||||
const coercedRecentReleaseVersion = computed(() =>
|
||||
recentRelease.value ? coerceVersion(recentRelease.value.version) : '0.0.0'
|
||||
)
|
||||
|
||||
// Release data from settings
|
||||
const locale = computed(() => settingStore.get('Comfy.Locale'))
|
||||
const releaseVersion = computed(() =>
|
||||
@@ -55,19 +63,13 @@ export const useReleaseStore = defineStore('release', () => {
|
||||
const isNewVersionAvailable = computed(
|
||||
() =>
|
||||
!!recentRelease.value &&
|
||||
semver.gt(
|
||||
semver.coerce(recentRelease.value.version) || '0.0.0',
|
||||
semver.coerce(currentComfyUIVersion.value) || '0.0.0'
|
||||
)
|
||||
gt(coercedRecentReleaseVersion.value, coercedCurrentVersion.value)
|
||||
)
|
||||
|
||||
const isLatestVersion = computed(
|
||||
() =>
|
||||
!!recentRelease.value &&
|
||||
semver.eq(
|
||||
semver.coerce(recentRelease.value.version) || '0.0.0',
|
||||
semver.coerce(currentComfyUIVersion.value) || '0.0.0'
|
||||
)
|
||||
eq(coercedRecentReleaseVersion.value, coercedCurrentVersion.value)
|
||||
)
|
||||
|
||||
const hasMediumOrHighAttention = computed(() =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash'
|
||||
import { defineStore } from 'pinia'
|
||||
import * as semver from 'semver'
|
||||
import { gte, rcompare, valid } from 'semver'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import type { Settings } from '@/schemas/apiSchema'
|
||||
@@ -8,6 +8,7 @@ import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
import type { SettingParams } from '@/types/settingTypes'
|
||||
import type { TreeNode } from '@/types/treeExplorerTypes'
|
||||
import { coerceVersion } from '@/utils/versionUtil'
|
||||
|
||||
export const getSettingInfo = (setting: SettingParams) => {
|
||||
const parts = setting.category || setting.id.split('.')
|
||||
@@ -133,24 +134,24 @@ export const useSettingStore = defineStore('setting', () => {
|
||||
if (installedVersion) {
|
||||
const sortedVersions = Object.keys(defaultsByInstallVersion).sort(
|
||||
(a, b) => {
|
||||
const versionA = semver.coerce(a)
|
||||
const versionB = semver.coerce(b)
|
||||
const versionA = coerceVersion(a, '')
|
||||
const versionB = coerceVersion(b, '')
|
||||
if (!versionA || !versionB) return 0
|
||||
return semver.rcompare(versionA, versionB)
|
||||
return rcompare(versionA, versionB)
|
||||
}
|
||||
)
|
||||
|
||||
for (const version of sortedVersions) {
|
||||
if (!semver.valid(version)) {
|
||||
if (!valid(version)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const installedSemver = semver.coerce(installedVersion)
|
||||
const targetSemver = semver.coerce(version)
|
||||
const installedSemver = coerceVersion(installedVersion, '')
|
||||
const targetSemver = coerceVersion(version, '')
|
||||
if (
|
||||
installedSemver &&
|
||||
targetSemver &&
|
||||
semver.gte(installedSemver, targetSemver)
|
||||
gte(installedSemver, targetSemver)
|
||||
) {
|
||||
const versionedDefault =
|
||||
defaultsByInstallVersion[
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { defineStore } from 'pinia'
|
||||
import * as semver from 'semver'
|
||||
import { gt } from 'semver'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import config from '@/config'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import { isValidSemver } from '@/utils/versionUtil'
|
||||
|
||||
const DISMISSAL_DURATION_MS = 7 * 24 * 60 * 60 * 1000 // 7 days
|
||||
|
||||
@@ -26,13 +27,13 @@ export const useVersionCompatibilityStore = defineStore(
|
||||
if (
|
||||
!frontendVersion.value ||
|
||||
!requiredFrontendVersion.value ||
|
||||
!semver.valid(frontendVersion.value) ||
|
||||
!semver.valid(requiredFrontendVersion.value)
|
||||
!isValidSemver(frontendVersion.value) ||
|
||||
!isValidSemver(requiredFrontendVersion.value)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
// Returns true if required version is greater than frontend version
|
||||
return semver.gt(requiredFrontendVersion.value, frontendVersion.value)
|
||||
return gt(requiredFrontendVersion.value, frontendVersion.value)
|
||||
})
|
||||
|
||||
const isFrontendNewer = computed(() => {
|
||||
|
||||
52
src/utils/versionUtil.ts
Normal file
52
src/utils/versionUtil.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { coerce, valid } from 'semver'
|
||||
|
||||
/**
|
||||
* Validates if a string is a valid semantic version.
|
||||
* @param version The version string to validate
|
||||
* @returns true if the version is a valid semver, false otherwise
|
||||
*/
|
||||
export function isValidSemver(version: string): boolean {
|
||||
return !!valid(version)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a version string is a Git hash (not a semantic version).
|
||||
* Git hashes are typically 7-40 characters of hexadecimal.
|
||||
* @param version The version string to check
|
||||
* @returns true if the version appears to be a Git hash, false otherwise
|
||||
*/
|
||||
export function isGitHash(version: string): boolean {
|
||||
// Check if it's NOT a valid semver and matches git hash pattern
|
||||
if (isValidSemver(version)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Git hash pattern: 7-40 hexadecimal characters
|
||||
const gitHashPattern = /^[0-9a-f]{7,40}$/i
|
||||
return gitHashPattern.test(version)
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerces a version string to a valid semver version with a fallback.
|
||||
* @param version The version string to coerce
|
||||
* @param fallback The fallback version if coercion fails (default: '0.0.0')
|
||||
* @returns A valid semver version string
|
||||
*/
|
||||
export function coerceVersion(
|
||||
version: string | undefined,
|
||||
fallback = '0.0.0'
|
||||
): string {
|
||||
if (!version) return fallback
|
||||
const coerced = coerce(version)
|
||||
return coerced ? coerced.version : fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a version string represents a nightly/development version.
|
||||
* This includes Git hashes and any non-semver version strings.
|
||||
* @param version The version string to check
|
||||
* @returns true if the version is a nightly/development version
|
||||
*/
|
||||
export function isNightlyVersion(version: string): boolean {
|
||||
return !isValidSemver(version)
|
||||
}
|
||||
Reference in New Issue
Block a user