diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c76cc4c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Adam Stankiewicz (sheerun.net) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README.md diff --git a/gqldoc.js b/gqldoc.js new file mode 100755 index 0000000..f527d45 --- /dev/null +++ b/gqldoc.js @@ -0,0 +1,218 @@ +#!/usr/bin/env node +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Yoshimune Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * */ + +/** + * @param {node} a + * @param {node} b + */ +function order(a, b){ + if (a.name && a.name.value && + b.name && b.name.value ){ + return a.name.value > b.name.value; + }else{ + return a.kind > b.kind; + } +} + + +/** + * @param {description} node + */ +function showFieldDef(node){ + let retval = ""; + let type; + let name; + let desc = "unset"; + + if (node.description && + node.description.kind && + (node.description.kind == 'StringValue')){ + desc = node.description.value; + } + + if (node.name && node.name.value){ + name = node.name.value; + } + + if (node.type && node.type.name && node.type.name.value){ + type = node.type.name.value; + } + + if (type && name){ + retval += " " + name + "" + type + "" + desc + "\n"; + }else{ + retval += "Gyaaaaaa!" + name + " type:" + type + "\n"; + } + + return retval; +} + + +/** + * @param {ObjectTypeDefinition} node + */ +function showTypeDef(node){ + let retval = ""; + if (!node || !node.kind || (node.kind!='ObjectTypeDefinition')){ + return retval; + } + + if (node.name && node.name.value){ + retval += "

" + node.name.value + "

\n"; + } + + if (node.description && node.description.value){ + retval += "

" + node.description.value + "

\n"; + } + + retval += " \n"; + + if (node.fields){ + retval += " \n"; + retval += " \n"; + + retval += " \n"; + retval += " \n"; + for (let x of node.fields){ + if (x.kind && (x.kind = 'FieldDefinition')){ + retval += " \n"; + retval += showFieldDef(x) + "\n"; + retval += " \n"; + } + } + retval += " \n"; + retval += "
Nametypedescription
\n"; + } + + return retval; +} + + +/** + * @param {FragmentDefinition} node + */ +function showFragDef(node){ + let retval = ""; + + if (!node.selectionSet || !node.selectionSet.selections || + !node.name || !node.name.value){ + return ""; + } + + retval += "

" + node.name.value + "

\n"; + retval += " \n"; + + return retval; +} + + +/** + * @param {OperationDefinition} node + */ +function showOpDef(node){ + let = ""; + + if (!node || !node.kind || (node.kind!='OperationDefinition')){ + return ""; + } + + if (node.name && node.name.value){ + retval += "

" + node.name.value + "

\n"; + } + + // TODO + retval += "TODO\n" + retval += node; + + return retval; +} + +/** + * @param {Document} node + */ +function showDocument(node){ + let retval = ''; + let ary; + if (node && node.kind && node.definitions && (node.kind = 'Document')){ + // Type + retval += "

Type

\n"; + ary = node.definitions.filter(v => v.kind && v.kind=='ObjectTypeDefinition'); + if (ary.length){ + for (let v of ary.sort(order)){ + retval += showTypeDef(v); + } + }else{ + retval += "

未定義

\n"; + } + retval += "\n"; + + // Operation + retval += "

Operation

\n"; + ary = node.definitions.filter(v => v.kind && v.kind=='OperationDefinition'); + if (ary.length){ + for (let v of ary.sort(order)){ + // TODO + retval += showFragDef(v); + } + }else{ + retval += "

未定義

\n"; + } + retval += "\n"; + + // Fragment + retval += "

Fragment

\n"; + ary = node.definitions.filter(v => v.kind && v.kind=='FragmentDefinition'); + if (ary.length){ + for (let v of ary.sort(order)){ + retval += showFragDef(v); + } + }else{ + retval += "

未定義

\n"; + } + retval += "\n"; + + retval += "

Others

