From 6a7bfce7ca257e98e05dccaeced60964f674053c Mon Sep 17 00:00:00 2001 From: Guilherme Carreiro Date: Thu, 12 Feb 2026 09:26:20 +0100 Subject: [PATCH] Fix hot-reloading for the {% javascript %} tag when serving compiled assets scripts with multibyte characters --- .changeset/lovely-terms-tell.md | 5 ++++ .../theme-environment/local-assets.ts | 4 +-- .../theme-environment.test.ts | 26 +++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 .changeset/lovely-terms-tell.md diff --git a/.changeset/lovely-terms-tell.md b/.changeset/lovely-terms-tell.md new file mode 100644 index 00000000000..5d697a52883 --- /dev/null +++ b/.changeset/lovely-terms-tell.md @@ -0,0 +1,5 @@ +--- +'@shopify/theme': patch +--- + +Fix hot-reloading for the {% javascript %} tag when serving compiled assets scripts with multibyte characters diff --git a/packages/theme/src/cli/utilities/theme-environment/local-assets.ts b/packages/theme/src/cli/utilities/theme-environment/local-assets.ts index 2c3f676fdc3..d8cfc201076 100644 --- a/packages/theme/src/cli/utilities/theme-environment/local-assets.ts +++ b/packages/theme/src/cli/utilities/theme-environment/local-assets.ts @@ -130,7 +130,7 @@ function handleStylesCss(ctx: DevServerContext, event: H3Event) { return serveStatic(event, { getContents: () => stylesheet, - getMeta: () => ({type: 'text/css', size: stylesheet.length, mtime: new Date()}), + getMeta: () => ({type: 'text/css', size: Buffer.byteLength(stylesheet), mtime: new Date()}), }) } @@ -190,7 +190,7 @@ function handleBlockScriptsJs(ctx: DevServerContext, event: H3Event, kind: 'bloc return serveStatic(event, { getContents: () => javascript, - getMeta: () => ({type: 'text/javascript', size: javascript.length, mtime: new Date()}), + getMeta: () => ({type: 'text/javascript', size: Buffer.byteLength(javascript), mtime: new Date()}), }) } diff --git a/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts b/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts index 02508a5ee67..a623668f62a 100644 --- a/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts +++ b/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts @@ -638,6 +638,32 @@ describe('setupDevServer', () => { `) }) + test('serves compiled_assets scripts with correct content-length when content contains multi-byte characters', async () => { + const snippetFile = { + key: 'snippets/multibyte-snippet.liquid', + checksum: 'multibyte1', + value: ` +
+ {% javascript %} + // ...not full cart — derive count from response + console.log("Hey, hey, heeeey!") + {% endjavascript %} +
`, + } + + localThemeFileSystem.files.set(snippetFile.key, snippetFile) + + const {res, body} = await dispatchEvent('/cdn/somepath/compiled_assets/snippet-scripts.js') + + const bodyString = body.toString() + const bodyByteLength = Buffer.byteLength(bodyString) + + // The dash (—) is 3 bytes in UTF-8 but only 1 character. + // content-length must reflect the byte length, not the character count. + expect(bodyByteLength).toBeGreaterThan(bodyString.length) + expect(res.getHeader('content-length')).toBe(bodyByteLength) + }) + test('forwards unknown compiled_assets requests to SFR', async () => { const fetchStub = vi.fn(async () => new Response()) vi.stubGlobal('fetch', fetchStub)