Files
flipper/website/generate-uidocs.js
John Knox 733d61132e Restore UI components page in sidebar
Summary: This was missing because it was still being generated in /docs.

Reviewed By: danielbuechele

Differential Revision: D15198144

fbshipit-source-id: aa9e77a1603fb63c1a7576091b70d8fa7124ab16
2019-05-03 07:10:57 -07:00

114 lines
3.1 KiB
JavaScript

/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
const reactDocs = require('react-docgen');
const glob = require('glob');
const fs = require('fs');
const babylon = require('@babel/parser');
const docblockParser = require('docblock-parser');
const TARGET = __dirname + '/../docs/extending/ui-components.md';
glob(__dirname + '/../src/ui/components/**/*.js', (err, files) => {
const content = files
.map(f => [f, fs.readFileSync(f)])
.map(([name, file]) => {
try {
const doc = reactDocs.parse(file);
console.log(`${name}`);
return doc;
} catch (e) {
const doc = parseHOC(name, file);
if (doc) {
console.log(`✅ HOC: ${name}`);
return doc;
} else {
console.error(`${name}: ${e.message}`);
return null;
}
}
})
.filter(Boolean)
.map(generateMarkdown)
.reduce((acc, cv) => acc + cv, '');
fs.writeFileSync(TARGET, fs.readFileSync(TARGET) + content);
});
// HOC are not supported by react-docgen. This means, styled-components will not
// work. This is why we implement our own parser to information from these HOCs.
function parseHOC(name, file) {
try {
const ast = babylon.parse(file.toString(), {
sourceType: 'module',
plugins: ['flow', 'objectRestSpread', 'classProperties'],
});
// find the default export from the file
const exportDeclaration = ast.program.body.find(
node => node.type === 'ExportDefaultDeclaration',
);
if (exportDeclaration) {
// find doc comment right before the export
const comment = ast.comments.find(
c => c.end + 1 === exportDeclaration.start,
);
if (comment) {
return {
// use the file's name as name for the component
displayName: name
.split('/')
.reverse()[0]
.replace(/\.js$/, ''),
description: docblockParser.parse(comment.value).text,
};
}
}
} catch (e) {}
return null;
}
function generateMarkdown(component) {
let props;
if (component.props && Object.keys(component.props).length > 0) {
props = '| Property | Type | Description |\n';
props += '|---------|------|-------------|\n';
Object.keys(component.props).forEach(prop => {
let {flowType, description} = component.props[prop];
let type = '';
if (flowType) {
if (flowType.nullable) {
type += '?';
}
type +=
flowType.name === 'signature' ||
flowType.name === 'union' ||
flowType.name === 'Array'
? flowType.raw
: flowType.name;
}
// escape pipes and new lines because they will break tables
type = type.replace(/\n/g, ' ').replace(/\|/g, '⎮');
description = description
? description.replace(/\n/g, ' ').replace(/\|/g, '⎮')
: '';
props += `| \`${prop}\` | \`${type}\` | ${description} |\n`;
});
}
return `
## ${component.displayName}
${component.description || ''}
${props || ''}
`;
}