Annotations for supermassive v3 ast (#359)

* Remove supermassive-ast

* Change files

* Add to def

* Address review comments
This commit is contained in:
Mikhail Novikov 2023-09-27 10:42:01 +02:00 коммит произвёл GitHub
Родитель 1babca8d4f
Коммит 04e7dffa35
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
30 изменённых файлов: 471 добавлений и 291 удалений

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Support supermassive v3 annotations",
"packageName": "@graphitation/apollo-react-relay-duct-tape-compiler",
"email": "mnovikov@microsoft.com",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Support supermassive v3 annotations",
"packageName": "@graphitation/graphql-codegen-supermassive-typed-document-node-plugin",
"email": "mnovikov@microsoft.com",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Add supermassive v3 annotations",
"packageName": "@graphitation/supermassive",
"email": "mnovikov@microsoft.com",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -38,7 +38,7 @@
"devDependencies": { "devDependencies": {
"@graphitation/apollo-react-relay-duct-tape-compiler": "^1.2.4", "@graphitation/apollo-react-relay-duct-tape-compiler": "^1.2.4",
"@graphitation/embedded-document-artefact-loader": "^0.6.4", "@graphitation/embedded-document-artefact-loader": "^0.6.4",
"@graphitation/supermassive-ast": "^2.0.1", "@graphitation/supermassive": "^3.0.1",
"@graphql-codegen/cli": "2.2.0", "@graphql-codegen/cli": "2.2.0",
"@graphql-codegen/typescript": "2.2.2", "@graphql-codegen/typescript": "2.2.2",
"@graphql-codegen/typescript-resolvers": "^2.2.1", "@graphql-codegen/typescript-resolvers": "^2.2.1",

Просмотреть файл

@ -41,7 +41,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"graphql": "^15.0.0", "graphql": "^15.0.0",
"@graphitation/supermassive-ast": "^2.0.1", "@graphitation/supermassive": "^3.0.1",
"typescript": "^4.3.5" "typescript": "^4.3.5"
}, },
"publishConfig": { "publishConfig": {

Просмотреть файл

@ -219,6 +219,93 @@ describe("formatModule", () => {
`); `);
}); });
it("adds supermassive MVS to the execution doc", async () => {
expect(
await formatModule(
{
emitDocuments: true,
emitSupermassiveDocuments: true,
emitNarrowObservables: false,
supermassiveDocumentNodeOutputType: "V3",
},
{
definition: {
kind: "Request",
} as Request,
typeText: `
export type MessageComponent_message = {
id: string;
};
`,
docText: `
fragment MessageComponent_message on Message {
id
}
`,
},
),
).toMatchInlineSnapshot(`
"/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
export type MessageComponent_message = {
id: string;
};
export const documents: import("@graphitation/apollo-react-relay-duct-tape-compiler").CompiledArtefactModule = {
"executionQueryDocument": {
"kind": "Document",
"definitions": [
{
"kind": "FragmentDefinition",
"name": {
"kind": "Name",
"value": "MessageComponent_message"
},
"typeCondition": {
"kind": "NamedType",
"name": {
"kind": "Name",
"value": "Message"
}
},
"selectionSet": {
"kind": "SelectionSet",
"selections": [
{
"kind": "Field",
"name": {
"kind": "Name",
"value": "id"
}
}
]
},
"__defs": {
"types": {
"Message": [
2,
{
"id": 10
},
[
"Node"
]
]
}
}
}
]
}
};
export default documents;"
`);
});
it("also prints the watch query doc when emitting narrow observables", async () => { it("also prints the watch query doc when emitting narrow observables", async () => {
expect( expect(
await formatModule( await formatModule(

Просмотреть файл

@ -105,6 +105,23 @@ async function main() {
default: false, default: false,
type: "boolean", type: "boolean",
}, },
supermassiveDocumentNodeOutputType: {
demandOption: false,
default: "V2",
type: "string",
coerce: (value) => {
switch (value) {
case "V2":
return "V2";
case "V3":
return "V3";
case "BOTH":
return "BOTH";
default:
return "V2";
}
},
},
}) })
.help().argv; .help().argv;

Просмотреть файл

@ -8,16 +8,19 @@ import { buildSchema, Source } from "graphql";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import dedupeJSONStringify from "relay-compiler/lib/util/dedupeJSONStringify"; import dedupeJSONStringify from "relay-compiler/lib/util/dedupeJSONStringify";
import type { DocumentNode } from "graphql"; import type { DocumentNode, GraphQLSchema } from "graphql";
import type { FormatModule } from "relay-compiler/lib/language/RelayLanguagePluginInterface"; import type { FormatModule } from "relay-compiler/lib/language/RelayLanguagePluginInterface";
import type { CompiledArtefactModule } from "./types"; import type { CompiledArtefactModule } from "./types";
import { Fragment } from "relay-compiler"; import { Fragment } from "relay-compiler";
export type SupermassiveOutputType = "V3" | "V2" | "BOTH";
export interface FormatModuleOptions { export interface FormatModuleOptions {
emitDocuments: boolean; emitDocuments: boolean;
emitNarrowObservables: boolean; emitNarrowObservables: boolean;
emitQueryDebugComments: boolean; emitQueryDebugComments: boolean;
emitSupermassiveDocuments: boolean; emitSupermassiveDocuments: boolean;
supermassiveDocumentNodeOutputType: SupermassiveOutputType;
schema: string; schema: string;
} }
@ -35,13 +38,17 @@ export async function formatModuleFactory(
) )
: null; : null;
let addTypesToRequestDocument: let addSupermassiveLegacyTypesToRequestDocument:
| undefined | undefined
| typeof import("@graphitation/supermassive-ast").addTypesToRequestDocument; | typeof import("@graphitation/supermassive").addSupermassiveLegacyTypesToRequestDocument;
let addMinimalViableSchemaToRequestDocument:
| undefined
| typeof import("@graphitation/supermassive").addMinimalViableSchemaToRequestDocument;
if (options.emitSupermassiveDocuments) { if (options.emitSupermassiveDocuments) {
({ addTypesToRequestDocument } = await import( ({
"@graphitation/supermassive-ast" addSupermassiveLegacyTypesToRequestDocument,
)); addMinimalViableSchemaToRequestDocument,
} = await import("@graphitation/supermassive"));
} }
function generateExports( function generateExports(
@ -70,11 +77,14 @@ export async function formatModuleFactory(
exports.metadata = extractMetadataTransform(exports.watchQueryDocument); exports.metadata = extractMetadataTransform(exports.watchQueryDocument);
} }
if (addTypesToRequestDocument && exports.executionQueryDocument) { if (options.emitSupermassiveDocuments && exports.executionQueryDocument) {
invariant(schema, "Expected a schema instance"); invariant(schema, "Expected a schema instance");
exports.executionQueryDocument = addTypesToRequestDocument( exports.executionQueryDocument = addTypesToRequestDocument(
schema, schema,
exports.executionQueryDocument, exports.executionQueryDocument,
options.supermassiveDocumentNodeOutputType,
addMinimalViableSchemaToRequestDocument,
addSupermassiveLegacyTypesToRequestDocument,
) as DocumentNode; ) as DocumentNode;
} }
@ -143,3 +153,33 @@ function printExports(exports: CompiledArtefactModule) {
exports, exports,
)};\n\nexport default documents;`; )};\n\nexport default documents;`;
} }
function addTypesToRequestDocument(
schema: GraphQLSchema,
document: DocumentNode,
supermassiveDocumentNodeOutputType: SupermassiveOutputType,
addMinimalViableSchemaToRequestDocument: any,
addSupermassiveLegacyTypesToRequestDocument: any,
) {
let finalDocument = document;
if (
supermassiveDocumentNodeOutputType === "V3" ||
supermassiveDocumentNodeOutputType === "BOTH"
) {
finalDocument = addMinimalViableSchemaToRequestDocument(schema, document, {
addTo: "PROPERTY",
});
}
if (
supermassiveDocumentNodeOutputType === "V2" ||
supermassiveDocumentNodeOutputType === "BOTH" ||
!supermassiveDocumentNodeOutputType
) {
finalDocument = addSupermassiveLegacyTypesToRequestDocument(
schema,
document,
) as unknown as DocumentNode;
}
return finalDocument;
}

