Skip to content

Commit

Permalink
Merge pull request #5093 from Shopify/shauns/12-12-check_that_shopify…
Browse files Browse the repository at this point in the history
…_shopify_function_has_a_run_module

Check that @shopify/shopify_function has a `run` module
  • Loading branch information
shauns authored Dec 13, 2024
2 parents f7d281e + 8b072a1 commit e817956
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 15 deletions.
26 changes: 24 additions & 2 deletions packages/app/src/cli/services/function/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {testApp, testFunctionExtension} from '../../models/app/app.test-data.js'
import {beforeEach, describe, expect, test, vi} from 'vitest'
import {exec} from '@shopify/cli-kit/node/system'
import {joinPath} from '@shopify/cli-kit/node/path'
import {inTemporaryDirectory, mkdir, writeFile} from '@shopify/cli-kit/node/fs'
import {inTemporaryDirectory, mkdir, writeFile, removeFile} from '@shopify/cli-kit/node/fs'
import {build as esBuild} from 'esbuild'

vi.mock('@shopify/cli-kit/node/system')
Expand Down Expand Up @@ -63,6 +63,10 @@ async function installShopifyLibrary(tmpDir: string) {
const shopifyFunction = joinPath(shopifyFunctionDir, 'index.ts')
await mkdir(shopifyFunctionDir)
await writeFile(shopifyFunction, '')

const runModule = joinPath(shopifyFunctionDir, 'run.ts')
await writeFile(runModule, '')

return shopifyFunction
}

Expand Down Expand Up @@ -112,7 +116,23 @@ describe('bundleExtension', () => {
const got = bundleExtension(ourFunction, {stdout, stderr, signal, app})

// Then
await expect(got).rejects.toThrow(/Could not find the Shopify Function runtime/)
await expect(got).rejects.toThrow(/Could not find the Shopify Functions JavaScript library/)
})
})

test('errors if shopify library lacks the run module', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const ourFunction = await testFunctionExtension({dir: tmpDir})
ourFunction.entrySourceFilePath = joinPath(tmpDir, 'src/index.ts')
const shopifyFunction = await installShopifyLibrary(tmpDir)
await removeFile(joinPath(shopifyFunction, '..', 'run.ts'))

// When
const got = bundleExtension(ourFunction, {stdout, stderr, signal, app})

// Then
await expect(got).rejects.toThrow(/Could not find the Shopify Functions JavaScript library/)
})
})

Expand Down Expand Up @@ -173,6 +193,7 @@ describe('ExportJavyBuilder', () => {
// Given
const ourFunction = await testFunctionExtension({dir: tmpDir})
ourFunction.entrySourceFilePath = joinPath(tmpDir, 'src/index.ts')
const shopifyFunction = await installShopifyLibrary(tmpDir)

// When
const got = builder.bundle(
Expand Down Expand Up @@ -210,6 +231,7 @@ describe('ExportJavyBuilder', () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const ourFunction = await testFunctionExtension({dir: tmpDir})
const shopifyFunction = await installShopifyLibrary(tmpDir)

// When
const got = builder.bundle(ourFunction, {stdout, stderr, signal, app})
Expand Down
48 changes: 35 additions & 13 deletions packages/app/src/cli/services/function/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {FunctionConfigType} from '../../models/extensions/specifications/functio
import {AppInterface} from '../../models/app/app.js'
import {EsbuildEnvVarRegex} from '../../constants.js'
import {hyphenate, camelize} from '@shopify/cli-kit/common/string'
import {outputDebug} from '@shopify/cli-kit/node/output'
import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output'
import {exec} from '@shopify/cli-kit/node/system'
import {joinPath} from '@shopify/cli-kit/node/path'
import {build as esBuild, BuildResult} from 'esbuild'
Expand All @@ -14,6 +14,7 @@ import {AbortSignal} from '@shopify/cli-kit/node/abort'
import {renderTasks} from '@shopify/cli-kit/node/ui'
import {pickBy} from '@shopify/cli-kit/common/object'
import {runWithTimer} from '@shopify/cli-kit/node/metadata'
import {AbortError} from '@shopify/cli-kit/node/error'
import {Writable} from 'stream'

interface JSFunctionBuildOptions {
Expand Down Expand Up @@ -104,24 +105,47 @@ export async function buildGraphqlTypes(
})
}

export async function bundleExtension(
fun: ExtensionInstance<FunctionConfigType>,
options: JSFunctionBuildOptions,
processEnv = process.env,
) {
async function checkForShopifyFunctionRuntimeEntrypoint(fun: ExtensionInstance<FunctionConfigType>) {
const entryPoint = await findPathUp('node_modules/@shopify/shopify_function/index.ts', {
type: 'file',
cwd: fun.directory,
})
if (!entryPoint) {
throw new Error(
"Could not find the Shopify Function runtime. Make sure you have '@shopify/shopify_function' installed",

const runModule = await findPathUp('node_modules/@shopify/shopify_function/run.ts', {
type: 'file',
cwd: fun.directory,
})

if (!entryPoint || !runModule) {
throw new AbortError(
'Could not find the Shopify Functions JavaScript library.',
outputContent`Make sure you have the latest ${outputToken.yellow(
'@shopify/shopify_function',
)} library installed.`,
[
outputContent`Add ${outputToken.green(
'"@shopify/shopify_function": "1.0.0"',
)} to the dependencies section of the package.json file in your function's directory, if not already present.`
.value,
`Run your package manager's install command to update dependencies.`,
],
)
}

if (!fun.entrySourceFilePath) {
throw new Error('Could not find your function entry point. It must be in src/index.js or src/index.ts')
throw new AbortError('Could not find your function entry point. It must be in src/index.js or src/index.ts')
}

return entryPoint
}

export async function bundleExtension(
fun: ExtensionInstance<FunctionConfigType>,
options: JSFunctionBuildOptions,
processEnv = process.env,
) {
const entryPoint = await checkForShopifyFunctionRuntimeEntrypoint(fun)

const esbuildOptions = {
...getESBuildOptions(fun.directory, fun.entrySourceFilePath, options.app.dotenv?.variables ?? {}, processEnv),
entryPoints: [entryPoint],
Expand Down Expand Up @@ -227,9 +251,7 @@ export class ExportJavyBuilder implements JavyBuilder {
}

async bundle(fun: ExtensionInstance<FunctionConfigType>, options: JSFunctionBuildOptions, processEnv = process.env) {
if (!fun.entrySourceFilePath) {
throw new Error('Could not find your function entry point. It must be in src/index.js or src/index.ts')
}
await checkForShopifyFunctionRuntimeEntrypoint(fun)

const contents = this.entrypointContents
outputDebug('Generating dist/function.js using generated module:')
Expand Down

0 comments on commit e817956

Please sign in to comment.