From ea296fb7edfa03a0c255578fa5e485c4c1486df9 Mon Sep 17 00:00:00 2001 From: Pavel Vasek Date: Thu, 4 May 2017 23:00:00 +0200 Subject: [PATCH 1/2] HOC defined outside of the component source file does not work - fixed --- package.json | 2 +- .../data/ColumnHigherOrderComponent.tsx | 16 ++-- .../data/ColumnHigherOrderComponentHoc.tsx | 5 ++ src/__tests__/data/transformAST.tsx | 16 +++- src/__tests__/data/transformAST_hoc.ts | 3 + src/__tests__/getFileDocumentation.spec.ts | 75 +++++++++++++------ src/__tests__/transformAST.spec.ts | 35 ++++++++- src/getFileDocumentation.ts | 20 ++--- src/transformAST.ts | 4 +- yarn.lock | 14 +--- 10 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 src/__tests__/data/ColumnHigherOrderComponentHoc.tsx create mode 100644 src/__tests__/data/transformAST_hoc.ts diff --git a/package.json b/package.json index c27b1c65..989f90c1 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@types/chai": "^3.4.35", "@types/mocha": "^2.2.39", - "@types/node": "^7.0.5", + "@types/node": "^7.0.16", "@types/react": "^15.0.14", "@types/react-dom": "^0.14.23", "babel-core": "^6.8.0", diff --git a/src/__tests__/data/ColumnHigherOrderComponent.tsx b/src/__tests__/data/ColumnHigherOrderComponent.tsx index 84c9de7a..6e4cfcd5 100644 --- a/src/__tests__/data/ColumnHigherOrderComponent.tsx +++ b/src/__tests__/data/ColumnHigherOrderComponent.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { externalHoc } from "./ColumnHigherOrderComponentHoc"; /** * Column properties. */ @@ -62,13 +63,16 @@ function hoc(C: T): T { return ((props) =>
{C}
) as any as T; } -/** ColumnHighOrderComponent1 specific comment */ -export const ColumnHighOrderComponent1 = hoc(Column); +/** ColumnHigherOrderComponent1 specific comment */ +export const ColumnHigherOrderComponent1 = hoc(Column); -export const ColumnHighOrderComponent2 = hoc(Column); +export const ColumnHigherOrderComponent2 = hoc(Column); -/** RowHighOrderComponent1 specific comment */ -export const RowHighOrderComponent1 = hoc(Row); +/** RowHigherOrderComponent1 specific comment */ +export const RowHigherOrderComponent1 = hoc(Row); -export const RowHighOrderComponent2 = hoc(Row); +export const RowHigherOrderComponent2 = hoc(Row); + +export const ColumnExternalHigherOrderComponent = externalHoc(Column); +export const RowExternalHigherOrderComponent = externalHoc(Row); diff --git a/src/__tests__/data/ColumnHigherOrderComponentHoc.tsx b/src/__tests__/data/ColumnHigherOrderComponentHoc.tsx new file mode 100644 index 00000000..fb4bd1dc --- /dev/null +++ b/src/__tests__/data/ColumnHigherOrderComponentHoc.tsx @@ -0,0 +1,5 @@ +import * as React from 'react'; + +export function externalHoc(C: T): T { + return ((props) =>
{C}
) as any as T; +} diff --git a/src/__tests__/data/transformAST.tsx b/src/__tests__/data/transformAST.tsx index 1a0cf4d1..172ddd5d 100644 --- a/src/__tests__/data/transformAST.tsx +++ b/src/__tests__/data/transformAST.tsx @@ -1,3 +1,5 @@ +import { externalHoc } from './transformAST_hoc'; + const unexportedVar = 10; export const exportedVar = 10; @@ -51,5 +53,15 @@ export function hoc(component: T): T { return component; } -/** exportedHoc comment */ -export const exportedHoc = hoc(ExportedClass); \ No newline at end of file +/** exportedHoc1 comment */ +export const exportedHoc1 = hoc(ExportedClass); + +/** exportedHoc2 comment */ +export const exportedHoc2 = hoc(exportedFunction); + + +/** exportedExternalHoc1 comment */ +export const exportedExternalHoc1 = externalHoc(ExportedClass); + +/** exportedExternalHoc2 comment */ +export const exportedExternalHoc2 = externalHoc(exportedFunction); diff --git a/src/__tests__/data/transformAST_hoc.ts b/src/__tests__/data/transformAST_hoc.ts new file mode 100644 index 00000000..229a28f7 --- /dev/null +++ b/src/__tests__/data/transformAST_hoc.ts @@ -0,0 +1,3 @@ +export function externalHoc(component: T): T { + return component; +} \ No newline at end of file diff --git a/src/__tests__/getFileDocumentation.spec.ts b/src/__tests__/getFileDocumentation.spec.ts index 6afbd13f..31ea868a 100644 --- a/src/__tests__/getFileDocumentation.spec.ts +++ b/src/__tests__/getFileDocumentation.spec.ts @@ -173,39 +173,72 @@ describe('getFileDocumentation', () => { const fileName = path.join(__dirname, '../../src/__tests__/data/ColumnHigherOrderComponent.tsx'); // it's running in ./temp const result = getFileDocumentation(fileName); assert.ok(result.components); - assert.equal(4, result.components.length); + assert.equal(6, result.components.length); const r1 = result.components[0]; - assert.equal('ColumnHighOrderComponent1', r1.name); - assert.equal('ColumnHighOrderComponent1 specific comment', r1.comment); + assert.equal('ColumnHigherOrderComponent1', r1.name); + assert.equal('ColumnHigherOrderComponent1 specific comment', r1.comment); assert.equal('Column', r1.extends); assert.isNotNull(r1.propInterface); - const i = r1.propInterface; - assert.equal('IColumnProps', i.name); - assert.equal('Column properties.', i.comment); - assert.equal(4, i.members.length); - assert.equal('prop1', i.members[0].name); - assert.equal('prop1 description', i.members[0].comment); - assert.equal(false, i.members[0].isRequired); + const p1 = r1.propInterface; + assert.equal('IColumnProps', p1.name); + assert.equal('Column properties.', p1.comment); + assert.equal(4, p1.members.length); + assert.equal('prop1', p1.members[0].name); + assert.equal('prop1 description', p1.members[0].comment); + assert.equal(false, p1.members[0].isRequired); - assert.equal('prop2', i.members[1].name); - assert.equal('prop2 description', i.members[1].comment); - assert.equal(true, i.members[1].isRequired); + assert.equal('prop2', p1.members[1].name); + assert.equal('prop2 description', p1.members[1].comment); + assert.equal(true, p1.members[1].isRequired); - assert.equal('prop3', i.members[2].name); - assert.equal('prop3 description', i.members[2].comment); - assert.equal(true, i.members[2].isRequired); + assert.equal('prop3', p1.members[2].name); + assert.equal('prop3 description', p1.members[2].comment); + assert.equal(true, p1.members[2].isRequired); - assert.equal('prop4', i.members[3].name); - assert.equal('prop4 description', i.members[3].comment); - assert.equal(true, i.members[3].isRequired); + assert.equal('prop4', p1.members[3].name); + assert.equal('prop4 description', p1.members[3].comment); + assert.equal(true, p1.members[3].isRequired); const r2 = result.components[1]; - assert.equal('ColumnHighOrderComponent2', r2.name); + assert.equal('ColumnHigherOrderComponent2', r2.name); assert.equal('Form column.', r2.comment); assert.equal('Column', r2.extends); assert.isNotNull(r2.propInterface); - + const p2 = r2.propInterface; + assert.equal('IColumnProps', p2.name); + + const r3 = result.components[2]; + assert.equal('ColumnExternalHigherOrderComponent', r3.name); + assert.equal('Form column.', r3.comment); + assert.equal('Column', r3.extends); + assert.isNotNull(r3.propInterface); + const p3 = r3.propInterface; + assert.equal('IColumnProps', p3.name); + + const r4 = result.components[3]; + assert.equal('RowHigherOrderComponent1', r4.name); + assert.equal('RowHigherOrderComponent1 specific comment', r4.comment); + assert.equal('Row', r4.extends); + assert.isNotNull(r4.propInterface); + const p4 = r4.propInterface; + assert.equal('IRowProps', p4.name); + + const r5 = result.components[4]; + assert.equal('RowHigherOrderComponent2', r5.name); + assert.equal('Form row.', r5.comment); + assert.equal('Row', r5.extends); + assert.isNotNull(r5.propInterface); + const p5 = r5.propInterface; + assert.equal('IRowProps', p5.name); + + const r6 = result.components[5]; + assert.equal('RowExternalHigherOrderComponent', r6.name); + assert.equal('Form row.', r6.comment); + assert.equal('Row', r6.extends); + assert.isNotNull(r6.propInterface); + const p6 = r6.propInterface; + assert.equal('IRowProps', p6.name); }); }); \ No newline at end of file diff --git a/src/__tests__/transformAST.spec.ts b/src/__tests__/transformAST.spec.ts index a2591e05..a016272f 100644 --- a/src/__tests__/transformAST.spec.ts +++ b/src/__tests__/transformAST.spec.ts @@ -18,7 +18,7 @@ describe('transformAST', () => { it('should provide data about variables', () => { const result = target.variables; - assert.equal(result.length, 5); + assert.equal(result.length, 8); const r1 = result[0]; assert.equal(r1.name, 'unexportedVar'); @@ -48,14 +48,41 @@ describe('transformAST', () => { assert.equal(r4.arrowFunctionType, 'string'); assert.deepEqual(r4.arrowFunctionParams, ['number', 'string']); - // hoc + // hoc class const r5 = result[4]; - assert.equal(r5.name, 'exportedHoc'); + assert.equal(r5.name, 'exportedHoc1'); assert.equal(r5.exported, true); assert.equal(r5.type, 'ExportedClass'); assert.equal(r5.kind, 'callExpression'); - assert.equal(r5.comment, 'exportedHoc comment'); + assert.equal(r5.comment, 'exportedHoc1 comment'); assert.deepEqual(r5.callExpressionArguments, ['ExportedClass']); + + // hoc function + const r6 = result[5]; + assert.equal(r6.name, 'exportedHoc2'); + assert.equal(r6.exported, true); + assert.equal(r6.type, 'exportedFunction'); + assert.equal(r6.kind, 'callExpression'); + assert.equal(r6.comment, 'exportedHoc2 comment'); + assert.deepEqual(r6.callExpressionArguments, ['exportedFunction']); + + // external hoc class + const r7 = result[6]; + assert.equal(r7.name, 'exportedExternalHoc1'); + assert.equal(r7.exported, true); + assert.equal(r7.type, 'ExportedClass'); + assert.equal(r7.kind, 'callExpression'); + assert.equal(r7.comment, 'exportedExternalHoc1 comment'); + assert.deepEqual(r7.callExpressionArguments, ['ExportedClass']); + + // external hoc function + const r8 = result[7]; + assert.equal(r8.name, 'exportedExternalHoc2'); + assert.equal(r8.exported, true); + assert.equal(r8.type, 'exportedFunction'); + assert.equal(r8.kind, 'callExpression'); + assert.equal(r8.comment, 'exportedExternalHoc2 comment'); + assert.deepEqual(r8.callExpressionArguments, ['exportedFunction']); }); it('should provide data about interfaces', () => { diff --git a/src/getFileDocumentation.ts b/src/getFileDocumentation.ts index 963c16ab..1cbac0f6 100644 --- a/src/getFileDocumentation.ts +++ b/src/getFileDocumentation.ts @@ -37,26 +37,28 @@ function isVarComponent( function isHocClassComponent(entry: VariableEntry, classes: ClassEntry[]): boolean { return entry.exported && entry.kind === 'callExpression' - && entry.type !== null + // quick fix for external hoc - && entry.type !== null + && entry.callExpressionArguments.length === 1 && classes .filter(i => isClassComponent(i, false)) - .some(i => i.name === entry.type); + .some(i => i.name === entry.callExpressionArguments[0]); } -function isVarClassComponent( +function isHocVarComponent( entry: VariableEntry, variables: VariableEntry[], interfaces: InterfaceEntry[]): boolean { return entry.exported && entry.kind === 'callExpression' - && entry.type === '__function' + // quick fix for external hoc - && entry.type === '__function' && entry.callExpressionArguments.length === 1 && variables .filter(i => isVarComponent(i, interfaces, false)) .some(i => i.name === entry.callExpressionArguments[0]); } + function getInterfaceDoc(entry: InterfaceEntry): InterfaceDoc { return { name: entry.name, @@ -90,7 +92,7 @@ function getPropInterface(interfaces: InterfaceEntry[], propInterfaceName: strin } /** Generate documention for all classes in a set of .ts files */ export function getFileDocumentation(fileName: string, options: ts.CompilerOptions = defaultOptions): FileDoc { - const components: ComponentDoc[] = []; + const components: ComponentDoc[] = []; let program = ts.createProgram([fileName], options); let checker = program.getTypeChecker(); const model = transformAST(program.getSourceFile(fileName), checker); @@ -119,24 +121,24 @@ export function getFileDocumentation(fileName: string, options: ts.CompilerOptio .filter(i => isHocClassComponent(i, classes)) .map(i => ({ variable: i, - origin: classes.filter(c => c.name === i.type)[0] + origin: classes.filter(c => c.name === i.callExpressionArguments[0])[0] })) .map(i => ({ name: i.variable.name, - extends: i.variable.type, + extends: i.variable.callExpressionArguments[0], comment: i.variable.comment || i.origin.comment, propInterface: getClassPropInterface(interfaces, i.origin), })); const hocVarComponents = variables - .filter(i => isVarClassComponent(i, variables, interfaces)) + .filter(i => isHocVarComponent(i, variables, interfaces)) .map(i => ({ variable: i, origin: variables.filter(c => c.name === i.callExpressionArguments[0])[0] })) .map(i => ({ name: i.variable.name, - extends: i.variable.type, + extends: i.variable.callExpressionArguments[0], comment: i.variable.comment || i.origin.comment, propInterface: getVarPropInterface(interfaces, i.origin), })); diff --git a/src/transformAST.ts b/src/transformAST.ts index a8137bab..558b90f4 100644 --- a/src/transformAST.ts +++ b/src/transformAST.ts @@ -76,7 +76,7 @@ function findAllNodes(rootNode: ts.Node, result: ts.Node[]) { * Transform source file AST (abstract syntax tree) to our * model (classes, interfaces, variables, methods). */ -export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker) { +export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker) { const nodes = []; findAllNodes(sourceFile, nodes); @@ -116,7 +116,7 @@ export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker) callExpressionArguments = callExpresson.arguments.map(i => i.getText()); } } - + return { name: identifier.text, exported: isNodeExported(i), diff --git a/yarn.lock b/yarn.lock index 1507b33b..55d4bb9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,7 +10,7 @@ version "2.2.41" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.41.tgz#e27cf0817153eb9f2713b2d3f6c68f1e1c3ca608" -"@types/node@^7.0.5": +"@types/node@^7.0.16": version "7.0.16" resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.16.tgz#e3440e3ce4d4931616ac418cc4dc16cd94b80092" @@ -1592,14 +1592,10 @@ escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" -escape-string-regexp@1.0.2: +escape-string-regexp@1.0.2, escape-string-regexp@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" -escape-string-regexp@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - esprima-fb@~15001.1001.0-dev-harmony-fb: version "15001.1001.0-dev-harmony-fb" resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" @@ -3298,11 +3294,7 @@ randomatic@^1.1.3: is-number "^2.0.2" kind-of "^3.0.2" -range-parser@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -range-parser@~1.0.3: +range-parser@^1.0.3, range-parser@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.0.3.tgz#6872823535c692e2c2a0103826afd82c2e0ff175" From 0aa98f3034cc24c7d768748c31318f975c7a1b29 Mon Sep 17 00:00:00 2001 From: Pavel Vasek Date: Thu, 4 May 2017 23:03:28 +0200 Subject: [PATCH 2/2] version increased --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 989f90c1..858a69fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-docgen-typescript", - "version": "0.0.9", + "version": "0.0.10", "description": "", "main": "lib/index.js", "scripts": {