UNPKG

@tobiastengler/create-relay-app

Version:

Easy configuration of Relay for existing projects

189 lines (177 loc) 6.67 kB
import { TaskBase } from "./TaskBase.js"; import { bold, prettifyCode } from "../utils/index.js"; import { EOL } from "os"; export const HTTP_ENDPOINT = "HTTP_ENDPOINT"; export const WEBSOCKET_ENDPOINT = "WEBSOCKET_ENDPOINT"; export class GenerateRelayEnvironmentTask extends TaskBase { constructor(context) { super(); this.context = context; this.message = "Generate Relay environment"; } isEnabled() { return true; } async run() { await this.addRelayEnvironmentFile(); } // todo: this could maybe be simplified by also using the AST. // this would also enable us to update an existing configuration. async addRelayEnvironmentFile() { this.updateMessage(this.message + " " + bold(this.context.relayEnvFile.rel)); if (this.context.fs.exists(this.context.relayEnvFile.abs)) { this.skip("File exists"); return; } const b = new CodeBuilder(); // Add imports const relayRuntimeImports = ["Environment", "Network", "RecordSource", "Store"]; if (this.context.args.subscriptions) { relayRuntimeImports.push("Observable"); } if (this.context.args.typescript) { relayRuntimeImports.push("FetchFunction"); if (this.context.args.subscriptions) { relayRuntimeImports.push("SubscribeFunction"); } } // prettier-ignore b.addLine(`import { ${relayRuntimeImports.join(", ")} } from "relay-runtime";`); if (this.context.args.subscriptions) { b.addLine(`import { createClient } from "graphql-ws";`); } b.addLine(); // Add configurations b.addLine(`const ${HTTP_ENDPOINT} = "http://localhost:5000/graphql";`); if (this.context.args.subscriptions) { b.addLine(`const ${WEBSOCKET_ENDPOINT} = "ws://localhost:5000/graphql";`); } b.addLine(); // Add fetchFn let fetchFn = `const fetchFn: FetchFunction = async (request, variables) => { const resp = await fetch(${HTTP_ENDPOINT}, { method: "POST", headers: { Accept: "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", "Content-Type": "application/json", // <-- Additional headers like 'Authorization' would go here }, body: JSON.stringify({ query: request.text, // <-- The GraphQL document composed by Relay variables, }), }); return await resp.json(); };`; if (!this.context.args.typescript) { // Remove TypeScript type fetchFn = fetchFn.replace("fetchFn: FetchFunction", "fetchFn"); } b.addLine(fetchFn); b.addLine(); // Add subscribeFn if (this.context.args.subscriptions) { if (this.context.args.typescript) { b.addLine(`let subscribeFn: SubscribeFunction; if (typeof window !== "undefined") { // We only want to setup subscriptions if we are on the client. const subscriptionsClient = createClient({ url: WEBSOCKET_ENDPOINT, }); subscribeFn = (request, variables) => { // To understand why we return Observable.create<any>, // please see: https://github.com/enisdenjo/graphql-ws/issues/316#issuecomment-1047605774 return Observable.create<any>((sink) => { if (!request.text) { return sink.error(new Error("Operation text cannot be empty")); } return subscriptionsClient.subscribe( { operationName: request.name, query: request.text, variables, }, sink ); }); }; }`); } else { b.addLine(`let subscribeFn; if (typeof window !== "undefined") { // We only want to setup subscriptions if we are on the client. const subscriptionsClient = createClient({ url: WEBSOCKET_ENDPOINT, }); subscribeFn = (request, variables) => { return Observable.create((sink) => { if (!request.text) { return sink.error(new Error("Operation text cannot be empty")); } return subscriptionsClient.subscribe( { operationName: request.name, query: request.text, variables, }, sink ); }); }; }`); } } b.addLine(); // Create environment let createEnv = `function createRelayEnvironment() { return new Environment({ network: Network.create(fetchFn), store: new Store(new RecordSource()), }); }`; if (this.context.args.subscriptions) { createEnv = createEnv.replace("fetchFn", "fetchFn, subscribeFn"); } b.addLine(createEnv); b.addLine(); // Export environment if (this.context.is("next")) { let initEnv = `let relayEnvironment: Environment | undefined; export function initRelayEnvironment() { const environment = relayEnvironment ?? createRelayEnvironment(); // For SSG and SSR always create a new Relay environment. if (typeof window === "undefined") { return environment; } // Create the Relay environment once in the client // and then reuse it. if (!relayEnvironment) { relayEnvironment = environment; } return relayEnvironment; }`; if (!this.context.args.typescript) { initEnv = initEnv.replace(": Environment | undefined", ""); } b.addLine(initEnv); } else { b.addLine(`export const RelayEnvironment = createRelayEnvironment();`); } const prettifiedCode = prettifyCode(b.code); await this.context.fs.createDirectory(this.context.relayEnvFile.parentDirectory); await this.context.fs.writeToFile(this.context.relayEnvFile.abs, prettifiedCode); } } class CodeBuilder { constructor() { this._code = ""; } get code() { return this._code; } addLine(line) { this._code += (line || "") + EOL; } }