From 30c4e007d844a0fec1a28e9c9b12749412721ce5 Mon Sep 17 00:00:00 2001 From: Igor Kozlov Date: Tue, 25 Aug 2026 16:20:57 -0400 Subject: [PATCH] Use Brotli in bundle size report --- .changeset/brotli-bundle-size-report.md | 5 ++++ .../cli/services/build/bundle-size.test.ts | 24 ++++++++++++------- .../app/src/cli/services/build/bundle-size.ts | 14 +++++++---- 3 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 .changeset/brotli-bundle-size-report.md diff --git a/.changeset/brotli-bundle-size-report.md b/.changeset/brotli-bundle-size-report.md new file mode 100644 index 00000000000..d2d3ad844e0 --- /dev/null +++ b/.changeset/brotli-bundle-size-report.md @@ -0,0 +1,5 @@ +--- +'@shopify/app': patch +--- + +Report extension compressed bundle size using Brotli diff --git a/packages/app/src/cli/services/build/bundle-size.test.ts b/packages/app/src/cli/services/build/bundle-size.test.ts index 28fefa4ca6d..4c42a5208ad 100644 --- a/packages/app/src/cli/services/build/bundle-size.test.ts +++ b/packages/app/src/cli/services/build/bundle-size.test.ts @@ -2,10 +2,19 @@ import {getBundleSize, formatBundleSize} from './bundle-size.js' import {describe, expect, test} from 'vitest' import {inTemporaryDirectory, writeFile} from '@shopify/cli-kit/node/fs' import {joinPath} from '@shopify/cli-kit/node/path' -import {deflate} from 'node:zlib' +import {brotliCompress, constants as zlibConstants} from 'node:zlib' import {promisify} from 'node:util' -const deflateAsync = promisify(deflate) +const brotliCompressAsync = promisify(brotliCompress) + +async function brotliSize(content: string) { + const compressed = await brotliCompressAsync(Buffer.from(content), { + params: { + [zlibConstants.BROTLI_PARAM_QUALITY]: 11, + }, + }) + return compressed.byteLength +} describe('getBundleSize', () => { test('returns raw and compressed sizes', async () => { @@ -20,12 +29,12 @@ describe('getBundleSize', () => { // Then expect(result.rawBytes).toBe(10000) - expect(result.compressedBytes).toBe((await deflateAsync(Buffer.from(content))).byteLength) + expect(result.compressedBytes).toBe(await brotliSize(content)) expect(result.compressedBytes).toBeLessThan(result.rawBytes) }) }) - test('compressed size uses deflate to match the backend (Ruby Zlib::Deflate.deflate)', async () => { + test('compressed size uses Brotli to match the backend size gate (Ruby Brotli.deflate)', async () => { await inTemporaryDirectory(async (tmpDir) => { // Given const content = JSON.stringify({key: 'value', nested: {array: [1, 2, 3]}}) @@ -36,8 +45,7 @@ describe('getBundleSize', () => { const result = await getBundleSize(filePath) // Then - const expectedCompressed = (await deflateAsync(Buffer.from(content))).byteLength - expect(result.compressedBytes).toBe(expectedCompressed) + expect(result.compressedBytes).toBe(await brotliSize(content)) }) }) }) @@ -49,7 +57,7 @@ describe('formatBundleSize', () => { const content = 'x'.repeat(50000) const filePath = joinPath(tmpDir, 'bundle.js') await writeFile(filePath, content) - const compressedSize = (await deflateAsync(Buffer.from(content))).byteLength + const compressedSize = await brotliSize(content) // When const result = await formatBundleSize(filePath) @@ -67,7 +75,7 @@ describe('formatBundleSize', () => { const content = 'a'.repeat(2 * 1024 * 1024) const filePath = joinPath(tmpDir, 'bundle.js') await writeFile(filePath, content) - const compressedSize = (await deflateAsync(Buffer.from(content))).byteLength + const compressedSize = await brotliSize(content) // When const result = await formatBundleSize(filePath) diff --git a/packages/app/src/cli/services/build/bundle-size.ts b/packages/app/src/cli/services/build/bundle-size.ts index 00edcd6a941..f2212d295d0 100644 --- a/packages/app/src/cli/services/build/bundle-size.ts +++ b/packages/app/src/cli/services/build/bundle-size.ts @@ -1,18 +1,22 @@ import {readFile} from '@shopify/cli-kit/node/fs' import {outputDebug} from '@shopify/cli-kit/node/output' -import {deflate} from 'node:zlib' +import {brotliCompress, constants as zlibConstants} from 'node:zlib' import {promisify} from 'node:util' -const deflateAsync = promisify(deflate) +const brotliCompressAsync = promisify(brotliCompress) /** - * Computes the raw and compressed (deflate) size of a file. - * Uses the same compression algorithm as the Shopify backend (Zlib::Deflate.deflate). + * Computes the raw and compressed (Brotli) size of a file. + * Uses the same compression algorithm as the Shopify backend (Ruby Brotli.deflate). */ export async function getBundleSize(filePath: string) { const content = await readFile(filePath) const rawBytes = Buffer.byteLength(content) - const compressed = await deflateAsync(Buffer.from(content)) + const compressed = await brotliCompressAsync(Buffer.from(content), { + params: { + [zlibConstants.BROTLI_PARAM_QUALITY]: 11, + }, + }) const compressedBytes = compressed.byteLength return {path: filePath, rawBytes, compressedBytes}