UNPKG

@dillonkearns/elm-graphql

Version:

<img src="https://cdn.jsdelivr.net/gh/martimatix/logo-graphqelm/logo.svg" alt="dillonearns/elm-graphql logo" width="40%" align="right">

223 lines (201 loc) 6.52 kB
const { Elm } = require("./Main.elm"); import * as fs from "fs-extra"; import { GraphQLClient } from "graphql-request"; import * as http from "http"; import * as request from "request"; import { applyElmFormat } from "./formatted-write"; import { introspectionQuery } from "./introspection-query"; import * as glob from "glob"; import * as path from "path"; import * as childProcess from "child_process"; import { removeGenerated, isGenerated, warnAndExitIfContainsNonGenerated } from "./cli/generated-code-handler"; const npmPackageVersion = require("../../package.json").version; const elmPackageVersion = require("../../elm.json").version; const targetComment = `-- Do not manually edit this file, it was auto-generated by dillonkearns/elm-graphql -- https://github.com/dillonkearns/elm-graphql `; const versionMessage = `npm version ${npmPackageVersion}\nTargeting elm package dillonkearns/elm-graphql@${elmPackageVersion}`; function prependBasePath( suffixPath: string, baseModule: string[], outputPath: string ): string { return path.join(outputPath, baseModule.join("/"), suffixPath); } let app = Elm.Main.init({ flags: { argv: process.argv, versionMessage } }); // app.ports.print.subscribe(console.log); app.ports.printAndExitFailure.subscribe((message: string) => { console.log(message); process.exit(1); }); app.ports.printAndExitSuccess.subscribe((message: string) => { console.log(message); process.exit(0); }); app.ports.introspectSchemaFromFile.subscribe( ({ introspectionFilePath, outputPath, baseModule, customDecodersModule }: { introspectionFilePath: string; outputPath: string; baseModule: string[]; customDecodersModule: string | null; }) => { warnAndExitIfContainsNonGenerated({ baseModule, outputPath }); const introspectionFileJson = JSON.parse( fs.readFileSync(introspectionFilePath).toString() ); onDataAvailable( introspectionFileJson.data || introspectionFileJson, outputPath, baseModule, customDecodersModule ); } ); app.ports.introspectSchemaFromUrl.subscribe( ({ graphqlUrl, excludeDeprecated, outputPath, baseModule, headers, customDecodersModule }: { graphqlUrl: string; excludeDeprecated: boolean; outputPath: string; baseModule: string[]; headers: {}; customDecodersModule: string | null; }) => { warnAndExitIfContainsNonGenerated({ baseModule, outputPath }); console.log("Fetching GraphQL schema..."); new GraphQLClient(graphqlUrl, { mode: "cors", headers: headers }) .request(introspectionQuery, { includeDeprecated: !excludeDeprecated }) .then(data => { onDataAvailable(data, outputPath, baseModule, customDecodersModule); }) .catch(err => { console.log(err.response || err); process.exit(1); }); } ); function makeEmptyDirectories( baseModule: string[], outputPath: string, directoryNames: string[] ): void { directoryNames.forEach(dir => { fs.mkdirpSync(prependBasePath(dir, baseModule, outputPath)); }); } function onDataAvailable( data: {}, outputPath: string, baseModule: string[], customDecodersModule: string | null ) { console.log("Generating files..."); app.ports.generatedFiles.subscribe(async function(generatedFile: { [s: string]: string; }) { removeGenerated(prependBasePath("/", baseModule, outputPath)); makeEmptyDirectories(baseModule, outputPath, [ "InputObject", "Object", "Interface", "Union", "Enum" ]); await Promise.all(writeGeneratedFiles(outputPath, generatedFile)).catch( err => { console.error("Error writing files", err); } ); writeIntrospectionFile(baseModule, outputPath); applyElmFormat(prependBasePath("/", baseModule, outputPath)); if (customDecodersModule) { verifyCustomCodecsFileIsValid( outputPath, baseModule, customDecodersModule ); } console.log("Success!"); }); app.ports.generateFiles.send(data); } function verifyCustomCodecsFileIsValid( outputPath: string, baseModule: string[], customDecodersModule: string ) { const verifyDecodersFile = path.join( outputPath, ...baseModule, "VerifyScalarCodecs.elm" ); try { childProcess.execSync(`elm make ${verifyDecodersFile} --output=/dev/null`, { stdio: "pipe" }); } catch (error) { console.error(error.message); console.error(`-------------------------------------------- INVALID SCALAR DECODERS FILE -------------------------------------------- Your custom scalar decoders module, \`${customDecodersModule}\`, is invalid. This is because either: 1) This is the first time you've run this CLI with the \`--scalar-codecs\` option. In this case, get a valid file, you can start by copy-pasting \`${baseModule.join( "." )}.ScalarCodecs\`. Then change the module name to \`${customDecodersModule}\` and you have a valid starting point! 2) You added or renamed a Custom Scalar in your GraphQL schema. To handle the new Custom Scalar, you can copy the relevant entries from \`${customDecodersModule}\`. Check the following: * You have a module called \`${customDecodersModule}\` * The module is somewhere in your elm path (check the \`source-directories\` in your \`elm.json\`) You must: * Have a type for every custom scalar * Expose each of these types * Expose a \`decoders\` value Above the dashes (----) there are some details that might help you debug the issue. Remember, you can always copy-paste the \`${baseModule.join( "." )}.ScalarCodecs\` module to get a valid file. After you've copy pasted the template file, or tried fixing the file, re-run this CLI command to make sure it is valid. `); process.exit(1); } } function writeGeneratedFiles( outputPath: string, generatedFile: { [s: string]: string; } ): Promise<void>[] { return Object.entries(generatedFile).map(([fileName, fileContents]) => { const filePath = path.join(outputPath, fileName); return fs.writeFile(filePath, targetComment + fileContents); }); } function writeIntrospectionFile(baseModule: string[], outputPath: string) { fs.writeFileSync( prependBasePath("elm-graphql-metadata.json", baseModule, outputPath), `{"targetElmPackageVersion": "${elmPackageVersion}", "generatedByNpmPackageVersion": "${npmPackageVersion}"}` ); }