Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use import id @prisma/client by default when possible #88

Merged
merged 7 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"build:module-facades": "ts-node scripts/build-module-facades",
"build": "yarn clean && yarn build:module-facades && tsc",
"test": "cross-env DEBUG=e2e jest",
"test:ci": "cross-env DEBUG=e2e jest --coverage --forceExit --runInBand",
"test:ci": "cross-env DEBUG='e2e' jest --coverage --forceExit --runInBand",
"tdd": "jest --watch",
"tdd:e2e:debug": "cross-env test_project_reuse=true jest --watch e2e",
"clean": "rm -rf dist && rm -rf node_modules/.cache",
Expand Down
94 changes: 88 additions & 6 deletions src/cli/nexus-prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

process.env.DEBUG_COLORS = 'true'
process.env.DEBUG_HIDE_DATE = 'true'
import { generatorHandler } from '@prisma/generator-helper'
import { GeneratorConfig, generatorHandler } from '@prisma/generator-helper'
import * as Path from 'path'
import { generateRuntimeAndEmit } from '../generator'
import { loadUserGentimeSettings } from '../generator/gentime/settingsLoader'
import { Gentime } from '../generator/gentime/settingsSingleton'
import { d } from '../helpers/debugNexusPrisma'
import { externalToInternalDmmf } from '../helpers/prismaExternalToInternalDMMF'

// todo by default error in ci and warn in local
Expand All @@ -28,13 +29,19 @@ generatorHandler({
async onGenerate({ dmmf, otherGenerators }) {
const prismaClientGenerator = otherGenerators.find((g) => g.provider.value === 'prisma-client-js')

// TODO test showing this pretty error in action
if (!prismaClientGenerator) {
// TODO consider a prisma-client-less mode
throw new Error(
`Nexus Prisma cannot be used without Prisma Client. Please add it to your Prisma Schema file.`
)
}

// WARNING: Make sure this logic comes before `loadUserGentimeSettings` below
// otherwise we will overwrite the user's choice for this setting if they have set it.
if (prismaClientGenerator?.output?.value) {
Gentime.settings.change({
prismaClientImportId: prismaClientGenerator.output.value,
})
}
Gentime.settings.change({
prismaClientImportId: getPrismaClientImportIdForItsGeneratorOutputConfig(prismaClientGenerator),
})

const internalDMMF = externalToInternalDmmf(dmmf)
loadUserGentimeSettings()
Expand All @@ -44,3 +51,78 @@ generatorHandler({
)
},
})

/**
* Hehlpers
jasonkuhrt marked this conversation as resolved.
Show resolved Hide resolved
*/

/**
* Get the appropiate import ID for Prisma Client.
*
* When generator output is set to its default location within node_modules, then we return the import ID of just its npm moniker "@prisma/client".
*
* Othewise we return an import ID as an absolute path to the output location.
*/
function getPrismaClientImportIdForItsGeneratorOutputConfig(
prismaClientGeneratorConfig: GeneratorConfig
): string {
const prismaClientPackageMoniker = `@prisma/client`
const prismaClientPackagePath = Path.join(`@prisma`, `client`)

if (!prismaClientGeneratorConfig.output || !prismaClientGeneratorConfig.output.value) {
return prismaClientPackageMoniker
}

if (prismaClientGeneratorConfig.output.value.endsWith(prismaClientPackagePath)) {
/**
* Goal of this code:
*
* Find out if we can import Prisma Client by simplify specifying its moniker (@prisma/client).
*
* Why do we want to import by Moniker? Because it is a good default because it is what a user would
* do in their own code. Also because not doing so has led to bugs https://github.com/prisma/nexus-prisma/issues/76.
*
* How this works:
*
* 1. Get the Prisma Client generatour output path minus the trailing node_moudles/@prisma/client (if present, it could be totally custom).
*
* Note that even if the user has not explicitly configured an output path in their PSL file by the time we get the geneator config from
* Prisma generator system the output default has been supplied so we always have a value here to work with.
*
* 2. Get the Nexus Prisma package path on the user's machine by starting from this module and going four directories up. Four directories
* up goes above the node_modules directory into their code.
*
* Note that we must go four up, not three up (which would be node_modules), because when using Yalc (used for development and E2E tests)
* Nexus Prisma is placed into <project>/.yalc rather than <project>/node_modules. And therefore, the check we want to achieve here would
* fail when it shouldn't.
*
* 3. With the two paths we check what the relative path between them is. If its an empty string, it means they are the same.
*
* Note this technique is better than string comparison because it guards against meaningless path difference details like windows vs posix.
* We're not certain what path standard we'll get from Prisma for example and ideally we don't care. Path.relative function should let us not
* care.
*/
const dirPrismaClientOutputWithoutTrailingNodeModulesMoniker =
prismaClientGeneratorConfig.output.value.replace(
new RegExp(`${Path.join('node_modules', prismaClientPackagePath)}$`),
''
)
const dirProjectForThisNexusPrisma = Path.join(__dirname, '../../../..')
const dirDiff = Path.relative(
dirPrismaClientOutputWithoutTrailingNodeModulesMoniker,
dirProjectForThisNexusPrisma
)

d(`found prisma client/nexus prisma layout:`, {
dirPrismaClientOutputWithoutTrailingNodeModulesMoniker,
dirProjectForThisNexusPrisma,
dirDiff,
})

if (dirDiff === '') {
return prismaClientPackageMoniker
}
}

return prismaClientGeneratorConfig.output.value
}
1 change: 1 addition & 0 deletions src/helpers/prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import kleur = require('kleur')

export const getPrismaClientDmmf = (importId = '@prisma/client'): DMMF.Document => {
d('get dmmf from @prisma/client')
console.log(importId)

let maybeDmmf: DMMF.Document | undefined

Expand Down
10 changes: 9 additions & 1 deletion tests/e2e/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function setupTestNexusPrismaProject(): TestProject {
license: 'MIT',
scripts: {
reflect: 'yarn -s reflect:prisma && yarn -s reflect:nexus',
'reflect:prisma': 'prisma generate',
'reflect:prisma': "cross-env DEBUG='*' prisma generate",
// peer dependency check will fail since we're using yalc, e.g.:
// " ... nexus-prisma@0.0.0-dripip+c2653557 does not officially support @prisma/client@2.22.1 ... "
'reflect:nexus': 'cross-env REFLECT=true ts-node --transpile-only src/schema',
Expand Down Expand Up @@ -334,6 +334,14 @@ it('When bundled custom scalars are used the project type checks and generates e

expect(results.fileTypegen).toMatchSnapshot('nexus typegen')

/**
* Sanity check the Prisma Client import ID
*/

expect(testProject.fs.read('node_modules/nexus-prisma/dist/runtime/index.js')).toMatch(
/.*"prismaClientImportId": "@prisma\/client".*/
)

/**
* Sanity check the runtime
*/
Expand Down