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: handle multiline prisma docs #134

Merged
merged 7 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Official Prisma plugin for Nexus.
- [Prisma Schema Docs Propagation](#prisma-schema-docs-propagation)
- [As GraphQL schema doc](#as-graphql-schema-doc)
- [As JSDoc](#as-jsdoc)
- [Rich Formatting](#rich-formatting)
- [ESM Support](#esm-support)
- [Refined DX](#refined-dx)
- [Recipes](#recipes)
Expand Down Expand Up @@ -798,6 +799,41 @@ User // JSDoc: A user.
User.id // JSDoc: A stable identifier to find users by.
```

#### Rich Formatting

It is possible to write multiline documentation in your Prisma Schema file. It is also possible to write markdown or whatever else you want.

```prisma
/// # Foo _bar_
/// qux
///
/// tot
model Foo {
/// Foo bar
/// qux
///
/// tot
foo String
}
```

However, you should understand the formatting logic Nexus Prisma uses as it may limit what you want to achieve. The current logic is:

1. Strip newlines
1. Collapse multi-spaces spaces into single-space

So the above would get extracted by Nexus Prisma as if it was written like this:

```prisma
/// # Foo _bar_ qux tot
model Foo {
/// Foo bar qux tot
foo String
}
```

This formatting logic is conservative. We are open to making it less so, in order to support more expressivity. Please [open an issue](https://github.com/prisma/nexus-prisma/issues/new?assignees=&labels=type%2Ffeat&template=10-feature.md&title=Better%20extraction%20of%20Prisma%20documentation) if you have an idea.

### ESM Support

Nexus Prisma supports both [ESM](https://nodejs.org/api/esm.html) and CJS. There shouldn't be anything you need to "do", things should "just work". Here's the highlights of how it works though:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@
"strip-ansi": "^6",
"ts-jest": "27.0.4",
"ts-node": "^10.1.0",
"ts-replace-all": "^1.0.0",
"type-fest": "^1.2.2",
"typescript": "4.3.5",
"zod": "^3.5.1"
Expand Down Expand Up @@ -132,6 +131,7 @@
"pluralize": "^8.0.0",
"semver": "^7.3.5",
"setset": "^0.0.7",
"ts-replace-all": "^1.0.0",
"tslib": "^2.3.0"
},
"nodemonConfig": {
Expand Down
39 changes: 30 additions & 9 deletions src/generator/helpers/JSDocTemplates.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DMMF } from '@prisma/client/runtime'
import dedent from 'dindist'
import { PrismaDocumentation } from '../../lib/prisma-documnetation'

type JSDoc = string

Expand All @@ -13,12 +14,11 @@ type FieldModelParams = {
*/

export function jsDocForEnum(enum_: DMMF.DatamodelEnum): JSDoc {
const enumDoc = enum_.documentation ? `* ${enum_.documentation}` : enumMissingDoc(enum_)
return dedent`
/**
${enumIntro(enum_)}
*
${enumDoc}
${nodeDocumentation({ enum: enum_ })}
*
* Contains these members: ${enum_.values.map((value) => value.name).join(', ')}
*
Expand Down Expand Up @@ -64,12 +64,11 @@ function enumMissingDoc(enum_: DMMF.DatamodelEnum): string {
*/

export function jsDocForModel(model: DMMF.Model): JSDoc {
const modelDoc = model.documentation ? `* ${model.documentation}` : modelMissingDoc(model)
return dedent`
/**
${modelIntro(model)}
*
${modelDoc}
${nodeDocumentation({ model })}
*
${modelExample(model)}
*/
Expand All @@ -82,6 +81,29 @@ function modelIntro(model: DMMF.Model): string {
`
}

const nodeDocumentation = (
params: { model: DMMF.Model } | { model: DMMF.Model; field: DMMF.Field } | { enum: DMMF.DatamodelEnum }
): string | undefined => {
const documentation =
'field' in params
? params.field.documentation
: 'model' in params
? params.model.documentation
: 'enum' in params
? params.enum.documentation
: null

const doc = documentation
? `* ${PrismaDocumentation.format(documentation)}`
: 'field' in params
? fieldMissingDoc({ field: params.field, model: params.model })
: 'model' in params
? modelMissingDoc(params.model)
: enumMissingDoc(params.enum)

return doc
}

function modelMissingDoc(model: DMMF.Model): string {
// TODO once https://stackoverflow.com/questions/61893953/how-to-escape-symbol-in-jsdoc-for-vscode
// is resolved then we can write better examples below like: id String @id
Expand Down Expand Up @@ -121,12 +143,11 @@ function modelExample(model: DMMF.Model): string {
*/

export function jsDocForField({ field, model }: FieldModelParams): JSDoc {
const fieldDocs = field.documentation ? `* ${field.documentation}` : fieldMissingDoc({ field, model })
return dedent`
/**
${fieldIntro({ field, model })}
${fieldIntro({ field, model })}
*
${fieldDocs}
${nodeDocumentation({ field, model })}
*
${fieldExample({ field, model })}
*/
Expand Down Expand Up @@ -186,10 +207,10 @@ function missingDocsIntro(

return dedent`
* ### ️⚠️ You have not writen documentation for ${thisItem}
*
*
* Replace this default advisory JSDoc with your own documentation about ${thisItem}
* by documenting it in your Prisma schema. For example:
*
*
`
}

Expand Down
28 changes: 16 additions & 12 deletions src/generator/models/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LiteralUnion } from 'type-fest'
import { StandardGraphQLScalarType, StandardGraphQLScalarTypes } from '../../helpers/graphql'
import { PrismaScalarType } from '../../helpers/prisma'
import { allCasesHandled } from '../../helpers/utils'
import { PrismaDmmf } from '../../lib/prisma-dmmf'
import { Gentime } from '../gentime/settingsSingleton'
import { jsDocForEnum, jsDocForField, jsDocForModel } from '../helpers/JSDocTemplates'
import { ModuleSpec } from '../types'
Expand Down Expand Up @@ -128,7 +129,6 @@ export function renderTypeScriptDeclarationForDocumentModels(

/**
* Adjust Nexus Prisma's [runtime settings](https://pris.ly/nexus-prisma/docs/settings/runtime).
*
*
* @example
*
Expand All @@ -152,8 +152,7 @@ export function renderTypeScriptDeclarationForDocumentModels(
* prismaClientContextField: 'db', // <-- Tell Nexus Prisma
* })
*
* @remarks This is _different_ than Nexus Prisma's [_gentime_
* settings](https://pris.ly/nexus-prisma/docs/settings/gentime).
* @remarks This is _different_ than Nexus Prisma's [_gentime_ settings](https://pris.ly/nexus-prisma/docs/settings/gentime).
*/
export const $settings: typeof Runtime.changeSettings
` + OS.EOL
Expand All @@ -162,9 +161,8 @@ export function renderTypeScriptDeclarationForDocumentModels(

function renderTypeScriptDeclarationForEnum(enum_: DMMF.DatamodelEnum, settings: Gentime.Settings): string {
const jsdoc = settings.data.docPropagation.JSDoc ? jsDocForEnum(enum_) + '\n' : ''
const description = `${
enum_.documentation && settings.data.docPropagation.GraphQLDocs ? `'${enum_.documentation}'` : 'undefined'
}`
const description = renderPrismaNodeDocumentationToDescription({ settings, node: enum_ })

return dedent`
${jsdoc}export interface ${enum_.name} {
name: '${enum_.name}'
Expand All @@ -176,9 +174,8 @@ function renderTypeScriptDeclarationForEnum(enum_: DMMF.DatamodelEnum, settings:

function renderTypeScriptDeclarationForModel(model: DMMF.Model, settings: Gentime.Settings): string {
const jsdoc = settings.data.docPropagation.JSDoc ? jsDocForModel(model) + '\n' : ''
const description = `${
model.documentation && settings.data.docPropagation.GraphQLDocs ? `'${model.documentation}'` : 'undefined'
}`
const description = renderPrismaNodeDocumentationToDescription({ settings, node: model })

return dedent`
${jsdoc}export interface ${model.name} {
$name: '${model.name}'
Expand All @@ -188,6 +185,15 @@ function renderTypeScriptDeclarationForModel(model: DMMF.Model, settings: Gentim
`
}

const renderPrismaNodeDocumentationToDescription = (params: {
settings: Gentime.Settings
node: PrismaDmmf.DocumentableNode
}): string => {
return `${
params.node.documentation && params.settings.data.docPropagation.GraphQLDocs ? `string` : `undefined`
}`
}

function renderTypeScriptDeclarationForModelFields(model: DMMF.Model, settings: Gentime.Settings): string {
return model.fields
.map((field) => renderTypeScriptDeclarationForField({ field, model, settings }))
Expand All @@ -204,9 +210,7 @@ function renderTypeScriptDeclarationForField({
settings: Gentime.Settings
}): string {
const jsdoc = settings.data.docPropagation.JSDoc ? jsDocForField({ field, model }) + '\n' : ''
const description = `${
field.documentation && settings.data.docPropagation.GraphQLDocs ? `string` : `undefined`
}`
const description = renderPrismaNodeDocumentationToDescription({ settings, node: field })
return dedent`
${jsdoc}${field.name}: {
/**
Expand Down
21 changes: 16 additions & 5 deletions src/generator/models/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { chain, lowerFirst } from 'lodash'
import * as Nexus from 'nexus'
import { NexusEnumTypeConfig, NexusListDef, NexusNonNullDef, NexusNullDef } from 'nexus/dist/core'
import { MaybePromise, RecordUnknown, Resolver } from '../../helpers/utils'
import { PrismaDmmf } from '../../lib/prisma-dmmf'
import { PrismaDocumentation } from '../../lib/prisma-documnetation'
import { Gentime } from '../gentime/settingsSingleton'
import {
buildWhereUniqueInput,
Expand Down Expand Up @@ -66,8 +68,8 @@ export function createModuleSpec(params: {
dmmf.datamodel.enums
.map((enum_) => {
return dedent`
export const ${enum_.name} = models['${enum_.name}']
`
export const ${enum_.name} = models['${enum_.name}']
`
})
.join('\n') || `// N/A -- You have not defined any enums in your Prisma Schema.`

Expand Down Expand Up @@ -166,13 +168,13 @@ function createNexusObjectTypeDefConfigurations(
.map((model) => {
return {
$name: model.name,
$description: settings.gentime.docPropagation.GraphQLDocs ? model.documentation : undefined,
$description: prismaNodeDocumentationToDescription({ settings, node: model }),
...chain(model.fields)
.map((field) => {
return {
name: field.name,
type: prismaFieldToNexusType(field, settings),
description: settings.gentime.docPropagation.GraphQLDocs ? field.documentation : undefined,
description: prismaNodeDocumentationToDescription({ settings, node: field }),
resolve: prismaFieldToNexusResolver(model, field, settings),
}
})
Expand All @@ -184,6 +186,15 @@ function createNexusObjectTypeDefConfigurations(
.value()
}

const prismaNodeDocumentationToDescription = (params: {
settings: Settings
node: PrismaDmmf.DocumentableNode
}): string | undefined => {
return params.settings.gentime.docPropagation.GraphQLDocs && params.node.documentation
? PrismaDocumentation.format(params.node.documentation)
: undefined
}

// Complex return type I don't really understand how to easily work with manually.
// eslint-disable-next-line
export function prismaFieldToNexusType(field: DMMF.Field, settings: Settings) {
Expand Down Expand Up @@ -295,7 +306,7 @@ function createNexusEnumTypeDefConfigurations(
.map((enum_): AnyNexusEnumTypeConfig => {
return {
name: enum_.name,
description: settings.gentime.docPropagation.GraphQLDocs ? enum_.documentation : undefined,
description: prismaNodeDocumentationToDescription({ settings, node: enum_ }),
members: enum_.values.map((val) => val.name),
}
})
Expand Down
15 changes: 15 additions & 0 deletions src/lib/prisma-dmmf/PrismaDmmf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DMMF } from '@prisma/client/runtime'

export type DocumentableNode = DMMF.Model | DMMF.Field | DMMF.DatamodelEnum

export const isModel = (node: DocumentableNode): node is DMMF.Model => {
return 'fields' in node
}

export const isField = (node: DocumentableNode): node is DMMF.Field => {
return 'isList' in node
}

export const isEnum = (node: DocumentableNode): node is DMMF.DatamodelEnum => {
return 'values' in node
}
1 change: 1 addition & 0 deletions src/lib/prisma-dmmf/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as PrismaDmmf from './PrismaDmmf'
7 changes: 7 additions & 0 deletions src/lib/prisma-documnetation/PrismaDocumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Remove this dep once we stop supporting Node 14.x
import 'ts-replace-all'

export const format = (rawComment: string): string => {
const formattedComment = rawComment.replaceAll(/\n/g, ' ').replaceAll(/ +/g, ' ').trim()
return formattedComment
}
1 change: 1 addition & 0 deletions src/lib/prisma-documnetation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as PrismaDocumentation from './PrismaDocumentation'
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import * as NexusCore from 'nexus/dist/core'
* Generated Nexus \`enumType\` configuration based on your Prisma schema's enum \`Foo\`.
*
* ### ️⚠️ You have not writen documentation for enum Foo
*
*
* Replace this default advisory JSDoc with your own documentation about enum Foo
* by documenting it in your Prisma schema. For example:
*
Expand Down Expand Up @@ -97,7 +97,6 @@ import { Runtime } from '../generator/runtime/settingsSingleton'

/**
* Adjust Nexus Prisma's [runtime settings](https://pris.ly/nexus-prisma/docs/settings/runtime).
*
*
* @example
*
Expand All @@ -121,8 +120,7 @@ import { Runtime } from '../generator/runtime/settingsSingleton'
* prismaClientContextField: 'db', // <-- Tell Nexus Prisma
* })
*
* @remarks This is _different_ than Nexus Prisma's [_gentime_
* settings](https://pris.ly/nexus-prisma/docs/settings/gentime).
* @remarks This is _different_ than Nexus Prisma's [_gentime_ settings](https://pris.ly/nexus-prisma/docs/settings/gentime).
*/
export const $settings: typeof Runtime.changeSettings
"
Expand Down Expand Up @@ -163,7 +161,7 @@ import * as NexusCore from 'nexus/dist/core'
*/
export interface Foo {
name: 'Foo'
description: 'Some documentation'
description: string
members: ['a']
}

Expand Down Expand Up @@ -212,7 +210,6 @@ import { Runtime } from '../generator/runtime/settingsSingleton'

/**
* Adjust Nexus Prisma's [runtime settings](https://pris.ly/nexus-prisma/docs/settings/runtime).
*
*
* @example
*
Expand All @@ -236,8 +233,7 @@ import { Runtime } from '../generator/runtime/settingsSingleton'
* prismaClientContextField: 'db', // <-- Tell Nexus Prisma
* })
*
* @remarks This is _different_ than Nexus Prisma's [_gentime_
* settings](https://pris.ly/nexus-prisma/docs/settings/gentime).
* @remarks This is _different_ than Nexus Prisma's [_gentime_ settings](https://pris.ly/nexus-prisma/docs/settings/gentime).
*/
export const $settings: typeof Runtime.changeSettings
"
Expand Down