diff --git a/.changeset/add-graphiql-cdn-package.md b/.changeset/add-graphiql-cdn-package.md new file mode 100644 index 00000000000..1c8c9cae626 --- /dev/null +++ b/.changeset/add-graphiql-cdn-package.md @@ -0,0 +1,5 @@ +--- +'@graphiql/cdn': major +--- + +Initial release. `@graphiql/cdn` is a pre-bundled CDN distribution of GraphiQL: a single ESM file (`dist/graphiql.js`) that loads in the browser from any static CDN with no build step, no importmap entries for transitive dependencies, and no third-party bundler in the request path. The package inlines `graphiql`, `@graphiql/react`, `@graphiql/plugin-explorer`, `@graphiql/toolkit`, and `graphql`; `react` and `react-dom` stay external. Monaco workers are emitted to `dist/workers/*` and loaded from the same origin as the bundle. diff --git a/packages/graphiql-cdn/README.md b/packages/graphiql-cdn/README.md new file mode 100644 index 00000000000..ab1ca0d8b52 --- /dev/null +++ b/packages/graphiql-cdn/README.md @@ -0,0 +1,76 @@ +# `@graphiql/cdn` + +A pre-bundled CDN distribution of [GraphiQL](https://github.com/graphql/graphiql). Load GraphiQL in a browser from a static CDN with no build step, no importmap, and no third-party bundler in the request path. + +The package ships a single ESM file (`dist/graphiql.js`) with the GraphiQL UI, the default plugins, and all dependencies inlined. `react` and `react-dom` stay external and must be supplied by the page. + +## Usage + +```html + + + + + GraphiQL + + + + + +
+ + + +``` + +Pin a specific `` for production use. + +## What is exported + +- `GraphiQL` — the main component (also the default export) +- `HISTORY_PLUGIN` — the history plugin instance +- `explorerPlugin` — the schema explorer plugin factory +- `createGraphiQLFetcher` — convenience fetcher for `GraphiQL`'s `fetcher` prop +- `createLocalStorage` — convenience storage factory for multi-instance pages +- `GraphiQLReact` — the full `@graphiql/react` namespace, for advanced customization +- `GraphQL` — the full `graphql-js` namespace, so plugins can reuse the same instance + +## When not to use this package + +If you have a build step (Vite, webpack, Next.js, etc.), install `graphiql` and the plugins you want from npm directly. This package exists for the no-build-step CDN use case. diff --git a/packages/graphiql-cdn/package.json b/packages/graphiql-cdn/package.json new file mode 100644 index 00000000000..e3cacf15665 --- /dev/null +++ b/packages/graphiql-cdn/package.json @@ -0,0 +1,54 @@ +{ + "name": "@graphiql/cdn", + "version": "0.0.0", + "description": "Pre-bundled CDN distribution of GraphiQL: load GraphiQL in a browser from a static CDN with no build step.", + "repository": { + "type": "git", + "url": "https://github.com/graphql/graphiql", + "directory": "packages/graphiql-cdn" + }, + "homepage": "https://github.com/graphql/graphiql/tree/main/packages/graphiql-cdn#readme", + "bugs": { + "url": "https://github.com/graphql/graphiql/issues?q=issue+label:@graphiql/cdn" + }, + "license": "MIT", + "type": "module", + "main": "dist/graphiql.js", + "module": "dist/graphiql.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "exports": { + "./package.json": "./package.json", + "./style.css": "./dist/style.css", + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/graphiql.js" + } + }, + "scripts": { + "types:check": "tsgo --noEmit", + "build": "vite build" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2 || ^17.0.0", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "dependencies": { + "@graphiql/plugin-explorer": "^5.1.1", + "@graphiql/react": "^0.37.0", + "@graphiql/toolkit": "^0.12.0", + "graphiql": "^5.2.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4", + "lightningcss": "^1.30.1", + "react": "^19", + "react-dom": "^19", + "terser": "^5", + "vite": "^6", + "vite-plugin-dts": "^4.5.4" + } +} diff --git a/packages/graphiql-cdn/src/env.d.ts b/packages/graphiql-cdn/src/env.d.ts new file mode 100644 index 00000000000..2a3c835b1e3 --- /dev/null +++ b/packages/graphiql-cdn/src/env.d.ts @@ -0,0 +1,7 @@ +/// +declare module '*.css'; + +declare namespace globalThis { + import type * as monaco from 'monaco-editor'; + var MonacoEnvironment: monaco.Environment | undefined; +} diff --git a/packages/graphiql-cdn/src/index.ts b/packages/graphiql-cdn/src/index.ts new file mode 100644 index 00000000000..ef4c57c429d --- /dev/null +++ b/packages/graphiql-cdn/src/index.ts @@ -0,0 +1,22 @@ +import * as GraphiQLReact from '@graphiql/react'; +import { explorerPlugin } from '@graphiql/plugin-explorer'; +import { createGraphiQLFetcher, createLocalStorage } from '@graphiql/toolkit'; +import * as GraphQL from 'graphql'; +import { GraphiQL, HISTORY_PLUGIN } from 'graphiql'; +import 'graphiql/style.css'; +import '@graphiql/plugin-explorer/style.css'; +// Configure MonacoEnvironment with workers inlined as blob URLs, so the +// bundle can be loaded from a CDN onto pages of any origin. +import './setup-workers.js'; + +export { + GraphiQL, + HISTORY_PLUGIN, + GraphiQLReact, + GraphQL, + createGraphiQLFetcher, + createLocalStorage, + explorerPlugin, +}; + +export default GraphiQL; diff --git a/packages/graphiql-cdn/src/setup-workers.ts b/packages/graphiql-cdn/src/setup-workers.ts new file mode 100644 index 00000000000..66fb658f33d --- /dev/null +++ b/packages/graphiql-cdn/src/setup-workers.ts @@ -0,0 +1,24 @@ +/** + * CDN-targeted Monaco worker setup. Workers are inlined as blob URLs + * (Vite's `?worker&inline`) so the spawning page can be any origin: there + * is no cross-origin `new Worker(url)` to fail. + * + * Equivalent to `@graphiql/react/setup-workers/vite`, but with `&inline` + * because the consumer-side bundler isn't in the picture. + */ +/* eslint-disable import-x/default -- false positive */ +import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker.js?worker&inline'; +import GraphQLWorker from 'monaco-graphql/esm/graphql.worker.js?worker&inline'; +import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker&inline'; + +globalThis.MonacoEnvironment = { + getWorker(_workerId: string, label: string) { + switch (label) { + case 'json': + return new JsonWorker(); + case 'graphql': + return new GraphQLWorker(); + } + return new EditorWorker(); + }, +}; diff --git a/packages/graphiql-cdn/tsconfig.json b/packages/graphiql-cdn/tsconfig.json new file mode 100644 index 00000000000..ee54eec68ce --- /dev/null +++ b/packages/graphiql-cdn/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../graphiql-react/tsconfig.json" +} diff --git a/packages/graphiql-cdn/vite.config.mts b/packages/graphiql-cdn/vite.config.mts new file mode 100644 index 00000000000..a1591f98031 --- /dev/null +++ b/packages/graphiql-cdn/vite.config.mts @@ -0,0 +1,75 @@ +import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + // Emit relative asset/worker URLs so the bundle can be served from any + // CDN path (jsdelivr/unpkg/esm.sh `?raw`) rather than only the origin root. + // Without this, Vite hard-codes `new Worker("/workers/json.worker.js", ...)` + // which resolves to `https:///workers/...` (404) instead of + // `https:///.../dist/workers/...`. + base: './', + define: { + // graphql v17 + 'globalThis.process.env.NODE_ENV': 'true', + // https://github.com/graphql/graphql-js/blob/16.x.x/website/pages/docs/going-to-production.mdx + 'globalThis.process': 'true', + 'process.env.NODE_ENV': '"production"', + }, + plugins: [ + react(), + dts({ + include: ['src/**'], + outDir: ['dist'], + }), + ], + css: { + transformer: 'lightningcss', + }, + build: { + minify: 'terser', + sourcemap: true, + lib: { + entry: 'src/index.ts', + formats: ['es'], + fileName: 'graphiql', + cssFileName: 'style', + }, + rollupOptions: { + external: id => + id === 'react' || + id.startsWith('react/') || + id === 'react-dom' || + id.startsWith('react-dom/') || + id === 'graphql' || + id.startsWith('graphql/'), + output: { + // Inline every lazy `import(...)` into the main bundle. Critical for + // the CDN use case: monaco-editor's language contributions lazy-load + // their tokenizers via dynamic imports; if those land in separate + // chunks they ship as separate URLs that fragment monaco-editor into + // multiple instances at runtime. A single self-contained file + // guarantees one instance. + inlineDynamicImports: true, + }, + }, + }, + worker: { + format: 'es', + rollupOptions: { + output: { + entryFileNames: 'workers/[name].js', + // Just to group worker assets, add shared/internal chunks too + chunkFileNames: 'workers/[name].js', + // Workers are inlined as blob URLs (Vite's `?worker&inline`). The + // default sourcemap setting writes a `.js.map` next to the worker + // and appends `//# sourceMappingURL=foo.worker.js.map` to the + // source — but blob URLs have no real origin, so the comment + // resolves to `blob://nullnull/foo.worker.js.map` and the browser + // logs a CORS/local-resource error trying to fetch it. Suppress + // worker sourcemaps to keep the consumer's console clean. + sourcemap: false, + }, + }, + }, +}); diff --git a/yarn.lock b/yarn.lock index 6ddce0fc312..40172f20952 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3008,6 +3008,28 @@ __metadata: languageName: node linkType: hard +"@graphiql/cdn@workspace:packages/graphiql-cdn": + version: 0.0.0-use.local + resolution: "@graphiql/cdn@workspace:packages/graphiql-cdn" + dependencies: + "@graphiql/plugin-explorer": "npm:^5.1.1" + "@graphiql/react": "npm:^0.37.0" + "@graphiql/toolkit": "npm:^0.12.0" + "@vitejs/plugin-react": "npm:^4" + graphiql: "npm:^5.2.0" + lightningcss: "npm:^1.30.1" + react: "npm:^19" + react-dom: "npm:^19" + terser: "npm:^5" + vite: "npm:^6" + vite-plugin-dts: "npm:^4.5.4" + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2 || ^17.0.0 + react: ^18 || ^19 + react-dom: ^18 || ^19 + languageName: unknown + linkType: soft + "@graphiql/plugin-code-exporter@npm:^5.1.2, @graphiql/plugin-code-exporter@workspace:packages/graphiql-plugin-code-exporter": version: 0.0.0-use.local resolution: "@graphiql/plugin-code-exporter@workspace:packages/graphiql-plugin-code-exporter" @@ -3055,7 +3077,7 @@ __metadata: languageName: unknown linkType: soft -"@graphiql/plugin-explorer@npm:^5.1.2, @graphiql/plugin-explorer@workspace:packages/graphiql-plugin-explorer": +"@graphiql/plugin-explorer@npm:^5.1.1, @graphiql/plugin-explorer@npm:^5.1.2, @graphiql/plugin-explorer@workspace:packages/graphiql-plugin-explorer": version: 0.0.0-use.local resolution: "@graphiql/plugin-explorer@workspace:packages/graphiql-plugin-explorer" dependencies: @@ -3099,7 +3121,7 @@ __metadata: languageName: unknown linkType: soft -"@graphiql/react@npm:^0.37.4, @graphiql/react@npm:^0.37.5, @graphiql/react@workspace:packages/graphiql-react": +"@graphiql/react@npm:^0.37.0, @graphiql/react@npm:^0.37.4, @graphiql/react@npm:^0.37.5, @graphiql/react@workspace:packages/graphiql-react": version: 0.0.0-use.local resolution: "@graphiql/react@workspace:packages/graphiql-react" dependencies: @@ -12950,7 +12972,7 @@ __metadata: languageName: unknown linkType: soft -"graphiql@npm:^5.2.3, graphiql@workspace:packages/graphiql": +"graphiql@npm:^5.2.0, graphiql@npm:^5.2.3, graphiql@workspace:packages/graphiql": version: 0.0.0-use.local resolution: "graphiql@workspace:packages/graphiql" dependencies: @@ -13155,9 +13177,9 @@ __metadata: linkType: hard "graphql@npm:^16, graphql@npm:^16.11.0, graphql@npm:^16.13.2, graphql@npm:^16.9.0": - version: 16.13.2 - resolution: "graphql@npm:16.13.2" - checksum: 10c0/64e822a0a0e4398781e4bc9765b88d370c08261498b517add4b878038ef7be2005b6b394a79a5102b9379d57052f60bc7f23fec8f39808d101984a74772ebd9d + version: 16.14.0 + resolution: "graphql@npm:16.14.0" + checksum: 10c0/baaa368fcfeb7bf2cdf94b9f31bf916e97eaa9e7e82148a7046f31cbd49b760b38190f22393b024cbdd9ca0f4f46287515de0136b6a0cc164074b36ad7b50618 languageName: node linkType: hard @@ -15009,92 +15031,102 @@ __metadata: languageName: node linkType: hard -"lightningcss-darwin-arm64@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-darwin-arm64@npm:1.29.3" +"lightningcss-android-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-android-arm64@npm:1.32.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-arm64@npm:1.32.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"lightningcss-darwin-x64@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-darwin-x64@npm:1.29.3" +"lightningcss-darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-x64@npm:1.32.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"lightningcss-freebsd-x64@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-freebsd-x64@npm:1.29.3" +"lightningcss-freebsd-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-freebsd-x64@npm:1.32.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"lightningcss-linux-arm-gnueabihf@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-linux-arm-gnueabihf@npm:1.29.3" +"lightningcss-linux-arm-gnueabihf@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"lightningcss-linux-arm64-gnu@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-linux-arm64-gnu@npm:1.29.3" +"lightningcss-linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"lightningcss-linux-arm64-musl@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-linux-arm64-musl@npm:1.29.3" +"lightningcss-linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"lightningcss-linux-x64-gnu@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-linux-x64-gnu@npm:1.29.3" +"lightningcss-linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"lightningcss-linux-x64-musl@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-linux-x64-musl@npm:1.29.3" +"lightningcss-linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-musl@npm:1.32.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"lightningcss-win32-arm64-msvc@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-win32-arm64-msvc@npm:1.29.3" +"lightningcss-win32-arm64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"lightningcss-win32-x64-msvc@npm:1.29.3": - version: 1.29.3 - resolution: "lightningcss-win32-x64-msvc@npm:1.29.3" +"lightningcss-win32-x64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"lightningcss@npm:^1.29.3": - version: 1.29.3 - resolution: "lightningcss@npm:1.29.3" +"lightningcss@npm:^1.29.3, lightningcss@npm:^1.30.1": + version: 1.32.0 + resolution: "lightningcss@npm:1.32.0" dependencies: detect-libc: "npm:^2.0.3" - lightningcss-darwin-arm64: "npm:1.29.3" - lightningcss-darwin-x64: "npm:1.29.3" - lightningcss-freebsd-x64: "npm:1.29.3" - lightningcss-linux-arm-gnueabihf: "npm:1.29.3" - lightningcss-linux-arm64-gnu: "npm:1.29.3" - lightningcss-linux-arm64-musl: "npm:1.29.3" - lightningcss-linux-x64-gnu: "npm:1.29.3" - lightningcss-linux-x64-musl: "npm:1.29.3" - lightningcss-win32-arm64-msvc: "npm:1.29.3" - lightningcss-win32-x64-msvc: "npm:1.29.3" + lightningcss-android-arm64: "npm:1.32.0" + lightningcss-darwin-arm64: "npm:1.32.0" + lightningcss-darwin-x64: "npm:1.32.0" + lightningcss-freebsd-x64: "npm:1.32.0" + lightningcss-linux-arm-gnueabihf: "npm:1.32.0" + lightningcss-linux-arm64-gnu: "npm:1.32.0" + lightningcss-linux-arm64-musl: "npm:1.32.0" + lightningcss-linux-x64-gnu: "npm:1.32.0" + lightningcss-linux-x64-musl: "npm:1.32.0" + lightningcss-win32-arm64-msvc: "npm:1.32.0" + lightningcss-win32-x64-msvc: "npm:1.32.0" dependenciesMeta: + lightningcss-android-arm64: + optional: true lightningcss-darwin-arm64: optional: true lightningcss-darwin-x64: @@ -15115,7 +15147,7 @@ __metadata: optional: true lightningcss-win32-x64-msvc: optional: true - checksum: 10c0/6817846ecc0767b31de7a22cfe977dfc679e7dd7be8b1f811364ce7c1f8db079d6d2161e9d5d4e6a7ecb166f920dfb20fe7f60fba328a1c60c3dc358ec3b45f2 + checksum: 10c0/70945bd55097af46fc9fab7f5ed09cd5869d85940a2acab7ee06d0117004a1d68155708a2d462531cea2fc3c67aefc9333a7068c80b0b78dd404c16838809e03 languageName: node linkType: hard @@ -20380,9 +20412,9 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.10.0, terser@npm:^5.17.4, terser@npm:^5.31.1": - version: 5.46.2 - resolution: "terser@npm:5.46.2" +"terser@npm:^5, terser@npm:^5.10.0, terser@npm:^5.17.4, terser@npm:^5.31.1": + version: 5.47.1 + resolution: "terser@npm:5.47.1" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.15.0" @@ -20390,7 +20422,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10c0/476f1820160c42e6b2f611410115b00321c4666d421f12db87f13810f8789de45cb254e3ad5178650696d0ba6b706f5a0a239272255d6d1be95816c660f8cbbb + checksum: 10c0/e7017001aff657b8eb444edb4c4ad2425b90c141c5329f06b1939161f38884622972ec7b12d197207229c8079d24d20bf44be93852f735fe3bb4b32d80775775 languageName: node linkType: hard @@ -21450,9 +21482,9 @@ __metadata: languageName: node linkType: hard -"vite-plugin-dts@npm:^4.0.1, vite-plugin-dts@npm:^4.5.3": - version: 4.5.3 - resolution: "vite-plugin-dts@npm:4.5.3" +"vite-plugin-dts@npm:^4.0.1, vite-plugin-dts@npm:^4.5.3, vite-plugin-dts@npm:^4.5.4": + version: 4.5.4 + resolution: "vite-plugin-dts@npm:4.5.4" dependencies: "@microsoft/api-extractor": "npm:^7.50.1" "@rollup/pluginutils": "npm:^5.1.4" @@ -21469,7 +21501,7 @@ __metadata: peerDependenciesMeta: vite: optional: true - checksum: 10c0/e86ff3a92bda138b5d69696297f520e796282d65af8f459c2ed7870e79fc67b3ea74afabe79678c2141e1ab651ec73c3928d8ae255ff08ff0c9e08477f8b1bcf + checksum: 10c0/5fcb7f3739d115f36195a692c0e9f9fca4e504bbbbabe29e71ee06630dd05ea2920169371e80e548eb4779d2eca14107277497838d7df588d53e1fadf84be861 languageName: node linkType: hard