Doc components
This commit is contained in:
38
site/package-lock.json
generated
38
site/package-lock.json
generated
@@ -11734,6 +11734,44 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-markdown": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-3.3.2.tgz",
|
||||
"integrity": "sha512-3GzTB2JY+ciBcpok/t1acM49gAJTuzjSdwbG2+L6r31V6EsZqwx/F0Ht2sbuuKGyGmFCmORpxNrowT2VVbzv+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prop-types": "^15.6.1",
|
||||
"remark-parse": "^5.0.0",
|
||||
"unified": "^6.1.5",
|
||||
"unist-util-visit": "^1.3.0",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"remark-parse": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz",
|
||||
"integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"collapse-white-space": "^1.0.2",
|
||||
"is-alphabetical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-whitespace-character": "^1.0.0",
|
||||
"is-word-character": "^1.0.0",
|
||||
"markdown-escapes": "^1.0.0",
|
||||
"parse-entities": "^1.1.0",
|
||||
"repeat-string": "^1.5.4",
|
||||
"state-toggle": "^1.0.0",
|
||||
"trim": "0.0.1",
|
||||
"trim-trailing-lines": "^1.0.0",
|
||||
"unherit": "^1.0.4",
|
||||
"unist-util-remove-position": "^1.0.0",
|
||||
"vfile-location": "^2.0.0",
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-proxy": {
|
||||
"version": "3.0.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-3.0.0-alpha.1.tgz",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"prop-types": "15.6.1",
|
||||
"react-emotion": "^9.1.3",
|
||||
"react-helmet": "5.2.0",
|
||||
"react-markdown": "^3.3.2",
|
||||
"rollup": "^0.58.1",
|
||||
"rollup-plugin-commonjs": "^9.1.0",
|
||||
"rollup-plugin-node-resolve": "^3.3.0",
|
||||
|
||||
76
site/src/components/doc/Class.js
Normal file
76
site/src/components/doc/Class.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import {string, array} from 'prop-types';
|
||||
import Markdown from 'react-markdown';
|
||||
import {slugify, getShortName, getShortModuleName} from '../../utils/doc';
|
||||
|
||||
class Class extends Component {
|
||||
static propTypes = {
|
||||
name: string.isRequired,
|
||||
description: string,
|
||||
params: array,
|
||||
exported: string
|
||||
};
|
||||
|
||||
renderArguments() {
|
||||
if (!this.props.params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h4>Arguments</h4>
|
||||
<ul>{this.props.params.map(this.renderArgument)}</ul>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderArgument(arg) {
|
||||
return (
|
||||
<li key={arg.name}>
|
||||
<code>{arg.name}</code>: {arg.description}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const name = this.props.name;
|
||||
const shortName = getShortName(name);
|
||||
const moduleName = getShortModuleName(name);
|
||||
const slug = slugify(name);
|
||||
const exported = this.props.exported;
|
||||
|
||||
let importSyntax;
|
||||
if (exported) {
|
||||
if (exported === 'default') {
|
||||
importSyntax = `import ${shortName} from '${moduleName}';\n\n`;
|
||||
} else if (exported !== shortName) {
|
||||
importSyntax = `import {${exported} as ${shortName}} from '${moduleName}';\n\n`;
|
||||
} else {
|
||||
importSyntax = `import {${exported}} from '${moduleName}';\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const params = this.props.params || [];
|
||||
const usage = `new ${shortName}(${params
|
||||
.map(param => param.name)
|
||||
.join(', ')});`;
|
||||
|
||||
const description = this.props.description || '';
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<a name={slug} href={`#${slug}`} />
|
||||
<pre>
|
||||
<code>
|
||||
{importSyntax}
|
||||
{usage}
|
||||
</code>
|
||||
</pre>
|
||||
<Markdown source={description} />
|
||||
{this.renderArguments()}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Class;
|
||||
76
site/src/components/doc/Func.js
Normal file
76
site/src/components/doc/Func.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import {string, array} from 'prop-types';
|
||||
import Markdown from 'react-markdown';
|
||||
import {slugify, getShortName, getShortModuleName} from '../../utils/doc';
|
||||
|
||||
class Func extends Component {
|
||||
static propTypes = {
|
||||
name: string.isRequired,
|
||||
description: string,
|
||||
params: array,
|
||||
exported: string
|
||||
};
|
||||
|
||||
renderArguments() {
|
||||
if (!this.props.params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h4>Arguments</h4>
|
||||
<ul>{this.props.params.map(this.renderArgument)}</ul>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderArgument(arg) {
|
||||
return (
|
||||
<li key={arg.name}>
|
||||
<code>{arg.name}</code>: {arg.description}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const name = this.props.name;
|
||||
const shortName = getShortName(name);
|
||||
const moduleName = getShortModuleName(name);
|
||||
const slug = slugify(name);
|
||||
const exported = this.props.exported;
|
||||
|
||||
let importSyntax;
|
||||
if (exported) {
|
||||
if (exported === 'default') {
|
||||
importSyntax = `import ${shortName} from '${moduleName}';\n\n`;
|
||||
} else if (exported !== shortName) {
|
||||
importSyntax = `import {${exported} as ${shortName}} from '${moduleName}';\n\n`;
|
||||
} else {
|
||||
importSyntax = `import {${exported}} from '${moduleName}';\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const params = this.props.params || [];
|
||||
const usage = `${shortName}(${params
|
||||
.map(param => param.name)
|
||||
.join(', ')});`;
|
||||
|
||||
const description = this.props.description || '';
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<a name={slug} href={`#${slug}`} />
|
||||
<pre>
|
||||
<code>
|
||||
{importSyntax}
|
||||
{usage}
|
||||
</code>
|
||||
</pre>
|
||||
<Markdown source={description} />
|
||||
{this.renderArguments()}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Func;
|
||||
57
site/src/components/doc/Module.js
Normal file
57
site/src/components/doc/Module.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import {string, array} from 'prop-types';
|
||||
import {slugify, getShortModuleName} from '../../utils/doc';
|
||||
import Func from './Func';
|
||||
import Class from './Class';
|
||||
|
||||
class Module extends Component {
|
||||
static propTypes = {
|
||||
name: string.isRequired,
|
||||
classes: array.isRequired,
|
||||
functions: array.isRequired
|
||||
};
|
||||
|
||||
renderClasses() {
|
||||
if (this.props.classes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h3>Classes</h3>
|
||||
{this.props.classes.map(cls => <Class key={cls.name} {...cls} />)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderFuncs() {
|
||||
if (this.props.functions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h3>Functions</h3>
|
||||
{this.props.functions.map(func => <Func key={func.name} {...func} />)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const name = this.props.name;
|
||||
const slug = slugify(name);
|
||||
return (
|
||||
<section>
|
||||
<a name={slug} href={`#${slug}`}>
|
||||
<h1>
|
||||
<code>{getShortModuleName(name)}</code>
|
||||
</h1>
|
||||
</a>
|
||||
{this.renderClasses()}
|
||||
{this.renderFuncs()}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Module;
|
||||
@@ -1,138 +1,18 @@
|
||||
import React, {Component} from 'react';
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import Module from '../components/doc/Module';
|
||||
import {getModules} from '../utils/doc';
|
||||
|
||||
import info from '../../../build/info.json';
|
||||
|
||||
const modules = [];
|
||||
const moduleLookup = {};
|
||||
info.modules.forEach(mod => {
|
||||
moduleLookup[mod.path] = mod;
|
||||
modules.push(mod);
|
||||
});
|
||||
|
||||
info.symbols.forEach(symbol => {
|
||||
const mod = moduleLookup[symbol.path];
|
||||
if (!mod) {
|
||||
throw new Error(`No module for symbol ${symbol.name}`);
|
||||
}
|
||||
if (
|
||||
(symbol.memberof && symbol.memberof.indexOf('~') !== -1) ||
|
||||
symbol.kind === 'class'
|
||||
) {
|
||||
const name = symbol.kind === 'class' ? symbol.name : symbol.memberof;
|
||||
if (!mod.classes) {
|
||||
mod.classes = {};
|
||||
}
|
||||
if (!mod.classes[name]) {
|
||||
mod.classes[name] = {};
|
||||
}
|
||||
mod.classes[name][symbol.name] = symbol;
|
||||
}
|
||||
if (!mod.symbols) {
|
||||
mod.symbols = [];
|
||||
}
|
||||
mod.symbols.push(symbol);
|
||||
});
|
||||
|
||||
function getModuleName(longname) {
|
||||
return longname.slice(7);
|
||||
}
|
||||
|
||||
function getName(longname) {
|
||||
return longname.split(/[~\.]/).pop();
|
||||
}
|
||||
|
||||
function isMember(symbol) {
|
||||
return symbol.name.indexOf('#') !== -1;
|
||||
}
|
||||
|
||||
function slugify(name) {
|
||||
return name.replace(/[#~\.]/g, '-');
|
||||
}
|
||||
const modules = getModules(info);
|
||||
|
||||
class Docs extends Component {
|
||||
renderModule = mod => {
|
||||
const slug = slugify(mod.name);
|
||||
return (
|
||||
<section key={mod.name}>
|
||||
<a name={slug} href={`#${slug}`}>
|
||||
<h1>{getModuleName(mod.name)}</h1>
|
||||
<h2>Classes</h2>
|
||||
{mod.classes &&
|
||||
Object.keys(mod.classes).map(cls => this.renderClass(cls, mod))}
|
||||
<h2>Functions</h2>
|
||||
{mod.symbols &&
|
||||
mod.symbols
|
||||
.filter(sym => sym.kind === 'function' && !isMember(sym))
|
||||
.map(fn => this.renderFunction(fn, mod))}
|
||||
<h2>Constants</h2>
|
||||
{mod.symbols &&
|
||||
mod.symbols
|
||||
.filter(sym => sym.kind === 'constant' && !isMember(sym))
|
||||
.map(constant => this.renderConstant(constant, mod))}
|
||||
</a>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
renderImport(longname, mod) {
|
||||
return (
|
||||
<code>
|
||||
import {getName(longname)} from '{getModuleName(mod.name)}';
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
renderParams(params) {
|
||||
//TODO Render types in a more structured way (like in default template?)
|
||||
//TODO Use markdown for description
|
||||
return (
|
||||
<ul>
|
||||
{params.map(param => (
|
||||
<li key={param.name}>
|
||||
{param.name}: {param.types.join('|')}
|
||||
<br />
|
||||
{param.description}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
renderConstructor(cls, mod) {
|
||||
if (cls in mod.classes && cls in mod.classes[cls]) {
|
||||
const params = mod.classes[cls][cls].params;
|
||||
return (
|
||||
<div>
|
||||
<p>{this.renderImport(cls, mod)}</p>
|
||||
<h4>
|
||||
new {getName(cls)}({params.map(p => p.name).join(', ')})
|
||||
</h4>
|
||||
<h5>Parameters</h5>
|
||||
{this.renderParams(params)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderClass(cls, mod) {
|
||||
return (
|
||||
<div key={cls}>
|
||||
<h3>{getName(cls)}</h3>
|
||||
{this.renderConstructor(cls, mod)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderFunction(fn, mod) {
|
||||
return <p key={fn.name}>{this.renderImport(fn.name, mod)}</p>;
|
||||
}
|
||||
|
||||
renderConstant(constant, mod) {
|
||||
return <p key={constant.name}>{this.renderImport(constant.name, mod)}</p>;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>{modules.map(this.renderModule)}</div>;
|
||||
return (
|
||||
<Fragment>
|
||||
{modules.map(mod => <Module key={mod.name} {...mod} />)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
111
site/src/utils/doc.js
Normal file
111
site/src/utils/doc.js
Normal file
@@ -0,0 +1,111 @@
|
||||
function getLongModuleName(name) {
|
||||
return name.split(/[~\.]/).shift();
|
||||
}
|
||||
|
||||
export function getShortModuleName(longname) {
|
||||
return getLongModuleName(longname).slice(7);
|
||||
}
|
||||
|
||||
export function getShortName(longname) {
|
||||
return longname.split(/[~\.]/).pop();
|
||||
}
|
||||
|
||||
export function slugify(name) {
|
||||
return name.replace(/[#~\.]/g, '-');
|
||||
}
|
||||
|
||||
function getExported(name, exports) {
|
||||
const local = getShortName(name);
|
||||
for (const exported in exports) {
|
||||
if (exports[exported] === local) {
|
||||
return exported;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getModules(info) {
|
||||
const moduleLookup = {};
|
||||
info.modules.forEach(mod => {
|
||||
moduleLookup[mod.name] = {...mod, functions: [], classes: []};
|
||||
});
|
||||
|
||||
// extract classes
|
||||
const classLookup = {};
|
||||
info.symbols.forEach(symbol => {
|
||||
const name = symbol.name;
|
||||
const parent = symbol.memberof;
|
||||
|
||||
if (symbol.kind === 'class') {
|
||||
const mod = moduleLookup[parent];
|
||||
if (!mod) {
|
||||
throw new Error(
|
||||
`No module found for class ${name} with parent ${parent}`
|
||||
);
|
||||
}
|
||||
const cls = {
|
||||
...symbol,
|
||||
methods: [],
|
||||
properties: [],
|
||||
exported: getExported(name, mod.exports)
|
||||
};
|
||||
|
||||
mod.classes.push(cls);
|
||||
classLookup[name] = cls;
|
||||
}
|
||||
});
|
||||
|
||||
info.symbols.forEach(symbol => {
|
||||
const name = symbol.name;
|
||||
const parent = symbol.memberof;
|
||||
|
||||
if (symbol.kind === 'member') {
|
||||
if (parent in classLookup) {
|
||||
classLookup[parent].properties.push(symbol);
|
||||
return;
|
||||
}
|
||||
|
||||
// instance property where constructor is not marked @api
|
||||
const moduleId = getLongModuleName(name);
|
||||
const mod = moduleLookup[moduleId];
|
||||
if (!mod) {
|
||||
throw new Error(`Unexpected member: ${name}`);
|
||||
}
|
||||
|
||||
const cls = {name: parent, methods: [], properties: [symbol]};
|
||||
mod.classes.push(cls);
|
||||
classLookup[parent] = cls;
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbol.kind === 'function') {
|
||||
if (parent in moduleLookup) {
|
||||
moduleLookup[parent].functions.push({
|
||||
...symbol,
|
||||
exported: getExported(name, moduleLookup[parent].exports)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent in classLookup) {
|
||||
classLookup[parent].methods.push(symbol);
|
||||
return;
|
||||
}
|
||||
|
||||
// method where constructor is not marked @api
|
||||
const moduleId = getLongModuleName(name);
|
||||
const mod = moduleLookup[moduleId];
|
||||
if (!mod) {
|
||||
throw new Error(`Unexpected function: ${name}`);
|
||||
}
|
||||
|
||||
const cls = {name: parent, methods: [symbol], properties: []};
|
||||
mod.classes.push(cls);
|
||||
classLookup[parent] = cls;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return Object.keys(moduleLookup)
|
||||
.sort()
|
||||
.map(id => moduleLookup[id]);
|
||||
}
|
||||
Reference in New Issue
Block a user