Skip to content

Commit 0718cf2

Browse files
committed
Remove SHOPIFY_CLI_DISABLE_WASM_TOML_PATCH opt-out
1 parent 817bf4b commit 0718cf2

File tree

10 files changed

+83
-265
lines changed

10 files changed

+83
-265
lines changed

.changeset/grumpy-doors-march.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/app': minor
3+
---
4+
5+
Remove opt-out of TOML patching with SHOPIFY_CLI_DISABLE_WASM_TOML_PATCH

packages/app/src/cli/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export const environmentVariableNames = {
88
enableUnsupportedConfigPropertyChecks: 'SHOPIFY_CLI_ENABLE_UNSUPPORTED_CONFIG_PROPERTY_CHECKS',
99
disableUnsupportedConfigPropertyChecks: 'SHOPIFY_CLI_DISABLE_UNSUPPORTED_CONFIG_PROPERTY_CHECKS',
1010
disableMinificationOnDev: 'SHOPIFY_CLI_DISABLE_MINIFICATION_ON_DEV',
11-
disableWasmTomlPatch: 'SHOPIFY_CLI_DISABLE_WASM_TOML_PATCH',
1211
}
1312

1413
export const configurationFileNames = {

packages/app/src/cli/services/app/patch-app-configuration-file.test.ts

Lines changed: 31 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@ import {
44
unsetAppConfigValue,
55
setManyAppConfigValues,
66
} from './patch-app-configuration-file.js'
7-
import {getAppVersionedSchema} from '../../models/app/app.js'
8-
import {loadLocalExtensionsSpecifications} from '../../models/extensions/load-specifications.js'
9-
import {environmentVariableNames} from '../../constants.js'
107
import {readFile, writeFileSync, inTemporaryDirectory} from '@shopify/cli-kit/node/fs'
118
import {joinPath} from '@shopify/cli-kit/node/path'
12-
import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest'
9+
import {describe, expect, test} from 'vitest'
1310

1411
const defaultToml = `# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
1512
client_id = "12345"
@@ -31,8 +28,6 @@ redirect_urls = [
3128
api_version = "2023-04"
3229
`
3330

34-
const schema = getAppVersionedSchema(await loadLocalExtensionsSpecifications(), false)
35-
3631
function writeDefaulToml(tmpDir: string) {
3732
const configPath = joinPath(tmpDir, 'shopify.app.toml')
3833
writeFileSync(configPath, defaultToml)
@@ -120,20 +115,12 @@ describe('patchAppHiddenConfigFile', () => {
120115
})
121116
})
122117

123-
describe.each([false, true])('setAppConfigValue (disableWasmTomlPatch: %s)', (disableWasmTomlPatch) => {
124-
beforeEach(() => {
125-
vi.stubEnv(environmentVariableNames.disableWasmTomlPatch, disableWasmTomlPatch.toString())
126-
})
127-
128-
afterEach(() => {
129-
vi.unstubAllEnvs()
130-
})
131-
118+
describe('setAppConfigValue', () => {
132119
test('sets a top-level value in the configuration', async () => {
133120
await inTemporaryDirectory(async (tmpDir) => {
134121
const configPath = writeDefaulToml(tmpDir)
135122

136-
await setAppConfigValue(configPath, 'name', 'Updated App Name', schema)
123+
await setAppConfigValue(configPath, 'name', 'Updated App Name')
137124

138125
const updatedTomlFile = await readFile(configPath)
139126
expect(updatedTomlFile).toContain('name = "Updated App Name"')
@@ -144,7 +131,7 @@ describe.each([false, true])('setAppConfigValue (disableWasmTomlPatch: %s)', (di
144131
await inTemporaryDirectory(async (tmpDir) => {
145132
const configPath = writeDefaulToml(tmpDir)
146133

147-
await setAppConfigValue(configPath, 'build.dev_store_url', 'example.myshopify.com', schema)
134+
await setAppConfigValue(configPath, 'build.dev_store_url', 'example.myshopify.com')
148135

149136
const updatedTomlFile = await readFile(configPath)
150137
expect(updatedTomlFile).toContain('[build]')
@@ -156,7 +143,7 @@ describe.each([false, true])('setAppConfigValue (disableWasmTomlPatch: %s)', (di
156143
await inTemporaryDirectory(async (tmpDir) => {
157144
const configPath = writeDefaulToml(tmpDir)
158145

159-
await setAppConfigValue(configPath, 'build.auth.settings', true, schema)
146+
await setAppConfigValue(configPath, 'build.auth.settings', true)
160147

161148
const updatedTomlFile = await readFile(configPath)
162149
expect(updatedTomlFile).toContain('[build.auth]')
@@ -165,20 +152,12 @@ describe.each([false, true])('setAppConfigValue (disableWasmTomlPatch: %s)', (di
165152
})
166153
})
167154

168-
describe.each([false, true])('unsetAppConfigValue (disableWasmTomlPatch: %s)', (disableWasmTomlPatch) => {
169-
beforeEach(() => {
170-
vi.stubEnv(environmentVariableNames.disableWasmTomlPatch, disableWasmTomlPatch.toString())
171-
})
172-
173-
afterEach(() => {
174-
vi.unstubAllEnvs()
175-
})
176-
155+
describe('unsetAppConfigValue', () => {
177156
test('unsets a top-level value in the configuration', async () => {
178157
await inTemporaryDirectory(async (tmpDir) => {
179158
const configPath = writeDefaulToml(tmpDir)
180159

181-
await unsetAppConfigValue(configPath, 'name', schema)
160+
await unsetAppConfigValue(configPath, 'name')
182161

183162
const updatedTomlFile = await readFile(configPath)
184163
expect(updatedTomlFile).not.toContain('name = "app1"')
@@ -190,10 +169,10 @@ describe.each([false, true])('unsetAppConfigValue (disableWasmTomlPatch: %s)', (
190169
const configPath = writeDefaulToml(tmpDir)
191170

192171
// Add a value first
193-
await setAppConfigValue(configPath, 'build.dev_store_url', 'example.myshopify.com', schema)
172+
await setAppConfigValue(configPath, 'build.dev_store_url', 'example.myshopify.com')
194173

195174
// Now unset it
196-
await unsetAppConfigValue(configPath, 'build.dev_store_url', schema)
175+
await unsetAppConfigValue(configPath, 'build.dev_store_url')
197176

198177
const updatedTomlFile = await readFile(configPath)
199178
expect(updatedTomlFile).toContain('[build]')
@@ -202,27 +181,15 @@ describe.each([false, true])('unsetAppConfigValue (disableWasmTomlPatch: %s)', (
202181
})
203182
})
204183

205-
describe.each([false, true])('setManyAppConfigValues (disableWasmTomlPatch: %s)', (disableWasmTomlPatch) => {
206-
beforeEach(() => {
207-
vi.stubEnv(environmentVariableNames.disableWasmTomlPatch, disableWasmTomlPatch.toString())
208-
})
209-
210-
afterEach(() => {
211-
vi.unstubAllEnvs()
212-
})
213-
184+
describe('setManyAppConfigValues', () => {
214185
test('sets multiple top-level values in the configuration', async () => {
215186
await inTemporaryDirectory(async (tmpDir) => {
216187
const configPath = writeDefaulToml(tmpDir)
217188

218-
await setManyAppConfigValues(
219-
configPath,
220-
[
221-
{keyPath: 'name', value: 'Updated App Name'},
222-
{keyPath: 'client_id', value: '67890'},
223-
],
224-
schema,
225-
)
189+
await setManyAppConfigValues(configPath, [
190+
{keyPath: 'name', value: 'Updated App Name'},
191+
{keyPath: 'client_id', value: '67890'},
192+
])
226193

227194
const updatedTomlFile = await readFile(configPath)
228195
expect(updatedTomlFile).toContain('name = "Updated App Name"')
@@ -234,14 +201,10 @@ describe.each([false, true])('setManyAppConfigValues (disableWasmTomlPatch: %s)'
234201
await inTemporaryDirectory(async (tmpDir) => {
235202
const configPath = writeDefaulToml(tmpDir)
236203

237-
await setManyAppConfigValues(
238-
configPath,
239-
[
240-
{keyPath: 'name', value: 'Updated App Name'},
241-
{keyPath: 'build.dev_store_url', value: 'example.myshopify.com'},
242-
],
243-
schema,
244-
)
204+
await setManyAppConfigValues(configPath, [
205+
{keyPath: 'name', value: 'Updated App Name'},
206+
{keyPath: 'build.dev_store_url', value: 'example.myshopify.com'},
207+
])
245208

246209
const updatedTomlFile = await readFile(configPath)
247210
expect(updatedTomlFile).toContain('name = "Updated App Name"')
@@ -254,11 +217,9 @@ describe.each([false, true])('setManyAppConfigValues (disableWasmTomlPatch: %s)'
254217
await inTemporaryDirectory(async (tmpDir) => {
255218
const configPath = writeDefaulToml(tmpDir)
256219

257-
await setManyAppConfigValues(
258-
configPath,
259-
[{keyPath: 'auth.redirect_urls', value: ['https://example.com/redirect3', 'https://example.com/redirect4']}],
260-
schema,
261-
)
220+
await setManyAppConfigValues(configPath, [
221+
{keyPath: 'auth.redirect_urls', value: ['https://example.com/redirect3', 'https://example.com/redirect4']},
222+
])
262223

263224
const updatedTomlFile = await readFile(configPath)
264225
expect(updatedTomlFile).toContain('[auth]')
@@ -274,14 +235,10 @@ describe.each([false, true])('setManyAppConfigValues (disableWasmTomlPatch: %s)'
274235
await inTemporaryDirectory(async (tmpDir) => {
275236
const configPath = writeDefaulToml(tmpDir)
276237

277-
await setManyAppConfigValues(
278-
configPath,
279-
[
280-
{keyPath: 'build.dev_store_url', value: 'example.myshopify.com'},
281-
{keyPath: 'build.automatically_update_urls_on_dev', value: true},
282-
],
283-
schema,
284-
)
238+
await setManyAppConfigValues(configPath, [
239+
{keyPath: 'build.dev_store_url', value: 'example.myshopify.com'},
240+
{keyPath: 'build.automatically_update_urls_on_dev', value: true},
241+
])
285242

286243
const updatedTomlFile = await readFile(configPath)
287244
expect(updatedTomlFile).toContain('[build]')
@@ -294,16 +251,12 @@ describe.each([false, true])('setManyAppConfigValues (disableWasmTomlPatch: %s)'
294251
await inTemporaryDirectory(async (tmpDir) => {
295252
const configPath = writeDefaulToml(tmpDir)
296253

297-
await setManyAppConfigValues(
298-
configPath,
299-
[
300-
{keyPath: 'name', value: 'Updated App Name'},
301-
{keyPath: 'application_url', value: 'https://example.com'},
302-
{keyPath: 'access_scopes.use_legacy_install_flow', value: false},
303-
{keyPath: 'auth.redirect_urls', value: ['https://example.com/redirect3', 'https://example.com/redirect4']},
304-
],
305-
schema,
306-
)
254+
await setManyAppConfigValues(configPath, [
255+
{keyPath: 'name', value: 'Updated App Name'},
256+
{keyPath: 'application_url', value: 'https://example.com'},
257+
{keyPath: 'access_scopes.use_legacy_install_flow', value: false},
258+
{keyPath: 'auth.redirect_urls', value: ['https://example.com/redirect3', 'https://example.com/redirect4']},
259+
])
307260

308261
const updatedTomlFile = await readFile(configPath)
309262
expect(updatedTomlFile).toContain('name = "Updated App Name"')

packages/app/src/cli/services/app/patch-app-configuration-file.ts

Lines changed: 8 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import {addDefaultCommentsToToml} from './write-app-configuration-file.js'
21
import {AppHiddenConfig} from '../../models/app/app.js'
3-
import {environmentVariableNames} from '../../constants.js'
42
import {deepMergeObjects} from '@shopify/cli-kit/common/object'
53
import {readFile, writeFile} from '@shopify/cli-kit/node/fs'
64
import {zod} from '@shopify/cli-kit/node/schema'
7-
import {decodeToml, encodeToml} from '@shopify/cli-kit/node/toml'
8-
import {isTruthy} from '@shopify/cli-kit/node/context/utilities'
95
import {updateTomlValues} from '@shopify/toml-patch'
106

117
export interface PatchTomlOptions {
@@ -16,41 +12,6 @@ export interface PatchTomlOptions {
1612

1713
type TomlPatchValue = string | number | boolean | undefined | string[]
1814

19-
function shouldUseWasmTomlPatch(env = process.env): boolean {
20-
return !isTruthy(env[environmentVariableNames.disableWasmTomlPatch])
21-
}
22-
23-
/**
24-
* Updates an app configuration file with the given patch.
25-
*
26-
* Only updates the given fields in the patch and leaves the rest of the file unchanged.
27-
* Keeps the same order of the keys as the original file.
28-
*
29-
* New keys are always added at the end of the file.
30-
*
31-
* @param path - The path to the app configuration file.
32-
* @param patch - The patch to apply to the app configuration file.
33-
* @param schema - The schema to validate the patch against. If not provided, the toml will not be validated.
34-
* @internal Internal function, use setAppConfigValue, unsetAppConfigValue, or setManyAppConfigValues instead
35-
*/
36-
async function patchAppConfigurationFile({path, patch, schema}: PatchTomlOptions) {
37-
const tomlContents = await readFile(path)
38-
const configuration = decodeToml(tomlContents)
39-
40-
// Deep merge the configuration with the patch.
41-
// Use replaceArrayStrategy to replace the destination array with the source array. (Arrays are not merged)
42-
const updatedConfig = deepMergeObjects(configuration, patch, replaceArrayStrategy)
43-
44-
// Re-parse the config with the schema to validate the patch
45-
// Make every field optional to not crash on tomls that are missing fields.
46-
const validSchema = schema ?? zod.object({}).passthrough()
47-
validSchema.partial().parse(updatedConfig)
48-
49-
let encodedString = encodeToml(updatedConfig)
50-
encodedString = addDefaultCommentsToToml(encodedString)
51-
await writeFile(path, encodedString)
52-
}
53-
5415
async function patchAppConfigurationFileWithWasm(
5516
path: string,
5617
configValues: {keyPath: string; value: TomlPatchValue}[],
@@ -69,98 +30,40 @@ async function patchAppConfigurationFileWithWasm(
6930
* @param path - The path to the app configuration file.
7031
* @param keyPath - The dotted key path to set the value at (e.g. 'build.dev_store_url')
7132
* @param value - The value to set
72-
* @param schema - The schema to validate the patch against. If not provided, the toml will not be validated.
7333
*/
74-
export async function setAppConfigValue(
75-
path: string,
76-
keyPath: string,
77-
value: TomlPatchValue,
78-
schema?: zod.AnyZodObject,
79-
) {
80-
if (shouldUseWasmTomlPatch()) {
81-
return patchAppConfigurationFileWithWasm(path, [{keyPath, value}])
82-
}
83-
const patch = createPatchFromDottedPath(keyPath, value)
84-
await patchAppConfigurationFile({path, patch, schema})
34+
export async function setAppConfigValue(path: string, keyPath: string, value: TomlPatchValue) {
35+
return patchAppConfigurationFileWithWasm(path, [{keyPath, value}])
8536
}
8637

8738
/**
8839
* Sets multiple values in the app configuration file.
8940
*
9041
* @param path - The path to the app configuration file
9142
* @param configValues - Array of keyPath and value pairs to set
92-
* @param schema - The schema to validate the patch against. If not provided, the toml will not be validated.
9343
*
9444
* @example
9545
* ```ts
9646
* await setManyAppConfigValues('shopify.app.toml', [
9747
* { keyPath: 'application_url', value: 'https://example.com' },
9848
* { keyPath: 'auth.redirect_urls', value: ['https://example.com/callback'] }
99-
* ], schema)
49+
* ])
10050
* ```
10151
*/
102-
export async function setManyAppConfigValues(
103-
path: string,
104-
configValues: {keyPath: string; value: TomlPatchValue}[],
105-
schema?: zod.AnyZodObject,
106-
) {
107-
if (shouldUseWasmTomlPatch()) {
108-
return patchAppConfigurationFileWithWasm(path, configValues)
109-
}
110-
const patch = configValues.reduce((acc, {keyPath, value}) => {
111-
const valuePatch = createPatchFromDottedPath(keyPath, value)
112-
return deepMergeObjects(acc, valuePatch, replaceArrayStrategy)
113-
}, {})
114-
115-
await patchAppConfigurationFile({path, patch, schema})
52+
export async function setManyAppConfigValues(path: string, configValues: {keyPath: string; value: TomlPatchValue}[]) {
53+
return patchAppConfigurationFileWithWasm(path, configValues)
11654
}
11755

11856
/**
11957
* Unsets a value in the app configuration file based on a dotted key path.
12058
*
12159
* @param path - The path to the app configuration file.
12260
* @param keyPath - The dotted key path to unset (e.g. 'build.include_config_on_deploy')
123-
* @param schema - The schema to validate the patch against. If not provided, the toml will not be validated.
12461
*/
125-
export async function unsetAppConfigValue(path: string, keyPath: string, schema?: zod.AnyZodObject) {
126-
if (shouldUseWasmTomlPatch()) {
127-
return patchAppConfigurationFileWithWasm(path, [{keyPath, value: undefined}])
128-
}
129-
const patch = createPatchFromDottedPath(keyPath, undefined)
130-
await patchAppConfigurationFile({path, patch, schema})
131-
}
132-
133-
/**
134-
* Creates a patch object from a dotted key path and a value
135-
* For example, 'build.dev_store_url' with value 'example.myshopify.com'
136-
* will create \{ build: \{ dev_store_url: 'example.myshopify.com' \} \}
137-
*/
138-
function createPatchFromDottedPath(keyPath: string, value: unknown): {[key: string]: unknown} {
139-
const keys = keyPath.split('.')
140-
if (keys.length === 1) {
141-
return {[keyPath]: value}
142-
}
143-
144-
const obj: {[key: string]: unknown} = {}
145-
let currentObj = obj
146-
147-
for (let i = 0; i < keys.length - 1; i++) {
148-
const key = keys[i]
149-
if (key) {
150-
currentObj[key] = {}
151-
currentObj = currentObj[key] as {[key: string]: unknown}
152-
}
153-
}
154-
155-
const lastKey = keys[keys.length - 1]
156-
if (lastKey) {
157-
currentObj[lastKey] = value
158-
}
159-
160-
return obj
62+
export async function unsetAppConfigValue(path: string, keyPath: string) {
63+
return patchAppConfigurationFileWithWasm(path, [{keyPath, value: undefined}])
16164
}
16265

163-
export function replaceArrayStrategy(_: unknown[], newArray: unknown[]): unknown[] {
66+
function replaceArrayStrategy(_: unknown[], newArray: unknown[]): unknown[] {
16467
return newArray
16568
}
16669

packages/app/src/cli/services/app/write-app-configuration-file.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export const rewriteConfiguration = <T extends zod.ZodTypeAny>(schema: T, config
6868
return config
6969
}
7070

71-
export function addDefaultCommentsToToml(fileString: string) {
71+
function addDefaultCommentsToToml(fileString: string) {
7272
const appTomlInitialComment = `# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration\n`
7373
const appTomlScopesComment = `\n# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes`
7474

0 commit comments

Comments
 (0)