Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
Add codedocs and readme to component page (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
notlee committed Oct 12, 2018
1 parent 9974ae0 commit 517d793
Show file tree
Hide file tree
Showing 124 changed files with 11,390 additions and 3,648 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -1,7 +1,9 @@
.DS_Store
.env
.nyc_output
coverage
node_modules/
npm-debug.log
public/
bower_components/
.vscode/
9 changes: 9 additions & 0 deletions .snyk
@@ -0,0 +1,9 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.12.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
'npm:mem:20180117':
- showdown > yargs > os-locale > mem:
reason: No patch avalible yet and DoS of registry README is not a high risk.
expires: '2018-11-02T17:09:51.357Z'
patch: {}
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -22,7 +22,7 @@ Get information about Origami components, services, and repositories.

## Requirements

Running Origami Registry UI requires [Node.js] 8.x and [npm].
Running Origami Registry UI requires [Node.js] 10.x and [npm].


## Running Locally
Expand Down
3 changes: 2 additions & 1 deletion bower.json
Expand Up @@ -17,11 +17,12 @@
"o-footer-services": "^2.0.1",
"o-forms": "^5.3.0",
"o-message": "^2.2.2",
"o-syntax-highlight": "^1.0.0",
"o-syntax-highlight": "^1.2.0",
"o-tabs": "^4.1.0",
"o-typography": "^5.6.0",
"o-normalise": "^1.6.2",
"o-overlay": "^2.4.1",
"o-table": "^6.8.1",
"o-visual-effects": "^2.0.3"
}
}
6 changes: 3 additions & 3 deletions circle.yml
Expand Up @@ -3,16 +3,16 @@ jobs:
test:
working_directory: ~/origami-registry-ui
docker:
- image: node:8
- image: node:10
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
key: node-10-dependency-cache-{{ checksum "package.json" }}
- run:
name: Install Node.js dependencies
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}
key: node-10-dependency-cache-{{ checksum "package.json" }}
paths:
- node_modules
- run:
Expand Down
2 changes: 2 additions & 0 deletions index.js
Expand Up @@ -10,6 +10,8 @@ const options = {
log: console,
name: 'Origami Registry',
repoDataApiKey: process.env.REPO_DATA_API_KEY,
codedocsApiKey: process.env.CODEDOCS_API_KEY,
codedocsEndpoint: process.env.CODEDOCS_ENDPOINT,
repoDataApiSecret: process.env.REPO_DATA_API_SECRET,
workers: process.env.WEB_CONCURRENCY || 1
};
Expand Down
11 changes: 11 additions & 0 deletions lib/code-docs/example.js
@@ -0,0 +1,11 @@
'use strict';

class Example {
constructor(code, type = '', caption = '') {
this.caption = caption ? `${caption}\n` : ''; // newline to render as <p> with markdown
this.type = type || '';
this.code = code;
}
}

module.exports = Example;
116 changes: 116 additions & 0 deletions lib/code-docs/jsdoc/index.js
@@ -0,0 +1,116 @@
'use strict';

const ClassNode = require('./nodes/class');
const EventNode = require('./nodes/event');
const FunctionNode = require('./nodes/function');
const MixinNode = require('./nodes/mixin');
const NamespaceNode = require('./nodes/namespace');
const PropertyNode = require('./nodes/property');
const ModuleNode = require('./nodes/module');

