Skip to content

Commit

Permalink
feat(gatsby-transformer-react-docgen): support multiple doclets of sa…
Browse files Browse the repository at this point in the history
…me tag (#10342)

BREAKING CHANGE: Shape of `doclet` field is changed. It's no longer object but array of objects with `tag` and `value` fields:

gatsby-transformer-react-docgen@^2.0.0:

```json
{
  "doclets": {
    "type": "{Foo}",
    "property": "{string} property1"
  }
}
```

gatsby-transformer-react-docgen@^3.0.0:

```json
{
  "doclets": [
    { "tag": "type", "value": "{Foo}" },
    { "tag": "property", "value": "{string} property1" },
    { "tag": "property", "value": "{number} property2" }
  ]
}
```
  • Loading branch information
jquense authored and pieh committed Dec 31, 2018
1 parent ab2c778 commit c5c84a0
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 52 deletions.
67 changes: 32 additions & 35 deletions packages/gatsby-transformer-react-docgen/src/Doclets.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const DOCLET_PATTERN = /^@(\w+)(?:$|\s((?:[^](?!^@\w))*))/gim

const { hasOwnProperty: has } = Object.prototype
let cleanDocletValue = str => {
str = str.trim()
if (str.endsWith(`}`) && str.startsWith(`{`)) str = str.slice(1, -1)
Expand All @@ -26,15 +25,15 @@ export const cleanDoclets = desc => {
* Updated to strip \r from the end of doclets
*/
const getDoclets = str => {
let doclets = Object.create(null)
let doclets = []
let match = DOCLET_PATTERN.exec(str)
let val

for (; match; match = DOCLET_PATTERN.exec(str)) {
val = match[2] ? match[2].replace(/\r$/, ``) : true
doclets[match[1]] = val
const key = match[1]
doclets.push({ tag: key, value: val })
}

return doclets
}

Expand All @@ -57,41 +56,39 @@ export const parseDoclets = obj => {
* @param {String} propName
*/
export const applyPropDoclets = prop => {
let doclets = prop.doclets
let value
prop.doclets.forEach(({ tag, value }) => {
// the @type doclet to provide a prop type
// Also allows enums (oneOf) if string literals are provided
// ex: @type {("optionA"|"optionB")}
if (tag === `type`) {
value = cleanDocletValue(value)
prop.type.name = value

// the @type doclet to provide a prop type
// Also allows enums (oneOf) if string literals are provided
// ex: @type {("optionA"|"optionB")}
if (doclets.type) {
value = cleanDocletValue(doclets.type)
prop.type.name = value
if (value[0] === `(`) {
value = value.substring(1, value.length - 1).split(`|`)
const name = value.every(isLiteral) ? `enum` : `union`
prop.type.name = name
prop.type.value = value.map(
value =>
name === `enum` ? { value, computed: false } : { name: value }
)
}
return
}

if (value[0] === `(`) {
value = value.substring(1, value.length - 1).split(`|`)
const name = value.every(isLiteral) ? `enum` : `union`
prop.type.name = name
prop.type.value = value.map(
value =>
name === `enum` ? { value, computed: false } : { name: value }
)
// Use @required to mark a prop as required
// useful for custom propTypes where there isn't a `.isRequired` addon
if (tag === `required` && value) {
prop.required = true
return
}
}

// Use @required to mark a prop as required
// useful for custom propTypes where there isn't a `.isRequired` addon
if (doclets.required) {
prop.required = true
}
const dft = has.call(doclets, `default`)
? doclets.default
: doclets.defaultValue
// Use @defaultValue to provide a prop's default value
if (dft != null) {
prop.defaultValue = {
value: dft,
computed: false,
// Use @defaultValue to provide a prop's default value
if ((tag === `default` || tag === `defaultValue`) && value != null) {
prop.defaultValue = { value, computed: false }
return
}
}
})

return prop
}
34 changes: 22 additions & 12 deletions packages/gatsby-transformer-react-docgen/src/__tests__/Doclets.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import { applyPropDoclets } from "../Doclets"

describe(`transformer-react-doc-gen: Doclets`, () => {
it(`should apply @required`, () => {
const doclets = {
required: true,
}
const doclets = [{ tag: `required`, value: true }]
expect(applyPropDoclets({ doclets })).toEqual({
doclets,
required: true,
})
})

it(`should apply @required`, () => {
const doclets = {
defaultValue: `() => {}`,
}
const doclets = [
{
tag: `defaultValue`,
value: `() => {}`,
},
]

expect(applyPropDoclets({ doclets })).toEqual({
doclets,
defaultValue: {
Expand All @@ -25,9 +27,13 @@ describe(`transformer-react-doc-gen: Doclets`, () => {
})

it(`should handle inline enum types`, () => {
const doclets = {
type: `{(true|'foo'|40|"bar")}`,
}
const doclets = [
{
tag: `type`,
value: `{(true|'foo'|40|"bar")}`,
},
]

expect(applyPropDoclets({ doclets, type: {} })).toEqual({
doclets,
type: {
Expand All @@ -43,9 +49,13 @@ describe(`transformer-react-doc-gen: Doclets`, () => {
})

it(`should create a union type for none-literals`, () => {
const doclets = {
type: `{(string|func|bool)}`,
}
const doclets = [
{
tag: `type`,
value: `{(string|func|bool)}`,
},
]

expect(applyPropDoclets({ doclets, type: {} })).toEqual({
doclets,
type: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Baz.Foo = () => <div />
* Description!
*
* @alias {MyComponent}
* @property {Foo} foo
* @property {Bar} bar
*/
class Bar extends React.Component {
static propTypes = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from "fs"
import path from "path"
import { groupBy } from "lodash"
import onCreateNode from "../on-node-create"
import path from "path"

const readFile = file =>
new Promise((y, n) => {
Expand Down Expand Up @@ -80,6 +80,16 @@ describe(`transformer-react-doc-gen: onCreateNode`, () => {
])
})

it(`should handle duplicate doclet values`, async () => {
await run(node)

let Bar = groupBy(createdNodes, `internal.type`).ComponentMetadata.find(
d => d.displayName === `Bar`
)

expect(Bar.doclets.filter(d => d.tag === `property`)).toHaveLength(2)
})

it(`should infer a name`, async () => {
node.__fixture = `unnamed.js`
node.absolutePath = path.join(__dirname, `UnnamedExport`)
Expand All @@ -102,10 +112,10 @@ describe(`transformer-react-doc-gen: onCreateNode`, () => {
expect(types.ComponentProp[0].description).toEqual(
`An object hash of field (fix this @mention?) errors for the form.`
)
expect(types.ComponentProp[0].doclets).toEqual({
type: `{Foo}`,
default: `blue`,
})
expect(types.ComponentProp[0].doclets).toEqual([
{ tag: `type`, value: `{Foo}` },
{ tag: `default`, value: `blue` },
])
})

it(`should extract create description nodes with markdown types`, async () => {
Expand Down

0 comments on commit c5c84a0

Please sign in to comment.