Просмотреть файл

@ -28,7 +28,7 @@
"@graphql-codegen/visitor-plugin-common": ">= ^1.17.0 < 2", "@graphql-codegen/visitor-plugin-common": ">= ^1.17.0 < 2",
"graphql-tag": ">= 2.11.0 < 3", "graphql-tag": ">= 2.11.0 < 3",
"@graphql-tools/optimize": "^1.0.1", "@graphql-tools/optimize": "^1.0.1",
"@graphitation/supermassive-ast": "^2.0.1" "@graphitation/supermassive": "^3.0.1"
}, },
"sideEffects": false, "sideEffects": false,
"access": "public", "access": "public",

Просмотреть файл

@ -20,15 +20,22 @@ import {
Kind, Kind,
OperationDefinitionNode, OperationDefinitionNode,
} from "graphql"; } from "graphql";
import { addTypesToRequestDocument } from "@graphitation/supermassive-ast"; import {
addSupermassiveLegacyTypesToRequestDocument,
addMinimalViableSchemaToRequestDocument,
} from "@graphitation/supermassive";
import { optimizeDocumentNode } from "@graphql-tools/optimize"; import { optimizeDocumentNode } from "@graphql-tools/optimize";
import gqlTag from "graphql-tag"; import gqlTag from "graphql-tag";
export type SupermassiveOutputType = "V3" | "V2" | "BOTH";
type RawClientSidePluginConfig = RawClientSideBasePluginConfig & { type RawClientSidePluginConfig = RawClientSideBasePluginConfig & {
supermassiveDocumentNodeConditional?: string; supermassiveDocumentNodeConditional?: string;
supermassiveDocumentNodeOutputType?: SupermassiveOutputType;
}; };
type ClientSidePluginConfig = ClientSideBasePluginConfig & { type ClientSidePluginConfig = ClientSideBasePluginConfig & {
supermassiveDocumentNodeConditional?: string; supermassiveDocumentNodeConditional?: string;
supermassiveDocumentNodeOutputType?: SupermassiveOutputType;
}; };
export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor< export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor<
@ -53,6 +60,8 @@ export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor<
{ {
supermassiveDocumentNodeConditional: supermassiveDocumentNodeConditional:
rawConfig.supermassiveDocumentNodeConditional, rawConfig.supermassiveDocumentNodeConditional,
supermassiveDocumentNodeOutputType:
rawConfig.supermassiveDocumentNodeOutputType || "V3",
}, },
documents, documents,
); );
@ -88,7 +97,7 @@ export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor<
node: FragmentDefinitionNode | OperationDefinitionNode, node: FragmentDefinitionNode | OperationDefinitionNode,
annotate = false, annotate = false,
): string { ): string {
const supermassiveNode = addTypesToRequestDocument(this._schema, { const supermassiveNode = this.addTypesToRequestDocument(this._schema, {
kind: Kind.DOCUMENT, kind: Kind.DOCUMENT,
definitions: [node], definitions: [node],
}).definitions[0] as FragmentDefinitionNode | OperationDefinitionNode; }).definitions[0] as FragmentDefinitionNode | OperationDefinitionNode;
@ -128,7 +137,7 @@ export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor<
...gqlObj.definitions.map((t) => ...gqlObj.definitions.map((t) =>
JSON.stringify( JSON.stringify(
annotate annotate
? addTypesToRequestDocument(this._schema, { ? this.addTypesToRequestDocument(this._schema, {
kind: Kind.DOCUMENT, kind: Kind.DOCUMENT,
definitions: [t], definitions: [t],
}).definitions[0] }).definitions[0]
@ -159,7 +168,7 @@ export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor<
...document, ...document,
definitions: document.definitions.map( definitions: document.definitions.map(
(t) => (t) =>
addTypesToRequestDocument(this._schema, { this.addTypesToRequestDocument(this._schema, {
kind: Kind.DOCUMENT, kind: Kind.DOCUMENT,
definitions: [t], definitions: [t],
}).definitions[0], }).definitions[0],
@ -181,4 +190,34 @@ export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor<
return super.getDocumentNodeSignature(resultType, variablesTypes, node); return super.getDocumentNodeSignature(resultType, variablesTypes, node);
} }
private addTypesToRequestDocument(
schema: GraphQLSchema,
document: DocumentNode,
) {
let finalDocument = document;
if (
this.config.supermassiveDocumentNodeOutputType === "V3" ||
this.config.supermassiveDocumentNodeOutputType === "BOTH"
) {
finalDocument = addMinimalViableSchemaToRequestDocument(
schema,
document,
{
addTo: "PROPERTY",
},
);
}
if (
this.config.supermassiveDocumentNodeOutputType === "V2" ||
this.config.supermassiveDocumentNodeOutputType === "BOTH"
) {
finalDocument = addSupermassiveLegacyTypesToRequestDocument(
schema,
document,
) as unknown as DocumentNode;
}
return finalDocument;
}
} }

Просмотреть файл

@ -1,4 +0,0 @@
{
"extends": ["../../.eslintrc.json"],
"root": true
}

Просмотреть файл

@ -1,35 +0,0 @@
{
"name": "@graphitation/supermassive-ast",
"entries": [
{
"date": "Thu, 21 Sep 2023 06:28:45 GMT",
"tag": "@graphitation/supermassive-ast_v2.0.1",
"version": "2.0.1",
"comments": {
"patch": [
{
"author": "sverre.johansen@gmail.com",
"package": "@graphitation/supermassive-ast",
"commit": "9faf12099c9af82b4e353e84fcee78e0659fb919",
"comment": "Add exports entry for types to package.json"
}
]
}
},
{
"date": "Mon, 19 Jun 2023 12:22:22 GMT",
"tag": "@graphitation/supermassive-ast_v2.0.0",
"version": "2.0.0",
"comments": {
"major": [
{
"author": "mnovikov@microsoft.com",
"package": "@graphitation/supermassive-ast",
"commit": "77593c97a1aeb72bcdbb095fe9fd803836517fe2",
"comment": "Separate ast annotator from supermassive"
}
]
}
}
]
}

Просмотреть файл

@ -1,21 +0,0 @@
# Change Log - @graphitation/supermassive-ast
This log was last generated on Thu, 21 Sep 2023 06:28:45 GMT and should not be manually modified.
<!-- Start content -->
## 2.0.1
Thu, 21 Sep 2023 06:28:45 GMT
### Patches
- Add exports entry for types to package.json (sverre.johansen@gmail.com)
## 2.0.0
Mon, 19 Jun 2023 12:22:22 GMT
### Major changes
- Separate ast annotator from supermassive (mnovikov@microsoft.com)

Просмотреть файл

@ -1,47 +0,0 @@
# @graphitation/supermassive-ast
AST definitions and AST transform for supermassive. Annotates a document so a supermassive can execute it without runtime schema.
## Transform
`addTypesToRequestDocument` converts untyped graphql-js AST node into a Supermassive typed one.
```js
function addTypesToRequestDocument(
schema: GraphQLSchema,
document: TypelessAST.DocumentNode
): TypedAST.DocumentNode
```
With `@graphitation/graphql-js-tag` and `@graphitation/ts-transform-graphql-js-tag` (in webpack config)
```js
import { buildASTSchema } from 'graphql'
import { getTransformer } from "@graphitation/ts-transform-graphql-js-tag";
import { annotateDocumentGraphQLTransform } from "@graphitation/supermassive";
// ...
{
test: /\.tsx?$/,
loader: "ts-loader",
options: {
getCustomTransformers: () => ({
before: [
getTransformer({
graphqlTagModuleExport: "graphql",
transformer: annotateDocumentGraphQLTransform(
buildASTSchema({
fs.readFileSync(
"PATH_TO_SCHEMA_TYPEDEFS.graphql",
{ encoding: "utf-8" }
),
)
),
}),
],
}),
},
},
}
```

Просмотреть файл

@ -1,45 +0,0 @@
{
"name": "@graphitation/supermassive-ast",
"license": "MIT",
"version": "2.0.1",
"main": "./src/index.ts",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/graphitation.git",
"directory": "packages/supermassive-ast"
},
"scripts": {
"build": "monorepo-scripts build",
"lint": "monorepo-scripts lint",
"test": "monorepo-scripts test",
"types": "monorepo-scripts types",
"just": "monorepo-scripts"
},
"dependencies": {},
"peerDependencies": {
"graphql": "^15.0.0 || ^16.0.0 || ^17.0.0"
},
"devDependencies": {
"graphql": "^15.6.1",
"@types/jest": "^26.0.22",
"monorepo-scripts": "*",
"ts-node": "^10.4.0"
},
"sideEffects": false,
"access": "public",
"publishConfig": {
"main": "./lib/index",
"types": "./lib/index.d.ts",
"module": "./lib/index.mjs",
"exports": {
".": {
"import": "./lib/index.mjs",
"require": "./lib/index.js",
"types": "./lib/index.d.ts"
}
},
"bin": {
"template": "./lib/cli.js"
}
}
}

Просмотреть файл

@ -1,16 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": ".tsbuildinfo",
"rootDir": "src",
"outDir": "lib"
},
"include": ["src"],
"references": [],
"ts-node": {
"transpileOnly": true,
"target": "es5",
"module": "commonjs"
}
}

