From a8678c6bbb0daa136f692993ebdb7119ad44b9b3 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Wed, 10 Jan 2024 20:51:54 +0000 Subject: [PATCH] Add definition merging boilerplate code --- package.json | 1 + pnpm-lock.yaml | 29 ++++++++++++++++++++++------- src/api.ts | 31 +++++++++++++++++++++++++++---- src/namespace.ts | 6 +++--- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index c0f5c289..37e63f6c 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-sucrase": "^5.0.1", "@rollup/plugin-terser": "^0.4.0", + "@types/node": "^20.10.8", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "dotenv": "^16.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba51085f..cee61c09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,9 @@ devDependencies: '@rollup/plugin-terser': specifier: ^0.4.0 version: 0.4.0(rollup@3.19.1) + '@types/node': + specifier: ^20.10.8 + version: 20.10.8 '@typescript-eslint/eslint-plugin': specifier: ^6.18.1 version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) @@ -90,7 +93,7 @@ devDependencies: version: 5.3.3 vitest: specifier: 1.0.4 - version: 1.0.4(terser@5.16.6) + version: 1.0.4(@types/node@20.10.8)(terser@5.16.6) packages: @@ -931,6 +934,12 @@ packages: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true + /@types/node@20.10.8: + resolution: {integrity: sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==} + dependencies: + undici-types: 5.26.5 + dev: true + /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true @@ -4075,6 +4084,10 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + /unicode-canonical-property-names-ecmascript@1.0.4: resolution: {integrity: sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==} engines: {node: '>=4'} @@ -4116,7 +4129,7 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /vite-node@1.0.4(terser@5.16.6): + /vite-node@1.0.4(@types/node@20.10.8)(terser@5.16.6): resolution: {integrity: sha512-9xQQtHdsz5Qn8hqbV7UKqkm8YkJhzT/zr41Dmt5N7AlD8hJXw/Z7y0QiD5I8lnTthV9Rvcvi0QW7PI0Fq83ZPg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4125,7 +4138,7 @@ packages: debug: 4.3.4 pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.0.8(terser@5.16.6) + vite: 5.0.8(@types/node@20.10.8)(terser@5.16.6) transitivePeerDependencies: - '@types/node' - less @@ -4137,7 +4150,7 @@ packages: - terser dev: true - /vite@5.0.8(terser@5.16.6): + /vite@5.0.8(@types/node@20.10.8)(terser@5.16.6): resolution: {integrity: sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4165,6 +4178,7 @@ packages: terser: optional: true dependencies: + '@types/node': 20.10.8 esbuild: 0.19.9 postcss: 8.4.32 rollup: 4.9.0 @@ -4173,7 +4187,7 @@ packages: fsevents: 2.3.3 dev: true - /vitest@1.0.4(terser@5.16.6): + /vitest@1.0.4(@types/node@20.10.8)(terser@5.16.6): resolution: {integrity: sha512-s1GQHp/UOeWEo4+aXDOeFBJwFzL6mjycbQwwKWX2QcYfh/7tIerS59hWQ20mxzupTJluA2SdwiBuWwQHH67ckg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4198,6 +4212,7 @@ packages: jsdom: optional: true dependencies: + '@types/node': 20.10.8 '@vitest/expect': 1.0.4 '@vitest/runner': 1.0.4 '@vitest/snapshot': 1.0.4 @@ -4216,8 +4231,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.8(terser@5.16.6) - vite-node: 1.0.4(terser@5.16.6) + vite: 5.0.8(@types/node@20.10.8)(terser@5.16.6) + vite-node: 1.0.4(@types/node@20.10.8)(terser@5.16.6) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/src/api.ts b/src/api.ts index badd695f..344ac40c 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,5 +1,5 @@ -import { parse as _parse } from '@0no-co/graphql.web'; -import type { DocumentNode } from '@0no-co/graphql.web'; +import { Kind, parse as _parse } from '@0no-co/graphql.web'; +import type { DocumentNode, DefinitionNode } from '@0no-co/graphql.web'; import type { IntrospectionQuery, @@ -59,9 +59,32 @@ function graphql< const Fragments extends readonly [...FragmentDefDecorationLike[]], >( input: In, - _fragments?: Fragments + fragments?: Fragments ): getDocumentNode> { - return _parse(input) as any; + const definitions = _parse(input).definitions as DefinitionNode[]; + const fragmentNames = new Map(); + for (const document of fragments || []) { + for (const definition of document.definitions) { + if (definition.kind !== Kind.FRAGMENT_DEFINITION) { + /*noop*/ + } else if (!fragmentNames.has(definition.name.value)) { + fragmentNames.set(definition.name.value, definition); + definitions.push(definition); + } else if ( + process.env.NODE_ENV !== 'production' && + fragmentNames.get(definition.name.value) !== definition + ) { + // Fragments with the same names is expected to have the same contents + console.warn( + '[WARNING: Duplicate Fragment] A fragment with name `' + + definition.name.value + + '` already exists in this document.\n' + + 'While fragment names may not be unique across your source, each name must be unique per document.' + ); + } + } + } + return { kind: Kind.DOCUMENT, definitions } as any; } /** A GraphQL `DocumentNode` with attached generics for its result data and variables. diff --git a/src/namespace.ts b/src/namespace.ts index a1bfab27..ed34c86d 100644 --- a/src/namespace.ts +++ b/src/namespace.ts @@ -1,4 +1,4 @@ -import type { Kind } from '@0no-co/graphql.web'; +import type { Kind, DocumentNode } from '@0no-co/graphql.web'; import type { DocumentNodeLike } from './parser'; import type { obj } from './utils'; @@ -33,7 +33,7 @@ type getFragmentsOfDocumentsRec = Documents extends readonly [ infer Document, ...infer Rest, ] - ? (Document extends FragmentDefDecorationLike + ? (Document extends { [$tada.fragmentDef]?: any } ? Exclude extends infer FragmentDef extends { kind: Kind.FRAGMENT_DEFINITION; name: any; @@ -45,7 +45,7 @@ type getFragmentsOfDocumentsRec = Documents extends readonly [ getFragmentsOfDocumentsRec : {}; -interface FragmentDefDecorationLike { +interface FragmentDefDecorationLike extends DocumentNode { [$tada.fragmentDef]?: { readonly [$tada.fragmentId]: symbol; kind: Kind.FRAGMENT_DEFINITION;