Repository: Shopify/cli
Pull request #5560: Support Remote DOM ui extensions
Author: login: vividviolet
Created: 2025-03-24T18:28:20Z | Merged: 2025-04-08T22:27:07Z

### WHY are these changes introduced?

Related to https://github.com/Shopify/app-ui/issues/2158

### WHAT is this pull request doing?
Enables support for Remote DOM UI extensions when using the `REMOTE_DOM_EXPERIMENT` environment variable.

### How to test your changes?

1. Checkout this branch and set your `CLI_KIT_VERSION` to `3.78.0`
1. Grab the relative path to the directory for an existing app (or create one using `pnpm create-app` if you don't have one)
1. Create a remote ui extension by running `pnpm shopify app generate --path <app-directory>`
1. Select an Admin Block extension and chose the Typescript React template
1. Verify that it successfully creates the remote-ui block extension
1. Select a Checkout UI extension and chose the Typescript React template
1. Verify that it successfully creates the checkout-ui extension
1. Create a remote dom extension by running `REMOTE_DOM_EXPERIMENT=true pnpm shopify app generate REMOTE_DOM_EXPERIMENT=true pnpm shopify app generate extension --clone-url https://github.com/Shopify/extensions-templates#2025-07 --path <app-directory>`
1. Select an Admin Block extension and chose the Typescript React template
1. Verify that it successfully creates the remote-dom block extension
1. Run `REMOTE_DOM_EXPERIMENT=true pnpm shopify app dev --path ../../../apps/remote-dom-feb-2025`
1. Verify that runs successfully.
    NOTE: You will see errors relating to updating the drafts like "Error while updating drafts: "2025-07" is not a valid API version" - this is expected until `2025-07` is publicly available.
1. Open the remote-dom Block extension's TOML file and update the target
1. Verify that after you update the target the `shopify.extension.target` is typed to the new target. 
1. Verify that updating the target to be an `action` extension like `"admin.gift-card-details.action.render"` you get type errors for trying to use the `<s-admin-block>` element.
1. Verify that the remote-ui extension still works the same as before:
- changing its target should not impact the shared type file
- no type reference comment is added

Uploading Screen Recording 2025-03-27 at 3.21.21 PM.mov…



### Checklist

- [x] I've considered possible cross-platform impacts (Mac, Linux, Windows)
- [x] I've considered possible [documentation](https://shopify.dev) changes



Diff:
diff --git a/.changeset/orange-poems-give.md b/.changeset/orange-poems-give.md
new file mode 100644
index 00000000000..50500929e2c
--- /dev/null
+++ b/.changeset/orange-poems-give.md
@@ -0,0 +1,5 @@
+---
+'@shopify/app': minor
+---
+
+Enable support for Polaris Unified extensions using an experimental environment variable
diff --git a/package.json b/package.json
index 8ee35a33ab1..99e2060b261 100644
--- a/package.json
+++ b/package.json
@@ -127,7 +127,8 @@
       "eslint-plugin-react-hooks",
       "@shopify/cli-hydrogen",
       "@shopify/theme-check-node",
-      "@shopify/theme-check-docs-updater"
+      "@shopify/theme-check-docs-updater",
+      "@shopify/ui-extensions"
     ],
     "ignoreWorkspaces": [
       "packages/eslint-plugin-cli"
diff --git a/packages/app/src/cli/models/app/app.test.ts b/packages/app/src/cli/models/app/app.test.ts
index b2596126452..7a9050feacf 100644
--- a/packages/app/src/cli/models/app/app.test.ts
+++ b/packages/app/src/cli/models/app/app.test.ts
@@ -26,8 +26,8 @@ import {FunctionConfigType} from '../extensions/specifications/function.js'
 import {WebhooksConfig} from '../extensions/specifications/types/app_config_webhook.js'
 import {EditorExtensionCollectionType} from '../extensions/specifications/editor_extension_collection.js'
 import {ApplicationURLs} from '../../services/dev/urls.js'
-import {describe, expect, test} from 'vitest'
-import {inTemporaryDirectory, mkdir, writeFile} from '@shopify/cli-kit/node/fs'
+import {describe, expect, test, vi} from 'vitest'
+import {inTemporaryDirectory, mkdir, readFile, writeFile} from '@shopify/cli-kit/node/fs'
 import {joinPath} from '@shopify/cli-kit/node/path'
 
 const CORRECT_CURRENT_APP_SCHEMA: CurrentAppConfiguration = {
@@ -243,9 +243,9 @@ describe('validateFunctionExtensionsWithUiHandle', () => {
     configuration_ui: true,
     metafields: [],
     name: 'test function extension',
-    type: type || 'product_discounts',
+    type: type ?? 'product_discounts',
     ui: {
-      handle: uiHandle || 'test-ui-handle',
+      handle: uiHandle ?? 'test-ui-handle',
     },
   })
 
@@ -617,6 +617,46 @@ describe('manifest', () => {
   })
 })
 
+describe('generateExtensionTypes', () => {
+  test('combines type definitions from multiple extensions', async () => {
+    await inTemporaryDirectory(async (tmpDir) => {
+      // Given
+      const uiExtension1 = await testUIExtension({type: 'ui_extension', directory: tmpDir})
+      const uiExtension2 = await testUIExtension({type: 'ui_extension', directory: tmpDir})
+
+      const app = testApp({
+        directory: tmpDir,
+        allExtensions: [uiExtension1, uiExtension2],
+      })
+
+      // Mock the extension contributions
+      vi.spyOn(uiExtension1, 'contributeToSharedTypeFile').mockResolvedValue([
+        {
+          libraryRoot: '@shopify/ui-extensions/base',
+          definition: `declare module './ext1' { // mocked definition }`,
+        },
+      ])
+      vi.spyOn(uiExtension2, 'contributeToSharedTypeFile').mockResolvedValue([
+        {
+          libraryRoot: '@shopify/ui-extensions/base',
+          definition: `declare module './ext2' { // mocked definition }`,
+        },
+      ])
+
+      // When
+      await app.generateExtensionTypes()
+
+      // Then
+      const typeFilePath = joinPath(tmpDir, 'shopify.d.ts')
+      const fileContent = await readFile(typeFilePath)
+      const normalizedContent = fileContent.toString().replace(/\\/g, '/')
+      expect(normalizedContent).toBe(`import '@shopify/ui-extensions/base';\n
+declare module './ext1' { // mocked definition }
+declare module './ext2' { // mocked definition }`)
+    })
+  })
+})
+
 function createPackageJson(tmpDir: string, type: string, version: string) {
   const packagePath = joinPath(tmpDir, 'node_modules', '@shopify', type, 'package.json')
   const packageJson = {name: 'name', version}
diff --git a/packages/app/src/cli/models/app/app.ts b/packages/app/src/cli/models/app/app.ts
index 1a06cf76bb5..7fd74eb30c6 100644
--- a/packages/app/src/cli/models/app/app.ts
+++ b/packages/app/src/cli/models/app/app.ts
@@ -17,7 +17,7 @@ import {patchAppHiddenConfigFile} from '../../services/app/patch-app-configurati
 import {ZodObjectOf, zod} from '@shopify/cli-kit/node/schema'
 import {DotEnvFile} from '@shopify/cli-kit/node/dot-env'
 import {getDependencies, PackageManager, readAndParsePackageJson} from '@shopify/cli-kit/node/node-package-manager'
-import {fileRealPath, findPathUp} from '@shopify/cli-kit/node/fs'
+import {fileExistsSync, fileRealPath, findPathUp, readFileSync, writeFileSync} from '@shopify/cli-kit/node/fs'
 import {joinPath} from '@shopify/cli-kit/node/path'
 import {AbortError} from '@shopify/cli-kit/node/error'
 import {normalizeDelimitedString} from '@shopify/cli-kit/common/string'
@@ -295,6 +295,7 @@ export interface AppInterface<
   removeExtension: (extensionUid: string) => void
   updateHiddenConfig: (values: Partial<AppHiddenConfig>) => Promise<void>
   setDevApplicationURLs: (devApplicationURLs: ApplicationURLs) => void
+  generateExtensionTypes(): Promise<void>
 }
 
 type AppConstructor<
@@ -497,6 +498,35 @@ export class App<
     this.realExtensions = this.realExtensions.filter((ext) => ext.uid !== extensionUid)
   }
 