Просмотреть файл

@ -1,140 +1,144 @@
export { executeWithSchema } from "./executeWithSchema";
export { export {
executeWithoutSchema, executeWithoutSchema,
isTotalExecutionResult,
isIncrementalExecutionResult, isIncrementalExecutionResult,
isTotalExecutionResult,
} from "./executeWithoutSchema"; } from "./executeWithoutSchema";
export { executeWithSchema } from "./executeWithSchema";
export { subscribeWithSchema } from "./subscribeWithSchema";
export { subscribeWithoutSchema } from "./subscribeWithoutSchema";
export { encodeASTSchema } from "./utilities/encodeASTSchema";
export { decodeASTSchema } from "./utilities/decodeASTSchema";
export { mergeSchemaDefinitions } from "./utilities/mergeSchemaDefinitions";
export { mergeResolvers } from "./utilities/mergeResolvers";
export { annotateDocumentGraphQLTransform } from "./utilities/annotateDocumentGraphQLTransform";
export {
addMinimalViableSchemaToRequestDocument,
addMinimalViableSchemaToExecutableDefinitionNode,
} from "./utilities/addMinimalViableSchemaToRequestDocument";
export { extractMinimalViableSchemaForRequestDocument } from "./utilities/extractMinimalViableSchemaForRequestDocument";
export { specifiedScalarResolvers } from "./schema/resolvers";
export { export {
isListType,
isNonNullType,
typeNameFromReference, typeNameFromReference,
typeReferenceFromName, typeReferenceFromName,
typeReferenceFromNode, typeReferenceFromNode,
isNonNullType,
isListType,
unwrap, unwrap,
unwrapAll, unwrapAll,
} from "./schema/reference"; } from "./schema/reference";
export type { export type {
TypeReference,
TypeName,
SpecTypeIndex, SpecTypeIndex,
TypeName,
TypeReference,
} from "./schema/reference"; } from "./schema/reference";
export { specifiedScalarResolvers } from "./schema/resolvers";
export { subscribeWithSchema } from "./subscribeWithSchema";
export { subscribeWithoutSchema } from "./subscribeWithoutSchema";
export {
addMinimalViableSchemaToExecutableDefinitionNode,
addMinimalViableSchemaToRequestDocument,
} from "./utilities/addMinimalViableSchemaToRequestDocument";
export { annotateDocumentGraphQLTransform } from "./utilities/annotateDocumentGraphQLTransform";
export { decodeASTSchema } from "./utilities/decodeASTSchema";
export { encodeASTSchema } from "./utilities/encodeASTSchema";
export { extractMinimalViableSchemaForRequestDocument } from "./utilities/extractMinimalViableSchemaForRequestDocument";
export { mergeResolvers } from "./utilities/mergeResolvers";
export { mergeSchemaDefinitions } from "./utilities/mergeSchemaDefinitions";
export type { ExtractMinimalViableSchemaResult } from "./utilities/extractMinimalViableSchemaForRequestDocument"; export type {
CompositeTypeTuple,
DirectiveDefinitionTuple,
DirectiveName,
DirectiveTuple,
EnumTypeDefinitionTuple,
FieldDefinition,
FieldDefinitionRecord,
FieldDefinitionTuple,
InputObjectTypeDefinitionTuple,
InputValueDefinition,
InputValueDefinitionRecord,
InputValueDefinitionTuple,
InterfaceImplementationsRecord,
InterfaceTypeDefinitionTuple,
ObjectTypeDefinitionTuple,
OperationTypes,
ScalarTypeDefinitionTuple,
SchemaDefinitions,
TypeDefinitionTuple,
TypeDefinitionsRecord,
UnionTypeDefinitionTuple,
} from "./schema/definition";
export type { export type {
AddMinimalViableSchemaToRequestDocumentOptions, AddMinimalViableSchemaToRequestDocumentOptions,
ExecutableDefinitionNodeWithInlinedSchema, ExecutableDefinitionNodeWithInlinedSchema,
} from "./utilities/addMinimalViableSchemaToRequestDocument"; } from "./utilities/addMinimalViableSchemaToRequestDocument";
export type { export type { ExtractMinimalViableSchemaResult } from "./utilities/extractMinimalViableSchemaForRequestDocument";
SchemaDefinitions,
OperationTypes,
TypeDefinitionsRecord,
TypeDefinitionTuple,
DirectiveDefinitionTuple,
InterfaceTypeDefinitionTuple,
ObjectTypeDefinitionTuple,
InputValueDefinition,
InputValueDefinitionRecord,
UnionTypeDefinitionTuple,
EnumTypeDefinitionTuple,
ScalarTypeDefinitionTuple,
InputObjectTypeDefinitionTuple,
FieldDefinition,
FieldDefinitionTuple,
CompositeTypeTuple,
FieldDefinitionRecord,
InputValueDefinitionTuple,
DirectiveTuple,
InterfaceImplementationsRecord,
DirectiveName,
} from "./schema/definition";
export type { export type {
ObjectTypeResolver,
ScalarTypeResolver,
EnumTypeResolver, EnumTypeResolver,
FunctionFieldResolver,
Resolvers,
UserResolvers,
ResolveInfo,
TotalExecutionResult,
ExecutionResult, ExecutionResult,
SubscriptionExecutionResult, FunctionFieldResolver,
IncrementalExecutionResult, IncrementalExecutionResult,
ObjectTypeResolver,
ResolveInfo,
Resolvers,
ScalarTypeResolver,
SchemaFragment, SchemaFragment,
SchemaFragmentForReturnTypeRequest,
SchemaFragmentForRuntimeTypeRequest,
SchemaFragmentLoader, SchemaFragmentLoader,
SchemaFragmentLoaderResult, SchemaFragmentLoaderResult,
SchemaFragmentRequest, SchemaFragmentRequest,
SchemaFragmentForReturnTypeRequest, SubscriptionExecutionResult,
SchemaFragmentForRuntimeTypeRequest, TotalExecutionResult,
UserResolvers,
} from "./types"; } from "./types";
export type { PromiseOrValue } from "./jsutils/PromiseOrValue"; export type { PromiseOrValue } from "./jsutils/PromiseOrValue";
export type { export type {
NameNode,
DocumentNode,
OperationDefinitionNode,
VariableDefinitionNode,
VariableNode,
SelectionSetNode,
FieldNode,
ArgumentNode, ArgumentNode,
BooleanValueNode,
DirectiveDefinitionNode,
DirectiveNode,
DocumentNode,
EnumTypeDefinitionNode,
EnumTypeExtensionNode,
EnumValueDefinitionNode,
EnumValueNode,
FieldDefinitionNode,
FieldNode,
FloatValueNode,
FragmentDefinitionNode,
FragmentSpreadNode, FragmentSpreadNode,
InlineFragmentNode, InlineFragmentNode,
FragmentDefinitionNode, InputObjectTypeDefinitionNode,
InputObjectTypeExtensionNode,
InputValueDefinitionNode,
IntValueNode, IntValueNode,
FloatValueNode, InterfaceTypeDefinitionNode,
StringValueNode, InterfaceTypeExtensionNode,
BooleanValueNode,
NullValueNode,
EnumValueNode,
ListValueNode,
ObjectValueNode,
ObjectFieldNode,
DirectiveNode,
NamedTypeNode,
ListTypeNode, ListTypeNode,
ListValueNode,
NameNode,
NamedTypeNode,
NonNullTypeNode, NonNullTypeNode,
SchemaDefinitionNode, NullValueNode,
ObjectFieldNode,
ObjectTypeDefinitionNode,
ObjectTypeExtensionNode,
ObjectValueNode,
OperationDefinitionNode,
OperationTypeDefinitionNode, OperationTypeDefinitionNode,
ScalarTypeDefinitionNode, ScalarTypeDefinitionNode,
ObjectTypeDefinitionNode,
FieldDefinitionNode,
InputValueDefinitionNode,
InterfaceTypeDefinitionNode,
UnionTypeDefinitionNode,
EnumTypeDefinitionNode,
EnumValueDefinitionNode,
InputObjectTypeDefinitionNode,
DirectiveDefinitionNode,
SchemaExtensionNode,
ScalarTypeExtensionNode, ScalarTypeExtensionNode,
ObjectTypeExtensionNode, SchemaDefinitionNode,
InterfaceTypeExtensionNode, SchemaExtensionNode,
SelectionSetNode,
StringValueNode,
UnionTypeDefinitionNode,
UnionTypeExtensionNode, UnionTypeExtensionNode,
EnumTypeExtensionNode, VariableDefinitionNode,
InputObjectTypeExtensionNode, VariableNode,
} from "graphql"; } from "graphql";
export type { export type {
BeforeFieldResolveHookArgs,
AfterFieldResolveHookArgs,
AfterFieldCompleteHookArgs,
BeforeFieldResolveHook,
AfterFieldResolveHook,
AfterFieldCompleteHook, AfterFieldCompleteHook,
AfterFieldCompleteHookArgs,
AfterFieldResolveHook,
AfterFieldResolveHookArgs,
BeforeFieldResolveHook,
BeforeFieldResolveHookArgs,
ExecutionHooks, ExecutionHooks,
} from "./hooks/types"; } from "./hooks/types";
export * as LegacyTypedAST from "./legacyAST/TypedAST";
export { addTypesToRequestDocument as addSupermassiveLegacyTypesToRequestDocument } from "./legacyAST/addTypesToRequestDocument";
export { annotateDocumentGraphQLTransform as annotateDocumentWithSupermassiveLegacyTypesGraphQLTransform } from "./legacyAST/annotateDocumentGraphQLTransform";

