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

feat(settings): allow disable jsdoc guide #136

Merged
merged 2 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.
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 @@ -63,7 +63,7 @@
"prepublishOnly": "yarn build"
},
"devDependencies": {
"@homer0/prettier-plugin-jsdoc": "^4.0.1",
"@homer0/prettier-plugin-jsdoc": "^4.0.5",
"@prisma-labs/prettier-config": "0.1.0",
"@prisma/client": "2.30.0",
"@prisma/sdk": "2.30.0",
Expand Down
33 changes: 33 additions & 0 deletions src/generator/gentime/settingsSingleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,36 @@ export namespace Gentime {
* @default 'Int'
*/
projectIdIntToGraphQL?: 'ID' | 'Int'
// TODO once fixed https://github.com/homer0/packages/issues/21
// use @default tag in this JSDoc block
/**
* Nexus Prisma will project your Prisma schema field/model/enum documentation into JSDoc of the generated Nexus Prisma API.
*
* This setting controls what Nexus Prisma should do when you have not written documentation in your Prisma Schema for a field/model/enum.
*
* The following modes are as follows:
*
* 1. `none`
*
* In this mode, no default JSDoc will be written.
*
* 2. `guide`
*
* In this mode, guide content into your JSDoc that looks something like the following:
*
* ```
* * ### ️⚠️ 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:
* * ...
* * ...
* * ...
* ```
*
* The default is `guide`.
*/
jsdocPropagationDefault?: 'none' | 'guide'
// TODO add some examples
/**
* Should Prisma Schema docs propagate as docs?
Expand Down Expand Up @@ -63,6 +93,9 @@ export namespace Gentime {
projectIdIntToGraphQL: {
initial: () => 'Int',
},
jsdocPropagationDefault: {
initial: () => 'guide',
},
docPropagation: {
shorthand: (enabled) => ({
GraphQLDocs: enabled,
Expand Down
123 changes: 77 additions & 46 deletions src/generator/helpers/JSDocTemplates.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import { DMMF } from '@prisma/client/runtime'
import dedent from 'dindist'
import { PrismaDocumentation } from '../../lib/prisma-documnetation'
import { Gentime } from '../gentime/settingsSingleton'

type JSDoc = string

type FieldModelParams = {
field: DMMF.Field
model: DMMF.Model
settings: Gentime.Settings
}

const jsdocIndent = ' '
const jsdocEmptyLine = `\n${jsdocIndent}*\n`

/**
* Enum
*/

export function jsDocForEnum(enum_: DMMF.DatamodelEnum): JSDoc {
return dedent`
/**
${enumIntro(enum_)}
*
${nodeDocumentation({ enum: enum_ })}
*
* Contains these members: ${enum_.values.map((value) => value.name).join(', ')}
*
${enumExample(enum_)}
*/
`
export function jsDocForEnum(params: { enum: DMMF.DatamodelEnum; settings: Gentime.Settings }): JSDoc {
const sections = [
enumIntro(params.enum),
nodeDocumentation({
enum: params.enum,
settings: params.settings,
}),
`* Contains these members: ${params.enum.values.map((value) => value.name).join(', ')}`,
enumExample(params.enum),
]
const jsdoc = jsDocBookends(joinSections(sections))
return jsdoc
}

function enumIntro(enum_: DMMF.DatamodelEnum): string {
Expand All @@ -44,7 +49,7 @@ function enumExample(enum_: DMMF.DatamodelEnum): string {
`
}

function enumMissingDoc(enum_: DMMF.DatamodelEnum): string {
function enumMissingDocGuide(enum_: DMMF.DatamodelEnum): string {
return dedent`
${missingDocsIntro({ kind: 'enum', enum: enum_ })}
*
Expand All @@ -63,16 +68,10 @@ function enumMissingDoc(enum_: DMMF.DatamodelEnum): string {
* Model
*/

export function jsDocForModel(model: DMMF.Model): JSDoc {
return dedent`
/**
${modelIntro(model)}
*
${nodeDocumentation({ model })}
*
${modelExample(model)}
*/
`
export function jsDocForModel(params: { model: DMMF.Model; settings: Gentime.Settings }): JSDoc {
const sections = [modelIntro(params.model), nodeDocumentation(params), modelExample(params.model)]
const jsdoc = jsDocBookends(joinSections(sections))
return jsdoc
}

function modelIntro(model: DMMF.Model): string {
Expand All @@ -82,8 +81,11 @@ function modelIntro(model: DMMF.Model): string {
}

const nodeDocumentation = (
params: { model: DMMF.Model } | { model: DMMF.Model; field: DMMF.Field } | { enum: DMMF.DatamodelEnum }
): string | undefined => {
params:
| { settings: Gentime.Settings; model: DMMF.Model }
| { settings: Gentime.Settings; model: DMMF.Model; field: DMMF.Field }
| { settings: Gentime.Settings; enum: DMMF.DatamodelEnum }
): string | null => {
const documentation =
'field' in params
? params.field.documentation
Expand All @@ -93,18 +95,28 @@ const nodeDocumentation = (
? 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)
if (documentation) {
return dedent`
* ${PrismaDocumentation.format(documentation)}
`
}

if (params.settings.data.jsdocPropagationDefault === 'guide') {
return 'field' in params
? fieldMissingDocGuide({
field: params.field,
model: params.model,
settings: params.settings,
})
: 'model' in params
? modelMissingDocGuide(params.model)
: enumMissingDocGuide(params.enum)
}

return doc
return null
}

function modelMissingDoc(model: DMMF.Model): string {
function modelMissingDocGuide(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
return dedent`
Expand Down Expand Up @@ -142,16 +154,10 @@ function modelExample(model: DMMF.Model): string {
* Field
*/

export function jsDocForField({ field, model }: FieldModelParams): JSDoc {
return dedent`
/**
${fieldIntro({ field, model })}
*
${nodeDocumentation({ field, model })}
*
${fieldExample({ field, model })}
*/
`
export function jsDocForField(params: FieldModelParams): JSDoc {
const sections = [fieldIntro(params), nodeDocumentation(params), fieldExample(params)]
const jsdoc = jsDocBookends(joinSections(sections))
return jsdoc
}

function fieldIntro({ model, field }: FieldModelParams): string {
Expand All @@ -160,7 +166,7 @@ function fieldIntro({ model, field }: FieldModelParams): string {
`
}

function fieldMissingDoc({ model, field }: FieldModelParams): string {
function fieldMissingDocGuide({ model, field }: FieldModelParams): string {
return dedent`
${missingDocsIntro({ kind: 'model', model })}
* \`\`\`prisma
Expand Down Expand Up @@ -210,8 +216,33 @@ function missingDocsIntro(
*
* Replace this default advisory JSDoc with your own documentation about ${thisItem}
* by documenting it in your Prisma schema. For example:
*
`
}

const missingDocsOutro = `* Learn more about documentation comments in Prisma schema files [here](https://www.prisma.io/docs/concepts/components/prisma-schema#comments).`

/**
* Convert a list of JSDoc sections into a single unified JSDoc section.
*
* Each joined section is separated by an empty line.
*
* Each section being joined is expected to handle its own JSDoc "spine" (e.g. `* some content here`).
*/
const joinSections = (sections: (string | null)[]) => {
return sections.filter((section) => section !== null).join(jsdocEmptyLine + jsdocIndent)
}

const jsDocBookends = (content: string) => {
const start = `/**`
const end = '*/'
const body = prefixBlock(jsdocIndent, content)

return `${start}\n${body}\n${jsdocIndent}${end}`
}

const prefixBlock = (prefix: string, content: string): string => {
return content
.split('\n')
.map((_) => `${prefix}${_.trim()}`)
.join('\n')
}
6 changes: 3 additions & 3 deletions src/generator/models/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export function renderTypeScriptDeclarationForDocumentModels(
}

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

return dedent`
Expand All @@ -173,7 +173,7 @@ 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 jsdoc = settings.data.docPropagation.JSDoc ? jsDocForModel({ model, settings }) + '\n' : ''
const description = renderPrismaNodeDocumentationToDescription({ settings, node: model })

return dedent`
Expand Down Expand Up @@ -209,7 +209,7 @@ function renderTypeScriptDeclarationForField({
model: DMMF.Model
settings: Gentime.Settings
}): string {
const jsdoc = settings.data.docPropagation.JSDoc ? jsDocForField({ field, model }) + '\n' : ''
const jsdoc = settings.data.docPropagation.JSDoc ? jsDocForField({ field, model, settings }) + '\n' : ''
const description = renderPrismaNodeDocumentationToDescription({ settings, node: field })
return dedent`
${jsdoc}${field.name}: {
Expand Down
13 changes: 12 additions & 1 deletion tests/__helpers__/testers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,19 @@ type IntegrationTestParams = IntegrationTestSpec & {
/**
* Test that the given Prisma schema generates the expected generated source code.
*/
export function testGeneratedModules(params: { databaseSchema: string; description: string }) {
export function testGeneratedModules(params: {
description: string
databaseSchema: string
/**
* The gentime settings to use.
*/
settings?: Gentime.SettingsInput
}) {
it(params.description, async () => {
Gentime.settings.reset()
if (params.settings) {
Gentime.settings.change(params.settings)
}
const { indexdts } = await generateModules(params.databaseSchema)
expect(indexdts).toMatchSnapshot('index.d.ts')
})
Expand Down
Loading