+  async generateExtensionTypes() {
+    const typeFilePath = joinPath(this.directory, 'shopify.d.ts')
+    let firstImport = ''
+    const sharedTypes = await Promise.all(
+      this.allExtensions.map((extension) => extension.contributeToSharedTypeFile(typeFilePath)),
+    )
+    const combinedDefinitions = new Set()
+    sharedTypes.forEach((shared) => {
+      shared.forEach(({libraryRoot, definition}) => {
+        if (!firstImport) {
+          firstImport = `import '${libraryRoot}';\n`
+        }
+        combinedDefinitions.add(definition)
+      })
+    })
+
+    if (combinedDefinitions.size === 0) {
+      return
+    }
+
+    const originalContent = fileExistsSync(typeFilePath) ? readFileSync(typeFilePath).toString() : ''
+    const typeContent = [firstImport, ...Array.from(combinedDefinitions)].join('\n')
+    if (originalContent === typeContent) {
+      return
+    }
+    // Adding a top-level import to the type file allows us to workaround the TS restriction of not allowing declaring modules with relative paths
+    writeFileSync(typeFilePath, [firstImport, ...Array.from(combinedDefinitions)].join('\n'))
+  }
+
   get includeConfigOnDeploy() {
     if (isLegacyAppSchema(this.configuration)) return false
     if (this.appManagementApiEnabled) return true
diff --git a/packages/app/src/cli/models/app/loader.test.ts b/packages/app/src/cli/models/app/loader.test.ts
index e726e67e686..406417a3d01 100644
--- a/packages/app/src/cli/models/app/loader.test.ts
+++ b/packages/app/src/cli/models/app/loader.test.ts
@@ -12,7 +12,7 @@ import {
   reloadApp,
   loadHiddenConfig,
 } from './loader.js'
-import {AppLinkedInterface, LegacyAppSchema, WebConfigurationSchema} from './app.js'
+import {App, AppLinkedInterface, LegacyAppSchema, WebConfigurationSchema} from './app.js'
 import {DEFAULT_CONFIG, buildVersionedAppSchema, getWebhookConfig} from './app.test-data.js'
 import {configurationFileNames, blocks} from '../../constants.js'
 import metadata from '../../metadata.js'
@@ -2377,6 +2377,19 @@ wrong = "property"
     expect(reloadedApp.usesWorkspaces).toBe(app.usesWorkspaces)
   })
 
+  test('call app.generateExtensionTypes', async () => {
+    // Given
+    await writeConfig(appConfiguration)
+    const generateTypesSpy = vi.spyOn(App.prototype, 'generateExtensionTypes')
+
+    // When
+    await loadTestingApp()
+
+    // Then
+    expect(generateTypesSpy).toHaveBeenCalled()
+    generateTypesSpy.mockRestore()
+  })
+
   const runningOnWindows = platformAndArch().platform === 'windows'
 
   test.skipIf(runningOnWindows)(
diff --git a/packages/app/src/cli/models/app/loader.ts b/packages/app/src/cli/models/app/loader.ts
index f44848625db..70563bc85a3 100644
--- a/packages/app/src/cli/models/app/loader.ts
+++ b/packages/app/src/cli/models/app/loader.ts
@@ -389,6 +389,8 @@ class AppLoader<TConfig extends AppConfiguration, TModuleSpec extends ExtensionS
       usedCustomLayoutForExtensions: configuration.extension_directories !== undefined,
     })
 
+    await appClass.generateExtensionTypes()
+
     return appClass
   }
 
diff --git a/packages/app/src/cli/models/extensions/extension-instance.ts b/packages/app/src/cli/models/extensions/extension-instance.ts
index 7e9452b48f0..1f3f2aea6ee 100644
--- a/packages/app/src/cli/models/extensions/extension-instance.ts
+++ b/packages/app/src/cli/models/extensions/extension-instance.ts
@@ -2,7 +2,7 @@
 
 import {BaseConfigType, MAX_EXTENSION_HANDLE_LENGTH} from './schemas.js'
 import {FunctionConfigType} from './specifications/function.js'
-import {ExtensionFeature, ExtensionSpecification} from './specification.js'
+import {ExtensionFeature, ExtensionSpecification, SharedType} from './specification.js'
 import {SingleWebhookSubscriptionType} from './specifications/app_config_webhook_schemas/webhooks_schema.js'
 import {AppHomeSpecIdentifier} from './specifications/app_config_app_home.js'
 import {AppAccessSpecIdentifier} from './specifications/app_config_app_access.js'
@@ -264,7 +264,7 @@ export class ExtensionInstance<TConfiguration extends BaseConfigType = BaseConfi
   }
 
   hasExtensionPointTarget(target: string): boolean {
-    return this.specification.hasExtensionPointTarget?.(this.configuration, target) || false
+    return this.specification.hasExtensionPointTarget?.(this.configuration, target) ?? false
   }
 
   // Functions specific properties
@@ -437,6 +437,10 @@ export class ExtensionInstance<TConfiguration extends BaseConfigType = BaseConfi
     this.specification.patchWithAppDevURLs(this.configuration, urls)
   }
 
