From 9628c0564d4154260ca24728cee155564f35b5ab Mon Sep 17 00:00:00 2001 From: evless Date: Mon, 23 Sep 2019 12:06:52 +0300 Subject: [PATCH] feat: added flag to return default value not only as string type --- .../StatelessWithDefaultPropsAsString.tsx | 30 +++++++++ src/__tests__/parser.ts | 67 ++++++++++++++++--- src/__tests__/testUtils.ts | 2 +- src/parser.ts | 41 ++++++++---- 4 files changed, 117 insertions(+), 23 deletions(-) create mode 100644 src/__tests__/data/StatelessWithDefaultPropsAsString.tsx diff --git a/src/__tests__/data/StatelessWithDefaultPropsAsString.tsx b/src/__tests__/data/StatelessWithDefaultPropsAsString.tsx new file mode 100644 index 00000000..9a13791d --- /dev/null +++ b/src/__tests__/data/StatelessWithDefaultPropsAsString.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; + +/** StatelessWithDefaultProps props */ +export interface StatelessWithDefaultPropsAsStringProps { + /** sampleTrue description */ + sampleTrue?: boolean; + /** sampleFalse description */ + sampleFalse?: boolean; + /** sampleNumberWithPrefix description */ + sampleNumberWithPrefix?: number; + /** sampleNumber description */ + sampleNumber?: number; + /** sampleNull description */ + sampleNull?: null; + /** sampleUndefined description */ + sampleUndefined?: undefined; +} + +export const StatelessWithDefaultPropsAsString: React.StatelessComponent< + StatelessWithDefaultPropsAsStringProps +> = props =>
test
; + +StatelessWithDefaultPropsAsString.defaultProps = { + sampleFalse: false, + sampleNumberWithPrefix: -1, + sampleNumber: 1, + sampleTrue: true, + sampleNull: null, + sampleUndefined: undefined +}; diff --git a/src/__tests__/parser.ts b/src/__tests__/parser.ts index d031fb3f..6e53cfa6 100644 --- a/src/__tests__/parser.ts +++ b/src/__tests__/parser.ts @@ -267,12 +267,12 @@ describe('parser', () => { type: '"hello" | "goodbye"' }, sampleFalse: { - defaultValue: 'false', + defaultValue: false, required: false, type: 'boolean' }, - sampleNull: { type: 'null', required: false, defaultValue: 'null' }, - sampleNumber: { type: 'number', required: false, defaultValue: '-1' }, + sampleNull: { type: 'null', required: false, defaultValue: null }, + sampleNumber: { type: 'number', required: false, defaultValue: -1 }, sampleObject: { defaultValue: `{ a: '1', b: 2, c: true, d: false, e: undefined, f: null, g: { a: '1' } }`, required: false, @@ -283,7 +283,7 @@ describe('parser', () => { required: false, type: 'string' }, - sampleTrue: { type: 'boolean', required: false, defaultValue: 'true' }, + sampleTrue: { type: 'boolean', required: false, defaultValue: true }, sampleUndefined: { defaultValue: 'undefined', required: false, @@ -401,12 +401,12 @@ describe('parser', () => { type: 'enumSample' }, sampleFalse: { - defaultValue: 'false', + defaultValue: false, required: false, type: 'boolean' }, - sampleNull: { type: 'null', required: false, defaultValue: 'null' }, - sampleNumber: { type: 'number', required: false, defaultValue: '-1' }, + sampleNull: { type: 'null', required: false, defaultValue: null }, + sampleNumber: { type: 'number', required: false, defaultValue: -1 }, sampleObject: { defaultValue: `{ a: '1', b: 2, c: true, d: false, e: undefined, f: null, g: { a: '1' } }`, required: false, @@ -417,9 +417,9 @@ describe('parser', () => { required: false, type: 'string' }, - sampleTrue: { type: 'boolean', required: false, defaultValue: 'true' }, + sampleTrue: { type: 'boolean', required: false, defaultValue: true }, sampleUndefined: { - defaultValue: 'undefined', + defaultValue: undefined, required: false, type: 'any' } @@ -446,7 +446,7 @@ describe('parser', () => { type: 'string' }, shorthandProp: { - defaultValue: '123', + defaultValue: 123, description: 'shorthandProp description', required: false, type: 'number' @@ -803,6 +803,53 @@ describe('parser', () => { ); }); }); + + describe('Returning not string default props ', () => { + it('returns not string defaultProps', () => { + check( + 'StatelessWithDefaultPropsAsString', + { + StatelessWithDefaultPropsAsString: { + sampleFalse: { + defaultValue: 'false', + required: false, + type: 'boolean' + }, + sampleNumberWithPrefix: { + type: 'number', + required: false, + defaultValue: '-1' + }, + sampleNumber: { + type: 'number', + required: false, + defaultValue: '1' + }, + sampleTrue: { + type: 'boolean', + required: false, + defaultValue: 'true' + }, + sampleNull: { + type: 'null', + required: false, + defaultValue: 'null' + }, + sampleUndefined: { + type: 'undefined', + required: false, + defaultValue: 'undefined' + } + } + }, + true, + null, + { + savePropValueAsString: true + } + ); + }); + }); }); describe('withCustomConfig', () => { diff --git a/src/__tests__/testUtils.ts b/src/__tests__/testUtils.ts index a1f03158..49d7a7bc 100644 --- a/src/__tests__/testUtils.ts +++ b/src/__tests__/testUtils.ts @@ -20,7 +20,7 @@ export interface ExpectedProp { type: string; required?: boolean; description?: string; - defaultValue?: string | null; + defaultValue?: string | number | boolean | null | undefined; parent?: { name: string; fileName: string; diff --git a/src/parser.ts b/src/parser.ts index 95c0681a..397057f0 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -79,6 +79,7 @@ export interface ParserOptions { propFilter?: StaticPropFilter | PropFilter; componentNameResolver?: ComponentNameResolver; shouldExtractLiteralValuesFromEnum?: boolean; + savePropValueAsString?: boolean; } export interface StaticPropFilter { @@ -135,7 +136,9 @@ export function withCustomConfig( ); if (error !== undefined) { - const errorText = `Cannot load custom tsconfig.json from provided path: ${tsconfigPath}, with error code: ${error.code}, message: ${error.messageText}`; + const errorText = `Cannot load custom tsconfig.json from provided path: ${tsconfigPath}, with error code: ${ + error.code + }, message: ${error.messageText}`; throw new Error(errorText); } @@ -196,13 +199,16 @@ export class Parser { private checker: ts.TypeChecker; private propFilter: PropFilter; private shouldExtractLiteralValuesFromEnum: boolean; + private savePropValueAsString: boolean; constructor(program: ts.Program, opts: ParserOptions) { + const { savePropValueAsString, shouldExtractLiteralValuesFromEnum } = opts; this.checker = program.getTypeChecker(); this.propFilter = buildFilter(opts); this.shouldExtractLiteralValuesFromEnum = Boolean( - opts.shouldExtractLiteralValuesFromEnum + shouldExtractLiteralValuesFromEnum ); + this.savePropValueAsString = Boolean(savePropValueAsString); } public getComponentInfo( @@ -715,7 +721,7 @@ export class Parser { public getLiteralValueFromPropertyAssignment( property: ts.PropertyAssignment | ts.BindingElement - ): string | null { + ): string | boolean | number | null | undefined { let { initializer } = property; // Shorthand properties, so inflect their actual value @@ -732,7 +738,7 @@ export class Parser { } if (!initializer) { - return null; + return undefined; } // Literal values @@ -740,17 +746,21 @@ export class Parser { case ts.SyntaxKind.PropertyAccessExpression: return initializer.getText(); case ts.SyntaxKind.FalseKeyword: - return 'false'; + return this.savePropValueAsString ? 'false' : false; case ts.SyntaxKind.TrueKeyword: - return 'true'; + return this.savePropValueAsString ? 'true' : true; case ts.SyntaxKind.StringLiteral: return (initializer as ts.StringLiteral).text.trim(); case ts.SyntaxKind.PrefixUnaryExpression: - return initializer.getFullText().trim(); + return this.savePropValueAsString + ? initializer.getFullText().trim() + : Number((initializer as ts.PrefixUnaryExpression).getFullText()); case ts.SyntaxKind.NumericLiteral: - return `${(initializer as ts.NumericLiteral).text}`; + return this.savePropValueAsString + ? `${(initializer as ts.NumericLiteral).text}` + : Number((initializer as ts.NumericLiteral).text); case ts.SyntaxKind.NullKeyword: - return 'null'; + return this.savePropValueAsString ? 'null' : null; case ts.SyntaxKind.Identifier: // can potentially find other identifiers in the source and map those in the future return (initializer as ts.Identifier).text === 'undefined' @@ -766,7 +776,7 @@ export class Parser { public getPropMap( properties: ts.NodeArray - ): StringIndexedObject { + ): StringIndexedObject { const propMap = properties.reduce( (acc, property) => { if (ts.isSpreadAssignment(property) || !property.name) { @@ -778,14 +788,21 @@ export class Parser { ); const propertyName = getPropertyName(property.name); - if (typeof literalValue === 'string' && propertyName !== null) { + if ( + (typeof literalValue === 'string' || + typeof literalValue === 'number' || + typeof literalValue === 'boolean' || + literalValue === null) && + propertyName !== null + ) { acc[propertyName] = literalValue; } return acc; }, - {} as StringIndexedObject + {} as StringIndexedObject ); + return propMap; } }