Skip to content

Commit 9c245e9

Browse files
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)
1 parent cb40da6 commit 9c245e9

File tree

4 files changed

+51
-15
lines changed

4 files changed

+51
-15
lines changed

src/components/helpcenter/HelpCenterMenuContent.vue

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ import type { CSSProperties, Component } from 'vue'
135135
import { useI18n } from 'vue-i18n'
136136
137137
import PuzzleIcon from '@/components/icons/PuzzleIcon.vue'
138+
import { isCloud } from '@/platform/distribution/types'
138139
import { useSettingStore } from '@/platform/settings/settingStore'
139140
import type { ReleaseNote } from '@/platform/updates/common/releaseService'
140141
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
@@ -265,7 +266,7 @@ const moreMenuItem = computed(() =>
265266
)
266267
267268
const menuItems = computed<MenuItem[]>(() => {
268-
return [
269+
const items: MenuItem[] = [
269270
{
270271
key: 'docs',
271272
type: 'item',
@@ -305,8 +306,12 @@ const menuItems = computed<MenuItem[]>(() => {
305306
void commandStore.execute('Comfy.ContactSupport')
306307
emit('close')
307308
}
308-
},
309-
{
309+
}
310+
]
311+
312+
// Extension manager - only in non-cloud distributions
313+
if (!isCloud) {
314+
items.push({
310315
key: 'manager',
311316
type: 'item',
312317
icon: PuzzleIcon,
@@ -319,17 +324,20 @@ const menuItems = computed<MenuItem[]>(() => {
319324
})
320325
emit('close')
321326
}
322-
},
323-
{
324-
key: 'more',
325-
type: 'item',
326-
icon: '',
327-
label: t('helpCenter.more'),
328-
visible: hasVisibleMoreItems.value,
329-
action: () => {}, // No action for more item
330-
items: moreItems.value
331-
}
332-
]
327+
})
328+
}
329+
330+
items.push({
331+
key: 'more',
332+
type: 'item',
333+
icon: '',
334+
label: t('helpCenter.more'),
335+
visible: hasVisibleMoreItems.value,
336+
action: () => {}, // No action for more item
337+
items: moreItems.value
338+
})
339+
340+
return items
333341
})
334342
335343
// Utility Functions
@@ -420,6 +428,9 @@ const formatReleaseDate = (dateString?: string): string => {
420428
}
421429
422430
const shouldShowUpdateButton = (release: ReleaseNote): boolean => {
431+
// Hide update buttons in cloud distribution
432+
if (isCloud) return false
433+
423434
return (
424435
releaseStore.shouldShowUpdateButton &&
425436
release === releaseStore.recentReleases[0]

src/platform/distribution/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Distribution types and compile-time constants for managing
3+
* multi-distribution builds (Desktop, Localhost, Cloud)
4+
*/
5+
6+
type Distribution = 'desktop' | 'localhost' | 'cloud'
7+
8+
declare global {
9+
const __DISTRIBUTION__: Distribution
10+
}
11+
12+
/** Current distribution - replaced at compile time */
13+
const DISTRIBUTION: Distribution = __DISTRIBUTION__
14+
15+
/** Distribution type checks */
16+
// const isDesktop = DISTRIBUTION === 'desktop'
17+
// const isLocalhost = DISTRIBUTION === 'localhost'
18+
export const isCloud = DISTRIBUTION === 'cloud'

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"src/types/**/*.d.ts",
5959
"tailwind.config.ts",
6060
"tests-ui/**/*",
61+
"vite.config.mts",
6162
"vitest.config.ts",
6263
// "vitest.setup.ts",
6364
]

vite.config.mts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ const DISABLE_VUE_PLUGINS = process.env.DISABLE_VUE_PLUGINS === 'true'
2424
const DEV_SERVER_COMFYUI_URL =
2525
process.env.DEV_SERVER_COMFYUI_URL || 'http://127.0.0.1:8188'
2626

27+
const DISTRIBUTION = (process.env.DISTRIBUTION || 'localhost') as
28+
| 'desktop'
29+
| 'localhost'
30+
| 'cloud'
31+
2732
export default defineConfig({
2833
base: '',
2934
server: {
@@ -195,7 +200,8 @@ export default defineConfig({
195200
__SENTRY_DSN__: JSON.stringify(process.env.SENTRY_DSN || ''),
196201
__ALGOLIA_APP_ID__: JSON.stringify(process.env.ALGOLIA_APP_ID || ''),
197202
__ALGOLIA_API_KEY__: JSON.stringify(process.env.ALGOLIA_API_KEY || ''),
198-
__USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true'
203+
__USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true',
204+
__DISTRIBUTION__: JSON.stringify(DISTRIBUTION)
199205
},
200206

201207
resolve: {

0 commit comments

Comments
 (0)