Skip to content

Commit 5104108

Browse files
authored
Merge pull request #7411 from Shopify/cherry-pick-build-command-fix
Cherry-pick fix from #7409
2 parents dbe4385 + 749b02a commit 5104108

8 files changed

Lines changed: 28 additions & 21 deletions

File tree

packages/app/src/cli/models/extensions/extension-instance.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,9 @@ export class ExtensionInstance<TConfiguration extends BaseConfigType = BaseConfi
203203
return this.specification.preDeployValidation(this)
204204
}
205205

206-
buildValidation(): Promise<void> {
206+
buildValidation({outputPath}: {outputPath: string}): Promise<void> {
207207
if (!this.specification.buildValidation) return Promise.resolve()
208-
return this.specification.buildValidation(this)
208+
return this.specification.buildValidation(this, outputPath)
209209
}
210210

211211
async keepBuiltSourcemapsLocally(inputPath: string): Promise<void> {

packages/app/src/cli/models/extensions/specification.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export interface ExtensionSpecification<TConfiguration extends BaseConfigType =
8383
) => Promise<Record<string, unknown> | undefined>
8484
validate?: (config: TConfiguration, configPath: string, directory: string) => Promise<Result<unknown, string>>
8585
preDeployValidation?: (extension: ExtensionInstance<TConfiguration>) => Promise<void>
86-
buildValidation?: (extension: ExtensionInstance<TConfiguration>) => Promise<void>
86+
buildValidation?: (extension: ExtensionInstance<TConfiguration>, outputPath: string) => Promise<void>
8787
hasExtensionPointTarget?(config: TConfiguration, target: string): boolean
8888
appModuleFeatures: (config?: TConfiguration) => ExtensionFeature[]
8989
getDevSessionUpdateMessages?: (config: TConfiguration) => Promise<string[]>

packages/app/src/cli/models/extensions/specifications/web_pixel_extension.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ const webPixelSpec = createExtensionSpecification({
3333
partnersWebIdentifier: 'web_pixel',
3434
schema: WebPixelSchema,
3535
appModuleFeatures: (_) => ['esbuild', 'single_js_entry_path'],
36-
getOutputRelativePath: (extension: ExtensionInstance<WebPixelConfigType>) => `${extension.handle}.js`,
36+
getOutputRelativePath: (extension: ExtensionInstance<WebPixelConfigType>) => `dist/${extension.handle}.js`,
3737
clientSteps: [
3838
{
3939
lifecycle: 'deploy',
40-
steps: [{id: 'bundle-ui', name: 'Bundle UI Extension', type: 'bundle_ui', config: {bundleFolder: 'dist/'}}],
40+
steps: [{id: 'bundle-ui', name: 'Bundle UI Extension', type: 'bundle_ui', config: {}}],
4141
},
4242
],
4343
deployConfig: async (config, _) => {
@@ -47,8 +47,8 @@ const webPixelSpec = createExtensionSpecification({
4747
runtime_configuration_definition: config.settings,
4848
}
4949
},
50-
buildValidation: async (extension) => {
51-
const bundleSize = await fileSize(extension.outputPath)
50+
buildValidation: async (_, outputPath) => {
51+
const bundleSize = await fileSize(outputPath)
5252
if (bundleSize > BUNDLE_SIZE_LIMIT) {
5353
const humanReadableBundleSize = `${(bundleSize / kilobytes).toFixed(2)} kB`
5454
throw new AbortError(

packages/app/src/cli/services/build/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export async function buildUIExtension(extension: ExtensionInstance, options: Ex
123123
throw newError
124124
}
125125

126-
await extension.buildValidation()
126+
await extension.buildValidation({outputPath: localOutputPath})
127127

128128
const duration = Math.round(performance.now() - startTime)
129129
const sizeInfo = await formatBundleSize(localOutputPath)

packages/app/src/cli/services/build/steps/bundle-ui-step.test.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,6 @@ describe('executeBundleUIStep', () => {
3535
config: {generatesAssetsManifest: false},
3636
}
3737

38-
test('skips the copy when local and bundle output directories are identical', async () => {
39-
// Given
40-
vi.mocked(buildExtension.buildUIExtension).mockResolvedValue('/test/extension/dist/handle.js')
41-
42-
// When
43-
await executeBundleUIStep(step, mockContext)
44-
45-
// Then — fs-extra would throw "Source and destination must not be the same"
46-
expect(fs.copyFile).not.toHaveBeenCalled()
47-
})
48-
4938
test('copies when local and bundle output directories differ', async () => {
5039
// Given
5140
mockContext.extension.outputPath = '/bundle/handle/handle.js'

packages/app/src/cli/services/build/steps/bundle-ui-step.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export async function executeBundleUIStep(step: BundleUIStep, context: BuildCont
2222
const config = context.extension.configuration
2323
context.options.buildDirectory = step.config?.bundleFolder ?? undefined
2424
const localOutputPath = await buildUIExtension(context.extension, context.options)
25-
// When invoked outside a bundle directory (e.g. `shopify app build`), localOutputPath and outputPath collapse onto the same directory; fs-extra rejects same-path copies.
2625
const localOutputDir = dirname(localOutputPath)
2726
const bundleOutputDir = step.config?.bundleFolder
2827
? joinPath(dirname(context.extension.outputPath), step.config.bundleFolder)

packages/cli-kit/src/public/node/fs.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ describe('copy', () => {
9292
await expect(readFile(joinPath(to, 'child', '.dotfile'))).resolves.toEqual(content)
9393
})
9494
})
95+
96+
test('skips the copy when source and destination resolve to the same path', async () => {
97+
await inTemporaryDirectory(async (tmpDir) => {
98+
// Given
99+
const content = 'test'
100+
const path = joinPath(tmpDir, 'file')
101+
await writeFile(path, content)
102+
103+
// When / Then — fs-extra would otherwise throw "Source and destination must not be the same"
104+
await expect(copyFile(path, joinPath(path, '..', 'file'))).resolves.not.toThrow()
105+
await expect(readFile(path)).resolves.toEqual(content)
106+
})
107+
})
95108
})
96109

97110
describe('move', () => {

packages/cli-kit/src/public/node/fs.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {outputContent, outputToken, outputDebug} from './output.js'
2-
import {joinPath, normalizePath} from './path.js'
2+
import {joinPath, normalizePath, resolvePath} from './path.js'
33
import {OverloadParameters} from '../../private/common/ts/overloaded-parameters.js'
44
import {getRandomName, RandomNameFamily} from '../common/string.js'
55
import {systemTempDir} from '../../private/node/temp-dir.js'
@@ -153,6 +153,12 @@ export async function fileRealPath(path: string): Promise<string> {
153153
* @param to - Destination path.
154154
*/
155155
export async function copyFile(from: string, to: string): Promise<void> {
156+
if (resolvePath(from) === resolvePath(to)) {
157+
outputDebug(
158+
outputContent`Skipping copy file step because source and destination is the same: ${outputToken.path(from)}`,
159+
)
160+
return
161+
}
156162
outputDebug(outputContent`Copying file from ${outputToken.path(from)} to ${outputToken.path(to)}...`)
157163
await fsCopy(from, to)
158164
}

0 commit comments

Comments
 (0)