From ec943d2699376f99fdbe064f5e67dba9306077ab Mon Sep 17 00:00:00 2001 From: TrevorBurnham Date: Sun, 14 Nov 2021 19:13:58 -0500 Subject: [PATCH 1/2] feat: Provide raw expression as part of parser output --- src/__tests__/parser.ts | 49 +++++++++++++++++++++++++++++++++++++++++ src/parser.ts | 3 +++ 2 files changed, 52 insertions(+) diff --git a/src/__tests__/parser.ts b/src/__tests__/parser.ts index f1149790..96b60b74 100644 --- a/src/__tests__/parser.ts +++ b/src/__tests__/parser.ts @@ -977,6 +977,55 @@ describe('parser', () => { }); }); + describe('expression name', () => { + it('should match the stateless component identifier (using named export)', () => { + const [parsed] = parse(fixturePath('StatelessDisplayName')); + assert.equal(parsed.expression.getName(), 'Stateless'); + }); + + it('should match the stateful component identifier (using named export)', () => { + const [parsed] = parse(fixturePath('StatefulDisplayName')); + assert.equal(parsed.expression.getName(), 'Stateful'); + }); + + it('should match the stateless component identifier (using default export)', () => { + const [parsed] = parse(fixturePath('StatelessDisplayNameDefaultExport')); + assert.equal(parsed.expression.getName(), 'default'); + }); + + it("should be taken from stateless component identifier (using default export) even if file name doesn't match", () => { + const [parsed] = parse( + fixturePath('StatelessDisplayNameDefaultExportDifferentFilename') + ); + assert.equal(parsed.expression.getName(), 'default'); + }); + + it('should be taken from stateful component identifier (using default export)', () => { + const [parsed] = parse(fixturePath('StatefulDisplayNameDefaultExport')); + assert.equal(parsed.expression.getName(), 'default'); + }); + + it('should be taken from the wrapped component identifier when default export is an HOC', () => { + const [parsed] = parse(fixturePath('StatelessDisplayNameHOC')); + assert.equal(parsed.expression.getName(), 'Stateless'); + }); + + it('should be taken from the wrapped component identifier when default export is an HOC', () => { + const [parsed] = parse(fixturePath('StatefulDisplayNameHOC')); + assert.equal(parsed.expression.getName(), 'Stateful'); + }); + + it('should be taken from stateless component identifier if file name is "index"', () => { + const [parsed] = parse(fixturePath('StatelessDisplayNameFolder/index')); + assert.equal(parsed.expression.getName(), 'default'); + }); + + it('should be taken from stateful component identifier if file name is "index"', () => { + const [parsed] = parse(fixturePath('StatefulDisplayNameFolder/index')); + assert.equal(parsed.expression.getName(), 'default'); + }); + }); + describe('Parser options', () => { describe('Property filtering', () => { describe('children', () => { diff --git a/src/parser.ts b/src/parser.ts index 8edf153d..cebaa02c 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -14,6 +14,7 @@ export interface StringIndexedObject { } export interface ComponentDoc { + expression: ts.Symbol; displayName: string; filePath: string; description: string; @@ -373,6 +374,7 @@ export class Parser { description, displayName, methods, + expression: rootExp, props }; } else if (description && displayName) { @@ -382,6 +384,7 @@ export class Parser { description, displayName, methods, + expression: rootExp, props: {} }; } From a7846017f82b9c13c71aa5692d082334be22eaf1 Mon Sep 17 00:00:00 2001 From: Trevor Burnham Date: Sun, 28 Nov 2021 13:58:41 -0500 Subject: [PATCH 2/2] Include expression if shouldIncludeExpression is set --- src/__tests__/parser.ts | 63 +++++++++-------------------------------- src/parser.ts | 21 +++++++++----- 2 files changed, 28 insertions(+), 56 deletions(-) diff --git a/src/__tests__/parser.ts b/src/__tests__/parser.ts index 96b60b74..e4ec11c8 100644 --- a/src/__tests__/parser.ts +++ b/src/__tests__/parser.ts @@ -977,55 +977,6 @@ describe('parser', () => { }); }); - describe('expression name', () => { - it('should match the stateless component identifier (using named export)', () => { - const [parsed] = parse(fixturePath('StatelessDisplayName')); - assert.equal(parsed.expression.getName(), 'Stateless'); - }); - - it('should match the stateful component identifier (using named export)', () => { - const [parsed] = parse(fixturePath('StatefulDisplayName')); - assert.equal(parsed.expression.getName(), 'Stateful'); - }); - - it('should match the stateless component identifier (using default export)', () => { - const [parsed] = parse(fixturePath('StatelessDisplayNameDefaultExport')); - assert.equal(parsed.expression.getName(), 'default'); - }); - - it("should be taken from stateless component identifier (using default export) even if file name doesn't match", () => { - const [parsed] = parse( - fixturePath('StatelessDisplayNameDefaultExportDifferentFilename') - ); - assert.equal(parsed.expression.getName(), 'default'); - }); - - it('should be taken from stateful component identifier (using default export)', () => { - const [parsed] = parse(fixturePath('StatefulDisplayNameDefaultExport')); - assert.equal(parsed.expression.getName(), 'default'); - }); - - it('should be taken from the wrapped component identifier when default export is an HOC', () => { - const [parsed] = parse(fixturePath('StatelessDisplayNameHOC')); - assert.equal(parsed.expression.getName(), 'Stateless'); - }); - - it('should be taken from the wrapped component identifier when default export is an HOC', () => { - const [parsed] = parse(fixturePath('StatefulDisplayNameHOC')); - assert.equal(parsed.expression.getName(), 'Stateful'); - }); - - it('should be taken from stateless component identifier if file name is "index"', () => { - const [parsed] = parse(fixturePath('StatelessDisplayNameFolder/index')); - assert.equal(parsed.expression.getName(), 'default'); - }); - - it('should be taken from stateful component identifier if file name is "index"', () => { - const [parsed] = parse(fixturePath('StatefulDisplayNameFolder/index')); - assert.equal(parsed.expression.getName(), 'default'); - }); - }); - describe('Parser options', () => { describe('Property filtering', () => { describe('children', () => { @@ -1543,6 +1494,20 @@ describe('parser', () => { ); }); }); + + describe('shouldIncludeExpression', () => { + it('should be disabled by default', () => { + const [parsed] = parse(fixturePath('StatelessDisplayName')); + assert.equal(parsed.expression, undefined); + }); + + it('should cause the parser to return the component expression when set to true', () => { + const [parsed] = parse(fixturePath('StatelessDisplayName'), { + shouldIncludeExpression: true + }); + assert.equal(parsed.expression!.name, 'Stateless'); + }); + }); }); describe('withCustomConfig', () => { diff --git a/src/parser.ts b/src/parser.ts index cebaa02c..c7a13734 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -14,7 +14,7 @@ export interface StringIndexedObject { } export interface ComponentDoc { - expression: ts.Symbol; + expression?: ts.Symbol; displayName: string; filePath: string; description: string; @@ -89,6 +89,7 @@ export interface ParserOptions { skipChildrenPropWithoutDoc?: boolean; savePropValueAsString?: boolean; shouldIncludePropTagMap?: boolean; + shouldIncludeExpression?: boolean; customComponentTypes?: string[]; } @@ -220,6 +221,7 @@ export class Parser { private readonly shouldExtractValuesFromUnion: boolean; private readonly savePropValueAsString: boolean; private readonly shouldIncludePropTagMap: boolean; + private readonly shouldIncludeExpression: boolean; constructor(program: ts.Program, opts: ParserOptions) { const { @@ -227,7 +229,8 @@ export class Parser { shouldExtractLiteralValuesFromEnum, shouldRemoveUndefinedFromOptional, shouldExtractValuesFromUnion, - shouldIncludePropTagMap + shouldIncludePropTagMap, + shouldIncludeExpression } = opts; this.checker = program.getTypeChecker(); this.propFilter = buildFilter(opts); @@ -240,6 +243,7 @@ export class Parser { this.shouldExtractValuesFromUnion = Boolean(shouldExtractValuesFromUnion); this.savePropValueAsString = Boolean(savePropValueAsString); this.shouldIncludePropTagMap = Boolean(shouldIncludePropTagMap); + this.shouldIncludeExpression = Boolean(shouldIncludeExpression); } private getComponentFromExpression(exp: ts.Symbol) { @@ -351,6 +355,7 @@ export class Parser { computeComponentName(nameSource, source, customComponentTypes); const methods = this.getMethodsInfo(type); + let result: ComponentDoc | null = null; if (propsType) { if (!commentSource.valueDeclaration) { return null; @@ -368,28 +373,30 @@ export class Parser { delete props[propName]; } } - return { + result = { tags, filePath, description, displayName, methods, - expression: rootExp, props }; } else if (description && displayName) { - return { + result = { tags, filePath, description, displayName, methods, - expression: rootExp, props: {} }; } - return null; + if (result !== null && this.shouldIncludeExpression) { + result.expression = rootExp; + } + + return result; } public extractPropsFromTypeIfStatelessComponent(