mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Add distribution detection pattern (#6028)
## Summary
Establishes distribution-specific code pattern using compile-time
constants and dead code elimination. Demonstrates with Help Center by
hiding extension manager and update buttons in cloud distribution.
Below commentary makes assumption that minifcation and tree-shaking is
enabled (which isn't true yet, but will be eventually).
## Changes
- **What**: Added `src/platform/distribution/types.ts` with distribution
detection via `__DISTRIBUTION__` variable
- **Build**: Vite replaces `__DISTRIBUTION__` at build time using
environment variables
- **Tree-shaking**: All code not relevant to target distribution is
DCR'd and eliminated from bundle
- **Example**: Help Center hides "Manager Extension" menu item and
"Update" buttons in cloud builds
## Pattern
This PR defines a `__DISTRIBUTION__` variable which gets replaced at
build time by Vite using environment variables. All code not relevant to
the given distribution is then DCR'd and tree-shaken.
For simple cases (like this Help Center PR), import `isCloud` and use
compile-time conditionals:
```typescript
import { isCloud } from '@/platform/distribution/types'
if (!isCloud) {
items.push({
key: 'manager',
action: async () => {
await useManagerState().openManager({ ... })
}
})
}
```
The code is DCR'd at build time so there's zero runtime overhead - we
don't even incur the `if (isCloud)` cost because Terser eliminates it.
For complex services later, we'll add interfaces and use an index.ts
that exports different implementations under the same alias per
distribution. It will resemble a DI container but simpler since we don't
need runtime discovery like backend devs do. This guarantees types and
makes testing easier.
Example for services:
```typescript
// src/platform/storage/index.ts
import { isCloud } from '@/platform/distribution/types'
if (isCloud) {
export { CloudStorage as StorageService } from './cloud'
} else {
export { LocalStorage as StorageService } from './local'
}
```
Example for component variants:
```typescript
// src/components/downloads/index.ts
import { isCloud } from '@/platform/distribution/types'
if (isCloud) {
export { default as DownloadButton } from './DownloadButton.cloud.vue'
} else {
export { default as DownloadButton } from './DownloadButton.desktop.vue'
}
```
## Implementation Details
Distribution types (`src/platform/distribution/types.ts`):
```typescript
type Distribution = 'desktop' | 'localhost' | 'cloud'
declare global {
const __DISTRIBUTION__: Distribution
}
const DISTRIBUTION: Distribution = __DISTRIBUTION__
export const isCloud = DISTRIBUTION === 'cloud'
```
Vite configuration adds the define:
```typescript
const DISTRIBUTION = (process.env.DISTRIBUTION || 'localhost') as
| 'desktop'
| 'localhost'
| 'cloud'
export default defineConfig({
define: {
__DISTRIBUTION__: JSON.stringify(DISTRIBUTION)
}
})
```
## Build Commands
```bash
pnpm build # localhost (default)
DISTRIBUTION=cloud pnpm build # cloud
DISTRIBUTION=desktop pnpm build # desktop
```
## Future Applications
This pattern can be used with auth or telemetry services - which will
guarantee all the telemetry code, for example, is not even in the code
distributed in OSS Comfy whatsoever while still being able to develop
off `main`.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6028-Add-distribution-detection-pattern-28a6d73d365081b08767d395472cd1bc)
by [Unito](https://www.unito.io)
This commit is contained in:
@@ -135,6 +135,7 @@ import type { CSSProperties, Component } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import PuzzleIcon from '@/components/icons/PuzzleIcon.vue'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import type { ReleaseNote } from '@/platform/updates/common/releaseService'
|
||||
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
|
||||
@@ -265,7 +266,7 @@ const moreMenuItem = computed(() =>
|
||||
)
|
||||
|
||||
const menuItems = computed<MenuItem[]>(() => {
|
||||
return [
|
||||
const items: MenuItem[] = [
|
||||
{
|
||||
key: 'docs',
|
||||
type: 'item',
|
||||
@@ -305,8 +306,12 @@ const menuItems = computed<MenuItem[]>(() => {
|
||||
void commandStore.execute('Comfy.ContactSupport')
|
||||
emit('close')
|
||||
}
|
||||
},
|
||||
{
|
||||
}
|
||||
]
|
||||
|
||||
// Extension manager - only in non-cloud distributions
|
||||
if (!isCloud) {
|
||||
items.push({
|
||||
key: 'manager',
|
||||
type: 'item',
|
||||
icon: PuzzleIcon,
|
||||
@@ -319,17 +324,20 @@ const menuItems = computed<MenuItem[]>(() => {
|
||||
})
|
||||
emit('close')
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'more',
|
||||
type: 'item',
|
||||
icon: '',
|
||||
label: t('helpCenter.more'),
|
||||
visible: hasVisibleMoreItems.value,
|
||||
action: () => {}, // No action for more item
|
||||
items: moreItems.value
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
items.push({
|
||||
key: 'more',
|
||||
type: 'item',
|
||||
icon: '',
|
||||
label: t('helpCenter.more'),
|
||||
visible: hasVisibleMoreItems.value,
|
||||
action: () => {}, // No action for more item
|
||||
items: moreItems.value
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// Utility Functions
|
||||
@@ -420,6 +428,9 @@ const formatReleaseDate = (dateString?: string): string => {
|
||||
}
|
||||
|
||||
const shouldShowUpdateButton = (release: ReleaseNote): boolean => {
|
||||
// Hide update buttons in cloud distribution
|
||||
if (isCloud) return false
|
||||
|
||||
return (
|
||||
releaseStore.shouldShowUpdateButton &&
|
||||
release === releaseStore.recentReleases[0]
|
||||
|
||||
18
src/platform/distribution/types.ts
Normal file
18
src/platform/distribution/types.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Distribution types and compile-time constants for managing
|
||||
* multi-distribution builds (Desktop, Localhost, Cloud)
|
||||
*/
|
||||
|
||||
type Distribution = 'desktop' | 'localhost' | 'cloud'
|
||||
|
||||
declare global {
|
||||
const __DISTRIBUTION__: Distribution
|
||||
}
|
||||
|
||||
/** Current distribution - replaced at compile time */
|
||||
const DISTRIBUTION: Distribution = __DISTRIBUTION__
|
||||
|
||||
/** Distribution type checks */
|
||||
// const isDesktop = DISTRIBUTION === 'desktop'
|
||||
// const isLocalhost = DISTRIBUTION === 'localhost'
|
||||
export const isCloud = DISTRIBUTION === 'cloud'
|
||||
@@ -58,6 +58,7 @@
|
||||
"src/types/**/*.d.ts",
|
||||
"tailwind.config.ts",
|
||||
"tests-ui/**/*",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
// "vitest.setup.ts",
|
||||
]
|
||||
|
||||
@@ -24,6 +24,11 @@ const DISABLE_VUE_PLUGINS = process.env.DISABLE_VUE_PLUGINS === 'true'
|
||||
const DEV_SERVER_COMFYUI_URL =
|
||||
process.env.DEV_SERVER_COMFYUI_URL || 'http://127.0.0.1:8188'
|
||||
|
||||
const DISTRIBUTION = (process.env.DISTRIBUTION || 'localhost') as
|
||||
| 'desktop'
|
||||
| 'localhost'
|
||||
| 'cloud'
|
||||
|
||||
export default defineConfig({
|
||||
base: '',
|
||||
server: {
|
||||
@@ -195,7 +200,8 @@ export default defineConfig({
|
||||
__SENTRY_DSN__: JSON.stringify(process.env.SENTRY_DSN || ''),
|
||||
__ALGOLIA_APP_ID__: JSON.stringify(process.env.ALGOLIA_APP_ID || ''),
|
||||
__ALGOLIA_API_KEY__: JSON.stringify(process.env.ALGOLIA_API_KEY || ''),
|
||||
__USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true'
|
||||
__USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true',
|
||||
__DISTRIBUTION__: JSON.stringify(DISTRIBUTION)
|
||||
},
|
||||
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user