├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── package.json ├── src ├── bin │ └── index.js ├── index.js └── utilities │ ├── formatSdl.js │ ├── index.js │ └── optionalize.js └── test ├── .eslintrc ├── format-graphql └── utilities │ ├── formatSdl.js │ └── optionalize.js └── helpers └── generateSchema.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "plugins": [ 5 | "istanbul" 6 | ] 7 | } 8 | }, 9 | "plugins": [ 10 | "transform-export-default-name", 11 | "@babel/transform-flow-strip-types" 12 | ], 13 | "presets": [ 14 | [ 15 | "@babel/env", 16 | { 17 | "targets": { 18 | "node": "10" 19 | } 20 | } 21 | ] 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gajus/format-graphql/ac60b744cc12198e702d3dee0e031ff2c07964c5/.eslintignore -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "canonical", 4 | "canonical/flowtype" 5 | ], 6 | "root": true 7 | } 8 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.*/test/.* 3 | /dist/.* 4 | 5 | [options] 6 | esproposal.optional_chaining=enable 7 | 8 | [untyped] 9 | /node_modules/graphql/error/GraphQLError.js.flow 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: gajus 2 | patreon: gajus 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | node_modules 4 | *.log 5 | .* 6 | !.github 7 | !.babelrc 8 | !.editorconfig 9 | !.eslintignore 10 | !.eslintrc 11 | !.flowconfig 12 | !.gitignore 13 | !.npmignore 14 | !.README 15 | !.travis.yml 16 | /package-lock.json 17 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | test 3 | coverage 4 | benchmark 5 | .* 6 | *.log 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | script: 5 | - npm run lint 6 | - npm run test 7 | - nyc --silent npm run test 8 | - nyc report --reporter=text-lcov | coveralls 9 | - nyc check-coverage --lines 60 10 | after_success: 11 | - NODE_ENV=production npm run build 12 | - semantic-release 13 | notifications: 14 | email: false 15 | sudo: false 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Gajus Kuizinas (http://gajus.com/) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Gajus Kuizinas (http://gajus.com/) nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL ANUARY BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # format-graphql 2 | 3 | [![GitSpo Mentions](https://gitspo.com/badges/mentions/gajus/format-graphql?style=flat-square)](https://gitspo.com/mentions/gajus/format-graphql) 4 | [![Travis build status](http://img.shields.io/travis/gajus/format-graphql/master.svg?style=flat-square)](https://travis-ci.org/gajus/format-graphql) 5 | [![Coveralls](https://img.shields.io/coveralls/gajus/format-graphql.svg?style=flat-square)](https://coveralls.io/github/gajus/format-graphql) 6 | [![NPM version](http://img.shields.io/npm/v/format-graphql.svg?style=flat-square)](https://www.npmjs.org/package/format-graphql) 7 | [![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) 8 | [![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social&label=Follow)](https://twitter.com/kuizinas) 9 | 10 | Formats GraphQL schema definition language (SDL) document. 11 | 12 | --- 13 | 14 | * [Motivation](#motivation) 15 | * [Behaviour](#behaviour) 16 | * [Example](#example) 17 | * [Usage](#usage) 18 | * [Command Line](#command-line) 19 | * [Node API](#node-api) 20 | * [Hooks](#hooks) 21 | 22 | ## Motivation 23 | 24 | As schema grows in size, it becomes desirable to automate schema organisation. The primary function of `format-graphql` is to sort definitions and fields in an alphabetical order, therefore enabling predictable discovery and grouping of related schema entities. 25 | 26 | ## Behaviour 27 | 28 | Alphabetically sorts definitions, fields and arguments. 29 | 30 | ### Example 31 | 32 | Input: 33 | 34 | ```graphql 35 | type Query { 36 | bananas: [Banana!]! 37 | apples: [Apple!]! 38 | } 39 | 40 | type Apple { 41 | name: String! 42 | id: ID! 43 | } 44 | 45 | type Banana { 46 | name: String! 47 | id: ID! 48 | } 49 | 50 | ``` 51 | 52 | Output: 53 | 54 | ```graphql 55 | type Apple { 56 | id: ID! 57 | name: String! 58 | } 59 | 60 | type Banana { 61 | id: ID! 62 | name: String! 63 | } 64 | 65 | type Query { 66 | apples: [Apple!]! 67 | bananas: [Banana!]! 68 | } 69 | 70 | ``` 71 | 72 | ## Usage 73 | 74 | ### Command Line 75 | 76 | ```bash 77 | $ format-graphql --help 78 | Sort GraphQL schema definition language (SDL) document. 79 | 80 | Positionals: 81 | sdl-path Path to the GraphQL schema definition (SDL) document. [string] 82 | 83 | Options: 84 | --version Show version number [boolean] 85 | --help Show help [boolean] 86 | --sort-arguments Sort on arguments [boolean] [default: true] 87 | --sort-definitions Sort on definitions [boolean] [default: true] 88 | --sort-enums Sort on enums [boolean] [default: true] 89 | --sort-fields Sort on fields [boolean] [default: true] 90 | --write Overrides contents of the SDL document. 91 | [boolean] [default: false] 92 | 93 | $ # Prints formatted schema. 94 | $ format-graphql ./schema.graphql 95 | $ 96 | $ # Overrides target schema. 97 | $ format-graphql --write=true ./schema.graphql 98 | 99 | ``` 100 | 101 | ### Node API 102 | 103 | `formatSdl(schema, options)` 104 | 105 | Returns a formatted GraphQL SDL String. 106 | 107 | #### Parameters 108 | 109 | - `schema`: string 110 | - `options` (optional): object: 111 | 112 | ``` 113 | { 114 | sortDefinitions?: boolean, 115 | sortEnums?: boolean, 116 | sortFields?: boolean, 117 | sortArguments?: boolean, 118 | } 119 | ``` 120 | 121 | #### Example 122 | 123 | ```js 124 | import {formatSdl} from 'format-graphql'; 125 | 126 | formatGraphql('type Foo { bar: String }'); 127 | ``` 128 | 129 | ### Hooks 130 | 131 | I recommend using [husky](https://www.npmjs.com/package/husky) to setup a pre-commit hook that would format the schema, e.g. 132 | 133 | ```json 134 | "husky": { 135 | "hooks": { 136 | "pre-commit": "format-graphql --write true src/schema.graphql" 137 | } 138 | }, 139 | 140 | ``` 141 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "email": "gajus@gajus.com", 4 | "name": "Gajus Kuizinas", 5 | "url": "http://gajus.com" 6 | }, 7 | "ava": { 8 | "babel": { 9 | "compileAsTests": [ 10 | "test/helpers/**/*" 11 | ] 12 | }, 13 | "files": [ 14 | "test/format-graphql/**/*" 15 | ], 16 | "require": [ 17 | "@babel/register" 18 | ] 19 | }, 20 | "bin": { 21 | "format-graphql": "./dist/bin/index.js" 22 | }, 23 | "dependencies": { 24 | "graphql": "^15.1.0", 25 | "yargs": "^15.3.1" 26 | }, 27 | "description": "Formats GraphQL schema definition language (SDL) document.", 28 | "devDependencies": { 29 | "@ava/babel": "^1.0.1", 30 | "@babel/cli": "^7.10.1", 31 | "@babel/core": "^7.10.2", 32 | "@babel/node": "^7.10.1", 33 | "@babel/plugin-transform-flow-strip-types": "^7.10.1", 34 | "@babel/preset-env": "^7.10.2", 35 | "@babel/register": "^7.10.1", 36 | "ava": "^3.9.0", 37 | "babel-plugin-istanbul": "^6.0.0", 38 | "babel-plugin-transform-export-default-name": "^2.0.4", 39 | "coveralls": "^3.1.0", 40 | "eslint": "^7.2.0", 41 | "eslint-config-canonical": "^20.0.6", 42 | "flow-bin": "^0.127.0", 43 | "flow-copy-source": "^2.0.9", 44 | "husky": "^4.2.5", 45 | "nyc": "^15.1.0", 46 | "semantic-release": "^17.0.8" 47 | }, 48 | "engines": { 49 | "node": ">=10.0" 50 | }, 51 | "keywords": [ 52 | "graphql", 53 | "schema", 54 | "sdl", 55 | "sort", 56 | "lint", 57 | "format" 58 | ], 59 | "main": "./dist/index.js", 60 | "name": "format-graphql", 61 | "nyc": { 62 | "include": [ 63 | "src/**/*.js" 64 | ], 65 | "instrument": false, 66 | "reporter": [ 67 | "text-lcov" 68 | ], 69 | "require": [ 70 | "@babel/register" 71 | ], 72 | "sourceMap": false 73 | }, 74 | "repository": { 75 | "type": "git", 76 | "url": "git@github.com:gajus/format-graphql.git" 77 | }, 78 | "scripts": { 79 | "build": "rm -fr ./dist && NODE_ENV=production babel ./src --out-dir ./dist --copy-files --source-maps && flow-copy-source src dist", 80 | "dev": "NODE_ENV=production babel ./src --out-dir ./dist --copy-files --source-maps --watch", 81 | "lint": "eslint ./src ./test && flow", 82 | "test": "NODE_ENV=test ava --serial --verbose" 83 | }, 84 | "version": "1.5.0" 85 | } 86 | -------------------------------------------------------------------------------- /src/bin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // @flow 4 | 5 | import fs from 'fs'; 6 | import path from 'path'; 7 | import yargs from 'yargs'; 8 | import { 9 | formatSdl, 10 | } from '../utilities'; 11 | 12 | const argv = yargs 13 | .env('FG') 14 | .help() 15 | .usage('$0 ', 'Sort GraphQL schema definition language (SDL) document.', (command) => { 16 | command.positional('sdl-path', { 17 | description: 'Path to the GraphQL schema definition (SDL) document.', 18 | type: 'string', 19 | }); 20 | }) 21 | .options({ 22 | 'sort-arguments': { 23 | default: true, 24 | description: 'Sort on arguments', 25 | type: 'boolean', 26 | }, 27 | 'sort-definitions': { 28 | default: true, 29 | description: 'Sort on definitions', 30 | type: 'boolean', 31 | }, 32 | 'sort-enums': { 33 | default: true, 34 | description: 'Sort on enums', 35 | type: 'boolean', 36 | }, 37 | 'sort-fields': { 38 | default: true, 39 | description: 'Sort on fields', 40 | type: 'boolean', 41 | }, 42 | write: { 43 | default: false, 44 | description: 'Overrides contents of the SDL document.', 45 | type: 'boolean', 46 | }, 47 | }) 48 | .parse(); 49 | 50 | const resolvedPath = path.resolve(argv.sdlPath); 51 | 52 | const inputSdl = fs.readFileSync(resolvedPath, 'utf8'); 53 | 54 | const { 55 | write, 56 | sortArguments, 57 | sortDefinitions, 58 | sortEnums, 59 | sortFields, 60 | } = argv; 61 | 62 | const outputSdl = formatSdl(inputSdl, { 63 | sortArguments, 64 | sortDefinitions, 65 | sortEnums, 66 | sortFields, 67 | }); 68 | 69 | if (write) { 70 | fs.writeFileSync(resolvedPath, outputSdl); 71 | 72 | // eslint-disable-next-line no-console 73 | console.log('Target SDL document has been overriden with the formatted SDL.'); 74 | } else { 75 | // eslint-disable-next-line no-console 76 | console.log(outputSdl); 77 | } 78 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export {formatSdl} from './utilities'; 4 | -------------------------------------------------------------------------------- /src/utilities/formatSdl.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { 4 | print, 5 | parse, 6 | } from 'graphql'; 7 | import type {ASTNode} from 'graphql'; 8 | import {getOptions} from './optionalize'; 9 | import type {OptionsType} from './optionalize'; 10 | 11 | const sortSchema = (key, node: ASTNode, options: OptionsType) => { 12 | const value = node[key]; 13 | 14 | const { 15 | sortArguments, 16 | sortDefinitions, 17 | sortEnums, 18 | sortFields, 19 | } = options; 20 | 21 | if ( 22 | sortDefinitions && key === 'definitions' || 23 | sortEnums && node.kind === 'EnumTypeDefinition' && key === 'values' || 24 | sortFields && key === 'fields' || 25 | sortArguments && key === 'arguments' 26 | ) { 27 | return value.slice().sort((a, b) => { 28 | if (a.kind === 'SchemaDefinition') { 29 | return -1; 30 | } 31 | 32 | if (b.kind === 'SchemaDefinition') { 33 | return 1; 34 | } 35 | 36 | return a.name.value.localeCompare(b.name.value); 37 | }); 38 | } 39 | 40 | return value; 41 | }; 42 | 43 | /** 44 | * We only care about rearranging: 45 | * - definitions 46 | * - enums 47 | * - fields 48 | * - arguments 49 | * 50 | * A GraphQL Schema AST looks something like this: 51 | * 52 | * { 53 | * "definitions": [ 54 | * { 55 | * fields: [ 56 | * { 57 | * arguments: [ 58 | * ... 59 | * ] 60 | * } 61 | * ... 62 | * ], 63 | * }, 64 | * { 65 | * values: [ 66 | * ... 67 | * ], 68 | * }, 69 | * ... 70 | * ] 71 | * } 72 | * 73 | * Note that there are no cycles - we don't need to recurse through the whole 74 | * AST. There's a finite nest depth of 3 node types for us to walk down: 75 | * 76 | * -> definitions -> fields -> arguments 77 | * 78 | * or, for enums: 79 | * 80 | * -> definitions -> values 81 | */ 82 | const walkAST = (node: ASTNode, options: OptionsType, key: ?string) => { 83 | // Map a node type to the child node type we should walk down next 84 | const nextKey = { 85 | arguments: null, 86 | definitions: 'fields', 87 | EnumTypeDefinition: 'values', 88 | fields: 'arguments', 89 | }; 90 | 91 | if (!key) { 92 | return node; 93 | } 94 | 95 | if (!Array.isArray(node[key])) { 96 | return node; 97 | } 98 | 99 | node[key] = sortSchema(key, node, options).map((child) => { 100 | return walkAST(child, options, nextKey[child.kind] || nextKey[key]); 101 | }); 102 | 103 | return node; 104 | }; 105 | 106 | export default (schemaSdl: string, options?: $Shape): string => { 107 | return print(walkAST(parse(schemaSdl), getOptions(options), 'definitions')); 108 | }; 109 | -------------------------------------------------------------------------------- /src/utilities/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export {default as formatSdl} from './formatSdl'; 4 | -------------------------------------------------------------------------------- /src/utilities/optionalize.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | type SortOptionsType = {| 4 | sortDefinitions: boolean, 5 | sortEnums: boolean, 6 | sortFields: boolean, 7 | sortArguments: boolean, 8 | |}; 9 | 10 | type OptionsType = SortOptionsType; 11 | 12 | // eslint-disable-next-line flowtype/no-weak-types 13 | const isBoolean = (value: any): %checks => { 14 | return typeof value === 'boolean'; 15 | }; 16 | 17 | const toBoolean = (value: T, defaultValue?: boolean): boolean => { 18 | if (isBoolean(value)) { 19 | return value; 20 | } 21 | 22 | return isBoolean(defaultValue) ? defaultValue : Boolean(value); 23 | }; 24 | 25 | const getSortOptions = (options?: $Shape): SortOptionsType => { 26 | const { 27 | sortArguments, 28 | sortDefinitions, 29 | sortEnums, 30 | sortFields, 31 | } = options || {}; 32 | 33 | return { 34 | sortArguments: toBoolean(sortArguments, true), 35 | sortDefinitions: toBoolean(sortDefinitions, true), 36 | sortEnums: toBoolean(sortEnums, true), 37 | sortFields: toBoolean(sortFields, true), 38 | }; 39 | }; 40 | 41 | const getOptions = (options?: $Shape): OptionsType => { 42 | const sortOptions = getSortOptions(options); 43 | 44 | return sortOptions; 45 | }; 46 | 47 | export { 48 | isBoolean, 49 | toBoolean, 50 | getOptions, 51 | }; 52 | export type {OptionsType}; 53 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "canonical/ava", 3 | "rules": { 4 | "filenames/match-regex": 0, 5 | "id-length": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/format-graphql/utilities/formatSdl.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test from 'ava'; 4 | import formatSdl from '../../../src/utilities/formatSdl'; 5 | import generateSchema from '../../helpers/generateSchema'; 6 | 7 | test('sorts definitions', (t) => { 8 | const input = ` 9 | type Foo { 10 | id: ID! 11 | } 12 | 13 | type Bar { 14 | id: ID! 15 | } 16 | `; 17 | 18 | const expectedOutput = `type Bar { 19 | id: ID! 20 | } 21 | 22 | type Foo { 23 | id: ID! 24 | } 25 | `; 26 | 27 | t.is(formatSdl(input), expectedOutput); 28 | }); 29 | 30 | test('puts schema at the top)', (t) => { 31 | const input = ` 32 | type Foo { 33 | id: ID! 34 | } 35 | 36 | schema { 37 | query: Foo 38 | } 39 | `; 40 | 41 | const expectedOutput = `schema { 42 | query: Foo 43 | } 44 | 45 | type Foo { 46 | id: ID! 47 | } 48 | `; 49 | 50 | t.is(formatSdl(input), expectedOutput); 51 | }); 52 | 53 | test('sorts fields', (t) => { 54 | const input = ` 55 | type Foo { 56 | foo: ID! 57 | bar: ID! 58 | } 59 | `; 60 | 61 | const expectedOutput = `type Foo { 62 | bar: ID! 63 | foo: ID! 64 | } 65 | `; 66 | 67 | t.is(formatSdl(input), expectedOutput); 68 | }); 69 | 70 | test('sorts enum values', (t) => { 71 | const input = ` 72 | enum Foo { 73 | FOO 74 | BAR 75 | } 76 | `; 77 | 78 | const expectedOutput = `enum Foo { 79 | BAR 80 | FOO 81 | } 82 | `; 83 | 84 | t.is(formatSdl(input), expectedOutput); 85 | }); 86 | 87 | test('does not sort parameters', (t) => { 88 | const input = ` 89 | type Foo { 90 | foo(bar: ID, foo: ID): ID! 91 | } 92 | `; 93 | 94 | const expectedOutput = `type Foo { 95 | foo(bar: ID, foo: ID): ID! 96 | } 97 | `; 98 | 99 | t.is(formatSdl(input), expectedOutput); 100 | }); 101 | 102 | test('does not strip description', (t) => { 103 | const input = ` 104 | type Foo { 105 | """foo""" 106 | foo: ID! 107 | } 108 | `; 109 | 110 | const expectedOutput = `type Foo { 111 | """foo""" 112 | foo: ID! 113 | } 114 | `; 115 | 116 | t.is(formatSdl(input), expectedOutput); 117 | }); 118 | 119 | // @see https://github.com/graphql/graphql-js/issues/2241#issuecomment-546711570 120 | // eslint-disable-next-line ava/no-skip-test 121 | test.skip('does not strip comments', (t) => { 122 | const input = ` 123 | type Foo { 124 | # foo 125 | foo: ID! 126 | } 127 | `; 128 | 129 | const expectedOutput = `type Foo { 130 | # foo 131 | foo: ID! 132 | } 133 | `; 134 | 135 | t.is(formatSdl(input), expectedOutput); 136 | }); 137 | 138 | test('does not sort definitions when sort argument is false', (t) => { 139 | const input = ` 140 | type Foo { 141 | foo: ID 142 | } 143 | 144 | type Bar { 145 | bar: ID 146 | } 147 | `; 148 | 149 | const expectedOutput = `type Foo { 150 | foo: ID 151 | } 152 | 153 | type Bar { 154 | bar: ID 155 | } 156 | `; 157 | 158 | t.is(formatSdl(input, {sortDefinitions: false}), expectedOutput); 159 | }); 160 | 161 | test('does not sort fields when sort argument is false', (t) => { 162 | const input = ` 163 | type Foo { 164 | apple: ID 165 | cat: ID 166 | banana: ID 167 | } 168 | `; 169 | 170 | const expectedOutput = `type Foo { 171 | apple: ID 172 | cat: ID 173 | banana: ID 174 | } 175 | `; 176 | 177 | t.is(formatSdl(input, {sortFields: false}), expectedOutput); 178 | }); 179 | 180 | test('does not sort arguments when sort argument is false', (t) => { 181 | const input = ` 182 | type Foo { 183 | bar(banana: ID, cat: ID, apple: ID): ID! 184 | } 185 | `; 186 | 187 | const expectedOutput = `type Foo { 188 | bar(banana: ID, cat: ID, apple: ID): ID! 189 | } 190 | `; 191 | 192 | t.is(formatSdl(input, {sortArguments: false}), expectedOutput); 193 | }); 194 | 195 | test('sort whether sort options is true or non exist', (t) => { 196 | const input = ` 197 | type Foo { 198 | apple: ID 199 | cat: ID 200 | banana: ID 201 | } 202 | 203 | type Bar { 204 | bar(banana: ID, cat: ID, apple: ID): ID! 205 | } 206 | `; 207 | 208 | const expectedOutput = `type Bar { 209 | bar(apple: ID, banana: ID, cat: ID): ID! 210 | } 211 | 212 | type Foo { 213 | apple: ID 214 | banana: ID 215 | cat: ID 216 | } 217 | `; 218 | 219 | t.is(formatSdl(input), expectedOutput); 220 | t.is(formatSdl(input, { 221 | sortArguments: true, 222 | sortDefinitions: true, 223 | sortFields: true, 224 | }), expectedOutput); 225 | }); 226 | 227 | // Regression test for https://github.com/gajus/format-graphql/issues/10 228 | test('does not fail for large schemas', (t) => { 229 | const input = generateSchema(1000, 1); 230 | 231 | // sanity check to make sure we don't blow up 232 | t.notThrows(() => { 233 | return formatSdl(input); 234 | }); 235 | }); 236 | 237 | -------------------------------------------------------------------------------- /test/format-graphql/utilities/optionalize.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import test from 'ava'; 4 | import {getOptions, toBoolean, isBoolean} from '../../../src/utilities/optionalize'; 5 | 6 | test('get sort options when no args', (t) => { 7 | const input = undefined; 8 | const expected = { 9 | sortArguments: true, 10 | sortDefinitions: true, 11 | sortEnums: true, 12 | sortFields: true, 13 | }; 14 | const actual = getOptions(input); 15 | 16 | t.deepEqual(actual, expected); 17 | }); 18 | 19 | test('get sort options when empty object', (t) => { 20 | const input = {}; 21 | const expected = { 22 | sortArguments: true, 23 | sortDefinitions: true, 24 | sortEnums: true, 25 | sortFields: true, 26 | }; 27 | const actual = getOptions(input); 28 | 29 | t.deepEqual(actual, expected); 30 | }); 31 | 32 | test('get sort options when proper value type addressed', (t) => { 33 | const input = { 34 | sortDefinitions: false, 35 | }; 36 | const expected = { 37 | sortArguments: true, 38 | sortDefinitions: false, 39 | sortEnums: true, 40 | sortFields: true, 41 | }; 42 | const actual = getOptions(input); 43 | 44 | t.deepEqual(actual, expected); 45 | }); 46 | 47 | test('to boolean with proper value addressed', (t) => { 48 | const inputs = [true, false]; 49 | 50 | inputs.forEach((input) => { 51 | const actual = isBoolean(toBoolean(input)); 52 | 53 | t.true(actual); 54 | }); 55 | }); 56 | 57 | test('to boolean with in-proper value addressed', (t) => { 58 | const inputs = [null, undefined, 0, '', Number.NaN]; 59 | 60 | inputs.forEach((input) => { 61 | const actual = toBoolean(input); 62 | 63 | t.false(actual); 64 | }); 65 | }); 66 | 67 | test('to boolean with in-proper value addressed and default true', (t) => { 68 | const inputs = [null, undefined, 0, '', Number.NaN]; 69 | 70 | inputs.forEach((input) => { 71 | const actual = toBoolean(input, true); 72 | 73 | t.true(actual); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/helpers/generateSchema.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Helper to generate a huge schema for testing purposes 3 | */ 4 | 5 | const getType = (typeName, numberFields) => { 6 | const fields = []; 7 | 8 | new Array(numberFields) 9 | .fill('') 10 | .forEach((_, idx) => { 11 | const fieldName = `dummyField${idx + 1}`; 12 | 13 | fields.push( 14 | [ 15 | ' """', 16 | ` Description for field: ${fieldName}`, 17 | ' """', 18 | ` ${fieldName}: String`, 19 | ].join('\n'), 20 | ); 21 | }); 22 | 23 | return [ 24 | '"""', 25 | `Description for type: ${typeName}`, 26 | '"""', 27 | `type ${typeName} {`, 28 | fields.join('\n\n'), 29 | '}', 30 | ].join('\n'); 31 | }; 32 | 33 | const generateSchema = ( 34 | // How many types should we generate? 35 | numberOfTypes, 36 | 37 | // How many fields per type should we generate? 38 | fieldsPerType, 39 | ) => { 40 | const types = []; 41 | 42 | new Array(numberOfTypes) 43 | .fill('') 44 | .forEach((_, idx) => { 45 | types.push(getType(`DummyType${idx + 1}`, fieldsPerType)); 46 | }); 47 | 48 | return types.join('\n\n'); 49 | }; 50 | 51 | export default generateSchema; 52 | --------------------------------------------------------------------------------