@@ -7,10 +7,17 @@ import {DEFAULT_IGNORE_PATTERNS, timestampDateFormat} from '../constants.js'
77import { glob , readFile , ReadOptions , fileExists , mkdir , writeFile , removeFile } from '@shopify/cli-kit/node/fs'
88import { joinPath , basename , relativePath } from '@shopify/cli-kit/node/path'
99import { lookupMimeType , setMimeTypes } from '@shopify/cli-kit/node/mimes'
10- import { outputContent , outputDebug , outputInfo , outputToken , outputWarn } from '@shopify/cli-kit/node/output'
10+ import {
11+ outputContent ,
12+ outputDebug ,
13+ outputInfo ,
14+ outputToken ,
15+ outputWarn ,
16+ TokenizedString ,
17+ } from '@shopify/cli-kit/node/output'
1118import { buildThemeAsset } from '@shopify/cli-kit/node/themes/factories'
1219import { AdminSession } from '@shopify/cli-kit/node/session'
13- import { bulkUploadThemeAssets , deleteThemeAssets } from '@shopify/cli-kit/node/themes/api'
20+ import { bulkUploadThemeAssets , deleteThemeAssets , fetchThemeAssets } from '@shopify/cli-kit/node/themes/api'
1421import EventEmitter from 'node:events'
1522import { fileURLToPath } from 'node:url'
1623import type {
@@ -125,6 +132,19 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti
125132 return notifier ?. notify ( fileKey ) ?? Promise . resolve ( )
126133 }
127134
135+ const write = async ( asset : ThemeAsset ) => {
136+ files . set (
137+ asset . key ,
138+ buildThemeAsset ( {
139+ key : asset . key ,
140+ checksum : asset . checksum ,
141+ value : asset . value ?? '' ,
142+ attachment : asset . attachment ?? '' ,
143+ } ) ,
144+ )
145+ await writeThemeFile ( root , asset )
146+ }
147+
128148 const handleFileUpdate = (
129149 eventName : 'add' | 'change' ,
130150 themeId : string ,
@@ -152,14 +172,15 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti
152172 // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
153173 value : file . value || undefined ,
154174 attachment : file . attachment ,
175+ checksum : file . checksum ,
155176 }
156177 } )
157178
158179 const syncPromise = contentPromise
159180 . then ( ( content ) => {
160181 if ( ! content ) return
161182
162- return handleSyncUpdate ( unsyncedFileKeys , uploadErrors , fileKey , themeId , adminSession ) ( content )
183+ return handleSyncUpdate ( unsyncedFileKeys , uploadErrors , fileKey , themeId , adminSession , write ) ( content )
163184 } )
164185 . catch ( createSyncingCatchError ( fileKey , 'upload' ) )
165186
@@ -242,18 +263,7 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti
242263 files . delete ( fileKey )
243264 await removeThemeFile ( root , fileKey )
244265 } ,
245- write : async ( asset : ThemeAsset ) => {
246- files . set (
247- asset . key ,
248- buildThemeAsset ( {
249- key : asset . key ,
250- checksum : asset . checksum ,
251- value : asset . value ?? '' ,
252- attachment : asset . attachment ?? '' ,
253- } ) ,
254- )
255- await writeThemeFile ( root , asset )
256- } ,
266+ write,
257267 read,
258268 applyIgnoreFilters : ( files ) => applyIgnoreFilters ( files , filterPatterns ) ,
259269 addEventListener : ( eventName , cb ) => {
@@ -282,7 +292,8 @@ export function handleSyncUpdate(
282292 fileKey : string ,
283293 themeId : string ,
284294 adminSession : AdminSession ,
285- ) : ( content : { value ?: string ; attachment ?: string } ) => PromiseLike < boolean > {
295+ write : ( asset : ThemeAsset ) => Promise < void > ,
296+ ) : ( content : { value ?: string ; attachment ?: string ; checksum : string } ) => PromiseLike < boolean > {
286297 return async ( content ) => {
287298 if ( ! unsyncedFileKeys . has ( fileKey ) ) {
288299 return false
@@ -306,6 +317,15 @@ export function handleSyncUpdate(
306317 unsyncedFileKeys . delete ( fileKey )
307318 outputSyncResult ( 'update' , fileKey )
308319
320+ if ( content . checksum !== result ?. asset ?. checksum ) {
321+ const [ remoteAsset ] = await fetchThemeAssets ( Number ( themeId ) , [ fileKey ] , adminSession )
322+
323+ if ( remoteAsset ) {
324+ await write ( remoteAsset )
325+ outputSyncResult ( 'rewrite' , fileKey )
326+ }
327+ }
328+
309329 return true
310330 }
311331}
@@ -462,10 +482,18 @@ function dirPath(filePath: string) {
462482 return filePath . substring ( 0 , fileNameIndex )
463483}
464484
465- function outputSyncResult ( action : 'update' | 'delete' , fileKey : string ) : void {
466- outputInfo (
467- outputContent `• ${ timestampDateFormat . format ( new Date ( ) ) } Synced ${ outputToken . raw ( '»' ) } ${ action } ${ fileKey } ` ,
468- )
485+ function outputSyncResult ( action : 'update' | 'delete' | 'rewrite' , fileKey : string ) : void {
486+ let content : TokenizedString
487+
488+ if ( action === 'rewrite' ) {
489+ content = outputContent `• ${ timestampDateFormat . format ( new Date ( ) ) } Overwritten locally ${ fileKey } `
490+ } else {
491+ content = outputContent `• ${ timestampDateFormat . format ( new Date ( ) ) } Synced ${ outputToken . raw (
492+ '»' ,
493+ ) } ${ action } ${ fileKey } `
494+ }
495+
496+ outputInfo ( content )
469497}
470498
471499export function inferLocalHotReloadScriptPath ( ) {
0 commit comments