From e33d6773d68e11154cfa827ebf2079f5d42696b8 Mon Sep 17 00:00:00 2001 From: Jon Wong Date: Mon, 5 Jun 2017 15:34:08 -0400 Subject: [PATCH] Adding support for `graphql` (#1982) * Initial GraphQL implementation This is the bare minimum amount of code to make graphql work. Turns out it's pretty easy to add support for other languages :) * Fixing support for `graphql` parsing There was a few structural changes with the addition of CSS and Typescript that we had to take into account. * Fixing file glob, adding `graphql` to yarn.lock * Removing extraneous file * Adding `graphql` parser, style changes * Splitting out graphql printer * Removing merge conflict * Fixing yarn.lock * Addressing code review, using `createError` * Adding release config * Using exact version * Removing destructuring --- bin/prettier.js | 3 +- package.json | 1 + parser.js | 2 + scripts/build/build.sh | 6 ++ src/parser-graphql.js | 26 ++++++ src/printer-graphql.js | 81 +++++++++++++++++++ src/printer.js | 2 + .../__snapshots__/jsfmt.spec.js.snap | 16 ++++ tests/graphql_hello/hello.graphql | 5 ++ tests/graphql_hello/jsfmt.spec.js | 1 + tests_config/run_spec.js | 7 +- yarn.lock | 10 +++ 12 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/parser-graphql.js create mode 100644 src/printer-graphql.js create mode 100644 tests/graphql_hello/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/graphql_hello/hello.graphql create mode 100644 tests/graphql_hello/jsfmt.spec.js diff --git a/bin/prettier.js b/bin/prettier.js index 78cc29ba1..d84e67da7 100755 --- a/bin/prettier.js +++ b/bin/prettier.js @@ -97,7 +97,8 @@ function getParserOption() { value === "flow" || value === "babylon" || value === "typescript" || - value === "postcss" + value === "postcss" || + value === "graphql" ) { return value; } diff --git a/package.json b/package.json index 7258d519d..3945374ee 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "flow-parser": "0.47.0", "get-stdin": "5.0.1", "glob": "7.1.2", + "graphql": "0.10.1", "jest": "20.0.0", "jest-validate": "20.0.3", "minimist": "1.2.0", diff --git a/parser.js b/parser.js index 2ffddf9b9..50a66dd92 100644 --- a/parser.js +++ b/parser.js @@ -5,6 +5,8 @@ function parse(text, opts) { if (opts.parser === "flow") { parseFunction = eval("require")("./src/parser-flow"); + } else if (opts.parser === "graphql") { + parseFunction = eval("require")("./src/parser-graphql"); } else if (opts.parser === "typescript") { parseFunction = eval("require")("./src/parser-typescript"); } else if (opts.parser === "postcss") { diff --git a/scripts/build/build.sh b/scripts/build/build.sh index 2b3ca012c..ddd9635d1 100755 --- a/scripts/build/build.sh +++ b/scripts/build/build.sh @@ -23,6 +23,9 @@ node_modules/.bin/rollup -c scripts/build/rollup.parser.config.js --environment echo 'Bundling lib flow...'; node_modules/.bin/rollup -c scripts/build/rollup.parser.config.js --environment parser:flow +echo 'Bundling lib graphql...'; +node_modules/.bin/rollup -c scripts/build/rollup.parser.config.js --environment parser:graphql + echo 'Bundling lib typescript...'; node_modules/.bin/rollup -c scripts/build/rollup.parser.config.js --environment parser:typescript @@ -45,6 +48,9 @@ node_modules/.bin/rollup -c scripts/build/rollup.docs.config.js --environment fi echo 'Bundling docs flow...'; node_modules/.bin/rollup -c scripts/build/rollup.docs.config.js --environment filepath:src/parser-flow.js +echo 'Bundling docs graphql...'; +node_modules/.bin/rollup -c scripts/build/rollup.docs.config.js --environment filepath:src/parser-graphql.js + echo 'Bundling docs typescript...'; node_modules/.bin/rollup -c scripts/build/rollup.docs.config.js --environment filepath:src/parser-typescript.js diff --git a/src/parser-graphql.js b/src/parser-graphql.js new file mode 100644 index 000000000..22ce241a5 --- /dev/null +++ b/src/parser-graphql.js @@ -0,0 +1,26 @@ +"use strict"; + +const createError = require("./parser-create-error"); + +function parse(text) { + // Inline the require to avoid loading all the JS if we don't use it + const parser = require("graphql/language"); + try { + const ast = parser.parse(text); + return ast; + } catch (error) { + const GraphQLError = require("graphql/error").GraphQLError; + if (error instanceof GraphQLError) { + throw createError(error.message, { + start: { + line: error.locations[0].line, + column: error.locations[0].column + } + }); + } else { + throw error; + } + } +} + +module.exports = parse; diff --git a/src/printer-graphql.js b/src/printer-graphql.js new file mode 100644 index 000000000..acbcb776a --- /dev/null +++ b/src/printer-graphql.js @@ -0,0 +1,81 @@ +"use strict"; + +const docBuilders = require("./doc-builders"); +const concat = docBuilders.concat; +const join = docBuilders.join; +const hardline = docBuilders.hardline; +const softline = docBuilders.softline; +const group = docBuilders.group; +const indent = docBuilders.indent; + +function genericPrint(path, options, print) { + const n = path.getValue(); + if (!n) { + return ""; + } + + if (typeof n === "string") { + return n; + } + + switch (n.kind) { + case "Document": { + return concat([join(hardline, path.map(print, "definitions")), hardline]); + } + case "OperationDefinition": { + return path.call(print, "selectionSet"); + } + case "SelectionSet": { + return concat([ + "{", + indent(concat([hardline, concat(path.map(print, "selections"))])), + hardline, + "}" + ]); + } + case "Field": { + return group( + concat([ + path.call(print, "name"), + n.arguments.length > 0 + ? group( + concat([ + "(", + indent( + concat([ + softline, + join( + concat([",", softline]), + path.map(print, "arguments") + ) + ]) + ), + softline, + ") " + ]) + ) + : "", + path.call(print, "selectionSet") + ]) + ); + } + case "Name": { + return n.value; + } + case "StringValue": { + return concat(['"', n.value, '"']); + } + case "Argument": { + return concat([ + path.call(print, "name"), + ": ", + path.call(print, "value") + ]); + } + + default: + throw new Error("unknown graphql type: " + JSON.stringify(n.kind)); + } +} + +module.exports = genericPrint; diff --git a/src/printer.js b/src/printer.js index c0dd970c8..99d5267f3 100644 --- a/src/printer.js +++ b/src/printer.js @@ -50,6 +50,8 @@ function shouldPrintComma(options, level) { function getPrintFunction(options) { switch (options.parser) { + case "graphql": + return require("./printer-graphql"); case "postcss": return require("./printer-postcss"); default: diff --git a/tests/graphql_hello/__snapshots__/jsfmt.spec.js.snap b/tests/graphql_hello/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..d067fb56d --- /dev/null +++ b/tests/graphql_hello/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`hello.graphql 1`] = ` +{ + project(name: "GraphQL") { + tagline + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{ + project(name: "GraphQL") { + tagline + } +} + +`; diff --git a/tests/graphql_hello/hello.graphql b/tests/graphql_hello/hello.graphql new file mode 100644 index 000000000..cec7c68e7 --- /dev/null +++ b/tests/graphql_hello/hello.graphql @@ -0,0 +1,5 @@ +{ + project(name: "GraphQL") { + tagline + } +} diff --git a/tests/graphql_hello/jsfmt.spec.js b/tests/graphql_hello/jsfmt.spec.js new file mode 100644 index 000000000..335646fb2 --- /dev/null +++ b/tests/graphql_hello/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "graphql" }); diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js index 5c1626878..ab3f68958 100644 --- a/tests_config/run_spec.js +++ b/tests_config/run_spec.js @@ -10,12 +10,15 @@ const AST_COMPARE = process.env["AST_COMPARE"]; const VERIFY_ALL_PARSERS = process.env["VERIFY_ALL_PARSERS"] || false; const ALL_PARSERS = process.env["ALL_PARSERS"] ? JSON.parse(process.env["ALL_PARSERS"]) - : ["flow", "babylon", "typescript"]; + : ["flow", "graphql", "babylon", "typescript"]; function run_spec(dirname, options, additionalParsers) { fs.readdirSync(dirname).forEach(filename => { const extension = extname(filename); - if (/^\.([jt]sx?|css)$/.test(extension) && filename !== "jsfmt.spec.js") { + if ( + /^\.([jt]sx?|css|graphql)$/.test(extension) && + filename !== "jsfmt.spec.js" + ) { const path = dirname + "/" + filename; let rangeStart = 0; let rangeEnd = Infinity; diff --git a/yarn.lock b/yarn.lock index 141d3348d..8a803d8e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1323,6 +1323,12 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" +graphql@0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.10.1.tgz#75c93c2ce73aeb5bae2eefb555a8e9e39c36027d" + dependencies: + iterall "^1.1.0" + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -1703,6 +1709,10 @@ istanbul-reports@^1.1.0: dependencies: handlebars "^4.0.3" +iterall@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.1.tgz#f7f0af11e9a04ec6426260f5019d9fcca4d50214" + jest-changed-files@^20.0.3: version "20.0.3" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-20.0.3.tgz#9394d5cc65c438406149bef1bf4d52b68e03e3f8"