From 71568bd13fc91fdb003ad67ef36897c5a4bb697a Mon Sep 17 00:00:00 2001 From: Elana Kopelevich Date: Fri, 3 Apr 2026 17:19:08 -0600 Subject: [PATCH 1/3] Remove HOSTED_APPS env var and make extension-only template the default The new shopify-app-template-extension-only template was previously gated behind the HOSTED_APPS env var (PR #7096). This removes the env var entirely and makes the hosted app template the default for the "none" (extension-only) option in `shopify app init`. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/src/cli/prompts/init/init.test.ts | 50 ++++--------------- packages/app/src/cli/prompts/init/init.ts | 19 +++---- .../cli-kit/src/private/node/constants.ts | 1 - .../src/public/node/context/local.test.ts | 25 ---------- .../cli-kit/src/public/node/context/local.ts | 9 ---- 5 files changed, 16 insertions(+), 88 deletions(-) diff --git a/packages/app/src/cli/prompts/init/init.test.ts b/packages/app/src/cli/prompts/init/init.test.ts index af3ee97d02..c28c8e3043 100644 --- a/packages/app/src/cli/prompts/init/init.test.ts +++ b/packages/app/src/cli/prompts/init/init.test.ts @@ -1,12 +1,10 @@ -import init, {buildNoneTemplate, InitOptions, visibleTemplates} from './init.js' +import init, {InitOptions} from './init.js' import {describe, expect, vi, test, beforeEach} from 'vitest' import {renderSelectPrompt} from '@shopify/cli-kit/node/ui' import {installGlobalCLIPrompt} from '@shopify/cli-kit/node/is-global' -import {isHostedAppsMode} from '@shopify/cli-kit/node/context/local' vi.mock('@shopify/cli-kit/node/ui') vi.mock('@shopify/cli-kit/node/is-global') -vi.mock('@shopify/cli-kit/node/context/local') const globalCLIResult = {install: true, alreadyInstalled: false} @@ -17,7 +15,7 @@ describe('init', () => { test('it renders the label for the template options', async () => { const answers = { - template: 'https://github.com/Shopify/shopify-app-template-none', + template: 'https://github.com/Shopify/shopify-app-template-extension-only', } const options: InitOptions = {} @@ -31,7 +29,10 @@ describe('init', () => { expect(renderSelectPrompt).toHaveBeenCalledWith({ choices: [ {label: 'Build a React Router app (recommended)', value: 'reactRouter'}, - {label: 'Build an extension-only app', value: 'none'}, + { + label: 'Build an extension-only app (Shopify-hosted Preact app home and extensions, no back-end)', + value: 'none', + }, ], message: 'Get started building your app:', defaultValue: 'reactRouter', @@ -39,40 +40,6 @@ describe('init', () => { expect(got).toEqual({...options, ...answers, templateType: 'none', globalCLIResult}) }) - describe('visibleTemplates', () => { - test('omits the deprecated remix template so it is not advertised in --template help text', () => { - expect(visibleTemplates).not.toContain('remix') - }) - - test('exposes only the templates that are still actively offered', () => { - expect(visibleTemplates).toEqual(['reactRouter', 'none']) - }) - }) - - describe('buildNoneTemplate', () => { - test('returns extension-only template URL when HOSTED_APPS is enabled', () => { - // Given - vi.mocked(isHostedAppsMode).mockReturnValue(true) - - // When - const got = buildNoneTemplate() - - // Then - expect(got.url).toBe('https://github.com/Shopify/shopify-app-template-extension-only') - }) - - test('returns default template URL when HOSTED_APPS is not set', () => { - // Given - vi.mocked(isHostedAppsMode).mockReturnValue(false) - - // When - const got = buildNoneTemplate() - - // Then - expect(got.url).toBe('https://github.com/Shopify/shopify-app-template-none') - }) - }) - test('it renders branches for templates that have them', async () => { const answers = { template: 'https://github.com/Shopify/shopify-app-template-react-router#javascript-cli', @@ -90,7 +57,10 @@ describe('init', () => { expect(renderSelectPrompt).toHaveBeenCalledWith({ choices: [ {label: 'Build a React Router app (recommended)', value: 'reactRouter'}, - {label: 'Build an extension-only app', value: 'none'}, + { + label: 'Build an extension-only app (Shopify-hosted Preact app home and extensions, no back-end)', + value: 'none', + }, ], message: 'Get started building your app:', defaultValue: 'reactRouter', diff --git a/packages/app/src/cli/prompts/init/init.ts b/packages/app/src/cli/prompts/init/init.ts index 5b840ba005..8bd5b1d8ce 100644 --- a/packages/app/src/cli/prompts/init/init.ts +++ b/packages/app/src/cli/prompts/init/init.ts @@ -1,5 +1,4 @@ import {InstallGlobalCLIPromptResult, installGlobalCLIPrompt} from '@shopify/cli-kit/node/is-global' -import {isHostedAppsMode} from '@shopify/cli-kit/node/context/local' import {renderSelectPrompt} from '@shopify/cli-kit/node/ui' export interface InitOptions { @@ -29,16 +28,6 @@ interface Template { } } -export function buildNoneTemplate(): Template { - return { - url: isHostedAppsMode() - ? 'https://github.com/Shopify/shopify-app-template-extension-only' - : 'https://github.com/Shopify/shopify-app-template-none', - label: 'Build an extension-only app', - visible: true, - } -} - // Eventually this list should be taken from a remote location // That way we don't have to update the CLI every time we add a template export const templates = { @@ -68,7 +57,11 @@ export const templates = { }, }, } as Template, - none: buildNoneTemplate(), + none: { + url: 'https://github.com/Shopify/shopify-app-template-extension-only', + label: 'Build an extension-only app (Shopify-hosted Preact app home and extensions, no back-end)', + visible: true, + } as Template, node: { url: 'https://github.com/Shopify/shopify-app-template-node', visible: false, @@ -77,7 +70,7 @@ export const templates = { url: 'https://github.com/Shopify/shopify-app-template-ruby', visible: false, } as Template, -} +} as const type PredefinedTemplate = keyof typeof templates const allTemplates = Object.keys(templates) as Readonly diff --git a/packages/cli-kit/src/private/node/constants.ts b/packages/cli-kit/src/private/node/constants.ts index 3b777bc319..a38e83f2d9 100644 --- a/packages/cli-kit/src/private/node/constants.ts +++ b/packages/cli-kit/src/private/node/constants.ts @@ -20,7 +20,6 @@ export const environmentVariables = { enableCliRedirect: 'SHOPIFY_CLI_ENABLE_CLI_REDIRECT', env: 'SHOPIFY_CLI_ENV', firstPartyDev: 'SHOPIFY_CLI_1P_DEV', - hostedApps: 'HOSTED_APPS', noAnalytics: 'SHOPIFY_CLI_NO_ANALYTICS', optOutInstrumentation: 'OPT_OUT_INSTRUMENTATION', appAutomationToken: 'SHOPIFY_APP_AUTOMATION_TOKEN', diff --git a/packages/cli-kit/src/public/node/context/local.test.ts b/packages/cli-kit/src/public/node/context/local.test.ts index db141564ce..abe71bb52b 100644 --- a/packages/cli-kit/src/public/node/context/local.test.ts +++ b/packages/cli-kit/src/public/node/context/local.test.ts @@ -2,7 +2,6 @@ import { ciPlatform, hasGit, isDevelopment, - isHostedAppsMode, isShopify, isTerminalInteractive, isUnitTest, @@ -84,30 +83,6 @@ describe('isUnitTest', () => { }) }) -describe('isHostedAppsMode', () => { - test('returns true when HOSTED_APPS is truthy', () => { - // Given - const env = {HOSTED_APPS: '1'} - - // When - const got = isHostedAppsMode(env) - - // Then - expect(got).toBe(true) - }) - - test('returns false when HOSTED_APPS is not set', () => { - // Given - const env = {} - - // When - const got = isHostedAppsMode(env) - - // Then - expect(got).toBe(false) - }) -}) - describe('isDevelopment', () => { test('returns true when SHOPIFY_CLI_ENV is debug', () => { // Given diff --git a/packages/cli-kit/src/public/node/context/local.ts b/packages/cli-kit/src/public/node/context/local.ts index c2ae8dce2e..2264bd596c 100644 --- a/packages/cli-kit/src/public/node/context/local.ts +++ b/packages/cli-kit/src/public/node/context/local.ts @@ -101,15 +101,6 @@ export function isUnitTest(env = process.env): boolean { return isTruthy(env[environmentVariables.unitTest]) } -/** - * Returns true if the CLI is running in hosted apps mode. - * - * @param env - The environment variables from the environment of the current process. - * @returns True if the HOSTED_APPS environment variable is truthy. - */ -export function isHostedAppsMode(env = process.env): boolean { - return isTruthy(env[environmentVariables.hostedApps]) -} /** * Returns true if reporting analytics is enabled. From 8943b1992451288d2e3df17f224efb394d8540b0 Mon Sep 17 00:00:00 2001 From: Mitch Lillie Date: Tue, 28 Apr 2026 10:54:57 -0700 Subject: [PATCH 2/3] Remove custom label for extension only apps --- .changeset/ninety-doodles-scream.md | 6 ++++++ .../app/src/cli/prompts/init/init.test.ts | 19 +++++++++++++------ packages/app/src/cli/prompts/init/init.ts | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 .changeset/ninety-doodles-scream.md diff --git a/.changeset/ninety-doodles-scream.md b/.changeset/ninety-doodles-scream.md new file mode 100644 index 0000000000..ec3bd6005c --- /dev/null +++ b/.changeset/ninety-doodles-scream.md @@ -0,0 +1,6 @@ +--- +'@shopify/cli-kit': minor +'@shopify/app': minor +--- + +Update extension-only template to include app home by default diff --git a/packages/app/src/cli/prompts/init/init.test.ts b/packages/app/src/cli/prompts/init/init.test.ts index c28c8e3043..a4a0644475 100644 --- a/packages/app/src/cli/prompts/init/init.test.ts +++ b/packages/app/src/cli/prompts/init/init.test.ts @@ -1,4 +1,4 @@ -import init, {InitOptions} from './init.js' +import init, {InitOptions, visibleTemplates} from './init.js' import {describe, expect, vi, test, beforeEach} from 'vitest' import {renderSelectPrompt} from '@shopify/cli-kit/node/ui' import {installGlobalCLIPrompt} from '@shopify/cli-kit/node/is-global' @@ -29,10 +29,7 @@ describe('init', () => { expect(renderSelectPrompt).toHaveBeenCalledWith({ choices: [ {label: 'Build a React Router app (recommended)', value: 'reactRouter'}, - { - label: 'Build an extension-only app (Shopify-hosted Preact app home and extensions, no back-end)', - value: 'none', - }, + {label: 'Build an extension-only app', value: 'none'}, ], message: 'Get started building your app:', defaultValue: 'reactRouter', @@ -40,6 +37,16 @@ describe('init', () => { expect(got).toEqual({...options, ...answers, templateType: 'none', globalCLIResult}) }) + describe('visibleTemplates', () => { + test('omits the deprecated remix template so it is not advertised in --template help text', () => { + expect(visibleTemplates).not.toContain('remix') + }) + + test('exposes only the templates that are still actively offered', () => { + expect(visibleTemplates).toEqual(['reactRouter', 'none']) + }) + }) + test('it renders branches for templates that have them', async () => { const answers = { template: 'https://github.com/Shopify/shopify-app-template-react-router#javascript-cli', @@ -58,7 +65,7 @@ describe('init', () => { choices: [ {label: 'Build a React Router app (recommended)', value: 'reactRouter'}, { - label: 'Build an extension-only app (Shopify-hosted Preact app home and extensions, no back-end)', + label: 'Build an extension-only app', value: 'none', }, ], diff --git a/packages/app/src/cli/prompts/init/init.ts b/packages/app/src/cli/prompts/init/init.ts index 8bd5b1d8ce..dc9d1c51e3 100644 --- a/packages/app/src/cli/prompts/init/init.ts +++ b/packages/app/src/cli/prompts/init/init.ts @@ -59,7 +59,7 @@ export const templates = { } as Template, none: { url: 'https://github.com/Shopify/shopify-app-template-extension-only', - label: 'Build an extension-only app (Shopify-hosted Preact app home and extensions, no back-end)', + label: 'Build an extension-only app', visible: true, } as Template, node: { From 9555d8cb8bb2ad011152158e2c5766cf127a00c9 Mon Sep 17 00:00:00 2001 From: Mitch Lillie Date: Tue, 26 May 2026 08:27:50 -0700 Subject: [PATCH 3/3] Fix lint after conflict resolution --- packages/cli-kit/src/public/node/context/local.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli-kit/src/public/node/context/local.ts b/packages/cli-kit/src/public/node/context/local.ts index 2264bd596c..ad816399db 100644 --- a/packages/cli-kit/src/public/node/context/local.ts +++ b/packages/cli-kit/src/public/node/context/local.ts @@ -101,7 +101,6 @@ export function isUnitTest(env = process.env): boolean { return isTruthy(env[environmentVariables.unitTest]) } - /** * Returns true if reporting analytics is enabled. *