/** * Copyright (c) 2016, John Hewson * All rights reserved. */ /// /// /// /// import 'source-map-support/register'; import * as fs from 'fs'; import * as path from 'path'; import * as child_process from 'child_process'; import * as request from 'request'; import * as commandLineArgs from 'command-line-args'; import { introspectionQuery, buildClientSchema } from 'graphql/utilities'; import { GraphQLSchema } from 'graphql/type'; import { queryToElm } from './query-to-elm'; // entry point let optionDefinitions = [ { name: 'init', type: Boolean }, { name: 'endpoint', type: String, defaultOption: true }, { name: 'schema', type: String }, { name: 'method', type: String }, { name: 'help', type: Boolean }, ]; let options: any = commandLineArgs(optionDefinitions); console.log(options); // usage if (options.help) { usage(); process.exit(1); } if (!options.endpoint) { console.error('Need endpointURL'); process.exit(1); } // output config let verb = 'GET'; let endpointUrl = options.endpoint; performIntrospectionQuery(body => { let result = JSON.parse(body); let schema = buildClientSchema(result.data); processFiles(schema); }); function performIntrospectionQuery(callback: (body: string) => void) { // introspection query let introspectionUrl = options.endpoint; if (!introspectionUrl) { console.log('Error: missing graphql endpoint in elm-package.json'); process.exit(1); } let method = 'GET'; let reqOpts = method == 'GET' ? { url: introspectionUrl, method, qs: { query: introspectionQuery.replace(/\n/g, '').replace(/\s+/g, ' ') } } : { url: introspectionUrl, method, headers: [{ 'Content-Type': 'application/json' }], body: JSON.stringify({ query: introspectionQuery }) }; request(reqOpts, function (err, res, body) { if (err) { throw new Error(err); } else if (res.statusCode == 200) { callback(body); } else { console.error('Error', res.statusCode, '-', res.statusMessage); console.error('\n', res.headers); console.error('\n', body.trim()); console.error('\nThe GraphQL server at ' + introspectionUrl + ' responded with an error.'); process.exit(1); } }); } function capitalize(str: string) { return str[0].toUpperCase() + str.substr(1); } function processFiles(schema: GraphQLSchema) { let paths = scanDir('.', []); for (let filePath of paths) { let fullpath = path.join(...filePath); let graphql = fs.readFileSync(fullpath, 'utf8'); let rootindex = fullpath.indexOf("src/elm/"); let rootpath = fullpath.substr(rootindex + 8); let pathdirs = rootpath.split('/'); let filepath = pathdirs.map(capitalize).join('.'); let basename = path.basename(fullpath); let extname = path.extname(fullpath); let filename = basename.substr(0, basename.length - extname.length); let moduleName = filepath.substr(0, filepath.length - extname.length); let outPath = path.join(path.dirname(fullpath), filename + '.elm'); let elm = queryToElm(graphql, moduleName, endpointUrl, verb, schema); fs.writeFileSync(outPath, elm); // if elm-format is available then run it on the output try { child_process.execSync('elm-format "' + outPath + '" --yes'); } catch (e) { // ignore } } let plural = paths.length != 1 ? 's' : ''; console.log('Success! Generated ' + paths.length + ' module' + plural + '.') } function scanDir(dirpath: string, parts: Array): Array> { let filenames = fs.readdirSync(dirpath); let found: Array> = []; for (let filename of filenames) { let fullPath = path.join(dirpath, filename); if (fs.statSync(fullPath).isDirectory() && filename[0] != '.') { found = found.concat(scanDir(fullPath, parts.concat([filename]))); } else { if (path.extname(filename) == '.graphql') { found.push(parts.concat(filename)); } } } return found; } function usage() { let version = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8')).version; console.error('elm-graphql ' + version); console.error(); console.error('Usage: elm graphql --init ENDPOINT-URL'); console.error(' '); console.error('Available options:'); console.error(' --schema URL URL of the schema endpoint, if different.'); }