diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index 8d411eb19..6bcacacb4 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -8,6 +8,7 @@ import { getLiteral, isDelegateModel, isDiscriminatorField, + normalizedRelative, type PluginOptions, } from '@zenstackhq/sdk'; import { @@ -159,7 +160,7 @@ ${ const zodAbsPath = path.isAbsolute(zodCustomOutput) ? zodCustomOutput : path.resolve(schemaDir, zodCustomOutput); - return path.relative(this.outDir, zodAbsPath) || '.'; + return normalizedRelative(this.outDir, zodAbsPath); } private createSimplePrismaImports(prismaImport: string) { diff --git a/packages/schema/src/plugins/enhancer/index.ts b/packages/schema/src/plugins/enhancer/index.ts index 8d05c50f4..c0cd7e13d 100644 --- a/packages/schema/src/plugins/enhancer/index.ts +++ b/packages/schema/src/plugins/enhancer/index.ts @@ -1,4 +1,11 @@ -import { PluginError, RUNTIME_PACKAGE, createProject, resolvePath, type PluginFunction } from '@zenstackhq/sdk'; +import { + PluginError, + RUNTIME_PACKAGE, + createProject, + normalizedRelative, + resolvePath, + type PluginFunction, +} from '@zenstackhq/sdk'; import path from 'path'; import { getDefaultOutputFolder } from '../plugin-utils'; import { EnhancerGenerator } from './enhance'; @@ -31,7 +38,7 @@ const run: PluginFunction = async (model, options, _dmmf, globalOptions) => { const prismaClientPathAbs = path.resolve(outDir, 'models'); // resolve it relative to the schema path - prismaClientPath = path.relative(path.dirname(options.schemaPath), prismaClientPathAbs); + prismaClientPath = normalizedRelative(path.dirname(options.schemaPath), prismaClientPathAbs); } else { prismaClientPath = `${RUNTIME_PACKAGE}/models`; } diff --git a/packages/schema/src/plugins/prisma/index.ts b/packages/schema/src/plugins/prisma/index.ts index d3a9c120a..7fa94cd92 100644 --- a/packages/schema/src/plugins/prisma/index.ts +++ b/packages/schema/src/plugins/prisma/index.ts @@ -1,4 +1,11 @@ -import { PluginError, type PluginFunction, type PluginOptions, getLiteral, resolvePath } from '@zenstackhq/sdk'; +import { + PluginError, + type PluginFunction, + type PluginOptions, + getLiteral, + normalizedRelative, + resolvePath, +} from '@zenstackhq/sdk'; import { GeneratorDecl, isGeneratorDecl } from '@zenstackhq/sdk/ast'; import { getDMMF } from '@zenstackhq/sdk/prisma'; import colors from 'colors'; @@ -59,7 +66,7 @@ const run: PluginFunction = async (model, options, _dmmf, _globalOptions) => { const absPath = path.resolve(path.dirname(output), clientOutput); // then make it relative to the zmodel schema location - prismaClientPath = path.relative(path.dirname(options.schemaPath), absPath); + prismaClientPath = normalizedRelative(path.dirname(options.schemaPath), absPath); } } } else { diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 27198da28..d7ee364af 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -2,6 +2,7 @@ export * from './code-gen'; export * from './constants'; export { generate as generateModelMeta } from './model-meta-generator'; export * from './names'; +export * from './path'; export * from './policy'; export * from './types'; export * from './typescript-expression-transformer'; diff --git a/packages/sdk/src/path.ts b/packages/sdk/src/path.ts new file mode 100644 index 000000000..618abb751 --- /dev/null +++ b/packages/sdk/src/path.ts @@ -0,0 +1,9 @@ +import path from 'path'; + +/** + * Gets the relative path from `from` to `to` and normalizes it to start it with `./` + */ +export function normalizedRelative(from: string, to: string) { + const result = path.relative(from, to); + return result.startsWith('.') ? result : `./${result}`; +} diff --git a/packages/sdk/src/prisma.ts b/packages/sdk/src/prisma.ts index 1edab3c3d..6688bd691 100644 --- a/packages/sdk/src/prisma.ts +++ b/packages/sdk/src/prisma.ts @@ -9,6 +9,7 @@ import { Model } from './ast'; import { RUNTIME_PACKAGE } from './constants'; import type { PluginOptions } from './types'; import { getDataSourceProvider } from './utils'; +import { normalizedRelative } from './path'; /** * Given an import context directory and plugin options, compute the import spec for the Prisma Client. @@ -34,12 +35,11 @@ export function getPrismaClientImportSpec(importingFromDir: string, options: Plu const resolvedPrismaClientOutput = path.resolve(path.dirname(options.schemaPath), options.prismaClientPath); // translate to path relative to the importing context directory - let result = path.relative(importingFromDir, resolvedPrismaClientOutput); + let result = normalizedRelative(importingFromDir, resolvedPrismaClientOutput); // remove leading `node_modules` (which may be provided by the user) result = result.replace(/^([./\\]*)?node_modules\//, ''); - // compute prisma client absolute output dir relative to the importing file return normalizePath(result); } diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index 7cce9bd36..4a7b575bd 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -134,6 +134,7 @@ export type SchemaLoadOptions = { preserveTsFiles?: boolean; generatePermissionChecker?: boolean; previewFeatures?: string[]; + prismaLoadPath?: string; prismaClientOptions?: object; generateNoCompile?: boolean; }; @@ -263,7 +264,13 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { fs.cpSync(dep, path.join(projectDir, 'node_modules', pkgJson.name), { recursive: true, force: true }); }); - const PrismaClient = require(path.join(projectDir, 'node_modules/.prisma/client')).PrismaClient; + const prismaLoadPath = options?.prismaLoadPath + ? path.isAbsolute(options.prismaLoadPath) + ? options.prismaLoadPath + : path.join(projectDir, options.prismaLoadPath) + : path.join(projectDir, 'node_modules/.prisma/client'); + const prismaModule = require(prismaLoadPath); + const PrismaClient = prismaModule.PrismaClient; let clientOptions: object = { log: ['info', 'warn', 'error'] }; if (options?.prismaClientOptions) { @@ -273,8 +280,6 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { // https://github.com/prisma/prisma/issues/18292 prisma[Symbol.for('nodejs.util.inspect.custom')] = 'PrismaClient'; - const prismaModule = loadModule('@prisma/client', projectDir).Prisma; - if (opt.pulseApiKey) { const withPulse = loadModule('@prisma/extension-pulse/node', projectDir).withPulse; prisma = prisma.$extends(withPulse({ apiKey: opt.pulseApiKey })); diff --git a/tests/regression/tests/issue-1743.test.ts b/tests/regression/tests/issue-1743.test.ts new file mode 100644 index 000000000..1c379ae44 --- /dev/null +++ b/tests/regression/tests/issue-1743.test.ts @@ -0,0 +1,34 @@ +import { loadSchema } from '@zenstackhq/testtools'; +describe('issue 1743', () => { + it('regression', async () => { + await loadSchema( + ` + generator client { + provider = "prisma-client-js" + output = '../lib/zenstack/prisma' + } + + datasource db { + provider = "sqlite" + url = "file:./dev.db" + } + + plugin enhancer { + provider = '@core/enhancer' + output = './lib/zenstack' + compile = false + } + + model User { + id Int @id + } + `, + { + addPrelude: false, + compile: true, + output: './lib/zenstack', + prismaLoadPath: './lib/zenstack/prisma', + } + ); + }); +});