/**
* Methods to prepare JsDoc json doclets for display within the registry.
*/
class JsDoc {
/**
* @param {Object[]} doclets - Raw json doclets generated by JSDoc.
*/
constructor(doclets) {
this.doclets = doclets;
}

/**
* @returns {string[]} Kinds of JSDoc doclets which are supported.
*/
static supportedDoclets() {
return ['class', 'function', 'constant', 'member', 'event', 'namespace', 'mixin', 'module'];
}

/**
* Format a doclet by removing superfluous properties and adding any custom properties.
* @param {Object} doclet - A raw json doclet generated by JSDoc.
* @returns {Object} Formatted node.
*/
static formatDoclet(doclet) {
switch (doclet.kind) {
case 'class':
return new ClassNode(doclet);
break;
case 'function':
return new FunctionNode(doclet);
break;
case 'constant':
case 'member':
return new PropertyNode(doclet);
break;
case 'event':
return new EventNode(doclet);
break;
case 'namespace':
return new NamespaceNode(doclet);
break;
case 'mixin':
return new MixinNode(doclet);
break;
case 'module':
return new ModuleNode(doclet);
break;
default:
throw new Error(`JsDoc doclet kind "${doclet.kind}" is not supported by the registry.`);
break;
};
}

/**
* @returns {Object[]} Formatted nodes.
*/
getNodes() {
if (!this._formattedDoclets) {
const supportedDoclets = this.doclets.filter((doclet) => {
const isNotIndicatedPrivate = doclet.name && doclet.name.indexOf('_') !== 0;
const isSupported = doclet.kind && JsDoc.supportedDoclets().includes(doclet.kind);
const isPublic = doclet.access !== 'private';
const isDocumented = doclet.undocumented !== true;
return isSupported && isPublic && isNotIndicatedPrivate && isDocumented;
});

this._formattedDoclets = supportedDoclets.map((doclet) => {
return JsDoc.formatDoclet(doclet);
});
}
return this._formattedDoclets;
}

/**
* @returns {Object[]} Formatted nodes.
*/
getNodesByTypeWithMembers() {
if (!this._formattedDocletsWithMembers) {
this._formattedDocletsWithMembers = JsDoc._addChildNodes(this.getNodes(), {});
}
return this._formattedDocletsWithMembers;
}

/**
* Add member nodes to parent nodes. E.g. add function nodes to their corresponding class node.
* @param {Object[]} nodes [{}] - Formatted JSDoc doclets.
* @param {Object|Object[]} parentNodes - Root nodes to add member nodes to (optional).
* @returns {Object[]} Nodes with member nodes grouped by kind e.g. {name: 'Example', type: class, examples: [{}], functions: [{}], properties: [{}]}
* @access private
*/
static _addChildNodes(nodes, parentNodes = {}) {
parentNodes = Array.isArray(parentNodes) ? parentNodes : [parentNodes];
const nodesWithMembers = parentNodes.map((parentNode) => {
const memberNodes = nodes.filter((node) => node.memberof === parentNode.longname);
return memberNodes.reduce((parentNode, node) => {
JsDoc._addChildNodes(nodes, node);
parentNode[node.group] = parentNode[node.group] || [];
parentNode[node.group].push(node);
return parentNode;
}, parentNode);
});
return (nodesWithMembers.length === 1 ? nodesWithMembers[0] : nodesWithMembers);
}
}

module.exports = JsDoc;
69 changes: 69 additions & 0 deletions lib/code-docs/jsdoc/nav.js
@@ -0,0 +1,69 @@
'use strict';

const NavNode = require('../nav-node');

/**
* Methods to build a navigation for JSDoc.
*/
class JsDocNav {

/**
* @param {JsDoc} JsDoc - Raw json doclets generated by JSDoc.
* @returns {NavNode[]} A navigation composing an array of sub-navigations e.g. [{title, items: [{title, link}]}]
*/
static createNavigation(JsDoc) {
const nav = [];
const nodes = JsDoc.getNodes();
// Add important doclets to the root of the nav with subnavs.
// E.g. add classes with funciton and proeprties as a subnav.
const jsDocByTypeWithMembers = JsDoc.getNodesByTypeWithMembers();
const docletGroups = [
{ 'name': 'classes', 'subGroups': ['functions', 'properties'] },
{ 'name': 'modules', 'subGroups': ['classes', 'functions', 'properties'] }];
docletGroups.forEach(docletGroup => {
if (jsDocByTypeWithMembers[docletGroup.name]) {
jsDocByTypeWithMembers[docletGroup.name].forEach((node) => {
nav.push(createSubNav(
`${node.name}`,
[].concat([node], ...docletGroup.subGroups.map(name => node[name]))
));
});
}
});
// Add events to nav, ignoring their hierarchy.
// I.e. Surface events which are not in the global scope.
const eventNodes = nodes.filter(node => node.kind === 'event');
if (eventNodes.length > 0) {
nav.push(createSubNav('Events', eventNodes));
}
// Add global nodes which are not already added to the nav.
const globalNodes = nodes.filter(node => node.memberof === undefined && ['class', 'module', 'events'].includes(node.kind) === false);
if (globalNodes.length > 0) {
nav.push(createSubNav('Global', globalNodes));
}
return nav;
}
}

/**
* @param {string} title - The title of the subnav.
* @param {Object[]} nodes - Formatted JS doclets to link to from the sub-navigation.
* @returns {NavNode} A sub-navigation e.g. {title, items: [{title, link}]}
* @access private
*/
function createSubNav(title, nodes) {
nodes = nodes || [];
const items = nodes.filter(node => node).map((node) => {
let title = `${node.label}: ${node.name}`;
if (node.kind === 'class' && node.memberof === undefined) {
title = 'Constructor & Overview';
}
if (node.kind === 'module' && node.memberof === undefined) {
title = 'Overview';
}
return new NavNode(title, `#${node.longname}`);
});
return new NavNode(title, items);
}

