From d48525c9f8bda08943c5e07909e9225116d275e7 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Wed, 19 Mar 2025 08:41:50 +0100 Subject: [PATCH] chore: fix type inference with different ways to define functions Signed-off-by: benjdlambert --- plugins/scaffolder-backend/report.api.md | 2 +- .../src/ScaffolderPlugin.ts | 2 +- .../templating/filters/parseRepoUrl/index.ts | 2 +- .../templating/filters/projectSlug/index.ts | 2 +- .../scaffolder-backend/src/service/router.ts | 2 +- .../src/util/templating.test.ts | 4 +- .../scaffolder-backend/src/util/templating.ts | 17 +-- plugins/scaffolder-node/report-alpha.api.md | 108 +++++++----------- .../src/alpha/filters/createTemplateFilter.ts | 23 ++-- .../src/alpha/filters/types.ts | 46 +++----- .../src/alpha/globals/createTemplateGlobal.ts | 20 ++-- .../src/alpha/globals/types.ts | 35 +----- plugins/scaffolder-node/src/alpha/index.ts | 4 +- 13 files changed, 108 insertions(+), 159 deletions(-) diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index 37afe780a9..c3100d8259 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -529,7 +529,7 @@ export interface RouterOptions { // (undocumented) additionalTemplateFilters?: | Record - | CreatedTemplateFilter[]; + | CreatedTemplateFilter[]; // (undocumented) additionalTemplateGlobals?: | Record diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index 4882b3cb2e..9fe78c7119 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -82,7 +82,7 @@ export const scaffolderPlugin = createBackendPlugin({ }, }); - const additionalTemplateFilters: CreatedTemplateFilter[] = []; + const additionalTemplateFilters: CreatedTemplateFilter[] = []; const additionalTemplateGlobals: CreatedTemplateGlobal[] = []; env.registerExtensionPoint(scaffolderTemplatingExtensionPoint, { diff --git a/plugins/scaffolder-backend/src/lib/templating/filters/parseRepoUrl/index.ts b/plugins/scaffolder-backend/src/lib/templating/filters/parseRepoUrl/index.ts index a726c140cf..1c65852bbb 100644 --- a/plugins/scaffolder-backend/src/lib/templating/filters/parseRepoUrl/index.ts +++ b/plugins/scaffolder-backend/src/lib/templating/filters/parseRepoUrl/index.ts @@ -46,5 +46,5 @@ export const createParseRepoUrl = (integrations: ScmIntegrations) => .describe('`RepoSpec`'), ), examples, - filter: (url: string) => parseRepoUrl(url, integrations), + filter: url => parseRepoUrl(url, integrations), }); diff --git a/plugins/scaffolder-backend/src/lib/templating/filters/projectSlug/index.ts b/plugins/scaffolder-backend/src/lib/templating/filters/projectSlug/index.ts index a833121423..05bb6d61cd 100644 --- a/plugins/scaffolder-backend/src/lib/templating/filters/projectSlug/index.ts +++ b/plugins/scaffolder-backend/src/lib/templating/filters/projectSlug/index.ts @@ -30,7 +30,7 @@ export const createProjectSlug = (integrations: ScmIntegrations) => z.string(), ), examples, - filter: (repoUrl: string) => { + filter: repoUrl => { const { owner, repo } = parseRepoUrl(repoUrl, integrations); return `${owner}/${repo}`; }, diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 1186e7d4f6..8258d8c36f 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -179,7 +179,7 @@ export interface RouterOptions { taskBroker?: TaskBroker; additionalTemplateFilters?: | Record - | CreatedTemplateFilter[]; + | CreatedTemplateFilter[]; additionalTemplateGlobals?: | Record | CreatedTemplateGlobal[]; diff --git a/plugins/scaffolder-backend/src/util/templating.test.ts b/plugins/scaffolder-backend/src/util/templating.test.ts index 51493f08a1..f32e94f95c 100644 --- a/plugins/scaffolder-backend/src/util/templating.test.ts +++ b/plugins/scaffolder-backend/src/util/templating.test.ts @@ -64,7 +64,7 @@ describe('templating utilities', () => { z.string().describe('separator').optional(), ) .returns(z.string()), - filter: (input: string, times: number, separator?: string) => + filter: (input, times, separator) => Array(times) .fill(input) .join(separator ?? ''), @@ -149,7 +149,7 @@ describe('templating utilities', () => { id: 'respond', schema: z => z.function().args(z.string().describe('prompt')).returns(z.string()), - fn: (prompt: string) => + fn: prompt => prompt === 'knock knock' ? "who's there?" : "nobody's home", }), ]; diff --git a/plugins/scaffolder-backend/src/util/templating.ts b/plugins/scaffolder-backend/src/util/templating.ts index 1f97e98b83..2607d5bef6 100644 --- a/plugins/scaffolder-backend/src/util/templating.ts +++ b/plugins/scaffolder-backend/src/util/templating.ts @@ -22,7 +22,7 @@ import { CreatedTemplateGlobal, CreatedTemplateGlobalFunction, CreatedTemplateGlobalValue, - TemplateGlobalFunctionSchema, + ZodFunctionSchema, } from '@backstage/plugin-scaffolder-node/alpha'; import { JsonValue } from '@backstage/types'; import { Schema } from 'jsonschema'; @@ -63,8 +63,12 @@ type ExportFilterSchema = { * Converts a Zod function schema to JSON schema */ function convertZodFunctionToJsonSchema( - t: ReturnType>, + t: ReturnType>, ): ExportFunctionSchema { + if (!('parameters' in t) || !('returnType' in t)) { + throw new Error('Invalid Zod function schema'); + } + const args = (t.parameters().items as ZodType[]).map( zt => zodToJsonSchema(zt) as Schema, ); @@ -142,7 +146,9 @@ export function extractFilterMetadata( if (filter.schema) { metadata.schema = convertToFilterSchema( - convertZodFunctionToJsonSchema(filter.schema(z)), + convertZodFunctionToJsonSchema( + filter.schema(z) as z.ZodFunction, + ), ); } @@ -167,10 +173,7 @@ export function extractFilterMetadata( */ function isGlobalFunction( global: CreatedTemplateGlobal, -): global is CreatedTemplateGlobalFunction< - TemplateGlobalFunctionSchema | undefined, - any -> { +): global is CreatedTemplateGlobalFunction { return 'fn' in global; } diff --git a/plugins/scaffolder-node/report-alpha.api.md b/plugins/scaffolder-node/report-alpha.api.md index 151af9ea37..6ff02c7ad0 100644 --- a/plugins/scaffolder-node/report-alpha.api.md +++ b/plugins/scaffolder-node/report-alpha.api.md @@ -29,56 +29,31 @@ export type AutocompleteHandler = ({ // @alpha (undocumented) export type CreatedTemplateFilter< - TSchema extends - | TemplateFilterSchema - | undefined - | unknown = unknown, - TFilterSchema extends TSchema extends TemplateFilterSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : TemplateFilter = TSchema extends TemplateFilterSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : TemplateFilter, + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, > = { id: string; description?: string; examples?: TemplateFilterExample[]; - schema?: TSchema; - filter: TFilterSchema; + schema?: ZodFunctionSchema; + filter: (...args: z.infer>) => z.infer; }; // @alpha (undocumented) export type CreatedTemplateGlobal = | CreatedTemplateGlobalValue - | CreatedTemplateGlobalFunction; + | CreatedTemplateGlobalFunction; // @alpha (undocumented) export type CreatedTemplateGlobalFunction< - TSchema extends - | TemplateGlobalFunctionSchema - | undefined - | unknown = unknown, - TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : Exclude< - TemplateGlobal, - JsonValue - > = TSchema extends TemplateGlobalFunctionSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : Exclude, + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, > = { id: string; description?: string; examples?: TemplateGlobalFunctionExample[]; - schema?: TSchema; - fn: TFilterSchema; + schema?: ZodFunctionSchema; + fn: (...args: z.infer>) => z.infer; }; // @alpha (undocumented) @@ -90,23 +65,27 @@ export type CreatedTemplateGlobalValue = { // @alpha export const createTemplateFilter: < - TSchema extends TemplateFilterSchema | undefined, - TFilterSchema extends TSchema extends TemplateFilterSchema - ? z.infer> - : (arg: JsonValue, ...rest: JsonValue[]) => JsonValue | undefined, ->( - filter: CreatedTemplateFilter, -) => CreatedTemplateFilter; + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, +>(options: { + id: string; + description?: string; + examples?: TemplateFilterExample[]; + schema?: ZodFunctionSchema; + filter: (...args: z.infer>) => z.infer; +}) => CreatedTemplateFilter; // @alpha export const createTemplateGlobalFunction: < - TSchema extends TemplateGlobalFunctionSchema | undefined, - TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema - ? z.infer> - : (...args: JsonValue[]) => JsonValue | undefined, ->( - fn: CreatedTemplateGlobalFunction, -) => CreatedTemplateGlobalFunction; + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, +>(options: { + id: string; + description?: string; + examples?: TemplateGlobalFunctionExample[]; + schema?: ZodFunctionSchema; + fn: (...args: z.infer>) => z.infer; +}) => CreatedTemplateGlobalFunction; // @alpha export const createTemplateGlobalValue: ( @@ -158,7 +137,7 @@ export interface ScaffolderTemplatingExtensionPoint { addTemplateFilters( filters: | Record - | CreatedTemplateFilter[], + | CreatedTemplateFilter[], ): void; // (undocumented) addTemplateGlobals( @@ -196,16 +175,6 @@ export type TemplateFilterExample = { notes?: string; }; -// @alpha (undocumented) -export type TemplateFilterSchema< - Args extends z.ZodTuple< - | [z.ZodType] - | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], - z.ZodType | z.ZodUnknown | null - >, - Result extends z.ZodType | z.ZodUndefined, -> = (zod: typeof z) => z.ZodFunction; - // @public (undocumented) export type TemplateGlobal = | ((...args: JsonValue[]) => JsonValue | undefined) @@ -218,15 +187,6 @@ export type TemplateGlobalFunctionExample = { notes?: string; }; -// @alpha (undocumented) -export type TemplateGlobalFunctionSchema< - Args extends z.ZodTuple< - [] | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], - z.ZodType | z.ZodUnknown | null - >, - Result extends z.ZodType | z.ZodUndefined, -> = (zod: typeof z) => z.ZodFunction; - // @alpha export interface WorkspaceProvider { // (undocumented) @@ -246,5 +206,17 @@ export interface WorkspaceProvider { }): Promise; } +// @alpha +export type ZodFunctionSchema< + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, +> = ( + zod: typeof z, +) => + | z.ZodFunction, TReturnType> + | z.ZodType< + (...args: z.infer>) => z.infer + >; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/scaffolder-node/src/alpha/filters/createTemplateFilter.ts b/plugins/scaffolder-node/src/alpha/filters/createTemplateFilter.ts index 219884b704..03fb7fd18e 100644 --- a/plugins/scaffolder-node/src/alpha/filters/createTemplateFilter.ts +++ b/plugins/scaffolder-node/src/alpha/filters/createTemplateFilter.ts @@ -14,8 +14,11 @@ * limitations under the License. */ -import { JsonValue } from '@backstage/types'; -import { CreatedTemplateFilter, TemplateFilterSchema } from './types'; +import { + CreatedTemplateFilter, + TemplateFilterExample, + ZodFunctionSchema, +} from './types'; import { z } from 'zod'; /** @@ -23,10 +26,12 @@ import { z } from 'zod'; * @alpha */ export const createTemplateFilter = < - TSchema extends TemplateFilterSchema | undefined, - TFilterSchema extends TSchema extends TemplateFilterSchema - ? z.infer> - : (arg: JsonValue, ...rest: JsonValue[]) => JsonValue | undefined, ->( - filter: CreatedTemplateFilter, -): CreatedTemplateFilter => filter; + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, +>(options: { + id: string; + description?: string; + examples?: TemplateFilterExample[]; + schema?: ZodFunctionSchema; + filter: (...args: z.infer>) => z.infer; +}): CreatedTemplateFilter => options; diff --git a/plugins/scaffolder-node/src/alpha/filters/types.ts b/plugins/scaffolder-node/src/alpha/filters/types.ts index 2f65baaefc..741d4e2ca7 100644 --- a/plugins/scaffolder-node/src/alpha/filters/types.ts +++ b/plugins/scaffolder-node/src/alpha/filters/types.ts @@ -14,21 +14,9 @@ * limitations under the License. */ import { z } from 'zod'; -import { TemplateFilter } from '../../types'; -import { JsonValue } from '@backstage/types'; export type { TemplateFilter } from '../../types'; -/** @alpha */ -export type TemplateFilterSchema< - Args extends z.ZodTuple< - | [z.ZodType] - | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], - z.ZodType | z.ZodUnknown | null - >, - Result extends z.ZodType | z.ZodUndefined, -> = (zod: typeof z) => z.ZodFunction; - /** @alpha */ export type TemplateFilterExample = { description?: string; @@ -36,25 +24,29 @@ export type TemplateFilterExample = { notes?: string; }; +/** + * Type for template filter schema functions that supports both Zod function definition methods: + * @alpha + */ +export type ZodFunctionSchema< + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, +> = ( + zod: typeof z, +) => + | z.ZodFunction, TReturnType> + | z.ZodType< + (...args: z.infer>) => z.infer + >; + /** @alpha */ export type CreatedTemplateFilter< - TSchema extends - | TemplateFilterSchema - | undefined - | unknown = unknown, - TFilterSchema extends TSchema extends TemplateFilterSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : TemplateFilter = TSchema extends TemplateFilterSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : TemplateFilter, + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, > = { id: string; description?: string; examples?: TemplateFilterExample[]; - schema?: TSchema; - filter: TFilterSchema; + schema?: ZodFunctionSchema; + filter: (...args: z.infer>) => z.infer; }; diff --git a/plugins/scaffolder-node/src/alpha/globals/createTemplateGlobal.ts b/plugins/scaffolder-node/src/alpha/globals/createTemplateGlobal.ts index 2361a50964..a277363f3a 100644 --- a/plugins/scaffolder-node/src/alpha/globals/createTemplateGlobal.ts +++ b/plugins/scaffolder-node/src/alpha/globals/createTemplateGlobal.ts @@ -18,9 +18,9 @@ import { z } from 'zod'; import { CreatedTemplateGlobalFunction, CreatedTemplateGlobalValue, - TemplateGlobalFunctionSchema, + TemplateGlobalFunctionExample, } from './types'; -import { JsonValue } from '@backstage/types'; +import { ZodFunctionSchema } from '../filters'; /** * This function is used to create new template global values in type-safe manner. @@ -39,10 +39,12 @@ export const createTemplateGlobalValue = ( * @alpha */ export const createTemplateGlobalFunction = < - TSchema extends TemplateGlobalFunctionSchema | undefined, - TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema - ? z.infer> - : (...args: JsonValue[]) => JsonValue | undefined, ->( - fn: CreatedTemplateGlobalFunction, -): CreatedTemplateGlobalFunction => fn; + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, +>(options: { + id: string; + description?: string; + examples?: TemplateGlobalFunctionExample[]; + schema?: ZodFunctionSchema; + fn: (...args: z.infer>) => z.infer; +}): CreatedTemplateGlobalFunction => options; diff --git a/plugins/scaffolder-node/src/alpha/globals/types.ts b/plugins/scaffolder-node/src/alpha/globals/types.ts index cbe74f1610..4754bcaa88 100644 --- a/plugins/scaffolder-node/src/alpha/globals/types.ts +++ b/plugins/scaffolder-node/src/alpha/globals/types.ts @@ -15,7 +15,7 @@ */ import { JsonValue } from '@backstage/types'; import { z } from 'zod'; -import { TemplateGlobal } from '../../types'; +import { ZodFunctionSchema } from '../filters/types'; export type { TemplateGlobal } from '../../types'; @@ -26,15 +26,6 @@ export type CreatedTemplateGlobalValue = { description?: string; }; -/** @alpha */ -export type TemplateGlobalFunctionSchema< - Args extends z.ZodTuple< - [] | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], - z.ZodType | z.ZodUnknown | null - >, - Result extends z.ZodType | z.ZodUndefined, -> = (zod: typeof z) => z.ZodFunction; - /** @alpha */ export type TemplateGlobalFunctionExample = { description?: string; @@ -44,31 +35,17 @@ export type TemplateGlobalFunctionExample = { /** @alpha */ export type CreatedTemplateGlobalFunction< - TSchema extends - | TemplateGlobalFunctionSchema - | undefined - | unknown = unknown, - TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : Exclude< - TemplateGlobal, - JsonValue - > = TSchema extends TemplateGlobalFunctionSchema - ? z.infer> - : TSchema extends unknown - ? unknown - : Exclude, + TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], + TReturnType extends z.ZodTypeAny, > = { id: string; description?: string; examples?: TemplateGlobalFunctionExample[]; - schema?: TSchema; - fn: TFilterSchema; + schema?: ZodFunctionSchema; + fn: (...args: z.infer>) => z.infer; }; /** @alpha */ export type CreatedTemplateGlobal = | CreatedTemplateGlobalValue - | CreatedTemplateGlobalFunction; + | CreatedTemplateGlobalFunction; diff --git a/plugins/scaffolder-node/src/alpha/index.ts b/plugins/scaffolder-node/src/alpha/index.ts index 1fd5abee18..eb97447a5c 100644 --- a/plugins/scaffolder-node/src/alpha/index.ts +++ b/plugins/scaffolder-node/src/alpha/index.ts @@ -73,9 +73,7 @@ export const scaffolderTaskBrokerExtensionPoint = */ export interface ScaffolderTemplatingExtensionPoint { addTemplateFilters( - filters: - | Record - | CreatedTemplateFilter[], + filters: Record | CreatedTemplateFilter[], ): void; addTemplateGlobals(