From 1bf342d8f0e5d74e4e8a2337383c63af4d0c8a99 Mon Sep 17 00:00:00 2001 From: River Date: Wed, 6 May 2026 16:42:41 +0000 Subject: [PATCH 1/2] Fix uid placement for single-entry [[extensions]] TOMLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit addUidToToml branched on `extensionsArray.length > 1` to decide between the regex-based positional insertion (inside the matching [[extensions]] block) and a top-level WASM patch. A single-entry [[extensions]] array fell into the top-level branch, so `uid` was written outside the [[extensions]] block — the wrong location. This is the shape produced by current `shopify app init` templates (e.g. ui_extension app-home), so every newly-init'd app from those templates ended up with a top-level `uid` after the first `linkedAppContext` run. Fix: branch on whether `extensionsArray` exists at all. Any array shape (length 1 or many) gets the regex insertion path. Legacy single-extension TOMLs with top-level `type`/`handle` fields still get the top-level `file.patch` path. Adds a regression test using the exact app init template TOML shape. Co-authored-by: Nick Wesselman --- ...ix-uid-placement-single-extension-array.md | 5 ++ .../app/add-uid-to-extension-toml.test.ts | 57 +++++++++++++++++++ .../services/app/add-uid-to-extension-toml.ts | 11 ++-- 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 .changeset/fix-uid-placement-single-extension-array.md diff --git a/.changeset/fix-uid-placement-single-extension-array.md b/.changeset/fix-uid-placement-single-extension-array.md new file mode 100644 index 00000000000..67fa8a0ff02 --- /dev/null +++ b/.changeset/fix-uid-placement-single-extension-array.md @@ -0,0 +1,5 @@ +--- +'@shopify/app': patch +--- + +Fix `uid` being inserted at the top level of extension TOML files (outside the `[[extensions]]` block) during `shopify app init` and other `linkedAppContext` flows when the TOML uses the modern `[[extensions]]` array-of-tables shape with a single entry — the shape produced by current `app init` templates. Previously, single-entry arrays fell into the legacy top-level `file.patch` branch; `uid` is now inserted inside the `[[extensions]]` block (after `handle`) for any array shape, regardless of length. Legacy single-extension TOMLs with top-level `type`/`handle` fields continue to use the top-level patch path. diff --git a/packages/app/src/cli/services/app/add-uid-to-extension-toml.test.ts b/packages/app/src/cli/services/app/add-uid-to-extension-toml.test.ts index 1406a8ce833..e3d91805dfd 100644 --- a/packages/app/src/cli/services/app/add-uid-to-extension-toml.test.ts +++ b/packages/app/src/cli/services/app/add-uid-to-extension-toml.test.ts @@ -67,6 +67,63 @@ describe('addUidToTomlsIfNecessary', () => { }) }) + test('adds uid inside the [[extensions]] block for a single-entry array TOML (matching the app init template shape)', async () => { + await inTemporaryDirectory(async (tmpDir) => { + // Given — a TOML using the modern `[[extensions]]` array-of-tables shape with a + // single extension. This is the shape produced by `shopify app init` templates. + const tomlPath = joinPath(tmpDir, 'shopify.extension.toml') + const tomlContent = `api_version = "2026-07" + +[[extensions]] +# Change the merchant-facing name of the extension in locales/en.default.json +name = "t:name" +handle = "app-home" +type = "ui_extension" + +[[extensions.targeting]] +module = "./src/AppHome.jsx" +target = "admin.app.home.render" + +[access_scopes] +scopes = "write_metaobject_definitions,write_metaobjects,write_products" +` + await writeFile(tomlPath, tomlContent) + + const extension = { + configurationPath: tomlPath, + handle: 'app-home', + isUUIDStrategyExtension: true, + uid: 'abc-123', + configuration: {}, + } as ExtensionInstance + + const client = testDeveloperPlatformClient({supportsAtomicDeployments: true}) + + // When + await addUidToTomlsIfNecessary([extension], client) + + // Then — uid must be inside the [[extensions]] block (right after handle), not at + // the top level of the file. + const updatedContent = await readFile(tomlPath) + expect(updatedContent).toBe(`api_version = "2026-07" + +[[extensions]] +# Change the merchant-facing name of the extension in locales/en.default.json +name = "t:name" +handle = "app-home" +uid = "abc-123" +type = "ui_extension" + +[[extensions.targeting]] +module = "./src/AppHome.jsx" +target = "admin.app.home.render" + +[access_scopes] +scopes = "write_metaobject_definitions,write_metaobjects,write_products" +`) + }) + }) + test('adds uid to multi-extension TOML', async () => { await inTemporaryDirectory(async (tmpDir) => { // Given diff --git a/packages/app/src/cli/services/app/add-uid-to-extension-toml.ts b/packages/app/src/cli/services/app/add-uid-to-extension-toml.ts index da816fecc0e..34b4aa17b2f 100644 --- a/packages/app/src/cli/services/app/add-uid-to-extension-toml.ts +++ b/packages/app/src/cli/services/app/add-uid-to-extension-toml.ts @@ -28,17 +28,20 @@ async function addUidToToml(extension: ExtensionInstance) { if (currentExtension && 'uid' in currentExtension) return } - if (extensionsArray && extensionsArray.length > 1) { - // Multi-extension TOML: use regex to insert uid after the correct handle. + if (extensionsArray) { + // [[extensions]] array-of-tables TOML: use regex to insert uid after the correct handle. // updateTomlValues (WASM) doesn't support patching individual array-of-tables entries, - // so transformRaw with positional insertion is the pragmatic choice here. + // so transformRaw with positional insertion is the pragmatic choice here. This applies + // whether the array has one entry or many — putting `uid` at the top level of the file + // would place it outside the [[extensions]] block, which is the wrong location. const handle = extension.handle await file.transformRaw((raw) => { const regex = new RegExp(`(\\n?(\\s*)handle\\s*=\\s*"${handle}")`) return raw.replace(regex, `$1\n$2uid = "${extension.uid}"`) }) } else { - // Single extension (or no extensions array): add uid at the top level via WASM patch + // Legacy single-extension TOML with top-level `type`/`handle` fields: add uid at the + // top level via WASM patch. await file.patch({uid: extension.uid}) } } From 35b2aa9c2508621cdac1e2a82ac5d1d8875ce415 Mon Sep 17 00:00:00 2001 From: Nick Wesselman <27013789+nickwesselman@users.noreply.github.com> Date: Wed, 6 May 2026 13:19:07 -0400 Subject: [PATCH 2/2] Shorten changeset entry --- .changeset/fix-uid-placement-single-extension-array.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fix-uid-placement-single-extension-array.md b/.changeset/fix-uid-placement-single-extension-array.md index 67fa8a0ff02..889da76dfdb 100644 --- a/.changeset/fix-uid-placement-single-extension-array.md +++ b/.changeset/fix-uid-placement-single-extension-array.md @@ -2,4 +2,4 @@ '@shopify/app': patch --- -Fix `uid` being inserted at the top level of extension TOML files (outside the `[[extensions]]` block) during `shopify app init` and other `linkedAppContext` flows when the TOML uses the modern `[[extensions]]` array-of-tables shape with a single entry — the shape produced by current `app init` templates. Previously, single-entry arrays fell into the legacy top-level `file.patch` branch; `uid` is now inserted inside the `[[extensions]]` block (after `handle`) for any array shape, regardless of length. Legacy single-extension TOMLs with top-level `type`/`handle` fields continue to use the top-level patch path. +Fix `uid` being written outside the `[[extensions]]` block in single-entry array-of-tables TOMLs (the shape produced by `shopify app init` templates).