diff options
Diffstat (limited to 'source/graphiql')
| -rwxr-xr-x | source/graphiql/markup.ts | 309 | ||||
| -rwxr-xr-x | source/graphiql/render.ts | 239 |
2 files changed, 69 insertions, 479 deletions
diff --git a/source/graphiql/markup.ts b/source/graphiql/markup.ts deleted file mode 100755 index 6bacbf3..0000000 --- a/source/graphiql/markup.ts +++ /dev/null @@ -1,309 +0,0 @@ - - - -/*** EXPORT ------------------------------------------- ***/ - -export const getLoadingMarkup = () => ({ - container: ` - <style> - @keyframes fadeIn { - from { - opacity: 0; - transform: translateY(-10px); - } - - to { - opacity: 1; - transform: translateY(0); - } - } - - @keyframes fadeOut { - from { - opacity: 1; - transform: translateY(0); - } - - to { - opacity: 0; - transform: translateY(-10px); - } - } - - .fadeOut { - animation: fadeOut 0.5s ease-out forwards; - } - - #loading-wrapper { - width: 100vw; height: 100vh; - - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - position: absolute; - } - - .logo { - width: 75px; height: 75px; - - animation: fadeIn 0.5s ease-out forwards; - margin-bottom: 20px; - opacity: 0; - } - - .text { - animation: fadeIn 0.5s ease-out forwards; - color: rgba(255, 255, 255, 0.6); - font-size: 32px; - font-weight: 200; - opacity: 0; - text-align: center; - } - - .text-inner { - font-weight: 400; - } - </style> - - <div id="loading-wrapper"> - <svg class="logo" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"> - <title>GraphQL Playground Logo</title> - - <style> - @keyframes appearIn { - from { - opacity: 0; - transform: translateY(0px); - } - - to { - opacity: 1; - transform: translateY(0); - } - } - - @keyframes scaleIn { - from { - transform: scale(0); - } - - to { - transform: scale(1); - } - } - - @keyframes innerDrawIn { - 0% { - stroke-dashoffset: 70; - } - - 50% { - stroke-dashoffset: 140; - } - - 100% { - stroke-dashoffset: 210; - } - } - - @keyframes outerDrawIn { - 0% { - stroke-dashoffset: 76; - } - - 100% { - stroke-dashoffset: 152; - } - } - - .circle-top, - .circle-top-right, - .circle-bottom-right, - .circle-bottom, - .circle-bottom-left, - .circle-top-left { - fill: white; - } - - .circle-top { - animation: scaleIn 0.25s linear forwards 0.2222222222222222s; - transform: scale(0); - transform-origin: 64px 28px; - } - - .circle-top-right { - animation: scaleIn 0.25s linear forwards 0.4222222222222222s; - transform: scale(0); - transform-origin: 95.98500061035156px 46.510000228881836px; - } - - .circle-bottom-right { - animation: scaleIn 0.25s linear forwards 0.6222222222222222s; - transform: scale(0); - transform-origin: 95.97162628173828px 83.4900016784668px; - } - - .circle-bottom { - animation: scaleIn 0.25s linear forwards 0.8222222222222223s; - transform: scale(0); - transform-origin: 64px 101.97999572753906px; - } - - .circle-bottom-left { - animation: scaleIn 0.25s linear forwards 1.0222222222222221s; - transform: scale(0); - transform-origin: 32.03982162475586px 83.4900016784668px; - } - - .circle-top-left { - animation: scaleIn 0.25s linear forwards 1.2222222222222223s; - transform: scale(0); - transform-origin: 32.033552169799805px 46.510000228881836px; - } - - .octoline-top-right, - .octoline-right, - .octoline-bottom-right, - .octoline-bottom-left, - .octoline-left, - .octoline-top-left { - stroke: white; - stroke-linecap: round; - stroke-linejoin: round; - stroke-width: 4; - } - - .octoline-top-right { - animation: outerDrawIn 0.5s ease-out forwards 0.3333333333333333s, appearIn 0.1s ease-out forwards 0.3333333333333333s; - animation-iteration-count: 1, 1; - opacity: 0; - stroke-dasharray: 76; - } - - .octoline-right { - animation: outerDrawIn 0.5s ease-out forwards 0.5333333333333333s, appearIn 0.1s ease-out forwards 0.5333333333333333s; - animation-iteration-count: 1, 1; - opacity: 0; - stroke-dasharray: 76; - } - - .octoline-bottom-right { - animation: outerDrawIn 0.5s ease-out forwards 0.7333333333333334s, appearIn 0.1s ease-out forwards 0.7333333333333334s; - animation-iteration-count: 1, 1; - opacity: 0; - stroke-dasharray: 76; - } - - .octoline-bottom-left { - animation: outerDrawIn 0.5s ease-out forwards 0.9333333333333333s, appearIn 0.1s ease-out forwards 0.9333333333333333s; - animation-iteration-count: 1, 1; - opacity: 0; - stroke-dasharray: 76; - } - - .octoline-left { - animation: outerDrawIn 0.5s ease-out forwards 1.1333333333333333s, appearIn 0.1s ease-out forwards 1.1333333333333333s; - animation-iteration-count: 1, 1; - opacity: 0; - stroke-dasharray: 76; - } - - .octoline-top-left { - animation: outerDrawIn 0.5s ease-out forwards 1.3333333333333333s, appearIn 0.1s ease-out forwards 1.3333333333333333s; - animation-iteration-count: 1, 1; - opacity: 0; - stroke-dasharray: 76; - } - - .triangle-bottom, - .triangle-left, - .triangle-right { - stroke: white; - stroke-linecap: round; - stroke-width: 4; - } - - .triangle-bottom { - animation: innerDrawIn 1s ease-in-out forwards 1.3666666666666667s, appearIn 0.1s linear forwards 1.3666666666666667s; - animation-iteration-count: infinite, 1; - opacity: 0; - stroke-dasharray: 70; - } - - .triangle-left { - animation: innerDrawIn 1s ease-in-out forwards 1.5333333333333332s, appearIn 0.1s linear forwards 1.5333333333333332s; - animation-iteration-count: infinite, 1; - opacity: 0; - stroke-dasharray: 70; - } - - .triangle-right { - animation: innerDrawIn 1s ease-in-out forwards 1.7000000000000002s, appearIn 0.1s linear forwards 1.7000000000000002s; - animation-iteration-count: infinite, 1; - opacity: 0; - stroke-dasharray: 70; - } - </style> - - <defs> - <linearGradient id="linearGradient" x1="4.86%" x2="96.21%" y1="0%" y2="99.66%"> - <stop stop-color="#e00082" stop-opacity="0.8" offset="0%"></stop> - <stop stop-color="#e00082" offset="100%"></stop> - </linearGradient> - </defs> - - <g> - <rect id="gradient" width="127.96" height="127.96" y="1" fill="url(#linearGradient)" rx="4"></rect> - <path id="border" fill="#e00082" fill-rule="nonzero" d="M4.7 2.84c-1.58 0-2.86 1.28-2.86 2.85v116.57c0 1.57 1.28 2.84 2.85 2.84h116.57c1.57 0 2.84-1.26 2.84-2.83V5.67c0-1.55-1.26-2.83-2.83-2.83H4.67zM4.7 0h116.58c3.14 0 5.68 2.55 5.68 5.7v116.58c0 3.14-2.54 5.68-5.68 5.68H4.68c-3.13 0-5.68-2.54-5.68-5.68V5.68C-1 2.56 1.55 0 4.7 0z"></path> - - <path - class="circle-top" x="64" y="28" - d="M64 36c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8" - style="transform: translate(100px, 100px);"></path> - <path - class="circle-top-right" x="95.98500061035156" y="46.510000228881836" - d="M89.04 50.52c-2.2-3.84-.9-8.73 2.94-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.76.9-10.97-2.94" - style="transform: translate(100px, 100px);"></path> - <path - class="circle-bottom-right" x="95.97162628173828" y="83.4900016784668" - d="M102.9 87.5c-2.2 3.84-7.1 5.15-10.94 2.94-3.84-2.2-5.14-7.12-2.94-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.86 2.23 5.16 7.12 2.94 10.96" - style="transform: translate(100px, 100px);"></path> - <path - class="circle-bottom" x="64" y="101.97999572753906" - d="M64 110c-4.43 0-8-3.6-8-8.02 0-4.44 3.57-8.02 8-8.02s8 3.58 8 8.02c0 4.4-3.57 8.02-8 8.02" - style="transform: translate(100px, 100px);"></path> - <path - class="circle-bottom-left" x="32.03982162475586" y="83.4900016784668" - d="M25.1 87.5c-2.2-3.84-.9-8.73 2.93-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.74.9-10.95-2.94" - style="transform: translate(100px, 100px);"></path> - <path - class="circle-top-left" x="32.033552169799805" y="46.510000228881836" - d="M38.96 50.52c-2.2 3.84-7.12 5.15-10.95 2.94-3.82-2.2-5.12-7.12-2.92-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.83 2.23 5.14 7.12 2.94 10.96" - style="transform: translate(100px, 100px);"></path> - - <path class="octoline-top-right" d="M63.55 27.5l32.9 19-32.9-19z"></path> - <path class="octoline-right" d="M96 46v38-38z"></path> - <path class="octoline-bottom-right" d="M96.45 84.5l-32.9 19 32.9-19z"></path> - <path class="octoline-bottom-left" d="M64.45 103.5l-32.9-19 32.9 19z"></path> - <path class="octoline-left" d="M32 84V46v38z"></path> - <path class="octoline-top-left" d="M31.55 46.5l32.9-19-32.9 19z"></path> - - <path class="triangle-bottom" d="M30 84h70"></path> - <path class="triangle-left" d="M65 26L30 87"></path> - <path class="triangle-right" d="M98 87L63 26"></path> - </g> - </svg> - - <div class="text">Loading - <span class="text-inner">GraphQL Playground</span> - </div> - </div> - `, - script: ` - const loadingWrapper = document.getElementById("loading-wrapper"); - - if (loadingWrapper) - loadingWrapper.classList.add("fadeOut"); - ` -}); diff --git a/source/graphiql/render.ts b/source/graphiql/render.ts index a0e4dc2..7adec57 100755 --- a/source/graphiql/render.ts +++ b/source/graphiql/render.ts @@ -3,213 +3,112 @@ /*** IMPORT ------------------------------------------- ***/ +import { default as dedent } from "@netopwibby/dedent"; import { filterXSS } from "xss"; /*** UTILITY ------------------------------------------ ***/ -import { getLoadingMarkup } from "./markup.ts"; +const filter = (val: string) => filterXSS(val, { + stripIgnoreTag: true, + stripIgnoreTagBody: ["script"], + whiteList: {} +}); -const CONFIG_ID = "playground-config"; -const loading = getLoadingMarkup(); - -const filter = (val: string) => { - return filterXSS(val, { - stripIgnoreTag: true, - stripIgnoreTagBody: ["script"], - whiteList: {} - }); -} - -const getCdnMarkup = ({ cdnUrl = "//cdn.jsdelivr.net/npm", faviconUrl, version }: { - cdnUrl?: string - faviconUrl?: string | null +const buildAssetUrl = ({ cdnUrl, suffix, version }: { + cdnUrl: string + suffix: string version?: string -}) => { - const buildCDNUrl = (packageName: string, suffix: string) => - filter(`${cdnUrl}/${packageName}${version ? `@${version}` : ""}/${suffix}` || ""); - - return ` - <link rel="stylesheet" href="${buildCDNUrl("graphql-playground-react", "build/static/css/index.css")}"/> - ${typeof faviconUrl === "string" ? `<link rel="shortcut icon" href="${filter(faviconUrl || "")}" />` : ""} - ${faviconUrl === undefined ? `<link rel="shortcut icon" href="${buildCDNUrl("graphql-playground-react", "build/favicon.png")}" />` : ""} - <script src="${buildCDNUrl("graphql-playground-react", "build/static/js/middleware.js")}"></script> - `; -} - -const renderConfig = (config: unknown) => { - return filterXSS(`<div id="${CONFIG_ID}">${JSON.stringify(config)}</div>`, { - whiteList: { div: ["id"] } - }); -}; +}) => filter(`${cdnUrl}/@eeeooolll/graphiql${version ? `@${version}` : ""}/dist/${suffix}`); /*** EXPORT ------------------------------------------- ***/ -export interface MiddlewareOptions { - codeTheme?: EditorColours; - config?: { [key: string]: unknown; }; - endpoint?: string; - env?: "electron" | "react"; - schema?: IntrospectionResult; - settings?: ISettings; - subscriptionEndpoint?: string; - tabs?: Tab[]; - workspaceName?: string; -} - -export type CursorShape = "line" | "block" | "underline"; -export type Theme = "dark" | "light"; - -export interface ISettings { - "editor.cursorShape": CursorShape; - "editor.fontFamily": string; - "editor.fontSize": number; - "editor.reuseHeaders": boolean; - "editor.theme": Theme; - "general.betaUpdates": boolean; - "request.credentials": string; - "request.globalHeaders": { [key: string]: string }; - "schema.polling.enable": boolean; - "schema.polling.endpointFilter": string; - "schema.polling.interval": number; - "tracing.hideTracingResponse": boolean; - "tracing.tracingSupported": boolean; -} - -export interface EditorColours { - atom: string; - attribute: string; - builtin: string; - comment: string; - cursorColor: string; - def: string; - editorBackground: string; - keyword: string; - leftDrawerBackground: string; - meta: string; - number: string; - property: string; - punctuation: string; - qualifier: string; - resultBackground: string; - rightDrawerBackground: string; - selection: string; - string: string; - string2: string; - variable: string; - ws: string; -} - -export interface IntrospectionResult { - __schema: { [key: string]: unknown; }; -} - -export interface RenderPageOptions extends MiddlewareOptions { +export interface RenderPageOptions { + /** Base CDN. Defaults to jsDelivr. */ cdnUrl?: string; - env?: "electron" | "react"; + /** GraphQL endpoint the embedded fetcher posts to. */ + endpoint?: string; + /** Optional `<link rel="shortcut icon">`. `null` skips, `undefined` falls back to default. */ faviconUrl?: string | null; + /** Document `<title>`. */ title?: string; + /** Pin `@eeeooolll/graphiql` to a specific version on the CDN. */ version?: string; } -export interface Tab { - endpoint: string; - headers?: { [key: string]: string }; - name?: string; - query: string; - responses?: string[]; - variables?: string; -} - /** - * Renders the GraphQL Playground HTML shell. + * Renders the GraphiQL HTML shell. + * + * Loads the prebuilt IIFE bundle from `@eeeooolll/graphiql` (registered as + * `window.EolGraphiQL`) and mounts it against `endpoint`. * * Usually called indirectly via `GraphQLHTTP({ graphiql: true })`; invoke it - * directly if you need to embed the Playground in a custom route. + * directly if you need to embed GraphiQL in a custom route. */ -export function renderPlaygroundPage(options: RenderPageOptions) { - const extendedOptions: - & Partial<{ - canSaveConfig: boolean - configString: string - }> - & RenderPageOptions = { - ...options, - canSaveConfig: false - }; - - if (options.config) - extendedOptions.configString = JSON.stringify(options.config, null, 2); - - if (!extendedOptions.endpoint && !extendedOptions.configString) - console.warn("WARNING: You did not provide an endpoint and do not have a .graphqlconfig. Make sure you have at least one of them."); - else if (extendedOptions.endpoint) - extendedOptions.endpoint = filter(extendedOptions.endpoint || ""); - - return ` +export function renderPlaygroundPage(options: RenderPageOptions): string { + const { + cdnUrl = "//cdn.jsdelivr.net/npm", + endpoint = "/graphql", + faviconUrl, + title = "GraphiQL", + version + } = options; + + const safeEndpoint = filter(endpoint); + const safeTitle = filter(title); + const scriptUrl = buildAssetUrl({ cdnUrl, suffix: "standalone.js", version }); + const styleUrl = buildAssetUrl({ cdnUrl, suffix: "standalone.css", version }); + + const faviconLink = + faviconUrl === null ? + "" : + typeof faviconUrl === "string" ? + `<link rel="shortcut icon" href="${filter(faviconUrl)}"/>` : + `<link rel="shortcut icon" href="${buildAssetUrl({ cdnUrl, suffix: "favicon.svg", version })}"/>`; + + return dedent` <!DOCTYPE html> <html lang="en"> <head> - <meta charset=utf-8/> - <meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui"/> - <link href="https://brick.a.ssl.fastly.net/Open+Sans:300,400,600,700/Source+Code+Pro:400,700" rel="stylesheet"/> - <title>${extendedOptions.title || "GraphQL Playground"}</title> - ${extendedOptions.env === "react" || extendedOptions.env === "electron" ? "" : getCdnMarkup(extendedOptions)} - </head> + <meta charset="utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1"/> + <title>${safeTitle}</title> + ${faviconLink} + <link rel="stylesheet" href="${styleUrl}"/> - <body> <style> - html { - font-family: "Open Sans", sans-serif; - overflow: hidden; - } - - body { - background-color: #172a3a; - margin: 0; + *, + *::before, + *::after { + margin: 0; padding: 0; + box-sizing: inherit; } - #${CONFIG_ID} { - display: none; + html { + box-sizing: border-box; } - .playgroundIn { - animation: playgroundIn 0.5s ease-out forwards; + html, + body { + height: 100%; } - @keyframes playgroundIn { - from { - opacity: 0; - transform: translateY(10px); - } - - to { - opacity: 1; - transform: translateY(0); - } + #root { + height: 100vh; } </style> + </head> - ${loading.container} - ${renderConfig(extendedOptions)} - <div id="root"/> + <body> + <div id="root"></div> + + <script src="${scriptUrl}"></script> <script> window.addEventListener("load", () => { - ${loading.script} - const root = document.getElementById("root"); - root.classList.add("playgroundIn"); - const configText = document.getElementById("${CONFIG_ID}").innerText; - - if (configText && configText.length) { - try { - GraphQLPlayground.init(root, JSON.parse(configText)); - } catch(_) { - console.error("could not find config"); - } - } else { - GraphQLPlayground.init(root); - } + const fetcher = window.EolGraphiQL.createHttpFetcher({ url: "${safeEndpoint}" }); + + window.EolGraphiQL.mount(root, { fetcher }); }); </script> </body> |