\n"; + for (let x of node.definitions.filter(v => ["ObjectTypeDefinition", + "FragmentDefinition", + "OperationDefinition"].indexOf(v.kind)==-1) + .sort((a,b) => (a.kind > b.kind))){ + retval += x + "\n"; + retval += "
\n"; + } + } + + return retval; +} + +if (typeof exports != 'undefined'){ + exports.showDocument = showDocument; +} + diff --git a/main.js b/main.js new file mode 100755 index 0000000..0bd0606 --- /dev/null +++ b/main.js @@ -0,0 +1,91 @@ +#!/usr/bin/env node + + +let fetch = require('isomorphic-fetch'); +let path = require('path'); +let meow = require('meow'); +let fs = require('fs'); +let GraphQL = require('graphql'); +let gqldoc = require('./gqldoc'); +let ejs = require('ejs'); +let strip = require('strip-bom'); + +let graphql = GraphQL.graphql; +let parse = GraphQL.parse; + +/* */ +function terminate () { + console.error(cli.help); + process.exit(1); +} + +/* */ +let cli = meow(` + Options: + -g --graphql use graphql schema language as input + -v --verbose print introspection result + -a --auth set Authorization header for graphql server + -t --title HTML title + + Usage: + $ gqldoc [param] + + Examples: + $ gqldoc path/to/schema.gql -g +`, { + flags: { + verbose: { + type: 'boolean', + alias: 'v' + }, + graphql: { + type: 'boolean', + alias: 'g' + }, + auth: { + type: 'string', + alias: 'a' + }, + title: { + type: 'string', + alias: 't' + } + } +}); + + +if (cli.flags.graphql && cli.input[0]){ + fs.readFile("./temp/index.ejs", "utf-8", function(err, temp){ + fs.readFile(cli.input[0], "utf-8", function(err, x){ + let str = gqldoc.showDocument(parse(x)); + console.log(ejs.render(temp, { + title:"Schema:" + path.basename(cli.input[0]), + main: str + })); + }) + }); +}else if (cli.input[0] && cli.input[0].slice(0, 4) === 'http'){ + let headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }; + if (cli.flags.auth) { + headers.Authorization = cli.flags.auth; + } + + p = fetch(cli.input[0], { + method: 'POST', + headers: headers, + body: JSON.stringify({"query":fs.readFileSync('./query.gql', 'utf-8')}) + }).then(function (res) { + if (!res.ok && cli.flags.verbose) { + console.log('Request for schema failed w/ ' + res.status + ' (' + res.statusText + ')'); + } + return res.text(); + }).then(function(text){ + // TODO + console.log(text); + }); +}else{ + terminate(); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..32c4cd1 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "gqldoc", + "version": "0.1.0", + "description": "GraphQLのスキーマ情報からドキュメントを作成", + "main": "main.js", + "dependencies": { + "ejs": "^2.6.1", + "fs": "0.0.1-security", + "graphql": "^14.0.2", + "isomorphic-fetch": "^2.2.1", + "meow": "^5.0.0" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/yhornisse/gqldoc.git" + }, + "keywords": [ + "graphql", + "javascript" + ], + "author": "Yoshimune Saito", + "license": "MIT", + "bugs": { + "url": "https://github.com/yhornisse/gqldoc/issues" + }, + "homepage": "https://github.com/yhornisse/gqldoc#readme" +} diff --git a/query.gql b/query.gql new file mode 100644 index 0000000..9669a76 --- /dev/null +++ b/query.gql @@ -0,0 +1,91 @@ +query IntrospectionQuery { + __schema { + queryType { name } + mutationType { name } + subscriptionType { name } + types { + ...FullType + } + directives { + name + description + locations + args { + ...InputValue + } + } + } +} + +fragment FullType on __Type { + kind + name + description + fields(includeDeprecated: true) { + name + description + args { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } +} + +fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue +} + +fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } +} diff --git a/temp/index.ejs b/temp/index.ejs new file mode 100644 index 0000000..808b5e4 --- /dev/null +++ b/temp/index.ejs @@ -0,0 +1,10 @@ + + + + <%= title %> + + +

<%= title %>

+ <%- main %> + +