Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const PROPERTY1_DEFAULT = 'hello';
const PROPERTY2_DEFAULT = 'goodbye';
const PROPERTY3_DEFAULT = 10;
const PROPERTY4_DEFAULT = 'this is a string';
const PROPERTY5_DEFAULT = true;

export {
PROPERTY1_DEFAULT,
PROPERTY2_DEFAULT,
PROPERTY3_DEFAULT,
PROPERTY4_DEFAULT,
PROPERTY5_DEFAULT
};
33 changes: 33 additions & 0 deletions src/__tests__/data/FunctionalComponentWithDesctructuredProps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';

const PROPERTY1_DEFAULT = 'hello';
const PROPERTY2_DEFAULT = 'goodbye';
const PROPERTY3_DEFAULT = 10;
const PROPERTY4_DEFAULT = 'this is a string';
const PROPERTY5_DEFAULT = true;

type Property1Type = 'hello' | 'world';

type Props = {
/** prop1 description */
prop1?: Property1Type;
/** prop2 description */
prop2?: 'goodbye' | 'farewell';
/** prop3 description */
prop3?: number;
/** prop4 description */
prop4?: string;
/** prop5 description */
prop5?: boolean;
};

/** FunctionalComponentWithDesctructuredProps description */
const FunctionalComponentWithDesctructuredProps: React.FC<Props> = ({
prop1 = PROPERTY1_DEFAULT,
prop2 = PROPERTY2_DEFAULT,
prop3 = PROPERTY3_DEFAULT,
prop4 = PROPERTY4_DEFAULT,
prop5 = PROPERTY5_DEFAULT
}) => <div />;

export default FunctionalComponentWithDesctructuredProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';

import {
PROPERTY1_DEFAULT,
PROPERTY2_DEFAULT,
PROPERTY3_DEFAULT,
PROPERTY4_DEFAULT,
PROPERTY5_DEFAULT
} from './FunctionalComponentWithDesctructuredProps.constants';

type Property1Type = 'hello' | 'world';

type Props = {
/** prop1 description */
prop1?: Property1Type;
/** prop2 description */
prop2?: 'goodbye' | 'farewell';
/** prop3 description */
prop3?: number;
/** prop4 description */
prop4?: string;
/** prop5 description */
prop5?: boolean;
};

/** FunctionalComponentWithDesctructuredPropsAndImportedConstants description */
const FunctionalComponentWithDesctructuredPropsAndImportedConstants: React.FC<Props> = ({
prop1 = PROPERTY1_DEFAULT,
prop2 = PROPERTY2_DEFAULT,
prop3 = PROPERTY3_DEFAULT,
prop4 = PROPERTY4_DEFAULT,
prop5 = PROPERTY5_DEFAULT
}) => <div />;

export default FunctionalComponentWithDesctructuredPropsAndImportedConstants;
64 changes: 64 additions & 0 deletions src/__tests__/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,70 @@ describe('parser', () => {
});
});

it('should parse functional component defined as const with default value assignments in immediately destructured props', () => {
check('FunctionalComponentWithDesctructuredProps', {
FunctionalComponentWithDesctructuredProps: {
prop1: {
type: 'Property1Type',
required: false,
defaultValue: 'hello'
},
prop2: {
type: '"goodbye" | "farewell"',
required: false,
defaultValue: 'goodbye'
},
prop3: {
type: 'number',
required: false,
defaultValue: 10
},
prop4: {
type: 'string',
required: false,
defaultValue: 'this is a string'
},
prop5: {
type: 'boolean',
required: false,
defaultValue: true
}
}
});
});

it('should parse functional component defined as const with default value (imported from a separate file) assignments in immediately destructured props', () => {
check('FunctionalComponentWithDesctructuredPropsAndImportedConstants', {
FunctionalComponentWithDesctructuredPropsAndImportedConstants: {
prop1: {
type: 'Property1Type',
required: false,
defaultValue: 'hello'
},
prop2: {
type: '"goodbye" | "farewell"',
required: false,
defaultValue: 'goodbye'
},
prop3: {
type: 'number',
required: false,
defaultValue: 10
},
prop4: {
type: 'string',
required: false,
defaultValue: 'this is a string'
},
prop5: {
type: 'boolean',
required: false,
defaultValue: true
}
}
});
});

it('should parse functional component component defined as const', () => {
check('FunctionDeclarationVisibleName', {
'Awesome Jumbotron': {
Expand Down
52 changes: 48 additions & 4 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,33 @@ export class Parser {
}, {});
}

public getLiteralValueFromImportSpecifier(
property: ts.ImportSpecifier
): string | boolean | number | null | undefined {
if (ts.isImportSpecifier(property)) {
const symbol = this.checker.getSymbolAtLocation(property.name);

if (!symbol) {
return null;
}

const aliasedSymbol = this.checker.getAliasedSymbol(symbol);
if (
aliasedSymbol &&
aliasedSymbol.declarations &&
aliasedSymbol.declarations.length
) {
return this.getLiteralValueFromPropertyAssignment(
aliasedSymbol.declarations[0] as ts.BindingElement
);
}

return null;
}

return null;
}

public getLiteralValueFromPropertyAssignment(
property: ts.PropertyAssignment | ts.BindingElement
): string | boolean | number | null | undefined {
Expand Down Expand Up @@ -943,10 +970,27 @@ export class Parser {
case ts.SyntaxKind.NullKeyword:
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'
? 'undefined'
: null;
if ((initializer as ts.Identifier).text === 'undefined') {
return 'undefined';
}

const symbol = this.checker.getSymbolAtLocation(
initializer as ts.Identifier
);

if (symbol && symbol.declarations && symbol.declarations.length) {
if (ts.isImportSpecifier(symbol.declarations[0])) {
return this.getLiteralValueFromImportSpecifier(
symbol.declarations[0] as ts.ImportSpecifier
);
}

return this.getLiteralValueFromPropertyAssignment(
symbol.declarations[0] as ts.BindingElement
);
}

return null;
case ts.SyntaxKind.PropertyAccessExpression: {
const symbol = this.checker.getSymbolAtLocation(
initializer as ts.PropertyAccessExpression
Expand Down