const docGen = require('react-docgen'); const resolver = docGen.resolver.findAllComponentDefinitions const {normalizeClassDefinition, getMemberValuePath, getMethodDocumentation} = docGen.utils; const handlers = docGen.handlers; const {types: {namedTypes: types}} = require('recast'); const {PydioCoreRequires} = require('../../res/js/dist/libdefs') const PydioUILibs = { Form: 'form', ReactUI: 'boot', Components: 'components', HOCs: 'hoc', Workspaces: 'workspaces' }; const PydioCoreLibResolver = function( ast, recast ) { const types = recast.types.namedTypes; let definitions = []; function classVisitor(path) { definitions.push(path); return false; } recast.visit(ast, { visitClassExpression: classVisitor, visitClassDeclaration: classVisitor }); return definitions; }; const PydioCoreMethodsHandler = function(documentation,path) { // Extract all methods from the class or object. let methodPaths = []; if (types.ClassDeclaration.check(path.node) || types.ClassExpression.check(path.node)) { methodPaths = path.get('body', 'body').filter(p => types.MethodDefinition.check(p.node)); } else if (types.ObjectExpression.check(path.node)) { methodPaths = path.get('properties').filter( p => types.FunctionExpression.check(p.get('value').node) ); // Add the statics object properties. const statics = getMemberValuePath(path, 'statics'); if (statics) { statics.get('properties').each(p => { if (isFunctionExpression(p)) { p.node.static = true; methodPaths.push(p); } }); } } const methods = methodPaths.map((p) => { return getMethodDocumentation(p); }); documentation.set('methods', methods); } const coreHandlers = [ handlers.propTypeHandler, handlers.propTypeCompositionHandler, handlers.propDocBlockHandler, handlers.flowTypeHandler, handlers.flowTypeDocBlockHandler, handlers.defaultPropsHandler, handlers.componentDocblockHandler, handlers.displayNameHandler, PydioCoreMethodsHandler, handlers.componentMethodsJsDocHandler, ]; module.exports = function(grunt){ grunt.registerMultiTask('docgen', 'Generate docs for react components', function () { //grunt.log.writeln(this.data.files + ': ' + this.target); let allDocs = {}; this.data.files.forEach((patternObject) => { grunt.log.writeln('Expanding ' + patternObject.cwd); const allFiles = grunt.file.expand(patternObject, patternObject.src); grunt.log.writeln('Found ' + allFiles.length + ' files'); allFiles.forEach((filePath) => { if(filePath.indexOf('build/') > -1) return; const isPydioCore = filePath.indexOf('res/js/core') > -1; try{ const src = grunt.file.read(patternObject.cwd + '/' + filePath); let doc = docGen.parse(src, isPydioCore ? PydioCoreLibResolver : resolver, isPydioCore ? coreHandlers : null ); filePath = filePath.replace('../', '').replace('res/react/', '').replace('res/js/', '').replace('/react/', '/'); const pluginId = filePath.split('/').shift(); if(pluginId === 'gui.ajax'){ filePath = filePath.replace('ui/', ''); const className = filePath.split('/').pop().replace('.js', '').replace('.es6', ''); if(isPydioCore){ if(className === 'Pydio'){ doc[0]['require'] = "const "+className+" = require('pydio')"; }else{ Object.keys(PydioCoreRequires).forEach((coreLibFile) => { if(filePath.endsWith(coreLibFile.replace('.js', '.es6'))) { doc[0]['require'] = "const "+className+" = require('"+PydioCoreRequires[coreLibFile]+"')"; } }); } } Object.keys(PydioUILibs).forEach((uiLibFile) => { if(filePath.indexOf(uiLibFile + '/') > -1) { doc[0]['require'] = "const {"+className+"} = require('pydio').requireLib('"+PydioUILibs[uiLibFile]+"')"; //grunt.log.writeln(doc[0]['require']); } }); } if(!allDocs[pluginId]){ allDocs[pluginId] = {}; } allDocs[pluginId][filePath.replace(pluginId + '/', '')] = doc; grunt.log.writeln('[OK] Parsed ' + filePath + ' successfully'); }catch(e){ grunt.verbose.writeln('[SKIP] Skipping ' + filePath + ' (' + e.message + ')'); } }); //grunt.log.writeln('All Docs' + Object.keys(allDocs)); grunt.file.write(patternObject.dest, JSON.stringify(allDocs, null, 2)); grunt.log.writeln('File ' + patternObject.dest + ' written'); }); }); }