Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
16b0b3f9
Commit
16b0b3f9
authored
Jul 05, 2020
by
Robert Knight
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a script to assist with prop-types => JSDoc conversion
parent
04e293bf
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
184 additions
and
0 deletions
+184
-0
jsdoc-from-proptypes.js
scripts/jsdoc-from-proptypes.js
+184
-0
No files found.
scripts/jsdoc-from-proptypes.js
0 → 100644
View file @
16b0b3f9
'use strict'
;
/**
* Utility to generate JSDoc types from prop-types definitions.
*
* Usage:
*
* node scripts/jsdoc-from-proptypes.js <src>
*
* Where `<src>` is a JS file defining a Preact UI component.
*
* The output is a JSDoc `@typedef` definition that can be used as a starting
* point for JSDoc. The output will need to be improved manually:
*
* - Comments should be line-wrapped / adjusted for readability (TODO:
* Do this as part of the script)
*
* - `prop-types` types are often very generic (eg. `propTypes.object`,
* `propTypes.array`, `propTypes.func`). Help human readers and machine
* checking by updating these with more specific types.
*
* - `prop-types` comments often state information that is obvious given a
* a more specific JSDoc type. These should be removed.
*
* - `prop-types` props may not correctly specify whether a prop is optional or
* required. Make sure the JSDoc type specifies this correctly.
*/
const
fs
=
require
(
'fs'
);
const
parser
=
require
(
'@babel/parser'
);
const
{
default
:
traverse
}
=
require
(
'@babel/traverse'
);
const
t
=
require
(
'@babel/types'
);
const
typeFromPropName
=
{
annotation
:
'Annotation'
,
group
:
'Group'
,
thread
:
'Thread'
,
};
function
jsdocTypeFromPropTypesType
(
memberExpression
)
{
if
(
!
t
.
isIdentifier
(
memberExpression
.
property
))
{
return
'Object'
;
}
switch
(
memberExpression
.
property
.
name
)
{
case
'array'
:
return
'Object[]'
;
case
'bool'
:
return
'boolean'
;
case
'func'
:
return
'() => any'
;
case
'number'
:
return
'number'
;
case
'string'
:
return
'string'
;
}
return
'Object'
;
}
function
isPropTypesIdentifier
(
node
)
{
return
t
.
isIdentifier
(
node
)
&&
node
.
name
===
'propTypes'
;
}
function
jsdocComment
(
lines
)
{
return
[
'/**'
,
...
lines
.
map
(
line
=>
' * '
+
line
),
' */'
].
join
(
'
\
n'
);
}
/**
* Extract UI component props information from the right-hand side of a
* `ComponentName.propTypes = { ... }` expression and return an equivalent
* JSDoc `@typedef` comment.
*
* @param {string} componentName
* @param {Object} An `ObjectExpression` AST node
*/
function
jsdocTypedefFromPropTypes
(
componentName
,
objectExpression
)
{
const
props
=
[];
// Extract property names, comments and types from object literal keys.
objectExpression
.
properties
.
forEach
(
objectProperty
=>
{
const
name
=
objectProperty
.
key
.
name
;
let
comment
;
// Extract comment above the prop-type definition.
const
leadingComments
=
objectProperty
.
leadingComments
;
if
(
Array
.
isArray
(
leadingComments
)
&&
leadingComments
.
length
>
0
)
{
comment
=
leadingComments
[
0
].
value
;
comment
=
comment
.
split
(
'
\
n'
)
.
map
(
line
=>
line
.
trim
().
replace
(
/^
\*
/
,
''
).
trim
())
.
join
(
' '
)
.
trim
();
}
let
type
=
'Object'
;
let
isOptional
=
true
;
// Attempt to map the `propTypes.<expression>` property value to a JSDoc type.
if
(
t
.
isMemberExpression
(
objectProperty
.
value
))
{
const
propTypeExpr
=
objectProperty
.
value
;
if
(
isPropTypesIdentifier
(
propTypeExpr
.
object
))
{
// Parse `propTypes.<expr>`
type
=
jsdocTypeFromPropTypesType
(
propTypeExpr
);
}
else
if
(
t
.
isMemberExpression
(
propTypeExpr
.
object
)
&&
isPropTypesIdentifier
(
propTypeExpr
.
object
.
object
)
)
{
// Parse `propTypes.<expr1>.<expr2>`
type
=
jsdocTypeFromPropTypesType
(
propTypeExpr
.
object
);
if
(
t
.
isIdentifier
(
propTypeExpr
.
property
)
&&
propTypeExpr
.
property
.
name
===
'isRequired'
)
{
isOptional
=
false
;
}
}
}
// If a specific type could not be determined from the `propTypes.<expression>`
// expression, attempt to guess based on the prop name.
if
(
type
===
'Object'
&&
name
in
typeFromPropName
)
{
type
=
typeFromPropName
[
name
];
}
props
.
push
({
name
,
type
,
comment
,
isOptional
});
});
// Generate the JSDoc typedef.
const
formatJSDocProp
=
({
name
,
type
,
comment
,
isOptional
})
=>
{
let
expr
=
`@prop {
${
type
}
} `
;
if
(
isOptional
)
{
expr
+=
'['
;
}
expr
+=
name
;
if
(
isOptional
)
{
expr
+=
']'
;
}
if
(
comment
)
{
expr
+=
' - '
+
comment
;
}
return
expr
;
};
// Generate JSDoc typedef from props.
const
commentLines
=
[
`@typedef
${
componentName
}
Props`
,
...
props
.
map
(
formatJSDocProp
),
];
return
jsdocComment
(
commentLines
);
}
function
processFile
(
filePath
)
{
const
code
=
fs
.
readFileSync
(
filePath
).
toString
();
const
ast
=
parser
.
parse
(
code
,
{
plugins
:
[
'jsx'
],
sourceType
:
'module'
,
});
traverse
(
ast
,
{
AssignmentExpression
(
path
)
{
// Look for `<identifier>.propTypes = { ... }` expressions.
const
isPropTypesAssignment
=
t
.
isMemberExpression
(
path
.
node
.
left
)
&&
t
.
isIdentifier
(
path
.
node
.
left
.
object
)
&&
t
.
isIdentifier
(
path
.
node
.
left
.
property
)
&&
path
.
node
.
left
.
property
.
name
===
'propTypes'
;
if
(
isPropTypesAssignment
&&
t
.
isObjectExpression
(
path
.
node
.
right
))
{
const
componentName
=
path
.
node
.
left
.
object
.
name
;
const
jsdoc
=
jsdocTypedefFromPropTypes
(
componentName
,
path
.
node
.
right
);
console
.
log
(
jsdoc
);
}
},
});
}
// Process all the files on the command line after the script name.
process
.
argv
.
slice
(
2
).
map
(
processFile
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment