Skip to content

Commit

Permalink
feat: Add field help texts to generated JSDoc (#293)
Browse files Browse the repository at this point in the history
* Add field help texts to generated JSDoc

* Change @helpText tag to @summary

* Pass single editor interface to appendType and render methods
  • Loading branch information
joakimgunst authored Dec 12, 2023
1 parent 6a16a69 commit 99ec2d1
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 22 deletions.
16 changes: 11 additions & 5 deletions src/cf-definitions-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from 'ts-morph';
import { moduleName } from './module-name';
import { ContentTypeRenderer, DefaultContentTypeRenderer } from './renderer';
import { CFContentType, WriteCallback } from './types';
import { CFContentType, CFEditorInterface, WriteCallback } from './types';
import { flatten } from 'lodash';

export default class CFDefinitionsBuilder {
Expand All @@ -36,14 +36,17 @@ export default class CFDefinitionsBuilder {
}
}

public appendType = (model: CFContentType): CFDefinitionsBuilder => {
public appendType = (
model: CFContentType,
editorInterface?: CFEditorInterface,
): CFDefinitionsBuilder => {
if (model.sys.type !== 'ContentType') {
throw new Error('given data is not describing a ContentType');
}

const file = this.addFile(moduleName(model.sys.id));
for (const renderer of this.contentTypeRenderers) {
renderer.render(model, file);
renderer.render(model, file, editorInterface);
}

file.organizeImports({
Expand All @@ -53,9 +56,12 @@ export default class CFDefinitionsBuilder {
return this;
};

public appendTypes = (models: CFContentType[]): CFDefinitionsBuilder => {
public appendTypes = (
models: CFContentType[],
editorInterface?: CFEditorInterface,
): CFDefinitionsBuilder => {
for (const model of models) {
this.appendType(model);
this.appendType(model, editorInterface);
}

return this;
Expand Down
9 changes: 7 additions & 2 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
V10ContentTypeRenderer,
V10TypeGuardRenderer,
} from '../renderer';
import { CFEditorInterface } from '../types';

class ContentfulMdg extends Command {
static description = 'Contentful Content Types (TS Definitions) Generator';
Expand Down Expand Up @@ -63,7 +64,6 @@ class ContentfulMdg extends Command {
spaceId: flags.spaceId,
managementToken: flags.token,
environmentId: flags.environment,
skipEditorInterfaces: true,
skipContent: true,
skipRoles: true,
skipWebhooks: true,
Expand Down Expand Up @@ -92,9 +92,14 @@ class ContentfulMdg extends Command {
renderers.push(flags.v10 ? new V10TypeGuardRenderer() : new TypeGuardRenderer());
}

const editorInterfaces = content.editorInterfaces as CFEditorInterface[] | undefined;

const builder = new CFDefinitionsBuilder(renderers);
for (const model of content.contentTypes) {
builder.appendType(model);
const editorInterface = editorInterfaces?.find(
(e) => e.sys.contentType.sys.id === model.sys.id,
);
builder.appendType(model, editorInterface);
}

if (flags.out) {
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/type/content-type-renderer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Project, SourceFile } from 'ts-morph';
import { CFContentType } from '../../types';
import { CFContentType, CFEditorInterface } from '../../types';
import { RenderContext } from './create-default-context';

export interface ContentTypeRenderer {
setup(project: Project): void;

render(contentType: CFContentType, file: SourceFile): void;
render(contentType: CFContentType, file: SourceFile, editorInterface?: CFEditorInterface): void;

createContext(): RenderContext;

Expand Down
42 changes: 29 additions & 13 deletions src/renderer/type/js-doc-renderer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ContentTypeField } from 'contentful';
import { ContentTypeProps } from 'contentful-management';
import { JSDocStructure, JSDocTagStructure, OptionalKind, SourceFile } from 'ts-morph';
import { CFContentType } from '../../types';
import { CFContentType, CFEditorInterface, CFEditorInterfaceControl } from '../../types';
import { BaseContentTypeRenderer } from './base-content-type-renderer';

type EntryDocsOptionsProps = {
Expand All @@ -19,6 +19,7 @@ type FieldsDocsOptionsProps = {

type FieldDocsOptionsProps = {
readonly field: ContentTypeField;
readonly control?: CFEditorInterfaceControl;
};

type SkeletonDocsOptionsProps = {
Expand Down Expand Up @@ -98,19 +99,28 @@ export const defaultJsDocRenderOptions: Required<JSDocRenderOptions> = {
};
},

renderFieldDocs: ({ field }) => {
renderFieldDocs: ({ field, control }) => {
const tags: OptionalKind<JSDocTagStructure>[] = [
{
tagName: 'name',
text: field.name,
},
{
tagName: 'localized',
text: field.localized.toString(),
},
];

if (control?.settings?.helpText) {
tags.push({
tagName: 'summary',
text: control?.settings?.helpText,
});
}

return {
description: `Field type definition for field '${field.id}' (${field.name})`,
tags: [
{
tagName: 'name',
text: field.name,
},
{
tagName: 'localized',
text: field.localized.toString(),
},
],
tags,
};
},

Expand Down Expand Up @@ -170,7 +180,11 @@ export class JsDocRenderer extends BaseContentTypeRenderer {
};
}

public render = (contentType: CFContentType, file: SourceFile): void => {
public render = (
contentType: CFContentType,
file: SourceFile,
editorInterface?: CFEditorInterface,
): void => {
const context = this.createContext();

const entryInterfaceName = context.moduleName(contentType.sys.id);
Expand Down Expand Up @@ -202,11 +216,13 @@ export class JsDocRenderer extends BaseContentTypeRenderer {
for (const field of fields) {
const fieldName = field.getName();
const contentTypeField = contentType.fields.find((f) => f.id === fieldName);
const control = editorInterface?.controls.find((c) => c.fieldId === fieldName);

if (contentTypeField) {
field.addJsDoc(
this.renderOptions.renderFieldDocs({
field: contentTypeField,
control,
}),
);
}
Expand Down
18 changes: 18 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,21 @@ export type CFContentType = {
};
fields: ContentTypeField[];
};

export type CFEditorInterface = {
sys: {
contentType: {
sys: {
id: string;
};
};
};
controls: CFEditorInterfaceControl[];
};

export type CFEditorInterfaceControl = {
fieldId: string;
settings?: {
helpText: string;
};
};
45 changes: 45 additions & 0 deletions test/renderer/type/js-doc-renderer.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Project, ScriptTarget, SourceFile } from 'ts-morph';
import {
CFContentType,
CFEditorInterface,
DefaultContentTypeRenderer,
JsDocRenderer,
V10ContentTypeRenderer,
Expand Down Expand Up @@ -258,6 +259,50 @@ describe('A JSDoc content type renderer class', () => {
`),
);
});

it('renders field @summary tag', () => {
const defaultRenderer = new DefaultContentTypeRenderer();
defaultRenderer.setup(project);
defaultRenderer.render(mockContentType, testFile);

const docsRenderer = new JsDocRenderer();

const editorInterface: CFEditorInterface = {
sys: { contentType: { sys: { id: 'animal' } } },
controls: [{ fieldId: 'bread', settings: { helpText: 'Help text for the bread field.' } }],
};

docsRenderer.render(mockContentType, testFile, editorInterface);

expect('\n' + testFile.getFullText()).toEqual(
stripIndent(`
import type { Entry, EntryFields } from "contentful";
/**
* Fields type definition for content type 'TypeAnimal'
* @name TypeAnimalFields
* @type {TypeAnimalFields}
* @memberof TypeAnimal
*/
export interface TypeAnimalFields {
/**
* Field type definition for field 'bread' (Bread)
* @name Bread
* @localized false
* @summary Help text for the bread field.
*/
bread: EntryFields.Symbol;
}
/**
* Entry type definition for content type 'animal' (Animal)
* @name TypeAnimal
* @type {TypeAnimal}
*/
export type TypeAnimal = Entry<TypeAnimalFields>;
`),
);
});
});

describe('with custom Entry Docs renderer', () => {
Expand Down

0 comments on commit 99ec2d1

Please sign in to comment.