Skip to content

Commit 33c5271

Browse files
committed
support --theme-editor-sync flag with --listing
1 parent ce7bcea commit 33c5271

File tree

3 files changed

+9245
-12297
lines changed

3 files changed

+9245
-12297
lines changed

packages/theme/src/cli/utilities/theme-fs.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import {getPatternsFromShopifyIgnore, applyIgnoreFilters} from './asset-ignore.js'
1212
import {triggerBrowserFullReload} from './theme-environment/hot-reload/server.js'
1313
import {removeFile, writeFile} from '@shopify/cli-kit/node/fs'
14+
import * as fsKit from '@shopify/cli-kit/node/fs'
1415
import {test, describe, expect, vi, beforeEach} from 'vitest'
1516
import chokidar from 'chokidar'
1617
import {bulkUploadThemeAssets, deleteThemeAssets, fetchThemeAssets} from '@shopify/cli-kit/node/themes/api'
@@ -593,6 +594,92 @@ describe('theme-fs', () => {
593594
// Then
594595
await changeEventPromise
595596
})
597+
598+
test('writes template JSON into base when listing file does not exist', async () => {
599+
// Given
600+
const themeFileSystem = mountThemeFileSystem(root, {listing: 'modern'})
601+
await themeFileSystem.ready()
602+
const asset = {key: 'templates/index.json', checksum: 'abcd', value: '{"sections":{}}'}
603+
vi.mocked(writeFile).mockClear()
604+
605+
// When
606+
await themeFileSystem.write(asset)
607+
608+
// Then: with Smart behavior, if overlay file does NOT exist yet, write to base
609+
expect(writeFile).toHaveBeenCalledWith(`${root}/templates/index.json`, asset.value)
610+
})
611+
612+
test('writes template JSON into listing folder when listing file already exists', async () => {
613+
// Given
614+
const themeFileSystem = mountThemeFileSystem(root, {listing: 'modern'})
615+
await themeFileSystem.ready()
616+
const asset = {key: 'templates/index.json', checksum: 'abcd', value: '{"sections":{}}'}
617+
// Simulate existing overlay file by making fileExists return true for the overlay check
618+
vi.spyOn(fsKit, 'fileExists').mockResolvedValueOnce(true)
619+
vi.mocked(writeFile).mockClear()
620+
621+
// When
622+
await themeFileSystem.write(asset)
623+
624+
// Then: writes to overlay
625+
expect(writeFile).toHaveBeenCalledWith(`${root}/listings/modern/templates/index.json`, asset.value)
626+
})
627+
628+
test('writes section JSON into listing folder when listing is active', async () => {
629+
// Given
630+
const themeFileSystem = mountThemeFileSystem(root, {listing: 'modern'})
631+
await themeFileSystem.ready()
632+
const asset = {key: 'sections/header.json', checksum: 'abc1', value: '{"name":"Header"}'}
633+
vi.mocked(writeFile).mockClear()
634+
635+
// When
636+
await themeFileSystem.write(asset)
637+
638+
// Then: Smart behavior writes to base if overlay does not exist
639+
expect(writeFile).toHaveBeenCalledWith(`${root}/sections/header.json`, asset.value)
640+
})
641+
642+
test('writes section JSON into listing folder when listing file already exists', async () => {
643+
// Given
644+
const themeFileSystem = mountThemeFileSystem(root, {listing: 'modern'})
645+
await themeFileSystem.ready()
646+
const asset = {key: 'sections/header.json', checksum: 'abc1', value: '{"name":"Header"}'}
647+
// Simulate existing overlay file by making fileExists return true for the overlay check
648+
vi.spyOn(fsKit, 'fileExists').mockResolvedValueOnce(true)
649+
vi.mocked(writeFile).mockClear()
650+
651+
// When
652+
await themeFileSystem.write(asset)
653+
654+
// Then: writes to overlay
655+
expect(writeFile).toHaveBeenCalledWith(`${root}/listings/modern/sections/header.json`, asset.value)
656+
})
657+
658+
test('writes non-JSON files to base when listing is active', async () => {
659+
// Given
660+
const themeFileSystem = mountThemeFileSystem(root, {listing: 'modern'})
661+
await themeFileSystem.ready()
662+
const asset = {key: 'sections/announcement-bar.liquid', checksum: 'abc2', value: '{% comment %}x{% endcomment %}'}
663+
664+
// When
665+
await themeFileSystem.write(asset as any)
666+
667+
// Then
668+
expect(writeFile).toHaveBeenCalledWith(`${root}/sections/announcement-bar.liquid`, asset.value)
669+
})
670+
671+
test('writes template JSON to base when no listing is specified', async () => {
672+
// Given
673+
const themeFileSystem = mountThemeFileSystem(root)
674+
await themeFileSystem.ready()
675+
const asset = {key: 'templates/index.json', checksum: 'ef01', value: '{"sections":{}}'}
676+
677+
// When
678+
await themeFileSystem.write(asset)
679+
680+
// Then
681+
expect(writeFile).toHaveBeenCalledWith(`${root}/templates/index.json`, asset.value)
682+
})
596683
})
597684

598685
describe('handleFileDelete', () => {

packages/theme/src/cli/utilities/theme-fs.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,37 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti
261261
await removeThemeFile(root, fileKey)
262262
},
263263
write: async (asset: ThemeAsset) => {
264+
// When a listing is active and the written asset is a template/section JSON file,
265+
// use smart behavior:
266+
// - If the corresponding listing file already exists, write to listings/<preset>/<key>
267+
// - Otherwise, write to base <key>
268+
const isTemplateOrSectionJson =
269+
(asset.key.startsWith('templates/') || asset.key.startsWith('sections/')) && asset.key.endsWith('.json')
270+
271+
if (options?.listing && isTemplateOrSectionJson) {
272+
const listingAssetKey = joinPath('listings', options.listing, asset.key)
273+
const listingAbsolutePath = joinPath(root, listingAssetKey)
274+
const listingFileExists = await fileExists(listingAbsolutePath)
275+
276+
// Keep checksum/value under the base key so checksums align with remote keys
277+
files.set(
278+
asset.key,
279+
buildThemeAsset({
280+
key: asset.key,
281+
checksum: asset.checksum,
282+
value: asset.value ?? '',
283+
attachment: asset.attachment ?? '',
284+
}),
285+
)
286+
287+
if (listingFileExists) {
288+
await writeThemeFile(root, {...asset, key: listingAssetKey})
289+
} else {
290+
await writeThemeFile(root, asset)
291+
}
292+
return
293+
}
294+
264295
files.set(
265296
asset.key,
266297
buildThemeAsset({

0 commit comments

Comments
 (0)