From 264b08714505f0c24a8d068d562202ff5ae0c39c Mon Sep 17 00:00:00 2001 From: Martin Gingras Date: Wed, 16 Oct 2024 20:38:18 +0000 Subject: [PATCH] Generate local sourcemap files for UI extensions --- .../extensions/extension-instance.test.ts | 24 +++++++++++++++++- .../models/extensions/extension-instance.ts | 25 +++++++++++++++++-- .../cli/models/extensions/specification.ts | 1 + .../specifications/checkout_ui_extension.ts | 9 ++++++- .../extensions/specifications/ui_extension.ts | 2 +- .../app/src/cli/services/build/extension.ts | 1 + .../app/src/cli/services/deploy/bundle.ts | 6 +++++ 7 files changed, 63 insertions(+), 5 deletions(-) diff --git a/packages/app/src/cli/models/extensions/extension-instance.test.ts b/packages/app/src/cli/models/extensions/extension-instance.test.ts index 9e8592d784..f03172b8e5 100644 --- a/packages/app/src/cli/models/extensions/extension-instance.test.ts +++ b/packages/app/src/cli/models/extensions/extension-instance.test.ts @@ -18,7 +18,7 @@ import {ExtensionBuildOptions} from '../../services/build/extension.js' import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js' import {joinPath} from '@shopify/cli-kit/node/path' import {describe, expect, test} from 'vitest' -import {inTemporaryDirectory, readFile} from '@shopify/cli-kit/node/fs' +import {inTemporaryDirectory, readFile, mkdir, writeFile, fileExists, tempDirectory} from '@shopify/cli-kit/node/fs' import {slugify} from '@shopify/cli-kit/common/string' import {hashString} from '@shopify/cli-kit/node/crypto' import {Writable} from 'stream' @@ -114,6 +114,28 @@ describe('watchPaths', async () => { }) }) +describe('keepBuiltSourcemapsLocally', async () => { + test('moves the appropriate source map files to the expected directory', async () => { + await inTemporaryDirectory(async (bundleDirectory: string) => { + const outputPath = tempDirectory() + const extensionInstance = await testUIExtension({handle: 'scriptToMove', directory: outputPath}) + const someDirPath = joinPath(bundleDirectory, 'some_dir') + const otherDirPath = joinPath(bundleDirectory, 'other_dir') + await mkdir(someDirPath).then(() => writeFile(joinPath(someDirPath, 'scriptToMove.js'), 'abc')) + await mkdir(someDirPath).then(() => writeFile(joinPath(someDirPath, 'scriptToMove.js.map'), 'abc map')) + await mkdir(otherDirPath).then(() => writeFile(joinPath(otherDirPath, 'scriptToIgnore.js'), 'abc')) + await mkdir(otherDirPath).then(() => writeFile(joinPath(otherDirPath, 'scriptToIgnore.js.map'), 'abc map')) + + await extensionInstance.keepBuiltSourcemapsLocally(bundleDirectory) + + expect(fileExists(joinPath(outputPath, 'scriptToMove.js'))).toBe(false) + expect(fileExists(joinPath(outputPath, 'scriptToMove.js.map'))).toBe(true) + expect(fileExists(joinPath(outputPath, 'scriptToIgnore.js'))).toBe(false) + expect(fileExists(joinPath(outputPath, 'scriptToIgnore.js.map'))).toBe(false) + }) + }) +}) + describe('build', async () => { test('creates a valid JS file for tax calculation extensions', async () => { await inTemporaryDirectory(async (tmpDir) => { diff --git a/packages/app/src/cli/models/extensions/extension-instance.ts b/packages/app/src/cli/models/extensions/extension-instance.ts index 52a11c98db..332e51307d 100644 --- a/packages/app/src/cli/models/extensions/extension-instance.ts +++ b/packages/app/src/cli/models/extensions/extension-instance.ts @@ -27,8 +27,8 @@ import {ok} from '@shopify/cli-kit/node/result' import {constantize, slugify} from '@shopify/cli-kit/common/string' import {hashString, randomUUID} from '@shopify/cli-kit/node/crypto' import {partnersFqdn} from '@shopify/cli-kit/node/context/fqdn' -import {joinPath} from '@shopify/cli-kit/node/path' -import {fileExists, touchFile, writeFile} from '@shopify/cli-kit/node/fs' +import {joinPath, basename} from '@shopify/cli-kit/node/path' +import {fileExists, touchFile, moveFile, writeFile, glob} from '@shopify/cli-kit/node/fs' import {getPathValue} from '@shopify/cli-kit/common/object' import {useThemebundling} from '@shopify/cli-kit/node/context/local' @@ -112,6 +112,10 @@ export class ExtensionInstance { + if (!this.isSourceMapGeneratingExtension) return Promise.resolve() + + const pathsToMove = await glob(`**/${this.handle}*.map`, { + cwd: bundleDirectory, + absolute: true, + followSymbolicLinks: false, + }) + + await Promise.all( + pathsToMove.map(async (filePath) => { + const outputPath = joinPath(this.directory, 'dist', basename(filePath)) + await moveFile(filePath, outputPath, {overwrite: true}) + }), + ) + } + async publishURL(options: {orgId: string; appId: string; extensionId?: string}) { const fqdn = await partnersFqdn() const parnersPath = this.specification.partnersWebIdentifier diff --git a/packages/app/src/cli/models/extensions/specification.ts b/packages/app/src/cli/models/extensions/specification.ts index 8e5624c282..59a7e006ee 100644 --- a/packages/app/src/cli/models/extensions/specification.ts +++ b/packages/app/src/cli/models/extensions/specification.ts @@ -19,6 +19,7 @@ export type ExtensionFeature = | 'cart_url' | 'esbuild' | 'single_js_entry_path' + | 'generates_source_maps' export interface TransformationConfig { [key: string]: string diff --git a/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts b/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts index c949624b4b..b8f9182c5e 100644 --- a/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts +++ b/packages/app/src/cli/models/extensions/specifications/checkout_ui_extension.ts @@ -18,7 +18,14 @@ const checkoutSpec = createExtensionSpecification({ identifier: 'checkout_ui_extension', dependency, schema: CheckoutSchema, - appModuleFeatures: (_) => ['ui_preview', 'bundling', 'cart_url', 'esbuild', 'single_js_entry_path'], + appModuleFeatures: (_) => [ + 'ui_preview', + 'bundling', + 'cart_url', + 'esbuild', + 'single_js_entry_path', + 'generates_source_maps', + ], deployConfig: async (config, directory) => { return { extension_points: config.extension_points, diff --git a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts index 384c0ecf7c..fc88456220 100644 --- a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts +++ b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts @@ -41,7 +41,7 @@ const uiExtensionSpec = createExtensionSpecification({ dependency, schema: UIExtensionSchema, appModuleFeatures: (config) => { - const basic: ExtensionFeature[] = ['ui_preview', 'bundling', 'esbuild'] + const basic: ExtensionFeature[] = ['ui_preview', 'bundling', 'esbuild', 'generates_source_maps'] const needsCart = config?.extension_points?.find((extensionPoint) => { return getExtensionPointTargetSurface(extensionPoint.target) === 'checkout' diff --git a/packages/app/src/cli/services/build/extension.ts b/packages/app/src/cli/services/build/extension.ts index 9eb14aff62..a694375c5d 100644 --- a/packages/app/src/cli/services/build/extension.ts +++ b/packages/app/src/cli/services/build/extension.ts @@ -104,6 +104,7 @@ export async function buildUIExtension(extension: ExtensionInstance, options: Ex env, stderr: options.stderr, stdout: options.stdout, + sourceMaps: extension.isSourceMapGeneratingExtension, }) } catch (extensionBundlingError) { // this fails if the app's own source code is broken; wrap such that this isn't flagged as a CLI bug diff --git a/packages/app/src/cli/services/deploy/bundle.ts b/packages/app/src/cli/services/deploy/bundle.ts index dd533a895b..b49a2084ec 100644 --- a/packages/app/src/cli/services/deploy/bundle.ts +++ b/packages/app/src/cli/services/deploy/bundle.ts @@ -48,6 +48,12 @@ export async function bundleAndBuildExtensions(options: BundleOptions, systemEnv showTimestamps: false, }) + await Promise.all( + options.app.allExtensions.map(async (extension) => { + await extension.keepBuiltSourcemapsLocally(bundleDirectory) + }), + ) + if (options.bundlePath) { await zip({ inputDirectory: bundleDirectory,