From b2f834b14f65e19c7e28889430f970dac89cebcc Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Mon, 6 Jul 2026 19:24:56 +0200 Subject: [PATCH] Use relative GraphiQL asset paths Assisted-By: devx/56dcddb4-0bb1-4e96-b794-1bb2fda2cba1 --- .../esbuild-plugin-graphiql-imports.js | 30 ++++++++----------- .../src/public/node/graphiql/server.ts | 9 ++---- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/bin/bundling/esbuild-plugin-graphiql-imports.js b/bin/bundling/esbuild-plugin-graphiql-imports.js index bd201644236..fdf5ea9d740 100644 --- a/bin/bundling/esbuild-plugin-graphiql-imports.js +++ b/bin/bundling/esbuild-plugin-graphiql-imports.js @@ -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; @@ -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')"), } }) }, diff --git a/packages/cli-kit/src/public/node/graphiql/server.ts b/packages/cli-kit/src/public/node/graphiql/server.ts index cffeebcb819..58f8ca16ede 100644 --- a/packages/cli-kit/src/public/node/graphiql/server.ts +++ b/packages/cli-kit/src/public/node/graphiql/server.ts @@ -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. @@ -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.') @@ -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) => {