Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .changeset/autocomplete-instant-local-search.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/guard-bundle-upload-size-and-paths.md

This file was deleted.

Empty file removed .jules/refactor.md
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ export type Scalars = {
ActionAuditID: { input: any; output: any; }
/** The ID for a Address. */
AddressID: { input: any; output: any; }
/** The ID for a Attestation. */
AttestationID: { input: any; output: any; }
/** The ID for a BulkDataOperation. */
BulkDataOperationID: { input: any; output: any; }
/** The ID for a BusinessUser. */
Expand Down
4 changes: 4 additions & 0 deletions packages/app/src/cli/commands/app/generate/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export default class AppGenerateExtension extends AppLinkedCommand {
}),
}

public static analyticsNameOverride(): string | undefined {
return 'app scaffold extension'
}

public async run(): Promise<AppLinkedCommandOutput> {
const {flags} = await this.parse(AppGenerateExtension)

Expand Down
112 changes: 52 additions & 60 deletions packages/app/src/cli/services/build/bundle-size.test.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,77 @@
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 {describe, expect, test, vi} from 'vitest'
import {readFile} from '@shopify/cli-kit/node/fs'
import {deflate} from 'node:zlib'
import {promisify} from 'node:util'

const deflateAsync = promisify(deflate)

vi.mock('@shopify/cli-kit/node/fs')

describe('getBundleSize', () => {
test('returns raw and compressed sizes', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const content = 'a'.repeat(10000)
const filePath = joinPath(tmpDir, 'bundle.js')
await writeFile(filePath, content)

// When
const result = await getBundleSize(filePath)

// Then
expect(result.rawBytes).toBe(10000)
expect(result.compressedBytes).toBe((await deflateAsync(Buffer.from(content))).byteLength)
expect(result.compressedBytes).toBeLessThan(result.rawBytes)
})
// Given
const content = 'a'.repeat(10000)
vi.mocked(readFile).mockResolvedValue(content as any)

// When
const result = await getBundleSize('/some/path.js')

// Then
expect(result.rawBytes).toBe(10000)
expect(result.compressedBytes).toBe((await deflateAsync(Buffer.from(content))).byteLength)
expect(result.compressedBytes).toBeLessThan(result.rawBytes)
})

test('compressed size uses deflate to match the backend (Ruby Zlib::Deflate.deflate)', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const content = JSON.stringify({key: 'value', nested: {array: [1, 2, 3]}})
const filePath = joinPath(tmpDir, 'bundle.js')
await writeFile(filePath, content)

// When
const result = await getBundleSize(filePath)

// Then
const expectedCompressed = (await deflateAsync(Buffer.from(content))).byteLength
expect(result.compressedBytes).toBe(expectedCompressed)
})
// Given
const content = JSON.stringify({key: 'value', nested: {array: [1, 2, 3]}})
vi.mocked(readFile).mockResolvedValue(content as any)

// When
const result = await getBundleSize('/some/path.js')

// Then
const expectedCompressed = (await deflateAsync(Buffer.from(content))).byteLength
expect(result.compressedBytes).toBe(expectedCompressed)
})
})

describe('formatBundleSize', () => {
test('returns formatted size string with raw and compressed sizes', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const content = 'x'.repeat(50000)
const filePath = joinPath(tmpDir, 'bundle.js')
await writeFile(filePath, content)
const compressedSize = (await deflateAsync(Buffer.from(content))).byteLength

// When
const result = await formatBundleSize(filePath)

// Then
const expectedRaw = (50000 / 1024).toFixed(1)
const expectedCompressed = (compressedSize / 1024).toFixed(1)
expect(result).toBe(` (${expectedRaw} KB original, ~${expectedCompressed} KB compressed)`)
})
// Given
const content = 'x'.repeat(50000)
const compressedSize = (await deflateAsync(Buffer.from(content))).byteLength
vi.mocked(readFile).mockResolvedValue(content as any)

// When
const result = await formatBundleSize('/some/path.js')

// Then
const expectedRaw = (50000 / 1024).toFixed(1)
const expectedCompressed = (compressedSize / 1024).toFixed(1)
expect(result).toBe(` (${expectedRaw} KB original, ~${expectedCompressed} KB compressed)`)
})

