aboutsummaryrefslogtreecommitdiff
path: root/source/graphiql/render.ts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xsource/graphiql/render.ts239
1 files changed, 69 insertions, 170 deletions
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>