diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 013e9d0..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,12 +0,0 @@ - - \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 5034327..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,12 +0,0 @@ - - \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 19d54c7..0000000 --- a/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -# Logs -logs -*.log - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git -node_modules - -# don't commit compiled files -lib -test-lib -dist -npm - -# webstorm -.idea/ - -# alm editor -.alm - -# output from the build tools in the root -./extracted_queries.json \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 08ad28d..0000000 --- a/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!lib/** -!bin/** diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7fb702a..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 - 2016 Meteor Development Group, Inc. - -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 index 43550ff..de4e37c 100644 --- a/README.md +++ b/README.md @@ -1,111 +1,7 @@ # PersistGraphQL -`persistgraphql` is a simple build tool that enables query whitelisting and persisted queries for GraphQL projects that use statically analyze-able GraphQL queries. +`persistgraphql` has been deprecated in favor of [`apollo-link-persisted-queries`](https://github.com/apollographql/apollo-link-persisted-queries) and [`apollo-cli`](https://github.com/apollographql/apollo-cli). [Persisted queries](https://www.apollographql.com/docs/guides/performance.html#automatic-persisted-queries) can automatically used with Apollo Client and Server. To extract queries, use the cli and a command such as: -It scans a code directory and extracts GraphQL query documents from `.graphql` files. It then assigns these queries ID values/hashes and produces a JSON file which maps from queries to hashes/IDs. This map can then be used by the client and server to perform query whitelisting, query lookups (i.e. client only sends the hash/id, the server just looks up the corresponding query), etc. - -The npm package also provides a network interface for [Apollo Client](https://github.com/apollostack/apollo-client) that manages the query lookups in `persistgraphql/lib/browser`. To see how to extract the queries on the server, see the [code snippets](#server-side) below. - -# Installation -For only the CLI tool: - -```shell -npm install -g persistgraphql -``` - -As a dependency (for Apollo Client network interface): - -```shell -npm install --save persistgraphql ``` - -# Build Tool Semantics - -The build tool binary is called `persistgraphql`. Running it with no other arguments should give: - -``` -Usage: persistgraphql input_file [output file] [--add_typename] -``` - -It can be called on a file containing GraphQL query definitions with extension `.graphql`: - -```shell -persistgraphql queries.graphql -``` - -It can also be called on a directory, which it will step through recursively: - -```shell -persistgraphql src/ -``` - -By default, the output will be placed in `extracted_queries.json`. An output file can be specified as the second argument: - -``` -persistgraphql index.ts output.json -``` - -## Adding Typenames to Extracted Queries - -It can also take the `--add_typename` flag which will apply a query transformation to the query documents, adding the `__typename` field at every level of the query. You must pass this option if your client code uses this query transformation. - -``` -persistgraphql src/ --add_typename -``` - -## Extracting Queries from TypeScript - -To extract GraphQL queries from TypeScript files, use `--js --extension=ts`. - -``` -persistgraphql src/index.js --js --extension=ts -``` - -## Extracting Queries from JavaScript - -It is also possible to extract GraphQL queries from JavaScript files using `--extension=js --js`. - -``` -persistgraphql src/index.js --js --extension=js -``` - -# Apollo Client Network Interface - -This package provides an implementation of an Apollo Client network interface that provides persisted query support. It serves as a drop-in replacement for the standard network interface and uses the query map given by `persistgraphql` in order to send only query hashes/ids to the serverather than the query document. - -This package also provides a way for you to alter any generic NetworkInterface to use persisted queries from a provided query map with the `addPersistedQueries(networkInterface: NetworkInterface, queryMap: OutputMap)` function. -This overrides the `query` member function of your network interface instance to replace your query with an id based on the query map provided. - -See the implementation as well as some documentation for it within `src/network_interface/ApolloNetworkInterface.ts`. - -# Server-side - -If you use the client network interface provided by this package, you can quickly roll your own middleware to get the GraphQL query instead of the query ID that the network interface sends. Here's an example with Express using the `lodash` `invert` method: - -```js -import queryMap from ‘../extracted_queries.json’; -import { invert } from 'lodash'; -app.use( - '/graphql', - (req, resp, next) => { - if (config.persistedQueries) { - const invertedMap = invert(queryMap); - req.body.query = invertedMap[req.body.id]; - } - next(); - }, -); -``` -Here's an example with a Hapi server extension using the `lodash` `invert` method: - -```js -import queryMap from ‘../extracted_queries.json’; -import { invert } from 'lodash'; -server.ext('onPreHandler', (req: Request, reply) => { - if (config.persistedQueries && req.url.path.indexOf('/graphql') >= 0 && req.payload.id) { - const invertedMap = invert(queryMap); - req.payload.query = invertedMap[req.payload.id] - } - return reply.continue(); -}); +apollo codegen:generate --schema=schema.json --queries="test.graphql" queries.json ``` diff --git a/bin/persistgraphql b/bin/persistgraphql deleted file mode 100755 index cafd757..0000000 --- a/bin/persistgraphql +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -require ('../lib/src/binary.js'); diff --git a/fetch-mock.typings.d.ts b/fetch-mock.typings.d.ts deleted file mode 100644 index 867d8f6..0000000 --- a/fetch-mock.typings.d.ts +++ /dev/null @@ -1,292 +0,0 @@ - -/* FETCH-MOCK */ -// This is a slightly modified version of https://www.npmjs.com/package/@types/fetch-mock -// We can't use the package directly because it depends on @types/whatwg-fetch -// which conflicts with our import @types/isomorphic fetch - -// Changes -// 1. Reference to isomorphic-fetch -// 2. Response -> IResponse -// 3. wrapping as a module - -// Type definitions for fetch-mock 5.0.0 -// Project: https://github.com/wheresrhys/fetch-mock -// Definitions by: Alexey Svetliakov , Tamir Duberstein -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -/// - -declare namespace FetchMock { - type MockRequest = Request | RequestInit; - - /** - * Mock matcher function - * @param url - * @param opts - */ - type MockMatcherFunction = (url: string, opts: MockRequest) => boolean - /** - * Mock matcher. Can be one of following: - * string: Either - * an exact url to match e.g. 'http://www.site.com/page.html' - * if the string begins with a `^`, the string following the `^` must - begin the url e.g. '^http://www.site.com' would match - 'http://www.site.com' or 'http://www.site.com/page.html' - * '*' to match any url - * RegExp: A regular expression to test the url against - * Function(url, opts): A function (returning a Boolean) that is passed the - url and opts fetch() is called with (or, if fetch() was called with one, - the Request instance) - */ - type MockMatcher = string | RegExp | MockMatcherFunction; - - /** - * Mock response object - */ - interface MockResponseObject { - /** - * Set the response body - */ - body?: string | {}; - /** - * Set the response status - * @default 200 - */ - status?: number; - /** - * Set the response headers. - */ - headers?: { [key: string]: string }; - /** - * If this property is present then a Promise rejected with the value - of throws is returned - */ - throws?: boolean; - /** - * This property determines whether or not the request body should be - JSON.stringified before being sent - * @default true - */ - sendAsJson?: boolean; - } - /** - * Response: A Response instance - will be used unaltered - * number: Creates a response with this status - * string: Creates a 200 response with the string as the response body - * object: As long as the object is not a MockResponseObject it is - converted into a json string and returned as the body of a 200 response - * If MockResponseObject was given then it's used to configure response - * Function(url, opts): A function that is passed the url and opts fetch() - is called with and that returns any of the responses listed above - */ - type MockResponse = Response | Promise - | number | Promise - | string | Promise - | Object | Promise - | MockResponseObject | Promise; - /** - * Mock response function - * @param url - * @param opts - */ - type MockResponseFunction = (url: string, opts: MockRequest) => MockResponse; - - /** - * Mock options object - */ - interface MockOptions { - /** - * A unique string naming the route. Used to subsequently retrieve - references to the calls, grouped by name. - * @default matcher.toString() - * - * Note: If a non-unique name is provided no error will be thrown - (because names are optional, auto-generated ones may legitimately - clash) - */ - name?: string; - /** - * http method to match - */ - method?: string; - /** - * as specified above - */ - matcher?: MockMatcher; - /** - * as specified above - */ - response?: MockResponse | MockResponseFunction; - } - - type MockCall = [string, MockRequest]; - - interface MatchedRoutes { - matched: Array; - unmatched: Array; - } - - interface MockOptionsMethodGet extends MockOptions { - method: 'GET' - } - - interface MockOptionsMethodPost extends MockOptions { - method: 'POST' - } - - interface MockOptionsMethodPut extends MockOptions { - method: 'PUT' - } - - interface MockOptionsMethodDelete extends MockOptions { - method: 'DELETE' - } - - interface MockOptionsMethodHead extends MockOptions { - method: 'HEAD' - } - - export interface FetchMockStatic { - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - */ - mock(matcher: MockMatcher, response: MockResponse | MockResponseFunction): this; - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param options Additional properties defining the route to mock - */ - mock(matcher: MockMatcher, response: MockResponse | MockResponseFunction, options: MockOptions): this; - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Calls to .mock() can be chained. - * @param options The route to mock - */ - mock(options: MockOptions): this; - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Shorthand for mock() restricted to the GET - method. Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - get(matcher: MockMatcher, reponse: MockResponse | MockResponseFunction, options?: MockOptionsMethodGet): this; - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Shorthand for mock() restricted to the POST - method. Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - post(matcher: MockMatcher, reponse: MockResponse | MockResponseFunction, options?: MockOptionsMethodPost): this; - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Shorthand for mock() restricted to the PUT - method. Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - put(matcher: MockMatcher, reponse: MockResponse | MockResponseFunction, options?: MockOptionsMethodPut): this; - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Shorthand for mock() restricted to the - DELETE method. Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - delete(matcher: MockMatcher, reponse: MockResponse | MockResponseFunction, options?: MockOptionsMethodDelete): this; - /** - * Replaces fetch() with a stub which records its calls, grouped by - route, and optionally returns a mocked Response object or passes the - call through to fetch(). Shorthand for mock() restricted to the HEAD - method. Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - head(matcher: MockMatcher, reponse: MockResponse | MockResponseFunction, options?: MockOptionsMethodHead): this; - /** - * Chainable method that restores fetch() to its unstubbed state and - clears all data recorded for its calls. - */ - restore(): this; - /** - * Chainable method that clears all data recorded for fetch()'s calls - */ - reset(): this; - /** - * Returns all calls to fetch, grouped by whether fetch-mock matched - them or not. - */ - calls(): MatchedRoutes; - /** - * Returns all calls to fetch matching matcherName. - */ - calls(matcherName?: string): Array; - /** - * Returns a Boolean indicating whether fetch was called and a route - was matched. - */ - called(): boolean; - /** - * Returns a Boolean indicating whether fetch was called and a route - named matcherName was matched. - */ - called(matcherName?: string): boolean; - /** - * Returns the arguments for the last matched call to fetch - */ - lastCall(): MockCall; - /** - * Returns the arguments for the last call to fetch matching - matcherName - */ - lastCall(matcherName?: string): MockCall; - /** - * Returns the url for the last matched call to fetch - */ - lastUrl(): string; - /** - * Returns the url for the last call to fetch matching matcherName - */ - lastUrl(matcherName?: string): string; - /** - * Returns the options for the last matched call to fetch - */ - lastOptions(): MockRequest; - /** - * Returns the options for the last call to fetch matching matcherName - */ - lastOptions(matcherName?: string): MockRequest; - /** - * Set some global config options, which include - * sendAsJson [default `true`] - by default fetchMock will - convert objects to JSON before sending. This is overrideable - for each call but for some scenarios, e.g. when dealing with a - lot of array buffers, it can be useful to default to `false` - */ - configure(opts: Object): void; - } -} - -declare var fetchMock: FetchMock.FetchMockStatic; - -declare module 'fetch-mock' { - export = fetchMock; -} diff --git a/index.ts b/index.ts deleted file mode 100644 index 9b9e526..0000000 --- a/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -// This file is to be imported for client code using the network interface. -import { - PersistedQueryNetworkInterface, - addPersistedQueries, -} from './src/network_interface/ApolloNetworkInterface'; - -import { - OutputMap, - getQueryDocumentKey, -} from './src/common'; - -export { - PersistedQueryNetworkInterface, - addPersistedQueries, - getQueryDocumentKey, - OutputMap, -}; diff --git a/package.json b/package.json deleted file mode 100644 index de3f946..0000000 --- a/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "persistgraphql", - "version": "0.3.11", - "description": "A build tool for GraphQL projects.", - "main": "lib/index.js", - "typings": "lib/index.d.ts", - "typescript": { - "definition": "lib/index.d.ts" - }, - "scripts": { - "pretest": "npm run compile", - "test": "mocha --reporter spec --full-trace lib/test/tests.js", - "posttest": "npm run lint", - "compile": "tsc", - "lint": "tslint -c tslint.json test/**/*.ts; tslint src/**/*.ts" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Poincare/apollo-query-whitelisting.git" - }, - "author": "Dhaivat Pandya", - "license": "MIT", - "bugs": { - "url": "https://github.com/Poincare/persistgraphql/issues" - }, - "homepage": "https://github.com/Poincare/persistgraphql#readme", - "bin": { - "persistgraphql": "./bin/persistgraphql" - }, - "dependencies": { - "apollo-client": "^1.1", - "graphql": ">=0.9.4 <0.11", - "graphql-tag": "^2.0.0", - "lodash": "^4.17.4", - "whatwg-fetch": "^2.0.3", - "yargs": "^7.1.0" - }, - "devDependencies": { - "@types/chai": "^3.5.1", - "@types/express": "^4.0.35", - "@types/lodash": "^4.14.63", - "@types/mocha": "^2.2.41", - "@types/node": "^7.0.14", - "@types/yargs": "^6.6.0", - "chai": "^3.5.0", - "es6-promise": "^4.1.0", - "fetch-mock": "^5.10.0", - "isomorphic-fetch": "^2.2.1", - "mocha": "^3.3.0", - "source-map-support": "^0.4.15", - "tslint": "^5.1.0", - "typescript": "^2.3.1" - }, - "optionalDependencies": { - "@types/graphql": "^0.9.0", - "@types/isomorphic-fetch": "0.0.34" - } -} diff --git a/src/ExtractGQL.ts b/src/ExtractGQL.ts deleted file mode 100644 index cf1f93f..0000000 --- a/src/ExtractGQL.ts +++ /dev/null @@ -1,374 +0,0 @@ -// This file implements the extractgql CLI tool. - -import fs = require('fs'); -import path = require('path'); - -import { - parse, - DocumentNode, - OperationDefinitionNode, - FragmentDefinitionNode, - print, - DefinitionNode, - separateOperations, -} from 'graphql'; - -import { - getOperationDefinitions, - getFragmentNames, - isFragmentDefinition, - isOperationDefinition, -} from './extractFromAST'; - -import { - findTaggedTemplateLiteralsInJS, - eliminateInterpolations, -} from './extractFromJS'; - -import { - getQueryKey, - getQueryDocumentKey, - sortFragmentsByName, - applyQueryTransformers, - TransformedQueryWithId, - OutputMap, - QueryTransformer, -} from './common'; - -import { - addTypenameTransformer, -} from './queryTransformers'; - -import _ = require('lodash'); - -export type ExtractGQLOptions = { - inputFilePath: string, - outputFilePath?: string, - queryTransformers?: QueryTransformer[], - extension?: string, - inJsCode?: boolean, -}; - -export class ExtractGQL { - public inputFilePath: string; - public outputFilePath: string; - - // Starting point for monotonically increasing query ids. - public queryId: number = 0; - - // List of query transformers that a query is put through (left to right) - // before being written as a transformedQuery within the OutputMap. - public queryTransformers: QueryTransformer[] = []; - - // The file extension to load queries from - public extension: string; - - // Whether to look for standalone .graphql files or template literals in JavaScript code - public inJsCode: boolean = false; - - // The template literal tag for GraphQL queries in JS code - public literalTag: string = 'gql'; - - // Given a file path, this returns the extension of the file within the - // file path. - public static getFileExtension(filePath: string): string { - const pieces = path.basename(filePath).split('.'); - if (pieces.length <= 1) { - return ''; - } - return pieces[pieces.length - 1]; - } - - // Reads a file into a string. - public static readFile(filePath: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(filePath, 'utf8', (err, data) => { - if (err) { - reject(err); - } else { - resolve(data); - } - }); - }); - } - - // Checks if a given path points to a directory. - public static isDirectory(path: string): Promise { - return new Promise((resolve, reject) => { - fs.stat(path, (err, stats) => { - if (err) { - reject(err); - } else { - resolve(stats.isDirectory()); - } - }); - }); - } - - constructor({ - inputFilePath, - outputFilePath = 'extracted_queries.json', - queryTransformers = [], - extension = 'graphql', - inJsCode = false, - }: ExtractGQLOptions) { - this.inputFilePath = inputFilePath; - this.outputFilePath = outputFilePath; - this.queryTransformers = queryTransformers; - this.extension = extension; - this.inJsCode = inJsCode; - } - - // Add a query transformer to the end of the list of query transformers. - public addQueryTransformer(queryTransformer: QueryTransformer) { - this.queryTransformers.push(queryTransformer); - } - - // Applies this.queryTransformers to a query document. - public applyQueryTransformers(document: DocumentNode) { - return applyQueryTransformers(document, this.queryTransformers); - } - - // Just calls getQueryKey with this.queryTransformers as its set of - // query transformers and returns a serialization of the query. - public getQueryKey(definition: OperationDefinitionNode): string { - return getQueryKey(definition, this.queryTransformers); - } - - // Just calls getQueryDocumentKey with this.queryTransformers as its - // set of query transformers and returns a serialization of the query. - public getQueryDocumentKey( - document: DocumentNode, - ): string { - return getQueryDocumentKey(document, this.queryTransformers); - } - - // Create an OutputMap from a GraphQL document that may contain - // queries, mutations and fragments. - public createMapFromDocument(document: DocumentNode): OutputMap { - const transformedDocument = this.applyQueryTransformers(document); - const queryDefinitions = getOperationDefinitions(transformedDocument); - const result: OutputMap = {}; - queryDefinitions.forEach((transformedDefinition) => { - const transformedQueryWithFragments = this.getQueryFragments(transformedDocument, transformedDefinition); - transformedQueryWithFragments.definitions.unshift(transformedDefinition); - const docQueryKey = this.getQueryDocumentKey(transformedQueryWithFragments); - result[docQueryKey] = this.getQueryId(); - }); - return result; - } - - // Given the path to a particular `.graphql` file, read it, extract the queries - // and return the promise to an OutputMap. Used primarily for unit tests. - public processGraphQLFile(graphQLFile: string): Promise { - return new Promise((resolve, reject) => { - ExtractGQL.readFile(graphQLFile).then((fileContents) => { - const graphQLDocument = parse(fileContents); - - resolve(this.createMapFromDocument(graphQLDocument)); - }).catch((err) => { - reject(err); - }); - }); - } - - // Creates an OutputMap from an array of GraphQL documents read as strings. - public createOutputMapFromString(docString: string): OutputMap { - const doc = parse(docString); - const docMap = separateOperations(doc); - - const resultMaps = Object.keys(docMap).map((operationName) => { - const document = docMap[operationName]; - return this.createMapFromDocument(document); - }); - - return (_.merge({} as OutputMap, ...resultMaps) as OutputMap); - } - - public readGraphQLFile(graphQLFile: string): Promise { - return ExtractGQL.readFile(graphQLFile); - } - - public readInputFile(inputFile: string): Promise { - return Promise.resolve().then(() => { - const extension = ExtractGQL.getFileExtension(inputFile); - - if (extension === this.extension) { - if (this.inJsCode) { - // Read from a JS file - return ExtractGQL.readFile(inputFile).then((result) => { - const literalContents = findTaggedTemplateLiteralsInJS(result, this.literalTag); - const noInterps = literalContents.map(eliminateInterpolations); - const joined = noInterps.join('\n'); - return joined; - }); - } else { - return this.readGraphQLFile(inputFile); - } - } else { - return ''; - } - }); - } - - // Processes an input path, which may be a path to a GraphQL file - // or a directory containing GraphQL files. Creates an OutputMap - // instance from these files. - public processInputPath(inputPath: string): Promise { - return new Promise((resolve, reject) => { - this.readInputPath(inputPath).then((docString: string) => { - resolve(this.createOutputMapFromString(docString)); - }).catch((err) => { - reject(err); - }); - }); - } - - public readInputPath(inputPath: string): Promise { - return new Promise((resolve, reject) => { - ExtractGQL.isDirectory(inputPath).then((isDirectory) => { - if (isDirectory) { - console.log(`Crawling ${inputPath}...`); - // Recurse over the files within this directory. - fs.readdir(inputPath, (err, items) => { - if (err) { - reject(err); - } - const promises: Promise[] = items.map((item) => { - return this.readInputPath(inputPath + '/' + item); - }); - - Promise.all(promises).then((queryStrings: string[]) => { - resolve(queryStrings.reduce((x, y) => x + y, '')); - }); - }); - } else { - this.readInputFile(inputPath).then((result: string) => { - resolve(result); - }).catch((err) => { - console.log(`Error occurred in processing path ${inputPath}: `); - console.log(err.message); - reject(err); - }); - } - }); - }); - } - - // Takes a document and a query definition contained within that document. Then, extracts - // the fragments that the query depends on from the document and returns a document containing - // only those fragments. - public getQueryFragments(document: DocumentNode, queryDefinition: OperationDefinitionNode): DocumentNode { - const queryFragmentNames = getFragmentNames(queryDefinition.selectionSet, document); - const retDocument: DocumentNode = { - kind: 'Document', - definitions: [], - }; - - const reduceQueryDefinitions = (carry: FragmentDefinitionNode[], definition: DefinitionNode) => { - const definitionName = (definition as (FragmentDefinitionNode | OperationDefinitionNode)).name; - if ((isFragmentDefinition(definition) && queryFragmentNames[definitionName.value] === 1)) { - const definitionExists = carry.findIndex( - (value: FragmentDefinitionNode) => value.name.value === definitionName.value, - ) !== -1; - - // If this definition doesn't exist yet, add it. - if (!definitionExists) { - return [...carry, definition]; - } - } - - return carry; - }; - - retDocument.definitions = document.definitions.reduce( - reduceQueryDefinitions, - ([] as FragmentDefinitionNode[]), - ).sort(sortFragmentsByName); - - return retDocument; - } - - // Returns unique query ids. - public getQueryId() { - this.queryId += 1; - return this.queryId; - } - - // Writes an OutputMap to a given file path. - public writeOutputMap(outputMap: OutputMap, outputFilePath: string): Promise { - return new Promise((resolve, reject) => { - fs.open(outputFilePath, 'w+', (openErr, fd) => { - if (openErr) { reject(openErr); } - fs.write(fd, JSON.stringify(outputMap), (writeErr, written, str) => { - if (writeErr) { reject(writeErr); } - resolve(); - }); - }); - }); - } - - // Extracts GraphQL queries from this.inputFilePath and produces - // an output JSON file in this.outputFilePath. - public extract() { - this.processInputPath(this.inputFilePath).then((outputMap: OutputMap) => { - this.writeOutputMap(outputMap, this.outputFilePath).then(() => { - console.log(`Wrote output file to ${this.outputFilePath}.`); - }).catch((err) => { - console.log(`Unable to process ouput path ${this.outputFilePath}. Error message: `); - console.log(`${err.message}`); - }); - }).catch((err) => { - console.log(`Unable to process input path ${this.inputFilePath}. Error message: `); - console.log(`${err.message}`); - }); - } -} - -// Type for the argument structure provided by the "yargs" library. -export interface YArgsv { - _: string[]; - [ key: string ]: any; -} - -// Main driving method for the command line tool -export const main = (argv: YArgsv) => { - // These are the unhypenated arguments that yargs does not process - // further. - const args: string[] = argv._; - let inputFilePath: string; - let outputFilePath: string; - const queryTransformers: QueryTransformer[] = []; - - if (args.length < 1) { - console.log('Usage: persistgraphql input_file [output_file]'); - } else if (args.length === 1) { - inputFilePath = args[0]; - } else { - inputFilePath = args[0]; - outputFilePath = args[1]; - } - - // Check if we are passed "--add_typename", if we are, we have to - // apply the typename query transformer. - if (argv['add_typename']) { - console.log('Using the add-typename query transformer.'); - queryTransformers.push(addTypenameTransformer); - } - - const options: ExtractGQLOptions = { - inputFilePath, - outputFilePath, - queryTransformers, - }; - - if (argv['js']) { - options.inJsCode = true; - } - - if (argv['extension']) { - options.extension = argv['extension']; - } - - new ExtractGQL(options).extract(); -}; diff --git a/src/binary.ts b/src/binary.ts deleted file mode 100644 index 6001a1c..0000000 --- a/src/binary.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { main } from './ExtractGQL'; -const argv = require('yargs').argv; - -main(argv); diff --git a/src/common.ts b/src/common.ts deleted file mode 100644 index 5c46b58..0000000 --- a/src/common.ts +++ /dev/null @@ -1,94 +0,0 @@ -// A set of things that are common across the CLI tool and the stuff that runs on the client/server -// (i.e. network interface, Express middleware, etc.) -// -// This file should not import any Node/server-specific modules. - -import { - DefinitionNode, - FragmentDefinitionNode, - OperationDefinitionNode, - DocumentNode, - print, -} from 'graphql'; - -// A map from a key (id or a hash) to a GraphQL document. -export interface OutputMap { - [key: string]: QueryId; -} - -export type QueryId = number | string; -export interface TransformedQueryWithId { - transformedQuery: DocumentNode; - id: number | string; -} - -export type QueryTransformer = (doc: DocumentNode) => DocumentNode; - -// Sorting strategy for fragment definitions. Sorts fragment -// definitions by their name and moves them to the end of the -// query. -export function sortFragmentsByName(a: DefinitionNode, b: DefinitionNode): number { - const aIsFragment = a.kind === 'FragmentDefinition'; - const bIsFragment = b.kind === 'FragmentDefinition'; - - // If both aren't fragments, just leave them in place. - if (!aIsFragment && !bIsFragment) { - return 0; - } - - // If both are fragments, sort them by their name. - if (aIsFragment && bIsFragment) { - const aName = (a as (FragmentDefinitionNode)).name.value; - const bName = (b as (FragmentDefinitionNode)).name.value; - return aName.localeCompare(bName); - } - - // Move fragments to the end. - return aIsFragment ? 1 : -1; -} - -// Apply sorting strategy for fragments. -export function applyFragmentDefinitionSort(document: DocumentNode): DocumentNode { - document.definitions = document.definitions.sort(sortFragmentsByName); - return document; -} - -// Apply queryTransformers to a query document. -export function applyQueryTransformers( - document: DocumentNode, - queryTransformers: QueryTransformer[] = [], -): DocumentNode { - let currentDocument = document; - queryTransformers.forEach((transformer) => { - currentDocument = transformer(currentDocument); - }); - return currentDocument; -} - -// Returns a key for a query operation definition. Currently just uses GraphQL printing as a -// serialization mechanism; may use hashes or ids in the future. Also applies the query -// transformers to the query definition before returning the key. -export function getQueryKey( - definition: OperationDefinitionNode, - queryTransformers: QueryTransformer[] = [], -): string { - const wrappingDocument: DocumentNode = { - kind: 'Document', - definitions: [ definition ], - }; - return print(applyQueryTransformers( - wrappingDocument, - queryTransformers, - ).definitions[0]); -} - -// Returns a key for a query in a document definition. Should include exactly one query and a set -// of fragments that the query references. Currently just uses GraphQL printing as a serialization -// mechanism; may use hashes or ids in the future. Also applies query transformers to the document -// before making it a document key. -export function getQueryDocumentKey( - document: DocumentNode, - queryTransformers: QueryTransformer[] = [], -): string { - return print(applyFragmentDefinitionSort(applyQueryTransformers(document, queryTransformers))); -} diff --git a/src/extractFromAST.ts b/src/extractFromAST.ts deleted file mode 100644 index f45a287..0000000 --- a/src/extractFromAST.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { - DocumentNode, - DefinitionNode, - OperationDefinitionNode, - SelectionSetNode, - SelectionNode, - FragmentSpreadNode, - FieldNode, - InlineFragmentNode, - FragmentDefinitionNode, -} from 'graphql'; - -import _ = require('lodash'); - -// Checks if a given GraphQL definition is an operation definition (i.e. either query or mutation). -export function isOperationDefinition(defn: DefinitionNode): defn is OperationDefinitionNode { - return (defn.kind === 'OperationDefinition'); -} - -// Checks if a given GraphQL selection is a FragmentSpread. -export function isFragmentSpread(selection: SelectionNode): selection is FragmentSpreadNode { - return (selection.kind === 'FragmentSpread'); -} - -// Checks if a given GraphQL definition is a FragmentDefinition. -export function isFragmentDefinition(selection: DefinitionNode): selection is FragmentDefinitionNode { - return (selection.kind === 'FragmentDefinition'); -} - -// Checks if a given GraphQL selection is a Field. -export function isField(selection: SelectionNode): selection is FieldNode { - return (selection.kind === 'Field'); -} - -// Checks if a given GraphQL selection is an InlineFragment. -export function isInlineFragment(selection: SelectionNode): selection is InlineFragmentNode { - return (selection.kind === 'InlineFragment'); -} - -export function isQueryDefinition(defn: DefinitionNode): defn is OperationDefinitionNode { - return (isOperationDefinition(defn) && defn.operation === 'query'); -} - -// Creates a query document out of a single query operation definition. -export function createDocumentFromQuery(definition: OperationDefinitionNode): DocumentNode { - return { - kind: 'Document', - definitions: [ definition ], - }; -} - -// Get query definitions from query document. -export function getQueryDefinitions(doc: DocumentNode): OperationDefinitionNode[] { - const queryDefinitions: OperationDefinitionNode[] = []; - doc.definitions.forEach((definition) => { - if (isQueryDefinition(definition)) { - queryDefinitions.push(definition); - } - }); - return queryDefinitions; -} - -export function getOperationDefinitions(doc: DocumentNode): OperationDefinitionNode[] { - return doc.definitions.filter(isOperationDefinition) as OperationDefinitionNode[]; -} - -// Extracts the names of fragments from a SelectionSet recursively, given a document in which -// each of the fragments defined are given. Returns a map going from -// the name of fragment to the integer "1" to support O(1) lookups. -export function getFragmentNames(selectionSet: SelectionSetNode, document: DocumentNode): { - [name: string]: number, -} { - if (!selectionSet) { - return {}; - } - - // Construct a map going from the name of a fragment to the definition of the fragment. - const fragmentDefinitions: { [name: string]: FragmentDefinitionNode } = {}; - document.definitions.forEach((definition) => { - if (isFragmentDefinition(definition)) { - fragmentDefinitions[definition.name.value] = definition; - } - }); - - let fragmentNames: { [name: string]: number } = {}; - selectionSet.selections.forEach((selection) => { - // If we encounter a fragment spread, we look inside it to unravel more fragment names. - if (isFragmentSpread(selection)) { - fragmentNames[selection.name.value] = 1; - const innerFragmentNames = getFragmentNames( - fragmentDefinitions[selection.name.value].selectionSet, - document, - ); - fragmentNames = _.merge(fragmentNames, innerFragmentNames); - } else if (isInlineFragment(selection) || isField(selection)) { - const innerFragmentNames = getFragmentNames(selection.selectionSet, document); - fragmentNames = _.merge(fragmentNames, innerFragmentNames); - } - }); - return fragmentNames; -} diff --git a/src/extractFromJS.ts b/src/extractFromJS.ts deleted file mode 100644 index 5a71102..0000000 --- a/src/extractFromJS.ts +++ /dev/null @@ -1,21 +0,0 @@ -export function findTaggedTemplateLiteralsInJS(jsCode: string, tag: string): string[] { - // m for multiline - // g for matching multiple results - // capture the text inside the template literal with parentheses - const regex = new RegExp(tag + '\\s*\`([\\s\\S]+?)\`', 'mg'); - const results = []; - - let result; - // run in a loop to get all results - while ((result = regex.exec(jsCode)) !== null) { - results.push(result[1]); - } - - return results; -} - -export function eliminateInterpolations(templateLiteralContents: string): string { - const regex = /\$\{[\s\S]+?\}/mg; - - return templateLiteralContents.replace(regex, ''); -} diff --git a/src/network_interface/ApolloNetworkInterface.ts b/src/network_interface/ApolloNetworkInterface.ts deleted file mode 100644 index a94ae5c..0000000 --- a/src/network_interface/ApolloNetworkInterface.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { - HTTPFetchNetworkInterface, - NetworkInterface, - Request, -} from 'apollo-client'; - -import { - ExecutionResult, -} from 'graphql'; - -import { - getQueryDocumentKey, - OutputMap, -} from '../common'; - -export class PersistedQueryNetworkInterface extends HTTPFetchNetworkInterface { - public queryMap: OutputMap = {}; - public _opts: RequestInit; - public _uri: string; - public enablePersistedQueries: boolean; - - // Constructor for this class. - // - // @param enablePersistedQueries Determines whether or not to use persisted queries or just - // send the query document string over the wire. - // - // @param uri URI of the GraphQL endpoint to which this network interface will send requests. - // - // @param queryMap The query map produced as output from the `extractgql` CLI tool. - // - // @param opts The set of options passed to fetch. - constructor({ - enablePersistedQueries = true, - uri, - queryMap, - opts = {}, - }: { - enablePersistedQueries?: boolean, - uri: string, - queryMap: OutputMap, - opts?: RequestInit, - }) { - super(uri, opts); - this._uri = uri; - this._opts = opts; - this.queryMap = queryMap; - this.enablePersistedQueries = enablePersistedQueries; - - if (enablePersistedQueries) { - addPersistedQueries(this, queryMap); - } - } -} - -export function addPersistedQueries(networkInterface: NetworkInterface, queryMap: OutputMap) { - const _query = networkInterface.query.bind(networkInterface); - - return Object.assign(networkInterface, { - query: (request: Request): Promise => { - const queryDocument = request.query; - const queryKey = getQueryDocumentKey(queryDocument); - - if (!queryMap[queryKey]) { - return Promise.reject(new Error('Could not find query inside query map.')); - } - - const serverRequest = { - id: queryMap[queryKey], - variables: request.variables, - operationName: request.operationName, - }; - - return _query(serverRequest); - }, - }); -} diff --git a/src/queryTransformers.ts b/src/queryTransformers.ts deleted file mode 100644 index 0f758c6..0000000 --- a/src/queryTransformers.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { - FieldNode, - SelectionSetNode, - DefinitionNode, - OperationDefinitionNode, - DocumentNode, - print, -} from 'graphql'; - -import { - QueryTransformer, -} from './common'; - -import { - isField, - isInlineFragment, - isOperationDefinition, - isFragmentDefinition, -} from './extractFromAST'; - -// TODO Most of this implementation has been copped from here: -// https://github.com/apollostack/apollo-client/blob/master/src/queries/queryTransform.ts -// -// This probably means that this implementation should be exported as some kind of library, -// along with some of the other AST-related stuff implemented for apollo-client. -const TYPENAME_FIELD: FieldNode = { - kind: 'Field', - alias: null, - name: { - kind: 'Name', - value: '__typename', - }, -}; - -function addTypenameToSelectionSet( - selectionSet: SelectionSetNode, - isRoot = false, -) { - if (selectionSet.selections) { - if (! isRoot) { - const alreadyHasThisField = selectionSet.selections.some((selection) => { - return selection.kind === 'Field' && (selection as FieldNode).name.value === '__typename'; - }); - - if (! alreadyHasThisField) { - selectionSet.selections.push(TYPENAME_FIELD); - } - } - - selectionSet.selections.forEach((selection) => { - if (selection.kind === 'Field' || selection.kind === 'InlineFragment') { - if (selection.selectionSet) { - addTypenameToSelectionSet(selection.selectionSet); - } - } - }); - } -} - -export const addTypenameTransformer: QueryTransformer = (doc: DocumentNode) => { - const docClone = JSON.parse(JSON.stringify(doc)); - - docClone.definitions.forEach((definition: DefinitionNode) => { - const isRoot = definition.kind === 'OperationDefinition'; - addTypenameToSelectionSet((definition as OperationDefinitionNode).selectionSet, isRoot); - }); - - return docClone; -}; diff --git a/test/common.ts b/test/common.ts deleted file mode 100644 index 2af6b3a..0000000 --- a/test/common.ts +++ /dev/null @@ -1,127 +0,0 @@ -import * as chai from 'chai'; -const { assert } = chai; - -import { - sortFragmentsByName, - applyFragmentDefinitionSort, -} from '../src/common'; - -import { - print, -} from 'graphql'; - -import gql from 'graphql-tag'; - -describe('common methods', () => { - describe('sortFragmentsByName', () => { - const queryDoc1 = gql` - query { field } - `; - const queryDoc2 = gql` - query { root } - `; - const fragmentDoc1 = gql` - fragment details on Author { - name - }`; - const fragmentDoc2 = gql` - fragment info on Author { - name { - firstName - lastName - } - }`; - const queryDef1 = queryDoc1.definitions[0]; - const queryDef2 = queryDoc2.definitions[0]; - const fragmentDef1 = fragmentDoc1.definitions[0]; - const fragmentDef2 = fragmentDoc2.definitions[0]; - - it('should return 0 if both args are not a fragment', () => { - assert.equal( - sortFragmentsByName(queryDef1, queryDef2), - 0, - ); - }); - - it('should return 1 if the first arg is a fragment and the second isn not', () => { - assert.equal( - sortFragmentsByName(fragmentDef1, queryDef1), - 1 - ); - }); - - it('should return -1 if the second arg is a fragment and the first arg is not', () => { - assert.equal( - sortFragmentsByName(queryDef2, fragmentDef2), - -1 - ); - }); - - it('correctly orders fragments by name', () => { - assert.equal( - sortFragmentsByName(fragmentDef1, fragmentDef2), - -1 - ); - - assert.equal( - sortFragmentsByName(fragmentDef2, fragmentDef1), - 1 - ); - }); - }); - - describe('applyFragmentDefinitionSort', () => { - it('leaves presorted doc unaltered', () => { - const doc = gql` - query { root } - fragment details on Author { name }`; - assert.equal( - applyFragmentDefinitionSort(doc), - doc - ); - }); - - it('moves fragment defintions to the end of the doc', () => { - const doc = gql` - fragment details on Author { name } - query { root }`; - const result = gql` - query { root } - fragment details on Author { name }`; - - assert.deepEqual( - print(applyFragmentDefinitionSort(doc)), - print(result), - ); - }); - - it('sorts fragments and moves them to the end of the doc', () => { - const doc = gql` - fragment d on Author { x } - fragment b on Author { x } - fragment c on Author { x } - fragment a on Author { x } - query { - ...a - ...b - ...c - ...d - }`; - const result = gql` - query { - ...a - ...b - ...c - ...d - } - fragment a on Author { x } - fragment b on Author { x } - fragment c on Author { x } - fragment d on Author { x }`; - assert.equal( - print(applyFragmentDefinitionSort(doc)), - print(doc) - ); - }); - }); -}); diff --git a/test/extractFromAST.ts b/test/extractFromAST.ts deleted file mode 100644 index 8f93a94..0000000 --- a/test/extractFromAST.ts +++ /dev/null @@ -1,128 +0,0 @@ -import * as chai from 'chai'; -const { assert } = chai; - -import { - getQueryDefinitions, - getFragmentNames, -} from '../src/extractFromAST'; -import gql from 'graphql-tag'; -import { print } from 'graphql'; - -describe('extractFromAST', () => { - describe('getFragmentNames', () => { - it('should extract the fragment names from top level references', () => { - const document = gql` - query { - ...rootDetails - ...otherRootDetails - } - fragment rootDetails on RootQuery { - author { - firstName - lastName - } - } - fragment otherRootDetails on RootQuery { - author { - firstName - lastName - } - } - `; - const fragmentNames = getFragmentNames(document.definitions[0].selectionSet, document); - assert.deepEqual(fragmentNames, { - 'rootDetails': 1, - 'otherRootDetails': 1, - }); - }); - - it('should extract the fragment names from deep references', () => { - const document = gql` - query { - author { - name { - ...nameInfo - } - ...generalAuthorInfo - } - } - fragment nameInfo on Name { - firstName - lastName - } - fragment generalAuthorInfo on Author { - age - } - `; - const fragmentNames = getFragmentNames(document.definitions[0].selectionSet, document); - assert.deepEqual(fragmentNames, { - nameInfo: 1, - generalAuthorInfo: 1, - }); - }); - - it('should extract fragment names referenced in fragments', () => { - const document = gql` - query { - author { - name { - ...nameInfo - } - } - } - fragment nameInfo on Name { - firstName - ...otherNameInfo - } - fragment otherNameInfo on Name { - otherThing { - lastName - } - } - `; - const fragmentNames = getFragmentNames(document.definitions[0].selectionSet, document); - assert.deepEqual(fragmentNames, { - nameInfo: 1, - otherNameInfo: 1, - }); - }); - }); - - describe('getQueryDefinitions', () => { - it('should extract query definitions out of a document containing multiple queries', () => { - const document = gql` - query { - author { - firstName - lastName - } - } - query { - person { - name - } - } - mutation createRandomAuthor { - name - }`; - const query1 = gql` - query { - author { - firstName - lastName - } - } - `; - const query2 = gql` - query { - person { - name - } - }`; - const queries = getQueryDefinitions(document); - assert.equal(queries.length, 2); - assert.equal(print(queries[0]), print(query1.definitions[0])); - assert.equal(print(queries[1]), print(query2.definitions[0])); - }); - }); -}); diff --git a/test/extractFromJS.ts b/test/extractFromJS.ts deleted file mode 100644 index d3c00ed..0000000 --- a/test/extractFromJS.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as chai from 'chai'; -const { assert } = chai; - -import { - findTaggedTemplateLiteralsInJS, - eliminateInterpolations, -} from '../src/extractFromJS'; - -import gql from 'graphql-tag'; -import { print } from 'graphql'; - -describe('extractFromJS', () => { - it('should be able to find tagged strings inside JS', () => { - const jsFile = ` - // Single line - const query = gql\`xxx\`; - - // Multi line - const query2 = gql\`y -y -y\`; - - // Has a space before tag - const query3 = gql \`zzz\`; - `; - - assert.deepEqual(findTaggedTemplateLiteralsInJS(jsFile, 'gql'), [ - 'xxx', - 'y\ny\ny', - 'zzz', - ]); - }); - - it('should eliminate interpolations', () => { - const contents = ` - { - a { b ...c } - } -\${c} - `; - - assert.deepEqual(eliminateInterpolations(contents), ` - { - a { b ...c } - } - - `); - }); -}); diff --git a/test/fixtures/extracted_queries.json b/test/fixtures/extracted_queries.json deleted file mode 100644 index e50c8d9..0000000 --- a/test/fixtures/extracted_queries.json +++ /dev/null @@ -1 +0,0 @@ -{"{\n author {\n ...details\n }\n}\n\nfragment details on Author {\n firstName\n lastName\n}\n":1} \ No newline at end of file diff --git a/test/fixtures/no_queries/empty_folder/test.txt b/test/fixtures/no_queries/empty_folder/test.txt deleted file mode 100644 index b402beb..0000000 --- a/test/fixtures/no_queries/empty_folder/test.txt +++ /dev/null @@ -1 +0,0 @@ -This is just a random file. \ No newline at end of file diff --git a/test/fixtures/no_queries/single_query/queries.graphql b/test/fixtures/no_queries/single_query/queries.graphql deleted file mode 100644 index e950eae..0000000 --- a/test/fixtures/no_queries/single_query/queries.graphql +++ /dev/null @@ -1,13 +0,0 @@ -query { - author { - firstName - lastName - } -} - -query otherQuery { - person { - firstName - lastName - } -} diff --git a/test/fixtures/single_fragment/fragment.graphql b/test/fixtures/single_fragment/fragment.graphql deleted file mode 100644 index e411baf..0000000 --- a/test/fixtures/single_fragment/fragment.graphql +++ /dev/null @@ -1,4 +0,0 @@ -fragment details on Author { - firstName - lastName -} \ No newline at end of file diff --git a/test/fixtures/single_fragment/with_fragment.graphql b/test/fixtures/single_fragment/with_fragment.graphql deleted file mode 100644 index aec9ff4..0000000 --- a/test/fixtures/single_fragment/with_fragment.graphql +++ /dev/null @@ -1,9 +0,0 @@ -#import "./fragment.graphql" -# Note that as per the current implementation, the above line -# doesn't do anything but we have it here because this is probably -# what a real query that uses graphql-tag/loader would look like. -query { - author { - ...details - } -} diff --git a/test/fixtures/single_fragment_js/code.js b/test/fixtures/single_fragment_js/code.js deleted file mode 100644 index 89d3894..0000000 --- a/test/fixtures/single_fragment_js/code.js +++ /dev/null @@ -1,16 +0,0 @@ -const frag = gql` - fragment details on Author { - firstName - lastName - } -`; - -const query = gql` - query { - author { - ...details - } - } - - ${frag} -`; diff --git a/test/fixtures/single_query/queries.graphql b/test/fixtures/single_query/queries.graphql deleted file mode 100644 index e950eae..0000000 --- a/test/fixtures/single_query/queries.graphql +++ /dev/null @@ -1,13 +0,0 @@ -query { - author { - firstName - lastName - } -} - -query otherQuery { - person { - firstName - lastName - } -} diff --git a/test/index.ts b/test/index.ts deleted file mode 100644 index 78e9b58..0000000 --- a/test/index.ts +++ /dev/null @@ -1,520 +0,0 @@ -import * as chai from 'chai'; -const { assert } = chai; - -import { - createDocumentFromQuery, -} from '../src/extractFromAST'; - -import { - ExtractGQL, -} from '../src/ExtractGQL'; - -import { - OutputMap, -} from '../src/common'; - -import { - addTypenameTransformer, -} from '../src/queryTransformers'; - -import { - parse, - print, - OperationDefinitionNode, - DocumentNode, -} from 'graphql'; - -import gql from 'graphql-tag'; - -describe('ExtractGQL', () => { - const queries = gql` - query { - author { - firstName - lastName - } - } - - query otherQuery { - person { - firstName - lastName - } - }`; - - const egql = new ExtractGQL({ inputFilePath: 'not-real'}); - const keys = [ - egql.getQueryKey(queries.definitions[0]), - egql.getQueryKey(queries.definitions[1]), - ]; - - it('should be able to construct an instance', () => { - assert.doesNotThrow(() => { - new ExtractGQL({ - inputFilePath: 'queries.graphql', - outputFilePath: 'output.json', - }); - }); - }); - - describe('isDirectory', () => { - it('should return true on a directory', (done) => { - ExtractGQL.isDirectory('./test/fixtures').then((result: boolean) => { - assert(result); - done(); - }); - }); - - it('should return false on a file', (done) => { - ExtractGQL.isDirectory('./test/fixtures/single_query/queries.graphql').then((result) => { - assert(!result); - done(); - }); - }); - }); - - describe('getFileExtension', () => { - it('should return the correct extension on a file with an extension', () => { - assert.equal(ExtractGQL.getFileExtension('../../path/source.graphql'), 'graphql'); - assert.equal(ExtractGQL.getFileExtension('/some/complicated/path.with.dots/dots../view.js'), 'js'); - }); - it('should return an empty string if the file has no extension', () => { - assert.equal(ExtractGQL.getFileExtension('/redherring.graphql/file'), ''); - assert.equal(ExtractGQL.getFileExtension('file'), ''); - }); - }); - - describe('readFile', () => { - it('should be able to read a file into a string', (done) => { - const filePath = 'test/fixtures/single_query/queries.graphql'; - ExtractGQL.readFile(filePath).then((result) => { - const graphQLString = print(parse(result)); - assert.deepEqual(graphQLString, print(queries)); - done(); - }); - }); - }); - - describe('createMapFromDocument', () => { - it('should be able to handle a document with no queries', () => { - const document = gql`fragment something on Type { otherThing }`; - const map = egql.createMapFromDocument(document); - assert.deepEqual(map, {}); - }); - - it('should be able to handle a document with a single query', () => { - const myegql = new ExtractGQL({ inputFilePath: 'nothing' }); - const document = gql`query author { - name - }`; - const map = myegql.createMapFromDocument(document); - const key = egql.getQueryDocumentKey(document); - assert.equal(Object.keys(map).length, 1); - assert.equal(map[key], 1); - }); - - it('should be able to handle a document with a fragment', () => { - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - const document = gql` - query authorList { - author { - ...authorDetails - } - } - fragment authorDetails on Author { - firstName - lastName - } - `; - const map = myegql.createMapFromDocument(document); - const key = myegql.getQueryDocumentKey(document); - assert.equal(Object.keys(map).length, 1); - assert.equal(map[key], 1); - }); - - it('should be able to handle a document with multiple fragments', () => { - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - const document = gql` - query authorList { - author { - ...authorDetails - ...otherDetails - } - } - fragment authorDetails on Author { - firstName - lastName - } - fragment otherDetails on Author { - author - }`; - const map = myegql.createMapFromDocument(document); - assert.equal(Object.keys(map)[0], print(document)); - }); - - it('should be able to handle a document with unused fragments', () => { - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - const document = gql` - query authorList { - author { - firstName - lastName - } - } - fragment pointlessFragment on Author { - firstName - lastName - } - `; - const map = egql.createMapFromDocument(document); - assert.equal( - Object.keys(map)[0], - print(createDocumentFromQuery(document.definitions[0])) - ); - }); - - it('should be able to handle a document with multiple queries sharing a fragment', () => { - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - const document = gql` - query authorList { - author { - ...authorDetails - } - } - query authorInfo { - author { - ...authorDetails - } - } - fragment authorDetails on Author { - firstName - lastName - } - `; - const authorList = gql` - query authorList { - author { - ...authorDetails - } - } - fragment authorDetails on Author { - firstName - lastName - } - `; - const authorInfo = gql` - query authorInfo { - author { - ...authorDetails - } - } - fragment authorDetails on Author { - firstName - lastName - } - `; - const map = myegql.createMapFromDocument(document); - const key1 = myegql.getQueryDocumentKey(authorList); - const key2 = myegql.getQueryDocumentKey(authorInfo); - assert.equal(key1, print(authorList)); - assert.property(map, key1); - - assert.equal(key2, print(authorInfo)); - assert.property(map, key2); - }); - - it('should be able to handle a document with multiple queries', () => { - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - const document = gql`query author { - name - } - query person { - name - }`; - const map = myegql.createMapFromDocument(document); - const keys = Object.keys(map); - assert.equal(keys.length, 2); - assert.include(keys, myegql.getQueryDocumentKey( - createDocumentFromQuery(document.definitions[0]) - )); - assert.include(keys, myegql.getQueryDocumentKey( - createDocumentFromQuery(document.definitions[1]) - )); - }); - - it('should be able to apply query transforms to a document with fragments', () => { - const myegql = new ExtractGQL({ - inputFilePath: 'empty', - queryTransformers: [ addTypenameTransformer ], - }); - const document = gql` - query { - author { - ...details - } - } - fragment details on Author { - name { - firstName - lastName - } - }`; - const transformedDocument = gql` - query { - author { - ...details - __typename - } - } - fragment details on Author { - name { - firstName - lastName - __typename - } - }`; - const map = myegql.createMapFromDocument(document); - assert.equal(Object.keys(map).length, 1); - const key = myegql.getQueryDocumentKey(transformedDocument); - assert.equal(Object.keys(map)[0], key); - assert.equal(map[key], 1); - }); - - it('should be able to handle a document with a mutation', () => { - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - const document = gql` - mutation changeAuthorStuff { - firstName - lastName - }`; - const map = myegql.createMapFromDocument(document); - const keys = Object.keys(map); - assert.equal(keys.length, 1); - assert.equal(keys[0], myegql.getQueryDocumentKey(document)); - assert.equal(map[keys[0]], 1); - }); - - it('should sort fragments correctly', () => { - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - const doc = gql` - fragment d on Author { x } - fragment b on Author { x } - fragment c on Author { x } - fragment a on Author { x } - query { - ...a - ...b - ...c - ...d - }`; - const result = gql` - query { - ...a - ...b - ...c - ...d - } - fragment a on Author { x } - fragment b on Author { x } - fragment c on Author { x } - fragment d on Author { x }`; - const map = myegql.createMapFromDocument(doc); - const keys = Object.keys(map); - assert.equal(keys.length, 1); - assert.equal(keys[0], print(result)); - }); - }); - - describe('queryTransformers', () => { - it('should be able to transform a document before writing it to the output map', () => { - const originalDocument = gql` - query { - author { - firstName - lastName - } - } - `; - const newDocument: DocumentNode = gql` - query { - person { - name - } - } - `; - const newQueryDef = newDocument.definitions[0] as OperationDefinitionNode; - const queryTransformer = (queryDoc: DocumentNode) => { - return newDocument; - }; - const myegql = new ExtractGQL({ inputFilePath: 'empty' }); - myegql.addQueryTransformer(queryTransformer); - const map = myegql.createMapFromDocument(originalDocument); - const keys = Object.keys(map); - assert.equal(keys.length, 1); - assert.equal( - keys[0], - myegql.getQueryDocumentKey(newDocument) - ); - }); - }); - - describe('processGraphQLFile', () => { - it('should be able to load a GraphQL file with multiple queries', (done) => { - egql.processGraphQLFile('./test/fixtures/single_query/queries.graphql').then((documentMap) => { - assert.equal(Object.keys(documentMap).length, 2); - done(); - }); - }); - }); - - describe('readInputFile', () => { - it('should return an empty string on a file with an unknown extension', (done) => { - egql.readInputFile('./test/fixtures/bad.c').then((result: string) => { - assert.deepEqual(result, ''); - done(); - }); - }); - - it('should correctly process a file with a .graphql extension', (done) => { - egql.readInputFile('./test/fixtures/single_query/queries.graphql').then((result: string) => { - assert.equal(result.split('\n').length, 14); - assert.include(result, 'query {'); - assert.include(result, 'person {'); - assert.include(result, 'lastName'); - done(); - }); - }); - }); - - describe('processInputPath', () => { - it('should process a single file', (done) => { - egql.processInputPath('./test/fixtures/single_query/queries.graphql').then((result: OutputMap) => { - assert.equal(Object.keys(result).length, 2); - assert.include( - Object.keys(result), - print(createDocumentFromQuery(queries.definitions[0])) - ); - assert.include( - Object.keys(result), - print(createDocumentFromQuery(queries.definitions[1])) - ); - done(); - }); - }); - - it('should process a directory with a single file', (done) => { - egql.processInputPath('./test/fixtures/single_query').then((result: OutputMap) => { - assert.equal(Object.keys(result).length, 2); - assert.include( - Object.keys(result), - print(createDocumentFromQuery(queries.definitions[0])) - ); - assert.include( - Object.keys(result), - print(createDocumentFromQuery(queries.definitions[1])) - ); - done(); - }); - }); - - it('should process a directory with no graphql files', (done) => { - egql.processInputPath('./test/fixtures/no_queries').then((result: OutputMap) => { - assert.equal(Object.keys(result).length, 2); - done(); - }); - }); - - it('should process a file with a fragment reference to a different file', () => { - const expectedQuery = gql` - query { - author { - ...details - } - } - fragment details on Author { - firstName - lastName - } - `; - - return egql.processInputPath('./test/fixtures/single_fragment').then((result: OutputMap) => { - const keys = Object.keys(result); - assert.equal(keys.length, 1); - assert.include( - Object.keys(result), - print(expectedQuery) - ); - }); - }); - - it('should process a JS file with queries', () => { - const expectedQuery = gql` - query { - author { - ...details - } - } - fragment details on Author { - firstName - lastName - } - `; - - const jsEgql = new ExtractGQL({ - inputFilePath: 'idk', - extension: 'js', - inJsCode: true, - outputFilePath: 'idk', - }); - - return jsEgql.processInputPath('./test/fixtures/single_fragment_js') - .then((result: OutputMap) => { - const keys = Object.keys(result); - assert.equal(keys.length, 1); - assert.equal( - keys[0], - print(expectedQuery) - ); - }); - }); - }); - - describe('writeOutputMap', () => { - it('should be able to write an OutputMap to a file', (done) => { - const outputMap = egql.createMapFromDocument(queries); - egql.writeOutputMap(outputMap, './test/output_tests/output.graphql').then(() => { - done(); - }).catch((err) => { - done(err); - }); - }); - }); - - describe('getQueryKey', () => { - it('should apply query transformers before returning the query key', () => { - const query = gql` - query { - author { - firstName - lastName - } - }`; - const transformedQuery = gql` - query { - author { - firstName - lastName - __typename - } - }`; - const myegql = new ExtractGQL({ - inputFilePath: "---", - queryTransformers: [ addTypenameTransformer ], - }); - assert.equal( - myegql.getQueryKey(query.definitions[0]), - print(transformedQuery.definitions[0]) - ); - }); - }); -}); diff --git a/test/network_interface/ApolloNetworkInterface.ts b/test/network_interface/ApolloNetworkInterface.ts deleted file mode 100644 index d35dff5..0000000 --- a/test/network_interface/ApolloNetworkInterface.ts +++ /dev/null @@ -1,389 +0,0 @@ -import * as chai from 'chai'; -const { assert } = chai; - -import { - ExecutionResult, - print, -} from 'graphql'; - -import gql from 'graphql-tag'; -import * as fetchMock from 'fetch-mock'; -const _ = require('lodash'); - -import { - parse, -} from 'graphql'; - -import { - NetworkInterface, - Request, -} from 'apollo-client/transport/networkInterface'; - -import { - getQueryDocumentKey, -} from '../../src/common'; - -import { - PersistedQueryNetworkInterface, - addPersistedQueries, -} from '../../src/network_interface/ApolloNetworkInterface'; - -import { - ExtractGQL, -} from '../../src/ExtractGQL'; - -describe('PersistedQueryNetworkInterface', () => { - it('should construct itself', () => { - const pni = new PersistedQueryNetworkInterface({ - uri: 'http://fake.com/fake', - queryMap: {}, - }); - assert.equal(pni._uri, 'http://fake.com/fake'); - assert.deepEqual(pni._opts, {}); - assert.deepEqual(pni.queryMap, {}); - }); - - it('should not use query mapping when enablePersistedQueries = false', (done) => { - const fetchUri = 'http://fake.com/fake'; - const query = gql`query { author }`; - - fetchMock.post(fetchUri, (url: string, opts: Object) => { - const requestQuery = parse(JSON.parse((opts as RequestInit).body.toString()).query); - assert.equal(print(requestQuery), print(query)); - fetchMock.restore(); - done(); - return null; - }); - - const pni = new PersistedQueryNetworkInterface({ - uri: fetchUri, - queryMap: {}, - enablePersistedQueries: false, - }); - pni.query({ query }); - }); - - - it('should fail to work when asked to lookup nonmapped query', (done) => { - const pni = new PersistedQueryNetworkInterface({ - uri: 'http://fake.com/fake', - queryMap: {}, - }); - - const request = { - query: gql` - query { - author { - firstName - lastName - } - } - `, - }; - - pni.query(request).then(() => { - done(new Error('Result resolved when it should not have.')); - }).catch((err: Error) => { - assert(err); - assert.include(err.message, 'Could not find'); - done(); - }); - }); - - describe('sending query ids', () => { - const egql = new ExtractGQL({ inputFilePath: 'nothing' }); - const queriesDocument = gql` - query { - author { - firstName - lastName - } - } - query { - person { - ...personDetails - } - } - query { - house { - address - } - } - query { - person(id: $id) { - name - } - } - query ListOfAuthors { - author { - firstName - lastName - } - } - mutation changeAuthorStuff { - firstName - lastName - } - fragment personDetails on Person { - firstName - lastName - } - `; - const simpleQueryRequest = { - id: 1, - }; - const simpleQueryData: Object = { - author: { - firstName: 'John', - lastName: 'Smith', - }, - }; - const fragmentQueryRequest = { - id: 2, - }; - const fragmentQueryData: Object = { - person: { - firstName: 'Jane', - lastName: 'Smith', - }, - }; - - const errorQueryRequest = { - id: 3, - }; - const errorQueryData: Object = { - house: { - address: null, - }, - }; - const errorQueryError = new Error('Could not compute error.'); - - const idVariableValue = '1'; - const variableQueryRequest = { - id: 4, - variables: { id: idVariableValue }, - }; - const variableQueryData: Object = { - person: { - name: 'Dhaivat Pandya', - }, - }; - const operationNameQueryRequest = { - operationName: 'ListOfAuthors', - id: 5, - }; - const operationNameQueryData: Object = { - author: [ - { name: 'Adam Smith' }, - { name: 'Thomas Piketty' }, - ], - }; - - const mutationRequest = { - id: 6, - }; - const mutationData = { - firstName: 'John', - lastName: 'Smith', - }; - - const queryMap = egql.createMapFromDocument(queriesDocument); - const uri = 'http://fake.com/fakegraphql'; - const pni = new PersistedQueryNetworkInterface({ - uri, - queryMap, - }); - - before(() => { - fetchMock.post(uri, (url: string, opts: Object): ExecutionResult => { - const receivedObject = JSON.parse((opts as RequestInit).body.toString()); - if (_.isEqual(receivedObject, simpleQueryRequest)) { - return { data: simpleQueryData }; - } else if (_.isEqual(receivedObject, fragmentQueryRequest)) { - return { data: fragmentQueryData }; - } else if (_.isEqual(receivedObject, errorQueryRequest)) { - return { data: errorQueryData, errors: [ errorQueryError ] }; - } else if (_.isEqual(receivedObject, variableQueryRequest)) { - return { data: variableQueryData }; - } else if (_.isEqual(receivedObject, operationNameQueryRequest)) { - return { data: operationNameQueryData }; - } else if (_.isEqual(receivedObject, mutationRequest)) { - return { data: mutationData }; - } else { - throw new Error('Received unmatched request in mock fetch.'); - } - }); - }); - - after(() => { - fetchMock.restore(); - }); - - it('should work for a single, no fragment query', (done) => { - pni.query({ - query: gql` - query { - author { - firstName - lastName - } - }`, - }).then((result) => { - assert.deepEqual(result.data, simpleQueryData); - done(); - }).catch((error) => { - done(error); - }); - }); - - it('should work for a query with a fragment', (done) => { - pni.query({ - query: gql` - query { - person { - ...personDetails - } - } - - fragment personDetails on Person { - firstName - lastName - } - `}).then((result) => { - assert.deepEqual(result.data, fragmentQueryData); - done(); - }); - }); - - it('should work for a query that returns an error', (done) => { - pni.query({ - query: gql` - query { - house { - address - } - } - `, - }).then((result) => { - assert.deepEqual(result.data, errorQueryData); - assert.deepEqual(result.errors.length, 1); - assert.deepEqual(result.errors[0], errorQueryError); - done(); - }); - }); - - it('should pass along variables to the server', (done) => { - pni.query({ - query: gql` - query { - person(id: $id) { - name - } - }`, - variables: { id: idVariableValue }, - }).then((result) => { - assert.deepEqual(result.data, variableQueryData); - done(); - }); - }); - - it('should pass along the operation name to the server', (done) => { - pni.query({ - query: gql` - query ListOfAuthors { - author { - firstName - lastName - } - }`, - operationName: 'ListOfAuthors', - }).then((result) => { - assert.deepEqual(result.data, operationNameQueryData); - done(); - }); - }); - - it('should also work with mutations', (done) => { - pni.query({ - query: gql` - mutation changeAuthorStuff { - firstName - lastName - }`, - }).then((result) => { - assert.deepEqual(result.data, mutationData); - done(); - }); - }); - }); -}); - -describe('addPersistedQueries', () => { - class GenericNetworkInterface implements NetworkInterface { - public query(originalQuery: Request): Promise { - return Promise.resolve(originalQuery as ExecutionResult); - } - } - - const egql = new ExtractGQL({ inputFilePath: 'nothing' }); - const queriesDocument = gql` - query { - author { - firstName - lastName - } - } - `; - - const queryMap = egql.createMapFromDocument(queriesDocument); - - const request = { - query: gql` - query { - author { - firstName - lastName - } - } - `, - variables: { - id: '1', - }, - operationName: '2', - }; - - it('should error with an unmapped query', (done) => { - const networkInterface = new GenericNetworkInterface(); - addPersistedQueries(networkInterface, {}); - networkInterface.query(request).then(() => { - done(new Error('Should not resolve')); - }).catch((err) => { - assert(err); - assert.include(err.message, 'Could not find'); - done(); - }); - }); - - it('should pass through a query with the persisted query id', () => { - type persistedQueryType = { - id: string, - variables: { - id: string, - }, - operationName: string, - }; - - const networkInterface = new GenericNetworkInterface(); - addPersistedQueries(networkInterface, queryMap); - const expectedId = queryMap[getQueryDocumentKey(request.query)]; - return networkInterface.query(request).then((result: ExecutionResult) => { - const persistedQuery: persistedQueryType = result; - const id = persistedQuery.id; - const variables = persistedQuery.variables; - const operationName = persistedQuery.operationName; - assert(id === expectedId, 'returned query id should equal expected document key'); - assert(variables.id === '1', 'should pass through variables property'); - assert(operationName === '2', 'should pass through operation name'); - }); - }); -}); diff --git a/test/output_tests/output.graphql b/test/output_tests/output.graphql deleted file mode 100644 index c5e9025..0000000 --- a/test/output_tests/output.graphql +++ /dev/null @@ -1 +0,0 @@ -{"{\n author {\n firstName\n lastName\n }\n}\n":11,"query otherQuery {\n person {\n firstName\n lastName\n }\n}\n":12} \ No newline at end of file diff --git a/test/queryTransformers.ts b/test/queryTransformers.ts deleted file mode 100644 index 87938cd..0000000 --- a/test/queryTransformers.ts +++ /dev/null @@ -1,88 +0,0 @@ -import * as chai from 'chai'; -const { assert } = chai; - -import gql from 'graphql-tag'; - -import { - print, - DocumentNode, -} from 'graphql'; - -import { - isOperationDefinition, -} from '../src/extractFromAST'; - -import { - addTypenameTransformer, -} from '../src/queryTransformers'; - -describe('query transformers', () => { - describe('typename query transformer', () => { - const assertTransform = (inputQuery: DocumentNode, expected: DocumentNode) => { - assert.equal( - print(addTypenameTransformer(inputQuery)), - print(expected) - ); - }; - - it('should add __typename to all the levels of a simple query', () => { - assertTransform( - gql` - query { - author { - firstName - lastName - } - }`, - gql` - query { - author { - firstName - lastName - __typename - } - }`); - }); - - it('should add __typename to a multiple level nested query with inlined fragments', () => { - assertTransform(gql` - query { - person { - name { - ... on Name { - firstName - lastName - } - } - address { - ... on Address { - zipcode - } - } - } - }`, - gql` - query { - person { - name { - ... on Name { - firstName - lastName - __typename - } - __typename - } - address { - ... on Address { - zipcode - __typename - } - __typename - } - __typename - } - }`); - - }); - }); -}); diff --git a/test/tests.ts b/test/tests.ts deleted file mode 100644 index 5c748a3..0000000 --- a/test/tests.ts +++ /dev/null @@ -1,9 +0,0 @@ -import 'es6-promise'; -process.env.NODE_ENV = 'test'; - -import './index'; -import './extractFromAST'; -import './network_interface/ApolloNetworkInterface'; -import './queryTransformers'; -import './extractFromJS'; -import './common'; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 7435db1..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "lib": ["es6", "dom", "esnext.asynciterable"], - "moduleResolution": "node", - "sourceMap": true, - "declaration": true, - "rootDir": ".", - "outDir": "lib", - "allowSyntheticDefaultImports": true, - "removeComments": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "types": [ "mocha", "node" ] - }, - "files": [ - "typings.d.ts", - "src/binary.ts", - "test/tests.ts", - "fetch-mock.typings.d.ts", - "index.ts" - ] -} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 71c0db0..0000000 --- a/tslint.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "rules": { - "align": [ - false, - "parameters", - "arguments", - "statements" - ], - "ban": false, - "class-name": true, - "curly": true, - "eofline": true, - "forin": true, - "indent": [ - true, - "spaces" - ], - "interface-name": false, - "jsdoc-format": true, - "label-position": true, - "max-line-length": [ - true, - 140 - ], - "member-access": true, - "member-ordering": [ - true, - "public-before-private", - "static-before-instance", - "variables-before-functions" - ], - "no-any": false, - "no-arg": true, - "no-bitwise": true, - "no-conditional-assignment": false, - "no-consecutive-blank-lines": false, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-construct": true, - "no-debugger": true, - "no-duplicate-variable": true, - "no-empty": true, - "no-eval": true, - "no-inferrable-types": false, - "no-internal-module": true, - "no-null-keyword": false, - "no-require-imports": false, - "no-shadowed-variable": true, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-unused-variable": true, - "no-use-before-declare": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [ - true, - "check-open-brace", - "check-catch", - "check-else", - "check-finally", - "check-whitespace" - ], - "quotemark": [ - true, - "single", - "avoid-escape" - ], - "radix": true, - "semicolon": [ - true, - "always" - ], - "switch-default": true, - "trailing-comma": [ - true, - { - "multiline": "always", - "singleline": "never" - } - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef": [ - false, - "call-signature", - "parameter", - "arrow-parameter", - "property-declaration", - "variable-declaration", - "member-variable-declaration" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "space", - "index-signature": "space", - "parameter": "space", - "property-declaration": "space", - "variable-declaration": "space" - } - ], - "variable-name": [ - true, - "check-format", - "allow-leading-underscore", - "ban-keywords" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] - } -} diff --git a/typings.d.ts b/typings.d.ts deleted file mode 100644 index 2a4d25b..0000000 --- a/typings.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - GRAPHQL -*/ -declare module 'graphql-tag/parser' { - import { Source, ParseOptions, DocumentNode } from 'graphql'; - // XXX figure out how to directly export this method - function parse( - source: Source | string, - options?: ParseOptions - ): Document; -} - -declare module 'graphql-tag/printer' { - function print(ast: any): string; -} - -declare module 'deep-assign' { - function deepAssign(...objects: any[]): any; - export = deepAssign; -}