UNPKG

mappls-map-react-native

Version:

A Mappls GL react native module for creating custom maps

284 lines (245 loc) 7.62 kB
const fs = require('fs'); const path = require('path'); const {exec} = require('child_process'); const dir = require('node-dir'); const docgen = require('react-docgen'); const parseJsDoc = require('react-docgen/dist/utils/parseJsDoc').default; const JSDocNodeTree = require('./JSDocNodeTree'); const COMPONENT_PATH = path.join( __dirname, '..', '..', 'javascript', 'components', ); const MODULES_PATH = path.join(__dirname, '..', '..', 'javascript', 'modules'); const OUTPUT_PATH = path.join(__dirname, '..', '..', 'docs', 'docs.json'); const IGNORE_FILES = [ 'AbstractLayer', 'AbstractSource', 'NativeBridgeComponent', ]; const IGNORE_METHODS = ['setNativeProps']; class DocJSONBuilder { constructor(styledLayers) { this._styledLayers = {}; for (const styleLayer of styledLayers) { const ComponentName = pascelCase(styleLayer.name); this._styledLayers[ ComponentName + (ComponentName === 'Light' ? '' : 'Layer') ] = styleLayer; } } get options() { return { match: /.js$/, shortName: true, }; } isPrivateMethod(methodName = '') { return !methodName || methodName.charAt(0) === '_'; } postprocess(component, name) { // Remove all private methods and parse examples from docblock if (!Array.isArray(component.methods)) { return; } component.name = name; // styles if (this._styledLayers[name] && this._styledLayers[name].properties) { component.styles = []; for (const prop of this._styledLayers[name].properties) { const docStyle = { name: prop.name, type: prop.type, values: [], minimum: prop.doc.minimum, maximum: prop.doc.maximum, units: prop.doc.units, default: prop.doc.default, description: prop.doc.description, requires: prop.doc.requires, disabledBy: prop.doc.disabledBy, allowedFunctionTypes: prop.allowedFunctionTypes || [], expression: prop.expression, transition: prop.transition, }; if (prop.type === 'enum') { docStyle.values = Object.keys(prop.doc.values).map(value => { return {value, doc: prop.doc.values[value].doc}; }); } else if (prop.type === 'array') { docStyle.type = `${docStyle.type}<${prop.value}>`; } component.styles.push(docStyle); } } function mapNestedProp(propMeta) { const result = { type: { name: propMeta.name, value: propMeta.value, }, description: propMeta.description, required: propMeta.required, }; if (propMeta.value) { result.type.value = propMeta.value; } return result; } function mapProp(propMeta, propName, array) { let result = {}; if (!array) { result = { name: propName || 'FIX ME NO NAME', required: propMeta.required || false, type: (propMeta.type && propMeta.type.name) || 'FIX ME UNKNOWN TYPE', default: !propMeta.defaultValue ? 'none' : propMeta.defaultValue.value.replace(/\n/g, ''), description: propMeta.description || 'FIX ME NO DESCRIPTION', }; } else { if (propName) { result.name = propName; } if (propMeta.required !== undefined) { result.required = propMeta.required; } result.type = (propMeta.type && propMeta.type.name) || 'FIX ME UNKNOWN TYPE'; if (propMeta.defaultValue) { result.default = propMeta.defaultValue.value.replace(/\n/g, ''); } if (propMeta.description) { result.description = propMeta.description; } } if ( propMeta.type && propMeta.type.name === 'arrayOf' && propMeta.type.value ) { result.type = { name: 'array', value: mapProp(mapNestedProp(propMeta.type.value), undefined, true), }; } if (propMeta.type && propMeta.type.name === 'func') { const jsdoc = parseJsDoc(propMeta.description); if (jsdoc && jsdoc.description) { result.description = jsdoc.description; } if (jsdoc && jsdoc.params && jsdoc.params.length > 0) { result.params = jsdoc.params; } if (jsdoc && jsdoc.returns) { result.returns = jsdoc.returns; } } if ( propMeta.type && propMeta.type.name === 'shape' && propMeta.type.value ) { const type = propMeta.type.value; const value = Object.keys(type).map(_name => mapProp(mapNestedProp(type[_name]), _name, false), ); result.type = {name: 'shape', value}; } return result; } // props component.props = Object.keys(component.props).map(propName => { const propMeta = component.props[propName]; return mapProp(propMeta, propName, false); }); // methods const privateMethods = []; for (const method of component.methods) { if (this.isPrivateMethod(method.name)) { privateMethods.push(method.name); continue; } if (method.docblock) { const examples = method.docblock .split('@') .filter(block => block.startsWith('example')); method.examples = examples.map(example => example.substring('example'.length), ); } } privateMethods.push(...IGNORE_METHODS); component.methods = component.methods.filter( method => !privateMethods.includes(method.name), ); } generateReactComponentsTask(results, filePath) { return new Promise((resolve, reject) => { dir.readFiles( filePath, this.options, (err, content, fileName, next) => { if (err) { return reject(err); } fileName = fileName.replace('.js', ''); if (IGNORE_FILES.includes(fileName)) { next(); return; } results[fileName] = docgen.parse(content, undefined, undefined, { filename: fileName, }); this.postprocess(results[fileName], fileName); next(); }, () => resolve(), ); }); } generateModulesTask(results, filePath) { return new Promise((resolve, reject) => { exec( `npx documentation build ${MODULES_PATH} -f json`, (err, stdout, stderr) => { if (err || stderr) { reject(err || stderr); return; } const modules = JSON.parse(stdout); for (const module of modules) { const node = new JSDocNodeTree(module); const name = `${module.name .charAt(0) .toLowerCase()}${module.name.substring(1)}`; results[name] = { name, description: node.getText(), props: [], styles: [], methods: node.getMethods(), }; } resolve(); }, ); }); } generate() { this.generateModulesTask({}, MODULES_PATH); const results = {}; const tasks = [ this.generateReactComponentsTask(results, COMPONENT_PATH), this.generateModulesTask(results, MODULES_PATH), ]; return Promise.all(tasks).then(() => { fs.writeFileSync(OUTPUT_PATH, JSON.stringify(results, null, 2)); return true; }); } } module.exports = DocJSONBuilder;