test('formats MB for large files', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
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

// When
const result = await formatBundleSize(filePath)

// Then
const expectedRaw = (Buffer.byteLength(content) / (1024 * 1024)).toFixed(2)
const expectedCompressed = (compressedSize / 1024).toFixed(1)
expect(result).toBe(` (${expectedRaw} MB original, ~${expectedCompressed} KB compressed)`)
})
// Given
const content = 'a'.repeat(2 * 1024 * 1024)
const compressedSize = (await deflateAsync(Buffer.from(content))).byteLength
vi.mocked(readFile).mockResolvedValue(content as any)

// When
const result = await formatBundleSize('/some/path.js')

// Then
const expectedRaw = (Buffer.byteLength(content) / (1024 * 1024)).toFixed(2)
const expectedCompressed = (compressedSize / 1024).toFixed(1)
expect(result).toBe(` (${expectedRaw} MB original, ~${expectedCompressed} KB compressed)`)
})

test('returns empty string on error', async () => {
// Given
vi.mocked(readFile).mockRejectedValue(new Error('file not found'))

// When
const result = await formatBundleSize('/missing/path.js')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('executeIncludeAssetsStep', () => {
options: {
stdout: mockStdout,
stderr: mockStderr,
app: {directory: '/test'} as any,
app: {} as any,
environment: 'production',
},
stepResults: new Map(),
Expand Down Expand Up @@ -552,11 +552,6 @@ describe('executeIncludeAssetsStep', () => {
})

describe('pattern entries', () => {
beforeEach(() => {
// copyByPattern now short-circuits if sourceDir doesn't exist; default true here.
vi.mocked(fs.fileExists).mockResolvedValue(true)
})

test('copies files matching include patterns', async () => {
// Given
vi.mocked(fs.glob).mockResolvedValue(['/test/extension/public/logo.png', '/test/extension/public/style.css'])
Expand Down Expand Up @@ -948,13 +943,11 @@ describe('executeIncludeAssetsStep', () => {
})

test('writes manifest.json with files array when generatesAssetsManifest is true and only pattern inclusions exist', async () => {
// Given — pattern entries contribute output paths to the manifest "files" array.
// sourceDir must exist for copyByPattern's pre-glob fileExists check to pass;
// everything else can read false (the parent beforeEach default).
// Given — pattern entries contribute output paths to the manifest "files" array
vi.mocked(fs.glob).mockResolvedValue(['/test/extension/public/logo.png'])
vi.mocked(fs.copyFile).mockResolvedValue()
vi.mocked(fs.mkdir).mockResolvedValue()
vi.mocked(fs.fileExists).mockImplementation(async (path) => String(path) === '/test/extension/public')
vi.mocked(fs.fileExists).mockResolvedValue(false)
vi.mocked(fs.writeFile).mockResolvedValue()

const step: LifecycleStep = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ export async function executeIncludeAssetsStep(
const config = IncludeAssetsConfigSchema.parse(step.config)
const {extension, options} = context
const outputDir = resolveOutputDir(extension.outputPath)
const appDirectory = options.app.directory

const aggregatedPathMap = new Map<string, string | string[]>()
// Track basenames written across all configKey entries in this build. In
Expand All @@ -148,7 +147,6 @@ export async function executeIncludeAssetsStep(
baseDir: extension.directory,
outputDir,
context,
appDirectory,
destination: sanitizedDest,
usedBasenames,
preserveFilePaths: entry.preserveFilePaths,
Expand All @@ -174,8 +172,6 @@ export async function executeIncludeAssetsStep(
outputDir: destinationDir,
patterns: entry.include,
ignore: entry.ignore ?? [],
appDirectory,
sourceDirConfigValue: entry.baseDir ?? '.',
},
options,
)
Expand All @@ -193,7 +189,6 @@ export async function executeIncludeAssetsStep(
destination: sanitizedDest,
baseDir: extension.directory,
outputDir,
appDirectory,
},
options,
)
Expand Down

This file was deleted.

This file was deleted.

Loading
Loading