+  async contributeToSharedTypeFile(typeFilePath: string): Promise<SharedType[]> {
+    return this.specification.contributeToSharedTypeFile?.(this, typeFilePath) ?? []
+  }
+
   private buildHandle() {
     switch (this.specification.uidStrategy) {
       case 'single':
diff --git a/packages/app/src/cli/models/extensions/specification.ts b/packages/app/src/cli/models/extensions/specification.ts
index a2ec144ce53..d99a1d0b8ba 100644
--- a/packages/app/src/cli/models/extensions/specification.ts
+++ b/packages/app/src/cli/models/extensions/specification.ts
@@ -46,6 +46,12 @@ export interface Asset {
   outputFileName: string
   content: string
 }
+
+export interface SharedType {
+  libraryRoot: string
+  definition: string
+}
+
 /**
  * Extension specification with all the needed properties and methods to load an extension.
  */
@@ -113,6 +119,11 @@ export interface ExtensionSpecification<TConfiguration extends BaseConfigType =
    * Parse some provided configuration into a valid configuration object for this extension.
    */
   parseConfigurationObject: (configurationObject: object) => ParseConfigurationResult<TConfiguration>
+
+  contributeToSharedTypeFile?: (
+    extension: ExtensionInstance<TConfiguration>,
+    typeFilePath: string,
+  ) => Promise<SharedType[]>
 }
 
 /**
diff --git a/packages/app/src/cli/models/extensions/specifications/ui_extension.test.ts b/packages/app/src/cli/models/extensions/specifications/ui_extension.test.ts
index c06e401fba5..491d29121e5 100644
--- a/packages/app/src/cli/models/extensions/specifications/ui_extension.test.ts
+++ b/packages/app/src/cli/models/extensions/specifications/ui_extension.test.ts
@@ -1,8 +1,10 @@
+import {getShouldRenderTarget} from './ui_extension.js'
 import * as loadLocales from '../../../utilities/extensions/locales-configuration.js'
 import {ExtensionInstance} from '../extension-instance.js'
 import {loadLocalExtensionsSpecifications} from '../load-specifications.js'
 import {placeholderAppConfiguration} from '../../app/app.test-data.js'
-import {inTemporaryDirectory, mkdir, touchFile} from '@shopify/cli-kit/node/fs'
+import {AssetIdentifier} from '../specification.js'
+import {inTemporaryDirectory, touchFile, writeFile, readFile, mkdir} from '@shopify/cli-kit/node/fs'
 import {joinPath} from '@shopify/cli-kit/node/path'
 import {err, ok} from '@shopify/cli-kit/node/result'
 import {zod} from '@shopify/cli-kit/node/schema'
@@ -11,6 +13,7 @@ import {describe, expect, test, vi} from 'vitest'
 describe('ui_extension', async () => {
   interface GetUIExtensionProps {
     directory: string
+    apiVersion?: string
     extensionPoints?: {
       target: string
       module: string
@@ -37,13 +40,13 @@ describe('ui_extension', async () => {
     }[]
   }
 
-  async function getTestUIExtension({directory, extensionPoints}: GetUIExtensionProps) {
+  async function getTestUIExtension({directory, extensionPoints, apiVersion}: GetUIExtensionProps) {
     const configurationPath = joinPath(directory, 'shopify.extension.toml')
     const allSpecs = await loadLocalExtensionsSpecifications()
     const specification = allSpecs.find((spec) => spec.identifier === 'ui_extension')!
     const configuration = {
       extension_points: extensionPoints,
-      api_version: '2023-01' as const,
+      api_version: apiVersion ?? ('2023-01' as const),
       name: 'UI Extension',
       description: 'This is an ordinary test extension.',
       type: 'ui_extension',
@@ -693,6 +696,78 @@ Please check the configuration in ${uiExtension.configurationPath}`),
         expect(stdInContent).toContain(`import './src/ExtensionPointB.js';`)
       })
     })
+
+    test('includes shopify.extend calls for all targets including should-render for Remote DOM API versions', async () => {
+      await inTemporaryDirectory(async (tmpDir) => {
+        // Given
+        const uiExtension = await getTestUIExtension({
+          directory: tmpDir,
+          // Remote DOM supported API version
+          apiVersion: '2025-07',
+          extensionPoints: [
+            {
+              target: 'admin.product-details.block.render',
+              module: './src/ExtensionPointA.js',
+              build_manifest: {
+                assets: {
+                  main: {
+                    module: './src/ExtensionPointA.js',
+                    filepath: '/test-ui-extension.js',
+                  },
+                  should_render: {
+                    module: './src/condition/should-render.js',
+                    filepath: '/test-ui-extension-conditions.js',
+                  },
+                },
+              },
+            },
+          ],
+        })
+
+        // When
+        const stdInContent = uiExtension.getBundleExtensionStdinContent()
+
+        // Then
+        expect(stdInContent.main).toBe(
+          `import Target_0 from './src/ExtensionPointA.js';shopify.extend('admin.product-details.block.render', (...args) => Target_0(...args));`,
+        )
+
+        expect(stdInContent.assets!.find((asset) => asset.identifier === AssetIdentifier.ShouldRender)?.content).toBe(
+          `import shouldRender from './src/condition/should-render.js';shopify.extend('admin.product-details.block.should-render', (...args) => shouldRender(...args));`,
+        )
+      })
+    })
+
+    test('uses regular imports for non-Remote DOM API versions', async () => {
+      await inTemporaryDirectory(async (tmpDir) => {
+        // Given
+        const uiExtension = await getTestUIExtension({
+          directory: tmpDir,
+          // Non-Remote DOM supported API version
+          apiVersion: '2025-01',
+          extensionPoints: [
+            {
+              target: 'admin.product-details.block.render',
+              module: './src/ExtensionPointA.js',
+              build_manifest: {
+                assets: {
+                  main: {
+                    module: './src/ExtensionPointA.js',
+                    filepath: '/test-ui-extension.js',
+                  },
+                },
+              },
+            },
+          ],
+        })
+
+        // When
+        const stdInContent = uiExtension.getBundleExtensionStdinContent().main
+
+        // Then
+        expect(stdInContent).toBe(`import './src/ExtensionPointA.js';`)
+      })
+    })
   })
 
   describe('shouldFetchCartUrl()', async () => {
@@ -770,4 +845,183 @@ Please check the configuration in ${uiExtension.configurationPath}`),
       })
     })
   })
+
+  async function setupUIExtensionWithNodeModules({
+    tmpDir,
+    fileContent,
+    shouldRenderFileContent,
+    apiVersion,
+    target = 'admin.product-details.block.render',
+  }: {
+    tmpDir: string
+    fileContent: string
+    shouldRenderFileContent?: string
+    apiVersion: string
+    target?: string
+  }) {
+    // Create extension files
+    const srcDir = joinPath(tmpDir, 'src')
+    await mkdir(srcDir)
+    const filePath = joinPath(srcDir, `index.jsx`)
+
+    await writeFile(filePath, fileContent)
+
+    // Create shopify.d.ts type file
+    const typeFilePath = joinPath(tmpDir, 'shopify.d.ts')
+    await writeFile(typeFilePath, '// Type definitions')
+
+    // Create node_modules structure
+    const nodeModulesPath = joinPath(tmpDir, 'node_modules', '@shopify', 'ui-extensions')
+    await mkdir(nodeModulesPath)
+
+    const targetPath = joinPath(nodeModulesPath, target)
+    await mkdir(targetPath)
+    await writeFile(joinPath(targetPath, 'index.js'), '// Mock UI extension target')
+
+    const libraryPath = joinPath(nodeModulesPath, 'index.js')
+    await writeFile(libraryPath, '// Mock UI extension library')
+
+    let shouldRenderFilePath
+    if (shouldRenderFileContent) {
+      const shouldRenderTargetPath = joinPath(nodeModulesPath, getShouldRenderTarget(target))
+      await mkdir(shouldRenderTargetPath)
+      await writeFile(joinPath(shouldRenderTargetPath, 'index.js'), '// Mock UI extension should-render target')
+
+      const shouldRenderDir = joinPath(srcDir, 'condition')
+      await mkdir(shouldRenderDir)
+      shouldRenderFilePath = joinPath(shouldRenderDir, 'should-render.js')
+
+      await writeFile(shouldRenderFilePath, shouldRenderFileContent)
+    }
+
+    // Get UI extension spec and create instance
+    const allSpecs = await loadLocalExtensionsSpecifications()
+    const specification = allSpecs.find((spec) => spec.identifier === 'ui_extension')!
+
+    const extension = new ExtensionInstance({
+      configuration: {
+        api_version: apiVersion,
+        extension_points: [
+          {
+            target,
+            module: `./src/index.jsx`,
+            build_manifest: {
+              assets: {
+                main: {
+                  module: './src/index.jsx',
+                },
+                should_render: shouldRenderFilePath
+                  ? {
+                      module: './src/condition/should-render.js',
+                    }
+                  : undefined,
+              },
+            },
+          },
+        ],
+        name: 'Test UI Extension',
+        type: 'ui_extension',
+        metafields: [],
+      },
+      configurationPath: joinPath(tmpDir, 'shopify.extension.toml'),
+      directory: tmpDir,
+      specification,
+      entryPath: filePath,
+    })
+
+    return {
+      typeFilePath,
+      filePath,
+      libraryPath,
+      extension,
+      shouldRenderFilePath,
+    }
+  }
+
+  describe('contributeToSharedTypeFile', () => {
+    test('updates both main and should-render modules with type reference and returns shared types definition when api version supports Remote DOM', async () => {
+      await inTemporaryDirectory(async (tmpDir) => {
+        const {extension, typeFilePath, filePath, libraryPath, shouldRenderFilePath} =
+          await setupUIExtensionWithNodeModules({
+            tmpDir,
+            fileContent: '// JSX code',
+            shouldRenderFileContent: '// JS code',
+            // Remote DOM supported version
+            apiVersion: '2025-07',
+          })
+
+        // When
+        const result = await extension.contributeToSharedTypeFile?.(typeFilePath)
+
+        // Then
+        expect(result).toHaveLength(2)
+
+        // Main module types
+        const expectedDefinition = `//@ts-ignore\ndeclare module './src/index.jsx' {
+  const globalThis: typeof import('./node_modules/@shopify/ui-extensions/admin.product-details.block.render/index.js');
+  const shopify: import('./node_modules/@shopify/ui-extensions/admin.product-details.block.render/index.js').Api;
+}\n`
+        expect(result[0]?.definition).toBe(expectedDefinition)
+        expect(result[0]?.libraryRoot.replace(/\\/g, '/')).toBe(libraryPath.replace(/\\/g, '/'))
+
+        // Check if the TS file was updated with reference
+        const moduleFileContent = await readFile(filePath)
+        expect(moduleFileContent.toString()).toContain('/// <reference types="../shopify.d.ts" />')
+
+        // Should-render module types
+        const expectedShouldRenderDefinition = `//@ts-ignore\ndeclare module './src/condition/should-render.js' {
+  const globalThis: typeof import('./node_modules/@shopify/ui-extensions/admin.product-details.block.should-render/index.js');
+  const shopify: import('./node_modules/@shopify/ui-extensions/admin.product-details.block.should-render/index.js').Api;
+}\n`
+        expect(result[1]?.definition).toBe(expectedShouldRenderDefinition)
+        expect(result[1]?.libraryRoot.replace(/\\/g, '/')).toBe(libraryPath.replace(/\\/g, '/'))
+
+        const shouldRenderFileContent = await readFile(shouldRenderFilePath!)
+        expect(shouldRenderFileContent.toString()).toContain('/// <reference types="../../shopify.d.ts" />')
+      })
+    })
+
+    test('throws error when and remove type reference when api version supports Remote DOM but type reference for target could not be found', async () => {
+      await inTemporaryDirectory(async (tmpDir) => {
+        const {extension, typeFilePath, filePath} = await setupUIExtensionWithNodeModules({
+          tmpDir,
+          fileContent: '/// <reference types="../shopify.d.ts" />\n// Preact code',
+          // Remote DOM supported version
+          apiVersion: '2025-07',
+        })
+
+        // Change target to a target that does not exist in the library
+        extension.configuration.extension_points[0]!.target = 'admin.unknown.action.render'
+        // Then
+        await expect(extension.contributeToSharedTypeFile?.(typeFilePath)).rejects.toThrow(
+          'Type reference for admin.unknown.action.render could not be found. You might be using the wrong @shopify/ui-extensions version. Fix the error by ensuring you install @shopify/ui-extensions@2025-07 in your dependencies.',
+        )
+
+        // TSX file should remain unchanged
+        const moduleFileContent = await readFile(filePath)
+        expect(moduleFileContent.toString()).toBe('// Preact code')
+      })
+    })
+
+    test('returns empty shared types definition and leaves file unchanged when api version does not support Remote DOM', async () => {
+      await inTemporaryDirectory(async (tmpDir) => {
+        const {extension, typeFilePath, filePath} = await setupUIExtensionWithNodeModules({
+          tmpDir,
+          fileContent: '// TypeScript React code',
+          // Non-Remote DOM supported version
+          apiVersion: '2025-01',
+        })
+
+        // When
+        const result = await extension.contributeToSharedTypeFile?.(typeFilePath)
+
+        // Then
+        expect(result).toEqual([])
+
+        // TSX file should remain unchanged
+        const moduleFileContent = await readFile(filePath)
+        expect(moduleFileContent.toString()).toBe('// TypeScript React code')
+      })
+    })
+  })
 })
diff --git a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts
index 3a092a53c73..7e48ed858de 100644
--- a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts
+++ b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts
@@ -1,12 +1,16 @@
-import {Asset, AssetIdentifier, ExtensionFeature, createExtensionSpecification} from '../specification.js'
+import {Asset, AssetIdentifier, ExtensionFeature, SharedType, createExtensionSpecification} from '../specification.js'
 import {NewExtensionPointSchemaType, NewExtensionPointsSchema, BaseSchema} from '../schemas.js'
 import {loadLocalesConfig} from '../../../utilities/extensions/locales-configuration.js'
 import {getExtensionPointTargetSurface} from '../../../services/dev/extension/utilities.js'
+import {ExtensionInstance} from '../extension-instance.js'
 import {err, ok, Result} from '@shopify/cli-kit/node/result'
-import {fileExists} from '@shopify/cli-kit/node/fs'
-import {joinPath} from '@shopify/cli-kit/node/path'
+import {fileExists, readFileSync, writeFileSync} from '@shopify/cli-kit/node/fs'
+import {dirname, joinPath, relativePath, relativizePath} from '@shopify/cli-kit/node/path'
 import {outputContent, outputToken} from '@shopify/cli-kit/node/output'
 import {zod} from '@shopify/cli-kit/node/schema'
+import {createRequire} from 'module'
+
+const require = createRequire(import.meta.url)
 
 const dependency = '@shopify/checkout-ui-extensions'
 
@@ -102,9 +106,13 @@ const uiExtensionSpec = createExtensionSpecification({
     }
   },
   getBundleExtensionStdinContent: (config) => {
+    const shouldIncludeShopifyExtend = isRemoteDomExtension(config)
     const main = config.extension_points
-      .map(({module}) => {
-        return `import '${module}'; `
+      .map(({target, module}, index) => {
+        if (shouldIncludeShopifyExtend) {
+          return `import Target_${index} from '${module}';shopify.extend('${target}', (...args) => Target_${index}(...args));`
+        }
+        return `import '${module}';`
       })
       .join('\n')
 
@@ -119,7 +127,11 @@ const uiExtensionSpec = createExtensionSpecification({
         assets[identifier] = {
           identifier: identifier as AssetIdentifier,
           outputFileName: asset.filepath,
-          content: `import '${asset.module}'`,
+          content: shouldIncludeShopifyExtend
+            ? `import shouldRender from '${asset.module}';shopify.extend('${getShouldRenderTarget(
+                extensionPoint.target,
+              )}', (...args) => shouldRender(...args));`
+            : `import '${asset.module}'`,
         }
       })
     })
@@ -137,6 +149,48 @@ const uiExtensionSpec = createExtensionSpecification({
       }) !== undefined
     )
   },
+  contributeToSharedTypeFile: async (extension, typeFilePath) => {
+    const sharedTypes: SharedType[] = []
+    if (!isRemoteDomExtension(extension.configuration)) {
+      return sharedTypes
+    }
+
+    const {configuration} = extension
+    for await (const extensionPoint of configuration.extension_points) {
+      const fullPath = joinPath(extension.directory, extensionPoint.module)
+      const fileParts = fullPath.split('.')
+      const fileExtension = fileParts.pop()
+      const exists = await fileExists(fullPath)
+      if (!fileExtension || !exists) {
+        continue
+      }
+
+      const mainTypes = getSharedTypeDefinition(
+        fullPath,
+        typeFilePath,
+        extensionPoint.target,
+        configuration.api_version,
+      )
+      if (mainTypes) {
+        sharedTypes.push(mainTypes)
+      }
+
+      if (extensionPoint.build_manifest.assets[AssetIdentifier.ShouldRender]?.module) {
+        const shouldRenderTypes = getSharedTypeDefinition(
+          joinPath(extension.directory, extensionPoint.build_manifest.assets[AssetIdentifier.ShouldRender].module),
+          typeFilePath,
+          getShouldRenderTarget(extensionPoint.target),
+
+          configuration.api_version,
+        )
+        if (shouldRenderTypes) {
+          sharedTypes.push(shouldRenderTypes)
+        }
+      }
+    }
+
+    return sharedTypes
+  },
 })
 
 function addDistPathToAssets(extP: NewExtensionPointSchemaType & {build_manifest: BuildManifest}) {
@@ -201,4 +255,77 @@ Please check the module path for ${target}`.value,
   return ok({})
 }
 
+function isRemoteDomExtension(
+  config: ExtensionInstance['configuration'],
+): config is ExtensionInstance<{api_version: string}>['configuration'] {
+  const apiVersion = config.api_version
+  const [year, month] = apiVersion?.split('-').map((part: string) => parseInt(part, 10)) ?? []
+  if (!year || !month) {
+    return false
+  }
+
+  return year > 2025 || (year === 2025 && month >= 7)
+}
+
+// eslint-disable-next-line no-useless-escape
+const TYPE_REF_REGEX = /^\/\/\/ <reference types="(?:[.\/]*)shopify\.d\.ts" \/>\n/
+
+function updateTypeReference({
+  fullPath,
+  template,
+  shouldAddTypeReference,
+}: {
+  fullPath: string
+  template: string
+  shouldAddTypeReference: boolean
+}) {
+  const originalContent = readFileSync(fullPath).toString()
+  let fileContent = originalContent
+  let match
+  while ((match = TYPE_REF_REGEX.exec(fileContent))) {
+    fileContent = fileContent.replace(match[0], '')
+  }
+
+  if (shouldAddTypeReference) {
+    fileContent = template.concat(fileContent)
+  }
+
+  if (originalContent !== fileContent) {
+    writeFileSync(fullPath, fileContent)
+  }
+}
+
+export function getShouldRenderTarget(target: string) {
+  return target.replace(/\.render$/, '.should-render')
+}
+
+function getSharedTypeDefinition(fullPath: string, typeFilePath: string, target: string, apiVersion: string) {
+  const template = `/// <reference types="${relativePath(dirname(fullPath), typeFilePath)}" />\n`
+  let types
+  try {
+    // We try to resolve from the module's path first with the app root as the fallback in case dependencies are hoisted to the shared workspace
+    const fullTypePath = require.resolve(`@shopify/ui-extensions/${target}`, {paths: [fullPath, typeFilePath]})
+    const libraryRoot = require.resolve('@shopify/ui-extensions', {paths: [fullPath, typeFilePath]})
+    const importPath = `./${relativizePath(fullTypePath, dirname(typeFilePath))}`
+
+    types = {
+      libraryRoot,
+      definition: `//@ts-ignore\ndeclare module './${relativizePath(fullPath, dirname(typeFilePath))}' {
+  const globalThis: typeof import('${importPath}');
+  const shopify: import('${importPath}').Api;
+}\n`,
+    }
+  } catch (_) {
+    // Remove the type reference from the source file
+    updateTypeReference({fullPath, template, shouldAddTypeReference: false})
+    throw new Error(
+      `Type reference for ${target} could not be found. You might be using the wrong @shopify/ui-extensions version. Fix the error by ensuring you install @shopify/ui-extensions@${apiVersion} in your dependencies.`,
+    )
+  }
+
+  // Update the type reference
+  updateTypeReference({fullPath, template, shouldAddTypeReference: true})
+  return types
+}
+
 export default uiExtensionSpec
diff --git a/packages/app/src/cli/services/dev/app-events/app-watcher-esbuild.ts b/packages/app/src/cli/services/dev/app-events/app-watcher-esbuild.ts
index 6203b89bee7..c3892ea1116 100644
--- a/packages/app/src/cli/services/dev/app-events/app-watcher-esbuild.ts
+++ b/packages/app/src/cli/services/dev/app-events/app-watcher-esbuild.ts
@@ -95,7 +95,6 @@ export class ESBuildContextManager {
    */
   async updateContexts(appEvent: AppEvent) {
     this.dotEnvVariables = appEvent.app.dotenv?.variables ?? {}
-
     const createdEsBuild = appEvent.extensionEvents
       .filter((extEvent) => extEvent.extension.isESBuildExtension)
       .filter((extEvent) => extEvent.type === 'created' || (extEvent.type === 'changed' && appEvent.appWasReloaded))
diff --git a/packages/app/src/cli/services/generate.ts b/packages/app/src/cli/services/generate.ts
index 360765ee94c..95302b045ab 100644
--- a/packages/app/src/cli/services/generate.ts
+++ b/packages/app/src/cli/services/generate.ts
@@ -106,7 +106,7 @@ async function saveAnalyticsMetadata(promptAnswers: GenerateExtensionPromptOutpu
 
 function buildGenerateOptions(
   promptAnswers: GenerateExtensionPromptOutput,
-  app: AppInterface,
+  app: AppLinkedInterface,
   options: GenerateOptions,
   developerPlatformClient: DeveloperPlatformClient,
 ): GenerateExtensionTemplateOptions {
diff --git a/packages/app/src/cli/services/generate/extension.test.ts b/packages/app/src/cli/services/generate/extension.test.ts
index 4c4cbfc16a3..ac18f4983bc 100644
--- a/packages/app/src/cli/services/generate/extension.test.ts
+++ b/packages/app/src/cli/services/generate/extension.test.ts
@@ -6,7 +6,7 @@ import {
 } from './extension.js'
 import * as extensionsCommon from '../extensions/common.js'
 import {blocks, configurationFileNames} from '../../constants.js'
-import {loadApp} from '../../models/app/loader.js'
+import {loadApp, reloadApp} from '../../models/app/loader.js'
 import * as functionBuild from '../function/build.js'
 import {
   checkoutUITemplate,
@@ -16,6 +16,8 @@ import {
 import {ExtensionTemplate} from '../../models/app/template.js'
 import {ExtensionSpecification} from '../../models/extensions/specification.js'
 import {loadLocalExtensionsSpecifications} from '../../models/extensions/load-specifications.js'
+import {AppLinkedInterface} from '../../models/app/app.js'
+import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js'
 import {describe, expect, vi, test} from 'vitest'
 import * as output from '@shopify/cli-kit/node/output'
 import {
@@ -40,6 +42,14 @@ vi.mock('@shopify/cli-kit/node/node-package-manager', async () => {
   }
 })
 
+vi.mock('../../models/app/loader.js', async () => {
+  const actual: any = await vi.importActual('../../models/app/loader.js')
+  return {
+    ...actual,
+    reloadApp: vi.fn(),
+  }
+})
+
 describe('initialize a extension', async () => {
   const allUITemplates = [checkoutUITemplate]
   const allFunctionTemplates = testRemoteExtensionTemplates
@@ -437,6 +447,85 @@ describe('initialize a extension', async () => {
       expect(file.fileExistsSync(joinPath(tmpDir, 'extensions', name))).toBeFalsy()
     })
   })
+
+  test('reloads the app after generating the extension', async () => {
+    await withTemporaryApp(async (tmpDir) => {
+      const downloadGitRepositorySpy = vi.spyOn(git, 'downloadGitRepository').mockResolvedValue()
+      vi.spyOn(extensionsCommon, 'ensureDownloadedExtensionFlavorExists').mockImplementationOnce(async () => tmpDir)
+
+      const name = 'my-ext-1'
+
+      const specification = checkoutUITemplate
+      const extensionFlavor = 'react'
+      await createFromTemplate({
+        name,
+        extensionTemplate: specification,
+        extensionFlavor,
+        appDirectory: tmpDir,
+        specifications,
+        developerPlatformClient: testDeveloperPlatformClient({
+          templateSpecifications: () =>
+            Promise.resolve([
+              {
+                identifier: 'ui_extension',
+                name: 'UI Extension',
+                defaultName: 'ui-extension',
+                group: 'Merchant Admin',
+                supportLinks: [],
+                type: 'ui_extension',
+                url: 'https://github.com/Shopify/extensions-templates',
+                extensionPoints: [],
+                supportedFlavors: [
+                  {
+                    name: 'JavaScript',
+                    value: 'vanilla-js',
+                  },
+                  {
+                    name: 'TypeScript',
+                    value: 'typescript',
+                  },
+                  {
+                    name: 'React',
+                    value: 'react',
+                  },
+                ],
+              },
+            ]),
+        }),
+      })
+
+      // Then
+      expect(vi.mocked(reloadApp)).toHaveBeenCalledOnce()
+    })
+  })
+
+  test('uses specification.url for the git repository URL by default', async () => {
+    await withTemporaryApp(async (tmpDir) => {
+      const downloadGitRepositorySpy = vi.spyOn(git, 'downloadGitRepository').mockResolvedValue()
+      vi.spyOn(extensionsCommon, 'ensureDownloadedExtensionFlavorExists').mockImplementationOnce(async () => tmpDir)
+
+      const name = 'my-ext-1'
+      const specification = allUITemplates.find((spec) => spec.identifier === 'checkout_ui')!
+      specification.url = 'default/template/url'
+      const extensionFlavor = 'vanilla-js'
+
+      // When
+      await createFromTemplate({
+        name,
+        extensionTemplate: specification,
+        extensionFlavor,
+        appDirectory: tmpDir,
+        specifications,
+      })
+
+      // Then
+      expect(downloadGitRepositorySpy).toHaveBeenCalledWith({
+        destination: expect.any(String),
+        repoUrl: specification.url,
+        shallow: true,
+      })
+    })
+  })
 })
 
 interface CreateFromTemplateOptions {
@@ -446,6 +535,7 @@ interface CreateFromTemplateOptions {
   extensionFlavor: ExtensionFlavorValue
   specifications: ExtensionSpecification[]
   onGetTemplateRepository?: (url: string, destination: string) => Promise<void>
+  developerPlatformClient?: DeveloperPlatformClient
 }
 async function createFromTemplate({
   name,
@@ -454,12 +544,17 @@ async function createFromTemplate({
   extensionFlavor,
   specifications,
   onGetTemplateRepository,
+  developerPlatformClient = testDeveloperPlatformClient(),
 }: CreateFromTemplateOptions): Promise<string> {
   const result = await generateExtensionTemplate({
     extensionTemplate: specification,
-    app: await loadApp({directory: appDirectory, specifications, userProvidedConfigName: undefined}),
+    app: (await loadApp({
+      directory: appDirectory,
+      specifications,
+      userProvidedConfigName: undefined,
+    })) as AppLinkedInterface,
     extensionChoices: {name, flavor: extensionFlavor},
-    developerPlatformClient: testDeveloperPlatformClient(),
+    developerPlatformClient,
     onGetTemplateRepository,
   })
   return result.directory
diff --git a/packages/app/src/cli/services/generate/extension.ts b/packages/app/src/cli/services/generate/extension.ts
index 816be7f709e..80cddab3e8f 100644
--- a/packages/app/src/cli/services/generate/extension.ts
+++ b/packages/app/src/cli/services/generate/extension.ts
@@ -1,10 +1,11 @@
 import {configurationFileNames, versions} from '../../constants.js'
-import {AppInterface} from '../../models/app/app.js'
+import {AppLinkedInterface} from '../../models/app/app.js'
 import {buildGraphqlTypes, PREFERRED_FUNCTION_NPM_PACKAGE_MAJOR_VERSION} from '../function/build.js'
 import {GenerateExtensionContentOutput} from '../../prompts/generate/extension.js'
 import {ExtensionFlavor, ExtensionTemplate} from '../../models/app/template.js'
 import {ensureDownloadedExtensionFlavorExists, ensureExtensionDirectoryExists} from '../extensions/common.js'
 import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js'
+import {reloadApp} from '../../models/app/loader.js'
 import {
   addNPMDependenciesIfNeeded,
   addResolutionOrOverride,
@@ -21,7 +22,7 @@ import {slugify} from '@shopify/cli-kit/common/string'
 import {randomUUID} from '@shopify/cli-kit/node/crypto'
 
 export interface GenerateExtensionTemplateOptions {
-  app: AppInterface
+  app: AppLinkedInterface
   cloneUrl?: string
   extensionChoices: GenerateExtensionContentOutput
   extensionTemplate: ExtensionTemplate
@@ -33,6 +34,7 @@ export interface GenerateExtensionTemplateOptions {
 
 export type ExtensionFlavorValue =
   | 'vanilla-js'
+  | 'preact'
   | 'react'
   | 'typescript'
   | 'typescript-react'
@@ -45,6 +47,7 @@ export type TemplateLanguage = 'javascript' | 'rust' | 'wasm' | undefined
 function getTemplateLanguage(flavor: ExtensionFlavorValue | undefined): TemplateLanguage {
   switch (flavor) {
     case 'vanilla-js':
+    case 'preact':
     case 'react':
     case 'typescript':
     case 'typescript-react':
@@ -65,7 +68,7 @@ export interface GeneratedExtension {
 interface ExtensionInitOptions {
   directory: string
   url: string
-  app: AppInterface
+  app: AppLinkedInterface
   type: string
   name: string
   extensionFlavor: ExtensionFlavor | undefined
@@ -82,7 +85,8 @@ export async function generateExtensionTemplate(
     (flavor) => flavor.value === extensionFlavorValue,
   )
   const directory = await ensureExtensionDirectoryExists({app: options.app, name: extensionName})
-  const url = options.cloneUrl || options.extensionTemplate.url
+  const url = options.cloneUrl ?? options.extensionTemplate.url
+
   const uid = options.developerPlatformClient.supportsAtomicDeployments ? randomUUID() : undefined
   const initOptions: ExtensionInitOptions = {
     directory,
@@ -174,7 +178,7 @@ async function functionExtensionInit({
       })
 
       if (templateLanguage === 'javascript') {
-        const srcFileExtension = getSrcFileExtension(extensionFlavor?.value || 'rust')
+        const srcFileExtension = getSrcFileExtension(extensionFlavor?.value ?? 'rust')
         await changeIndexFileExtension(directory, srcFileExtension, '!(*.graphql)')
       }
     },
@@ -277,6 +281,14 @@ async function uiExtensionInit({
       },
     },
   ]
+
+  tasks.push({
+    title: 'Update shared type definition',
+    task: async () => {
+      await reloadApp(app)
+    },
+  })
+
   await renderTasks(tasks)
 }
 
@@ -284,6 +296,7 @@ type SrcFileExtension = 'ts' | 'tsx' | 'js' | 'jsx' | 'rs' | 'wasm' | 'liquid' |
 function getSrcFileExtension(extensionFlavor: ExtensionFlavorValue): SrcFileExtension {
   const flavorToSrcFileExtension: {[key in ExtensionFlavorValue]: SrcFileExtension} = {
     'vanilla-js': 'js',
+    preact: 'jsx',
     react: 'jsx',
     typescript: 'ts',
     'typescript-react': 'tsx',
@@ -319,6 +332,11 @@ async function changeIndexFileExtension(extensionDirectory: string, fileExtensio
 }
 
 async function removeUnwantedTemplateFilesPerFlavor(extensionDirectory: string, extensionFlavor: ExtensionFlavorValue) {
+  // Preact needs the tsconfig.json to set the `"jsxImportSource": "preact"` so it can properly build
+  if (extensionFlavor === 'preact') {
+    return
+  }
+
   // tsconfig.json file is only needed in extension folder to inform the IDE
   // About the `react-jsx` tsconfig option, so IDE don't complain about missing react import
   if (extensionFlavor !== 'typescript-react') {
diff --git a/packages/app/src/cli/services/generate/fetch-template-specifications.test.ts b/packages/app/src/cli/services/generate/fetch-template-specifications.test.ts
index 49327b0465d..346cabf2fd2 100644
--- a/packages/app/src/cli/services/generate/fetch-template-specifications.test.ts
+++ b/packages/app/src/cli/services/generate/fetch-template-specifications.test.ts
@@ -1,7 +1,13 @@
 import {fetchExtensionTemplates} from './fetch-template-specifications.js'
+import {ExtensionFlavorValue} from './extension.js'
 import {testDeveloperPlatformClient, testOrganizationApp} from '../../models/app/app.test-data.js'
-import {ExtensionTemplate} from '../../models/app/template.js'
-import {describe, expect, test} from 'vitest'
+import {ExtensionTemplate, ExtensionFlavor} from '../../models/app/template.js'
+import {describe, expect, test, vi} from 'vitest'
+import * as experimentModule from '@shopify/cli-kit/node/is-polaris-unified-enabled'
+
+vi.mock('@shopify/cli-kit/node/is-polaris-unified-enabled', () => ({
+  isPolarisUnifiedEnabled: vi.fn().mockReturnValue(false),
+}))
 
 describe('fetchTemplateSpecifications', () => {
   test('returns the remote specs', async () => {
@@ -26,4 +32,135 @@ describe('fetchTemplateSpecifications', () => {
     // Since the ui_extension specification is not enabled, this template should not be included.
     expect(identifiers).not.toContain('ui_extension')
   })
+
+  describe('ui_extension', () => {
+    const preactFlavor: ExtensionFlavor = {
+      name: 'Preact',
+      value: 'preact' as ExtensionFlavorValue,
+    }
+
+    const oldFlavors = [
+      {
+        name: 'JavaScript React',
+        value: 'react' as ExtensionFlavorValue,
+        path: 'admin-action',
+      },
+      {
+        name: 'JavaScript',
+        value: 'vanilla-js' as ExtensionFlavorValue,
+        path: 'admin-action',
+      },
+      {
+        name: 'TypeScript React',
+        value: 'typescript-react' as ExtensionFlavorValue,
+        path: 'admin-action',
+      },
+      {
+        name: 'TypeScript',
+        value: 'typescript' as ExtensionFlavorValue,
+        path: 'admin-action',
+      },
+    ]
+    const allFlavors = [...oldFlavors, preactFlavor]
+
+    async function getTemplates() {
+      const templates: ExtensionTemplate[] = await fetchExtensionTemplates(
+        testDeveloperPlatformClient({
+          templateSpecifications: () =>
+            Promise.resolve([
+              {
+                identifier: 'ui_extension',
+                name: 'UI Extension',
+                defaultName: 'ui-extension',
+                group: 'Merchant Admin',
+                supportLinks: [],
+                type: 'ui_extension',
+                url: 'https://github.com/Shopify/extensions-templates',
+                extensionPoints: [],
+                supportedFlavors: allFlavors,
+              },
+            ]),
+        }),
+        testOrganizationApp(),
+        ['ui_extension'],
+      )
+      return templates
+    }
+
+    test('returns only the preact flavor when POLARIS_UNIFIED is enabled', async () => {
+      // Given
+      vi.spyOn(experimentModule, 'isPolarisUnifiedEnabled').mockReturnValueOnce(true)
+
+      // When
+      const templates = await getTemplates()
+
+      // Then
+      expect(templates[0]!.supportedFlavors).toEqual([preactFlavor])
+    })
+
+    test('excludes the preact flavor by default', async () => {
+      // When
+      const templates = await getTemplates()
+
+      // Then
+      expect(templates[0]!.supportedFlavors).toEqual(oldFlavors)
+    })
+
+    test('filter out templates that have no flavors available by default', async () => {
+      // When
+      const templates: ExtensionTemplate[] = await fetchExtensionTemplates(
+        testDeveloperPlatformClient({
+          templateSpecifications: () =>
+            Promise.resolve([
+              {
+                identifier: 'ui_extension',
+                name: 'UI Extension',
+                defaultName: 'ui-extension',
+                group: 'Merchant Admin',
+                supportLinks: [],
+                type: 'ui_extension',
+                url: 'https://github.com/Shopify/extensions-templates',
+                extensionPoints: [],
+                supportedFlavors: [preactFlavor],
+              },
+            ]),
+        }),
+        testOrganizationApp(),
+        ['ui_extension'],
+      )
+
+      // Then
+      expect(templates).toEqual([])
+    })
+
+    test('filter out templates that have no flavors available POLARIS_UNIFIED is enabled', async () => {
+      // Given
+      vi.spyOn(experimentModule, 'isPolarisUnifiedEnabled').mockReturnValueOnce(true)
+
+      // When
+      const templates: ExtensionTemplate[] = await fetchExtensionTemplates(
+        testDeveloperPlatformClient({
+          templateSpecifications: () =>
+            Promise.resolve([
+              {
+                identifier: 'ui_extension',
+                name: 'UI Extension',
+                defaultName: 'ui-extension',
+                group: 'Merchant Admin',
+                supportLinks: [],
+                type: 'ui_extension',
+                url: 'https://github.com/Shopify/extensions-templates',
+                extensionPoints: [],
+                supportedFlavors: oldFlavors,
+              },
+            ]),
+        }),
+        testOrganizationApp(),
+        ['ui_extension'],
+      )
+
+      // Then
+      expect(templates).toEqual([])
+    })
+  })
 })
diff --git a/packages/app/src/cli/services/generate/fetch-template-specifications.ts b/packages/app/src/cli/services/generate/fetch-template-specifications.ts
index 28c6cbc9ce2..e246c475515 100644
--- a/packages/app/src/cli/services/generate/fetch-template-specifications.ts
+++ b/packages/app/src/cli/services/generate/fetch-template-specifications.ts
@@ -1,6 +1,7 @@
 import {ExtensionTemplate} from '../../models/app/template.js'
 import {MinimalAppIdentifiers} from '../../models/organization.js'
 import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js'
+import {isPolarisUnifiedEnabled} from '@shopify/cli-kit/node/is-polaris-unified-enabled'
 
 export async function fetchExtensionTemplates(
   developerPlatformClient: DeveloperPlatformClient,
@@ -8,8 +9,23 @@ export async function fetchExtensionTemplates(
   availableSpecifications: string[],
 ): Promise<ExtensionTemplate[]> {
   const remoteTemplates: ExtensionTemplate[] = await developerPlatformClient.templateSpecifications(app)
-  return remoteTemplates.filter(
-    (template) =>
-      availableSpecifications.includes(template.identifier) || availableSpecifications.includes(template.type),
-  )
+  const remoteDomExperimentEnabled = isPolarisUnifiedEnabled()
+
+  return remoteTemplates
+    .filter(
+      (template) =>
+        availableSpecifications.includes(template.identifier) || availableSpecifications.includes(template.type),
+    )
+    .map((template) => {
+      if (template.type === 'ui_extension') {
+        return {
+          ...template,
+          supportedFlavors: template.supportedFlavors.filter((flavor) =>
+            remoteDomExperimentEnabled ? flavor.value === 'preact' : flavor.value !== 'preact',
+          ),
+        }
+      }
+      return template
+    })
+    .filter((template) => template.supportedFlavors.length > 0)
 }
diff --git a/packages/cli-kit/src/public/node/is-polaris-unified-enabled.test.ts b/packages/cli-kit/src/public/node/is-polaris-unified-enabled.test.ts
new file mode 100644
index 00000000000..6c1d3de5c8a
--- /dev/null
+++ b/packages/cli-kit/src/public/node/is-polaris-unified-enabled.test.ts
@@ -0,0 +1,18 @@
+import {isPolarisUnifiedEnabled} from './is-polaris-unified-enabled.js'
+import {describe, test, expect, vi} from 'vitest'
+
+describe('isPolarisUnifiedEnabled', () => {
+  test('returns true when POLARIS_UNIFIED is set to truthy value', () => {
+    vi.stubEnv('POLARIS_UNIFIED', 'true')
+    expect(isPolarisUnifiedEnabled()).toBe(true)
+  })
+
+  test('returns false when POLARIS_UNIFIED is set to falsy value', () => {
+    vi.stubEnv('POLARIS_UNIFIED', 'false')
+    expect(isPolarisUnifiedEnabled()).toBe(false)
+  })
+
+  test('returns false when POLARIS_UNIFIED is not set', () => {
+    expect(isPolarisUnifiedEnabled()).toBe(false)
+  })
+})
diff --git a/packages/cli-kit/src/public/node/is-polaris-unified-enabled.ts b/packages/cli-kit/src/public/node/is-polaris-unified-enabled.ts
new file mode 100644
index 00000000000..f57bf19fa79
--- /dev/null
+++ b/packages/cli-kit/src/public/node/is-polaris-unified-enabled.ts
@@ -0,0 +1,10 @@
+import {isTruthy} from './context/utilities.js'
+
+/**
+ * Returns true if the POLARIS_UNIFIED environment variable is set to true.
+ *
+ * @returns `true` if the POLARIS_UNIFIED environment variable is set to true.
+ */
+export function isPolarisUnifiedEnabled(): boolean {
+  return isTruthy(process.env.POLARIS_UNIFIED)
+}


Reviews:
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:11:58Z
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:12:34Z
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:13:47Z
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:14:16Z
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:19:03Z
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T10:21:07Z
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T14:37:08Z
user: login: craigmichaelmartin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T16:24:11Z
user: login: vividviolet
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T16:26:15Z
user: login: craigmichaelmartin
body: Looks great!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T17:28:17Z
user: login: isaacroldan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T12:48:40Z

Review comments:
diff_hunk: @@ -2377,6 +2378,31 @@ wrong = "property"
     expect(reloadedApp.usesWorkspaces).toBe(app.usesWorkspaces)
   })
 
+  test('does not call app.generateExtensionTypes by default', async () => {
+    // Given
+    await writeConfig(appConfiguration)
+    const generateTypesSpy = vi.spyOn(App.prototype, 'generateExtensionTypes')
+    // When
+    await loadTestingApp()
+    // Then
+    expect(generateTypesSpy).not.toHaveBeenCalled()
+    generateTypesSpy.mockRestore()
+  })
+
+  test('call app.generateExtensionTypes when REMOTE_DOM_EXPERIMENT is true', async () => {
+    process.env.REMOTE_DOM_EXPERIMENT = 'true'
path: packages/app/src/cli/models/app/loader.test.ts
user: login: isaacroldan
body: We prefer not to modify the real `process.env` in a test. Because the process might crash before you can clean it up.

Instead, we should inject the `env` to the loader so it can be replaced in a test:
This can be easily solved with few changes:
- modify `AppLoaderConstructorArgs` to accept an optional `env?: NodeJS.ProcessEnv`
- Add a new property to the loader and set it in the constructor: `private readonly env: NodeJS.ProcessEnv` & `this.env = env ?? process.env`
- use `this.env.REMOTE_DOM_EXPERIMENT` inside the loader.
- In the test, modify `loadTestingApp` to also accept an optional env `env?: NodeJS.ProcessEnv`
- When running your tests, inject the value you need when calling `loadTestingApp`
created_at: 2025-03-28T10:11:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2377,6 +2378,31 @@ wrong = "property"
     expect(reloadedApp.usesWorkspaces).toBe(app.usesWorkspaces)
   })
 
+  test('does not call app.generateExtensionTypes by default', async () => {
+    // Given
+    await writeConfig(appConfiguration)
+    const generateTypesSpy = vi.spyOn(App.prototype, 'generateExtensionTypes')
+    // When
+    await loadTestingApp()
+    // Then
+    expect(generateTypesSpy).not.toHaveBeenCalled()
+    generateTypesSpy.mockRestore()
+  })
+
+  test('call app.generateExtensionTypes when REMOTE_DOM_EXPERIMENT is true', async () => {
+    process.env.REMOTE_DOM_EXPERIMENT = 'true'
path: packages/app/src/cli/models/app/loader.test.ts
user: login: isaacroldan
body: Ping me if some of this is not clear! :) 
created_at: 2025-03-28T10:12:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -389,6 +389,10 @@ class AppLoader<TConfig extends AppConfiguration, TModuleSpec extends ExtensionS
       usedCustomLayoutForExtensions: configuration.extension_directories !== undefined,
     })
 
+    if (process.env.REMOTE_DOM_EXPERIMENT) {
path: packages/app/src/cli/models/app/loader.ts
user: login: isaacroldan
body: use `isTruthy(process.env.REMOTE_DOM_EXPERIMENT)`, otherwise using `REMOTE_DOM_EXPERIMENT=false` would also pass this `if`.

(and following my previous comment about injecting the env -> `isTruthy(this.env.REMOTE_DOM_EXPERIMENT)`)
created_at: 2025-03-28T10:13:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -438,6 +438,10 @@ export class ExtensionInstance<TConfiguration extends BaseConfigType = BaseConfi
     this.specification.patchWithAppDevURLs(this.configuration, urls)
   }
 
+  contributeToSharedTypeFile(typeFilePath: string) {
+    return this.specification.contributeToSharedTypeFile?.(this, typeFilePath) ?? ([] as SharedType[])
+  }
path: packages/app/src/cli/models/extensions/extension-instance.ts
user: login: isaacroldan
body: If you add a return type in this function you won't need to cast the default result
created_at: 2025-03-28T10:14:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,6 +281,15 @@ async function uiExtensionInit({
       },
     },
   ]
+
+  // @todo: How to get an instance for the newly generated extension?
+  // if (process.env.REMOTE_DOM_EXPERIMENT) {
+  //   tasks.push({
+  //     title: 'Update shared type definition',
+  //     task: async () => app.generateExtensionTypes(),
+  //   })
+  // }
+
path: packages/app/src/cli/services/generate/extension.ts
user: login: isaacroldan
body: You'd need to reload the app to get it
created_at: 2025-03-28T10:19:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -389,6 +389,10 @@ class AppLoader<TConfig extends AppConfiguration, TModuleSpec extends ExtensionS
       usedCustomLayoutForExtensions: configuration.extension_directories !== undefined,
     })
 
+    if (process.env.REMOTE_DOM_EXPERIMENT) {
+      await appClass.generateExtensionTypes()
path: packages/app/src/cli/models/app/loader.ts
user: login: isaacroldan
body: can we measure how long this takes? for both cases where we don't have a type file and when we already have one.
(best to use `const start = startHRTime()` and `endHRTimeInMs(start)` for accurate values)
created_at: 2025-03-28T10:21:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -497,6 +500,38 @@ export class App<
     this.realExtensions = this.realExtensions.filter((ext) => ext.uid !== extensionUid)
   }
 
+  async generateExtensionTypes() {
+    const start = startHRTime()
path: packages/app/src/cli/models/app/app.ts
user: login: isaacroldan
body: remember to delete this before merging
created_at: 2025-04-08T14:37:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -497,6 +500,38 @@ export class App<
     this.realExtensions = this.realExtensions.filter((ext) => ext.uid !== extensionUid)
   }
 
+  async generateExtensionTypes() {
+    const start = startHRTime()
path: packages/app/src/cli/models/app/app.ts
user: login: craigmichaelmartin
body: @vividviolet is this still relevant?
created_at: 2025-04-08T16:24:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -497,6 +500,38 @@ export class App<
     this.realExtensions = this.realExtensions.filter((ext) => ext.uid !== extensionUid)
   }
 
+  async generateExtensionTypes() {
+    const start = startHRTime()
path: packages/app/src/cli/models/app/app.ts
user: login: vividviolet
body: Oops, I just pushed a commit to remove it
created_at: 2025-04-08T16:26:15Z
author_association: MEMBER
diff_hunk: @@ -1,15 +1,31 @@
 import {ExtensionTemplate} from '../../models/app/template.js'
 import {MinimalAppIdentifiers} from '../../models/organization.js'
 import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js'
+import {isPolarisUnifiedEnabled} from '@shopify/cli-kit/node/is-polaris-unified-enabled'
 
 export async function fetchExtensionTemplates(
   developerPlatformClient: DeveloperPlatformClient,
   app: MinimalAppIdentifiers,
   availableSpecifications: string[],
 ): Promise<ExtensionTemplate[]> {
   const remoteTemplates: ExtensionTemplate[] = await developerPlatformClient.templateSpecifications(app)
-  return remoteTemplates.filter(
-    (template) =>
-      availableSpecifications.includes(template.identifier) || availableSpecifications.includes(template.type),
-  )
+  const remoteDomExperimentEnabled = isPolarisUnifiedEnabled()
path: packages/app/src/cli/services/generate/fetch-template-specifications.ts
user: login: isaacroldan
body: forgot to change the name here
created_at: 2025-04-09T12:48:40Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-24T18:32:26Z
body: <!-- jest coverage report action for options with hash 4c542401e6e51275fe98fdb0130d9f77 -->

## Coverage report 



| <div title="Status of coverage:&#10; 🟢 - ok&#10; 🟡 - slightly more than threshold&#10; 🔴 - under the threshold">St.<sup>:grey_question:</sup></div> | Category   | Percentage                                                     | Covered / Total |
| :----------------------------------------------------------------------------------------------------------------------------------------------------: | :--------- | :------------------------------------------------------------- | :-------------: |
|                                                                           🟡                                                                           | Statements | <div title="Base coverage is: 76.55%">76.74% (+0.19% 🔼)</div> |    9486/12361   |
|                                                                           🟡                                                                           | Branches   | <div title="Base coverage is: 71.7%">71.85% (+0.15% 🔼)</div>  |    4648/6469    |
|                                                                           🟡                                                                           | Functions  | <div title="Base coverage is: 76.33%">76.49% (+0.16% 🔼)</div> |    2460/3216    |
|                                                                           🟡                                                                           | Lines      | <div title="Base coverage is: 77.08%">77.25% (+0.17% 🔼)</div> |    8969/11610   |

<details><summary>Show new covered files 🐣</summary>
<br/>

| <div title="Status of coverage:&#10; 🟢 - ok&#10; 🟡 - slightly more than threshold&#10; 🔴 - under the threshold">St.<sup>:grey_question:</sup></div> | File                                                                                                           | Statements | Branches | Functions | Lines |
| :----------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------- | :--------- | :------- | :-------- | :---- |
|                                                                           🟢                                                                           | <div title="cli-kit/src/public/node/is-polaris-unified-enabled.ts">`...` / is-polaris-unified-enabled.ts</div> | 100%       | 100%     | 100%      | 100%  |

</details>


<details><summary>Show files with reduced coverage 🔻</summary>
<br/>

| <div title="Status of coverage:&#10; 🟢 - ok&#10; 🟡 - slightly more than threshold&#10; 🔴 - under the threshold">St.<sup>:grey_question:</sup></div> | File                                                                                                    | Statements                                                     | Branches                                                       | Functions                                                      | Lines                                                          |
| :----------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------- | :------------------------------------------------------------- | :------------------------------------------------------------- | :------------------------------------------------------------- |
|                                                                           🟢                                                                           | <div title="app/src/cli/models/extensions/specifications/ui_extension.ts">`...` / ui_extension.ts</div> | <div title="Base coverage is: 98.08%">98.02% (-0.06% 🔻)</div> | <div title="Base coverage is: 90.63%">86.89% (-3.74% 🔻)</div> | 100%                                                           | <div title="Base coverage is: 98.08%">98% (-0.08% 🔻)</div>    |
|                                                                           🟢                                                                           | <div title="app/src/cli/services/deploy/upload.ts">`...` / upload.ts</div>                              | <div title="Base coverage is: 84.54%">80.87% (-3.67% 🔻)</div> | <div title="Base coverage is: 77.05%">74.63% (-2.42% 🔻)</div> | <div title="Base coverage is: 84.85%">84.21% (-0.64% 🔻)</div> | <div title="Base coverage is: 85.06%">81.37% (-3.68% 🔻)</div> |
|                                                                           🟢                                                                           | <div title="app/src/cli/services/generate/extension.ts">`...` / extension.ts</div>                      | <div title="Base coverage is: 91.3%">90.63% (-0.68% 🔻)</div>  | <div title="Base coverage is: 73.58%">73.21% (-0.37% 🔻)</div> | <div title="Base coverage is: 91.67%">92% (+0.33% 🔼)</div>    | <div title="Base coverage is: 91.11%">90.43% (-0.69% 🔻)</div> |

</details>

## Test suite run success
2185 tests passing in 955 suites.

<p align="right">Report generated by <a href="https://github.com/ArtiomTr/jest-coverage-report-action">🧪jest coverage report action</a> from 04477bad4c6a7ce3ba449ddfe0cc18ad4de53f6d</p>
<!-- Sticky Pull Request Comment -->
author_association: CONTRIBUTOR
user: login: vividviolet
created_at: 2025-03-27T19:35:28Z
body: /snapit
author_association: MEMBER
user: login: vividviolet
created_at: 2025-03-27T19:37:17Z
body: > ### Unlisted dependencies (1)
> Filename	unlisted
> packages/app/src/cli/models/extensions/specifications/ui_extension.ts	`@shopify/ui-extensions`

This isn't a real dependency - we try to resolve the dependency to generate the shared type definition. We ignore it if it cannot be found.
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-27T19:37:20Z
body: 🫰✨ **Thanks @vividviolet! Your snapshot has been published to npm.**

Test the snapshot by installing your package globally:
```bash
pnpm i -g @shopify/cli@0.0.0-snapshot-20250327193559
```

> [!TIP]
> If you get an `ETARGET` error, install it with NPM and the flag `--@shopify:registry=https://registry.npmjs.org`

> [!CAUTION]
> After installing, validate the version by running just `shopify` in your terminal.
> If the versions don't match, you might have multiple global instances installed.
> Use `which shopify` to find out which one you are running and uninstall it.
author_association: CONTRIBUTOR
user: login: vividviolet
created_at: 2025-03-28T04:10:13Z
body: /snapit
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-28T04:12:06Z
body: 🫰✨ **Thanks @vividviolet! Your snapshot has been published to npm.**

Test the snapshot by installing your package globally:
```bash
pnpm i -g @shopify/cli@0.0.0-snapshot-20250328041045
```

> [!TIP]
> If you get an `ETARGET` error, install it with NPM and the flag `--@shopify:registry=https://registry.npmjs.org`

> [!CAUTION]
> After installing, validate the version by running just `shopify` in your terminal.
> If the versions don't match, you might have multiple global instances installed.
> Use `which shopify` to find out which one you are running and uninstall it.
author_association: CONTRIBUTOR
user: login: vividviolet
created_at: 2025-03-28T05:41:53Z
body: /snapit
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-28T05:43:46Z
body: 🫰✨ **Thanks @vividviolet! Your snapshot has been published to npm.**

Test the snapshot by installing your package globally:
```bash
pnpm i -g @shopify/cli@0.0.0-snapshot-20250328054223
```

> [!TIP]
> If you get an `ETARGET` error, install it with NPM and the flag `--@shopify:registry=https://registry.npmjs.org`

> [!CAUTION]
> After installing, validate the version by running just `shopify` in your terminal.
> If the versions don't match, you might have multiple global instances installed.
> Use `which shopify` to find out which one you are running and uninstall it.
author_association: CONTRIBUTOR
user: login: vividviolet
created_at: 2025-03-31T18:53:30Z
body: /snapit
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-31T18:55:19Z
body: 🫰✨ **Thanks @vividviolet! Your snapshot has been published to npm.**

Test the snapshot by installing your package globally:
```bash
pnpm i -g @shopify/cli@0.0.0-snapshot-20250331185408
```

> [!TIP]
> If you get an `ETARGET` error, install it with NPM and the flag `--@shopify:registry=https://registry.npmjs.org`

> [!CAUTION]
> After installing, validate the version by running just `shopify` in your terminal.
> If the versions don't match, you might have multiple global instances installed.
> Use `which shopify` to find out which one you are running and uninstall it.
author_association: CONTRIBUTOR
user: login: vividviolet
created_at: 2025-03-31T20:55:09Z
body: /snapit
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-31T20:57:01Z
body: 🫰✨ **Thanks @vividviolet! Your snapshot has been published to npm.**

Test the snapshot by installing your package globally:
```bash
pnpm i -g @shopify/cli@0.0.0-snapshot-20250331205549
```

> [!TIP]
> If you get an `ETARGET` error, install it with NPM and the flag `--@shopify:registry=https://registry.npmjs.org`

> [!CAUTION]
> After installing, validate the version by running just `shopify` in your terminal.
> If the versions don't match, you might have multiple global instances installed.
> Use `which shopify` to find out which one you are running and uninstall it.
author_association: CONTRIBUTOR
user: login: vividviolet
created_at: 2025-04-02T21:35:13Z
body: /snapit
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-04-02T21:37:16Z
body: 🫰✨ **Thanks @vividviolet! Your snapshot has been published to npm.**

Test the snapshot by installing your package globally:
```bash
pnpm i -g @shopify/cli@0.0.0-snapshot-20250402213552
```

> [!TIP]
> If you get an `ETARGET` error, install it with NPM and the flag `--@shopify:registry=https://registry.npmjs.org`

> [!CAUTION]
> After installing, validate the version by running just `shopify` in your terminal.
> If the versions don't match, you might have multiple global instances installed.
> Use `which shopify` to find out which one you are running and uninstall it.
author_association: CONTRIBUTOR
user: login: vividviolet
created_at: 2025-04-08T17:03:07Z
body: /snapit
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-04-08T17:04:59Z
body: 🫰✨ **Thanks @vividviolet! Your snapshot has been published to npm.**

Test the snapshot by installing your package globally:
```bash
pnpm i -g @shopify/cli@0.0.0-snapshot-20250408170344
```

> [!TIP]
> If you get an `ETARGET` error, install it with NPM and the flag `--@shopify:registry=https://registry.npmjs.org`

> [!CAUTION]
> After installing, validate the version by running just `shopify` in your terminal.
> If the versions don't match, you might have multiple global instances installed.
> Use `which shopify` to find out which one you are running and uninstall it.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-08T21:55:17Z
body: ## Differences in type declarations
We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to **ensure they are backward-compatible.** Here are some important things to keep in mind:
- Some seemingly private modules might be re-exported through public modules.
- If the branch is behind `main` you might see odd diffs, rebase `main` into this branch.

### New type declarations

<details>
<summary>
packages/cli-kit/dist/public/node/is-polaris-unified-enabled.d.ts
</summary>

```diff
/**
 * Returns true if the POLARIS_UNIFIED environment variable is set to true.
 *
 * @returns `true` if the POLARIS_UNIFIED environment variable is set to true.
 */
export declare function isPolarisUnifiedEnabled(): boolean;

```

</details>

### Existing type declarations
We found no diffs with existing type declarations
<!-- Sticky Pull Request CommentType-diff -->
author_association: CONTRIBUTOR