Skip to content

Commit

Permalink
Merge pull request #338 from michelengelen/feature/parse-default-valu…
Browse files Browse the repository at this point in the history
…esinimmediately-destructured-props

parse default values in immediately destructured props
  • Loading branch information
pvasek authored Apr 4, 2021
2 parents b85d97a + a0b4c35 commit 9ef6271
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 4 deletions.
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 @@ -911,6 +911,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 @@ -952,10 +979,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

0 comments on commit 9ef6271

Please sign in to comment.