module.exports = JsDocNav;
100 changes: 100 additions & 0 deletions lib/code-docs/jsdoc/nodes/base.js
@@ -0,0 +1,100 @@
'use strict';

const Example = require('../../example');
const { URL } = require('url');

class BaseNode {
constructor(doclet) {
this.name = doclet.name;
this.longname = doclet.longname;
this.kind = doclet.kind;
this.memberof = doclet.memberof;
this.description = this.replaceLinks(doclet.description) || '';
this.access = doclet.access || '';
this.virtual = Boolean(doclet.virtual);
this.file = {
path: doclet.meta && doclet.meta.path ? doclet.meta.path : '',
name: doclet.meta && doclet.meta.filename ? doclet.meta.filename : ''
};

if (doclet.meta && isNaN(doclet.meta.lineno) === false) {
this.file.lineno = doclet.meta.lineno;
}
if (doclet.meta && isNaN(doclet.meta.columnno) === false) {
this.file.columnno = doclet.meta.columnno;
}
if (doclet.deprecated) {
this.deprecated = this.replaceLinks(doclet.deprecated);
}
}

replaceLinks(jsDocComment) {
if (typeof jsDocComment !== 'string') {
return jsDocComment;
}
return jsDocComment.replace(/(?:\[(.*)\])?{@link ([^\s|}]*)\s?(?:[|])?([^}]*)}/g, (match, p1, p2, p3) => {
//{@link namepathOrURL}
let href = p2;
let text = null;
//[link text]{@link namepathOrURL}
text = text || p1;
//{@link namepathOrURL|text}
//{@link namepathOrURL text (after the first space)}
text = text || p3;
// If no text show url/namepath.
text = text || href;
// If unable to construct url assume a JSDoc namepath and turn into a hash.
try {
new URL(href);
} catch (error) {
href = `#${href}`;
}
return `[${text}](${href})`;
});
}

addExamples(doclet, target = this) {
target.examples = target.examples || [];
if (Array.isArray(doclet.examples)) {
doclet.examples.forEach((example) => {
let code = example;
let caption;

/** @see https://github.com/jsdoc3/jsdoc/blob/832dfd704a01fc931ef54ef0a02896d89a5ee9ad/templates/default/publish.js#L470 */
if (example.match(/^\s*<caption>([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) {
caption = RegExp.$1;
code = RegExp.$3;
}

if (typeof code === 'string' && code.length > 1) {
target.examples.push(new Example(code, 'js', caption));
}
});
}
}

addParameters(doclet, target = this) {
target.parameters = target.parameters || [];
if (Array.isArray(doclet.params)) {
doclet.params.forEach((param) => target.parameters.push({
'name': param.name,
'type': param.type && param.type.names ? param.type.names : [], // param can accept multiple types
'description': param.description || '',
'default': param.hasOwnProperty('defaultvalue') ? param.defaultvalue : '',
'optional': typeof param.optional === 'boolean' ? param.optional : '',
'nullable': typeof param.nullable === 'boolean' ? param.nullable : ''
}));
}
}

addReturns(doclet, target = this) {
if (doclet.returns) {
target.returns = {
'type': doclet.returns[0] && doclet.returns[0].type && doclet.returns[0].type.names ? doclet.returns[0].type.names : [], // return can be of multiple types
'description': doclet.returns[0] && doclet.returns[0].description || ''
};
}
}
}

module.exports = BaseNode;
25 changes: 25 additions & 0 deletions lib/code-docs/jsdoc/nodes/class.js
@@ -0,0 +1,25 @@
'use strict';

const BaseNode = require('./base');

class ClassNode extends BaseNode {
constructor(doclet) {
super(doclet);
this.group = 'classes';
this.label = doclet.meta && doclet.meta.code && doclet.meta.code.type === 'ClassDeclaration' ? 'Class' : 'Constructor Function';
this.description = this.replaceLinks(doclet.classdesc) || '';
this.extends = doclet.augments || [];
this.fires = doclet.fires || [];
this.constructor = {
'name': doclet.name,
'description': this.replaceLinks(doclet.description) || '',
'parameters': [
],
'examples': []
};
this.addExamples(doclet, this.constructor);
this.addParameters(doclet, this.constructor);
}
};

module.exports = ClassNode;

0 comments on commit 517d793

Please sign in to comment.