Skip to content

Commit

Permalink
update doc-handler
Browse files Browse the repository at this point in the history
  • Loading branch information
jquense committed Jun 5, 2018
1 parent 95276b5 commit 56e0b67
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ function Foo() {
return <div />
}

Baz.Foo = () => <div />

/**
* Description!
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,22 @@ describe(`transformer-react-doc-gen: onCreateNode`, () => {
await run(node)

let types = groupBy(createdNodes, n => n.internal.type)
expect(types.ComponentMetadata).toHaveLength(5)
expect(types.ComponentMetadata).toHaveLength(6)
})

it(`should give all components a name`, async () => {
await run(node)

let types = groupBy(createdNodes, `internal.type`)
expect(types.ComponentMetadata.every(c => c.displayName)).toBe(true)

expect(types.ComponentMetadata.map(c => c.displayName)).toEqual([
`Baz`,
`Buz`,
`Foo`,
`Baz.Foo`,
`Bar`,
`Qux`,
])
})

it(`should infer a name`, async () => {
Expand Down Expand Up @@ -125,7 +133,7 @@ describe(`transformer-react-doc-gen: onCreateNode`, () => {
})
it(`should add flow type info`, async () => {
await run(node)
expect(createdNodes[1].flowType).toEqual({
expect(createdNodes[0].flowType).toEqual({
name: `number`,
})
})
Expand Down
122 changes: 122 additions & 0 deletions packages/gatsby-transformer-react-docgen/src/displayNameHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* @flow */
/** Forked from https://github.com/nerdlabs/react-docgen-displayname-handler/blob/master/source/index.js */

import path from "path"
import recast from "recast"
import { utils } from "react-docgen"
const { getMemberValuePath, getNameOrValue } = utils

const {
types: { namedTypes: types },
} = recast

const DEFAULT_NAME = `UnknownComponent`

type Documentation = Map<string, string>
type NodePath = any

function getNameFromPath(path: NodePath): ?string {
var node = path.node
switch (node.type) {
case types.Identifier.name:
case types.Literal.name:
return getNameOrValue(path)
case types.MemberExpression.name:
return utils
.getMembers(path)
.reduce(
(name, { path, computed }) =>
computed && getNameFromPath(path)
? name
: `${name}.${getNameFromPath(path) || ``}`,
getNameFromPath(path.get(`object`))
)
default:
return null
}
}

function getStaticDisplayName(path: NodePath): ?string {
let displayName = null
const staticMember = getMemberValuePath(path, `displayName`)
if (staticMember && types.Literal.check(staticMember.node)) {
displayName = getNameOrValue(staticMember)
}

return displayName || null
}

function getNodeIdentifier(path: NodePath): ?string {
let displayName = null
if (
types.FunctionExpression.check(path.node) ||
types.FunctionDeclaration.check(path.node) ||
types.ClassExpression.check(path.node) ||
types.ClassDeclaration.check(path.node)
) {
displayName = getNameFromPath(path.get(`id`))
}

return displayName || null
}

function getVariableIdentifier(path: NodePath): ?string {
let displayName = null
let searchPath = path

while (searchPath !== null) {
if (types.VariableDeclarator.check(searchPath.node)) {
displayName = getNameFromPath(searchPath.get(`id`))
break
}
if (
types.AssignmentExpression.check(searchPath.node) &&
!utils.isExportsOrModuleAssignment(searchPath)
) {
displayName = getNameFromPath(searchPath.get(`left`))
break
}
searchPath = searchPath.parentPath
}

return displayName || null
}

function getNameFromFilePath(filePath: string = ``): ?string {
let displayName = null

const filename = path.basename(filePath, path.extname(filePath))
if (filename === `index`) {
const parts = path.dirname(filePath).split(path.sep)
displayName = parts[parts.length - 1]
} else {
displayName = filename
}

return displayName
.charAt(0)
.toUpperCase()
.concat(displayName.slice(1))
.replace(/-([a-z])/, (_, match) => match.toUpperCase())
}

export default function createDisplayNameHandler(
filePath: string
): (docs: Documentation, path: NodePath) => void {
return function displayNameHandler(
documentation: Documentation,
path: NodePath
): void {
let displayName: ?string = [
getStaticDisplayName,
getNodeIdentifier,
getVariableIdentifier,
].reduce((name, getDisplayName) => name || getDisplayName(path), ``)

if (!displayName) {
displayName = getNameFromFilePath(filePath)
}

documentation.set(`displayName`, displayName || DEFAULT_NAME)
}
}
49 changes: 13 additions & 36 deletions packages/gatsby-transformer-react-docgen/src/parse.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,30 @@
import path from "path"
import { codeFrameColumns } from "@babel/code-frame"
import * as types from "babel-types"
import { parse, defaultHandlers, resolver } from "react-docgen"
import { parse, resolver, handlers } from "react-docgen"
import { ERROR_MISSING_DEFINITION } from "react-docgen/dist/parse"

import { cleanDoclets, parseDoclets, applyPropDoclets } from "./Doclets"
import displayNameHandler from "./displayNameHandler"

function getAssignedIdenifier(path) {
let property = path.parentPath
while (property) {
if (types.isVariableDeclarator(property.node)) return property.node.id.name
property = property.parentPath
}
return null
}
const defaultHandlers = [
handlers.propTypeHandler,
handlers.propTypeCompositionHandler,
handlers.propDocBlockHandler,
handlers.flowTypeHandler,
handlers.defaultPropsHandler,
handlers.componentDocblockHandler,
handlers.componentMethodsHandler,
handlers.componentMethodsJsDocHandler,
]

let fileCount = 0
function nameHandler(filePath) {
let defaultName = path.basename(filePath, path.extname(filePath))
let componentCount = 0

return (docs, nodePath) => {
let displayName = docs.get(`displayName`)
if (displayName) return

if (
types.isArrowFunctionExpression(nodePath.node) ||
types.isFunctionExpression(nodePath.node) ||
types.isObjectExpression(nodePath.node)
) {
displayName = getAssignedIdenifier(nodePath)
} else if (
types.isFunctionDeclaration(nodePath.node) ||
types.isClassDeclaration(nodePath.node)
) {
displayName = nodePath.node.id.name
}

docs.set(`displayName`, displayName || `${defaultName}${++componentCount}`)
}
}

/**
* Wrap handlers to pass in additional arguments such as the File node
*/
function makeHandlers(node, handlers) {
handlers = (handlers || []).map(h => (...args) => h(...args, node))
return [
nameHandler(node.absolutePath || `/UnknownComponent${++fileCount}`),
displayNameHandler(node.absolutePath || `/UnknownComponent${++fileCount}`),
...handlers,
]
}
Expand Down

0 comments on commit 56e0b67

Please sign in to comment.