forked from react-bootstrap/react-bootstrap
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate-metadata.js
More file actions
154 lines (124 loc) · 4.3 KB
/
generate-metadata.js
File metadata and controls
154 lines (124 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import metadata from 'react-component-metadata';
import glob from 'glob';
import fsp from 'fs-promise';
import promisify from '../tools/promisify';
import marked from 'marked';
import defaultDescriptions from './src/defaultPropDescriptions';
marked.setOptions({
xhtml: true
});
let globp = promisify(glob);
// removes doclet syntax from comments
let cleanDoclets = desc => {
let idx = desc.indexOf('@');
return (idx === -1 ? desc : desc.substr(0, idx )).trim();
};
let cleanDocletValue = str => str.trim().replace(/^\{/, '').replace(/\}$/, '');
let quote = str => str && `'${str}'`;
let isLiteral = str => (/^('|")/).test(str.trim());
/**
* parse out description doclets to an object and remove the comment
*
* @param {ComponentMetadata|PropMetadata} obj
*/
function parseDoclets(obj, propName) {
let desc = obj.desc || defaultDescriptions[propName] || '';
obj.doclets = metadata.parseDoclets(desc) || {};
obj.desc = cleanDoclets(desc);
obj.descHtml = marked(obj.desc);
}
/**
* Reads the JSDoc "doclets" and applies certain ones to the prop type data
* This allows us to "fix" parsing errors, or unparsable data with JSDoc style comments
*
* @param {Object} props Object Hash of the prop metadata
* @param {String} propName
*/
function applyPropDoclets(props, propName) {
let prop = props[propName];
let doclets = prop.doclets;
let 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('|');
prop.type.value = value;
prop.type.name = value.every(isLiteral) ? 'enum' : 'union';
}
}
// 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;
}
// Use @defaultValue to provide a prop's default value
if (doclets.defaultValue) {
prop.defaultValue = cleanDocletValue(doclets.defaultValue);
}
}
function addBootstrapPropTypes(Component, componentData) {
let propTypes = Component.propTypes || {};
let defaultProps = Component.defaultProps || {};
function bsPropInfo(propName) {
let props = componentData.props;
let prop = propTypes[propName];
if (prop && !props[propName]) {
let values = prop._values || [];
props[propName] = {
desc: '',
defaultValue: quote(defaultProps[propName]),
type: {
name: 'enum',
value: values.map( v => `"${v}"`),
}
};
}
}
bsPropInfo('bsStyle');
bsPropInfo('bsSize');
if (propTypes.bsClass) {
componentData.props.bsClass = {
desc: '',
defaultValue: quote(defaultProps.bsClass),
type: { name: 'string' }
};
}
}
export default function generate(destination, options = { mixins: true, inferComponent: true }) {
return globp(__dirname + '/../src/**/*.js') // eslint-disable-line no-path-concat
.then( files => {
let results = files.map(
filename => fsp.readFile(filename, 'utf-8').then(content => metadata(content, options)));
return Promise.all(results)
.then( data => {
let result = {};
data.forEach(components => {
Object.keys(components).forEach(key => {
let Component;
try {
// require the actual component to inspect props we can only get a runtime
Component = require('../src/' + key);
} catch (e) {} //eslint-disable-line
const component = components[key];
if (Component) {
addBootstrapPropTypes(Component, component);
}
parseDoclets(component);
Object.keys(component.props).forEach( propName => {
const prop = component.props[propName];
parseDoclets(prop, propName);
applyPropDoclets(component.props, propName);
});
});
// combine all the component metadata into one large object
result = { ...result, ...components };
});
return result;
})
.catch( e => setTimeout(()=> { throw e; }));
});
}