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

Add the supplyDefaultsInJsDoc setting #442

Merged
merged 3 commits into from
Mar 15, 2024
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ export interface Settings {
* @default false
*/
supplyDefaultsInType: boolean;
/**
* If a field has a default value, add its stringified representation
* to the JsDoc using the @default annotation
* @default false
*/
supplyDefaultsInJsDoc: boolean;
/**
* Filter files you wish to parse
* The class `InputFileFilter` contains some default options
Expand Down
100 changes: 99 additions & 1 deletion src/__tests__/defaults/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe('Test behaviour for optional fields with supplied defaults', function (
});
it('Test defaults when using the empty default constructor (user-provided value)', function () {
const converted = convertSchema(
{ supplyDefaultsInType: true, treatDefaultedOptionalAsRequired: true },
{ supplyDefaultsInType: true, supplyDefaultsInJsDoc: true, treatDefaultedOptionalAsRequired: true },
Joi.object({
special: Joi.string()
}).default({ special: 'deep' }),
Expand All @@ -173,6 +173,104 @@ describe('Test behaviour for optional fields with supplied defaults', function (
expect(converted).toBeDefined();
expect(converted?.content).toEqual(`export type Test = {"something":"deep"} | {
something?: string;
}`);
});
it('Adds defaults to docs', function () {
const converted = convertSchema(
{ supplyDefaultsInJsDoc: true },
schema.append({
fieldWithDoc: Joi.string().description('A field with a\nmultiline doc').default('My string!'),
fieldWithAnyNull: Joi.any().default(null),
fieldWithMultilineString: Joi.string().default(`A multiline\nstring with\nsome lines`),
fieldWithBigDefault: Joi.array()
.items(Joi.string())
.default(['i', 'have', 'more', 'than', '5', 'values', 'more', 'more', 'more'])
}),
'Test'
);
expect(converted).toBeDefined();
expect(converted?.content).toEqual(`export interface Test {
/**
* @default 'Test'
*/
alt?: string | number;
/**
* @default { val: false }
*/
alt2?: string | number | {
/**
* @default true
*/
val?: boolean;
};
/**
* @default [ 1, 2, 3 ]
*/
arr?: number[];
/**
* @default [ 'X', 'Y', 'Z' ]
*/
arr2?: string[];
/**
* @default true
*/
bool?: boolean;
/**
* @default true
*/
boolOptional?: boolean;
/**
* @default null
*/
fieldWithAnyNull?: any;
/**
* @default
* [
* 'i', 'have',
* 'more', 'than',
* '5', 'values',
* 'more', 'more',
* 'more'
* ]
*/
fieldWithBigDefault?: string[];
/**
* A field with a
* multiline doc
*
* @default 'My string!'
*/
fieldWithDoc?: string;
/**
* @default 'A multiline\\nstring with\\nsome lines'
*/
fieldWithMultilineString?: string;
/**
* @default 1
*/
num?: number;
/**
* @default 1
*/
numOptional?: number;
/**
* @default { val: 'Test' }
*/
obj?: {
val?: string;
};
/**
* @default 'Test'
*/
str?: string;
/**
* @default 'Test'
*/
strOptional?: string;
/**
* @default 'Test\\\\World$Hello🚀Hey\\nYay'
*/
strWithSpecialChars?: string;
}`);
});
});
8 changes: 6 additions & 2 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
getIsReadonly,
getMetadataFromDetails
} from './joiUtils';
import { getIndentStr, getJsDocString } from './write'; // see __tests__/joiTypes.ts for more information
import { getIndentStr, getJsDocString } from './write';
import util from 'node:util'; // see __tests__/joiTypes.ts for more information

// see __tests__/joiTypes.ts for more information
export const supportedJoiTypes = ['array', 'object', 'alternatives', 'any', 'boolean', 'date', 'number', 'string'];
Expand Down Expand Up @@ -49,6 +50,9 @@ function getCommonDetails(
value = undefined;
}

const defaultJsDoc =
settings.supplyDefaultsInJsDoc && value !== undefined ? util.inspect(value, { depth: null }) : undefined;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const examples: string[] = ((details.examples || []) as any[])
.filter(e => e !== undefined)
Expand All @@ -75,7 +79,7 @@ function getCommonDetails(
}
return {
interfaceOrTypeName,
jsDoc: { description, examples, disable: disableJsDoc },
jsDoc: { description, examples, default: defaultJsDoc, disable: disableJsDoc },
required,
value,
isReadonly
Expand Down
12 changes: 11 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,15 @@ export interface Settings {
/**
* If a field has a default, modify the resulting field to equal
* `field: <default> | type` rather than `field: type`
* @defatult false
* @default false
*/
readonly supplyDefaultsInType: boolean;
/**
* If a field has a default value, add its stringified representation
* to the JsDoc using the @default annotation
* @default false
*/
readonly supplyDefaultsInJsDoc: boolean;
/**
* Filter files you wish to parse
* The class `InputFileFilter` contains some default options
Expand Down Expand Up @@ -317,6 +323,10 @@ export interface JsDoc {
* @example example values
*/
examples?: string[];
/**
* @default default value
*/
default?: string;
/**
* If true, completely disables printing JsDoc
*/
Expand Down
15 changes: 13 additions & 2 deletions src/write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function getJsDocString(settings: Settings, name: string, jsDoc?: JsDoc,
return '';
}

if (!settings.commentEverything && !jsDoc?.description && (jsDoc?.examples?.length ?? 0) === 0) {
if (!settings.commentEverything && !jsDoc?.description && !jsDoc?.default && (jsDoc?.examples?.length ?? 0) === 0) {
return '';
}

Expand All @@ -64,10 +64,21 @@ export function getJsDocString(settings: Settings, name: string, jsDoc?: JsDoc,
}

// Add a JsDoc divider if needed
if ((jsDoc?.examples?.length ?? 0) > 0 && lines.length > 0) {
if (((jsDoc?.examples?.length ?? 0) > 0 || jsDoc?.default) && lines.length > 0) {
lines.push(' *');
}

if (jsDoc?.default) {
const deIndented = getStringIndentation(jsDoc.default).deIndentedString;

if (deIndented.includes('\n')) {
lines.push(` * @default`);
lines.push(...deIndented.split('\n').map(line => ` * ${line}`.trimEnd()));
} else {
lines.push(` * @default ${deIndented}`);
}
}

for (const example of jsDoc?.examples ?? []) {
const deIndented = getStringIndentation(example).deIndentedString;

Expand Down
Loading
Loading