From 9f34ab7d6598c39a58b76f09901c95b145a2b2fc Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 23 Oct 2024 20:00:24 +0200 Subject: [PATCH 01/19] Alpha release --- .azure-devops/graphitation-release.yml | 1 + package.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.azure-devops/graphitation-release.yml b/.azure-devops/graphitation-release.yml index e5ff2a284..6d400ddce 100644 --- a/.azure-devops/graphitation-release.yml +++ b/.azure-devops/graphitation-release.yml @@ -2,6 +2,7 @@ pr: none trigger: - main - alloy/relay-apollo-duct-tape + - jvejr/context-and-async-hooks-alpha variables: - group: InfoSec-SecurityResults diff --git a/package.json b/package.json index 6d0c7990b..ddb51adef 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,10 @@ "lint": "lage lint --continue", "lage": "lage", "ci": "yarn lage build types test lint && yarn checkchange", - "beachball": "beachball -b origin/main", + "beachball": "beachball -b origin/jvejr/context-and-async-hooks-alpha", "change": "yarn beachball change", "checkchange": "yarn beachball check", - "release": "yarn beachball publish -t latest", + "release": "yarn beachball publish -t alpha", "postinstall": "patch-package" }, "devDependencies": { From da0b7755de235224f65e6d372e35e5e1aeb2ef33 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 23 Oct 2024 20:45:59 +0200 Subject: [PATCH 02/19] Context alpha test [DONT MERGE] (#459) * [SUPERMASSIVE] async before hooks and context mapping added --- .vscode/settings.json | 5 +- ...-655d32f2-7b28-45d9-8409-768de8b551fe.json | 7 + ...-87e2107d-b60a-465e-a377-903c60f7550f.json | 7 + ...-d1d73434-b834-44cc-967d-ffeb17e7ea76.json | 7 + packages/cli/src/supermassive.ts | 63 +- .../supermassive/src/__tests__/hooks.test.ts | 594 ++++++++++++++- .../supermassive/src/executeWithoutSchema.ts | 96 ++- packages/supermassive/src/hooks/types.ts | 10 +- .../ts-codegen/src/__tests__/context.test.ts | 683 ++++++++++++++++++ packages/ts-codegen/src/codegen.ts | 3 + packages/ts-codegen/src/context/index.ts | 193 ++++- packages/ts-codegen/src/resolvers.ts | 170 ++++- packages/ts-codegen/src/utilities.ts | 10 +- 13 files changed, 1737 insertions(+), 111 deletions(-) create mode 100644 change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json create mode 100644 change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json create mode 100644 change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json create mode 100644 packages/ts-codegen/src/__tests__/context.test.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 8892811ad..3b750d0cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, - "cSpell.words": ["Daichi", "fukuda", "Kadji"] + "cSpell.words": ["Daichi", "fukuda", "Kadji"], + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } } diff --git a/change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json b/change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json new file mode 100644 index 000000000..e6ded4330 --- /dev/null +++ b/change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Context mapping", + "packageName": "@graphitation/cli", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json b/change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json new file mode 100644 index 000000000..3d7eb954f --- /dev/null +++ b/change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "async before hooks added", + "packageName": "@graphitation/supermassive", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json b/change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json new file mode 100644 index 000000000..640a00268 --- /dev/null +++ b/change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "context mapping", + "packageName": "@graphitation/ts-codegen", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/cli/src/supermassive.ts b/packages/cli/src/supermassive.ts index 2276cb0fe..d998dbcb5 100644 --- a/packages/cli/src/supermassive.ts +++ b/packages/cli/src/supermassive.ts @@ -19,6 +19,7 @@ type GenerateInterfacesOptions = { enumMigrationJsonFile?: string; enumMigrationExceptionsJsonFile?: string; generateOnlyEnums?: boolean; + contextMappingFile?: string; scope?: string; }; @@ -48,6 +49,10 @@ export function supermassive(): Command { "-ci, --context-import [contextImport]", "from where to import context", ) + .option( + "-cm, --context-mapping-file [contextMappingFile]", + "context mapping file", + ) .option("-cn, --context-name [contextName]", "Context name") .option("-ei, --enums-import [enumsImport]", "from where to import enums") .option("-l, --legacy", "generate legacy types") @@ -58,14 +63,6 @@ export function supermassive(): Command { ) .option("--generate-only-enums", "Generate only enum file") .option("--scope [scope]", "generate models only for scope") - .option( - "--enum-migration-json-file [enumMigrationJsonFile]", - "File containing array of enum names, which should be migrated to string unions", - ) - .option( - "--enum-migration-exceptions-json-file [enumMigrationExceptionsJsonFile]", - "File containing array of enum names, which should remain typescript enums", - ) .description("generate interfaces and models") .action( async (inputs: Array, options: GenerateInterfacesOptions) => { @@ -130,42 +127,25 @@ async function generateInterfaces( path.dirname(fullPath), options.outputDir ? options.outputDir : "__generated__", ); - let enumNamesToMigrate; - let enumNamesToKeep; - if (options.enumMigrationJsonFile) { - const content = JSON.parse( - await fs.readFile( - path.join(process.cwd(), options.enumMigrationJsonFile), - { - encoding: "utf-8", - }, - ), - ); - - if (!Array.isArray(content)) { - throw new Error("enumMigrationJsonFile doesn't contain an array"); - } - - enumNamesToMigrate = content; - } - if (options.enumMigrationExceptionsJsonFile) { - const content = JSON.parse( - await fs.readFile( - path.join(process.cwd(), options.enumMigrationExceptionsJsonFile), - { - encoding: "utf-8", - }, - ), - ); - - if (!Array.isArray(content)) { - throw new Error( - "enumMigrationExceptionsJsonFile doesn't contain an array", + const { contextMappingFile } = options; + let contextMappingContent: Record | null = null; + let fullContextMappingFilePath: string; + if (contextMappingFile) { + if (path.isAbsolute(contextMappingFile)) { + fullContextMappingFilePath = contextMappingFile; + } else { + fullContextMappingFilePath = path.join( + process.cwd(), + contextMappingFile, ); } - enumNamesToKeep = content; + if (fsSync.existsSync(fullContextMappingFilePath)) { + contextMappingContent = JSON.parse( + await fs.readFile(fullContextMappingFilePath, { encoding: "utf-8" }), + ); + } } const result = generateTS(document, { @@ -178,9 +158,8 @@ async function generateInterfaces( legacyNoModelsForObjects: !!options.legacyModels, useStringUnionsInsteadOfEnums: !!options.useStringUnionsInsteadOfEnums, generateOnlyEnums: !!options.generateOnlyEnums, - enumNamesToMigrate, - enumNamesToKeep, modelScope: options.scope || null, + contextMappingContent: contextMappingContent, }); await fs.mkdir(outputPath, { recursive: true }); diff --git a/packages/supermassive/src/__tests__/hooks.test.ts b/packages/supermassive/src/__tests__/hooks.test.ts index 33d20955a..65a1c067f 100644 --- a/packages/supermassive/src/__tests__/hooks.test.ts +++ b/packages/supermassive/src/__tests__/hooks.test.ts @@ -108,28 +108,7 @@ describe.each([ // BOE: beforeOperationExecute // BSE: beforeSubscriptionEventEmit // ABR: afterBuildResponse - const hooks: ExecutionHooks = { - beforeOperationExecute: jest - .fn() - .mockImplementation( - ({ operation }: BaseExecuteOperationHookArgs) => { - hookCalls.push(`BOE|${operation.name?.value}`); - }, - ), - beforeSubscriptionEventEmit: jest - .fn() - .mockImplementation( - ({ - operation, - eventPayload, - }: BeforeSubscriptionEventEmitHookArgs) => { - hookCalls.push( - `BSE|${operation.name?.value}|${ - (eventPayload as any).emitPersons.name - }`, - ); - }, - ), + const syncAfterHooks: ExecutionHooks = { afterBuildResponse: jest .fn() .mockImplementation( @@ -137,13 +116,6 @@ describe.each([ hookCalls.push(`ABR|${operation.name?.value}`); }, ), - beforeFieldResolve: jest - .fn() - .mockImplementation( - ({ resolveInfo }: BaseExecuteFieldHookArgs) => { - hookCalls.push(`BFR|${pathToArray(resolveInfo.path).join(".")}`); - }, - ), afterFieldResolve: jest .fn() .mockImplementation( @@ -186,6 +158,68 @@ describe.each([ ), }; + const syncBeforeHooks: ExecutionHooks = { + beforeOperationExecute: jest + .fn() + .mockImplementation( + ({ operation }: BaseExecuteOperationHookArgs) => { + hookCalls.push(`BOE|${operation.name?.value}`); + }, + ), + beforeSubscriptionEventEmit: jest + .fn() + .mockImplementation( + ({ + operation, + eventPayload, + }: BeforeSubscriptionEventEmitHookArgs) => { + hookCalls.push( + `BSE|${operation.name?.value}|${ + (eventPayload as any).emitPersons.name + }`, + ); + }, + ), + beforeFieldResolve: jest + .fn() + .mockImplementation( + ({ resolveInfo }: BaseExecuteFieldHookArgs) => { + hookCalls.push(`BFR|${pathToArray(resolveInfo.path).join(".")}`); + }, + ), + }; + + const asyncBeforeHooks: ExecutionHooks = { + beforeOperationExecute: jest + .fn() + .mockImplementation( + async ({ operation }: BaseExecuteOperationHookArgs) => { + hookCalls.push(`BOE|${operation.name?.value}`); + }, + ), + beforeSubscriptionEventEmit: jest + .fn() + .mockImplementation( + async ({ + operation, + eventPayload, + }: BeforeSubscriptionEventEmitHookArgs) => { + hookCalls.push( + `BSE|${operation.name?.value}|${ + (eventPayload as any).emitPersons.name + }`, + ); + }, + ), + beforeFieldResolve: jest + .fn() + .mockImplementation( + async ({ resolveInfo }: BaseExecuteFieldHookArgs) => { + hookCalls.push(`BFR|${pathToArray(resolveInfo.path).join(".")}`); + }, + ), + }; + beforeEach(() => { jest.clearAllMocks(); hookCalls = []; @@ -520,6 +554,374 @@ describe.each([ }, ]; + const asyncHooksTestCases: Array = [ + { + name: "succeeded sync resolver with async hooks", + document: `query GetPerson + { + person(id: 1) { + name + } + }`, + resolvers: { + ...resolvers, + Person: { + name: (parent: any, _args: unknown, _context: any) => { + return parent.name; + }, + }, + } as UserResolvers, + expectedHookCalls: [ + "BOE|GetPerson", + "BFR|person", + "AFR|person|[object]|undefined", + "BFR|person.name", + "AFR|person.name|Luke Skywalker|undefined", + "AFC|person.name|Luke Skywalker|undefined", + "AFC|person|[object]|undefined", + "ABR|GetPerson", + ], + resultHasErrors: false, + isStrictHookCallsOrder: true, + }, + { + name: "succeeded async resolver with async hooks", + document: `query GetPerson + { + person(id: 1) { + name + } + }`, + resolvers: { + ...resolvers, + Person: { + name: async (parent: any, _args: unknown, _context: any) => { + return Promise.resolve(parent.name); + }, + }, + } as UserResolvers, + expectedHookCalls: [ + "BOE|GetPerson", + "BFR|person", + "AFR|person|[object]|undefined", + "BFR|person.name", + "AFR|person.name|Luke Skywalker|undefined", + "AFC|person.name|Luke Skywalker|undefined", + "AFC|person|[object]|undefined", + "ABR|GetPerson", + ], + resultHasErrors: false, + isStrictHookCallsOrder: false, + }, + { + name: "error in sync resolver for nullable field with async hooks", + document: `query GetFilm + { + film(id: 1) { + producer + } + }`, + resolvers: { + ...resolvers, + Film: { + producer: (_parent: any, _args: unknown, _context: any) => { + throw new Error("Resolver error"); + }, + }, + } as UserResolvers, + expectedHookCalls: [ + "BOE|GetFilm", + "BFR|film", + "AFR|film|[object]|undefined", + "BFR|film.producer", + "AFR|film.producer|undefined|Resolver error", + "AFC|film.producer|undefined|Resolver error", + "AFC|film|[object]|undefined", + "ABR|GetFilm", + ], + resultHasErrors: true, + isStrictHookCallsOrder: true, + }, + { + name: "error in async resolver for nullable field with async hooks", + document: `query GetFilm + { + film(id: 1) { + producer + } + }`, + resolvers: { + ...resolvers, + Film: { + producer: async (_parent: any, _args: unknown, _context: any) => { + return Promise.reject(new Error("Resolver error")); + }, + }, + } as UserResolvers, + expectedHookCalls: [ + "BOE|GetFilm", + "BFR|film", + "AFR|film|[object]|undefined", + "BFR|film.producer", + "AFR|film.producer|undefined|Resolver error", + "AFC|film.producer|undefined|Resolver error", + "AFC|film|[object]|undefined", + "ABR|GetFilm", + ], + resultHasErrors: true, + isStrictHookCallsOrder: false, + }, + { + name: "error in sync resolver for non-nullable field with async hooks", + document: `query GetFilm + { + film(id: 1) { + title + } + }`, + resolvers: { + ...resolvers, + Film: { + title: (_parent: any, _args: unknown, _context: any) => { + throw new Error("Resolver error"); + }, + }, + } as UserResolvers, + expectedHookCalls: [ + "BOE|GetFilm", + "BFR|film", + "AFR|film|[object]|undefined", + "BFR|film.title", + "AFR|film.title|undefined|Resolver error", + "AFC|film.title|undefined|Resolver error", + "AFC|film|undefined|Resolver error", + "ABR|GetFilm", + ], + resultHasErrors: true, + isStrictHookCallsOrder: true, + }, + { + name: "error in async resolver for non-nullable field with async hooks", + document: `query GetFilm + { + film(id: 1) { + title + } + }`, + resolvers: { + ...resolvers, + Film: { + title: async (_parent: any, _args: unknown, _context: any) => { + return Promise.reject(new Error("Resolver error")); + }, + }, + } as UserResolvers, + expectedHookCalls: [ + "BOE|GetFilm", + "BFR|film", + "AFR|film|[object]|undefined", + "BFR|film.title", + "AFR|film.title|undefined|Resolver error", + "AFC|film.title|undefined|Resolver error", + "AFC|film|undefined|Resolver error", + "ABR|GetFilm", + ], + resultHasErrors: true, + isStrictHookCallsOrder: false, + }, + { + name: "do not invoke hooks for the field with default resolver with async hooks", + document: `query GetFilm + { + film(id: 1) { + title + } + }`, + resolvers: resolvers as UserResolvers, + expectedHookCalls: [ + "BOE|GetFilm", + "BFR|film", + "AFR|film|[object]|undefined", + "AFC|film|[object]|undefined", + "ABR|GetFilm", + ], + resultHasErrors: false, + isStrictHookCallsOrder: true, + }, + { + name: "do not invoke hooks for the __typename with async hooks", + document: `query GetFilm + { + film(id: 1) { + __typename + title + } + }`, + resolvers: resolvers as UserResolvers, + expectedHookCalls: [ + "BOE|GetFilm", + "BFR|film", + "AFR|film|[object]|undefined", + "AFC|film|[object]|undefined", + "ABR|GetFilm", + ], + resultHasErrors: false, + isStrictHookCallsOrder: true, + }, + { + name: "multiple root fields in selection set with async hooks", + document: `query GetFilmAndPerson + { + film(id: 1) { + title + } + person(id: 1) { + name + } + }`, + resolvers: resolvers as UserResolvers, + expectedHookCalls: [ + "BOE|GetFilmAndPerson", + "BFR|film", + "BFR|person", + "AFR|film|[object]|undefined", + "AFR|person|[object]|undefined", + "AFC|film|[object]|undefined", + "AFC|person|[object]|undefined", + "ABR|GetFilmAndPerson", + ], + resultHasErrors: false, + isStrictHookCallsOrder: true, + }, + { + name: "subscription hooks", + document: `subscription EmitPersons($limit: Int!) + { + emitPersons(limit: $limit) { + name + } + }`, + variables: { + limit: 3, + }, + resolvers: resolvers as UserResolvers, + expectedHookCalls: [ + "BOE|EmitPersons", + "BFR|emitPersons", + "AFR|emitPersons|[object]|undefined", + "BSE|EmitPersons|Luke Skywalker", + "ABR|EmitPersons", + "BSE|EmitPersons|C-3PO", + "ABR|EmitPersons", + "BSE|EmitPersons|R2-D2", + "ABR|EmitPersons", + ], + resultHasErrors: false, + isStrictHookCallsOrder: true, + }, + { + name: "error in sync subscribe() resolver with async hooks", + document: `subscription EmitPersons($limit: Int!) + { + emitPersons(limit: $limit) { + name + } + }`, + resolvers: { + ...resolvers, + Subscription: { + emitPersons: { + subscribe: (_parent: any, _args: unknown, _context: any) => { + throw new Error("Subscribe error"); + }, + }, + }, + } as UserResolvers, + variables: { + limit: 1, + }, + expectedHookCalls: [ + "BOE|EmitPersons", + "BFR|emitPersons", + "AFR|emitPersons|undefined|Subscribe error", + ], + resultHasErrors: true, + isStrictHookCallsOrder: true, + }, + { + name: "error in async subscribe() resolver with async hooks", + document: `subscription EmitPersons($limit: Int!) + { + emitPersons(limit: $limit) { + name + } + }`, + resolvers: { + ...resolvers, + Subscription: { + emitPersons: { + subscribe: async ( + _parent: any, + _args: unknown, + _context: any, + ) => { + return Promise.reject(new Error("Subscribe error")); + }, + }, + }, + } as UserResolvers, + variables: { + limit: 1, + }, + expectedHookCalls: [ + "BOE|EmitPersons", + "BFR|emitPersons", + "AFR|emitPersons|undefined|Subscribe error", + ], + resultHasErrors: true, + isStrictHookCallsOrder: true, + }, + ]; + + it.each(asyncHooksTestCases)( + "$name", + async ({ + document, + resolvers, + expectedHookCalls, + resultHasErrors, + isStrictHookCallsOrder, + variables, + }) => { + expect.assertions(4); + const parsedDocument = parse(document); + + const result = await drainExecution( + await execute( + parsedDocument, + resolvers, + { ...asyncBeforeHooks, ...syncAfterHooks }, + variables, + ), + ); + + if (isStrictHookCallsOrder) { + expect(hookCalls).toEqual(expectedHookCalls); + } else { + // for async resolvers order of resolving isn't strict, + // so just verify whether corresponding hook calls happened + expect(hookCalls).toEqual(expect.arrayContaining(expectedHookCalls)); + } + expect(hookCalls).toHaveLength(expectedHookCalls.length); + expect(isTotalExecutionResult(result as TotalExecutionResult)).toBe( + true, + ); + expect(((result as TotalExecutionResult).errors?.length ?? 0) > 0).toBe( + resultHasErrors, + ); + }, + ); + it.each(testCases)( "$name", async ({ @@ -534,7 +936,12 @@ describe.each([ const parsedDocument = parse(document); const result = await drainExecution( - await execute(parsedDocument, resolvers, hooks, variables), + await execute( + parsedDocument, + resolvers, + { ...syncAfterHooks, ...syncBeforeHooks }, + variables, + ), ); if (isStrictHookCallsOrder) { @@ -553,6 +960,60 @@ describe.each([ ); }, ); + + test("BFR returns promise conditionally", async () => { + const result = await drainExecution( + await execute( + parse(`query GetFilmAndPerson + { + film(id: 1) { + title + } + person(id: 1) { + name + } + }`), + resolvers as UserResolvers, + { + ...asyncBeforeHooks, + ...syncAfterHooks, + beforeFieldResolve: jest + .fn() + .mockImplementation( + ({ resolveInfo }: BaseExecuteFieldHookArgs) => { + hookCalls.push( + `BFR|${pathToArray(resolveInfo.path).join(".")}`, + ); + if (resolveInfo.fieldName === "film") + return Promise.resolve(); + return; + }, + ), + }, + { + limit: 1, + }, + ), + ); + + const expectedHookCalls = [ + "BOE|GetFilmAndPerson", + "BFR|film", + "BFR|person", + "AFR|person|[object]|undefined", + "AFC|person|[object]|undefined", + "AFR|film|[object]|undefined", + "AFC|film|[object]|undefined", + "ABR|GetFilmAndPerson", + ]; + expect(hookCalls).toEqual(expectedHookCalls); + + expect(hookCalls).toHaveLength(expectedHookCalls.length); + expect(isTotalExecutionResult(result as TotalExecutionResult)).toBe(true); + expect(((result as TotalExecutionResult).errors?.length ?? 0) > 0).toBe( + false, + ); + }); }); describe("Error thrown in the hook doesn't break execution and is returned in response 'errors'", () => { @@ -759,6 +1220,48 @@ describe.each([ expectedErrorMessage: 'Unexpected error in beforeSubscriptionEventEmit hook: "Hook error"', }, + { + name: "async beforeSubscriptionEventEmit (Error is thrown)", + document: `subscription EmitPersons($limit: Int!) + { + emitPersons(limit: $limit) { + name + } + }`, + variables: { + limit: 1, + }, + hooks: { + beforeSubscriptionEventEmit: jest + .fn() + .mockImplementation(async () => { + throw new Error("Hook error"); + }), + }, + expectedErrorMessage: + "Unexpected error in beforeSubscriptionEventEmit hook: Hook error", + }, + { + name: "async beforeSubscriptionEventEmit (string is thrown)", + document: `subscription EmitPersons($limit: Int!) + { + emitPersons(limit: $limit) { + name + } + }`, + variables: { + limit: 1, + }, + hooks: { + beforeSubscriptionEventEmit: jest + .fn() + .mockImplementation(async () => { + throw "Hook error"; + }), + }, + expectedErrorMessage: + 'Unexpected error in beforeSubscriptionEventEmit hook: "Hook error"', + }, ]; it.each(testCases)( @@ -817,4 +1320,35 @@ describe.each([ expect.objectContaining({ hookContext: afterHookContext }), ); }); + + it('passes async "before" hook context but "after" hook should already receive resolved promise', async () => { + expect.assertions(2); + + const query = ` + { + film(id: 1) { + title + } + }`; + const beforeHookContext = { + foo: "foo", + }; + const afterHookContext = { + bar: "bar", + }; + const hooks: ExecutionHooks = { + beforeFieldResolve: jest.fn(async () => beforeHookContext), + afterFieldResolve: jest.fn(() => afterHookContext), + afterFieldComplete: jest.fn(), + }; + + await execute(parse(query), resolvers as UserResolvers, hooks); + + expect(hooks.afterFieldResolve).toHaveBeenCalledWith( + expect.objectContaining({ hookContext: beforeHookContext }), + ); + expect(hooks.afterFieldComplete).toHaveBeenCalledWith( + expect.objectContaining({ hookContext: afterHookContext }), + ); + }); }); diff --git a/packages/supermassive/src/executeWithoutSchema.ts b/packages/supermassive/src/executeWithoutSchema.ts index 4f67386af..a9bea6a09 100644 --- a/packages/supermassive/src/executeWithoutSchema.ts +++ b/packages/supermassive/src/executeWithoutSchema.ts @@ -144,7 +144,7 @@ export function executeWithoutSchema( if (!("schemaFragment" in exeContext)) { return { errors: exeContext }; } else { - return executeOperation(exeContext); + return executeOperationWithBeforeHook(exeContext); } } @@ -277,23 +277,35 @@ function buildPerEventExecutionContext( }; } +function executeOperationWithBeforeHook( + exeContext: ExecutionContext, +): PromiseOrValue { + const hooks = exeContext.fieldExecutionHooks; + let hook: Promise | void | undefined; + if (hooks?.beforeOperationExecute) { + hook = invokeBeforeOperationExecuteHook(exeContext); + } + + if (isPromise(hook)) { + return hook.then(() => executeOperation(exeContext)); + } + + return executeOperation(exeContext); +} + function executeOperation( exeContext: ExecutionContext, ): PromiseOrValue { try { const { operation, rootValue } = exeContext; const rootTypeName = getOperationRootTypeName(operation); - const { groupedFieldSet, patches } = collectFields( exeContext, rootTypeName, ); const path = undefined; let result; - const hooks = exeContext.fieldExecutionHooks; - if (hooks?.beforeOperationExecute) { - invokeBeforeOperationExecuteHook(exeContext); - } + // Note: cannot use OperationTypeNode from graphql-js as it doesn't exist in 15.x switch (operation.operation) { case "query": @@ -686,7 +698,12 @@ function executeSubscriptionImpl( // Call the `subscribe()` resolver or the default resolver to produce an // AsyncIterable yielding raw payloads. - const result = resolveFn(rootValue, args, contextValue, info); + const result = isPromise(hookContext) + ? hookContext.then((context) => { + hookContext = context; + return resolveFn(rootValue, args, contextValue, info); + }) + : resolveFn(rootValue, args, contextValue, info); if (isPromise(result)) { return result.then(assertEventStream).then( @@ -796,18 +813,33 @@ function mapResultOrEventStreamOrPromise( payload, ); const hooks = exeContext?.fieldExecutionHooks; + let beforeExecuteFieldsHook: void | Promise | undefined; if (hooks?.beforeSubscriptionEventEmit) { - invokeBeforeSubscriptionEventEmitHook(perEventContext, payload); - } - try { - const data = executeFields( - exeContext, - parentTypeName, + beforeExecuteFieldsHook = invokeBeforeSubscriptionEventEmitHook( + perEventContext, payload, - path, - groupedFieldSet, - undefined, ); + } + try { + const data = isPromise(beforeExecuteFieldsHook) + ? beforeExecuteFieldsHook.then(() => + executeFields( + exeContext, + parentTypeName, + payload, + path, + groupedFieldSet, + undefined, + ), + ) + : executeFields( + exeContext, + parentTypeName, + payload, + path, + groupedFieldSet, + undefined, + ); // This is typechecked in collect values return buildResponse(perEventContext, data) as TotalExecutionResult; } catch (error) { @@ -919,7 +951,12 @@ function resolveAndCompleteField( hookContext = invokeBeforeFieldResolveHook(info, exeContext); } - const result = resolveFn(source, args, contextValue, info); + const result = isPromise(hookContext) + ? hookContext.then((context) => { + hookContext = context; + return resolveFn(source, args, contextValue, info); + }) + : resolveFn(source, args, contextValue, info); let completed; if (isPromise(result)) { @@ -1944,19 +1981,34 @@ function invokeAfterBuildResponseHook( } function executeSafe( - execute: () => T, + execute: () => T | Promise, onComplete: (result: T | undefined, error: unknown) => void, -): T { +): T | Promise { let error: unknown; - let result: T | undefined; + let result: T | Promise | undefined; try { result = execute(); } catch (e) { error = e; } finally { - onComplete(result, error); + if (!isPromise(result)) { + onComplete(result, error); + } } - return result as T; + + if (!isPromise(result)) { + return result as T; + } + + return result + .then((hookResult) => { + onComplete(hookResult, error); + return hookResult; + }) + .catch((e) => { + onComplete(undefined, e); + return undefined; + }) as Promise; } function toGraphQLError( diff --git a/packages/supermassive/src/hooks/types.ts b/packages/supermassive/src/hooks/types.ts index 2197621ae..b41165c23 100644 --- a/packages/supermassive/src/hooks/types.ts +++ b/packages/supermassive/src/hooks/types.ts @@ -46,7 +46,9 @@ export interface BeforeFieldResolveHook< ResolveContext = unknown, BeforeHookContext = unknown, > { - (args: BaseExecuteFieldHookArgs): BeforeHookContext; + (args: BaseExecuteFieldHookArgs): + | Promise + | BeforeHookContext; } export interface AfterFieldResolveHook< @@ -71,11 +73,13 @@ export interface AfterBuildResponseHook { } export interface BeforeOperationExecuteHook { - (args: BaseExecuteOperationHookArgs): void; + (args: BaseExecuteOperationHookArgs): void | Promise; } export interface BeforeSubscriptionEventEmitHook { - (args: BeforeSubscriptionEventEmitHookArgs): void; + ( + args: BeforeSubscriptionEventEmitHookArgs, + ): void | Promise; } export interface ExecutionHooks< diff --git a/packages/ts-codegen/src/__tests__/context.test.ts b/packages/ts-codegen/src/__tests__/context.test.ts new file mode 100644 index 000000000..3c6c98bf8 --- /dev/null +++ b/packages/ts-codegen/src/__tests__/context.test.ts @@ -0,0 +1,683 @@ +import ts from "typescript"; +import { parse } from "graphql"; +import { blankGraphQLTag as graphql } from "../utilities"; +import { generateTS } from ".."; + +describe(generateTS, () => { + describe("Tests basic syntax GraphQL syntax", () => { + test("all possible nullable and non-nullable combinations", () => { + const { resolvers, models, enums, inputs } = runGenerateTest(graphql` + extend schema @import(from: "@msteams/packages-test", defs: ["Avatar"]) + type Post @model(from: "./post-model.interface", tsType: "PostModel") { + id: ID! + } + + type Message { + id: ID! @context(uses: ["MessageStateMachine"]) + } + + type User @context(uses: ["UserStateMachine"]) { + id: ID! @context(uses: ["IdUserStateMachine"]) + name: String + messagesWithAnswersNonRequired: [[Message]] + messagesWithAnswersRequired: [[Message]]! + messagesWithAnswersAllRequired: [[Message!]!]! + messagesNonRequired: [Message] + messagesWithArrayRequired: [Message]! + messagesRequired: [Message!]! + messagesOnlyMessageRequired: [Message!] + post: Post @context(uses: ["PostStateMachine"]) + postRequired: Post! + avatar: Avatar + avatarRequired: Avatar! + } + + extend type Query { + requiredUsers: [User!]! + optionalUsers: [User] + optionalUser: User + requiredUser: User! + requiredPost: Post! + optionalPost: Post @context(uses: ["OptionalPostStateMachine"]) + } + `); + expect(enums).toMatchInlineSnapshot(`undefined`); + expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(models).toMatchInlineSnapshot(` + "import type { Avatar } from "@msteams/packages-test"; + import type { PostModel as _Post } from "../post-model.interface"; + // Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface Post extends BaseModel, _Post { + readonly __typename?: "Post"; + } + export interface Message extends BaseModel { + readonly __typename?: "Message"; + readonly id: string; + } + export interface User extends BaseModel { + readonly __typename?: "User"; + readonly id: string; + readonly name?: string | null; + readonly messagesWithAnswersNonRequired?: ReadonlyArray | null> | null; + readonly messagesWithAnswersRequired: ReadonlyArray | null>; + readonly messagesWithAnswersAllRequired: ReadonlyArray>; + readonly messagesNonRequired?: ReadonlyArray | null; + readonly messagesWithArrayRequired: ReadonlyArray; + readonly messagesRequired: ReadonlyArray; + readonly messagesOnlyMessageRequired?: ReadonlyArray | null; + readonly post?: Post | null; + readonly postRequired: Post; + readonly avatar?: Avatar | null; + readonly avatarRequired: Avatar; + } + " + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { Avatar } from "@msteams/packages-test"; + import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace Post { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.Post, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Message { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.Message, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace User { + export interface Resolvers { + readonly id?: id; + readonly name?: name; + readonly messagesWithAnswersNonRequired?: messagesWithAnswersNonRequired; + readonly messagesWithAnswersRequired?: messagesWithAnswersRequired; + readonly messagesWithAnswersAllRequired?: messagesWithAnswersAllRequired; + readonly messagesNonRequired?: messagesNonRequired; + readonly messagesWithArrayRequired?: messagesWithArrayRequired; + readonly messagesRequired?: messagesRequired; + readonly messagesOnlyMessageRequired?: messagesOnlyMessageRequired; + readonly post?: post; + readonly postRequired?: postRequired; + readonly avatar?: avatar; + readonly avatarRequired?: avatarRequired; + } + export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type messagesWithAnswersNonRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined> | null | undefined>; + export type messagesWithAnswersRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>>; + export type messagesWithAnswersAllRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>>; + export type messagesNonRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type messagesWithArrayRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + export type messagesRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + export type messagesOnlyMessageRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type post = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type postRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type avatar = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type avatarRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Query { + export interface Resolvers { + readonly requiredUsers?: requiredUsers; + readonly optionalUsers?: optionalUsers; + readonly optionalUser?: optionalUser; + readonly requiredUser?: requiredUser; + readonly requiredPost?: requiredPost; + readonly optionalPost?: optionalPost; + } + export type requiredUsers = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + export type optionalUsers = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type optionalUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type requiredUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type requiredPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type optionalPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + " + `); + }); + test("Subscription", () => { + const { resolvers, models, enums, inputs } = runGenerateTest(graphql` + type User { + id: ID! + } + + extend type Subscription { + userUpdated: User! + } + `); + expect(enums).toMatchInlineSnapshot(`undefined`); + expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(models).toMatchInlineSnapshot(` + "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface User extends BaseModel { + readonly __typename?: "User"; + readonly id: string; + } + " + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace User { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Subscription { + export interface Resolvers { + readonly userUpdated?: userUpdated; + } + export type userUpdated = { + subscribe: (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + } | { + subscribe: (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + resolve: (subcribeResult: SubscribeResult, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + }; + } + " + `); + }); + test("Subscription with model", () => { + const { resolvers } = runGenerateTest(graphql` + type User @model(from: "./user-model.interface", tsType: "UserModel") { + id: ID! + } + + extend type Subscription { + userUpdated: User! + } + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace User { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Subscription { + export interface Resolvers { + readonly userUpdated?: userUpdated; + } + export type userUpdated = { + subscribe: (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + } | { + subscribe: (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + resolve: (subcribeResult: SubscribeResult, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + }; + } + " + `); + }); + test("extends by exteding a type with pre-generated BaseModel type", () => { + const { resolvers, models, enums, inputs } = runGenerateTest(graphql` + type User { + id: ID! + } + + extend type Query { + users: [User!]! + } + `); + expect(enums).toMatchInlineSnapshot(`undefined`); + expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(models).toMatchInlineSnapshot(` + "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface User extends BaseModel { + readonly __typename?: "User"; + readonly id: string; + } + " + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace User { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Query { + export interface Resolvers { + readonly users?: users; + } + export type users = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + } + " + `); + }); + test("case when interface implements multiple interfaces", () => { + const { resolvers, models, enums, inputs } = runGenerateTest(graphql` + interface Node @context(uses: ["NodeStateMachine"]) { + id: ID! + } + + interface Persona @context(uses: ["PersonaStateMachine"]) { + phone: String! + } + + interface User implements Node & Persona { + id: ID! + name: String! + } + + type Admin implements Node & Persona + @context(uses: ["AdminStateMachine"]) { + id: ID! + rank: Int! + } + + extend type Query { + users: [User] + admins: [Admin] + } + `); + expect(enums).toMatchInlineSnapshot(`undefined`); + expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(models).toMatchInlineSnapshot(` + "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface Node extends BaseModel { + readonly __typename?: string; + } + export interface Persona extends BaseModel { + readonly __typename?: string; + } + export interface User extends BaseModel, Node, Persona { + readonly __typename?: string; + } + export interface Admin extends BaseModel, Node, Persona { + readonly __typename?: "Admin"; + readonly id: string; + readonly rank: number; + } + " + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace Node { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Persona { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace User { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Admin { + export interface Resolvers { + readonly id?: id; + readonly rank?: rank; + } + export type id = (model: Models.Admin, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type rank = (model: Models.Admin, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Query { + export interface Resolvers { + readonly users?: users; + readonly admins?: admins; + } + export type users = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type admins = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + } + " + `); + }); + test("extensions are not generated in the models", () => { + const { models } = runGenerateTest(graphql` + extend schema @import(from: "@msteams/packages-test", defs: ["User"]) + + extend type User { + id: ID! + name: String! + } + + type Post { + id: ID! + user: User! + } + `); + expect(models).toMatchInlineSnapshot(` + "import type { User } from "@msteams/packages-test"; + // Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface Post extends BaseModel { + readonly __typename?: "Post"; + readonly id: string; + readonly user: User; + } + " + `); + }); + + test("implements", () => { + const { resolvers, models, enums, inputs } = runGenerateTest(graphql` + interface Node @context(uses: ["NodeStateMachine"]) { + id: ID! + } + + type User implements Node { + id: ID! + name: String! + } + + extend type Query { + users: [User] + } + `); + expect(enums).toMatchInlineSnapshot(`undefined`); + expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(models).toMatchInlineSnapshot(` + "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface Node extends BaseModel { + readonly __typename?: string; + } + export interface User extends BaseModel, Node { + readonly __typename?: "User"; + readonly id: string; + readonly name: string; + } + " + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace Node { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace User { + export interface Resolvers { + readonly id?: id; + readonly name?: name; + } + export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Query { + export interface Resolvers { + readonly users?: users; + } + export type users = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + } + " + `); + }); + + test("Enum", () => { + const { resolvers, models, enums, inputs } = runGenerateTest(graphql` + enum PresenceAvailability { + Available + Away + Offline + } + type User { + id: ID! + availability: PresenceAvailability! + } + + extend type Query { + userById(id: ID!): User + } + `); + expect(enums).toMatchInlineSnapshot(` + "export enum PresenceAvailability { + Available = "Available", + Away = "Away", + Offline = "Offline" + } + " + `); + expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(models).toMatchInlineSnapshot(` + "import * as Enums from "./enums.interface"; + export * from "./enums.interface"; + // Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface User extends BaseModel { + readonly __typename?: "User"; + readonly id: string; + readonly availability: Enums.PresenceAvailability; + } + " + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace User { + export interface Resolvers { + readonly id?: id; + readonly availability?: availability; + } + export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type availability = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Query { + export interface Resolvers { + readonly userById?: userById; + } + export type userById = (model: unknown, args: { + readonly id: string; + }, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + " + `); + }); + + test("Union and interface types", () => { + const { resolvers, models, enums, inputs } = runGenerateTest(graphql` + type Customer @context(uses: ["CustomStateMachine"]) { + id: ID! + } + + type Admin @context(uses: ["AdminStateMachine"]) { + id: ID! + } + + interface Node { + id: ID! + } + + union whatever = User | Admin + + extend type Query { + userById(id: ID!): whatever @context(uses: ["WhateverStateMachine"]) + userByMail(mail: String): whatever + @context(uses: ["DifferentWhateverStateMachine"]) + node(id: ID!): Node + } + `); + expect(enums).toMatchInlineSnapshot(`undefined`); + expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(models).toMatchInlineSnapshot(` + "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) + export interface BaseModel { + readonly __typename?: string; + } + export interface Customer extends BaseModel { + readonly __typename?: "Customer"; + readonly id: string; + } + export interface Admin extends BaseModel { + readonly __typename?: "Admin"; + readonly id: string; + } + export interface Node extends BaseModel { + readonly __typename?: string; + } + export type whatever = User | Admin; + " + `); + expect(resolvers).toMatchInlineSnapshot(` + "import type { PromiseOrValue } from "@graphitation/supermassive"; + import type { ResolveInfo } from "@graphitation/supermassive"; + import * as Models from "./models.interface"; + export declare namespace Customer { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.Customer, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Admin { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.Admin, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Node { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace whatever { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: Models.User | Models.Admin, context: unknown, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; + } + export declare namespace Query { + export interface Resolvers { + readonly userById?: userById; + readonly userByMail?: userByMail; + readonly node?: node; + } + export type userById = (model: unknown, args: { + readonly id: string; + }, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type userByMail = (model: unknown, args: { + readonly mail?: string | null; + }, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type node = (model: unknown, args: { + readonly id: string; + }, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + " + `); + }); + }); +}); + +function runGenerateTest( + doc: string, + options: { + outputPath?: string; + documentPath?: string; + contextImport?: string; + contextName?: string; + legacyCompat?: boolean; + enumsImport?: string; + legacyNoModelsForObjects?: boolean; + legacyEnumsCompatibility?: boolean; + useStringUnionsInsteadOfEnums?: boolean; + enumNamesToMigrate?: string[]; + enumNamesToKeep?: string[]; + modelScope?: string; + contextMappingContent?: Record | null; + } = {}, +): { + enums?: string; + inputs?: string; + models: string; + resolvers: string; + legacyTypes?: string; + legacyResolvers?: string; + legacyNoModelsForObjects?: boolean; + legacyEnumsCompatibility?: boolean; + useStringUnionsInsteadOfEnums?: boolean; + enumNamesToMigrate?: string[]; + enumNamesToKeep?: string[]; + modelScope?: string; + contextMappingContent?: Record | null; +} { + const fullOptions: { + outputPath: string; + documentPath: string; + contextImport?: string | null; + contextName?: string; + legacyCompat?: boolean; + legacyEnumsCompatibility?: boolean; + legacyNoModelsForObjects?: boolean; + useStringUnionsInsteadOfEnums?: boolean; + enumNamesToMigrate?: string[]; + enumNamesToKeep?: string[]; + contextMappingContent?: Record | null; + } = { + outputPath: "__generated__", + documentPath: "./typedef.graphql", + ...options, + }; + const document = parse(doc); + const { files } = generateTS(document, fullOptions); + + function getFileByFileName(fileName: string) { + return files.find((file) => file.fileName === fileName); + } + + const { models, resolvers, enums, inputs, legacyTypes, legacyResolvers } = { + models: getFileByFileName("models.interface.ts") as ts.SourceFile, + inputs: getFileByFileName("inputs.interface.ts"), + enums: getFileByFileName("enums.interface.ts"), + resolvers: getFileByFileName("resolvers.interface.ts") as ts.SourceFile, + legacyTypes: getFileByFileName("legacy-types.interface.ts"), + legacyResolvers: getFileByFileName("legacy-resolvers.interface.ts"), + }; + + const printer = ts.createPrinter(); + + return { + enums: enums && printer.printFile(enums), + inputs: inputs && printer.printFile(inputs), + models: printer.printFile(models), + contextMappingContent: options.contextMappingContent || null, + resolvers: printer.printFile(resolvers), + legacyTypes: legacyTypes && printer.printFile(legacyTypes), + legacyResolvers: legacyResolvers && printer.printFile(legacyResolvers), + }; +} diff --git a/packages/ts-codegen/src/codegen.ts b/packages/ts-codegen/src/codegen.ts index 281b67095..9162ec7f0 100644 --- a/packages/ts-codegen/src/codegen.ts +++ b/packages/ts-codegen/src/codegen.ts @@ -23,6 +23,7 @@ export function generateTS( generateOnlyEnums, enumNamesToMigrate, enumNamesToKeep, + contextMappingContent, }: { outputPath: string; documentPath: string; @@ -36,6 +37,7 @@ export function generateTS( generateOnlyEnums?: boolean; enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; + contextMappingContent?: Record | null; }, ): { files: ts.SourceFile[]; @@ -54,6 +56,7 @@ export function generateTS( modelScope, enumNamesToMigrate, enumNamesToKeep, + contextMappingContent, }, document, outputPath, diff --git a/packages/ts-codegen/src/context/index.ts b/packages/ts-codegen/src/context/index.ts index c0fc75971..8b0fe06da 100644 --- a/packages/ts-codegen/src/context/index.ts +++ b/packages/ts-codegen/src/context/index.ts @@ -51,6 +51,7 @@ export type TsCodegenContextOptions = { }; legacyCompat: boolean; legacyNoModelsForObjects: boolean; + contextMappingContent?: Record | null; useStringUnionsInsteadOfEnums: boolean; enumNamesToMigrate: string[] | null; enumNamesToKeep: string[] | null; @@ -97,7 +98,10 @@ type ModelNameAndImport = { modelName: string; imp: DefinitionImport }; export class TsCodegenContext { private allTypes: Array; + private typeContextMap: any; + private allRootTypeNames: Set; private typeNameToType: Map; + private contextMappingContent: Record | null; private usedEntitiesInModels: Set; private usedEntitiesInResolvers: Set; private usedEntitiesInInputs: Set; @@ -108,6 +112,7 @@ export class TsCodegenContext { >; private typeNameToModels: Map; private legacyInterfaces: Set; + context?: { name: string; from: string }; hasUsedModelInInputs: boolean; hasUsedEnumsInModels: boolean; hasEnums: boolean; @@ -115,6 +120,8 @@ export class TsCodegenContext { constructor(private options: TsCodegenContextOptions) { this.allTypes = []; + this.typeContextMap = {}; + this.allRootTypeNames = new Set(); this.typeNameToType = new Map(); this.usedEntitiesInModels = new Set(); this.usedEntitiesInResolvers = new Set(); @@ -128,6 +135,128 @@ export class TsCodegenContext { this.hasInputs = false; this.hasEnums = Boolean(options.enumsImport); this.hasUsedEnumsInModels = false; + this.contextMappingContent = options.contextMappingContent || null; + if (options.context.from && options.context.name) { + this.context = { + name: options.context.name, + from: options.context.from, + }; + } + } + + private findNamedTyped(type: TypeNode): string | null { + if (type.kind === "NamedType") { + return type.name.value; + } else if (type.kind === "NonNullType") { + return this.findNamedTyped(type.type); + } else if (type.kind === "ListType") { + return this.findNamedTyped(type.type); + } + + return null; + } + + public mergeContexts(typeNames: string[]): { __context: string[] } | null { + const output = typeNames.reduce<{ __context: string[] }>( + (contextRootType, interfaceName) => { + if (this.getContextMap()[interfaceName]?.__context) { + contextRootType.__context = [ + ...contextRootType.__context, + ...this.getContextMap()[interfaceName].__context, + ]; + } + return contextRootType; + }, + { __context: [] }, + ); + + return output.__context.length ? output : null; + } + + public getContextTypes( + contextRootType: T & { + __context?: string[]; + }, + ): string[] | null { + if (contextRootType) { + if (contextRootType.__context) { + return contextRootType.__context; + } + } + return null; + } + + public getContextMappingContent(): Record | null { + return this.contextMappingContent; + } + + public getContextTypeNode(typeNames?: string[] | null) { + if (!typeNames || !typeNames.length) { + return this.getContextType().toTypeReference(); + } else if ( + (typeNames.length === 1 && this.context) || + typeNames.length > 1 + ) { + return factory.createIntersectionTypeNode( + (this.context ? [this.context.name, ...typeNames] : typeNames).map( + (type: string) => { + return factory.createTypeReferenceNode( + factory.createIdentifier(type), + undefined, + ); + }, + ), + ); + } else { + return new TypeLocation(null, typeNames[0]).toTypeReference(); + } + } + + // FIX any + public initContextMap(ancestors: any, values: string[]) { + if (ancestors.length < 2) { + throw new Error("wtf is happening"); + } + + const node = ancestors[ancestors.length - 1]; + + if ( + node?.kind === "ObjectTypeDefinition" || + node?.kind === "InterfaceTypeDefinition" + ) { + if (this.typeContextMap[node.name.value]?.__context) { + throw new Error("Type already visited"); + } + + const typeName = ancestors[ancestors.length - 1].name.value; + if (!this.typeContextMap[typeName]) { + this.typeContextMap[typeName] = {}; + } + + this.typeContextMap[typeName].__context = values; + + if (node.interfaces.length) { + this.typeContextMap[typeName].__interfaces = node.interfaces.map( + (interfaceDefinitionNode: any) => interfaceDefinitionNode.name.value, + ); + } + + if (node.interfaces.length && node?.kind === "InterfaceTypeDefinition") { + this.typeContextMap[typeName].__interfaces = node.interfaces.map( + (interfaceDefinitionNode: any) => interfaceDefinitionNode.name.value, + ); + } + } else if (node?.kind === "FieldDefinition") { + const typeName = ancestors[ancestors.length - 3].name.value; + if (!this.typeContextMap[typeName]) { + this.typeContextMap[typeName] = {}; + } + + this.typeContextMap[typeName][ + ancestors[ancestors.length - 1].name.value + ] = values; + } + console.log(JSON.stringify(this.typeContextMap, null, 2)); } isLegacyCompatMode(): boolean { @@ -142,11 +271,23 @@ export class TsCodegenContext { return this.options.enumsImport; } + getContextMap() { + return this.typeContextMap; + } + + addRootTypeNames(typename: string): void { + this.allRootTypeNames.add(typename); + } + addType(type: Type): void { this.allTypes.push(type); this.typeNameToType.set(type.name, type); } + getAllRootTypeNames(): Set { + return this.allRootTypeNames; + } + getAllTypes(): Array { return this.allTypes; } @@ -188,6 +329,20 @@ export class TsCodegenContext { } } + getTypeFromTypeNode(node: TypeNode | string): ts.TypeNode | string { + if (typeof node === "string") { + return node; + } + + if (node.kind === Kind.NON_NULL_TYPE) { + return this.getTypeFromTypeNode(node.type) as ts.TypeNode; + } else if (node.kind === Kind.LIST_TYPE) { + return this.getTypeFromTypeNode(node.type) as ts.TypeNode; + } else { + return node.name.value; + } + } + isUseStringUnionsInsteadOfEnumsEnabled(): boolean { return Boolean(this.options.useStringUnionsInsteadOfEnums); } @@ -376,11 +531,7 @@ export class TsCodegenContext { } getContextType(): TypeLocation { - return new TypeLocation( - null, - this.options.context.name || - (TsCodegenContextDefault.context.name as string), - ); + return new TypeLocation(null, "unknown"); } getResolveInfoType(): TypeLocation { @@ -526,10 +677,10 @@ export function extractContext( ...options, }; const context = new TsCodegenContext(fullOptions); - + const { contextMappingContent } = options; visit(document, { Directive: { - enter(node, _key, _parent, _path, ancestors) { + enter(node, _key, _parent, _path: any, ancestors) { if (node.name.value === IMPORT_DIRECTIVE_NAME) { context.addImport( processImportDirective(node, outputPath, documentPath), @@ -556,6 +707,30 @@ export function extractContext( } const typeName = (typeDef as InterfaceTypeDefinitionNode).name.value; context.addLegacyInterface(typeName); + } else if (node.name.value === "context" && contextMappingContent) { + if ( + node.arguments?.length !== 1 || + node.arguments[0].name.value !== "uses" || + node.arguments[0].value.kind !== "ListValue" + ) { + throw new Error("Invalid context use"); + } + // TODO ADD validation + + const directiveValues = node.arguments[0].value.values.map((item) => { + if (item.kind !== "StringValue") { + throw new Error("Invalid context use"); + } + return item.value; + }); + + const filtredDirectiveValues = directiveValues.filter( + (directiveValue) => contextMappingContent[directiveValue], + ); + + if (filtredDirectiveValues.length) { + context.initContextMap(ancestors, filtredDirectiveValues); + } } }, }, @@ -573,6 +748,7 @@ export function extractContext( }, ObjectTypeDefinition: { leave(node) { + context.addRootTypeNames(node.name.value); context.addType({ kind: "OBJECT", name: node.name.value, @@ -587,6 +763,7 @@ export function extractContext( }, InterfaceTypeDefinition: { leave(node) { + context.addRootTypeNames(node.name.value); context.addType({ kind: "INTERFACE", name: node.name.value, @@ -611,6 +788,7 @@ export function extractContext( }, ObjectTypeExtension: { leave(node) { + context.addRootTypeNames(node.name.value); context.addType({ kind: "OBJECT", name: node.name.value, @@ -624,6 +802,7 @@ export function extractContext( }, UnionTypeDefinition: { leave(node) { + context.addRootTypeNames(node.name.value); context.addType({ kind: "UNION", name: node.name.value, diff --git a/packages/ts-codegen/src/resolvers.ts b/packages/ts-codegen/src/resolvers.ts index e58d8737d..23eab60db 100644 --- a/packages/ts-codegen/src/resolvers.ts +++ b/packages/ts-codegen/src/resolvers.ts @@ -29,6 +29,124 @@ export function generateResolvers(context: TsCodegenContext): ts.SourceFile { ), ); + if ( + Object.keys(context.getContextMap()).length && + context.getContextMappingContent() + ) { + const contextImportNames: Set = new Set(); + + for (const [, root] of Object.entries(context.getContextMap())) { + const rootValue: string[] = (root as any).__context; + if (rootValue) { + if ( + rootValue.every((importName: string) => + contextImportNames.has(importName), + ) + ) { + continue; + } + + const imports = (rootValue as string[]).reduce< + Record + >((acc: Record, importName: string) => { + const importPath = context.getContextMappingContent()?.[importName]; + if (importPath) { + if (!acc[importPath]) { + acc[importPath] = []; + } + acc[importPath].push(importName); + } + return acc; + }, {}); + + for (const [importPath, importNames] of Object.entries(imports)) { + statements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + true, + undefined, + factory.createNamedImports( + importNames + .map((importName: string) => { + if (contextImportNames.has(importName)) { + return; + } + contextImportNames.add(importName); + + return factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier(importName), + ); + }) + .filter(Boolean) as ts.ImportSpecifier[], + ), + ), + factory.createStringLiteral(importPath), + ), + ); + } + } + + for (const [key, value] of Object.entries(root as any)) { + if (key.startsWith("__")) { + continue; + } + + if ( + (value as string[]).every((importName: string) => + contextImportNames.has(importName), + ) + ) { + continue; + } + const imports = (value as string[]).reduce>( + (acc: Record, importName: string) => { + const importPath = context.getContextMappingContent()?.[importName]; + if (importPath) { + if (!acc[importPath]) { + acc[importPath] = []; + } + acc[importPath].push(importName); + } + return acc; + }, + {}, + ); + + for (const [importPath, importNames] of Object.entries(imports)) { + statements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + true, + undefined, + factory.createNamedImports( + importNames + .map((importName: string) => { + if (contextImportNames.has(importName)) { + return; + } + contextImportNames.add(importName); + + return factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier(importName), + ); + }) + .filter(Boolean) as ts.ImportSpecifier[], + ), + ), + factory.createStringLiteral(importPath), + ), + ); + } + } + } + } + if (context.hasInputs) { statements.push( factory.createImportDeclaration( @@ -144,6 +262,27 @@ function createResolverField( .toTypeReference(); } + let contextRootType = + context.getContextMap()[type.name] || + context.getContextMap()[context.getTypeFromTypeNode(field.type) as string]; + + if ( + (type.kind === "OBJECT" || type.kind === "INTERFACE") && + !contextRootType && + type.interfaces.length + ) { + contextRootType = context.mergeContexts(type.interfaces); + } + + let contextTypes; + if (contextRootType) { + if (contextRootType[field.name]) { + contextTypes = contextRootType[field.name]; + } else if (contextRootType.__context) { + contextTypes = contextRootType.__context; + } + } + const resolverParametersDefinitions = { parent: { name: "model", @@ -164,7 +303,7 @@ function createResolverField( }, context: { name: "context", - type: context.getContextType().toTypeReference(), + type: context.getContextTypeNode(contextTypes), }, resolveInfo: { name: "info", @@ -179,6 +318,7 @@ function createResolverField( field.name, ); } + return factory.createTypeAliasDeclaration( [factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(toValidFieldName(field.name)), @@ -218,6 +358,15 @@ function createUnionTypeResolvers( ), ], ); + + let contextRootType = context.getContextMap()[type.name]; + + if (!contextRootType && type.types.length) { + contextRootType = context.mergeContexts(type.types); + } + + const contextTypes = context.getContextTypes(contextRootType); + return factory.createModuleDeclaration( [ factory.createModifier(ts.SyntaxKind.ExportKeyword), @@ -230,7 +379,11 @@ function createUnionTypeResolvers( [factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("__resolveType"), undefined, - createUnionResolveType(context, type), + createUnionResolveType( + context, + type, + context.getContextTypeNode(contextTypes), + ), ), ]), ts.NodeFlags.Namespace, @@ -241,6 +394,14 @@ function createInterfaceTypeResolvers( context: TsCodegenContext, type: InterfaceType, ): ts.ModuleDeclaration { + let contextRootType = context.getContextMap()[type.name]; + + if (!contextRootType && type.interfaces.length) { + contextRootType = context.mergeContexts(type.interfaces); + } + + const contextTypes = context.getContextTypes(contextRootType); + const resolversObject = factory.createInterfaceDeclaration( [factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Resolvers"), @@ -268,7 +429,10 @@ function createInterfaceTypeResolvers( [factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("__resolveType"), undefined, - createInterfaceResolveType(context), + createInterfaceResolveType( + context, + context.getContextTypeNode(contextTypes), + ), ), ]), ts.NodeFlags.Namespace, diff --git a/packages/ts-codegen/src/utilities.ts b/packages/ts-codegen/src/utilities.ts index 4591fed3d..c0aac7b03 100644 --- a/packages/ts-codegen/src/utilities.ts +++ b/packages/ts-codegen/src/utilities.ts @@ -23,13 +23,16 @@ type ResolverParameterDefinition = { name: string; type: T }; type ResolverParametersDefinitions = { parent: ResolverParameterDefinition; args?: ResolverParameterDefinition; - context: ResolverParameterDefinition; + context: ResolverParameterDefinition< + ts.TypeReferenceNode | ts.IntersectionTypeNode + >; resolveInfo: ResolverParameterDefinition; }; export function createUnionResolveType( context: TsCodegenContext, type: UnionType, + contextType: ts.TypeReferenceNode | ts.IntersectionTypeNode, ): ts.FunctionTypeNode { return factory.createFunctionTypeNode( undefined, @@ -44,7 +47,7 @@ export function createUnionResolveType( }, context: { name: "context", - type: context.getContextType().toTypeReference(), + type: contextType, }, resolveInfo: { name: "info", @@ -68,6 +71,7 @@ export function createUnionResolveType( export function createInterfaceResolveType( context: TsCodegenContext, + contextType: ts.TypeReferenceNode | ts.IntersectionTypeNode, ): ts.FunctionTypeNode { return factory.createFunctionTypeNode( undefined, @@ -78,7 +82,7 @@ export function createInterfaceResolveType( }, context: { name: "context", - type: context.getContextType().toTypeReference(), + type: contextType, }, resolveInfo: { name: "info", From c46a2f4e31d6d1379f37d02f4330ed003877c7dd Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Wed, 23 Oct 2024 19:19:35 +0000 Subject: [PATCH 03/19] applying package updates --- ...phitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json | 7 ------- ...-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json | 7 ------- ...on-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json | 7 ------- 3 files changed, 21 deletions(-) delete mode 100644 change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json delete mode 100644 change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json delete mode 100644 change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json diff --git a/change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json b/change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json deleted file mode 100644 index e6ded4330..000000000 --- a/change/@graphitation-cli-655d32f2-7b28-45d9-8409-768de8b551fe.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "Context mapping", - "packageName": "@graphitation/cli", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "patch" -} diff --git a/change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json b/change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json deleted file mode 100644 index 3d7eb954f..000000000 --- a/change/@graphitation-supermassive-87e2107d-b60a-465e-a377-903c60f7550f.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "async before hooks added", - "packageName": "@graphitation/supermassive", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "patch" -} diff --git a/change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json b/change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json deleted file mode 100644 index 640a00268..000000000 --- a/change/@graphitation-ts-codegen-d1d73434-b834-44cc-967d-ffeb17e7ea76.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "context mapping", - "packageName": "@graphitation/ts-codegen", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "patch" -} From 03f525a3da9dc2c74b3734e795c4779631603dd3 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:56:34 +0100 Subject: [PATCH 04/19] Alpha release and update (#467) * metadata written to a file and tests fixed --- ...-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json | 7 + ...-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json | 7 + packages/cli/src/supermassive.ts | 10 + .../ts-codegen/src/__tests__/context.test.ts | 272 ++++++++++-------- packages/ts-codegen/src/codegen.ts | 6 +- packages/ts-codegen/src/context/index.ts | 13 - 6 files changed, 188 insertions(+), 127 deletions(-) create mode 100644 change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json create mode 100644 change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json diff --git a/change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json b/change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json new file mode 100644 index 000000000..538601845 --- /dev/null +++ b/change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "metadata written to a file and tests fixed", + "packageName": "@graphitation/cli", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json b/change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json new file mode 100644 index 000000000..534dade59 --- /dev/null +++ b/change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "metadata written to a file and tests fixed", + "packageName": "@graphitation/ts-codegen", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/cli/src/supermassive.ts b/packages/cli/src/supermassive.ts index d998dbcb5..cc1b3644a 100644 --- a/packages/cli/src/supermassive.ts +++ b/packages/cli/src/supermassive.ts @@ -174,6 +174,16 @@ async function generateInterfaces( ), ); + if (result.contextMappingOutput) { + outputs.push( + fs.writeFile( + path.join(outputPath, "schema-context-mapping-metadata.json"), + JSON.stringify(result.contextMappingOutput, null, 2), + { encoding: "utf-8" }, + ), + ); + } + await Promise.all(outputs); } } diff --git a/packages/ts-codegen/src/__tests__/context.test.ts b/packages/ts-codegen/src/__tests__/context.test.ts index 3c6c98bf8..5b179eb9d 100644 --- a/packages/ts-codegen/src/__tests__/context.test.ts +++ b/packages/ts-codegen/src/__tests__/context.test.ts @@ -6,41 +6,52 @@ import { generateTS } from ".."; describe(generateTS, () => { describe("Tests basic syntax GraphQL syntax", () => { test("all possible nullable and non-nullable combinations", () => { - const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - extend schema @import(from: "@msteams/packages-test", defs: ["Avatar"]) - type Post @model(from: "./post-model.interface", tsType: "PostModel") { - id: ID! - } + const { resolvers, models, enums, inputs } = runGenerateTest( + graphql` + extend schema + @import(from: "@msteams/packages-test", defs: ["Avatar"]) + type Post + @model(from: "./post-model.interface", tsType: "PostModel") { + id: ID! + } - type Message { - id: ID! @context(uses: ["MessageStateMachine"]) - } + type Message { + id: ID! @context(uses: ["MessageStateMachine"]) + } - type User @context(uses: ["UserStateMachine"]) { - id: ID! @context(uses: ["IdUserStateMachine"]) - name: String - messagesWithAnswersNonRequired: [[Message]] - messagesWithAnswersRequired: [[Message]]! - messagesWithAnswersAllRequired: [[Message!]!]! - messagesNonRequired: [Message] - messagesWithArrayRequired: [Message]! - messagesRequired: [Message!]! - messagesOnlyMessageRequired: [Message!] - post: Post @context(uses: ["PostStateMachine"]) - postRequired: Post! - avatar: Avatar - avatarRequired: Avatar! - } + type User @context(uses: ["UserStateMachine"]) { + id: ID! @context(uses: ["IdUserStateMachine"]) + name: String + messagesWithAnswersNonRequired: [[Message]] + messagesWithAnswersRequired: [[Message]]! + messagesWithAnswersAllRequired: [[Message!]!]! + messagesNonRequired: [Message] + messagesWithArrayRequired: [Message]! + messagesRequired: [Message!]! + messagesOnlyMessageRequired: [Message!] + post: Post @context(uses: ["PostStateMachine"]) + postRequired: Post! + avatar: Avatar + avatarRequired: Avatar! + } - extend type Query { - requiredUsers: [User!]! - optionalUsers: [User] - optionalUser: User - requiredUser: User! - requiredPost: Post! - optionalPost: Post @context(uses: ["OptionalPostStateMachine"]) - } - `); + extend type Query { + requiredUsers: [User!]! + optionalUsers: [User] + optionalUser: User + requiredUser: User! + requiredPost: Post! + optionalPost: Post @context(uses: ["OptionalPostStateMachine"]) + } + `, + { + contextMappingContent: { + UserStateMachine: "user-state-machine", + PostStateMachine: "post-state-machine", + IdUserStateMachine: "id-user-state-machine", + }, + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` @@ -80,6 +91,9 @@ describe(generateTS, () => { import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; + import type { UserStateMachine } from "user-state-machine"; + import type { IdUserStateMachine } from "id-user-state-machine"; + import type { PostStateMachine } from "post-state-machine"; export declare namespace Post { export interface Resolvers { readonly id?: id; @@ -108,19 +122,19 @@ describe(generateTS, () => { readonly avatar?: avatar; readonly avatarRequired?: avatarRequired; } - export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type name = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type messagesWithAnswersNonRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined> | null | undefined>; - export type messagesWithAnswersRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>>; - export type messagesWithAnswersAllRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>>; - export type messagesNonRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type messagesWithArrayRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; - export type messagesRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; - export type messagesOnlyMessageRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type post = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type postRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type avatar = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type avatarRequired = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.User, args: {}, context: IdUserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type messagesWithAnswersNonRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined> | null | undefined>; + export type messagesWithAnswersRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>>; + export type messagesWithAnswersAllRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>>; + export type messagesNonRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type messagesWithArrayRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>; + export type messagesRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>; + export type messagesOnlyMessageRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type post = (model: Models.User, args: {}, context: PostStateMachine, info: ResolveInfo) => PromiseOrValue; + export type postRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type avatar = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type avatarRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -131,10 +145,10 @@ describe(generateTS, () => { readonly requiredPost?: requiredPost; readonly optionalPost?: optionalPost; } - export type requiredUsers = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; - export type optionalUsers = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type optionalUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type requiredUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type requiredUsers = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>; + export type optionalUsers = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type optionalUser = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type requiredUser = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; export type requiredPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; export type optionalPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; } @@ -269,31 +283,40 @@ describe(generateTS, () => { `); }); test("case when interface implements multiple interfaces", () => { - const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - interface Node @context(uses: ["NodeStateMachine"]) { - id: ID! - } + const { resolvers, models, enums, inputs } = runGenerateTest( + graphql` + interface Node @context(uses: ["NodeStateMachine"]) { + id: ID! + } - interface Persona @context(uses: ["PersonaStateMachine"]) { - phone: String! - } + interface Persona @context(uses: ["PersonaStateMachine"]) { + phone: String! + } - interface User implements Node & Persona { - id: ID! - name: String! - } + interface User implements Node & Persona { + id: ID! + name: String! + } - type Admin implements Node & Persona - @context(uses: ["AdminStateMachine"]) { - id: ID! - rank: Int! - } + type Admin implements Node & Persona + @context(uses: ["AdminStateMachine"]) { + id: ID! + rank: Int! + } - extend type Query { - users: [User] - admins: [Admin] - } - `); + extend type Query { + users: [User] + admins: [Admin] + } + `, + { + contextMappingContent: { + AdminStateMachine: "admin-state-machine", + PersonaStateMachine: "persona-state-machine", + NodeStateMachine: "node-state-machine", + }, + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` @@ -321,31 +344,34 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; + import type { NodeStateMachine } from "node-state-machine"; + import type { PersonaStateMachine } from "persona-state-machine"; + import type { AdminStateMachine } from "admin-state-machine"; export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Persona { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: NodeStateMachine & PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { readonly id?: id; readonly rank?: rank; } - export type id = (model: Models.Admin, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type rank = (model: Models.Admin, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Admin, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue; + export type rank = (model: Models.Admin, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -353,7 +379,7 @@ describe(generateTS, () => { readonly admins?: admins; } export type users = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type admins = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type admins = (model: unknown, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; } " `); @@ -388,20 +414,27 @@ describe(generateTS, () => { }); test("implements", () => { - const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - interface Node @context(uses: ["NodeStateMachine"]) { - id: ID! - } + const { resolvers, models, enums, inputs } = runGenerateTest( + graphql` + interface Node @context(uses: ["NodeStateMachine"]) { + id: ID! + } - type User implements Node { - id: ID! - name: String! - } + type User implements Node { + id: ID! + name: String! + } - extend type Query { - users: [User] - } - `); + extend type Query { + users: [User] + } + `, + { + contextMappingContent: { + NodeStateMachine: "node-state-machine", + }, + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` @@ -423,19 +456,20 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; + import type { NodeStateMachine } from "node-state-machine"; export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { readonly id?: id; readonly name?: name; } - export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type name = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.User, args: {}, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -511,28 +545,37 @@ describe(generateTS, () => { }); test("Union and interface types", () => { - const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - type Customer @context(uses: ["CustomStateMachine"]) { - id: ID! - } + const { resolvers, models, enums, inputs } = runGenerateTest( + graphql` + type Customer @context(uses: ["CustomStateMachine"]) { + id: ID! + } - type Admin @context(uses: ["AdminStateMachine"]) { - id: ID! - } + type Admin @context(uses: ["AdminStateMachine"]) { + id: ID! + } - interface Node { - id: ID! - } + interface Node { + id: ID! + } - union whatever = User | Admin + union whatever = User | Admin - extend type Query { - userById(id: ID!): whatever @context(uses: ["WhateverStateMachine"]) - userByMail(mail: String): whatever - @context(uses: ["DifferentWhateverStateMachine"]) - node(id: ID!): Node - } - `); + extend type Query { + userById(id: ID!): whatever @context(uses: ["WhateverStateMachine"]) + userByMail(mail: String): whatever + @context(uses: ["DifferentWhateverStateMachine"]) + node(id: ID!): Node + } + `, + { + contextMappingContent: { + WhateverStateMachine: "whatever-state-machine", + AdminStateMachine: "admin-state-machine", + CustomStateMachine: "custom-state-machine", + }, + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` @@ -558,17 +601,20 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; + import type { CustomStateMachine } from "custom-state-machine"; + import type { AdminStateMachine } from "admin-state-machine"; + import type { WhateverStateMachine } from "whatever-state-machine"; export declare namespace Customer { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Customer, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Customer, args: {}, context: CustomStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Admin, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Admin, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Node { export interface Resolvers { @@ -580,7 +626,7 @@ describe(generateTS, () => { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: Models.User | Models.Admin, context: unknown, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; + export type __resolveType = (parent: Models.User | Models.Admin, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; } export declare namespace Query { export interface Resolvers { @@ -590,7 +636,7 @@ describe(generateTS, () => { } export type userById = (model: unknown, args: { readonly id: string; - }, context: unknown, info: ResolveInfo) => PromiseOrValue; + }, context: WhateverStateMachine, info: ResolveInfo) => PromiseOrValue; export type userByMail = (model: unknown, args: { readonly mail?: string | null; }, context: unknown, info: ResolveInfo) => PromiseOrValue; diff --git a/packages/ts-codegen/src/codegen.ts b/packages/ts-codegen/src/codegen.ts index 9162ec7f0..ff5801c31 100644 --- a/packages/ts-codegen/src/codegen.ts +++ b/packages/ts-codegen/src/codegen.ts @@ -41,6 +41,7 @@ export function generateTS( }, ): { files: ts.SourceFile[]; + contextMappingOutput: Record | null; } { try { const context = extractContext( @@ -79,7 +80,10 @@ export function generateTS( result.push(generateLegacyResolvers(context)); } } - return { files: result }; + return { + files: result, + contextMappingOutput: context.getContextMappingContent(), + }; } catch (e) { console.error(e); throw e; diff --git a/packages/ts-codegen/src/context/index.ts b/packages/ts-codegen/src/context/index.ts index 8b0fe06da..133f69517 100644 --- a/packages/ts-codegen/src/context/index.ts +++ b/packages/ts-codegen/src/context/index.ts @@ -144,18 +144,6 @@ export class TsCodegenContext { } } - private findNamedTyped(type: TypeNode): string | null { - if (type.kind === "NamedType") { - return type.name.value; - } else if (type.kind === "NonNullType") { - return this.findNamedTyped(type.type); - } else if (type.kind === "ListType") { - return this.findNamedTyped(type.type); - } - - return null; - } - public mergeContexts(typeNames: string[]): { __context: string[] } | null { const output = typeNames.reduce<{ __context: string[] }>( (contextRootType, interfaceName) => { @@ -256,7 +244,6 @@ export class TsCodegenContext { ancestors[ancestors.length - 1].name.value ] = values; } - console.log(JSON.stringify(this.typeContextMap, null, 2)); } isLegacyCompatMode(): boolean { From 05ce9e179819f3f555450c97b347e64d34a7a2e3 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:15:22 +0100 Subject: [PATCH 05/19] version bumped --- packages/cli/package.json | 2 +- packages/ts-codegen/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 68014b5ab..f887b5325 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/cli", "license": "MIT", - "version": "1.12.1", + "version": "1.13.0-alpha.0", "bin": { "supermassive": "./bin/supermassive.js" }, diff --git a/packages/ts-codegen/package.json b/packages/ts-codegen/package.json index 57acab373..ab19c6f24 100644 --- a/packages/ts-codegen/package.json +++ b/packages/ts-codegen/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/ts-codegen", "license": "MIT", - "version": "2.12.1", + "version": "2.13.0-alpha.0", "main": "./src/index.ts", "repository": { "type": "git", From d69740884c12998eb1fb43ce8dc46afc1a577956 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:29:01 +0100 Subject: [PATCH 06/19] version fix --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index f887b5325..c67473fe6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@graphitation/supermassive-extractors": "^2.2.5", - "@graphitation/ts-codegen": "^2.12.1", + "@graphitation/ts-codegen": "^2.13.0-alpha.0", "commander": "^8.3.0", "fast-glob": "^3.2.12", "graphql": "^15.6.1" From ce6dae9176be6ef868f33096a4f86f83d1620f57 Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Wed, 30 Oct 2024 09:41:18 +0000 Subject: [PATCH 07/19] applying package updates --- ...-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json | 7 ------ ...-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json | 7 ------ examples/apollo-watch-fragments/package.json | 6 ++--- examples/supermassive-todomvc/package.json | 8 +++---- .../CHANGELOG.json | 15 ++++++++++++ .../CHANGELOG.md | 10 +++++++- .../package.json | 4 ++-- .../CHANGELOG.json | 15 ++++++++++++ .../apollo-react-relay-duct-tape/CHANGELOG.md | 10 +++++++- .../apollo-react-relay-duct-tape/package.json | 4 ++-- packages/cli/CHANGELOG.json | 23 +++++++++++++++++++ packages/cli/CHANGELOG.md | 14 ++++++++++- packages/cli/package.json | 4 ++-- .../CHANGELOG.json | 15 ++++++++++++ .../CHANGELOG.md | 10 +++++++- .../package.json | 4 ++-- .../CHANGELOG.json | 15 ++++++++++++ .../CHANGELOG.md | 10 +++++++- .../package.json | 4 ++-- packages/supermassive/package.json | 2 +- packages/ts-codegen/CHANGELOG.json | 15 ++++++++++++ packages/ts-codegen/CHANGELOG.md | 10 +++++++- packages/ts-codegen/package.json | 2 +- packages/webpack-loader/CHANGELOG.json | 15 ++++++++++++ packages/webpack-loader/CHANGELOG.md | 10 +++++++- packages/webpack-loader/package.json | 4 ++-- 26 files changed, 201 insertions(+), 42 deletions(-) delete mode 100644 change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json delete mode 100644 change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json diff --git a/change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json b/change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json deleted file mode 100644 index 538601845..000000000 --- a/change/@graphitation-cli-e50d8020-00b6-4a85-a9a0-c6928e6bdc29.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "metadata written to a file and tests fixed", - "packageName": "@graphitation/cli", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "patch" -} diff --git a/change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json b/change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json deleted file mode 100644 index 534dade59..000000000 --- a/change/@graphitation-ts-codegen-eb1aa4e1-1341-402c-83d4-47e24b8a8c60.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "metadata written to a file and tests fixed", - "packageName": "@graphitation/ts-codegen", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "patch" -} diff --git a/examples/apollo-watch-fragments/package.json b/examples/apollo-watch-fragments/package.json index c7aca3b1f..b9e68b2c6 100644 --- a/examples/apollo-watch-fragments/package.json +++ b/examples/apollo-watch-fragments/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@apollo/client": "~3.6.0", - "@graphitation/apollo-react-relay-duct-tape": "^1.3.5", + "@graphitation/apollo-react-relay-duct-tape": "^1.3.6", "@graphitation/graphql-js-tag": "^0.9.4", "@graphql-tools/schema": "^9.0.19", "graphql": "^15.6.0", @@ -36,9 +36,9 @@ ] }, "devDependencies": { - "@graphitation/apollo-react-relay-duct-tape-compiler": "^1.6.4", + "@graphitation/apollo-react-relay-duct-tape-compiler": "^1.6.5", "@graphitation/embedded-document-artefact-loader": "^0.8.5", - "@graphitation/supermassive": "^3.6.2", + "@graphitation/supermassive": "^3.6.3", "@graphql-codegen/cli": "2.2.0", "@graphql-codegen/typescript": "2.2.2", "@graphql-codegen/typescript-resolvers": "^2.2.1", diff --git a/examples/supermassive-todomvc/package.json b/examples/supermassive-todomvc/package.json index 9d40f595a..2e001f20b 100644 --- a/examples/supermassive-todomvc/package.json +++ b/examples/supermassive-todomvc/package.json @@ -15,10 +15,10 @@ "dependencies": { "@apollo/client": "~3.6.0", "@babel/runtime": "^7.12.0", - "@graphitation/apollo-react-relay-duct-tape": "^1.3.5", - "@graphitation/apollo-react-relay-duct-tape-compiler": "^1.6.4", + "@graphitation/apollo-react-relay-duct-tape": "^1.3.6", + "@graphitation/apollo-react-relay-duct-tape-compiler": "^1.6.5", "@graphitation/graphql-js-tag": "^0.9.4", - "@graphitation/supermassive": "^3.6.2", + "@graphitation/supermassive": "^3.6.3", "@graphitation/ts-transform-graphql-js-tag": "^1.4.4", "concurrently": "^6.5.1", "graphql": "^15.6.1", @@ -58,7 +58,7 @@ "@types/uuid": "^8.3.1", "graphql-let": "^0.18.5", "prettier": "^2.8.7", - "@graphitation/webpack-loader": "^1.0.12", + "@graphitation/webpack-loader": "^1.0.13", "typescript": "^5.5.3", "webpack-dev-server": "^4.13.3" } diff --git a/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.json b/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.json index 408f14a17..93122eb6b 100644 --- a/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.json +++ b/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/apollo-react-relay-duct-tape-compiler", "entries": [ + { + "date": "Wed, 30 Oct 2024 09:41:18 GMT", + "version": "1.6.5", + "tag": "@graphitation/apollo-react-relay-duct-tape-compiler_v1.6.5", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@graphitation/apollo-react-relay-duct-tape-compiler", + "comment": "Bump @graphitation/supermassive to v3.6.3", + "commit": "not available" + } + ] + } + }, { "date": "Thu, 24 Oct 2024 13:15:11 GMT", "version": "1.6.4", diff --git a/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.md b/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.md index 03e2b6c1d..685b0c340 100644 --- a/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.md +++ b/packages/apollo-react-relay-duct-tape-compiler/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/apollo-react-relay-duct-tape-compiler - + +## 1.6.5 + +Wed, 30 Oct 2024 09:41:18 GMT + +### Patches + +- Bump @graphitation/supermassive to v3.6.3 + ## 1.6.4 Thu, 24 Oct 2024 13:15:11 GMT diff --git a/packages/apollo-react-relay-duct-tape-compiler/package.json b/packages/apollo-react-relay-duct-tape-compiler/package.json index d18297958..9296ea83d 100644 --- a/packages/apollo-react-relay-duct-tape-compiler/package.json +++ b/packages/apollo-react-relay-duct-tape-compiler/package.json @@ -2,7 +2,7 @@ "name": "@graphitation/apollo-react-relay-duct-tape-compiler", "description": "The build tools to cater to @graphitation/apollo-react-relay-duct-tape's needs.", "license": "MIT", - "version": "1.6.4", + "version": "1.6.5", "main": "./src/index.ts", "bin": { "duct-tape-compiler": "./src/cli-cjs.js" @@ -42,7 +42,7 @@ }, "peerDependencies": { "graphql": "^15.0.0", - "@graphitation/supermassive": "^3.6.2", + "@graphitation/supermassive": "^3.6.3", "typescript": "^5.5.3" }, "publishConfig": { diff --git a/packages/apollo-react-relay-duct-tape/CHANGELOG.json b/packages/apollo-react-relay-duct-tape/CHANGELOG.json index f970e33a0..1f4360330 100644 --- a/packages/apollo-react-relay-duct-tape/CHANGELOG.json +++ b/packages/apollo-react-relay-duct-tape/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/apollo-react-relay-duct-tape", "entries": [ + { + "date": "Wed, 30 Oct 2024 09:41:18 GMT", + "version": "1.3.6", + "tag": "@graphitation/apollo-react-relay-duct-tape_v1.3.6", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@graphitation/apollo-react-relay-duct-tape", + "comment": "Bump @graphitation/apollo-react-relay-duct-tape-compiler to v1.6.5", + "commit": "not available" + } + ] + } + }, { "date": "Thu, 24 Oct 2024 13:15:11 GMT", "version": "1.3.5", diff --git a/packages/apollo-react-relay-duct-tape/CHANGELOG.md b/packages/apollo-react-relay-duct-tape/CHANGELOG.md index 8f499e2ed..365ca87c4 100644 --- a/packages/apollo-react-relay-duct-tape/CHANGELOG.md +++ b/packages/apollo-react-relay-duct-tape/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/apollo-react-relay-duct-tape - + +## 1.3.6 + +Wed, 30 Oct 2024 09:41:18 GMT + +### Patches + +- Bump @graphitation/apollo-react-relay-duct-tape-compiler to v1.6.5 + ## 1.3.5 Thu, 24 Oct 2024 13:15:11 GMT diff --git a/packages/apollo-react-relay-duct-tape/package.json b/packages/apollo-react-relay-duct-tape/package.json index 56ef34b6b..281f979ab 100644 --- a/packages/apollo-react-relay-duct-tape/package.json +++ b/packages/apollo-react-relay-duct-tape/package.json @@ -2,7 +2,7 @@ "name": "@graphitation/apollo-react-relay-duct-tape", "description": "A compatibility wrapper that provides the react-relay API on top of Apollo Client.", "license": "MIT", - "version": "1.3.5", + "version": "1.3.6", "repository": { "type": "git", "url": "https://github.com/microsoft/graphitation.git", @@ -30,7 +30,7 @@ "graphql": "^15.0.0", "monorepo-scripts": "*", "react": "^18.2.0", - "@graphitation/apollo-react-relay-duct-tape-compiler": "^1.6.4", + "@graphitation/apollo-react-relay-duct-tape-compiler": "^1.6.5", "ts-expect": "^1.3.0" }, "peerDependencies": { diff --git a/packages/cli/CHANGELOG.json b/packages/cli/CHANGELOG.json index 8a1ff4674..506bd9f9a 100644 --- a/packages/cli/CHANGELOG.json +++ b/packages/cli/CHANGELOG.json @@ -1,6 +1,29 @@ { "name": "@graphitation/cli", "entries": [ + { + "date": "Wed, 30 Oct 2024 09:41:18 GMT", + "version": "1.13.0", + "tag": "@graphitation/cli_v1.13.0", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/cli", + "commit": "d69740884c12998eb1fb43ce8dc46afc1a577956", + "comment": "metadata written to a file and tests fixed" + } + ], + "patch": [ + { + "author": "beachball", + "package": "@graphitation/cli", + "comment": "Bump @graphitation/ts-codegen to v2.13.0-alpha.1", + "commit": "not available" + } + ] + } + }, { "date": "Thu, 24 Oct 2024 13:15:11 GMT", "version": "1.12.1", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index d81fe5bfb..0fd037acb 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,9 +1,21 @@ # Change Log - @graphitation/cli - + +## 1.13.0 + +Wed, 30 Oct 2024 09:41:18 GMT + +### Patches + +- Bump @graphitation/ts-codegen to v2.13.0-alpha.1 + +### Changes + +- metadata written to a file and tests fixed (77059398+vejrj@users.noreply.github.com) + ## 1.12.1 Thu, 24 Oct 2024 13:15:11 GMT diff --git a/packages/cli/package.json b/packages/cli/package.json index c67473fe6..6c7555069 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/cli", "license": "MIT", - "version": "1.13.0-alpha.0", + "version": "1.13.0", "bin": { "supermassive": "./bin/supermassive.js" }, @@ -24,7 +24,7 @@ }, "dependencies": { "@graphitation/supermassive-extractors": "^2.2.5", - "@graphitation/ts-codegen": "^2.13.0-alpha.0", + "@graphitation/ts-codegen": "^2.13.0-alpha.1", "commander": "^8.3.0", "fast-glob": "^3.2.12", "graphql": "^15.6.1" diff --git a/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.json b/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.json index 5a0ed92e2..ae42bf9d1 100644 --- a/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.json +++ b/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/graphql-codegen-supermassive-schema-extraction-plugin", "entries": [ + { + "date": "Wed, 30 Oct 2024 09:41:18 GMT", + "version": "2.0.15", + "tag": "@graphitation/graphql-codegen-supermassive-schema-extraction-plugin_v2.0.15", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@graphitation/graphql-codegen-supermassive-schema-extraction-plugin", + "comment": "Bump @graphitation/supermassive to v3.6.3", + "commit": "not available" + } + ] + } + }, { "date": "Thu, 24 Oct 2024 13:15:11 GMT", "version": "2.0.14", diff --git a/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.md b/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.md index de03a506f..5e59822d9 100644 --- a/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.md +++ b/packages/graphql-codegen-supermassive-schema-extraction-plugin/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/graphql-codegen-supermassive-schema-extraction-plugin - + +## 2.0.15 + +Wed, 30 Oct 2024 09:41:18 GMT + +### Patches + +- Bump @graphitation/supermassive to v3.6.3 + ## 2.0.14 Thu, 24 Oct 2024 13:15:11 GMT diff --git a/packages/graphql-codegen-supermassive-schema-extraction-plugin/package.json b/packages/graphql-codegen-supermassive-schema-extraction-plugin/package.json index 7ba0f5082..793336342 100644 --- a/packages/graphql-codegen-supermassive-schema-extraction-plugin/package.json +++ b/packages/graphql-codegen-supermassive-schema-extraction-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/graphql-codegen-supermassive-schema-extraction-plugin", "license": "MIT", - "version": "2.0.14", + "version": "2.0.15", "main": "./src/index.ts", "repository": { "type": "git", @@ -23,7 +23,7 @@ "@graphql-codegen/plugin-helpers": ">= 1.18.0 < 2" }, "dependencies": { - "@graphitation/supermassive": "^3.6.2", + "@graphitation/supermassive": "^3.6.3", "graphql": "^15.0.0" }, "sideEffects": false, diff --git a/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.json b/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.json index 946a34e0f..a2e4be16d 100644 --- a/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.json +++ b/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/graphql-codegen-supermassive-typed-document-node-plugin", "entries": [ + { + "date": "Wed, 30 Oct 2024 09:41:18 GMT", + "version": "1.0.8", + "tag": "@graphitation/graphql-codegen-supermassive-typed-document-node-plugin_v1.0.8", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@graphitation/graphql-codegen-supermassive-typed-document-node-plugin", + "comment": "Bump @graphitation/supermassive to v3.6.3", + "commit": "not available" + } + ] + } + }, { "date": "Thu, 24 Oct 2024 13:15:11 GMT", "version": "1.0.7", diff --git a/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.md b/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.md index 7a2742f96..2f5cf6d82 100644 --- a/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.md +++ b/packages/graphql-codegen-supermassive-typed-document-node-plugin/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/graphql-codegen-supermassive-typed-document-node-plugin - + +## 1.0.8 + +Wed, 30 Oct 2024 09:41:18 GMT + +### Patches + +- Bump @graphitation/supermassive to v3.6.3 + ## 1.0.7 Thu, 24 Oct 2024 13:15:11 GMT diff --git a/packages/graphql-codegen-supermassive-typed-document-node-plugin/package.json b/packages/graphql-codegen-supermassive-typed-document-node-plugin/package.json index 5fcb45c0a..0290227a2 100644 --- a/packages/graphql-codegen-supermassive-typed-document-node-plugin/package.json +++ b/packages/graphql-codegen-supermassive-typed-document-node-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/graphql-codegen-supermassive-typed-document-node-plugin", "license": "MIT", - "version": "1.0.7", + "version": "1.0.8", "main": "./src/index.ts", "repository": { "type": "git", @@ -29,7 +29,7 @@ "@graphql-codegen/visitor-plugin-common": ">= ^1.17.0 < 2", "graphql-tag": ">= 2.11.0 < 3", "@graphql-tools/optimize": "^1.0.1", - "@graphitation/supermassive": "^3.6.2" + "@graphitation/supermassive": "^3.6.3" }, "sideEffects": false, "access": "public", diff --git a/packages/supermassive/package.json b/packages/supermassive/package.json index db5eeca8c..60e85e88c 100644 --- a/packages/supermassive/package.json +++ b/packages/supermassive/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/supermassive", "license": "MIT", - "version": "3.6.2", + "version": "3.6.3", "main": "./src/index.ts", "repository": { "type": "git", diff --git a/packages/ts-codegen/CHANGELOG.json b/packages/ts-codegen/CHANGELOG.json index 466d0383e..62dbfacc0 100644 --- a/packages/ts-codegen/CHANGELOG.json +++ b/packages/ts-codegen/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/ts-codegen", "entries": [ + { + "date": "Wed, 30 Oct 2024 09:41:18 GMT", + "version": "2.13.0-alpha.1", + "tag": "@graphitation/ts-codegen_v2.13.0-alpha.1", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/ts-codegen", + "commit": "d69740884c12998eb1fb43ce8dc46afc1a577956", + "comment": "metadata written to a file and tests fixed" + } + ] + } + }, { "date": "Thu, 24 Oct 2024 13:15:11 GMT", "version": "2.12.1", diff --git a/packages/ts-codegen/CHANGELOG.md b/packages/ts-codegen/CHANGELOG.md index 0d4e706cb..f15ae6085 100644 --- a/packages/ts-codegen/CHANGELOG.md +++ b/packages/ts-codegen/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/ts-codegen - + +## 2.13.0-alpha.1 + +Wed, 30 Oct 2024 09:41:18 GMT + +### Changes + +- metadata written to a file and tests fixed (77059398+vejrj@users.noreply.github.com) + ## 2.12.1 Thu, 24 Oct 2024 13:15:11 GMT diff --git a/packages/ts-codegen/package.json b/packages/ts-codegen/package.json index ab19c6f24..fc927043c 100644 --- a/packages/ts-codegen/package.json +++ b/packages/ts-codegen/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/ts-codegen", "license": "MIT", - "version": "2.13.0-alpha.0", + "version": "2.13.0-alpha.1", "main": "./src/index.ts", "repository": { "type": "git", diff --git a/packages/webpack-loader/CHANGELOG.json b/packages/webpack-loader/CHANGELOG.json index de3331ae3..bc0494d85 100644 --- a/packages/webpack-loader/CHANGELOG.json +++ b/packages/webpack-loader/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/webpack-loader", "entries": [ + { + "date": "Wed, 30 Oct 2024 09:41:18 GMT", + "version": "1.0.13", + "tag": "@graphitation/webpack-loader_v1.0.13", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@graphitation/webpack-loader", + "comment": "Bump @graphitation/supermassive to v3.6.3", + "commit": "not available" + } + ] + } + }, { "date": "Thu, 24 Oct 2024 13:15:11 GMT", "version": "1.0.12", diff --git a/packages/webpack-loader/CHANGELOG.md b/packages/webpack-loader/CHANGELOG.md index 6a5a2d666..ce2be76cc 100644 --- a/packages/webpack-loader/CHANGELOG.md +++ b/packages/webpack-loader/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/webpack-loader - + +## 1.0.13 + +Wed, 30 Oct 2024 09:41:18 GMT + +### Patches + +- Bump @graphitation/supermassive to v3.6.3 + ## 1.0.12 Thu, 24 Oct 2024 13:15:11 GMT diff --git a/packages/webpack-loader/package.json b/packages/webpack-loader/package.json index c397c80c6..378b3f7b5 100644 --- a/packages/webpack-loader/package.json +++ b/packages/webpack-loader/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/webpack-loader", "license": "MIT", - "version": "1.0.12", + "version": "1.0.13", "description": "A fork of @graphql-tools/webpack-loader with supermassive SDL encoding format support", "repository": { "type": "git", @@ -17,7 +17,7 @@ }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", - "@graphitation/supermassive": "^3.6.2" + "@graphitation/supermassive": "^3.6.3" }, "dependencies": { "@graphql-tools/optimize": "^1.1.1", From 22df54e57ce24fd771c02b1e52dc3bd8feb73187 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:56:18 +0100 Subject: [PATCH 08/19] Alpha CLI bump (#468) * CLI alpha bump --- ...phitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json | 7 +++++++ packages/cli/package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json diff --git a/change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json b/change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json new file mode 100644 index 000000000..b1a01f4ab --- /dev/null +++ b/change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "CLI alpha bump", + "packageName": "@graphitation/cli", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/cli/package.json b/packages/cli/package.json index 6c7555069..e5ce19027 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/cli", "license": "MIT", - "version": "1.13.0", + "version": "1.13.0-alpha.0", "bin": { "supermassive": "./bin/supermassive.js" }, From 227a0a842cd5557200329aedb294f82539704be4 Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Wed, 30 Oct 2024 10:07:52 +0000 Subject: [PATCH 09/19] applying package updates --- ...-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json | 7 ------- packages/cli/CHANGELOG.json | 15 +++++++++++++++ packages/cli/CHANGELOG.md | 10 +++++++++- packages/cli/package.json | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) delete mode 100644 change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json diff --git a/change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json b/change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json deleted file mode 100644 index b1a01f4ab..000000000 --- a/change/@graphitation-cli-808a4ecc-9e27-4278-815f-a32cd6c9d64e.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "CLI alpha bump", - "packageName": "@graphitation/cli", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/packages/cli/CHANGELOG.json b/packages/cli/CHANGELOG.json index 506bd9f9a..b92d9eb2d 100644 --- a/packages/cli/CHANGELOG.json +++ b/packages/cli/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/cli", "entries": [ + { + "date": "Wed, 30 Oct 2024 10:07:52 GMT", + "version": "1.13.0-alpha.1", + "tag": "@graphitation/cli_v1.13.0-alpha.1", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/cli", + "commit": "22df54e57ce24fd771c02b1e52dc3bd8feb73187", + "comment": "CLI alpha bump" + } + ] + } + }, { "date": "Wed, 30 Oct 2024 09:41:18 GMT", "version": "1.13.0", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 0fd037acb..2baa28e5f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/cli - + +## 1.13.0-alpha.1 + +Wed, 30 Oct 2024 10:07:52 GMT + +### Changes + +- CLI alpha bump (77059398+vejrj@users.noreply.github.com) + ## 1.13.0 Wed, 30 Oct 2024 09:41:18 GMT diff --git a/packages/cli/package.json b/packages/cli/package.json index e5ce19027..1bb37276a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/cli", "license": "MIT", - "version": "1.13.0-alpha.0", + "version": "1.13.0-alpha.1", "bin": { "supermassive": "./bin/supermassive.js" }, From 826804f7e05cb0b5385f3f1f0367381aba329c47 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:10:20 +0100 Subject: [PATCH 10/19] Codegen returns metadata (#471) * ts-codegen update --- ...on-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json | 7 +++++++ packages/ts-codegen/src/codegen.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json diff --git a/change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json b/change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json new file mode 100644 index 000000000..426cdc39a --- /dev/null +++ b/change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "[HOTFIX] ts-codegen context metadata map returned", + "packageName": "@graphitation/ts-codegen", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/ts-codegen/src/codegen.ts b/packages/ts-codegen/src/codegen.ts index ff5801c31..24ab314b0 100644 --- a/packages/ts-codegen/src/codegen.ts +++ b/packages/ts-codegen/src/codegen.ts @@ -82,7 +82,7 @@ export function generateTS( } return { files: result, - contextMappingOutput: context.getContextMappingContent(), + contextMappingOutput: context.getContextMap(), }; } catch (e) { console.error(e); From 586495129beef3f2431c3f481352325137d2a5e2 Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Wed, 30 Oct 2024 20:39:21 +0000 Subject: [PATCH 11/19] applying package updates --- ...egen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json | 7 ------- packages/cli/CHANGELOG.json | 15 +++++++++++++++ packages/cli/package.json | 2 +- packages/ts-codegen/CHANGELOG.json | 15 +++++++++++++++ packages/ts-codegen/CHANGELOG.md | 10 +++++++++- packages/ts-codegen/package.json | 2 +- 6 files changed, 41 insertions(+), 10 deletions(-) delete mode 100644 change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json diff --git a/change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json b/change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json deleted file mode 100644 index 426cdc39a..000000000 --- a/change/@graphitation-ts-codegen-a72d421b-bf1f-4b43-a411-d9c761131ac1.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "[HOTFIX] ts-codegen context metadata map returned", - "packageName": "@graphitation/ts-codegen", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/packages/cli/CHANGELOG.json b/packages/cli/CHANGELOG.json index b92d9eb2d..2c6e85b50 100644 --- a/packages/cli/CHANGELOG.json +++ b/packages/cli/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/cli", "entries": [ + { + "date": "Wed, 30 Oct 2024 20:39:21 GMT", + "version": "1.13.0-alpha.1", + "tag": "@graphitation/cli_v1.13.0-alpha.1", + "comments": { + "none": [ + { + "author": "beachball", + "package": "@graphitation/cli", + "comment": "Bump @graphitation/ts-codegen to v2.13.0-alpha.2", + "commit": "not available" + } + ] + } + }, { "date": "Wed, 30 Oct 2024 10:07:52 GMT", "version": "1.13.0-alpha.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 1bb37276a..ee3c1a5e5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@graphitation/supermassive-extractors": "^2.2.5", - "@graphitation/ts-codegen": "^2.13.0-alpha.1", + "@graphitation/ts-codegen": "^2.13.0-alpha.2", "commander": "^8.3.0", "fast-glob": "^3.2.12", "graphql": "^15.6.1" diff --git a/packages/ts-codegen/CHANGELOG.json b/packages/ts-codegen/CHANGELOG.json index 62dbfacc0..a65a4ce0f 100644 --- a/packages/ts-codegen/CHANGELOG.json +++ b/packages/ts-codegen/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/ts-codegen", "entries": [ + { + "date": "Wed, 30 Oct 2024 20:39:21 GMT", + "version": "2.13.0-alpha.2", + "tag": "@graphitation/ts-codegen_v2.13.0-alpha.2", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/ts-codegen", + "commit": "826804f7e05cb0b5385f3f1f0367381aba329c47", + "comment": "[HOTFIX] ts-codegen context metadata map returned" + } + ] + } + }, { "date": "Wed, 30 Oct 2024 09:41:18 GMT", "version": "2.13.0-alpha.1", diff --git a/packages/ts-codegen/CHANGELOG.md b/packages/ts-codegen/CHANGELOG.md index f15ae6085..b86f01ec0 100644 --- a/packages/ts-codegen/CHANGELOG.md +++ b/packages/ts-codegen/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/ts-codegen - + +## 2.13.0-alpha.2 + +Wed, 30 Oct 2024 20:39:21 GMT + +### Changes + +- [HOTFIX] ts-codegen context metadata map returned (77059398+vejrj@users.noreply.github.com) + ## 2.13.0-alpha.1 Wed, 30 Oct 2024 09:41:18 GMT diff --git a/packages/ts-codegen/package.json b/packages/ts-codegen/package.json index fc927043c..35ee9facc 100644 --- a/packages/ts-codegen/package.json +++ b/packages/ts-codegen/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/ts-codegen", "license": "MIT", - "version": "2.13.0-alpha.1", + "version": "2.13.0-alpha.2", "main": "./src/index.ts", "repository": { "type": "git", From dd33b80a0c6975c118d8376cbba61cda706802c7 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:20:28 +0100 Subject: [PATCH 12/19] Added context namespace into the codegen (#472) * Added context namespace into the codegen --- ...-94366309-29c0-4ffe-8676-8f92be7757b0.json | 7 + ...-de943d51-cf4e-4173-9c74-c0e650e09424.json | 7 + packages/cli/src/supermassive.ts | 36 ++--- .../ts-codegen/src/__tests__/context.test.ts | 118 +++++++--------- packages/ts-codegen/src/codegen.ts | 11 +- packages/ts-codegen/src/context/index.ts | 62 +++++---- packages/ts-codegen/src/resolvers.ts | 130 +++--------------- 7 files changed, 141 insertions(+), 230 deletions(-) create mode 100644 change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json create mode 100644 change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json diff --git a/change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json b/change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json new file mode 100644 index 000000000..881d295f5 --- /dev/null +++ b/change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Added context namespace into the codegen", + "packageName": "@graphitation/cli", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json b/change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json new file mode 100644 index 000000000..b6e5fa278 --- /dev/null +++ b/change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Added context namespace into the codegen", + "packageName": "@graphitation/ts-codegen", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/cli/src/supermassive.ts b/packages/cli/src/supermassive.ts index cc1b3644a..09ff19786 100644 --- a/packages/cli/src/supermassive.ts +++ b/packages/cli/src/supermassive.ts @@ -19,7 +19,8 @@ type GenerateInterfacesOptions = { enumMigrationJsonFile?: string; enumMigrationExceptionsJsonFile?: string; generateOnlyEnums?: boolean; - contextMappingFile?: string; + contextNamespaceName?: string; + contextNamespacePath?: string; scope?: string; }; @@ -49,11 +50,15 @@ export function supermassive(): Command { "-ci, --context-import [contextImport]", "from where to import context", ) + .option("-cn, --context-name [contextName]", "Context name") .option( - "-cm, --context-mapping-file [contextMappingFile]", - "context mapping file", + "-cm, --context-namespace-name [contextNamespaceName]", + "context namespace name", + ) + .option( + "-cm, --context-namespace-path [contextNamespacePath]", + "context namespace path", ) - .option("-cn, --context-name [contextName]", "Context name") .option("-ei, --enums-import [enumsImport]", "from where to import enums") .option("-l, --legacy", "generate legacy types") .option("--legacy-models", "do not use models for object types") @@ -128,26 +133,6 @@ async function generateInterfaces( options.outputDir ? options.outputDir : "__generated__", ); - const { contextMappingFile } = options; - let contextMappingContent: Record | null = null; - let fullContextMappingFilePath: string; - if (contextMappingFile) { - if (path.isAbsolute(contextMappingFile)) { - fullContextMappingFilePath = contextMappingFile; - } else { - fullContextMappingFilePath = path.join( - process.cwd(), - contextMappingFile, - ); - } - - if (fsSync.existsSync(fullContextMappingFilePath)) { - contextMappingContent = JSON.parse( - await fs.readFile(fullContextMappingFilePath, { encoding: "utf-8" }), - ); - } - } - const result = generateTS(document, { outputPath, documentPath: fullPath, @@ -159,7 +144,8 @@ async function generateInterfaces( useStringUnionsInsteadOfEnums: !!options.useStringUnionsInsteadOfEnums, generateOnlyEnums: !!options.generateOnlyEnums, modelScope: options.scope || null, - contextMappingContent: contextMappingContent, + contextNamespaceName: options.contextNamespaceName, + contextNamespacePath: options.contextNamespacePath, }); await fs.mkdir(outputPath, { recursive: true }); diff --git a/packages/ts-codegen/src/__tests__/context.test.ts b/packages/ts-codegen/src/__tests__/context.test.ts index 5b179eb9d..d3cdc514d 100644 --- a/packages/ts-codegen/src/__tests__/context.test.ts +++ b/packages/ts-codegen/src/__tests__/context.test.ts @@ -45,11 +45,8 @@ describe(generateTS, () => { } `, { - contextMappingContent: { - UserStateMachine: "user-state-machine", - PostStateMachine: "post-state-machine", - IdUserStateMachine: "id-user-state-machine", - }, + contextNamespaceName: "ContextNamespace", + contextNamespacePath: "context-package", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -91,9 +88,7 @@ describe(generateTS, () => { import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { UserStateMachine } from "user-state-machine"; - import type { IdUserStateMachine } from "id-user-state-machine"; - import type { PostStateMachine } from "post-state-machine"; + import type { ContextNamespace } from "context-package"; export declare namespace Post { export interface Resolvers { readonly id?: id; @@ -104,7 +99,7 @@ describe(generateTS, () => { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Message, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Message, args: {}, context: ContextNamespace.MessageStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { @@ -122,19 +117,19 @@ describe(generateTS, () => { readonly avatar?: avatar; readonly avatarRequired?: avatarRequired; } - export type id = (model: Models.User, args: {}, context: IdUserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type name = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type messagesWithAnswersNonRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined> | null | undefined>; - export type messagesWithAnswersRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>>; - export type messagesWithAnswersAllRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>>; - export type messagesNonRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type messagesWithArrayRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>; - export type messagesRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>; - export type messagesOnlyMessageRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type post = (model: Models.User, args: {}, context: PostStateMachine, info: ResolveInfo) => PromiseOrValue; - export type postRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type avatar = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type avatarRequired = (model: Models.User, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.User, args: {}, context: ContextNamespace.IdUserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type messagesWithAnswersNonRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined> | null | undefined>; + export type messagesWithAnswersRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>>; + export type messagesWithAnswersAllRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue>>; + export type messagesNonRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type messagesWithArrayRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue>; + export type messagesRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue>; + export type messagesOnlyMessageRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type post = (model: Models.User, args: {}, context: ContextNamespace.PostStateMachine, info: ResolveInfo) => PromiseOrValue; + export type postRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type avatar = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type avatarRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -145,12 +140,12 @@ describe(generateTS, () => { readonly requiredPost?: requiredPost; readonly optionalPost?: optionalPost; } - export type requiredUsers = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue>; - export type optionalUsers = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type optionalUser = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type requiredUser = (model: unknown, args: {}, context: UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type requiredUsers = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue>; + export type optionalUsers = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type optionalUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type requiredUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; export type requiredPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type optionalPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type optionalPost = (model: unknown, args: {}, context: ContextNamespace.OptionalPostStateMachine, info: ResolveInfo) => PromiseOrValue; } " `); @@ -310,11 +305,8 @@ describe(generateTS, () => { } `, { - contextMappingContent: { - AdminStateMachine: "admin-state-machine", - PersonaStateMachine: "persona-state-machine", - NodeStateMachine: "node-state-machine", - }, + contextNamespaceName: "ContextNamespace", + contextNamespacePath: "context-package", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -344,34 +336,32 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { NodeStateMachine } from "node-state-machine"; - import type { PersonaStateMachine } from "persona-state-machine"; - import type { AdminStateMachine } from "admin-state-machine"; + import type { ContextNamespace } from "context-package"; export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Persona { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: ContextNamespace.PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: NodeStateMachine & PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: ContextNamespace.NodeStateMachine & ContextNamespace.PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { readonly id?: id; readonly rank?: rank; } - export type id = (model: Models.Admin, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue; - export type rank = (model: Models.Admin, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Admin, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue; + export type rank = (model: Models.Admin, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -379,7 +369,7 @@ describe(generateTS, () => { readonly admins?: admins; } export type users = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type admins = (model: unknown, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type admins = (model: unknown, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; } " `); @@ -430,9 +420,8 @@ describe(generateTS, () => { } `, { - contextMappingContent: { - NodeStateMachine: "node-state-machine", - }, + contextNamespaceName: "ContextNamespace", + contextNamespacePath: "context-package", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -456,20 +445,20 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { NodeStateMachine } from "node-state-machine"; + import type { ContextNamespace } from "context-package"; export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { readonly id?: id; readonly name?: name; } - export type id = (model: Models.User, args: {}, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; - export type name = (model: Models.User, args: {}, context: NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.User, args: {}, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -569,11 +558,8 @@ describe(generateTS, () => { } `, { - contextMappingContent: { - WhateverStateMachine: "whatever-state-machine", - AdminStateMachine: "admin-state-machine", - CustomStateMachine: "custom-state-machine", - }, + contextNamespaceName: "ContextNamespace", + contextNamespacePath: "context-package", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -601,20 +587,18 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { CustomStateMachine } from "custom-state-machine"; - import type { AdminStateMachine } from "admin-state-machine"; - import type { WhateverStateMachine } from "whatever-state-machine"; + import type { ContextNamespace } from "context-package"; export declare namespace Customer { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Customer, args: {}, context: CustomStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Customer, args: {}, context: ContextNamespace.CustomStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Admin, args: {}, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Admin, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue; } export declare namespace Node { export interface Resolvers { @@ -626,7 +610,7 @@ describe(generateTS, () => { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: Models.User | Models.Admin, context: AdminStateMachine, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; + export type __resolveType = (parent: Models.User | Models.Admin, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; } export declare namespace Query { export interface Resolvers { @@ -636,10 +620,10 @@ describe(generateTS, () => { } export type userById = (model: unknown, args: { readonly id: string; - }, context: WhateverStateMachine, info: ResolveInfo) => PromiseOrValue; + }, context: ContextNamespace.WhateverStateMachine, info: ResolveInfo) => PromiseOrValue; export type userByMail = (model: unknown, args: { readonly mail?: string | null; - }, context: unknown, info: ResolveInfo) => PromiseOrValue; + }, context: ContextNamespace.DifferentWhateverStateMachine, info: ResolveInfo) => PromiseOrValue; export type node = (model: unknown, args: { readonly id: string; }, context: unknown, info: ResolveInfo) => PromiseOrValue; @@ -665,7 +649,8 @@ function runGenerateTest( enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; modelScope?: string; - contextMappingContent?: Record | null; + contextNamespaceName?: string; + contextNamespacePath?: string; } = {}, ): { enums?: string; @@ -680,7 +665,8 @@ function runGenerateTest( enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; modelScope?: string; - contextMappingContent?: Record | null; + contextNamespaceName?: string; + contextNamespacePath?: string; } { const fullOptions: { outputPath: string; @@ -693,7 +679,8 @@ function runGenerateTest( useStringUnionsInsteadOfEnums?: boolean; enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; - contextMappingContent?: Record | null; + contextNamespaceName?: string; + contextNamespacePath?: string; } = { outputPath: "__generated__", documentPath: "./typedef.graphql", @@ -721,7 +708,8 @@ function runGenerateTest( enums: enums && printer.printFile(enums), inputs: inputs && printer.printFile(inputs), models: printer.printFile(models), - contextMappingContent: options.contextMappingContent || null, + contextNamespaceName: options.contextNamespaceName, + contextNamespacePath: options.contextNamespacePath, resolvers: printer.printFile(resolvers), legacyTypes: legacyTypes && printer.printFile(legacyTypes), legacyResolvers: legacyResolvers && printer.printFile(legacyResolvers), diff --git a/packages/ts-codegen/src/codegen.ts b/packages/ts-codegen/src/codegen.ts index 24ab314b0..5344a6b3c 100644 --- a/packages/ts-codegen/src/codegen.ts +++ b/packages/ts-codegen/src/codegen.ts @@ -23,7 +23,8 @@ export function generateTS( generateOnlyEnums, enumNamesToMigrate, enumNamesToKeep, - contextMappingContent, + contextNamespaceName, + contextNamespacePath, }: { outputPath: string; documentPath: string; @@ -37,11 +38,12 @@ export function generateTS( generateOnlyEnums?: boolean; enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; - contextMappingContent?: Record | null; + contextNamespaceName?: string; + contextNamespacePath?: string; }, ): { files: ts.SourceFile[]; - contextMappingOutput: Record | null; + contextMappingOutput: any | null; } { try { const context = extractContext( @@ -57,7 +59,8 @@ export function generateTS( modelScope, enumNamesToMigrate, enumNamesToKeep, - contextMappingContent, + contextNamespaceName, + contextNamespacePath, }, document, outputPath, diff --git a/packages/ts-codegen/src/context/index.ts b/packages/ts-codegen/src/context/index.ts index 133f69517..848b8ac89 100644 --- a/packages/ts-codegen/src/context/index.ts +++ b/packages/ts-codegen/src/context/index.ts @@ -51,7 +51,8 @@ export type TsCodegenContextOptions = { }; legacyCompat: boolean; legacyNoModelsForObjects: boolean; - contextMappingContent?: Record | null; + contextNamespaceName?: string; + contextNamespacePath?: string; useStringUnionsInsteadOfEnums: boolean; enumNamesToMigrate: string[] | null; enumNamesToKeep: string[] | null; @@ -101,7 +102,6 @@ export class TsCodegenContext { private typeContextMap: any; private allRootTypeNames: Set; private typeNameToType: Map; - private contextMappingContent: Record | null; private usedEntitiesInModels: Set; private usedEntitiesInResolvers: Set; private usedEntitiesInInputs: Set; @@ -113,6 +113,7 @@ export class TsCodegenContext { private typeNameToModels: Map; private legacyInterfaces: Set; context?: { name: string; from: string }; + contextNamespace?: { name: string; from: string }; hasUsedModelInInputs: boolean; hasUsedEnumsInModels: boolean; hasEnums: boolean; @@ -135,7 +136,13 @@ export class TsCodegenContext { this.hasInputs = false; this.hasEnums = Boolean(options.enumsImport); this.hasUsedEnumsInModels = false; - this.contextMappingContent = options.contextMappingContent || null; + + if (options.contextNamespaceName && options.contextNamespacePath) { + this.contextNamespace = { + name: options.contextNamespaceName, + from: options.contextNamespacePath, + }; + } if (options.context.from && options.context.name) { this.context = { name: options.context.name, @@ -174,29 +181,38 @@ export class TsCodegenContext { return null; } - public getContextMappingContent(): Record | null { - return this.contextMappingContent; + public getContextNamespace(): { name: string; from: string } | null { + return this.contextNamespace || null; } public getContextTypeNode(typeNames?: string[] | null) { - if (!typeNames || !typeNames.length) { + const contextNamespace = this.contextNamespace; + if (!typeNames || !typeNames.length || !contextNamespace) { return this.getContextType().toTypeReference(); } else if ( (typeNames.length === 1 && this.context) || typeNames.length > 1 ) { + const typeNameWithNamespace = typeNames.map((typeName) => { + return `${contextNamespace.name}.${typeName}`; + }); + return factory.createIntersectionTypeNode( - (this.context ? [this.context.name, ...typeNames] : typeNames).map( - (type: string) => { - return factory.createTypeReferenceNode( - factory.createIdentifier(type), - undefined, - ); - }, - ), + (this.context + ? [this.context.name, ...typeNameWithNamespace] + : typeNameWithNamespace + ).map((type: string) => { + return factory.createTypeReferenceNode( + factory.createIdentifier(type), + undefined, + ); + }), ); } else { - return new TypeLocation(null, typeNames[0]).toTypeReference(); + return new TypeLocation( + null, + `${contextNamespace.name}.${typeNames[0]}`, + ).toTypeReference(); } } @@ -664,7 +680,7 @@ export function extractContext( ...options, }; const context = new TsCodegenContext(fullOptions); - const { contextMappingContent } = options; + const { contextNamespaceName, contextNamespacePath } = options; visit(document, { Directive: { enter(node, _key, _parent, _path: any, ancestors) { @@ -694,7 +710,11 @@ export function extractContext( } const typeName = (typeDef as InterfaceTypeDefinitionNode).name.value; context.addLegacyInterface(typeName); - } else if (node.name.value === "context" && contextMappingContent) { + } else if ( + node.name.value === "context" && + contextNamespaceName && + contextNamespacePath + ) { if ( node.arguments?.length !== 1 || node.arguments[0].name.value !== "uses" || @@ -711,12 +731,8 @@ export function extractContext( return item.value; }); - const filtredDirectiveValues = directiveValues.filter( - (directiveValue) => contextMappingContent[directiveValue], - ); - - if (filtredDirectiveValues.length) { - context.initContextMap(ancestors, filtredDirectiveValues); + if (directiveValues.length) { + context.initContextMap(ancestors, directiveValues); } } }, diff --git a/packages/ts-codegen/src/resolvers.ts b/packages/ts-codegen/src/resolvers.ts index 23eab60db..7231537de 100644 --- a/packages/ts-codegen/src/resolvers.ts +++ b/packages/ts-codegen/src/resolvers.ts @@ -29,122 +29,26 @@ export function generateResolvers(context: TsCodegenContext): ts.SourceFile { ), ); - if ( - Object.keys(context.getContextMap()).length && - context.getContextMappingContent() - ) { - const contextImportNames: Set = new Set(); - - for (const [, root] of Object.entries(context.getContextMap())) { - const rootValue: string[] = (root as any).__context; - if (rootValue) { - if ( - rootValue.every((importName: string) => - contextImportNames.has(importName), - ) - ) { - continue; - } - - const imports = (rootValue as string[]).reduce< - Record - >((acc: Record, importName: string) => { - const importPath = context.getContextMappingContent()?.[importName]; - if (importPath) { - if (!acc[importPath]) { - acc[importPath] = []; - } - acc[importPath].push(importName); - } - return acc; - }, {}); - - for (const [importPath, importNames] of Object.entries(imports)) { - statements.push( - factory.createImportDeclaration( - undefined, - factory.createImportClause( - true, - undefined, - factory.createNamedImports( - importNames - .map((importName: string) => { - if (contextImportNames.has(importName)) { - return; - } - contextImportNames.add(importName); - - return factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier(importName), - ); - }) - .filter(Boolean) as ts.ImportSpecifier[], - ), - ), - factory.createStringLiteral(importPath), - ), - ); - } - } - - for (const [key, value] of Object.entries(root as any)) { - if (key.startsWith("__")) { - continue; - } - - if ( - (value as string[]).every((importName: string) => - contextImportNames.has(importName), - ) - ) { - continue; - } - const imports = (value as string[]).reduce>( - (acc: Record, importName: string) => { - const importPath = context.getContextMappingContent()?.[importName]; - if (importPath) { - if (!acc[importPath]) { - acc[importPath] = []; - } - acc[importPath].push(importName); - } - return acc; - }, - {}, - ); + const contextNamespace = context.getContextNamespace(); - for (const [importPath, importNames] of Object.entries(imports)) { - statements.push( - factory.createImportDeclaration( + if (Object.keys(context.getContextMap()).length && contextNamespace) { + statements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + true, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, undefined, - factory.createImportClause( - true, - undefined, - factory.createNamedImports( - importNames - .map((importName: string) => { - if (contextImportNames.has(importName)) { - return; - } - contextImportNames.add(importName); - - return factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier(importName), - ); - }) - .filter(Boolean) as ts.ImportSpecifier[], - ), - ), - factory.createStringLiteral(importPath), + factory.createIdentifier(contextNamespace.name), ), - ); - } - } - } + ]), + ), + factory.createStringLiteral(contextNamespace.from), + ), + ); } if (context.hasInputs) { From 469f45bbdb1e83ffaf83f52fe2670e8cf62d666f Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Mon, 4 Nov 2024 17:34:20 +0000 Subject: [PATCH 13/19] applying package updates --- ...-94366309-29c0-4ffe-8676-8f92be7757b0.json | 7 ------- ...-de943d51-cf4e-4173-9c74-c0e650e09424.json | 7 ------- packages/cli/CHANGELOG.json | 21 +++++++++++++++++++ packages/cli/CHANGELOG.md | 11 +++++++++- packages/cli/package.json | 4 ++-- packages/ts-codegen/CHANGELOG.json | 15 +++++++++++++ packages/ts-codegen/CHANGELOG.md | 10 ++++++++- packages/ts-codegen/package.json | 2 +- 8 files changed, 58 insertions(+), 19 deletions(-) delete mode 100644 change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json delete mode 100644 change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json diff --git a/change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json b/change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json deleted file mode 100644 index 881d295f5..000000000 --- a/change/@graphitation-cli-94366309-29c0-4ffe-8676-8f92be7757b0.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Added context namespace into the codegen", - "packageName": "@graphitation/cli", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json b/change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json deleted file mode 100644 index b6e5fa278..000000000 --- a/change/@graphitation-ts-codegen-de943d51-cf4e-4173-9c74-c0e650e09424.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Added context namespace into the codegen", - "packageName": "@graphitation/ts-codegen", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/packages/cli/CHANGELOG.json b/packages/cli/CHANGELOG.json index 2c6e85b50..ef9c20e55 100644 --- a/packages/cli/CHANGELOG.json +++ b/packages/cli/CHANGELOG.json @@ -1,6 +1,27 @@ { "name": "@graphitation/cli", "entries": [ + { + "date": "Mon, 04 Nov 2024 17:34:20 GMT", + "version": "1.13.0-alpha.2", + "tag": "@graphitation/cli_v1.13.0-alpha.2", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/cli", + "commit": "dd33b80a0c6975c118d8376cbba61cda706802c7", + "comment": "Added context namespace into the codegen" + }, + { + "author": "beachball", + "package": "@graphitation/cli", + "comment": "Bump @graphitation/ts-codegen to v2.13.0-alpha.3", + "commit": "not available" + } + ] + } + }, { "date": "Wed, 30 Oct 2024 20:39:21 GMT", "version": "1.13.0-alpha.1", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 2baa28e5f..8a5e40892 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @graphitation/cli - + +## 1.13.0-alpha.2 + +Mon, 04 Nov 2024 17:34:20 GMT + +### Changes + +- Added context namespace into the codegen (77059398+vejrj@users.noreply.github.com) +- Bump @graphitation/ts-codegen to v2.13.0-alpha.3 + ## 1.13.0-alpha.1 Wed, 30 Oct 2024 10:07:52 GMT diff --git a/packages/cli/package.json b/packages/cli/package.json index ee3c1a5e5..e686b7f10 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/cli", "license": "MIT", - "version": "1.13.0-alpha.1", + "version": "1.13.0-alpha.2", "bin": { "supermassive": "./bin/supermassive.js" }, @@ -24,7 +24,7 @@ }, "dependencies": { "@graphitation/supermassive-extractors": "^2.2.5", - "@graphitation/ts-codegen": "^2.13.0-alpha.2", + "@graphitation/ts-codegen": "^2.13.0-alpha.3", "commander": "^8.3.0", "fast-glob": "^3.2.12", "graphql": "^15.6.1" diff --git a/packages/ts-codegen/CHANGELOG.json b/packages/ts-codegen/CHANGELOG.json index a65a4ce0f..c0fed1e75 100644 --- a/packages/ts-codegen/CHANGELOG.json +++ b/packages/ts-codegen/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/ts-codegen", "entries": [ + { + "date": "Mon, 04 Nov 2024 17:34:20 GMT", + "version": "2.13.0-alpha.3", + "tag": "@graphitation/ts-codegen_v2.13.0-alpha.3", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/ts-codegen", + "commit": "dd33b80a0c6975c118d8376cbba61cda706802c7", + "comment": "Added context namespace into the codegen" + } + ] + } + }, { "date": "Wed, 30 Oct 2024 20:39:21 GMT", "version": "2.13.0-alpha.2", diff --git a/packages/ts-codegen/CHANGELOG.md b/packages/ts-codegen/CHANGELOG.md index b86f01ec0..bb4e297b6 100644 --- a/packages/ts-codegen/CHANGELOG.md +++ b/packages/ts-codegen/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/ts-codegen - + +## 2.13.0-alpha.3 + +Mon, 04 Nov 2024 17:34:20 GMT + +### Changes + +- Added context namespace into the codegen (77059398+vejrj@users.noreply.github.com) + ## 2.13.0-alpha.2 Wed, 30 Oct 2024 20:39:21 GMT diff --git a/packages/ts-codegen/package.json b/packages/ts-codegen/package.json index 35ee9facc..fb923ec02 100644 --- a/packages/ts-codegen/package.json +++ b/packages/ts-codegen/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/ts-codegen", "license": "MIT", - "version": "2.13.0-alpha.2", + "version": "2.13.0-alpha.3", "main": "./src/index.ts", "repository": { "type": "git", From f3b1a34db0e9fc7bbf4ddf972fd509f4e7d9f5ac Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:00:22 +0100 Subject: [PATCH 14/19] POC added state machines import templating (#475) * Added import state machines templating --- ...-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json | 7 + ...-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json | 7 + packages/cli/src/supermassive.ts | 12 +- .../ts-codegen/src/__tests__/context.test.ts | 133 +++++++++-------- packages/ts-codegen/src/codegen.ts | 12 +- packages/ts-codegen/src/context/index.ts | 55 ++++--- packages/ts-codegen/src/resolvers.ts | 138 ++++++++++++++++-- 7 files changed, 257 insertions(+), 107 deletions(-) create mode 100644 change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json create mode 100644 change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json diff --git a/change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json b/change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json new file mode 100644 index 000000000..e77232752 --- /dev/null +++ b/change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Added import state machines templating", + "packageName": "@graphitation/cli", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json b/change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json new file mode 100644 index 000000000..9c6ef0a37 --- /dev/null +++ b/change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Added import state machines templating", + "packageName": "@graphitation/ts-codegen", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/cli/src/supermassive.ts b/packages/cli/src/supermassive.ts index 09ff19786..72ba43070 100644 --- a/packages/cli/src/supermassive.ts +++ b/packages/cli/src/supermassive.ts @@ -19,8 +19,8 @@ type GenerateInterfacesOptions = { enumMigrationJsonFile?: string; enumMigrationExceptionsJsonFile?: string; generateOnlyEnums?: boolean; - contextNamespaceName?: string; - contextNamespacePath?: string; + contextImportNameTemplate?: string; + contextImportPathTemplate?: string; scope?: string; }; @@ -52,11 +52,11 @@ export function supermassive(): Command { ) .option("-cn, --context-name [contextName]", "Context name") .option( - "-cm, --context-namespace-name [contextNamespaceName]", + "-cm, --context-import-name-template [contextImportNameTemplate]", "context namespace name", ) .option( - "-cm, --context-namespace-path [contextNamespacePath]", + "-cm, --context-import-path-template [contextImportPathTemplate]", "context namespace path", ) .option("-ei, --enums-import [enumsImport]", "from where to import enums") @@ -144,8 +144,8 @@ async function generateInterfaces( useStringUnionsInsteadOfEnums: !!options.useStringUnionsInsteadOfEnums, generateOnlyEnums: !!options.generateOnlyEnums, modelScope: options.scope || null, - contextNamespaceName: options.contextNamespaceName, - contextNamespacePath: options.contextNamespacePath, + contextImportNameTemplate: options.contextImportNameTemplate, + contextImportPathTemplate: options.contextImportPathTemplate, }); await fs.mkdir(outputPath, { recursive: true }); diff --git a/packages/ts-codegen/src/__tests__/context.test.ts b/packages/ts-codegen/src/__tests__/context.test.ts index d3cdc514d..ee96cb66b 100644 --- a/packages/ts-codegen/src/__tests__/context.test.ts +++ b/packages/ts-codegen/src/__tests__/context.test.ts @@ -16,11 +16,11 @@ describe(generateTS, () => { } type Message { - id: ID! @context(uses: ["MessageStateMachine"]) + id: ID! @context(stateMachines: ["message"]) } - type User @context(uses: ["UserStateMachine"]) { - id: ID! @context(uses: ["IdUserStateMachine"]) + type User @context(stateMachines: ["user"]) { + id: ID! @context(stateMachines: ["id-user"]) name: String messagesWithAnswersNonRequired: [[Message]] messagesWithAnswersRequired: [[Message]]! @@ -29,7 +29,7 @@ describe(generateTS, () => { messagesWithArrayRequired: [Message]! messagesRequired: [Message!]! messagesOnlyMessageRequired: [Message!] - post: Post @context(uses: ["PostStateMachine"]) + post: Post @context(stateMachines: ["post"]) postRequired: Post! avatar: Avatar avatarRequired: Avatar! @@ -41,12 +41,12 @@ describe(generateTS, () => { optionalUser: User requiredUser: User! requiredPost: Post! - optionalPost: Post @context(uses: ["OptionalPostStateMachine"]) + optionalPost: Post @context(stateMachines: ["optional-post"]) } `, { - contextNamespaceName: "ContextNamespace", - contextNamespacePath: "context-package", + contextImportNameTemplate: "I${contextName}StateMachineContext", + contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -88,7 +88,11 @@ describe(generateTS, () => { import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { ContextNamespace } from "context-package"; + import type { IMessageStateMachineContext } from "@msteams/core-cdl-sync-message"; + import type { IUserStateMachineContext } from "@msteams/core-cdl-sync-user"; + import type { IIdUserStateMachineContext } from "@msteams/core-cdl-sync-id-user"; + import type { IPostStateMachineContext } from "@msteams/core-cdl-sync-post"; + import type { IOptionalPostStateMachineContext } from "@msteams/core-cdl-sync-optional-post"; export declare namespace Post { export interface Resolvers { readonly id?: id; @@ -99,7 +103,7 @@ describe(generateTS, () => { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Message, args: {}, context: ContextNamespace.MessageStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Message, args: {}, context: IMessageStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { @@ -117,19 +121,19 @@ describe(generateTS, () => { readonly avatar?: avatar; readonly avatarRequired?: avatarRequired; } - export type id = (model: Models.User, args: {}, context: ContextNamespace.IdUserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type name = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type messagesWithAnswersNonRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined> | null | undefined>; - export type messagesWithAnswersRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>>; - export type messagesWithAnswersAllRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue>>; - export type messagesNonRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type messagesWithArrayRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue>; - export type messagesRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue>; - export type messagesOnlyMessageRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type post = (model: Models.User, args: {}, context: ContextNamespace.PostStateMachine, info: ResolveInfo) => PromiseOrValue; - export type postRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type avatar = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; - export type avatarRequired = (model: Models.User, args: {}, context: ContextNamespace.UserStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.User, args: {}, context: IIdUserStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type messagesWithAnswersNonRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue | null | undefined> | null | undefined>; + export type messagesWithAnswersRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue | null | undefined>>; + export type messagesWithAnswersAllRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue>>; + export type messagesNonRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type messagesWithArrayRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue>; + export type messagesRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue>; + export type messagesOnlyMessageRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type post = (model: Models.User, args: {}, context: IPostStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type postRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type avatar = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type avatarRequired = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -145,7 +149,7 @@ describe(generateTS, () => { export type optionalUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; export type requiredUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; export type requiredPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type optionalPost = (model: unknown, args: {}, context: ContextNamespace.OptionalPostStateMachine, info: ResolveInfo) => PromiseOrValue; + export type optionalPost = (model: unknown, args: {}, context: IOptionalPostStateMachineContext, info: ResolveInfo) => PromiseOrValue; } " `); @@ -280,11 +284,11 @@ describe(generateTS, () => { test("case when interface implements multiple interfaces", () => { const { resolvers, models, enums, inputs } = runGenerateTest( graphql` - interface Node @context(uses: ["NodeStateMachine"]) { + interface Node @context(stateMachines: ["node"]) { id: ID! } - interface Persona @context(uses: ["PersonaStateMachine"]) { + interface Persona @context(stateMachines: ["persona"]) { phone: String! } @@ -294,7 +298,7 @@ describe(generateTS, () => { } type Admin implements Node & Persona - @context(uses: ["AdminStateMachine"]) { + @context(stateMachines: ["admin"]) { id: ID! rank: Int! } @@ -305,8 +309,8 @@ describe(generateTS, () => { } `, { - contextNamespaceName: "ContextNamespace", - contextNamespacePath: "context-package", + contextImportNameTemplate: "I${contextName}StateMachineContext", + contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -336,32 +340,34 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { ContextNamespace } from "context-package"; + import type { INodeStateMachineContext } from "@msteams/core-cdl-sync-node"; + import type { IPersonaStateMachineContext } from "@msteams/core-cdl-sync-persona"; + import type { IAdminStateMachineContext } from "@msteams/core-cdl-sync-admin"; export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: INodeStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace Persona { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: ContextNamespace.PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: IPersonaStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: ContextNamespace.NodeStateMachine & ContextNamespace.PersonaStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: INodeStateMachineContext & IPersonaStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { readonly id?: id; readonly rank?: rank; } - export type id = (model: Models.Admin, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue; - export type rank = (model: Models.Admin, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Admin, args: {}, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type rank = (model: Models.Admin, args: {}, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -369,7 +375,7 @@ describe(generateTS, () => { readonly admins?: admins; } export type users = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type admins = (model: unknown, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type admins = (model: unknown, args: {}, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue | null | undefined>; } " `); @@ -406,7 +412,7 @@ describe(generateTS, () => { test("implements", () => { const { resolvers, models, enums, inputs } = runGenerateTest( graphql` - interface Node @context(uses: ["NodeStateMachine"]) { + interface Node @context(stateMachines: ["node"]) { id: ID! } @@ -420,8 +426,8 @@ describe(generateTS, () => { } `, { - contextNamespaceName: "ContextNamespace", - contextNamespacePath: "context-package", + contextImportNameTemplate: "I${contextName}StateMachineContext", + contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -445,20 +451,20 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { ContextNamespace } from "context-package"; + import type { INodeStateMachineContext } from "@msteams/core-cdl-sync-node"; export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: INodeStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace User { export interface Resolvers { readonly id?: id; readonly name?: name; } - export type id = (model: Models.User, args: {}, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; - export type name = (model: Models.User, args: {}, context: ContextNamespace.NodeStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.User, args: {}, context: INodeStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: INodeStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -536,11 +542,11 @@ describe(generateTS, () => { test("Union and interface types", () => { const { resolvers, models, enums, inputs } = runGenerateTest( graphql` - type Customer @context(uses: ["CustomStateMachine"]) { + type Customer @context(stateMachines: ["custom"]) { id: ID! } - type Admin @context(uses: ["AdminStateMachine"]) { + type Admin @context(stateMachines: ["admin"]) { id: ID! } @@ -551,15 +557,15 @@ describe(generateTS, () => { union whatever = User | Admin extend type Query { - userById(id: ID!): whatever @context(uses: ["WhateverStateMachine"]) + userById(id: ID!): whatever @context(stateMachines: ["whatever"]) userByMail(mail: String): whatever - @context(uses: ["DifferentWhateverStateMachine"]) + @context(stateMachines: ["different-whatever"]) node(id: ID!): Node } `, { - contextNamespaceName: "ContextNamespace", - contextNamespacePath: "context-package", + contextImportNameTemplate: "I${contextName}StateMachineContext", + contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -587,18 +593,21 @@ describe(generateTS, () => { "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { ContextNamespace } from "context-package"; + import type { ICustomStateMachineContext } from "@msteams/core-cdl-sync-custom"; + import type { IAdminStateMachineContext } from "@msteams/core-cdl-sync-admin"; + import type { IWhateverStateMachineContext } from "@msteams/core-cdl-sync-whatever"; + import type { IDifferentWhateverStateMachineContext } from "@msteams/core-cdl-sync-different-whatever"; export declare namespace Customer { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Customer, args: {}, context: ContextNamespace.CustomStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Customer, args: {}, context: ICustomStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Admin, args: {}, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Admin, args: {}, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue; } export declare namespace Node { export interface Resolvers { @@ -610,7 +619,7 @@ describe(generateTS, () => { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: Models.User | Models.Admin, context: ContextNamespace.AdminStateMachine, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; + export type __resolveType = (parent: Models.User | Models.Admin, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; } export declare namespace Query { export interface Resolvers { @@ -620,10 +629,10 @@ describe(generateTS, () => { } export type userById = (model: unknown, args: { readonly id: string; - }, context: ContextNamespace.WhateverStateMachine, info: ResolveInfo) => PromiseOrValue; + }, context: IWhateverStateMachineContext, info: ResolveInfo) => PromiseOrValue; export type userByMail = (model: unknown, args: { readonly mail?: string | null; - }, context: ContextNamespace.DifferentWhateverStateMachine, info: ResolveInfo) => PromiseOrValue; + }, context: IDifferentWhateverStateMachineContext, info: ResolveInfo) => PromiseOrValue; export type node = (model: unknown, args: { readonly id: string; }, context: unknown, info: ResolveInfo) => PromiseOrValue; @@ -649,8 +658,8 @@ function runGenerateTest( enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; modelScope?: string; - contextNamespaceName?: string; - contextNamespacePath?: string; + contextImportNameTemplate?: string; + contextImportPathTemplate?: string; } = {}, ): { enums?: string; @@ -665,8 +674,8 @@ function runGenerateTest( enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; modelScope?: string; - contextNamespaceName?: string; - contextNamespacePath?: string; + contextImportNameTemplate?: string; + contextImportPathTemplate?: string; } { const fullOptions: { outputPath: string; @@ -679,8 +688,8 @@ function runGenerateTest( useStringUnionsInsteadOfEnums?: boolean; enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; - contextNamespaceName?: string; - contextNamespacePath?: string; + contextImportNameTemplate?: string; + contextImportPathTemplate?: string; } = { outputPath: "__generated__", documentPath: "./typedef.graphql", @@ -708,8 +717,8 @@ function runGenerateTest( enums: enums && printer.printFile(enums), inputs: inputs && printer.printFile(inputs), models: printer.printFile(models), - contextNamespaceName: options.contextNamespaceName, - contextNamespacePath: options.contextNamespacePath, + contextImportNameTemplate: options.contextImportNameTemplate, + contextImportPathTemplate: options.contextImportPathTemplate, resolvers: printer.printFile(resolvers), legacyTypes: legacyTypes && printer.printFile(legacyTypes), legacyResolvers: legacyResolvers && printer.printFile(legacyResolvers), diff --git a/packages/ts-codegen/src/codegen.ts b/packages/ts-codegen/src/codegen.ts index 5344a6b3c..e0560ac85 100644 --- a/packages/ts-codegen/src/codegen.ts +++ b/packages/ts-codegen/src/codegen.ts @@ -23,8 +23,8 @@ export function generateTS( generateOnlyEnums, enumNamesToMigrate, enumNamesToKeep, - contextNamespaceName, - contextNamespacePath, + contextImportNameTemplate, + contextImportPathTemplate, }: { outputPath: string; documentPath: string; @@ -38,8 +38,8 @@ export function generateTS( generateOnlyEnums?: boolean; enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; - contextNamespaceName?: string; - contextNamespacePath?: string; + contextImportNameTemplate?: string; + contextImportPathTemplate?: string; }, ): { files: ts.SourceFile[]; @@ -59,8 +59,8 @@ export function generateTS( modelScope, enumNamesToMigrate, enumNamesToKeep, - contextNamespaceName, - contextNamespacePath, + contextImportNameTemplate, + contextImportPathTemplate, }, document, outputPath, diff --git a/packages/ts-codegen/src/context/index.ts b/packages/ts-codegen/src/context/index.ts index 848b8ac89..4bdecadc6 100644 --- a/packages/ts-codegen/src/context/index.ts +++ b/packages/ts-codegen/src/context/index.ts @@ -25,6 +25,7 @@ import ts, { import { DefinitionImport, DefinitionModel } from "../types"; import { createImportDeclaration } from "./utilities"; import { + camelCase, createListType, createNonNullableType, createNullableType, @@ -51,8 +52,8 @@ export type TsCodegenContextOptions = { }; legacyCompat: boolean; legacyNoModelsForObjects: boolean; - contextNamespaceName?: string; - contextNamespacePath?: string; + contextImportPathTemplate?: string; + contextImportNameTemplate?: string; useStringUnionsInsteadOfEnums: boolean; enumNamesToMigrate: string[] | null; enumNamesToKeep: string[] | null; @@ -113,7 +114,7 @@ export class TsCodegenContext { private typeNameToModels: Map; private legacyInterfaces: Set; context?: { name: string; from: string }; - contextNamespace?: { name: string; from: string }; + contextTemplate?: { nameTemplate: string; pathTemplate: string }; hasUsedModelInInputs: boolean; hasUsedEnumsInModels: boolean; hasEnums: boolean; @@ -137,10 +138,13 @@ export class TsCodegenContext { this.hasEnums = Boolean(options.enumsImport); this.hasUsedEnumsInModels = false; - if (options.contextNamespaceName && options.contextNamespacePath) { - this.contextNamespace = { - name: options.contextNamespaceName, - from: options.contextNamespacePath, + if ( + options.contextImportNameTemplate && + options.contextImportPathTemplate + ) { + this.contextTemplate = { + nameTemplate: options.contextImportNameTemplate, + pathTemplate: options.contextImportPathTemplate, }; } if (options.context.from && options.context.name) { @@ -181,20 +185,34 @@ export class TsCodegenContext { return null; } - public getContextNamespace(): { name: string; from: string } | null { - return this.contextNamespace || null; + public replaceTemplateWithContextName( + template: string, + contextName: string, + camelCased = true, + ) { + return template.replace( + "${contextName}", + camelCased ? camelCase(contextName, { pascalCase: true }) : contextName, + ); + } + + public getContextTemplate() { + return this.contextTemplate || null; } public getContextTypeNode(typeNames?: string[] | null) { - const contextNamespace = this.contextNamespace; - if (!typeNames || !typeNames.length || !contextNamespace) { + const contextTemplate = this.contextTemplate; + if (!typeNames || !typeNames.length || !contextTemplate) { return this.getContextType().toTypeReference(); } else if ( (typeNames.length === 1 && this.context) || typeNames.length > 1 ) { const typeNameWithNamespace = typeNames.map((typeName) => { - return `${contextNamespace.name}.${typeName}`; + return this.replaceTemplateWithContextName( + contextTemplate.nameTemplate, + typeName, + ); }); return factory.createIntersectionTypeNode( @@ -211,7 +229,10 @@ export class TsCodegenContext { } else { return new TypeLocation( null, - `${contextNamespace.name}.${typeNames[0]}`, + this.replaceTemplateWithContextName( + contextTemplate.nameTemplate, + typeNames[0], + ), ).toTypeReference(); } } @@ -680,7 +701,7 @@ export function extractContext( ...options, }; const context = new TsCodegenContext(fullOptions); - const { contextNamespaceName, contextNamespacePath } = options; + const { contextImportNameTemplate, contextImportPathTemplate } = options; visit(document, { Directive: { enter(node, _key, _parent, _path: any, ancestors) { @@ -712,12 +733,12 @@ export function extractContext( context.addLegacyInterface(typeName); } else if ( node.name.value === "context" && - contextNamespaceName && - contextNamespacePath + contextImportNameTemplate && + contextImportPathTemplate ) { if ( node.arguments?.length !== 1 || - node.arguments[0].name.value !== "uses" || + node.arguments[0].name.value !== "stateMachines" || node.arguments[0].value.kind !== "ListValue" ) { throw new Error("Invalid context use"); diff --git a/packages/ts-codegen/src/resolvers.ts b/packages/ts-codegen/src/resolvers.ts index 7231537de..9dba5aa35 100644 --- a/packages/ts-codegen/src/resolvers.ts +++ b/packages/ts-codegen/src/resolvers.ts @@ -29,26 +29,132 @@ export function generateResolvers(context: TsCodegenContext): ts.SourceFile { ), ); - const contextNamespace = context.getContextNamespace(); + const contextTemplate = context.getContextTemplate(); - if (Object.keys(context.getContextMap()).length && contextNamespace) { - statements.push( - factory.createImportDeclaration( - undefined, - factory.createImportClause( - true, - undefined, - factory.createNamedImports([ - factory.createImportSpecifier( + if (Object.keys(context.getContextMap()).length && contextTemplate) { + const contextImportNames: Set = new Set(); + for (const [, root] of Object.entries(context.getContextMap())) { + const rootValue: string[] = (root as any).__context; + if (rootValue) { + if ( + rootValue.every((importName: string) => + contextImportNames.has(importName), + ) + ) { + continue; + } + const imports = (rootValue as string[]).reduce< + Record + >((acc: Record, importName: string) => { + const importPath = context.replaceTemplateWithContextName( + contextTemplate.pathTemplate, + importName, + false, + ); + if (importPath) { + if (!acc[importPath]) { + acc[importPath] = []; + } + acc[importPath].push( + context.replaceTemplateWithContextName( + contextTemplate.nameTemplate, + importName, + ), + ); + } + return acc; + }, {}); + for (const [importPath, importNames] of Object.entries(imports)) { + statements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + true, + undefined, + factory.createNamedImports( + importNames + .map((importName: string) => { + if (contextImportNames.has(importName)) { + return; + } + contextImportNames.add(importName); + return factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier(importName), + ); + }) + .filter(Boolean) as ts.ImportSpecifier[], + ), + ), + factory.createStringLiteral(importPath), + ), + ); + } + } + for (const [key, value] of Object.entries(root as any)) { + if (key.startsWith("__")) { + continue; + } + if ( + (value as string[]).every((importName: string) => + contextImportNames.has(importName), + ) + ) { + continue; + } + const imports = (value as string[]).reduce>( + (acc: Record, importName: string) => { + const importPath = context.replaceTemplateWithContextName( + contextTemplate.pathTemplate, + importName, false, + ); + if (importPath) { + if (!acc[importPath]) { + acc[importPath] = []; + } + acc[importPath].push( + context.replaceTemplateWithContextName( + contextTemplate.nameTemplate, + importName, + ), + ); + } + return acc; + }, + {}, + ); + + for (const [importPath, importNames] of Object.entries(imports)) { + statements.push( + factory.createImportDeclaration( undefined, - factory.createIdentifier(contextNamespace.name), + factory.createImportClause( + true, + undefined, + factory.createNamedImports( + importNames + .map((importName: string) => { + if (contextImportNames.has(importName)) { + return; + } + contextImportNames.add(importName); + return factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier(importName), + ); + }) + .filter(Boolean) as ts.ImportSpecifier[], + ), + ), + factory.createStringLiteral(importPath), ), - ]), - ), - factory.createStringLiteral(contextNamespace.from), - ), - ); + ); + } + } + } } if (context.hasInputs) { From d5cf785720b3fc449c135202f374d83b470add1f Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Mon, 11 Nov 2024 14:13:12 +0000 Subject: [PATCH 15/19] applying package updates --- ...-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json | 7 ------- ...-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json | 7 ------- packages/cli/CHANGELOG.json | 21 +++++++++++++++++++ packages/cli/CHANGELOG.md | 11 +++++++++- packages/cli/package.json | 4 ++-- packages/ts-codegen/CHANGELOG.json | 15 +++++++++++++ packages/ts-codegen/CHANGELOG.md | 10 ++++++++- packages/ts-codegen/package.json | 2 +- 8 files changed, 58 insertions(+), 19 deletions(-) delete mode 100644 change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json delete mode 100644 change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json diff --git a/change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json b/change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json deleted file mode 100644 index e77232752..000000000 --- a/change/@graphitation-cli-ab959660-1ac4-4fa1-9c22-d35a03354ef4.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Added import state machines templating", - "packageName": "@graphitation/cli", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json b/change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json deleted file mode 100644 index 9c6ef0a37..000000000 --- a/change/@graphitation-ts-codegen-e4c9d6bb-0a5e-4ec3-9eba-ee7b713b05b6.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Added import state machines templating", - "packageName": "@graphitation/ts-codegen", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/packages/cli/CHANGELOG.json b/packages/cli/CHANGELOG.json index ef9c20e55..e173427c4 100644 --- a/packages/cli/CHANGELOG.json +++ b/packages/cli/CHANGELOG.json @@ -1,6 +1,27 @@ { "name": "@graphitation/cli", "entries": [ + { + "date": "Mon, 11 Nov 2024 14:13:12 GMT", + "version": "1.13.0-alpha.3", + "tag": "@graphitation/cli_v1.13.0-alpha.3", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/cli", + "commit": "f3b1a34db0e9fc7bbf4ddf972fd509f4e7d9f5ac", + "comment": "Added import state machines templating" + }, + { + "author": "beachball", + "package": "@graphitation/cli", + "comment": "Bump @graphitation/ts-codegen to v2.13.0-alpha.4", + "commit": "not available" + } + ] + } + }, { "date": "Mon, 04 Nov 2024 17:34:20 GMT", "version": "1.13.0-alpha.2", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 8a5e40892..aaf42998e 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @graphitation/cli - + +## 1.13.0-alpha.3 + +Mon, 11 Nov 2024 14:13:12 GMT + +### Changes + +- Added import state machines templating (77059398+vejrj@users.noreply.github.com) +- Bump @graphitation/ts-codegen to v2.13.0-alpha.4 + ## 1.13.0-alpha.2 Mon, 04 Nov 2024 17:34:20 GMT diff --git a/packages/cli/package.json b/packages/cli/package.json index e686b7f10..d9926d2ac 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/cli", "license": "MIT", - "version": "1.13.0-alpha.2", + "version": "1.13.0-alpha.3", "bin": { "supermassive": "./bin/supermassive.js" }, @@ -24,7 +24,7 @@ }, "dependencies": { "@graphitation/supermassive-extractors": "^2.2.5", - "@graphitation/ts-codegen": "^2.13.0-alpha.3", + "@graphitation/ts-codegen": "^2.13.0-alpha.4", "commander": "^8.3.0", "fast-glob": "^3.2.12", "graphql": "^15.6.1" diff --git a/packages/ts-codegen/CHANGELOG.json b/packages/ts-codegen/CHANGELOG.json index c0fed1e75..bb818c9ec 100644 --- a/packages/ts-codegen/CHANGELOG.json +++ b/packages/ts-codegen/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/ts-codegen", "entries": [ + { + "date": "Mon, 11 Nov 2024 14:13:12 GMT", + "version": "2.13.0-alpha.4", + "tag": "@graphitation/ts-codegen_v2.13.0-alpha.4", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/ts-codegen", + "commit": "f3b1a34db0e9fc7bbf4ddf972fd509f4e7d9f5ac", + "comment": "Added import state machines templating" + } + ] + } + }, { "date": "Mon, 04 Nov 2024 17:34:20 GMT", "version": "2.13.0-alpha.3", diff --git a/packages/ts-codegen/CHANGELOG.md b/packages/ts-codegen/CHANGELOG.md index bb4e297b6..e7559d69a 100644 --- a/packages/ts-codegen/CHANGELOG.md +++ b/packages/ts-codegen/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/ts-codegen - + +## 2.13.0-alpha.4 + +Mon, 11 Nov 2024 14:13:12 GMT + +### Changes + +- Added import state machines templating (77059398+vejrj@users.noreply.github.com) + ## 2.13.0-alpha.3 Mon, 04 Nov 2024 17:34:20 GMT diff --git a/packages/ts-codegen/package.json b/packages/ts-codegen/package.json index fb923ec02..ff55ddc60 100644 --- a/packages/ts-codegen/package.json +++ b/packages/ts-codegen/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/ts-codegen", "license": "MIT", - "version": "2.13.0-alpha.3", + "version": "2.13.0-alpha.4", "main": "./src/index.ts", "repository": { "type": "git", From e9a34254f389d06042d7deb11bca819dd2d642ed Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Wed, 13 Nov 2024 21:39:45 +0100 Subject: [PATCH 16/19] Solution cleanup - new cli parameters and inheritance fixes (#477) Solution cleanup - new cli parameters and inheritance fixes --- ...-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json | 7 + ...-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json | 7 + packages/cli/src/supermassive.ts | 53 +++-- .../ts-codegen/src/__tests__/context.test.ts | 93 +++++--- .../ts-codegen/src/__tests__/index.test.ts | 10 +- packages/ts-codegen/src/codegen.ts | 30 ++- packages/ts-codegen/src/context/index.ts | 209 +++++++++++------- packages/ts-codegen/src/context/utilities.ts | 32 ++- packages/ts-codegen/src/resolvers.ts | 158 ++++--------- 9 files changed, 348 insertions(+), 251 deletions(-) create mode 100644 change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json create mode 100644 change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json diff --git a/change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json b/change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json new file mode 100644 index 000000000..62dbbf751 --- /dev/null +++ b/change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Solution cleanup - new cli parameters and inheritance fixes", + "packageName": "@graphitation/cli", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json b/change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json new file mode 100644 index 000000000..905827b65 --- /dev/null +++ b/change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Solution cleanup - new cli parameters and inheritance fixes", + "packageName": "@graphitation/ts-codegen", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/cli/src/supermassive.ts b/packages/cli/src/supermassive.ts index 72ba43070..d4b2a16a9 100644 --- a/packages/cli/src/supermassive.ts +++ b/packages/cli/src/supermassive.ts @@ -10,8 +10,8 @@ import * as glob from "fast-glob"; type GenerateInterfacesOptions = { outputDir?: string; - contextImport?: string; - contextName?: string; + contextTypePath?: string; + contextTypeName?: string; enumsImport?: string; legacy?: boolean; legacyModels?: boolean; @@ -19,8 +19,10 @@ type GenerateInterfacesOptions = { enumMigrationJsonFile?: string; enumMigrationExceptionsJsonFile?: string; generateOnlyEnums?: boolean; - contextImportNameTemplate?: string; - contextImportPathTemplate?: string; + contextSubTypeNameTemplate?: string; + contextSubTypePathTemplate?: string; + defaultContextSubTypePath?: string; + defaultContextSubTypeName?: string; scope?: string; }; @@ -47,16 +49,24 @@ export function supermassive(): Command { "output directory relative to file, default generated", ) .option( - "-ci, --context-import [contextImport]", + "-ci, --context-type-path [contextTypePath]", "from where to import context", ) - .option("-cn, --context-name [contextName]", "Context name") + .option("-cn, --context-type-name [contextTypeName]", "Context type name") .option( - "-cm, --context-import-name-template [contextImportNameTemplate]", + "-dcp, --default-context-sub-type-path [defaultContextSubTypePath]", + "from where to import context", + ) + .option( + "-dcn, --default-context-sub-type-name [defaultContextSubTypeName]", + "Context name", + ) + .option( + "-cm, --context-sub-type-name-template [contextSubTypeNameTemplate]", "context namespace name", ) .option( - "-cm, --context-import-path-template [contextImportPathTemplate]", + "-cm, --context-sub-type-path-template [contextSubTypePathTemplate]", "context namespace path", ) .option("-ei, --enums-import [enumsImport]", "from where to import enums") @@ -93,16 +103,19 @@ function getFiles(inputs: Array) { .flat() .filter(Boolean); } -function getContextPath(outputDir: string, contextImport: string | undefined) { - if (!contextImport) { +function getContextPath( + outputDir: string, + contextTypePath: string | undefined, +) { + if (!contextTypePath) { return; } - if (!contextImport.startsWith(".")) { - return contextImport; + if (!contextTypePath.startsWith(".")) { + return contextTypePath; } - const contextDir = path.join(process.cwd(), contextImport); + const contextDir = path.join(process.cwd(), contextTypePath); return path .relative(outputDir, contextDir) @@ -136,16 +149,22 @@ async function generateInterfaces( const result = generateTS(document, { outputPath, documentPath: fullPath, - contextImport: getContextPath(outputPath, options.contextImport) || null, - contextName: options.contextName, + contextTypePath: + getContextPath(outputPath, options.contextTypePath) || null, + contextTypeName: options.contextTypeName, + contextSubTypeNameTemplate: options.contextSubTypeNameTemplate, + contextSubTypePathTemplate: options.contextSubTypePathTemplate, + defaultContextSubTypePath: getContextPath( + outputPath, + options.defaultContextSubTypePath, + ), + defaultContextSubTypeName: options.defaultContextSubTypeName, enumsImport: getContextPath(outputPath, options.enumsImport) || null, legacyCompat: !!options.legacy, legacyNoModelsForObjects: !!options.legacyModels, useStringUnionsInsteadOfEnums: !!options.useStringUnionsInsteadOfEnums, generateOnlyEnums: !!options.generateOnlyEnums, modelScope: options.scope || null, - contextImportNameTemplate: options.contextImportNameTemplate, - contextImportPathTemplate: options.contextImportPathTemplate, }); await fs.mkdir(outputPath, { recursive: true }); diff --git a/packages/ts-codegen/src/__tests__/context.test.ts b/packages/ts-codegen/src/__tests__/context.test.ts index ee96cb66b..7bb7c4a04 100644 --- a/packages/ts-codegen/src/__tests__/context.test.ts +++ b/packages/ts-codegen/src/__tests__/context.test.ts @@ -41,12 +41,12 @@ describe(generateTS, () => { optionalUser: User requiredUser: User! requiredPost: Post! - optionalPost: Post @context(stateMachines: ["optional-post"]) + optionalPost: Post } `, { - contextImportNameTemplate: "I${contextName}StateMachineContext", - contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -92,7 +92,6 @@ describe(generateTS, () => { import type { IUserStateMachineContext } from "@msteams/core-cdl-sync-user"; import type { IIdUserStateMachineContext } from "@msteams/core-cdl-sync-id-user"; import type { IPostStateMachineContext } from "@msteams/core-cdl-sync-post"; - import type { IOptionalPostStateMachineContext } from "@msteams/core-cdl-sync-optional-post"; export declare namespace Post { export interface Resolvers { readonly id?: id; @@ -149,7 +148,7 @@ describe(generateTS, () => { export type optionalUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; export type requiredUser = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; export type requiredPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; - export type optionalPost = (model: unknown, args: {}, context: IOptionalPostStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type optionalPost = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; } " `); @@ -309,8 +308,8 @@ describe(generateTS, () => { } `, { - contextImportNameTemplate: "I${contextName}StateMachineContext", - contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -359,7 +358,7 @@ describe(generateTS, () => { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: unknown, context: INodeStateMachineContext & IPersonaStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { @@ -375,7 +374,7 @@ describe(generateTS, () => { readonly admins?: admins; } export type users = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; - export type admins = (model: unknown, args: {}, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue | null | undefined>; + export type admins = (model: unknown, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue | null | undefined>; } " `); @@ -409,14 +408,20 @@ describe(generateTS, () => { `); }); - test("implements", () => { + test("implements -> @context in interfaces should be used only in resolveType", () => { const { resolvers, models, enums, inputs } = runGenerateTest( graphql` interface Node @context(stateMachines: ["node"]) { id: ID! } - type User implements Node { + interface Customer implements Node + @context(stateMachines: ["customer"]) { + id: ID! + name: String! + } + + type User implements Node & Customer { id: ID! name: String! } @@ -426,8 +431,8 @@ describe(generateTS, () => { } `, { - contextImportNameTemplate: "I${contextName}StateMachineContext", - contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -440,7 +445,10 @@ describe(generateTS, () => { export interface Node extends BaseModel { readonly __typename?: string; } - export interface User extends BaseModel, Node { + export interface Customer extends BaseModel, Node { + readonly __typename?: string; + } + export interface User extends BaseModel, Node, Customer { readonly __typename?: "User"; readonly id: string; readonly name: string; @@ -452,19 +460,26 @@ describe(generateTS, () => { import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; import type { INodeStateMachineContext } from "@msteams/core-cdl-sync-node"; + import type { ICustomerStateMachineContext } from "@msteams/core-cdl-sync-customer"; export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; } export type __resolveType = (parent: unknown, context: INodeStateMachineContext, info: ResolveInfo) => PromiseOrValue; } + export declare namespace Customer { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: unknown, context: ICustomerStateMachineContext, info: ResolveInfo) => PromiseOrValue; + } export declare namespace User { export interface Resolvers { readonly id?: id; readonly name?: name; } - export type id = (model: Models.User, args: {}, context: INodeStateMachineContext, info: ResolveInfo) => PromiseOrValue; - export type name = (model: Models.User, args: {}, context: INodeStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + export type name = (model: Models.User, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; } export declare namespace Query { export interface Resolvers { @@ -476,13 +491,14 @@ describe(generateTS, () => { `); }); - test("Enum", () => { + test("applying @context to enum shouldn't affect anything", () => { const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - enum PresenceAvailability { + enum PresenceAvailability @context(stateMachines: ["shouldnt-apply"]) { Available Away Offline } + type User { id: ID! availability: PresenceAvailability! @@ -550,6 +566,10 @@ describe(generateTS, () => { id: ID! } + type User @context(stateMachines: ["user"]) { + id: ID! + } + interface Node { id: ID! } @@ -564,8 +584,8 @@ describe(generateTS, () => { } `, { - contextImportNameTemplate: "I${contextName}StateMachineContext", - contextImportPathTemplate: "@msteams/core-cdl-sync-${contextName}", + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", }, ); expect(enums).toMatchInlineSnapshot(`undefined`); @@ -583,6 +603,10 @@ describe(generateTS, () => { readonly __typename?: "Admin"; readonly id: string; } + export interface User extends BaseModel { + readonly __typename?: "User"; + readonly id: string; + } export interface Node extends BaseModel { readonly __typename?: string; } @@ -595,6 +619,7 @@ describe(generateTS, () => { import * as Models from "./models.interface"; import type { ICustomStateMachineContext } from "@msteams/core-cdl-sync-custom"; import type { IAdminStateMachineContext } from "@msteams/core-cdl-sync-admin"; + import type { IUserStateMachineContext } from "@msteams/core-cdl-sync-user"; import type { IWhateverStateMachineContext } from "@msteams/core-cdl-sync-whatever"; import type { IDifferentWhateverStateMachineContext } from "@msteams/core-cdl-sync-different-whatever"; export declare namespace Customer { @@ -609,6 +634,12 @@ describe(generateTS, () => { } export type id = (model: Models.Admin, args: {}, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue; } + export declare namespace User { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.User, args: {}, context: IUserStateMachineContext, info: ResolveInfo) => PromiseOrValue; + } export declare namespace Node { export interface Resolvers { readonly __resolveType?: __resolveType; @@ -619,7 +650,7 @@ describe(generateTS, () => { export interface Resolvers { readonly __resolveType?: __resolveType; } - export type __resolveType = (parent: Models.User | Models.Admin, context: IAdminStateMachineContext, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; + export type __resolveType = (parent: Models.User | Models.Admin, context: unknown, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; } export declare namespace Query { export interface Resolvers { @@ -648,7 +679,7 @@ function runGenerateTest( options: { outputPath?: string; documentPath?: string; - contextImport?: string; + defaultContextTypePath?: string; contextName?: string; legacyCompat?: boolean; enumsImport?: string; @@ -658,8 +689,8 @@ function runGenerateTest( enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; modelScope?: string; - contextImportNameTemplate?: string; - contextImportPathTemplate?: string; + contextSubTypeNameTemplate?: string; + contextSubTypePathTemplate?: string; } = {}, ): { enums?: string; @@ -674,13 +705,13 @@ function runGenerateTest( enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; modelScope?: string; - contextImportNameTemplate?: string; - contextImportPathTemplate?: string; + contextSubTypeNameTemplate?: string; + contextSubTypePathTemplate?: string; } { const fullOptions: { outputPath: string; documentPath: string; - contextImport?: string | null; + defaultContextTypePath?: string | null; contextName?: string; legacyCompat?: boolean; legacyEnumsCompatibility?: boolean; @@ -688,8 +719,8 @@ function runGenerateTest( useStringUnionsInsteadOfEnums?: boolean; enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; - contextImportNameTemplate?: string; - contextImportPathTemplate?: string; + contextSubTypeNameTemplate?: string; + contextSubTypePathTemplate?: string; } = { outputPath: "__generated__", documentPath: "./typedef.graphql", @@ -717,8 +748,8 @@ function runGenerateTest( enums: enums && printer.printFile(enums), inputs: inputs && printer.printFile(inputs), models: printer.printFile(models), - contextImportNameTemplate: options.contextImportNameTemplate, - contextImportPathTemplate: options.contextImportPathTemplate, + contextSubTypeNameTemplate: options.contextSubTypeNameTemplate, + contextSubTypePathTemplate: options.contextSubTypePathTemplate, resolvers: printer.printFile(resolvers), legacyTypes: legacyTypes && printer.printFile(legacyTypes), legacyResolvers: legacyResolvers && printer.printFile(legacyResolvers), diff --git a/packages/ts-codegen/src/__tests__/index.test.ts b/packages/ts-codegen/src/__tests__/index.test.ts index 9c5794e7b..f92107b33 100644 --- a/packages/ts-codegen/src/__tests__/index.test.ts +++ b/packages/ts-codegen/src/__tests__/index.test.ts @@ -1639,7 +1639,7 @@ describe(generateTS, () => { }); }); - it("generateTS without ContextName and ContextImport", () => { + it("generateTS without ContextName and defaultContextTypePath", () => { const { models, resolvers, enums, inputs } = runGenerateTest(graphql` interface Node { id: ID! @@ -2299,8 +2299,8 @@ function runGenerateTest( options: { outputPath?: string; documentPath?: string; - contextImport?: string; - contextName?: string; + defaultContextTypePath?: string; + contextTypeName?: string; legacyCompat?: boolean; enumsImport?: string; legacyNoModelsForObjects?: boolean; @@ -2327,8 +2327,8 @@ function runGenerateTest( const fullOptions: { outputPath: string; documentPath: string; - contextImport?: string | null; - contextName?: string; + defaultContextTypePath?: string | null; + contextTypeName?: string; legacyCompat?: boolean; legacyEnumsCompatibility?: boolean; legacyNoModelsForObjects?: boolean; diff --git a/packages/ts-codegen/src/codegen.ts b/packages/ts-codegen/src/codegen.ts index e0560ac85..0f93b093b 100644 --- a/packages/ts-codegen/src/codegen.ts +++ b/packages/ts-codegen/src/codegen.ts @@ -13,8 +13,8 @@ export function generateTS( { outputPath, documentPath, - contextImport, - contextName, + contextTypePath, + contextTypeName, enumsImport, legacyCompat, useStringUnionsInsteadOfEnums, @@ -23,13 +23,15 @@ export function generateTS( generateOnlyEnums, enumNamesToMigrate, enumNamesToKeep, - contextImportNameTemplate, - contextImportPathTemplate, + contextSubTypeNameTemplate, + contextSubTypePathTemplate, + defaultContextSubTypePath, + defaultContextSubTypeName, }: { outputPath: string; documentPath: string; - contextImport?: string | null; - contextName?: string; + contextTypePath?: string | null; + contextTypeName?: string; enumsImport?: string | null; legacyCompat?: boolean; useStringUnionsInsteadOfEnums?: boolean; @@ -38,8 +40,10 @@ export function generateTS( generateOnlyEnums?: boolean; enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; - contextImportNameTemplate?: string; - contextImportPathTemplate?: string; + contextSubTypeNameTemplate?: string; + contextSubTypePathTemplate?: string; + defaultContextSubTypePath?: string; + defaultContextSubTypeName?: string; }, ): { files: ts.SourceFile[]; @@ -49,8 +53,8 @@ export function generateTS( const context = extractContext( { context: { - name: contextName, - from: contextImport || null, + name: contextTypeName, + from: contextTypePath || null, }, legacyCompat, useStringUnionsInsteadOfEnums, @@ -59,8 +63,10 @@ export function generateTS( modelScope, enumNamesToMigrate, enumNamesToKeep, - contextImportNameTemplate, - contextImportPathTemplate, + contextSubTypeNameTemplate, + contextSubTypePathTemplate, + defaultContextSubTypePath, + defaultContextSubTypeName, }, document, outputPath, diff --git a/packages/ts-codegen/src/context/index.ts b/packages/ts-codegen/src/context/index.ts index 4bdecadc6..894f70d68 100644 --- a/packages/ts-codegen/src/context/index.ts +++ b/packages/ts-codegen/src/context/index.ts @@ -52,8 +52,10 @@ export type TsCodegenContextOptions = { }; legacyCompat: boolean; legacyNoModelsForObjects: boolean; - contextImportPathTemplate?: string; - contextImportNameTemplate?: string; + contextSubTypePathTemplate?: string; + contextSubTypeNameTemplate?: string; + defaultContextSubTypePath?: string; + defaultContextSubTypeName?: string; useStringUnionsInsteadOfEnums: boolean; enumNamesToMigrate: string[] | null; enumNamesToKeep: string[] | null; @@ -97,10 +99,12 @@ const TsCodegenContextDefault: TsCodegenContextOptions = { }; type ModelNameAndImport = { modelName: string; imp: DefinitionImport }; - +type ContextTypeItem = { __context?: string[] } & { [key: string]: string[] }; export class TsCodegenContext { private allTypes: Array; - private typeContextMap: any; + private typeContextMap: { + [key: string]: ContextTypeItem; + }; private allRootTypeNames: Set; private typeNameToType: Map; private usedEntitiesInModels: Set; @@ -114,7 +118,11 @@ export class TsCodegenContext { private typeNameToModels: Map; private legacyInterfaces: Set; context?: { name: string; from: string }; - contextTemplate?: { nameTemplate: string; pathTemplate: string }; + contextDefaultSubTypeTemplate?: { + nameTemplate: string; + pathTemplate: string; + }; + contextDefaultSubTypeContext?: { name: string; from: string }; hasUsedModelInInputs: boolean; hasUsedEnumsInModels: boolean; hasEnums: boolean; @@ -139,14 +147,25 @@ export class TsCodegenContext { this.hasUsedEnumsInModels = false; if ( - options.contextImportNameTemplate && - options.contextImportPathTemplate + options.contextSubTypeNameTemplate && + options.contextSubTypePathTemplate ) { - this.contextTemplate = { - nameTemplate: options.contextImportNameTemplate, - pathTemplate: options.contextImportPathTemplate, + this.contextDefaultSubTypeTemplate = { + nameTemplate: options.contextSubTypeNameTemplate, + pathTemplate: options.contextSubTypePathTemplate, }; } + + if ( + options.defaultContextSubTypeName && + options.defaultContextSubTypePath + ) { + this.contextDefaultSubTypeContext = { + name: options.defaultContextSubTypeName, + from: options.defaultContextSubTypePath, + }; + } + if (options.context.from && options.context.name) { this.context = { name: options.context.name, @@ -155,23 +174,6 @@ export class TsCodegenContext { } } - public mergeContexts(typeNames: string[]): { __context: string[] } | null { - const output = typeNames.reduce<{ __context: string[] }>( - (contextRootType, interfaceName) => { - if (this.getContextMap()[interfaceName]?.__context) { - contextRootType.__context = [ - ...contextRootType.__context, - ...this.getContextMap()[interfaceName].__context, - ]; - } - return contextRootType; - }, - { __context: [] }, - ); - - return output.__context.length ? output : null; - } - public getContextTypes( contextRootType: T & { __context?: string[]; @@ -197,27 +199,28 @@ export class TsCodegenContext { } public getContextTemplate() { - return this.contextTemplate || null; + return this.contextDefaultSubTypeTemplate || null; } public getContextTypeNode(typeNames?: string[] | null) { - const contextTemplate = this.contextTemplate; - if (!typeNames || !typeNames.length || !contextTemplate) { + const contextDefaultSubTypeTemplate = this.contextDefaultSubTypeTemplate; + + if (!typeNames || !typeNames.length || !contextDefaultSubTypeTemplate) { return this.getContextType().toTypeReference(); } else if ( - (typeNames.length === 1 && this.context) || + (typeNames.length === 1 && this.contextDefaultSubTypeContext) || typeNames.length > 1 ) { const typeNameWithNamespace = typeNames.map((typeName) => { return this.replaceTemplateWithContextName( - contextTemplate.nameTemplate, + contextDefaultSubTypeTemplate.nameTemplate, typeName, ); }); return factory.createIntersectionTypeNode( - (this.context - ? [this.context.name, ...typeNameWithNamespace] + (this.contextDefaultSubTypeContext + ? [this.contextDefaultSubTypeContext.name, ...typeNameWithNamespace] : typeNameWithNamespace ).map((type: string) => { return factory.createTypeReferenceNode( @@ -230,59 +233,111 @@ export class TsCodegenContext { return new TypeLocation( null, this.replaceTemplateWithContextName( - contextTemplate.nameTemplate, + contextDefaultSubTypeTemplate.nameTemplate, typeNames[0], ), ).toTypeReference(); } } + private isNonArrayNode( + node: ASTNode | ReadonlyArray, + ): node is ASTNode { + return !Array.isArray(node); + } + // FIX any - public initContextMap(ancestors: any, values: string[]) { + public initContextMap( + ancestors: ReadonlyArray>, + values: string[], + ) { if (ancestors.length < 2) { - throw new Error("wtf is happening"); + throw new Error("Invalid document provided"); } const node = ancestors[ancestors.length - 1]; + const nonArrayNode = this.isNonArrayNode(node) ? node : null; + + if (nonArrayNode) { + if ( + nonArrayNode?.kind === "ObjectTypeDefinition" || + nonArrayNode?.kind === "InterfaceTypeDefinition" + ) { + if (this.typeContextMap[nonArrayNode.name.value]?.__context) { + throw new Error("Type already visited"); + } - if ( - node?.kind === "ObjectTypeDefinition" || - node?.kind === "InterfaceTypeDefinition" - ) { - if (this.typeContextMap[node.name.value]?.__context) { - throw new Error("Type already visited"); - } + const typeName = nonArrayNode.name.value; + if (!this.typeContextMap[typeName]) { + this.typeContextMap[typeName] = {}; + } - const typeName = ancestors[ancestors.length - 1].name.value; - if (!this.typeContextMap[typeName]) { - this.typeContextMap[typeName] = {}; - } + this.typeContextMap[typeName].__context = values; - this.typeContextMap[typeName].__context = values; + if (nonArrayNode?.interfaces?.length) { + this.typeContextMap[typeName].__interfaces = + nonArrayNode.interfaces.map( + (interfaceDefinitionNode: any) => + interfaceDefinitionNode.name.value, + ); + } - if (node.interfaces.length) { - this.typeContextMap[typeName].__interfaces = node.interfaces.map( - (interfaceDefinitionNode: any) => interfaceDefinitionNode.name.value, - ); - } + if ( + nonArrayNode?.interfaces?.length && + nonArrayNode?.kind === "InterfaceTypeDefinition" + ) { + this.typeContextMap[typeName].__interfaces = + nonArrayNode.interfaces.map( + (interfaceDefinitionNode: any) => + interfaceDefinitionNode.name.value, + ); + } + } else if (nonArrayNode?.kind === "FieldDefinition") { + const node = ancestors[ancestors.length - 3]; + const typeName = + this.isNonArrayNode(node) && + (node.kind === "ObjectTypeDefinition" || + node.kind === "ObjectTypeExtension") + ? node.name.value + : null; + + if (typeName) { + if (!this.typeContextMap[typeName]) { + this.typeContextMap[typeName] = {}; + } - if (node.interfaces.length && node?.kind === "InterfaceTypeDefinition") { - this.typeContextMap[typeName].__interfaces = node.interfaces.map( - (interfaceDefinitionNode: any) => interfaceDefinitionNode.name.value, - ); - } - } else if (node?.kind === "FieldDefinition") { - const typeName = ancestors[ancestors.length - 3].name.value; - if (!this.typeContextMap[typeName]) { - this.typeContextMap[typeName] = {}; + this.typeContextMap[typeName][nonArrayNode.name.value] = values; + } } - - this.typeContextMap[typeName][ - ancestors[ancestors.length - 1].name.value - ] = values; } } + getSubTypeNamesFromTemplate( + subTypes: string[], + nameTemplate: string, + pathTemplate: string, + ) { + return subTypes.reduce>( + (acc: Record, importName: string) => { + const importPath = this.replaceTemplateWithContextName( + pathTemplate, + importName, + false, + ); + if (importPath) { + if (!acc[importPath]) { + acc[importPath] = []; + } + acc[importPath].push( + this.replaceTemplateWithContextName(nameTemplate, importName), + ); + } + return acc; + }, + {}, + ); + } + isLegacyCompatMode(): boolean { return this.options.legacyCompat; } @@ -353,15 +408,15 @@ export class TsCodegenContext { } } - getTypeFromTypeNode(node: TypeNode | string): ts.TypeNode | string { + getTypeFromTypeNode(node: TypeNode): string { if (typeof node === "string") { return node; } if (node.kind === Kind.NON_NULL_TYPE) { - return this.getTypeFromTypeNode(node.type) as ts.TypeNode; + return this.getTypeFromTypeNode(node.type); } else if (node.kind === Kind.LIST_TYPE) { - return this.getTypeFromTypeNode(node.type) as ts.TypeNode; + return this.getTypeFromTypeNode(node.type); } else { return node.name.value; } @@ -555,7 +610,11 @@ export class TsCodegenContext { } getContextType(): TypeLocation { - return new TypeLocation(null, "unknown"); + return new TypeLocation( + null, + this.options.context.name || + (TsCodegenContextDefault.context.name as string), + ); } getResolveInfoType(): TypeLocation { @@ -700,8 +759,10 @@ export function extractContext( ...TsCodegenContextDefault, ...options, }; + const context = new TsCodegenContext(fullOptions); - const { contextImportNameTemplate, contextImportPathTemplate } = options; + const { contextSubTypeNameTemplate, contextSubTypePathTemplate } = options; + visit(document, { Directive: { enter(node, _key, _parent, _path: any, ancestors) { @@ -733,8 +794,8 @@ export function extractContext( context.addLegacyInterface(typeName); } else if ( node.name.value === "context" && - contextImportNameTemplate && - contextImportPathTemplate + contextSubTypeNameTemplate && + contextSubTypePathTemplate ) { if ( node.arguments?.length !== 1 || diff --git a/packages/ts-codegen/src/context/utilities.ts b/packages/ts-codegen/src/context/utilities.ts index a2f9d67ca..4a9805bb9 100644 --- a/packages/ts-codegen/src/context/utilities.ts +++ b/packages/ts-codegen/src/context/utilities.ts @@ -1,6 +1,36 @@ -import { factory } from "typescript"; +import ts, { factory } from "typescript"; import path from "path"; +export function getImportIdentifierForTypenames( + importNames: string[], + importPath: string, + contextImportNames: Set, +) { + return factory.createImportDeclaration( + undefined, + factory.createImportClause( + true, + undefined, + factory.createNamedImports( + importNames + .map((importName: string) => { + if (contextImportNames.has(importName)) { + return; + } + contextImportNames.add(importName); + return factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier(importName), + ); + }) + .filter(Boolean) as ts.ImportSpecifier[], + ), + ), + factory.createStringLiteral(importPath), + ); +} + export function createImportDeclaration( importNames: string[], from: string, diff --git a/packages/ts-codegen/src/resolvers.ts b/packages/ts-codegen/src/resolvers.ts index 9dba5aa35..c7e9f4248 100644 --- a/packages/ts-codegen/src/resolvers.ts +++ b/packages/ts-codegen/src/resolvers.ts @@ -13,6 +13,10 @@ import { createUnionResolveType, createInterfaceResolveType, } from "./utilities"; +import { + createImportDeclaration, + getImportIdentifierForTypenames, +} from "./context/utilities"; export function generateResolvers(context: TsCodegenContext): ts.SourceFile { const statements: ts.Statement[] = []; @@ -32,9 +36,21 @@ export function generateResolvers(context: TsCodegenContext): ts.SourceFile { const contextTemplate = context.getContextTemplate(); if (Object.keys(context.getContextMap()).length && contextTemplate) { + if ( + context.contextDefaultSubTypeContext?.from && + context.contextDefaultSubTypeContext?.name + ) { + statements.push( + createImportDeclaration( + [context.contextDefaultSubTypeContext.name], + context.contextDefaultSubTypeContext.from, + ), + ); + } + const contextImportNames: Set = new Set(); for (const [, root] of Object.entries(context.getContextMap())) { - const rootValue: string[] = (root as any).__context; + const rootValue: string[] | undefined = root.__context; if (rootValue) { if ( rootValue.every((importName: string) => @@ -43,113 +59,46 @@ export function generateResolvers(context: TsCodegenContext): ts.SourceFile { ) { continue; } - const imports = (rootValue as string[]).reduce< - Record - >((acc: Record, importName: string) => { - const importPath = context.replaceTemplateWithContextName( - contextTemplate.pathTemplate, - importName, - false, - ); - if (importPath) { - if (!acc[importPath]) { - acc[importPath] = []; - } - acc[importPath].push( - context.replaceTemplateWithContextName( - contextTemplate.nameTemplate, - importName, - ), - ); - } - return acc; - }, {}); + const imports = context.getSubTypeNamesFromTemplate( + rootValue, + contextTemplate.nameTemplate, + contextTemplate.pathTemplate, + ); + for (const [importPath, importNames] of Object.entries(imports)) { statements.push( - factory.createImportDeclaration( - undefined, - factory.createImportClause( - true, - undefined, - factory.createNamedImports( - importNames - .map((importName: string) => { - if (contextImportNames.has(importName)) { - return; - } - contextImportNames.add(importName); - return factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier(importName), - ); - }) - .filter(Boolean) as ts.ImportSpecifier[], - ), - ), - factory.createStringLiteral(importPath), + getImportIdentifierForTypenames( + importNames, + importPath, + contextImportNames, ), ); } } - for (const [key, value] of Object.entries(root as any)) { + for (const [key, value] of Object.entries(root)) { if (key.startsWith("__")) { continue; } if ( - (value as string[]).every((importName: string) => + value.every((importName: string) => contextImportNames.has(importName), ) ) { continue; } - const imports = (value as string[]).reduce>( - (acc: Record, importName: string) => { - const importPath = context.replaceTemplateWithContextName( - contextTemplate.pathTemplate, - importName, - false, - ); - if (importPath) { - if (!acc[importPath]) { - acc[importPath] = []; - } - acc[importPath].push( - context.replaceTemplateWithContextName( - contextTemplate.nameTemplate, - importName, - ), - ); - } - return acc; - }, - {}, + + const imports = context.getSubTypeNamesFromTemplate( + value, + contextTemplate.nameTemplate, + contextTemplate.pathTemplate, ); for (const [importPath, importNames] of Object.entries(imports)) { statements.push( - factory.createImportDeclaration( - undefined, - factory.createImportClause( - true, - undefined, - factory.createNamedImports( - importNames - .map((importName: string) => { - if (contextImportNames.has(importName)) { - return; - } - contextImportNames.add(importName); - return factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier(importName), - ); - }) - .filter(Boolean) as ts.ImportSpecifier[], - ), - ), - factory.createStringLiteral(importPath), + getImportIdentifierForTypenames( + importNames, + importPath, + contextImportNames, ), ); } @@ -256,13 +205,16 @@ function createObjectTypeResolvers( ); } +function isRootOperationType(type: string): boolean { + return ["Query", "Mutation", "Subscription"].includes(type); +} function createResolverField( context: TsCodegenContext, type: Type, field: Field, ): ts.TypeAliasDeclaration { let modelIdentifier; - if (["Query", "Mutation", "Subscription"].includes(type.name)) { + if (isRootOperationType(type.name)) { modelIdentifier = factory.createKeywordTypeNode( ts.SyntaxKind.UnknownKeyword, ); @@ -272,17 +224,10 @@ function createResolverField( .toTypeReference(); } - let contextRootType = + const contextRootType = context.getContextMap()[type.name] || - context.getContextMap()[context.getTypeFromTypeNode(field.type) as string]; - - if ( - (type.kind === "OBJECT" || type.kind === "INTERFACE") && - !contextRootType && - type.interfaces.length - ) { - contextRootType = context.mergeContexts(type.interfaces); - } + (!isRootOperationType(type.name) && + context.getContextMap()[context.getTypeFromTypeNode(field.type)]); let contextTypes; if (contextRootType) { @@ -369,11 +314,7 @@ function createUnionTypeResolvers( ], ); - let contextRootType = context.getContextMap()[type.name]; - - if (!contextRootType && type.types.length) { - contextRootType = context.mergeContexts(type.types); - } + const contextRootType = context.getContextMap()[type.name]; const contextTypes = context.getContextTypes(contextRootType); @@ -404,12 +345,7 @@ function createInterfaceTypeResolvers( context: TsCodegenContext, type: InterfaceType, ): ts.ModuleDeclaration { - let contextRootType = context.getContextMap()[type.name]; - - if (!contextRootType && type.interfaces.length) { - contextRootType = context.mergeContexts(type.interfaces); - } - + const contextRootType = context.getContextMap()[type.name]; const contextTypes = context.getContextTypes(contextRootType); const resolversObject = factory.createInterfaceDeclaration( From 78b20d508756725d01ae70899be4125832aec6e8 Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Wed, 13 Nov 2024 20:52:03 +0000 Subject: [PATCH 17/19] applying package updates --- ...-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json | 7 ------- ...-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json | 7 ------- packages/cli/CHANGELOG.json | 21 +++++++++++++++++++ packages/cli/CHANGELOG.md | 11 +++++++++- packages/cli/package.json | 4 ++-- packages/ts-codegen/CHANGELOG.json | 15 +++++++++++++ packages/ts-codegen/CHANGELOG.md | 10 ++++++++- packages/ts-codegen/package.json | 2 +- 8 files changed, 58 insertions(+), 19 deletions(-) delete mode 100644 change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json delete mode 100644 change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json diff --git a/change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json b/change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json deleted file mode 100644 index 62dbbf751..000000000 --- a/change/@graphitation-cli-5ecf7d81-bc21-406f-b23b-d0fe52baf580.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Solution cleanup - new cli parameters and inheritance fixes", - "packageName": "@graphitation/cli", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json b/change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json deleted file mode 100644 index 905827b65..000000000 --- a/change/@graphitation-ts-codegen-fc3a17e4-3d18-48d0-9b00-e5e2f03132c0.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Solution cleanup - new cli parameters and inheritance fixes", - "packageName": "@graphitation/ts-codegen", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/packages/cli/CHANGELOG.json b/packages/cli/CHANGELOG.json index e173427c4..29b9436b7 100644 --- a/packages/cli/CHANGELOG.json +++ b/packages/cli/CHANGELOG.json @@ -1,6 +1,27 @@ { "name": "@graphitation/cli", "entries": [ + { + "date": "Wed, 13 Nov 2024 20:52:03 GMT", + "version": "1.13.0-alpha.4", + "tag": "@graphitation/cli_v1.13.0-alpha.4", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/cli", + "commit": "e9a34254f389d06042d7deb11bca819dd2d642ed", + "comment": "Solution cleanup - new cli parameters and inheritance fixes" + }, + { + "author": "beachball", + "package": "@graphitation/cli", + "comment": "Bump @graphitation/ts-codegen to v2.13.0-alpha.5", + "commit": "not available" + } + ] + } + }, { "date": "Mon, 11 Nov 2024 14:13:12 GMT", "version": "1.13.0-alpha.3", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index aaf42998e..ab5afa0ef 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @graphitation/cli - + +## 1.13.0-alpha.4 + +Wed, 13 Nov 2024 20:52:03 GMT + +### Changes + +- Solution cleanup - new cli parameters and inheritance fixes (77059398+vejrj@users.noreply.github.com) +- Bump @graphitation/ts-codegen to v2.13.0-alpha.5 + ## 1.13.0-alpha.3 Mon, 11 Nov 2024 14:13:12 GMT diff --git a/packages/cli/package.json b/packages/cli/package.json index d9926d2ac..aeca8a3bd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/cli", "license": "MIT", - "version": "1.13.0-alpha.3", + "version": "1.13.0-alpha.4", "bin": { "supermassive": "./bin/supermassive.js" }, @@ -24,7 +24,7 @@ }, "dependencies": { "@graphitation/supermassive-extractors": "^2.2.5", - "@graphitation/ts-codegen": "^2.13.0-alpha.4", + "@graphitation/ts-codegen": "^2.13.0-alpha.5", "commander": "^8.3.0", "fast-glob": "^3.2.12", "graphql": "^15.6.1" diff --git a/packages/ts-codegen/CHANGELOG.json b/packages/ts-codegen/CHANGELOG.json index bb818c9ec..694aaa8c2 100644 --- a/packages/ts-codegen/CHANGELOG.json +++ b/packages/ts-codegen/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/ts-codegen", "entries": [ + { + "date": "Wed, 13 Nov 2024 20:52:03 GMT", + "version": "2.13.0-alpha.5", + "tag": "@graphitation/ts-codegen_v2.13.0-alpha.5", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/ts-codegen", + "commit": "e9a34254f389d06042d7deb11bca819dd2d642ed", + "comment": "Solution cleanup - new cli parameters and inheritance fixes" + } + ] + } + }, { "date": "Mon, 11 Nov 2024 14:13:12 GMT", "version": "2.13.0-alpha.4", diff --git a/packages/ts-codegen/CHANGELOG.md b/packages/ts-codegen/CHANGELOG.md index e7559d69a..0f1957950 100644 --- a/packages/ts-codegen/CHANGELOG.md +++ b/packages/ts-codegen/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/ts-codegen - + +## 2.13.0-alpha.5 + +Wed, 13 Nov 2024 20:52:03 GMT + +### Changes + +- Solution cleanup - new cli parameters and inheritance fixes (77059398+vejrj@users.noreply.github.com) + ## 2.13.0-alpha.4 Mon, 11 Nov 2024 14:13:12 GMT diff --git a/packages/ts-codegen/package.json b/packages/ts-codegen/package.json index ff55ddc60..af5a5661f 100644 --- a/packages/ts-codegen/package.json +++ b/packages/ts-codegen/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/ts-codegen", "license": "MIT", - "version": "2.13.0-alpha.4", + "version": "2.13.0-alpha.5", "main": "./src/index.ts", "repository": { "type": "git", From 2c90bfecd577995d71baf009e376376d576a79b8 Mon Sep 17 00:00:00 2001 From: vejrj <77059398+vejrj@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:05:48 +0100 Subject: [PATCH 18/19] Interface inheritance logic cleanup, tests added and union fixed (#478) * Interface inheritance logic cleanup, tests added and union fixed --- ...-c487d786-6fa4-4909-9501-05db96c929a6.json | 7 + .../ts-codegen/src/__tests__/context.test.ts | 443 ++++++++++++------ packages/ts-codegen/src/codegen.ts | 4 +- packages/ts-codegen/src/context/index.ts | 54 +-- 4 files changed, 308 insertions(+), 200 deletions(-) create mode 100644 change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json diff --git a/change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json b/change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json new file mode 100644 index 000000000..2c72989b9 --- /dev/null +++ b/change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Interface inheritence logic cleanup, tests added and union fixed", + "packageName": "@graphitation/ts-codegen", + "email": "77059398+vejrj@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/ts-codegen/src/__tests__/context.test.ts b/packages/ts-codegen/src/__tests__/context.test.ts index 7bb7c4a04..cfad75e9c 100644 --- a/packages/ts-codegen/src/__tests__/context.test.ts +++ b/packages/ts-codegen/src/__tests__/context.test.ts @@ -2,54 +2,76 @@ import ts from "typescript"; import { parse } from "graphql"; import { blankGraphQLTag as graphql } from "../utilities"; import { generateTS } from ".."; +import { ContextMap } from "../context"; describe(generateTS, () => { describe("Tests basic syntax GraphQL syntax", () => { test("all possible nullable and non-nullable combinations", () => { - const { resolvers, models, enums, inputs } = runGenerateTest( - graphql` - extend schema - @import(from: "@msteams/packages-test", defs: ["Avatar"]) - type Post - @model(from: "./post-model.interface", tsType: "PostModel") { - id: ID! - } + const { resolvers, models, enums, inputs, contextMappingOutput } = + runGenerateTest( + graphql` + extend schema + @import(from: "@msteams/packages-test", defs: ["Avatar"]) + type Post + @model(from: "./post-model.interface", tsType: "PostModel") { + id: ID! + } - type Message { - id: ID! @context(stateMachines: ["message"]) - } + type Message { + id: ID! @context(stateMachines: ["message"]) + } - type User @context(stateMachines: ["user"]) { - id: ID! @context(stateMachines: ["id-user"]) - name: String - messagesWithAnswersNonRequired: [[Message]] - messagesWithAnswersRequired: [[Message]]! - messagesWithAnswersAllRequired: [[Message!]!]! - messagesNonRequired: [Message] - messagesWithArrayRequired: [Message]! - messagesRequired: [Message!]! - messagesOnlyMessageRequired: [Message!] - post: Post @context(stateMachines: ["post"]) - postRequired: Post! - avatar: Avatar - avatarRequired: Avatar! - } + type User @context(stateMachines: ["user"]) { + id: ID! @context(stateMachines: ["id-user"]) + name: String + messagesWithAnswersNonRequired: [[Message]] + messagesWithAnswersRequired: [[Message]]! + messagesWithAnswersAllRequired: [[Message!]!]! + messagesNonRequired: [Message] + messagesWithArrayRequired: [Message]! + messagesRequired: [Message!]! + messagesOnlyMessageRequired: [Message!] + post: Post @context(stateMachines: ["post"]) + postRequired: Post! + avatar: Avatar + avatarRequired: Avatar! + } - extend type Query { - requiredUsers: [User!]! - optionalUsers: [User] - optionalUser: User - requiredUser: User! - requiredPost: Post! - optionalPost: Post - } - `, - { - contextSubTypeNameTemplate: "I${contextName}StateMachineContext", - contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", - }, - ); + extend type Query { + requiredUsers: [User!]! + optionalUsers: [User] + optionalUser: User + requiredUser: User! + requiredPost: Post! + optionalPost: Post + } + `, + { + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); + expect(contextMappingOutput).toMatchInlineSnapshot(` + { + "Message": { + "id": [ + "message", + ], + }, + "User": { + "__context": [ + "user", + ], + "id": [ + "id-user", + ], + "post": [ + "post", + ], + }, + } + `); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` "import type { Avatar } from "@msteams/packages-test"; @@ -154,16 +176,18 @@ describe(generateTS, () => { `); }); test("Subscription", () => { - const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - type User { - id: ID! - } + const { resolvers, models, enums, inputs, contextMappingOutput } = + runGenerateTest(graphql` + type User { + id: ID! + } - extend type Subscription { - userUpdated: User! - } - `); + extend type Subscription { + userUpdated: User! + } + `); expect(enums).toMatchInlineSnapshot(`undefined`); + expect(contextMappingOutput).toMatchInlineSnapshot(`{}`); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) @@ -239,16 +263,18 @@ describe(generateTS, () => { `); }); test("extends by exteding a type with pre-generated BaseModel type", () => { - const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - type User { - id: ID! - } + const { resolvers, models, enums, inputs, contextMappingOutput } = + runGenerateTest(graphql` + type User { + id: ID! + } - extend type Query { - users: [User!]! - } - `); + extend type Query { + users: [User!]! + } + `); expect(enums).toMatchInlineSnapshot(`undefined`); + expect(contextMappingOutput).toMatchInlineSnapshot(`{}`); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) @@ -281,38 +307,58 @@ describe(generateTS, () => { `); }); test("case when interface implements multiple interfaces", () => { - const { resolvers, models, enums, inputs } = runGenerateTest( - graphql` - interface Node @context(stateMachines: ["node"]) { - id: ID! - } + const { resolvers, models, enums, inputs, contextMappingOutput } = + runGenerateTest( + graphql` + interface Node @context(stateMachines: ["node"]) { + id: ID! + } - interface Persona @context(stateMachines: ["persona"]) { - phone: String! - } + interface Persona @context(stateMachines: ["persona"]) { + phone: String! + } - interface User implements Node & Persona { - id: ID! - name: String! - } + interface User implements Node & Persona { + id: ID! + name: String! + } - type Admin implements Node & Persona - @context(stateMachines: ["admin"]) { - id: ID! - rank: Int! - } + type Admin implements Node & Persona + @context(stateMachines: ["admin"]) { + id: ID! + rank: Int! + } - extend type Query { - users: [User] - admins: [Admin] - } - `, - { - contextSubTypeNameTemplate: "I${contextName}StateMachineContext", - contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", - }, - ); + extend type Query { + users: [User] + admins: [Admin] + } + `, + { + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); + expect(contextMappingOutput).toMatchInlineSnapshot(` + { + "Admin": { + "__context": [ + "admin", + ], + }, + "Node": { + "__context": [ + "node", + ], + }, + "Persona": { + "__context": [ + "persona", + ], + }, + } + `); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) @@ -409,33 +455,48 @@ describe(generateTS, () => { }); test("implements -> @context in interfaces should be used only in resolveType", () => { - const { resolvers, models, enums, inputs } = runGenerateTest( - graphql` - interface Node @context(stateMachines: ["node"]) { - id: ID! - } + const { resolvers, models, enums, inputs, contextMappingOutput } = + runGenerateTest( + graphql` + interface Node @context(stateMachines: ["node"]) { + id: ID! + } - interface Customer implements Node - @context(stateMachines: ["customer"]) { - id: ID! - name: String! - } + interface Customer implements Node + @context(stateMachines: ["customer"]) { + id: ID! + name: String! + } - type User implements Node & Customer { - id: ID! - name: String! - } + type User implements Node & Customer { + id: ID! + name: String! + } - extend type Query { - users: [User] - } - `, - { - contextSubTypeNameTemplate: "I${contextName}StateMachineContext", - contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", - }, - ); + extend type Query { + users: [User] + } + `, + { + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); + expect(contextMappingOutput).toMatchInlineSnapshot(` + { + "Customer": { + "__context": [ + "customer", + ], + }, + "Node": { + "__context": [ + "node", + ], + }, + } + `); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) @@ -492,22 +553,24 @@ describe(generateTS, () => { }); test("applying @context to enum shouldn't affect anything", () => { - const { resolvers, models, enums, inputs } = runGenerateTest(graphql` - enum PresenceAvailability @context(stateMachines: ["shouldnt-apply"]) { - Available - Away - Offline - } + const { resolvers, models, enums, inputs, contextMappingOutput } = + runGenerateTest(graphql` + enum PresenceAvailability + @context(stateMachines: ["shouldnt-apply"]) { + Available + Away + Offline + } - type User { - id: ID! - availability: PresenceAvailability! - } + type User { + id: ID! + availability: PresenceAvailability! + } - extend type Query { - userById(id: ID!): User - } - `); + extend type Query { + userById(id: ID!): User + } + `); expect(enums).toMatchInlineSnapshot(` "export enum PresenceAvailability { Available = "Available", @@ -517,6 +580,7 @@ describe(generateTS, () => { " `); expect(inputs).toMatchInlineSnapshot(`undefined`); + expect(contextMappingOutput).toMatchInlineSnapshot(`{}`); expect(models).toMatchInlineSnapshot(` "import * as Enums from "./enums.interface"; export * from "./enums.interface"; @@ -556,39 +620,83 @@ describe(generateTS, () => { }); test("Union and interface types", () => { - const { resolvers, models, enums, inputs } = runGenerateTest( - graphql` - type Customer @context(stateMachines: ["custom"]) { - id: ID! - } + const { resolvers, models, enums, inputs, contextMappingOutput } = + runGenerateTest( + graphql` + type Customer { + id: ID! + } - type Admin @context(stateMachines: ["admin"]) { - id: ID! - } + type Company { + id: ID! + } - type User @context(stateMachines: ["user"]) { - id: ID! - } + type Admin @context(stateMachines: ["admin"]) { + id: ID! + } - interface Node { - id: ID! - } + type User @context(stateMachines: ["user"]) { + id: ID! + } - union whatever = User | Admin + interface Node { + id: ID! + } - extend type Query { - userById(id: ID!): whatever @context(stateMachines: ["whatever"]) - userByMail(mail: String): whatever - @context(stateMachines: ["different-whatever"]) - node(id: ID!): Node - } - `, - { - contextSubTypeNameTemplate: "I${contextName}StateMachineContext", - contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", - }, - ); + union UserOrAdmin = User | Admin + union UserOrCustomer @context(stateMachines: ["user-or-customer"]) = + User + | Customer + union CompanyOrCustomer + @context(stateMachines: ["company-or-customer"]) = + Company + | Customer + + extend type Query { + userById(id: ID!): whatever @context(stateMachines: ["whatever"]) + userByMail(mail: String): whatever + @context(stateMachines: ["different-whatever"]) + node(id: ID!): Node + } + `, + { + contextSubTypeNameTemplate: "I${contextName}StateMachineContext", + contextSubTypePathTemplate: "@msteams/core-cdl-sync-${contextName}", + }, + ); expect(enums).toMatchInlineSnapshot(`undefined`); + expect(contextMappingOutput).toMatchInlineSnapshot(` + { + "Admin": { + "__context": [ + "admin", + ], + }, + "CompanyOrCustomer": { + "__context": [ + "company-or-customer", + ], + }, + "Query": { + "userById": [ + "whatever", + ], + "userByMail": [ + "different-whatever", + ], + }, + "User": { + "__context": [ + "user", + ], + }, + "UserOrCustomer": { + "__context": [ + "user-or-customer", + ], + }, + } + `); expect(inputs).toMatchInlineSnapshot(`undefined`); expect(models).toMatchInlineSnapshot(` "// Base type for all models. Enables automatic resolution of abstract GraphQL types (interfaces, unions) @@ -599,6 +707,10 @@ describe(generateTS, () => { readonly __typename?: "Customer"; readonly id: string; } + export interface Company extends BaseModel { + readonly __typename?: "Company"; + readonly id: string; + } export interface Admin extends BaseModel { readonly __typename?: "Admin"; readonly id: string; @@ -610,23 +722,32 @@ describe(generateTS, () => { export interface Node extends BaseModel { readonly __typename?: string; } - export type whatever = User | Admin; + export type UserOrAdmin = User | Admin; + export type UserOrCustomer = User | Customer; + export type CompanyOrCustomer = Company | Customer; " `); expect(resolvers).toMatchInlineSnapshot(` "import type { PromiseOrValue } from "@graphitation/supermassive"; import type { ResolveInfo } from "@graphitation/supermassive"; import * as Models from "./models.interface"; - import type { ICustomStateMachineContext } from "@msteams/core-cdl-sync-custom"; import type { IAdminStateMachineContext } from "@msteams/core-cdl-sync-admin"; import type { IUserStateMachineContext } from "@msteams/core-cdl-sync-user"; + import type { IUserOrCustomerStateMachineContext } from "@msteams/core-cdl-sync-user-or-customer"; + import type { ICompanyOrCustomerStateMachineContext } from "@msteams/core-cdl-sync-company-or-customer"; import type { IWhateverStateMachineContext } from "@msteams/core-cdl-sync-whatever"; import type { IDifferentWhateverStateMachineContext } from "@msteams/core-cdl-sync-different-whatever"; export declare namespace Customer { export interface Resolvers { readonly id?: id; } - export type id = (model: Models.Customer, args: {}, context: ICustomStateMachineContext, info: ResolveInfo) => PromiseOrValue; + export type id = (model: Models.Customer, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; + } + export declare namespace Company { + export interface Resolvers { + readonly id?: id; + } + export type id = (model: Models.Company, args: {}, context: unknown, info: ResolveInfo) => PromiseOrValue; } export declare namespace Admin { export interface Resolvers { @@ -646,12 +767,24 @@ describe(generateTS, () => { } export type __resolveType = (parent: unknown, context: unknown, info: ResolveInfo) => PromiseOrValue; } - export declare namespace whatever { + export declare namespace UserOrAdmin { export interface Resolvers { readonly __resolveType?: __resolveType; } export type __resolveType = (parent: Models.User | Models.Admin, context: unknown, info: ResolveInfo) => PromiseOrValue<"User" | "Admin" | null>; } + export declare namespace UserOrCustomer { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: Models.User | Models.Customer, context: IUserOrCustomerStateMachineContext, info: ResolveInfo) => PromiseOrValue<"User" | "Customer" | null>; + } + export declare namespace CompanyOrCustomer { + export interface Resolvers { + readonly __resolveType?: __resolveType; + } + export type __resolveType = (parent: Models.Company | Models.Customer, context: ICompanyOrCustomerStateMachineContext, info: ResolveInfo) => PromiseOrValue<"Company" | "Customer" | null>; + } export declare namespace Query { export interface Resolvers { readonly userById?: userById; @@ -705,8 +838,7 @@ function runGenerateTest( enumNamesToMigrate?: string[]; enumNamesToKeep?: string[]; modelScope?: string; - contextSubTypeNameTemplate?: string; - contextSubTypePathTemplate?: string; + contextMappingOutput: ContextMap | null; } { const fullOptions: { outputPath: string; @@ -727,7 +859,7 @@ function runGenerateTest( ...options, }; const document = parse(doc); - const { files } = generateTS(document, fullOptions); + const { files, contextMappingOutput } = generateTS(document, fullOptions); function getFileByFileName(fileName: string) { return files.find((file) => file.fileName === fileName); @@ -748,10 +880,9 @@ function runGenerateTest( enums: enums && printer.printFile(enums), inputs: inputs && printer.printFile(inputs), models: printer.printFile(models), - contextSubTypeNameTemplate: options.contextSubTypeNameTemplate, - contextSubTypePathTemplate: options.contextSubTypePathTemplate, resolvers: printer.printFile(resolvers), legacyTypes: legacyTypes && printer.printFile(legacyTypes), legacyResolvers: legacyResolvers && printer.printFile(legacyResolvers), + contextMappingOutput, }; } diff --git a/packages/ts-codegen/src/codegen.ts b/packages/ts-codegen/src/codegen.ts index 0f93b093b..43c061fe7 100644 --- a/packages/ts-codegen/src/codegen.ts +++ b/packages/ts-codegen/src/codegen.ts @@ -1,6 +1,6 @@ import ts from "typescript"; import { DocumentNode } from "graphql"; -import { extractContext } from "./context/index"; +import { ContextMap, extractContext } from "./context/index"; import { generateResolvers } from "./resolvers"; import { generateModels } from "./models"; import { generateLegacyTypes } from "./legacyTypes"; @@ -47,7 +47,7 @@ export function generateTS( }, ): { files: ts.SourceFile[]; - contextMappingOutput: any | null; + contextMappingOutput: ContextMap | null; } { try { const context = extractContext( diff --git a/packages/ts-codegen/src/context/index.ts b/packages/ts-codegen/src/context/index.ts index 894f70d68..e58e82f1b 100644 --- a/packages/ts-codegen/src/context/index.ts +++ b/packages/ts-codegen/src/context/index.ts @@ -99,13 +99,17 @@ const TsCodegenContextDefault: TsCodegenContextOptions = { }; type ModelNameAndImport = { modelName: string; imp: DefinitionImport }; -type ContextTypeItem = { __context?: string[] } & { [key: string]: string[] }; + +export type ContextMap = { + [key: string]: ContextMapTypeItem; +}; + +export type ContextMapTypeItem = { __context?: string[] } & { + [key: string]: string[]; +}; export class TsCodegenContext { private allTypes: Array; - private typeContextMap: { - [key: string]: ContextTypeItem; - }; - private allRootTypeNames: Set; + private typeContextMap: ContextMap; private typeNameToType: Map; private usedEntitiesInModels: Set; private usedEntitiesInResolvers: Set; @@ -131,7 +135,6 @@ export class TsCodegenContext { constructor(private options: TsCodegenContextOptions) { this.allTypes = []; this.typeContextMap = {}; - this.allRootTypeNames = new Set(); this.typeNameToType = new Map(); this.usedEntitiesInModels = new Set(); this.usedEntitiesInResolvers = new Set(); @@ -246,7 +249,6 @@ export class TsCodegenContext { return !Array.isArray(node); } - // FIX any public initContextMap( ancestors: ReadonlyArray>, values: string[], @@ -261,7 +263,8 @@ export class TsCodegenContext { if (nonArrayNode) { if ( nonArrayNode?.kind === "ObjectTypeDefinition" || - nonArrayNode?.kind === "InterfaceTypeDefinition" + nonArrayNode?.kind === "InterfaceTypeDefinition" || + nonArrayNode?.kind === "UnionTypeDefinition" ) { if (this.typeContextMap[nonArrayNode.name.value]?.__context) { throw new Error("Type already visited"); @@ -273,25 +276,6 @@ export class TsCodegenContext { } this.typeContextMap[typeName].__context = values; - - if (nonArrayNode?.interfaces?.length) { - this.typeContextMap[typeName].__interfaces = - nonArrayNode.interfaces.map( - (interfaceDefinitionNode: any) => - interfaceDefinitionNode.name.value, - ); - } - - if ( - nonArrayNode?.interfaces?.length && - nonArrayNode?.kind === "InterfaceTypeDefinition" - ) { - this.typeContextMap[typeName].__interfaces = - nonArrayNode.interfaces.map( - (interfaceDefinitionNode: any) => - interfaceDefinitionNode.name.value, - ); - } } else if (nonArrayNode?.kind === "FieldDefinition") { const node = ancestors[ancestors.length - 3]; const typeName = @@ -354,19 +338,11 @@ export class TsCodegenContext { return this.typeContextMap; } - addRootTypeNames(typename: string): void { - this.allRootTypeNames.add(typename); - } - addType(type: Type): void { this.allTypes.push(type); this.typeNameToType.set(type.name, type); } - getAllRootTypeNames(): Set { - return this.allRootTypeNames; - } - getAllTypes(): Array { return this.allTypes; } @@ -765,7 +741,7 @@ export function extractContext( visit(document, { Directive: { - enter(node, _key, _parent, _path: any, ancestors) { + enter(node, _key, _parent, _path, ancestors) { if (node.name.value === IMPORT_DIRECTIVE_NAME) { context.addImport( processImportDirective(node, outputPath, documentPath), @@ -804,8 +780,6 @@ export function extractContext( ) { throw new Error("Invalid context use"); } - // TODO ADD validation - const directiveValues = node.arguments[0].value.values.map((item) => { if (item.kind !== "StringValue") { throw new Error("Invalid context use"); @@ -833,7 +807,6 @@ export function extractContext( }, ObjectTypeDefinition: { leave(node) { - context.addRootTypeNames(node.name.value); context.addType({ kind: "OBJECT", name: node.name.value, @@ -848,7 +821,6 @@ export function extractContext( }, InterfaceTypeDefinition: { leave(node) { - context.addRootTypeNames(node.name.value); context.addType({ kind: "INTERFACE", name: node.name.value, @@ -873,7 +845,6 @@ export function extractContext( }, ObjectTypeExtension: { leave(node) { - context.addRootTypeNames(node.name.value); context.addType({ kind: "OBJECT", name: node.name.value, @@ -887,7 +858,6 @@ export function extractContext( }, UnionTypeDefinition: { leave(node) { - context.addRootTypeNames(node.name.value); context.addType({ kind: "UNION", name: node.name.value, From 3b985be72b71768dcaf5e988e358cabab33db08a Mon Sep 17 00:00:00 2001 From: Graphitation Service Account Date: Thu, 14 Nov 2024 14:18:34 +0000 Subject: [PATCH 19/19] applying package updates --- ...egen-c487d786-6fa4-4909-9501-05db96c929a6.json | 7 ------- packages/cli/CHANGELOG.json | 15 +++++++++++++++ packages/cli/package.json | 2 +- packages/ts-codegen/CHANGELOG.json | 15 +++++++++++++++ packages/ts-codegen/CHANGELOG.md | 10 +++++++++- packages/ts-codegen/package.json | 2 +- 6 files changed, 41 insertions(+), 10 deletions(-) delete mode 100644 change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json diff --git a/change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json b/change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json deleted file mode 100644 index 2c72989b9..000000000 --- a/change/@graphitation-ts-codegen-c487d786-6fa4-4909-9501-05db96c929a6.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Interface inheritence logic cleanup, tests added and union fixed", - "packageName": "@graphitation/ts-codegen", - "email": "77059398+vejrj@users.noreply.github.com", - "dependentChangeType": "none" -} diff --git a/packages/cli/CHANGELOG.json b/packages/cli/CHANGELOG.json index 29b9436b7..a90fcc794 100644 --- a/packages/cli/CHANGELOG.json +++ b/packages/cli/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/cli", "entries": [ + { + "date": "Thu, 14 Nov 2024 14:18:34 GMT", + "version": "1.13.0-alpha.4", + "tag": "@graphitation/cli_v1.13.0-alpha.4", + "comments": { + "none": [ + { + "author": "beachball", + "package": "@graphitation/cli", + "comment": "Bump @graphitation/ts-codegen to v2.13.0-alpha.6", + "commit": "not available" + } + ] + } + }, { "date": "Wed, 13 Nov 2024 20:52:03 GMT", "version": "1.13.0-alpha.4", diff --git a/packages/cli/package.json b/packages/cli/package.json index aeca8a3bd..1467d2bbd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@graphitation/supermassive-extractors": "^2.2.5", - "@graphitation/ts-codegen": "^2.13.0-alpha.5", + "@graphitation/ts-codegen": "^2.13.0-alpha.6", "commander": "^8.3.0", "fast-glob": "^3.2.12", "graphql": "^15.6.1" diff --git a/packages/ts-codegen/CHANGELOG.json b/packages/ts-codegen/CHANGELOG.json index 694aaa8c2..ff0d98add 100644 --- a/packages/ts-codegen/CHANGELOG.json +++ b/packages/ts-codegen/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@graphitation/ts-codegen", "entries": [ + { + "date": "Thu, 14 Nov 2024 14:18:34 GMT", + "version": "2.13.0-alpha.6", + "tag": "@graphitation/ts-codegen_v2.13.0-alpha.6", + "comments": { + "prerelease": [ + { + "author": "77059398+vejrj@users.noreply.github.com", + "package": "@graphitation/ts-codegen", + "commit": "2c90bfecd577995d71baf009e376376d576a79b8", + "comment": "Interface inheritence logic cleanup, tests added and union fixed" + } + ] + } + }, { "date": "Wed, 13 Nov 2024 20:52:03 GMT", "version": "2.13.0-alpha.5", diff --git a/packages/ts-codegen/CHANGELOG.md b/packages/ts-codegen/CHANGELOG.md index 0f1957950..4f33cb1f9 100644 --- a/packages/ts-codegen/CHANGELOG.md +++ b/packages/ts-codegen/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - @graphitation/ts-codegen - + +## 2.13.0-alpha.6 + +Thu, 14 Nov 2024 14:18:34 GMT + +### Changes + +- Interface inheritence logic cleanup, tests added and union fixed (77059398+vejrj@users.noreply.github.com) + ## 2.13.0-alpha.5 Wed, 13 Nov 2024 20:52:03 GMT diff --git a/packages/ts-codegen/package.json b/packages/ts-codegen/package.json index af5a5661f..8f30003db 100644 --- a/packages/ts-codegen/package.json +++ b/packages/ts-codegen/package.json @@ -1,7 +1,7 @@ { "name": "@graphitation/ts-codegen", "license": "MIT", - "version": "2.13.0-alpha.5", + "version": "2.13.0-alpha.6", "main": "./src/index.ts", "repository": { "type": "git",