Просмотреть файл

@ -1,3 +1,7 @@
import {
DirectiveLocation as GraphQLDirectiveLocation,
DirectiveLocationEnum,
} from "graphql";
import { isSpecifiedScalarType } from "./resolvers"; import { isSpecifiedScalarType } from "./resolvers";
import { TypeName, TypeReference, typeNameFromReference } from "./reference"; import { TypeName, TypeReference, typeNameFromReference } from "./reference";
@ -103,18 +107,67 @@ const enum InputValueKeys {
export type InputValueDefinition = TypeReference | InputValueDefinitionTuple; export type InputValueDefinition = TypeReference | InputValueDefinitionTuple;
export type InputValueDefinitionRecord = Record<string, InputValueDefinition>; export type InputValueDefinitionRecord = Record<string, InputValueDefinition>;
const DirectiveLocation = {
QUERY: 1,
MUTATION: 2,
SUBSCRIPTION: 3,
FIELD: 4,
FRAGMENT_DEFINITION: 5,
FRAGMENT_SPREAD: 6,
INLINE_FRAGMENT: 7,
VARIABLE_DEFINITION: 8,
/** Type System Definitions */
SCHEMA: 9,
SCALAR: 10,
OBJECT: 11,
FIELD_DEFINITION: 12,
ARGUMENT_DEFINITION: 13,
INTERFACE: 14,
UNION: 15,
ENUM: 16,
ENUM_VALUE: 17,
INPUT_OBJECT: 18,
INPUT_FIELD_DEFINITION: 19,
} as const;
type DirectiveLocation =
(typeof DirectiveLocation)[keyof typeof DirectiveLocation];
const DirectiveLocationToGraphQL = {
1: GraphQLDirectiveLocation.QUERY,
2: GraphQLDirectiveLocation.MUTATION,
3: GraphQLDirectiveLocation.SUBSCRIPTION,
4: GraphQLDirectiveLocation.FIELD,
5: GraphQLDirectiveLocation.FRAGMENT_DEFINITION,
6: GraphQLDirectiveLocation.FRAGMENT_SPREAD,
7: GraphQLDirectiveLocation.INLINE_FRAGMENT,
8: GraphQLDirectiveLocation.VARIABLE_DEFINITION,
/** Type System Definitions */
9: GraphQLDirectiveLocation.SCHEMA,
10: GraphQLDirectiveLocation.SCALAR,
11: GraphQLDirectiveLocation.OBJECT,
12: GraphQLDirectiveLocation.FIELD_DEFINITION,
13: GraphQLDirectiveLocation.ARGUMENT_DEFINITION,
14: GraphQLDirectiveLocation.INTERFACE,
15: GraphQLDirectiveLocation.UNION,
16: GraphQLDirectiveLocation.ENUM,
17: GraphQLDirectiveLocation.ENUM_VALUE,
18: GraphQLDirectiveLocation.INPUT_OBJECT,
19: GraphQLDirectiveLocation.INPUT_FIELD_DEFINITION,
} as const;
export type DirectiveName = string; export type DirectiveName = string;
export type DirectiveTuple = [ export type DirectiveTuple = [
name: DirectiveName, name: DirectiveName,
arguments?: Record<string, unknown>, // JS values (cannot be a variable inside schema definition, so it is fine) arguments: Record<string, unknown>, // JS values (cannot be a variable inside schema definition, so it is fine)
]; ];
export type DirectiveDefinitionTuple = [ export type DirectiveDefinitionTuple = [
name: DirectiveName, name: DirectiveName,
locations: DirectiveLocation[],
arguments?: InputValueDefinitionRecord, arguments?: InputValueDefinitionRecord,
]; ];
const enum DirectiveKeys { const enum DirectiveKeys {
name = 0, name = 0,
arguments = 1, locations = 1,
arguments = 2,
} }
export type TypeDefinitionsRecord = Record<TypeName, TypeDefinitionTuple>; export type TypeDefinitionsRecord = Record<TypeName, TypeDefinitionTuple>;
@ -372,6 +425,23 @@ export function getDirectiveName(tuple: DirectiveDefinitionTuple): string {
return tuple[DirectiveKeys.name]; return tuple[DirectiveKeys.name];
} }
export function getDirectiveLocations(
tuple: DirectiveDefinitionTuple,
): DirectiveLocation[] {
return tuple[DirectiveKeys.locations];
}
export function encodeDirectiveLocation(
location: DirectiveLocationEnum,
): DirectiveLocation {
return DirectiveLocation[location];
}
export function decodeDirectiveLocation(
location: DirectiveLocation,
): DirectiveLocationEnum {
return DirectiveLocationToGraphQL[location];
}
export function isObjectTypeDefinition( export function isObjectTypeDefinition(
type: TypeDefinitionTuple, type: TypeDefinitionTuple,
): type is ObjectTypeDefinitionTuple { ): type is ObjectTypeDefinitionTuple {
@ -532,9 +602,14 @@ export function getUnionTypeMembers(
return tuple[UnionKeys.types]; return tuple[UnionKeys.types];
} }
export function getDefinitionArguments( export function getFieldArguments(
def: FieldDefinition | DirectiveDefinitionTuple, def: FieldDefinition,
): InputValueDefinitionRecord | undefined { ): InputValueDefinitionRecord | undefined {
// Note: both FieldDefinition and DirectiveDefinition store arguments at the same position
return Array.isArray(def) ? def[FieldKeys.arguments] : undefined; return Array.isArray(def) ? def[FieldKeys.arguments] : undefined;
} }
export function getDirectiveArguments(
def: DirectiveDefinitionTuple,
): InputValueDefinitionRecord | undefined {
return Array.isArray(def) ? def[DirectiveKeys.arguments] : undefined;
}

Просмотреть файл

@ -1,6 +1,8 @@
import { DirectiveLocation } from "graphql";
import { import {
DirectiveDefinitionTuple, DirectiveDefinitionTuple,
DirectiveName, DirectiveName,
encodeDirectiveLocation,
getDirectiveName, getDirectiveName,
} from "./definition"; } from "./definition";
@ -9,8 +11,12 @@ import {
*/ */
export const GraphQLIncludeDirective: DirectiveDefinitionTuple = [ export const GraphQLIncludeDirective: DirectiveDefinitionTuple = [
"include", "include",
[
DirectiveLocation.FIELD,
DirectiveLocation.FRAGMENT_SPREAD,
DirectiveLocation.INLINE_FRAGMENT,
].map(encodeDirectiveLocation),
{ if: "Boolean!" }, { if: "Boolean!" },
// TODO: locations for all directives (maybe not - they are irrelevant for runtime)?
]; ];
/** /**
@ -18,6 +24,11 @@ export const GraphQLIncludeDirective: DirectiveDefinitionTuple = [
*/ */
export const GraphQLSkipDirective: DirectiveDefinitionTuple = [ export const GraphQLSkipDirective: DirectiveDefinitionTuple = [
"skip", "skip",
[
DirectiveLocation.FIELD,
DirectiveLocation.FRAGMENT_SPREAD,
DirectiveLocation.INLINE_FRAGMENT,
].map(encodeDirectiveLocation),
{ if: "Boolean!" }, { if: "Boolean!" },
]; ];
@ -25,12 +36,12 @@ export const GraphQLSkipDirective: DirectiveDefinitionTuple = [
* Constant string used for default reason for a deprecation. * Constant string used for default reason for a deprecation.
*/ */
export const DEFAULT_DEPRECATION_REASON = "No longer supported"; export const DEFAULT_DEPRECATION_REASON = "No longer supported";
/** /**
* Used to declare element of a GraphQL schema as deprecated. * Used to declare element of a GraphQL schema as deprecated.
*/ */
export const GraphQLDeprecatedDirective: DirectiveDefinitionTuple = [ export const GraphQLDeprecatedDirective: DirectiveDefinitionTuple = [
"deprecated", "deprecated",
[DirectiveLocation.FIELD_DEFINITION].map(encodeDirectiveLocation),
{ reason: ["String", DEFAULT_DEPRECATION_REASON] }, { reason: ["String", DEFAULT_DEPRECATION_REASON] },
]; ];
@ -39,6 +50,7 @@ export const GraphQLDeprecatedDirective: DirectiveDefinitionTuple = [
*/ */
export const GraphQLSpecifiedByDirective: DirectiveDefinitionTuple = [ export const GraphQLSpecifiedByDirective: DirectiveDefinitionTuple = [
"specifiedBy", "specifiedBy",
[DirectiveLocation.INPUT_OBJECT].map(encodeDirectiveLocation),
{ url: "String!" }, { url: "String!" },
]; ];
@ -47,6 +59,9 @@ export const GraphQLSpecifiedByDirective: DirectiveDefinitionTuple = [
*/ */
export const GraphQLDeferDirective: DirectiveDefinitionTuple = [ export const GraphQLDeferDirective: DirectiveDefinitionTuple = [
"defer", "defer",
[DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT].map(
encodeDirectiveLocation,
),
{ if: ["Boolean!", true], label: "String" }, { if: ["Boolean!", true], label: "String" },
]; ];
@ -55,6 +70,7 @@ export const GraphQLDeferDirective: DirectiveDefinitionTuple = [
*/ */
export const GraphQLStreamDirective: DirectiveDefinitionTuple = [ export const GraphQLStreamDirective: DirectiveDefinitionTuple = [
"stream", "stream",
[DirectiveLocation.FIELD].map(encodeDirectiveLocation),
{ if: ["Boolean!", true], label: "String", initialCount: ["Int", 0] }, { if: ["Boolean!", true], label: "String", initialCount: ["Int", 0] },
]; ];
@ -70,16 +86,19 @@ export const specifiedDirectives: ReadonlyArray<DirectiveDefinitionTuple> =
GraphQLDeferDirective, GraphQLDeferDirective,
GraphQLStreamDirective, GraphQLStreamDirective,
]); ]);
export const SUPERMASSIVE_SCHEMA_DIRECTIVE_NAME = "schema"; export const SUPERMASSIVE_SCHEMA_DIRECTIVE_NAME = "schema";
export const SUPERMASSIVE_SCHEMA_DIRECTIVE_DEFINITIONS_ARGUMENT_NAME = export const SUPERMASSIVE_SCHEMA_DIRECTIVE_DEFINITIONS_ARGUMENT_NAME =
"definitions"; "definitions";
/** /**
* Used to conditionally stream list fields. * Used to conditionally stream list fields.
*/ */
export const SupermassiveSchemaDirective: DirectiveDefinitionTuple = [ export const SupermassiveSchemaDirective: DirectiveDefinitionTuple = [
SUPERMASSIVE_SCHEMA_DIRECTIVE_NAME, SUPERMASSIVE_SCHEMA_DIRECTIVE_NAME,
[
DirectiveLocation.QUERY,
DirectiveLocation.MUTATION,
DirectiveLocation.SUBSCRIPTION,
].map(encodeDirectiveLocation),
{ [SUPERMASSIVE_SCHEMA_DIRECTIVE_DEFINITIONS_ARGUMENT_NAME]: "String!" }, // Essentially JSON { [SUPERMASSIVE_SCHEMA_DIRECTIVE_DEFINITIONS_ARGUMENT_NAME]: "String!" }, // Essentially JSON
]; ];
@ -93,7 +112,6 @@ export function isKnownDirective(
name === SUPERMASSIVE_SCHEMA_DIRECTIVE_NAME name === SUPERMASSIVE_SCHEMA_DIRECTIVE_NAME
); );
} }
export function isSpecifiedDirective( export function isSpecifiedDirective(
directive: DirectiveName | DirectiveDefinitionTuple, directive: DirectiveName | DirectiveDefinitionTuple,
): boolean { ): boolean {

Просмотреть файл

@ -6,24 +6,43 @@ exports[`encodeASTSchema correctly encodes kitchen sink AST schema 1`] = `
"directives": [ "directives": [
[ [
"skip", "skip",
[
4,
6,
7,
],
{ {
"if": 7, "if": 7,
}, },
], ],
[ [
"include", "include",
[
4,
6,
7,
],
{ {
"if": 7, "if": 7,
}, },
], ],
[ [
"include2", "include2",
[
4,
6,
7,
],
{ {
"if": 7, "if": 7,
}, },
], ],
[ [
"myRepeatableDir", "myRepeatableDir",
[
11,
14,
],
{ {
"name": 6, "name": 6,
}, },
@ -341,6 +360,9 @@ exports[`encodeASTSchema correctly encodes swapi AST schema 1`] = `
"directives": [ "directives": [
[ [
"i18n", "i18n",
[
1,
],
{ {
"locale": 1, "locale": 1,
}, },

Просмотреть файл

@ -37,7 +37,7 @@ describe(addMinimalViableSchemaToRequestDocument, () => {
}`, }`,
); );
expect(printedDoc).toMatchInlineSnapshot(` expect(printedDoc).toMatchInlineSnapshot(`
"query @i18n(locale: "en_US") @schema(definitions: "{\\"types\\":{\\"Query\\":[2,{\\"film\\":[\\"Film\\",{\\"id\\":10}]}],\\"Film\\":[2,{},[\\"Node\\"]]},\\"directives\\":[[\\"i18n\\",{\\"locale\\":1}]]}") { "query @i18n(locale: "en_US") @schema(definitions: "{\\"types\\":{\\"Query\\":[2,{\\"film\\":[\\"Film\\",{\\"id\\":10}]}],\\"Film\\":[2,{},[\\"Node\\"]]},\\"directives\\":[[\\"i18n\\",[1],{\\"locale\\":1}]]}") {
film(id: 42) { film(id: 42) {
__typename __typename
} }

Просмотреть файл

@ -15,6 +15,7 @@ import {
UnionTypeExtensionNode, UnionTypeExtensionNode,
EnumTypeExtensionNode, EnumTypeExtensionNode,
ScalarTypeExtensionNode, ScalarTypeExtensionNode,
DirectiveLocationEnum,
} from "graphql"; } from "graphql";
import { import {
DirectiveDefinitionTuple, DirectiveDefinitionTuple,
@ -35,6 +36,7 @@ import {
createInputObjectTypeDefinition, createInputObjectTypeDefinition,
createInterfaceTypeDefinition, createInterfaceTypeDefinition,
createUnionTypeDefinition, createUnionTypeDefinition,
encodeDirectiveLocation,
} from "../schema/definition"; } from "../schema/definition";
import { typeReferenceFromNode, TypeReference } from "../schema/reference"; import { typeReferenceFromNode, TypeReference } from "../schema/reference";
import { valueFromASTUntyped } from "./valueFromASTUntyped"; import { valueFromASTUntyped } from "./valueFromASTUntyped";
@ -194,7 +196,20 @@ function encodeInputValue(
function encodeDirective( function encodeDirective(
node: DirectiveDefinitionNode, node: DirectiveDefinitionNode,
): DirectiveDefinitionTuple { ): DirectiveDefinitionTuple {
return !node.arguments?.length if (node.arguments?.length) {
? [node.name.value] return [
: [node.name.value, encodeArguments(node)]; node.name.value,
node.locations.map((node) =>
encodeDirectiveLocation(node.value as DirectiveLocationEnum),
),
encodeArguments(node),
];
} else {
return [
node.name.value,
node.locations.map((node) =>
encodeDirectiveLocation(node.value as DirectiveLocationEnum),
),
];
}
} }

Просмотреть файл

@ -36,6 +36,7 @@ import {
createScalarTypeDefinition, createScalarTypeDefinition,
createUnionTypeDefinition, createUnionTypeDefinition,
DirectiveDefinitionTuple, DirectiveDefinitionTuple,
encodeDirectiveLocation,
EnumTypeDefinitionTuple, EnumTypeDefinitionTuple,
FieldDefinition, FieldDefinition,
getDirectiveDefinitionArgs, getDirectiveDefinitionArgs,
@ -76,7 +77,7 @@ export function extractMinimalViableSchemaForRequestDocument(
schema: GraphQLSchema, schema: GraphQLSchema,
requestDocument: DocumentNode, requestDocument: DocumentNode,
): ExtractMinimalViableSchemaResult { ): ExtractMinimalViableSchemaResult {
const types: TypeDefinitionsRecord = Object.create(null); const types: TypeDefinitionsRecord = {};
const directives: DirectiveDefinitionTuple[] = []; const directives: DirectiveDefinitionTuple[] = [];
const unknownDirectives: DirectiveNode[] = []; const unknownDirectives: DirectiveNode[] = [];
@ -244,7 +245,7 @@ function addDirective(
const name = directive.name; const name = directive.name;
let tuple = directives.find((d) => getDirectiveName(d) === name); let tuple = directives.find((d) => getDirectiveName(d) === name);
if (!tuple) { if (!tuple) {
tuple = [directive.name]; tuple = [directive.name, directive.locations.map(encodeDirectiveLocation)];
directives.push(tuple); directives.push(tuple);
} }

Просмотреть файл

@ -13,12 +13,13 @@ import { ExecutionContext } from "./executeWithoutSchema";
import { import {
DirectiveDefinitionTuple, DirectiveDefinitionTuple,
FieldDefinition, FieldDefinition,
getDefinitionArguments, getFieldArguments,
getDirectiveName, getDirectiveName,
getInputDefaultValue, getInputDefaultValue,
getInputValueTypeReference, getInputValueTypeReference,
isDefined, isDefined,
isInputType, isInputType,
getDirectiveArguments,
} from "./schema/definition"; } from "./schema/definition";
import { valueFromAST } from "./utilities/valueFromAST"; import { valueFromAST } from "./utilities/valueFromAST";
import { coerceInputValue } from "./utilities/coerceInputValue"; import { coerceInputValue } from "./utilities/coerceInputValue";
@ -168,7 +169,10 @@ export function getArgumentValues(
): { [argument: string]: unknown } { ): { [argument: string]: unknown } {
const definitions = exeContext.schemaFragment.definitions; const definitions = exeContext.schemaFragment.definitions;
const coercedValues: { [argument: string]: unknown } = {}; const coercedValues: { [argument: string]: unknown } = {};
const argumentDefs = getDefinitionArguments(def); const argumentDefs =
node.kind === Kind.FIELD
? getFieldArguments(def as FieldDefinition)
: getDirectiveArguments(def as DirectiveDefinitionTuple);
if (!argumentDefs) { if (!argumentDefs) {
return coercedValues; return coercedValues;
} }

Просмотреть файл

@ -1692,6 +1692,11 @@
"@n1ru4l/push-pull-async-iterable-iterator" "^3.1.0" "@n1ru4l/push-pull-async-iterable-iterator" "^3.1.0"
meros "^1.1.4" meros "^1.1.4"
"@graphitation/supermassive-ast@*":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@graphitation/supermassive-ast/-/supermassive-ast-2.0.0.tgz#d49155c6b7213811d99b8609d496d6c460a4d1e2"
integrity sha512-YXguoTpIMI/ISavl98CrSOLohYVMr3JmPRhT6Qg4q6MfaonSKajvva9OUFOxxEmuAaEXfjhZ33ySWaNSu/XcLQ==
"@graphql-codegen/add@^3.1.0": "@graphql-codegen/add@^3.1.0":
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.1.0.tgz#cd02fd6d80a7f62839cb27160b62e48366a237c5" resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.1.0.tgz#cd02fd6d80a7f62839cb27160b62e48366a237c5"