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": {
|
"react-proxy": {
|
||||||
"version": "3.0.0-alpha.1",
|
"version": "3.0.0-alpha.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-3.0.0-alpha.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-3.0.0-alpha.1.tgz",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"prop-types": "15.6.1",
|
"prop-types": "15.6.1",
|
||||||
"react-emotion": "^9.1.3",
|
"react-emotion": "^9.1.3",
|
||||||
"react-helmet": "5.2.0",
|
"react-helmet": "5.2.0",
|
||||||
|
"react-markdown": "^3.3.2",
|
||||||
"rollup": "^0.58.1",
|
"rollup": "^0.58.1",
|
||||||
"rollup-plugin-commonjs": "^9.1.0",
|
"rollup-plugin-commonjs": "^9.1.0",
|
||||||
"rollup-plugin-node-resolve": "^3.3.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';
|
import info from '../../../build/info.json';
|
||||||
|
|
||||||
const modules = [];
|
const modules = getModules(info);
|
||||||
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, '-');
|
|
||||||
}
|
|
||||||
|
|
||||||
class Docs extends Component {
|
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() {
|
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