diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..760df44e3 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "arrowParens": "always", + "semi": false, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/package.json b/package.json index e6e1b20ee..ec3ea5d4f 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,9 @@ "bundlesize": "0.17.0", "fetch-cookie": "0.7.2", "fetch-mock": "5.13.1", - "tslint": "5.9.1", - "tslint-config-standard": "7.0.0", - "typescript": "2.7.2" + "prettier": "1.14.2", + "tslint": "5.11.0", + "tslint-config-standard": "8.0.1", + "typescript": "3.0.3" } } diff --git a/src/index.ts b/src/index.ts index fa680707b..a3197981b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,20 +1,28 @@ -import { ClientError, GraphQLError, Headers as HttpHeaders, Options, Variables } from './types' -export { ClientError } from './types' import 'cross-fetch/polyfill' +import { ClientError, GraphQLError, Variables } from './types' + +export { ClientError } from './types' + export class GraphQLClient { private url: string - private options: Options + private options: RequestInit - constructor(url: string, options?: Options) { + constructor(url: string, options?: RequestInit) { this.url = url this.options = options || {} } - async rawRequest( + async rawRequest( query: string, variables?: Variables, - ): Promise<{ data?: T, extensions?: any, headers: Headers, status: number, errors?: GraphQLError[] }> { + ): Promise<{ + data?: T + extensions?: any + headers: Request['headers'] + status: number + errors?: GraphQLError[] + }> { const { headers, ...others } = this.options const body = JSON.stringify({ @@ -24,7 +32,7 @@ export class GraphQLClient { const response = await fetch(this.url, { method: 'POST', - headers: Object.assign({ 'Content-Type': 'application/json' }, headers), + headers: { 'Content-Type': 'application/json', ...headers }, body, ...others, }) @@ -44,10 +52,7 @@ export class GraphQLClient { } } - async request( - query: string, - variables?: Variables, - ): Promise { + async request(query: string, variables?: Variables): Promise { const { headers, ...others } = this.options const body = JSON.stringify({ @@ -57,7 +62,7 @@ export class GraphQLClient { const response = await fetch(this.url, { method: 'POST', - headers: Object.assign({ 'Content-Type': 'application/json' }, headers), + headers: { 'Content-Type': 'application/json', ...headers }, body, ...others, }) @@ -76,7 +81,7 @@ export class GraphQLClient { } } - setHeaders(headers: HttpHeaders): GraphQLClient { + setHeaders(headers: Response['headers']): GraphQLClient { this.options.headers = headers return this @@ -94,17 +99,23 @@ export class GraphQLClient { } } -export async function rawRequest( +export async function rawRequest( url: string, query: string, variables?: Variables, -): Promise<{ data?: T, extensions?: any, headers: Headers, status: number, errors?: GraphQLError[] }> { +): Promise<{ + data?: T + extensions?: any + headers: Request['headers'] + status: number + errors?: GraphQLError[] +}> { const client = new GraphQLClient(url) return client.rawRequest(query, variables) } -export async function request( +export async function request( url: string, query: string, variables?: Variables, diff --git a/src/types.ts b/src/types.ts index e982f6458..9ab9e3dae 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,24 +1,8 @@ export type Variables = { [key: string]: any } -export interface Headers { - [key: string]: string -} - -export interface Options { - method?: RequestInit['method'] - headers?: Headers - mode?: RequestInit['mode'] - credentials?: RequestInit['credentials'] - cache?: RequestInit['cache'] - redirect?: RequestInit['redirect'] - referrer?: RequestInit['referrer'] - referrerPolicy?: RequestInit['referrerPolicy'] - integrity?: RequestInit['integrity'] -} - export interface GraphQLError { message: string - locations: { line: number, column: number }[] + locations: { line: number; column: number }[] path: string[] } @@ -36,12 +20,14 @@ export interface GraphQLRequestContext { } export class ClientError extends Error { - response: GraphQLResponse request: GraphQLRequestContext - constructor (response: GraphQLResponse, request: GraphQLRequestContext) { - const message = `${ClientError.extractMessage(response)}: ${JSON.stringify({ response, request })}` + constructor(response: GraphQLResponse, request: GraphQLRequestContext) { + const message = `${ClientError.extractMessage(response)}: ${JSON.stringify({ + response, + request, + })}` super(message) @@ -55,7 +41,7 @@ export class ClientError extends Error { } } - private static extractMessage (response: GraphQLResponse): string { + private static extractMessage(response: GraphQLResponse): string { try { return response.errors![0].message } catch (e) { diff --git a/tests/index.test.ts b/tests/index.test.ts index 851064c01..a3194f8d2 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -1,7 +1,7 @@ import test from 'ava' import * as fetchMock from 'fetch-mock' + import { ClientError, rawRequest, request, GraphQLClient } from '../src/index' -import { Options } from '../src/types' test('minimal query', async (t) => { const data = { @@ -10,8 +10,11 @@ test('minimal query', async (t) => { }, } - await mock({body: {data}}, async () => { - t.deepEqual(await request('https://mock-api.com/graphql', `{ viewer { id } }`), data) + await mock({ body: { data } }, async () => { + t.deepEqual( + await request('https://mock-api.com/graphql', `{ viewer { id } }`), + data, + ) }) }) @@ -26,9 +29,12 @@ test('minimal raw query', async (t) => { version: '1', } - await mock({body: {data, extensions}}, async () => { - const { headers, ...result } = await rawRequest('https://mock-api.com/graphql', `{ viewer { id } }`) - t.deepEqual(result, {data, extensions, status: 200}) + await mock({ body: { data, extensions } }, async () => { + const { headers, ...result } = await rawRequest( + 'https://mock-api.com/graphql', + `{ viewer { id } }`, + ) + t.deepEqual(result, { data, extensions, status: 200 }) }) }) @@ -48,43 +54,53 @@ test('minimal raw query with response headers', async (t) => { 'X-Custom-Header': 'test-custom-header', } - await mock({headers: reqHeaders, body: {data, extensions}}, async () => { - const { headers, ...result } = await rawRequest('https://mock-api.com/graphql', `{ viewer { id } }`) - t.deepEqual(result, {data, extensions, status: 200}) + await mock({ headers: reqHeaders, body: { data, extensions } }, async () => { + const { headers, ...result } = await rawRequest( + 'https://mock-api.com/graphql', + `{ viewer { id } }`, + ) + + t.deepEqual(result, { data, extensions, status: 200 }) t.deepEqual(headers.get('X-Custom-Header'), reqHeaders['X-Custom-Header']) }) }) test('basic error', async (t) => { const errors = { - message: "Syntax Error GraphQL request (1:1) Unexpected Name \"x\"\n\n1: x\n ^\n", + message: + 'Syntax Error GraphQL request (1:1) Unexpected Name "x"\n\n1: x\n ^\n', locations: [ { line: 1, - column: 1 - } - ] + column: 1, + }, + ], } - await mock({body: {errors}}, async () => { - const err: ClientError = await t.throws(request('https://mock-api.com/graphql', `x`)) + await mock({ body: { errors } }, async () => { + const err: ClientError = await t.throws( + request('https://mock-api.com/graphql', `x`), + ) t.deepEqual(err.response.errors, errors) }) }) test('raw request error', async (t) => { const errors = { - message: "Syntax Error GraphQL request (1:1) Unexpected Name \"x\"\n\n1: x\n ^\n", + message: + 'Syntax Error GraphQL request (1:1) Unexpected Name "x"\n\n1: x\n ^\n', locations: [ { line: 1, - column: 1 - } - ] + column: 1, + }, + ], } - await mock({body: {errors}}, async () => { - const err: ClientError = await t.throws(rawRequest('https://mock-api.com/graphql', `x`)) + await mock({ body: { errors } }, async () => { + const err: ClientError = await t.throws( + rawRequest('https://mock-api.com/graphql', `x`), + ) t.deepEqual(err.response.errors, errors) }) }) @@ -96,32 +112,40 @@ test('content-type with charset', async (t) => { }, } - await mock({ - headers: {'Content-Type': 'application/json; charset=utf-8'}, - body: {data} - }, async () => { - t.deepEqual(await request('https://mock-api.com/graphql', `{ viewer { id } }`), data) - }) + await mock( + { + headers: { 'Content-Type': 'application/json; charset=utf-8' }, + body: { data }, + }, + async () => { + t.deepEqual( + await request('https://mock-api.com/graphql', `{ viewer { id } }`), + data, + ) + }, + ) }) - test('extra fetch options', async (t) => { - const options: Options = { + const options: RequestInit = { credentials: 'include', mode: 'cors', cache: 'reload', } const client = new GraphQLClient('https://mock-api.com/graphql', options) - await mock({ - body: { data: {test: 'test'} } - }, async () => { - await client.request('{ test }') - const actualOptions = fetchMock.lastCall()[1] - for (let name in options) { - t.deepEqual(actualOptions[name], options[name]) - } - }) + await mock( + { + body: { data: { test: 'test' } }, + }, + async () => { + await client.request('{ test }') + const actualOptions = fetchMock.lastCall()[1] + for (let name in options) { + t.deepEqual(actualOptions[name], options[name]) + } + }, + ) }) async function mock(response: any, testFn: () => Promise) { diff --git a/tsconfig.json b/tsconfig.json index 0957ff49f..9ae4d965a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,14 @@ { "compilerOptions": { + "lib": ["es2015", "es2016", "dom", "esnext.asynciterable"], "module": "commonjs", "moduleResolution": "node", + "noUnusedLocals": true, + "outDir": "dist", "rootDir": ".", - "target": "es5", "sourceMap": true, "strictNullChecks": true, - "noUnusedLocals": true, - "outDir": "dist", - "lib": ["es2015", "es2016", "dom", "esnext.asynciterable"] + "target": "es5" }, - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } diff --git a/yarn.lock b/yarn.lock index b755d58c3..b8694e1a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1127,7 +1127,7 @@ diff@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" -doctrine@^0.7.2: +doctrine@0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523" dependencies: @@ -2553,6 +2553,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.2.tgz#0ac1c6e1a90baa22a62925f41963c841983282f9" + pretty-ms@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-0.2.2.tgz#da879a682ff33a37011046f13d627f67c73b84f6" @@ -3222,31 +3226,35 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -tslib@^1.0.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" +tslib@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" tslib@^1.8.0: version "1.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.1.tgz#6946af2d1d651a7b1863b531d6e5afa41aa44eac" -tslint-config-standard@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/tslint-config-standard/-/tslint-config-standard-7.0.0.tgz#47bbf25578ed2212456f892d51e1abe884a29f15" +tslib@^1.8.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tslint-config-standard@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/tslint-config-standard/-/tslint-config-standard-8.0.1.tgz#e4dd3128e84b0e34b51990b68715a641f2b417e4" dependencies: - tslint-eslint-rules "^4.1.1" + tslint-eslint-rules "^5.3.1" -tslint-eslint-rules@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz#7c30e7882f26bc276bff91d2384975c69daf88ba" +tslint-eslint-rules@^5.3.1: + version "5.4.0" + resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz#e488cc9181bf193fe5cd7bfca213a7695f1737b5" dependencies: - doctrine "^0.7.2" - tslib "^1.0.0" - tsutils "^1.4.0" + doctrine "0.7.2" + tslib "1.9.0" + tsutils "^3.0.0" -tslint@5.9.1: - version "5.9.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae" +tslint@5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" dependencies: babel-code-frame "^6.22.0" builtin-modules "^1.1.1" @@ -3259,17 +3267,19 @@ tslint@5.9.1: resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" - tsutils "^2.12.1" + tsutils "^2.27.2" -tsutils@^1.4.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0" +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + dependencies: + tslib "^1.8.1" -tsutils@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.14.0.tgz#bc5291622aa2448c1baffc544bcc14ecfa528fb7" +tsutils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.0.0.tgz#0c5070a17a0503e056da038c48b5a1870a50a9ad" dependencies: - tslib "^1.8.0" + tslib "^1.8.1" tunnel-agent@^0.6.0: version "0.6.0" @@ -3281,9 +3291,9 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" -typescript@2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" +typescript@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8" uid-number@^0.0.6: version "0.0.6"