From 7a7f6e123eb245e4a32bf9c772bdf04ea477f5d5 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:34:21 +0800 Subject: [PATCH 1/2] feat: trpc plugin, generate client helpers to provide prisima-like typing --- packages/plugins/trpc/package.json | 7 +- packages/plugins/trpc/res/client/next.ts | 17 ++ packages/plugins/trpc/res/client/react.ts | 17 ++ packages/plugins/trpc/res/client/utils.ts | 32 +++ packages/plugins/trpc/src/generator.ts | 244 +++++++++++++++------- packages/plugins/trpc/src/helpers.ts | 210 ++++++++++++++++++- packages/plugins/trpc/tests/trpc.test.ts | 55 +++++ pnpm-lock.yaml | 201 ++++++++++++++++++ 8 files changed, 705 insertions(+), 78 deletions(-) create mode 100644 packages/plugins/trpc/res/client/next.ts create mode 100644 packages/plugins/trpc/res/client/react.ts create mode 100644 packages/plugins/trpc/res/client/utils.ts diff --git a/packages/plugins/trpc/package.json b/packages/plugins/trpc/package.json index df5e11664..198f8ce8a 100644 --- a/packages/plugins/trpc/package.json +++ b/packages/plugins/trpc/package.json @@ -10,7 +10,7 @@ }, "scripts": { "clean": "rimraf dist", - "build": "pnpm lint && pnpm clean && tsc && copyfiles ./package.json ./README.md ./LICENSE dist", + "build": "pnpm lint && pnpm clean && tsc && copyfiles ./package.json ./README.md ./LICENSE 'res/**/*' dist", "watch": "tsc --watch", "lint": "eslint src --ext ts", "prepublishOnly": "pnpm build", @@ -32,15 +32,20 @@ "prettier": "^2.8.3", "ts-morph": "^16.0.0", "tslib": "^2.4.1", + "upper-case-first": "^2.0.2", "zod": "3.21.1" }, "devDependencies": { + "@trpc/next": "^10.32.0", + "@trpc/react-query": "^10.32.0", + "@trpc/server": "^10.32.0", "@types/jest": "^29.5.0", "@types/lower-case-first": "^1.0.1", "@types/prettier": "^2.7.2", "@zenstackhq/testtools": "workspace:*", "copyfiles": "^2.4.1", "jest": "^29.5.0", + "next": "^13.4.7", "rimraf": "^3.0.2", "ts-jest": "^29.0.5", "typescript": "^4.9.4" diff --git a/packages/plugins/trpc/res/client/next.ts b/packages/plugins/trpc/res/client/next.ts new file mode 100644 index 000000000..f4cc33629 --- /dev/null +++ b/packages/plugins/trpc/res/client/next.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ + +import type { AnyRouter } from '@trpc/server'; +import type { NextPageContext } from 'next'; +import { type CreateTRPCNext, createTRPCNext as _createTRPCNext } from '@trpc/next'; +import type { DeepOverrideAtPath } from './utils'; +import type { ClientType } from '../routers'; + +export function createTRPCReact< + TRouter extends AnyRouter, + TPath extends string | undefined = undefined, + TSSRContext extends NextPageContext = NextPageContext, + TFlags = null +>(opts: Parameters[0]) { + const r: CreateTRPCNext = _createTRPCNext(opts); + return r as DeepOverrideAtPath, ClientType, TPath>; +} diff --git a/packages/plugins/trpc/res/client/react.ts b/packages/plugins/trpc/res/client/react.ts new file mode 100644 index 000000000..e4c67e3c5 --- /dev/null +++ b/packages/plugins/trpc/res/client/react.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ + +import type { AnyRouter } from '@trpc/server'; +import type { CreateTRPCReactOptions } from '@trpc/react-query/shared'; +import { type CreateTRPCReact, createTRPCReact as _createTRPCReact } from '@trpc/react-query'; +import type { DeepOverrideAtPath } from './utils'; +import type { ClientType } from '../routers'; + +export function createTRPCReact< + TRouter extends AnyRouter, + TPath extends string | undefined = undefined, + TSSRContext = unknown, + TFlags = null +>(opts?: CreateTRPCReactOptions) { + const r: CreateTRPCReact = _createTRPCReact(opts); + return r as DeepOverrideAtPath, ClientType, TPath>; +} diff --git a/packages/plugins/trpc/res/client/utils.ts b/packages/plugins/trpc/res/client/utils.ts new file mode 100644 index 000000000..223fde54d --- /dev/null +++ b/packages/plugins/trpc/res/client/utils.ts @@ -0,0 +1,32 @@ +/* eslint-disable */ + +// inspired by: https://stackoverflow.com/questions/70632026/generic-to-recursively-modify-a-given-type-interface-in-typescript + +type Primitive = string | Function | number | boolean | Symbol | undefined | null; + +/** + * Recursively merges `T` and `R`. If there's a shared key, use `R`'s field type to overwrite `T`. + */ +export type DeepOverride = T extends Primitive + ? R + : R extends Primitive + ? R + : { + [K in keyof T]: K extends keyof R ? DeepOverride : T[K]; + } & { + [K in Exclude]: R[K]; + }; + +/** + * Traverse to `Path` (denoted by dot separated string literal type) in `T`, and starting from there, + * recursively merge with `R`. + */ +export type DeepOverrideAtPath = Path extends undefined + ? DeepOverride + : Path extends `${infer P1}.${infer P2}` + ? P1 extends keyof T + ? Omit & Record>> + : never + : Path extends keyof T + ? Omit & Record> + : never; diff --git a/packages/plugins/trpc/src/generator.ts b/packages/plugins/trpc/src/generator.ts index b2cb7c39b..c43db83bc 100644 --- a/packages/plugins/trpc/src/generator.ts +++ b/packages/plugins/trpc/src/generator.ts @@ -9,16 +9,19 @@ import { saveProject, } from '@zenstackhq/sdk'; import { Model } from '@zenstackhq/sdk/ast'; -import { promises as fs } from 'fs'; +import fs from 'fs'; import { lowerCaseFirst } from 'lower-case-first'; import path from 'path'; -import { Project } from 'ts-morph'; +import { InterfaceDeclarationStructure, Project, PropertySignatureStructure, StructureKind } from 'ts-morph'; +import { upperCaseFirst } from 'upper-case-first'; import { name } from '.'; import { generateHelperImport, generateProcedure, generateRouterSchemaImports, - getInputTypeByOpName, + generateRouterTyping, + generateRouterTypingImports, + getInputSchemaByOpName, resolveModelsComments, } from './helpers'; import { project } from './project'; @@ -30,29 +33,15 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. outDir = resolvePath(outDir, options); // resolve "generateModelActions" option - let generateModelActions: string[] | undefined = undefined; - if (options.generateModelActions) { - if (typeof options.generateModelActions === 'string') { - // comma separated string - generateModelActions = options.generateModelActions - .split(',') - .filter((i) => !!i) - .map((i) => i.trim()); - } else if ( - Array.isArray(options.generateModelActions) && - options.generateModelActions.every((i) => typeof i === 'string') - ) { - // string array - generateModelActions = options.generateModelActions as string[]; - } else { - throw new PluginError( - name, - `Invalid "generateModelActions" option: must be a comma-separated string or an array of strings` - ); - } + const generateModelActions = parseOptionAsStrings(options, 'generateModelActions'); + + // resolve "generateClientHelpers" option + const generateClientHelpers = parseOptionAsStrings(options, 'generateClientHelpers'); + if (generateClientHelpers && !generateClientHelpers.every((v) => ['react', 'next'].includes(v))) { + throw new PluginError(name, `Option "generateClientHelpers" only support values "react" and "next"`); } - await fs.mkdir(outDir, { recursive: true }); + await fs.promises.mkdir(outDir, { recursive: true }); await removeDir(outDir, true); await PrismaZodGenerator(model, options, dmmf); @@ -64,7 +53,7 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. const hiddenModels: string[] = []; resolveModelsComments(models, hiddenModels); - createAppRouter(outDir, modelOperations, hiddenModels, generateModelActions); + createAppRouter(outDir, modelOperations, hiddenModels, generateModelActions, generateClientHelpers); createHelper(outDir); await saveProject(project); @@ -74,7 +63,8 @@ function createAppRouter( outDir: string, modelOperations: DMMF.ModelMapping[], hiddenModels: string[], - generateModelActions: string[] | undefined + generateModelActions: string[] | undefined, + generateClientHelpers: string[] | undefined ) { const appRouter = project.createSourceFile(path.resolve(outDir, 'routers', `index.ts`), undefined, { overwrite: true, @@ -92,7 +82,7 @@ function createAppRouter( moduleSpecifier: '@prisma/client', }, { - namedImports: ['createRouterFactory'], + namedImports: ['createRouterFactory', 'AnyRouter'], moduleSpecifier: '@trpc/server/dist/core/router', }, { @@ -121,46 +111,97 @@ function createAppRouter( `); - const createFunction = appRouter.addFunction({ - name: 'createRouter', - parameters: [ - { name: 'router', type: 'RouterFactory' }, - { name: 'procedure', type: 'ProcBuilder' }, - ], - isExported: true, - }); + const filteredModelOperations = modelOperations.filter((mo) => !hiddenModels.includes(mo.model)); - createFunction.setBodyText((writer) => { - writer.write('return router('); - writer.block(() => { - for (const modelOperation of modelOperations) { - const { model, ...operations } = modelOperation; - if (hiddenModels.includes(model)) { - continue; - } + appRouter + .addFunction({ + name: 'createRouter', + parameters: [ + { name: 'router', type: 'RouterFactory' }, + { name: 'procedure', type: 'ProcBuilder' }, + ], + isExported: true, + }) + .setBodyText((writer) => { + writer.write('return router('); + writer.block(() => { + for (const modelOperation of filteredModelOperations) { + const { model, ...operations } = modelOperation; + generateModelCreateRouter( + project, + model, + operations, + outDir, + generateModelActions, + generateClientHelpers + ); + + appRouter.addImportDeclaration({ + defaultImport: `create${model}Router`, + moduleSpecifier: `./${model}.router`, + }); - generateModelCreateRouter(project, model, operations, outDir, generateModelActions); + writer.writeLine(`${lowerCaseFirst(model)}: create${model}Router(router, procedure),`); + } + }); + writer.write(');'); + }); + if (generateClientHelpers) { + appRouter.addInterface({ + name: 'ClientType', + typeParameters: ['AppRouter extends AnyRouter'], + isExported: true, + properties: filteredModelOperations.map(({ model }) => { appRouter.addImportDeclaration({ - defaultImport: `create${model}Router`, + namedImports: [{ name: 'ClientType', alias: `${upperCaseFirst(model)}ClientType` }], moduleSpecifier: `./${model}.router`, }); - - writer.writeLine(`${lowerCaseFirst(model)}: create${model}Router(router, procedure),`); - } + return { + name: lowerCaseFirst(model), + type: `${upperCaseFirst(model)}ClientType`, + } as PropertySignatureStructure; + }), }); - writer.write(');'); - }); + + createClientHelpers(outDir, generateClientHelpers); + } appRouter.formatText(); } +function createClientHelpers(outputDir: string, generateClientHelpers: string[]) { + const utils = project.createSourceFile(path.resolve(outputDir, 'client', `utils.ts`), undefined, { + overwrite: true, + }); + utils.replaceWithText(fs.readFileSync(path.join(__dirname, './res/client/utils.ts'), 'utf-8')); + + for (const client of generateClientHelpers) { + switch (client) { + case 'react': { + const content = fs.readFileSync(path.join(__dirname, './res/client/react.ts'), 'utf-8'); + project.createSourceFile(path.resolve(outputDir, 'client', 'react.ts'), content, { + overwrite: true, + }); + break; + } + + case 'next': { + const content = fs.readFileSync(path.join(__dirname, './res/client/next.ts'), 'utf-8'); + project.createSourceFile(path.resolve(outputDir, 'client', 'next.ts'), content, { overwrite: true }); + break; + } + } + } +} + function generateModelCreateRouter( project: Project, model: string, operations: Record, outputDir: string, - generateModelActions: string[] | undefined + generateModelActions: string[] | undefined, + generateClientHelpers: string[] | undefined ) { const modelRouter = project.createSourceFile(path.resolve(outputDir, 'routers', `${model}.router.ts`), undefined, { overwrite: true, @@ -177,36 +218,66 @@ function generateModelCreateRouter( generateRouterSchemaImports(modelRouter, model); generateHelperImport(modelRouter); + if (generateClientHelpers) { + generateRouterTypingImports(modelRouter); + } - modelRouter - .addFunction({ - name: 'createRouter', - parameters: [ - { name: 'router', type: 'RouterFactory' }, - { name: 'procedure', type: 'ProcBuilder' }, - ], + const createRouterFunc = modelRouter.addFunction({ + name: 'createRouter', + parameters: [ + { name: 'router', type: 'RouterFactory' }, + { name: 'procedure', type: 'ProcBuilder' }, + ], + isExported: true, + isDefaultExport: true, + }); + + let routerTypingStructure: InterfaceDeclarationStructure | undefined = undefined; + if (generateClientHelpers) { + // generate an interface for precise Prisma-like typing for the router procedures + // which will be used to correct tRPC's typing on the client side + routerTypingStructure = { + kind: StructureKind.Interface, + name: 'ClientType', isExported: true, - isDefaultExport: true, - }) - .setBodyText((writer) => { - writer.write('return router('); - writer.block(() => { - for (const [opType, opNameWithModel] of Object.entries(operations)) { - const baseOpType = opType.replace('OrThrow', ''); - const inputType = getInputTypeByOpName(baseOpType, model); - const generateOpName = opType.replace(/One$/, ''); - - if ( - opNameWithModel && - inputType && - (!generateModelActions || generateModelActions.includes(generateOpName)) - ) { - generateProcedure(writer, generateOpName, inputType, model, baseOpType); + typeParameters: ['AppRouter extends AnyRouter', `Context = AppRouter['_def']['_config']['$types']['ctx']`], + properties: [] as PropertySignatureStructure[], + }; + } + + createRouterFunc.setBodyText((funcWriter) => { + funcWriter.write('return router('); + funcWriter.block(() => { + for (const [opType, opNameWithModel] of Object.entries(operations)) { + const baseOpType = opType.replace('OrThrow', ''); + const inputType = getInputSchemaByOpName(baseOpType, model); + const generateOpName = opType.replace(/One$/, ''); + + if ( + opNameWithModel && + inputType && + (!generateModelActions || generateModelActions.includes(generateOpName)) + ) { + generateProcedure(funcWriter, generateOpName, inputType, model, baseOpType); + + if (routerTypingStructure) { + routerTypingStructure.properties?.push({ + kind: StructureKind.PropertySignature, + name: generateOpName, + type: (writer) => { + generateRouterTyping(writer, generateOpName, model, baseOpType); + }, + }); } } - }); - writer.write(');'); + } }); + funcWriter.write(');'); + }); + + if (routerTypingStructure) { + modelRouter.addInterface(routerTypingStructure); + } modelRouter.formatText(); } @@ -315,3 +386,24 @@ function createHelper(outDir: string) { ); checkRead.formatText(); } + +function parseOptionAsStrings(options: PluginOptions, optionaName: string) { + const value = options[optionaName]; + if (value === undefined) { + return undefined; + } else if (typeof value === 'string') { + // comma separated string + return value + .split(',') + .filter((i) => !!i) + .map((i) => i.trim()); + } else if (Array.isArray(value) && value.every((i) => typeof i === 'string')) { + // string array + return value as string[]; + } else { + throw new PluginError( + name, + `Invalid "${optionaName}" option: must be a comma-separated string or an array of strings` + ); + } +} diff --git a/packages/plugins/trpc/src/helpers.ts b/packages/plugins/trpc/src/helpers.ts index 3972c79e0..41fc3f2d1 100644 --- a/packages/plugins/trpc/src/helpers.ts +++ b/packages/plugins/trpc/src/helpers.ts @@ -1,5 +1,8 @@ import { DMMF } from '@prisma/generator-helper'; +import { PluginError } from '@zenstackhq/sdk'; import { CodeBlockWriter, SourceFile } from 'ts-morph'; +import { upperCaseFirst } from 'upper-case-first'; +import { name } from '.'; import { uncapitalizeFirstLetter } from './utils/uncapitalizeFirstLetter'; export function generateProcedure( @@ -27,6 +30,211 @@ export function generateProcedure( } } +/** + * Given a model and Prisma operation, returns related TS types. + */ +function getPrismaOperationTypes(model: string, operation: string) { + // TODO: find a way to derive from Prisma Client API's generic types + // instead of duplicating them + + const capModel = upperCaseFirst(model); + const capOperation = upperCaseFirst(operation); + + let genericBase = `Prisma.${capModel}${capOperation}Args`; + const getPayload = `Prisma.${capModel}GetPayload`; + const selectSubset = `Prisma.SelectSubset`; + + let argsType: string; + let resultType: string; + + switch (operation) { + case 'findUnique': + case 'findUniqueOrThrow': + argsType = selectSubset; + resultType = getPayload; + break; + + case 'findFirst': + case 'findFirstOrThrow': + argsType = selectSubset; + resultType = getPayload; + break; + + case 'findMany': + argsType = selectSubset; + resultType = getPayload; + break; + + case 'create': + argsType = selectSubset; + resultType = getPayload; + break; + + case 'createMany': + argsType = selectSubset; + resultType = `Prisma.BatchPayload`; + break; + + case 'update': + argsType = selectSubset; + resultType = getPayload; + break; + + case 'updateMany': + argsType = selectSubset; + resultType = `Prisma.BatchPayload`; + break; + + case 'upsert': + argsType = selectSubset; + resultType = getPayload; + break; + + case 'delete': + argsType = selectSubset; + resultType = getPayload; + break; + + case 'deleteMany': + argsType = selectSubset; + resultType = `Prisma.BatchPayload`; + break; + + case 'count': + argsType = `Prisma.Subset`; + resultType = `'select' extends keyof T' + ? T['select'] extends true + ? number + : GetScalarType + : number`; + break; + + case 'aggregate': + argsType = `Prisma.Subset`; + resultType = `Prisma.Get${capModel}AggregateType`; + break; + + case 'groupBy': + genericBase = `Prisma.${capModel}GroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: Prisma.${capModel}GroupByArgs['orderBy'] } + : { orderBy?: Prisma.${capModel}GroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.TupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? \`Error: "by" must not be empty.\` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? \`Error: Field "\${P}" used in "having" needs to be provided in "by".\` + : [ + Error, + 'Field ', + P, + \` in "having" needs to be provided in "by"\`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : \`Error: Field "\${P}" in "orderBy" needs to be provided in "by"\` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : \`Error: Field "\${P}" in "orderBy" needs to be provided in "by"\` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : \`Error: Field "\${P}" in "orderBy" needs to be provided in "by"\` + }[OrderFields] + `; + argsType = `Prisma.SubsetIntersection & InputErrors`; + resultType = `{} extends InputErrors ? Prisma.Get${capModel}GroupByPayload : InputErrors`; + break; + + default: + throw new PluginError(name, `Unsupported operation: "${operation}"`); + } + + return { genericBase, argsType, resultType }; +} + +/** + * Generate precise Prisma-like typing for router procedures. + */ +export function generateRouterTyping(writer: CodeBlockWriter, opType: string, modelName: string, baseOpType: string) { + const procType = getProcedureTypeByOpName(baseOpType); + const { genericBase, argsType, resultType } = getPrismaOperationTypes(modelName, opType); + const errorType = `TRPCClientErrorLike`; + + writer.block(() => { + if (procType === 'query') { + writer.writeLine(` + useQuery: ( + input: ${argsType}, + opts?: UseTRPCQueryOptions + ) => UseTRPCQueryResult< + ${resultType}, + ${errorType} + >; + useInfiniteQuery: ( + input: Omit<${argsType}, 'cursor'>, + opts?: UseTRPCInfiniteQueryOptions + ) => UseTRPCInfiniteQueryResult< + ${resultType}, + ${errorType} + >; + `); + } else if (procType === 'mutation') { + writer.writeLine(` + useMutation: (opts?: UseTRPCMutationOptions< + ${genericBase}, + ${errorType}, + Prisma.${upperCaseFirst(modelName)}GetPayload, + Context + >,) => + Omit, 'mutateAsync'> & { + mutateAsync: + (variables: T, opts?: UseTRPCMutationOptions) => Promise<${resultType}> + }; + `); + } + }); +} + +export function generateRouterTypingImports(sourceFile: SourceFile) { + sourceFile.addStatements([ + `import type { Prisma } from '@prisma/client';`, + `import type { UseTRPCMutationOptions, UseTRPCMutationResult, UseTRPCQueryOptions, UseTRPCQueryResult, UseTRPCInfiniteQueryOptions, UseTRPCInfiniteQueryResult } from '@trpc/react-query/shared';`, + `import type { TRPCClientErrorLike } from '@trpc/client';`, + `import type { AnyRouter } from '@trpc/server';`, + ]); +} + export function generateRouterSchemaImports(sourceFile: SourceFile, name: string) { sourceFile.addStatements(`import { ${name}Schema } from '../schemas/${name}.schema';`); } @@ -35,7 +243,7 @@ export function generateHelperImport(sourceFile: SourceFile) { sourceFile.addStatements(`import { checkRead, checkMutate } from '../helper';`); } -export const getInputTypeByOpName = (opName: string, modelName: string) => { +export const getInputSchemaByOpName = (opName: string, modelName: string) => { let inputType; switch (opName) { case 'findUnique': diff --git a/packages/plugins/trpc/tests/trpc.test.ts b/packages/plugins/trpc/tests/trpc.test.ts index 6d04b320c..16e907ee7 100644 --- a/packages/plugins/trpc/tests/trpc.test.ts +++ b/packages/plugins/trpc/tests/trpc.test.ts @@ -180,4 +180,59 @@ model Post { expect(content).not.toContain('create:'); expect(content).not.toContain('aggregate:'); }); + + const BLOG_BASE_SCHEMA = ` + model User { + id String @id + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + email String @unique + posts Post[] + } + + model Post { + id String @id + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String + author User? @relation(fields: [authorId], references: [id]) + authorId String? + } + `; + + it('generate client helper react-query', async () => { + await loadSchema( + ` + plugin trpc { + provider = '${process.cwd()}/dist' + output = '$projectRoot/trpc' + generateClientHelpers = 'react' + } + + ${BLOG_BASE_SCHEMA} + `, + true, + false, + [`${origDir}/dist`, '@trpc/client', '@trpc/server', '@trpc/react-query'], + true + ); + }); + + it('generate client helper next', async () => { + await loadSchema( + ` + plugin trpc { + provider = '${process.cwd()}/dist' + output = '$projectRoot/trpc' + generateClientHelpers = 'next' + } + + ${BLOG_BASE_SCHEMA} + `, + true, + false, + [`${origDir}/dist`, '@trpc/client', '@trpc/server', '@trpc/next'], + true + ); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f8ada7f7..a170769d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -425,10 +425,22 @@ importers: tslib: specifier: ^2.4.1 version: 2.4.1 + upper-case-first: + specifier: ^2.0.2 + version: 2.0.2 zod: specifier: 3.21.1 version: 3.21.1 devDependencies: + '@trpc/next': + specifier: ^10.32.0 + version: 10.32.0(@tanstack/react-query@4.29.7)(@trpc/client@10.32.0)(@trpc/react-query@10.32.0)(@trpc/server@10.32.0)(next@13.4.7)(react-dom@18.2.0)(react@18.2.0) + '@trpc/react-query': + specifier: ^10.32.0 + version: 10.32.0(@tanstack/react-query@4.29.7)(@trpc/client@10.32.0)(@trpc/server@10.32.0)(react-dom@18.2.0)(react@18.2.0) + '@trpc/server': + specifier: ^10.32.0 + version: 10.32.0 '@types/jest': specifier: ^29.5.0 version: 29.5.0 @@ -447,6 +459,9 @@ importers: jest: specifier: ^29.5.0 version: 29.5.0(@types/node@14.18.29)(ts-node@10.9.1) + next: + specifier: ^13.4.7 + version: 13.4.7(@babel/core@7.20.5)(@opentelemetry/api@1.3.0)(react-dom@18.2.0)(react@18.2.0) rimraf: specifier: ^3.0.2 version: 3.0.2 @@ -2192,6 +2207,10 @@ packages: resolution: {integrity: sha512-SG/gKH6eij4vwQy87b/3mbpQ1X3x2vUdnpwq6/qL2IQWjtq58EY/UuNAp9CoEZoC9sI4L9AD1r+73Z9r4d3uug==} dev: true + /@next/env@13.4.7: + resolution: {integrity: sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw==} + dev: true + /@next/swc-android-arm-eabi@12.3.1: resolution: {integrity: sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==} engines: {node: '>= 10'} @@ -2225,6 +2244,15 @@ packages: dev: true optional: true + /@next/swc-darwin-arm64@13.4.7: + resolution: {integrity: sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@next/swc-darwin-x64@12.3.1: resolution: {integrity: sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==} engines: {node: '>= 10'} @@ -2242,6 +2270,15 @@ packages: dev: true optional: true + /@next/swc-darwin-x64@13.4.7: + resolution: {integrity: sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@next/swc-freebsd-x64@12.3.1: resolution: {integrity: sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==} engines: {node: '>= 10'} @@ -2275,6 +2312,15 @@ packages: dev: true optional: true + /@next/swc-linux-arm64-gnu@13.4.7: + resolution: {integrity: sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@next/swc-linux-arm64-musl@12.3.1: resolution: {integrity: sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==} engines: {node: '>= 10'} @@ -2292,6 +2338,15 @@ packages: dev: true optional: true + /@next/swc-linux-arm64-musl@13.4.7: + resolution: {integrity: sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@next/swc-linux-x64-gnu@12.3.1: resolution: {integrity: sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==} engines: {node: '>= 10'} @@ -2309,6 +2364,15 @@ packages: dev: true optional: true + /@next/swc-linux-x64-gnu@13.4.7: + resolution: {integrity: sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@next/swc-linux-x64-musl@12.3.1: resolution: {integrity: sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==} engines: {node: '>= 10'} @@ -2326,6 +2390,15 @@ packages: dev: true optional: true + /@next/swc-linux-x64-musl@13.4.7: + resolution: {integrity: sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@next/swc-win32-arm64-msvc@12.3.1: resolution: {integrity: sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==} engines: {node: '>= 10'} @@ -2343,6 +2416,15 @@ packages: dev: true optional: true + /@next/swc-win32-arm64-msvc@13.4.7: + resolution: {integrity: sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@next/swc-win32-ia32-msvc@12.3.1: resolution: {integrity: sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==} engines: {node: '>= 10'} @@ -2360,6 +2442,15 @@ packages: dev: true optional: true + /@next/swc-win32-ia32-msvc@13.4.7: + resolution: {integrity: sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@next/swc-win32-x64-msvc@12.3.1: resolution: {integrity: sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==} engines: {node: '>= 10'} @@ -2377,6 +2468,15 @@ packages: dev: true optional: true + /@next/swc-win32-x64-msvc@13.4.7: + resolution: {integrity: sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@noble/hashes@1.3.0: resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} dev: false @@ -2795,6 +2895,55 @@ packages: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} + /@trpc/client@10.32.0(@trpc/server@10.32.0): + resolution: {integrity: sha512-hrj6XV84nE6DH5AmsPJ0JzIRcx3f6zU0tvS705DnZtNBOoMk7Lx+wX+KCDxj4DxLsTXC+roAxZxWwAB6/LGsXA==} + peerDependencies: + '@trpc/server': 10.32.0 + dependencies: + '@trpc/server': 10.32.0 + dev: true + + /@trpc/next@10.32.0(@tanstack/react-query@4.29.7)(@trpc/client@10.32.0)(@trpc/react-query@10.32.0)(@trpc/server@10.32.0)(next@13.4.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-qJ3yvJDTVmiOOPW1x/FCpmOvtcMJXnP2t3EbcUFrqwgH+D1KdbU2Kk1iSa89Gwy/BBEmXUxaKkWoM3e4Id80gw==} + peerDependencies: + '@tanstack/react-query': ^4.18.0 + '@trpc/client': 10.32.0 + '@trpc/react-query': 10.32.0 + '@trpc/server': 10.32.0 + next: '*' + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@tanstack/react-query': 4.29.7(react-dom@18.2.0)(react@18.2.0) + '@trpc/client': 10.32.0(@trpc/server@10.32.0) + '@trpc/react-query': 10.32.0(@tanstack/react-query@4.29.7)(@trpc/client@10.32.0)(@trpc/server@10.32.0)(react-dom@18.2.0)(react@18.2.0) + '@trpc/server': 10.32.0 + next: 13.4.7(@babel/core@7.20.5)(@opentelemetry/api@1.3.0)(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-ssr-prepass: 1.5.0(react@18.2.0) + dev: true + + /@trpc/react-query@10.32.0(@tanstack/react-query@4.29.7)(@trpc/client@10.32.0)(@trpc/server@10.32.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-M4W1aGm3VAfQ9u4FJyK8M+pkcAV4lp32ZHxKnkRydxcsladhsqveZC9oqZqwklprEcYHtWU8v3R+jRGUjfCxpw==} + peerDependencies: + '@tanstack/react-query': ^4.18.0 + '@trpc/client': 10.32.0 + '@trpc/server': 10.32.0 + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@tanstack/react-query': 4.29.7(react-dom@18.2.0)(react@18.2.0) + '@trpc/client': 10.32.0(@trpc/server@10.32.0) + '@trpc/server': 10.32.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@trpc/server@10.32.0: + resolution: {integrity: sha512-C4HRHpaffw2pekpfNSHQtwlocq6kwAQq48mR9RIsUZGNbjXZbgvBC927vRP6QPAoG+n4oHPWyZ9G1hs3KMcPiw==} + dev: true + /@ts-morph/common@0.17.0: resolution: {integrity: sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==} dependencies: @@ -7652,6 +7801,50 @@ packages: - babel-plugin-macros dev: true + /next@13.4.7(@babel/core@7.20.5)(@opentelemetry/api@1.3.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==} + engines: {node: '>=16.8.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + fibers: '>= 3.1.0' + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + fibers: + optional: true + sass: + optional: true + dependencies: + '@next/env': 13.4.7 + '@opentelemetry/api': 1.3.0 + '@swc/helpers': 0.5.1 + busboy: 1.6.0 + caniuse-lite: 1.0.30001439 + postcss: 8.4.14 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.20.5)(react@18.2.0) + watchpack: 2.4.0 + zod: 3.21.4 + optionalDependencies: + '@next/swc-darwin-arm64': 13.4.7 + '@next/swc-darwin-x64': 13.4.7 + '@next/swc-linux-arm64-gnu': 13.4.7 + '@next/swc-linux-arm64-musl': 13.4.7 + '@next/swc-linux-x64-gnu': 13.4.7 + '@next/swc-linux-x64-musl': 13.4.7 + '@next/swc-win32-arm64-msvc': 13.4.7 + '@next/swc-win32-ia32-msvc': 13.4.7 + '@next/swc-win32-x64-msvc': 13.4.7 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: true + /no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: @@ -8283,6 +8476,14 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true + /react-ssr-prepass@1.5.0(react@18.2.0): + resolution: {integrity: sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: true + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} From 11662397280301185e8a40fbc1e21e39fc3aa4a1 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sat, 24 Jun 2023 21:11:33 +0800 Subject: [PATCH 2/2] fix svelte version dependency --- packages/plugins/tanstack-query/tests/plugin.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugins/tanstack-query/tests/plugin.test.ts b/packages/plugins/tanstack-query/tests/plugin.test.ts index b3a7b2c36..fd2f300fa 100644 --- a/packages/plugins/tanstack-query/tests/plugin.test.ts +++ b/packages/plugins/tanstack-query/tests/plugin.test.ts @@ -90,7 +90,7 @@ ${sharedModel} `, true, false, - [`${origDir}/dist`, 'svelte', '@types/react', '@tanstack/svelte-query'], + [`${origDir}/dist`, 'svelte@^3.0.0', '@types/react', '@tanstack/svelte-query'], true ); }); @@ -109,7 +109,7 @@ ${sharedModel} `, true, false, - [`${origDir}/dist`, 'svelte', '@types/react', '@tanstack/svelte-query', 'superjson'], + [`${origDir}/dist`, 'svelte@^3.0.0', '@types/react', '@tanstack/svelte-query', 'superjson'], true ); });