Skip to content

Commit

Permalink
fix(docz-core): add native styled-components support
Browse files Browse the repository at this point in the history
A react-docgen resolver can be passed via the doczrc config

If one is not provided we used to default to resolver.findAllExportedComponents

findAllExportedComponents failed at parsing components created with sc.

This commit introduces a default resolver that uses both findAllExportedComponents and a custom resolver to detect sc components.
  • Loading branch information
rakannimer committed Dec 12, 2019
1 parent fc59596 commit b072f78
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 4 deletions.
150 changes: 150 additions & 0 deletions core/docz-core/src/utils/docgen/docz-docgen-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Based on https://github.com/reactjs/react-docgen/issues/256#issuecomment-417352843

const { default: resolveHOC } = require('react-docgen/dist/utils/resolveHOC')
const {
default: resolveToModule,
} = require('react-docgen/dist/utils/resolveToModule')
const { utils, resolver: reactDocgenResolver } = require('react-docgen')

type Todo = any

export const createStyledComponentResolvers = () => {
const moduleName = 'styled-components'
const isStyledExpression = (tagPath: Todo, t: Todo) =>
(t.CallExpression.check(tagPath.node) &&
tagPath.get('callee').node.name === 'styled') ||
(t.MemberExpression.check(tagPath.node) &&
tagPath.get('object').node.name === 'styled')

function isStyledComponent(def: Todo, t: Todo) {
if (
!t.TaggedTemplateExpression.check(def.node) ||
!isStyledExpression(def.get('tag'), t)
) {
return false
}
const module = resolveToModule(def.get('tag'))
return !!module && module === moduleName
}

const exportTagged = (path: Todo, t: Todo) => {
const definitions: Todo[] = utils.resolveExportDeclaration(path, t)
const components: Todo[] = []
definitions.filter(Boolean).forEach(def => {
let comp = def
if (isStyledComponent(comp, t)) {
components.push(comp)
} else {
comp = utils.resolveToValue(resolveHOC(comp))

if (isStyledComponent(comp, t)) components.push(comp)
}
})
return components
}

function findExportedStyledComponent(ast: Todo, recast: Todo) {
const components: Todo[] = []
const t = recast.types.namedTypes

const visitor = (path: Todo) => {
components.push(...exportTagged(path, t))
return false
}

recast.visit(ast, {
visitFunctionDeclaration: false,
visitFunctionExpression: false,
visitClassDeclaration: false,
visitClassExpression: false,
visitIfStatement: false,
visitWithStatement: false,
visitSwitchStatement: false,
visitCatchCause: false,
visitWhileStatement: false,
visitDoWhileStatement: false,
visitForStatement: false,
visitForInStatement: false,

visitExportDefaultDeclaration: visitor,
})

return components
}

function findAllExportedStyledComponents(ast: Todo, recast: Todo) {
const components: Todo[] = []
const t = recast.types.namedTypes

const visitor = (path: Todo) => {
components.push(...exportTagged(path, t))
return false
}

recast.visit(ast, {
visitFunctionDeclaration: false,
visitFunctionExpression: false,
visitClassDeclaration: false,
visitClassExpression: false,
visitIfStatement: false,
visitWithStatement: false,
visitSwitchStatement: false,
visitCatchCause: false,
visitWhileStatement: false,
visitDoWhileStatement: false,
visitForStatement: false,
visitForInStatement: false,

visitExportDeclaration: visitor,
visitExportNamedDeclaration: visitor,
visitExportDefaultDeclaration: visitor,
})
return components
}

function findAllStyledComponents(ast: Todo, recast: Todo) {
const components: Todo[] = []
const t = recast.types.namedTypes

recast.visit(ast, {
visitFunctionDeclaration: false,
visitFunctionExpression: false,
visitClassDeclaration: false,
visitClassExpression: false,
visitIfStatement: false,
visitWithStatement: false,
visitSwitchStatement: false,
visitCatchCause: false,
visitWhileStatement: false,
visitDoWhileStatement: false,
visitForStatement: false,
visitForInStatement: false,

visitTaggedTemplateExpression(path: Todo) {
let comp = path
if (isStyledComponent(path, t)) {
components.push(path)
} else {
comp = utils.resolveToValue(resolveHOC(path))

if (isStyledComponent(comp, t)) components.push(comp)
}
return false
},
})
return components
}

return {
findAllStyledComponents,
findAllExportedStyledComponents,
findExportedStyledComponent,
}
}
export const resolver = (ast: Todo, recast: Todo) => [
...reactDocgenResolver.findAllExportedComponentDefinitions(ast, recast),
...createStyledComponentResolvers().findAllExportedStyledComponents(
ast,
recast
),
]
7 changes: 3 additions & 4 deletions core/docz-core/src/utils/docgen/javascript.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import * as fs from 'fs-extra'
import * as path from 'path'
import logger from 'signale'
import externalProptypesHandler from './externalProptypesHandler'
import actualNameHandler from 'react-docgen-actual-name-handler'
import * as reactDocgen from 'react-docgen'

import { Config } from '../../config/argv'
import { getRootDir } from '../../config/paths'
import { unixPath } from '.'
import { resolver as doczResolver } from './docz-docgen-resolver'
import externalProptypesHandler from './externalProptypesHandler'

const throwError = (err: any) => {
logger.fatal(`Error parsing static types`)
logger.error(err)
}

export const jsParser = (files: string[], config: Config) => {
const resolver =
config.docgenConfig.resolver ||
reactDocgen.resolver.findAllExportedComponentDefinitions
const resolver = config.docgenConfig.resolver || doczResolver

const root = getRootDir(config)
const parseFilepathProps = (filepath: string) => {
Expand Down

0 comments on commit b072f78

Please sign in to comment.