Skip to content

Commit c538478

Browse files
committed
Surface rewritten liquid files to dev and push commands
1 parent d8a6b7c commit c538478

File tree

11 files changed

+148
-30
lines changed

11 files changed

+148
-30
lines changed

packages/cli-kit/src/cli/api/graphql/admin/generated/theme_files_upsert.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type ThemeFilesUpsertMutationVariables = Types.Exact<{
1010

1111
export type ThemeFilesUpsertMutation = {
1212
themeFilesUpsert?: {
13-
upsertedThemeFiles?: {filename: string}[] | null
13+
upsertedThemeFiles?: {filename: string; checksumMd5?: string | null}[] | null
1414
userErrors: {filename?: string | null; message: string}[]
1515
} | null
1616
}
@@ -71,6 +71,7 @@ export const ThemeFilesUpsert = {
7171
kind: 'SelectionSet',
7272
selections: [
7373
{kind: 'Field', name: {kind: 'Name', value: 'filename'}},
74+
{kind: 'Field', name: {kind: 'Name', value: 'checksumMd5'}},
7475
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
7576
],
7677
},

packages/cli-kit/src/cli/api/graphql/admin/mutations/theme_files_upsert.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mutation themeFilesUpsert($files: [OnlineStoreThemeFilesUpsertFileInput!]!, $the
22
themeFilesUpsert(files: $files, themeId: $themeId) {
33
upsertedThemeFiles {
44
filename
5+
checksumMd5
56
}
67
userErrors {
78
filename

packages/cli-kit/src/public/node/themes/api.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,11 +553,19 @@ describe('bulkUploadThemeAssets', async () => {
553553
key: 'snippets/product-variant-picker.liquid',
554554
success: true,
555555
operation: Operation.Upload,
556+
asset: {
557+
checksum: '',
558+
key: 'snippets/product-variant-picker.liquid',
559+
},
556560
},
557561
{
558562
key: 'templates/404.json',
559563
success: true,
560564
operation: Operation.Upload,
565+
asset: {
566+
checksum: '',
567+
key: 'templates/404.json',
568+
},
561569
},
562570
])
563571
})

packages/cli-kit/src/public/node/themes/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ function processUploadResults(uploadResults: ThemeFilesUpsertMutation): Result[]
315315
key: file.filename,
316316
success: true,
317317
operation: Operation.Upload,
318+
asset: {
319+
key: file.filename,
320+
checksum: file.checksumMd5 ?? '',
321+
},
318322
})
319323
})
320324

packages/theme/src/cli/services/dev.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,15 @@ export async function dev(options: DevOptions) {
100100
session.storefrontPassword = await storefrontPasswordPromise
101101
}
102102

103-
const {serverStart, renderDevSetupProgress} = setupDevServer(options.theme, ctx)
103+
const {serverStart, renderDevSetupProgress, syncRewrittenFilesPromise} = setupDevServer(options.theme, ctx)
104104

105105
if (!options['theme-editor-sync']) {
106106
session.storefrontPassword = await storefrontPasswordPromise
107107
}
108108

109109
await renderDevSetupProgress()
110110
await serverStart()
111+
await syncRewrittenFilesPromise()
111112

112113
renderLinks(urls)
113114
if (options.open) {

packages/theme/src/cli/services/push.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,10 @@ async function executePush(
210210
session,
211211
themeChecksums,
212212
themeFileSystem,
213-
options,
213+
{
214+
...options,
215+
handleRewrittenFiles: 'warn',
216+
},
214217
context,
215218
)
216219

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ function buildDownloadTasks(
8686
return batches
8787
}
8888

89-
async function downloadFiles(theme: Theme, fileSystem: ThemeFileSystem, filenames: string[], session: AdminSession) {
89+
export async function downloadFiles(
90+
theme: Theme,
91+
fileSystem: ThemeFileSystem,
92+
filenames: string[],
93+
session: AdminSession,
94+
) {
9095
const assets = await fetchThemeAssets(theme.id, filenames, session)
9196
if (!assets) return
9297

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ vi.mock('../theme-uploader.js', async () => {
3434
})
3535
beforeEach(() => {
3636
vi.mocked(uploadTheme).mockImplementation(() => {
37-
return {workPromise: Promise.resolve(), uploadResults: new Map(), renderThemeSyncProgress: () => Promise.resolve()}
37+
return {
38+
workPromise: Promise.resolve(),
39+
uploadResults: new Map(),
40+
renderThemeSyncProgress: () => Promise.resolve(),
41+
syncRewrittenFilesPromise: Promise.resolve(),
42+
}
3843
})
3944
})
4045

@@ -125,6 +130,7 @@ describe('setupDevServer', () => {
125130
nodelete: true,
126131
deferPartialWork: true,
127132
backgroundWorkCatch: expect.any(Function),
133+
handleRewrittenFiles: 'fix',
128134
})
129135
})
130136

@@ -175,6 +181,7 @@ describe('setupDevServer', () => {
175181
nodelete: true,
176182
deferPartialWork: true,
177183
backgroundWorkCatch: expect.any(Function),
184+
handleRewrittenFiles: 'fix',
178185
})
179186
})
180187

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export function setupDevServer(theme: Theme, ctx: DevServerContext) {
2525
serverStart: server.start,
2626
dispatchEvent: server.dispatch,
2727
renderDevSetupProgress: envSetup.renderProgress,
28+
syncRewrittenFilesPromise: () => envSetup.syncRewrittenFilesPromise,
2829
}
2930
}
3031

@@ -44,6 +45,7 @@ function ensureThemeEnvironmentSetup(theme: Theme, ctx: DevServerContext) {
4445
nodelete: ctx.options.noDelete,
4546
deferPartialWork: true,
4647
backgroundWorkCatch: abort,
48+
handleRewrittenFiles: 'fix',
4749
})
4850
})
4951
.catch(abort)
@@ -67,6 +69,7 @@ function ensureThemeEnvironmentSetup(theme: Theme, ctx: DevServerContext) {
6769

6870
await renderThemeSyncProgress()
6971
},
72+
syncRewrittenFilesPromise: uploadPromise.then((result) => result.syncRewrittenFilesPromise).catch(abort),
7073
}
7174
}
7275

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

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ import {DEFAULT_IGNORE_PATTERNS, timestampDateFormat} from '../constants.js'
77
import {glob, readFile, ReadOptions, fileExists, mkdir, writeFile, removeFile} from '@shopify/cli-kit/node/fs'
88
import {joinPath, basename, relativePath} from '@shopify/cli-kit/node/path'
99
import {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'
1118
import {buildThemeAsset} from '@shopify/cli-kit/node/themes/factories'
1219
import {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'
1421
import EventEmitter from 'node:events'
1522
import {fileURLToPath} from 'node:url'
1623
import 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

471499
export function inferLocalHotReloadScriptPath() {

0 commit comments

Comments
 (0)