From ee6ef2e9afa84f5b60d281d0df529ed8a8b3e86f Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Fri, 25 Nov 2022 15:14:55 +0100 Subject: [PATCH 1/4] simpler hook types --- packages/graphql-codegen-cli/src/hooks.ts | 41 ++++----------------- packages/utils/plugins-helpers/src/types.ts | 20 +++++----- 2 files changed, 18 insertions(+), 43 deletions(-) diff --git a/packages/graphql-codegen-cli/src/hooks.ts b/packages/graphql-codegen-cli/src/hooks.ts index ae303ba945e..8710988a31a 100644 --- a/packages/graphql-codegen-cli/src/hooks.ts +++ b/packages/graphql-codegen-cli/src/hooks.ts @@ -4,7 +4,7 @@ import { exec } from 'child_process'; import { delimiter, sep } from 'path'; import { quote } from 'shell-quote'; -const DEFAULT_HOOKS: Types.LifecycleHooksDefinition = { +const DEFAULT_HOOKS: Types.LifecycleHooksDefinition = { afterStart: [], beforeDone: [], onWatchTriggered: [], @@ -15,37 +15,6 @@ const DEFAULT_HOOKS: Types.LifecycleHooksDefinition = { beforeAllFileWrite: [], }; -function normalizeHooks( - _hooks: Partial -): Types.LifecycleHooksDefinition<(string | Types.HookFunction)[]> { - const keys = Object.keys({ - ...DEFAULT_HOOKS, - ..._hooks, - }); - - return keys.reduce((prev: Types.LifecycleHooksDefinition<(string | Types.HookFunction)[]>, hookName: string) => { - if (typeof _hooks[hookName] === 'string') { - return { - ...prev, - [hookName]: [_hooks[hookName]] as string[], - }; - } - if (typeof _hooks[hookName] === 'function') { - return { - ...prev, - [hookName]: [_hooks[hookName]], - }; - } - if (Array.isArray(_hooks[hookName])) { - return { - ...prev, - [hookName]: _hooks[hookName] as string[], - }; - } - return prev; - }, {} as Types.LifecycleHooksDefinition<(string | Types.HookFunction)[]>); -} - function execShellCommand(cmd: string): Promise { return new Promise((resolve, reject) => { exec( @@ -72,10 +41,11 @@ function execShellCommand(cmd: string): Promise { async function executeHooks( hookName: string, - scripts: (string | Types.HookFunction)[] = [], + _scripts: Types.LifeCycleHookValue = [], args: string[] = [] ): Promise { debugLog(`Running lifecycle hook "${hookName}" scripts...`); + const scripts = Array.isArray(_scripts) ? _scripts : [_scripts]; const quotedArgs = quote(args); for (const script of scripts) { @@ -90,7 +60,10 @@ async function executeHooks( } export const lifecycleHooks = (_hooks: Partial = {}) => { - const hooks = normalizeHooks(_hooks); + const hooks = { + ...DEFAULT_HOOKS, + ..._hooks, + }; return { afterStart: async (): Promise => executeHooks('afterStart', hooks.afterStart), diff --git a/packages/utils/plugins-helpers/src/types.ts b/packages/utils/plugins-helpers/src/types.ts index 8ec12e84a0a..74e90c61707 100644 --- a/packages/utils/plugins-helpers/src/types.ts +++ b/packages/utils/plugins-helpers/src/types.ts @@ -524,50 +524,52 @@ export namespace Types { export type PluginOutput = string | ComplexPluginOutput; export type HookFunction = (...args: any[]) => void | Promise; + export type LifeCycleHookValue = string | HookFunction | (string | HookFunction)[]; + /** * @description All available lifecycle hooks * @additionalProperties false */ - export type LifecycleHooksDefinition = { + export type LifecycleHooksDefinition = { /** * @description Triggered with no arguments when the codegen starts (after the `codegen.yml` has beed parsed). * * Specify a shell command to run. */ - afterStart: T; + afterStart: LifeCycleHookValue; /** * @description Triggered with no arguments, right before the codegen closes, or when watch mode is stopped. * * Specify a shell command to run. */ - beforeDone: T; + beforeDone: LifeCycleHookValue; /** * @description Triggered every time a file changes when using watch mode. * Triggered with two arguments: the type of the event (for example, `changed`) and the path of the file. */ - onWatchTriggered: T; + onWatchTriggered: LifeCycleHookValue; /** * @description Triggered in case of a general error in the codegen. The argument is a string containing the error. */ - onError: T; + onError: LifeCycleHookValue; /** * @description Triggered after a file is written to the file-system. Executed with the path for the file. * If the content of the file hasn't changed since last execution - this hooks won't be triggered. * * > This is a very useful hook, you can use it for integration with Prettier or other linters. */ - afterOneFileWrite: T; + afterOneFileWrite: LifeCycleHookValue; /** * @description Executed after writing all the files to the file-system. * Triggered with multiple arguments - paths for all files. */ - afterAllFileWrite: T; + afterAllFileWrite: LifeCycleHookValue; /** * @description Triggered before a file is written to the file-system. Executed with the path for the file. * * If the content of the file hasn't changed since last execution - this hooks won't be triggered. */ - beforeOneFileWrite: T; + beforeOneFileWrite: LifeCycleHookValue; /** * @description Executed after the codegen has done creating the output and before writing the files to the file-system. * @@ -575,7 +577,7 @@ export namespace Types { * * > Not all the files will be actually written to the file-system, because this is triggered before checking if the file has changed since last execution. */ - beforeAllFileWrite: T; + beforeAllFileWrite: LifeCycleHookValue; }; export type SkipDocumentsValidationOptions = From af1ed813a07c4d195edf4a9499763ece09264d20 Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Fri, 25 Nov 2022 15:20:35 +0100 Subject: [PATCH 2/4] add changeset --- .changeset/plenty-bottles-prove.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/plenty-bottles-prove.md diff --git a/.changeset/plenty-bottles-prove.md b/.changeset/plenty-bottles-prove.md new file mode 100644 index 00000000000..1ac98576582 --- /dev/null +++ b/.changeset/plenty-bottles-prove.md @@ -0,0 +1,6 @@ +--- +'@graphql-codegen/cli': patch +'@graphql-codegen/plugin-helpers': patch +--- + +improve typings for life cycle hooks From 1a5c805a6a6484fca764bf59cf02b2bb9671c771 Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Mon, 28 Nov 2022 19:02:57 +0100 Subject: [PATCH 3/4] Update .changeset/plenty-bottles-prove.md Co-authored-by: Saihajpreet Singh --- .changeset/plenty-bottles-prove.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/plenty-bottles-prove.md b/.changeset/plenty-bottles-prove.md index 1ac98576582..d68850499de 100644 --- a/.changeset/plenty-bottles-prove.md +++ b/.changeset/plenty-bottles-prove.md @@ -1,6 +1,6 @@ --- '@graphql-codegen/cli': patch -'@graphql-codegen/plugin-helpers': patch +'@graphql-codegen/plugin-helpers': major --- improve typings for life cycle hooks From b1cb681008d1a8ba15f61efed0c3fe639b6a1dda Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Mon, 28 Nov 2022 19:18:33 +0100 Subject: [PATCH 4/4] only type changes --- packages/graphql-codegen-cli/src/hooks.ts | 39 ++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/graphql-codegen-cli/src/hooks.ts b/packages/graphql-codegen-cli/src/hooks.ts index 8710988a31a..e31ee965b5f 100644 --- a/packages/graphql-codegen-cli/src/hooks.ts +++ b/packages/graphql-codegen-cli/src/hooks.ts @@ -15,6 +15,40 @@ const DEFAULT_HOOKS: Types.LifecycleHooksDefinition = { beforeAllFileWrite: [], }; +function normalizeHooks(_hooks: Partial): { + [key in keyof Types.LifecycleHooksDefinition]: (string | Types.HookFunction)[]; +} { + const keys = Object.keys({ + ...DEFAULT_HOOKS, + ..._hooks, + }); + + return keys.reduce( + (prev: { [key in keyof Types.LifecycleHooksDefinition]: (string | Types.HookFunction)[] }, hookName: string) => { + if (typeof _hooks[hookName] === 'string') { + return { + ...prev, + [hookName]: [_hooks[hookName]] as string[], + }; + } + if (typeof _hooks[hookName] === 'function') { + return { + ...prev, + [hookName]: [_hooks[hookName]], + }; + } + if (Array.isArray(_hooks[hookName])) { + return { + ...prev, + [hookName]: _hooks[hookName] as string[], + }; + } + return prev; + }, + {} as { [key in keyof Types.LifecycleHooksDefinition]: (string | Types.HookFunction)[] } + ); +} + function execShellCommand(cmd: string): Promise { return new Promise((resolve, reject) => { exec( @@ -60,10 +94,7 @@ async function executeHooks( } export const lifecycleHooks = (_hooks: Partial = {}) => { - const hooks = { - ...DEFAULT_HOOKS, - ..._hooks, - }; + const hooks = normalizeHooks(_hooks); return { afterStart: async (): Promise => executeHooks('afterStart', hooks.afterStart),