Skip to content
Open
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
30 changes: 12 additions & 18 deletions bin/bundling/esbuild-plugin-graphiql-imports.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { readFile } from 'fs/promises'

const createRequireStatement = /const require = createRequire\(import\.meta\.url\);?\r?\n/
const resolveGraphiQLAssetImports = `import {existsSync} from 'node:fs'
import {dirname, join, parse} from 'node:path'
import {fileURLToPath} from 'node:url'
`

const resolveGraphiQLAssetHelper = `function resolveGraphiQLAsset(asset) {
const {existsSync} = require('node:fs')
const {dirname, join, parse} = require('node:path')
const {fileURLToPath} = require('node:url')

for (
let directory = dirname(fileURLToPath(import.meta.url));
directory !== parse(directory).root;
Expand All @@ -16,32 +15,27 @@ const resolveGraphiQLAssetHelper = `function resolveGraphiQLAsset(asset) {
if (existsSync(candidate)) return candidate
}

return require.resolve(\`@shopify/cli-kit/assets/graphiql/\${asset}\`)
return new URL(\`../../../../assets/graphiql/\${asset}\`, import.meta.url)
}
`

const GraphiQLImportsPlugin = {
name: 'GraphiQLImportsPlugin',
setup(build) {
// GraphiQL uses require.resolve with paths that won't work with esbuild
// We need to replace them with valid paths
// graphiql/server.ts uses require.resolve('@shopify/cli-kit/assets/...'). The bundled CLI does not ship
// @shopify/cli-kit as a dependency; assets are copied to dist/assets. Rewrite to a resolver that works whether
// esbuild emits the GraphiQL server into a top-level shared chunk or a nested command file.
// GraphiQL reads package-local assets using paths relative to server.ts. Those paths won't work when esbuild
// emits the GraphiQL server into a top-level shared chunk or a nested command file. The bundled CLI does not
// ship @shopify/cli-kit as a dependency; assets are copied to dist/assets, so rewrite the relative asset URLs
// to a resolver that searches upward from the emitted chunk.
build.onLoad({filter: /[/\\]graphiql[/\\]server\.[cm]?[jt]s$/}, async (args) => {
const contents = await readFile(args.path, 'utf8')
if (!createRequireStatement.test(contents)) {
throw new Error(`Could not find the GraphiQL server createRequire statement in ${args.path}`)
}
// When `contents` is returned, esbuild defaults the loader to `js` unless set — TypeScript would then
// fail to parse (e.g. "Expected ')' but found ':'" on parameter type annotations).
const loader = args.path.endsWith('.tsx') ? 'tsx' : 'ts'
return {
loader,
contents: contents
.replace(createRequireStatement, (match) => `${match}\n${resolveGraphiQLAssetHelper}`)
.replace("require.resolve('@shopify/cli-kit/assets/graphiql/favicon.ico')", "resolveGraphiQLAsset('favicon.ico')")
.replace("require.resolve('@shopify/cli-kit/assets/graphiql/style.css')", "resolveGraphiQLAsset('style.css')"),
contents: `${resolveGraphiQLAssetImports}\n${resolveGraphiQLAssetHelper}\n${contents}`
.replace("new URL('../../../../assets/graphiql/favicon.ico', import.meta.url)", "resolveGraphiQLAsset('favicon.ico')")
.replace("new URL('../../../../assets/graphiql/style.css', import.meta.url)", "resolveGraphiQLAsset('style.css')"),
}
})
},
Expand Down
9 changes: 2 additions & 7 deletions packages/cli-kit/src/public/node/graphiql/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {createHmac, randomBytes} from 'crypto'
import {createServer, Server} from 'http'
import {readFileSync} from 'fs'
import {Writable} from 'stream'
import {createRequire} from 'module'

/**
* Derives a deterministic GraphiQL authentication key from the app's API secret and store FQDN.
Expand Down Expand Up @@ -54,8 +53,6 @@ export function resolveGraphiQLKey(providedKey: string | undefined, apiSecret: s
return providedKey?.trim() || deriveGraphiQLKey(apiSecret, storeFqdn)
}

const require = createRequire(import.meta.url)

class TokenRefreshError extends AbortError {
constructor() {
super('Failed to refresh credentials. Check that your app is installed, and try again.')
Expand Down Expand Up @@ -155,10 +152,8 @@ export function setupGraphiQLServer(options: SetupGraphiQLServerOptions): Server
)
}

const faviconPath = require.resolve('@shopify/cli-kit/assets/graphiql/favicon.ico')
const faviconContent = readFileSync(faviconPath)
const stylePath = require.resolve('@shopify/cli-kit/assets/graphiql/style.css')
const styleContent = readFileSync(stylePath, 'utf8')
const faviconContent = readFileSync(new URL('../../../../assets/graphiql/favicon.ico', import.meta.url))
const styleContent = readFileSync(new URL('../../../../assets/graphiql/style.css', import.meta.url), 'utf8')

app.use(
defineEventHandler((event) => {
Expand Down
Loading