diff --git a/tools/jsdoc/Check.js b/tools/jsdoc/Check.js new file mode 100644 index 0000000000..b7e0fb1298 --- /dev/null +++ b/tools/jsdoc/Check.js @@ -0,0 +1,140 @@ +var fs = require('fs'); +var path = require('path'); + +function endsWith(path, exts) { + var result = false; + exts.forEach(function(ext) { + if (path.endsWith(ext)) { + result = true; + } + }); + return result; +} + +function WarningObject(file, type, issues){ + this.file = file; + this.type = type; + this.issues = issues; +} + +var warnings = []; + +function parse() { + var rootFolder = __dirname; + console.log("Scanning hifi source for jsdoc comments..."); + + // directories to scan for jsdoc comments + var dirList = [ + '../../interface/src', + '../../interface/src/assets', + '../../interface/src/audio', + '../../interface/src/avatar', + '../../interface/src/commerce', + '../../interface/src/devices', + '../../interface/src/java', + '../../interface/src/networking', + '../../interface/src/ui/', + '../../interface/src/scripting', + '../../interface/src/ui/overlays', + '../../interface/src/raypick', + '../../libraries/animation/src', + '../../libraries/audio-client/src', + '../../libraries/audio/src', + '../../libraries/avatars/src', + '../../libraries/avatars-renderer/src/avatars-renderer', + '../../libraries/controllers/src/controllers/', + '../../libraries/controllers/src/controllers/impl/', + '../../libraries/display-plugins/src/display-plugins/', + '../../libraries/entities/src', + '../../libraries/graphics-scripting/src/graphics-scripting/', + '../../libraries/input-plugins/src/input-plugins', + '../../libraries/model-networking/src/model-networking/', + '../../libraries/networking/src', + '../../libraries/octree/src', + '../../libraries/physics/src', + '../../libraries/pointers/src', + '../../libraries/script-engine/src', + '../../libraries/shared/src', + '../../libraries/shared/src/shared', + '../../libraries/trackers/src/trackers', + '../../libraries/ui/src/ui', + '../../plugins/oculus/src', + '../../plugins/openvr/src' + ]; + + // only files with this extension will be searched for jsdoc comments. + var exts = ['.h', '.cpp']; + + dirList.forEach(function (dir) { + var joinedDir = path.join(rootFolder, dir); + var files = fs.readdirSync(joinedDir); + files.forEach(function (file) { + var pathDir = path.join(joinedDir, file); + if (fs.lstatSync(pathDir).isFile() && endsWith(pathDir, exts)) { + // load entire file into a string + var data = fs.readFileSync(pathDir, "utf8"); + var fileName = path.basename(file); + var badJSDocWarnings = checkForBadJSDoc(data, fileName); + if (badJSDocWarnings.length > 0){ + warnings.push(badJSDocWarnings); + } + var badWordsList = checkForBadwordlist(data, fileName); + if (badWordsList){ + warnings.push(badWordsList); + } + + } + }); + }); +} + +function checkForBadJSDoc(dataToSearch, file){ + var warningList = []; + var reg = /\/\*\*js.*/g; + var matches = dataToSearch.match(reg); + if (matches) { + // add to source, but strip off c-comment asterisks + var filtered = matches.filter( item => { + return item.trim() !== '/**jsdoc'; + }); + if (filtered.length > 0){ + warningList.push(new WarningObject(file, "badJSDOC", filtered)); + } + } + return warningList; +} + +var badWordList = ["@params", "@return", "@bool"]; + +function checkForBadwordlist(dataToSearch, file){ + var warningList = []; + var reg = /(\/\*\*jsdoc(.|[\r\n])*?\*\/)/g; + var matches = dataToSearch.match(reg); + if (matches) { + var filtered = matches.forEach( item => { + var split = item.split(" "); + var filterList = []; + item.split(" ").forEach( item => { + badWordList.forEach(searchTerm => { + if (item === searchTerm) { + filterList.push(searchTerm); + } + }) + }) + if (filterList.length > 0) { + warningList.push(filterList); + } + }); + } + let flatten = warningList.reduce( (prev, cur) => { + return [...prev, ...cur]; + },[]) + let unique = [...new Set(flatten)]; + if (warningList.length > 0) { + return new WarningObject(file, "badWordList", unique); + } + +} + +parse(); +fs.writeFileSync(path.join(__dirname, "warningLog"), warnings.map( item => JSON.stringify(item)).join("\n")); \ No newline at end of file diff --git a/tools/jsdoc/gravPrep-Explore.js b/tools/jsdoc/gravPrep-Explore.js new file mode 100644 index 0000000000..4f31928923 --- /dev/null +++ b/tools/jsdoc/gravPrep-Explore.js @@ -0,0 +1,308 @@ +// Dependencies + const htmlclean = require('htmlclean'); + const fs = require('fs'); + const path = require('path'); + const pretty = require('pretty'); + const cheerio = require('cheerio'); + const rimraf = require('rimraf'); + const dedent = require('dedent-js'); + +// Required directories + let dir_out = path.join(__dirname, 'out'); + + let dir_grav = path.join(dir_out, 'grav'); + let dir_css = path.join(dir_grav, 'css'); + let dir_js = path.join(dir_grav, 'js'); + let dir_template = path.join(dir_grav, 'templates'); + + let dir_md = path.join(dir_grav, '06.api-reference'); + let dir_md_objects = path.join(dir_md, '02.Objects'); + let dir_md_namespaces = path.join(dir_md, '01.Namespaces'); + let dir_md_globals = path.join(dir_md, '03.Globals'); + +// Target Copy Directories + let targetTemplateDirectory = "D:/ROLC/Organize/O_Projects/Hifi/Docs/hifi-docs-grav/user/themes/learn2/"; + let targetMDDirectory = "D:/ROLC/Organize/O_Projects/Hifi/Docs/hifi-docs-grav-content/"; + +// Array to itterate over and create if doesn't exist + let dirArray = [dir_grav, dir_css, dir_js, dir_template, dir_md, dir_md_objects, dir_md_namespaces, dir_md_globals]; + +// Maps for directory names + let map_dir_md = { + "API-Reference": dir_md, + "Globals": dir_md_globals, + "Objects": dir_md_objects, + "Namespaces": dir_md_namespaces, + "Class": dir_md_objects, + "Namespace": dir_md_namespaces, + "Global": dir_md_globals + } + +// Base Grouping Directories for MD files + let baseMDDirectories = ["API-Reference", "Globals", "Namespaces", "Objects"] + +// Html variables to be handle regex replacements + const html_reg_static = /\(static\)<\/span>/g + const html_reg_title = /\.+?\<\/h1\>/g; + const html_reg_htmlExt = /\.html/g; + const html_reg_objectHeader = /
[\s\S]+?<\/header>/; + const html_reg_objectSpanNew = /

<\/h4>/; + const html_reg_brRemove = /
[\s\S]+?
/; + +// Mapping for GroupNames and Members + let groupNameMemberMap = { + "Objects": [], + "Namespaces": [], + "Globals": [] + } + +// Procedural functions + function createMD(title, directory, needsDir, isGlobal){ + let mdSource = makeMdSource(title); + + if (needsDir){ + if (!fs.existsSync(directory)) { + fs.mkdirSync(directory); + } + } + + let destinationMDFile = path.join(directory, `API_${title}.md`); + fs.writeFileSync(destinationMDFile, mdSource); + } + + function createTemplate(title,content){ + let twigBasePartial = makeTwigFile(content); + let destinationFile = path.join(dir_template, `API_${title}.html.twig`); + fs.writeFileSync(destinationFile, twigBasePartial); + } + + function copyFileSync( source, target ) { + let targetFile = target; + + // If target is a directory a new file with the same name will be created + if ( fs.existsSync( target ) ) { + // console.log("target exists"); + if ( fs.lstatSync( target ).isDirectory() ) { + // console.log("target is a directory"); + + targetFile = path.join( target, path.basename( source ) ); + } + } + + fs.writeFileSync(targetFile, fs.readFileSync(source)); + } + + function copyFolderRecursiveSync( source, target ) { + var files = []; + + // Check if folder needs to be created or integrated + var targetFolder = path.join( target, path.basename( source ) ); + if ( !fs.existsSync( targetFolder ) ) { + fs.mkdirSync( targetFolder ); + } + + // Copy + if ( fs.lstatSync( source ).isDirectory() ) { + files = fs.readdirSync( source ); + files.forEach( function ( file ) { + var curSource = path.join( source, file ); + if ( fs.lstatSync( curSource ).isDirectory() ) { + copyFolderRecursiveSync( curSource, targetFolder ); + } else { + copyFileSync( curSource, targetFolder ); + } + } ); + } + } + + function prepareHtml(source){ + let htmlBefore = fs.readFileSync(source, {encoding: 'utf8'}); + let htmlAfter = htmlclean(htmlBefore); + let htmlAfterPretty = pretty(htmlAfter); + return cheerio.load(htmlAfterPretty); + } + + function makeMdSource(title){ + return dedent( + ` + --- + title: ${title} + taxonomy: + category: + - docs + visible: true + --- + ` + ) + } + + function makeTwigFile(contentHtml){ + return dedent( + ` + {% extends 'partials/base_noGit.html.twig' %} + {% set tags = page.taxonomy.tag %} + {% if tags %} + {% set progress = page.collection({'items':{'@taxonomy':{'category': 'docs', 'tag': tags}},'order': {'by': 'default', 'dir': 'asc'}}) %} + {% else %} + {% set progress = page.collection({'items':{'@taxonomy':{'category': 'docs'}},'order': {'by': 'default', 'dir': 'asc'}}) %} + {% endif %} + + {% block navigation %} + + {% endblock %} + + {% block content %} +
+

{{ page.title }}

+ ${contentHtml} +
+ {% endblock %} + ` + ) + } + + function handleNamespace(title, content){ + groupNameMemberMap["Namespaces"].push(title); + let destinationDirectory = path.join(map_dir_md["Namespace"], title); + + createMD(title, destinationDirectory, true); + createTemplate(title, content); + } + + function handleClass(title, content){ + groupNameMemberMap["Objects"].push(title); + let destinationDirectory = path.join(map_dir_md["Class"], title); + createMD(title, destinationDirectory, true) + + let formatedHtml = content + .replace(html_reg_objectSpanNew,"") + .replace(html_reg_brRemove, ""); + createTemplate(title, formatedHtml); + } + + function handleGlobal(title, content){ + groupNameMemberMap["Globals"].push("Globals"); + createMD("Globals", map_dir_md["Global"], false, true); + createTemplate("Globals", content); + } + + function makeGroupTOC(group){ + let mappedGroup; + if (!Array.isArray(group)){ + mappedGroup = groupNameMemberMap[group]; + } else { + mappedGroup = group; + } + let htmlGroup = mappedGroup.map( item => { + return dedent( + ` +
+ ${item} +
+ ` + ) + }) + return htmlGroup.join("\n"); + } + +// Remove grav directory if exists to make sure old files aren't kept + if (fs.existsSync(dir_grav)){ + console.log("dir_grav exists"); + rimraf.sync(dir_grav); + } + +// Create Grav directories in JSDOC output + dirArray.forEach(function(dir){ + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + }) + +// Create baseMD files + baseMDDirectories.forEach( md => { + createMD(md, map_dir_md[md]); + }) + +// Read jsdoc output folder and process html files + let curSource = path.join(dir_out, "Selection.html"); + // Clean up the html source + let loadedHtml = prepareHtml(curSource); + + // Extract the title, group name, and the main div + let splitTitle = loadedHtml("title").text().split(": "); + let groupName = splitTitle[1]; + let htmlTitle = splitTitle.pop(); + let mainDiv = loadedHtml("#main") + + // Exploring Extractions + let array = mainDiv.find('h4').toArray(); + + // console.log(array[2]) + var reducedArray = array.reduce((prev, cur) => { + try { + // console.log(cur.children[1]); + prev.push(cur.children[1].data); + } catch(e) { + + } + return prev; + }, []) + console.log("name", reducedArray.length); + + // Strip out undesired regex + let mainDivRegexed = mainDiv.html() + .replace(html_reg_static,"") + .replace(html_reg_title,"") + .replace(html_reg_objectHeader,"") + .replace(html_reg_htmlExt,""); + // Handle Unique Categories + switch(groupName){ + case "Namespace": + handleNamespace(htmlTitle, mainDivRegexed); + break; + case "Class": + handleClass(htmlTitle, mainDivRegexed); + break; + case "Global": + handleGlobal(htmlTitle, mainDivRegexed); + break; + default: + console.log(`Case not handled for ${groupName}`); + } + +// Create the base Templates after processing individual files + createTemplate("API-Reference", makeGroupTOC(["Namespaces", "Objects", "Globals"])); + createTemplate("Namespaces", makeGroupTOC("Namespaces")); + createTemplate("Objects", makeGroupTOC("Objects")); + +// Copy files to the Twig Directory + let templateFiles = fs.readdirSync(path.resolve(targetTemplateDirectory)); + // Remove Existing API files + templateFiles.forEach(function(file){ + let curSource = path.join(targetTemplateDirectory, file); + + if(path.basename(file, '.html').indexOf("API") > -1){ + fs.unlink(curSource); + } + }) + copyFolderRecursiveSync(dir_template, targetTemplateDirectory); + +// Copy files to the Md Directory + let baseMdRefDir = path.join(targetMDDirectory,"06.api-reference"); + // Remove existing MD directory + if (fs.existsSync(baseMdRefDir)){ + rimraf.sync(baseMdRefDir); + } + copyFolderRecursiveSync(dir_md, targetMDDirectory); diff --git a/tools/jsdoc/gravPrep.js b/tools/jsdoc/gravPrep.js index 04aeaa9069..d02116a02f 100644 --- a/tools/jsdoc/gravPrep.js +++ b/tools/jsdoc/gravPrep.js @@ -48,6 +48,16 @@ const html_reg_objectHeader = /
[\s\S]+?<\/header>/; const html_reg_objectSpanNew = /

<\/h4>/; const html_reg_brRemove = /
[\s\S]+?
/; + const html_reg_methodEdit = /

Methods<\/h3>/; + const html_reg_methodEdit_replace = '

Methods
'; + const html_reg_classesEdit = /

Classes<\/h3>/; + const html_reg_classesEdit_replace = '

Classes
'; + const html_reg_typeEdit = /(
Returns[\s\S]*?Type)(<\/dt[\s\S]*?type">)(.*?)(<\/span><\/dd>[\s\S]*?<\/dl>)/g; + const html_reg_typeEdit_replace = '$1: $3' + const html_reg_methodSize = /()(<\/h4>)/g; + const html_reg_methodSize_replace = ''; + const html_reg_returnSize = /
Returns:<\/h5>/g; + const html_reg_returnSize_replace = '
Returns:<\/h6>'; // Mapping for GroupNames and Members let groupNameMemberMap = { @@ -60,11 +70,6 @@ function createMD(title, directory, needsDir, isGlobal){ let mdSource = makeMdSource(title); - // if (isGlobal){ - // mdSource = - // destinationMDFile = path.join(directory, `Globals.md`); - // } - if (needsDir){ if (!fs.existsSync(directory)) { fs.mkdirSync(directory); @@ -188,7 +193,6 @@ let formatedHtml = content .replace(html_reg_objectSpanNew,"") - .replace(html_reg_brRemove, ""); createTemplate(title, formatedHtml); } @@ -251,14 +255,21 @@ let splitTitle = loadedHtml("title").text().split(": "); let groupName = splitTitle[1]; let htmlTitle = splitTitle.pop(); - let mainDiv = loadedHtml("#main").html(); - - // Strip out undesired regex - let mainDivRegexed = mainDiv + let mainDiv = loadedHtml("#main") + + // regex edits + let mainDivRegexed = mainDiv.html() .replace(html_reg_static,"") .replace(html_reg_title,"") .replace(html_reg_objectHeader,"") - .replace(html_reg_htmlExt,""); + .replace(html_reg_htmlExt,"") + .replace(html_reg_brRemove, "") + .replace(html_reg_methodEdit, html_reg_methodEdit_replace) + .replace(html_reg_classesEdit, html_reg_classesEdit_replace) + .replace(html_reg_typeEdit, html_reg_typeEdit_replace) + .replace(html_reg_returnSize, html_reg_returnSize_replace) + .replace(html_reg_methodSize, html_reg_methodSize_replace); + // Handle Unique Categories switch(groupName){ case "Namespace":