Gatsby setup for API docs
This commit is contained in:
@@ -50,3 +50,11 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: build/apidoc
|
||||
destination: apidoc
|
||||
|
||||
- run:
|
||||
name: Build Website
|
||||
command: npm run build-site
|
||||
|
||||
- store_artifacts:
|
||||
path: public
|
||||
destination: website
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
||||
/coverage/
|
||||
/dist/
|
||||
node_modules/
|
||||
/.cache/
|
||||
/public/
|
||||
|
||||
23
config/jsdoc/api-info/conf.json
Normal file
23
config/jsdoc/api-info/conf.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"opts": {
|
||||
"recurse": true,
|
||||
"template": "node_modules/jsdoc-json"
|
||||
},
|
||||
"tags": {
|
||||
"allowUnknownTags": true
|
||||
},
|
||||
"source": {
|
||||
"includePattern": "\\.js$",
|
||||
"include": [
|
||||
"src/ol"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
"jsdoc-plugin-typescript",
|
||||
"config/jsdoc/api-info/plugins/api",
|
||||
"config/jsdoc/api-info/plugins/module"
|
||||
],
|
||||
"typescript": {
|
||||
"moduleRoot": "src"
|
||||
}
|
||||
}
|
||||
15
config/jsdoc/api-info/plugins/api.js
Normal file
15
config/jsdoc/api-info/plugins/api.js
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
/**
|
||||
* Handle the api annotation.
|
||||
* @param {Object} dictionary The tag dictionary.
|
||||
*/
|
||||
exports.defineTags = dictionary => {
|
||||
|
||||
dictionary.defineTag('api', {
|
||||
onTagged: (doclet, tag) => {
|
||||
doclet.api = true;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
170
config/jsdoc/api-info/plugins/module.js
Normal file
170
config/jsdoc/api-info/plugins/module.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* This plugin adds an `exportMap` property to @module doclets. Each export map
|
||||
* is an object with properties named like the local identifier and values named
|
||||
* like the exported identifier.
|
||||
*
|
||||
* For example, the code below
|
||||
*
|
||||
* export {foo as bar};
|
||||
*
|
||||
* would be a map like `{foo: 'bar'}`.
|
||||
*
|
||||
* In the case of an export declaration with a source, the export identifier is
|
||||
* prefixed by the source. For example, this code
|
||||
*
|
||||
* export {foo as bar} from 'ol/bam';
|
||||
*
|
||||
* would be a map like `{'ol/bam foo': 'bar'}`.
|
||||
*
|
||||
* If a default export is a literal or object expression, the local name will be
|
||||
* an empty string. For example
|
||||
*
|
||||
* export default {foo: 'bar'};
|
||||
*
|
||||
* would be a map like `{'': 'default'}`.
|
||||
*/
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
|
||||
|
||||
/**
|
||||
* A lookup of export maps per source filepath.
|
||||
*/
|
||||
const exportMapLookup = {};
|
||||
|
||||
function loc(filepath, node) {
|
||||
return `${filepath}:${node.loc.start.line}`;
|
||||
}
|
||||
|
||||
function nameFromChildIdentifier(filepath, node) {
|
||||
assert.ok(node.id, `expected identifer in ${loc(filepath, node)}`);
|
||||
assert.strictEqual(node.id.type, 'Identifier', `expected identifer in ${loc(filepath, node)}`);
|
||||
return node.id.name;
|
||||
}
|
||||
|
||||
function handleExportNamedDeclaration(filepath, node) {
|
||||
if (!(filepath in exportMapLookup)) {
|
||||
exportMapLookup[filepath] = {};
|
||||
}
|
||||
const exportMap = exportMapLookup[filepath];
|
||||
|
||||
const declaration = node.declaration;
|
||||
if (declaration) {
|
||||
// `export class Foo{}` or `export function foo() {}`
|
||||
if (declaration.type === 'ClassDeclaration' || declaration.type === 'FunctionDeclaration') {
|
||||
const name = nameFromChildIdentifier(filepath, declaration);
|
||||
exportMap[name] = name;
|
||||
return;
|
||||
}
|
||||
|
||||
// `export const foo = 'bar', bam = 42`
|
||||
if (declaration.type === 'VariableDeclaration') {
|
||||
const declarations = declaration.declarations;
|
||||
assert.ok(declarations.length > 0, `expected variable declarations in ${loc(filepath, declaration)}`);
|
||||
for (const declarator of declarations) {
|
||||
assert.strictEqual(declarator.type, 'VariableDeclarator', `unexpected "${declarator.type}" in ${loc(filepath, declarator)}`);
|
||||
const name = nameFromChildIdentifier(filepath, declarator);
|
||||
exportMap[name] = name;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected named export "${declaration.type}" in ${loc(filepath, declaration)}`);
|
||||
}
|
||||
|
||||
let prefix = '';
|
||||
const source = node.source;
|
||||
if (source) {
|
||||
// `export foo from 'bar'`
|
||||
assert.strictEqual(source.type, 'Literal', `unexpected export source "${source.type}" in ${loc(filepath, source)}`);
|
||||
prefix = `${source.value} `;
|
||||
}
|
||||
|
||||
const specifiers = node.specifiers;
|
||||
assert.ok(specifiers.length > 0, `expected export specifiers in ${loc(filepath, node)}`);
|
||||
// `export {foo, bar}` or `export {default as Foo} from 'bar'`
|
||||
for (const specifier of specifiers) {
|
||||
assert.strictEqual(specifier.type, 'ExportSpecifier', `unexpected export specifier in ${loc(filepath, specifier)}`);
|
||||
|
||||
const local = specifier.local;
|
||||
assert.strictEqual(local.type, 'Identifier', `unexpected local specifier "${local.type} in ${loc(filepath, local)}`);
|
||||
|
||||
const exported = specifier.exported;
|
||||
assert.strictEqual(local.type, 'Identifier', `unexpected exported specifier "${exported.type} in ${loc(filepath, exported)}`);
|
||||
|
||||
exportMap[prefix + local.name] = exported.name;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDefaultDeclaration(filepath, node) {
|
||||
if (!(filepath in exportMapLookup)) {
|
||||
exportMapLookup[filepath] = {};
|
||||
}
|
||||
const exportMap = exportMapLookup[filepath];
|
||||
|
||||
const declaration = node.declaration;
|
||||
if (declaration) {
|
||||
// `export default class Foo{}` or `export default function foo () {}`
|
||||
if (declaration.type === 'ClassDeclaration' || declaration.type === 'FunctionDeclaration') {
|
||||
const name = nameFromChildIdentifier(filepath, declaration);
|
||||
exportMap[name] = 'default';
|
||||
return;
|
||||
}
|
||||
|
||||
// `export default foo`
|
||||
if (declaration.type === 'Identifier') {
|
||||
exportMap[declaration.name] = 'default';
|
||||
return;
|
||||
}
|
||||
|
||||
// `export default {foo: 'bar'}` or `export default 42`
|
||||
if (declaration.type === 'ObjectExpression' || declaration.type === 'Literal') {
|
||||
exportMap[''] = 'default';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected default export "${declaration.type}" in ${loc(filepath, declaration)}`);
|
||||
}
|
||||
|
||||
exports.astNodeVisitor = {
|
||||
visitNode: (node, event, parser, filepath) => {
|
||||
if (node.type === 'ExportNamedDeclaration') {
|
||||
return handleExportNamedDeclaration(filepath, node);
|
||||
}
|
||||
|
||||
if (node.type === 'ExportDefaultDeclaration') {
|
||||
return handleDefaultDeclaration(filepath, node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const moduleLookup = {};
|
||||
|
||||
exports.handlers = {
|
||||
|
||||
// create a lookup of @module doclets
|
||||
newDoclet: event => {
|
||||
const doclet = event.doclet;
|
||||
if (doclet.kind === 'module') {
|
||||
const filepath = path.join(doclet.meta.path, doclet.meta.filename);
|
||||
|
||||
assert.ok(!(filepath in moduleLookup), `duplicate @module doc in ${filepath}`);
|
||||
moduleLookup[filepath] = doclet;
|
||||
}
|
||||
},
|
||||
|
||||
// assign the `exportMap` property to @module doclets
|
||||
parseComplete: event => {
|
||||
for (const filepath in moduleLookup) {
|
||||
assert.ok(filepath in exportMapLookup, `missing ${filepath} in export map lookup`);
|
||||
moduleLookup[filepath].exportMap = exportMapLookup[filepath];
|
||||
}
|
||||
|
||||
// make sure there was a @module doclet for each export map
|
||||
for (const filepath in exportMapLookup) {
|
||||
assert.ok(filepath in moduleLookup, `missing @module doclet in ${filepath}`);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
11
gatsby-config.js
Normal file
11
gatsby-config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
'gatsby-plugin-emotion',
|
||||
{
|
||||
resolve: 'gatsby-plugin-typography',
|
||||
options: {
|
||||
pathToConfigModule: 'site/util/typography'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
23
gatsby-node.js
Normal file
23
gatsby-node.js
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
function getDocs() {
|
||||
// TODO: build if not present
|
||||
const info = require('./build/api-info.json');
|
||||
|
||||
return info.docs.filter(doc => !doc.ignore && (doc.api || doc.kind === 'module'));
|
||||
}
|
||||
|
||||
function createPages({actions: {createPage}}) {
|
||||
createPage({
|
||||
path: '/api',
|
||||
component: require.resolve('./site/pages/API.js'),
|
||||
context: {docs: getDocs()}
|
||||
});
|
||||
|
||||
createPage({
|
||||
path: '/info',
|
||||
component: require.resolve('./site/pages/Info.js'),
|
||||
context: {docs: getDocs()}
|
||||
});
|
||||
}
|
||||
|
||||
exports.createPages = createPages;
|
||||
21
package.json
21
package.json
@@ -24,7 +24,10 @@
|
||||
"copy-css": "shx cp src/ol/ol.css build/ol/ol.css",
|
||||
"transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && node tasks/serialize-workers && tsc --project config/tsconfig-build.json",
|
||||
"typecheck": "tsc --pretty",
|
||||
"apidoc": "jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc"
|
||||
"apidoc": "jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc",
|
||||
"api-info": "jsdoc --configure config/jsdoc/api-info/conf.json --destination build/api-info.json",
|
||||
"serve-site": "gatsby develop",
|
||||
"build-site": "npm run api-info && gatsby build"
|
||||
},
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -43,6 +46,8 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.0",
|
||||
"@babel/preset-env": "^7.4.4",
|
||||
"@emotion/core": "^10.0.10",
|
||||
"@emotion/styled": "^10.0.11",
|
||||
"@openlayers/eslint-plugin": "^4.0.0-beta.2",
|
||||
"@types/arcgis-rest-api": "^10.4.4",
|
||||
"@types/geojson": "^7946.0.7",
|
||||
@@ -57,9 +62,14 @@
|
||||
"coveralls": "3.0.3",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-openlayers": "^11.0.0",
|
||||
"eslint-config-tschaub": "^13.1.0",
|
||||
"eslint-plugin-react": "^7.13.0",
|
||||
"expect.js": "0.3.1",
|
||||
"front-matter": "^3.0.2",
|
||||
"fs-extra": "^8.0.0",
|
||||
"gatsby": "^2.4.3",
|
||||
"gatsby-plugin-emotion": "^4.0.6",
|
||||
"gatsby-plugin-typography": "^2.2.13",
|
||||
"glob": "^7.1.4",
|
||||
"globby": "^9.2.0",
|
||||
"handlebars": "4.1.2",
|
||||
@@ -68,6 +78,7 @@
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jquery": "3.4.1",
|
||||
"jsdoc": "3.6.2",
|
||||
"jsdoc-json": "^2.0.2",
|
||||
"jsdoc-plugin-typescript": "^2.0.1",
|
||||
"karma": "^4.1.0",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
@@ -84,7 +95,13 @@
|
||||
"pixelmatch": "^4.0.2",
|
||||
"pngjs": "^3.4.0",
|
||||
"proj4": "2.5.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"puppeteer": "~1.16.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-markdown": "^4.0.8",
|
||||
"react-syntax-highlighter": "^10.2.1",
|
||||
"react-typography": "^0.16.19",
|
||||
"rollup": "^1.12.0",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-commonjs": "^10.0.0",
|
||||
@@ -95,6 +112,8 @@
|
||||
"sinon": "^7.3.2",
|
||||
"terser-webpack-plugin": "^1.2.3",
|
||||
"typescript": "^3.4.5",
|
||||
"typography": "^0.16.19",
|
||||
"typography-theme-alton": "^0.16.19",
|
||||
"url-polyfill": "^1.1.5",
|
||||
"walk": "^2.3.9",
|
||||
"webpack": "4.31.0",
|
||||
|
||||
3
site/.eslintrc.json
Normal file
3
site/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "tschaub/react"
|
||||
}
|
||||
29
site/components/Class.js
Normal file
29
site/components/Class.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import {object} from 'prop-types';
|
||||
import React from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import Code from './Code';
|
||||
|
||||
function Class({cls, module}) {
|
||||
const exportedName = module.getExportedName(cls.name);
|
||||
let importCode;
|
||||
if (exportedName === 'default') {
|
||||
importCode = `import ${cls.name} from '${module.id}';`;
|
||||
} else {
|
||||
importCode = `import {${exportedName}} from '${module.id}';`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>{cls.name}</h3>
|
||||
<Code value={importCode} />
|
||||
<Markdown source={cls.doc.classdesc} renderers={{code: Code}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Class.propTypes = {
|
||||
cls: object.isRequired,
|
||||
module: object.isRequired
|
||||
};
|
||||
|
||||
export default Class;
|
||||
26
site/components/Code.js
Normal file
26
site/components/Code.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import {string} from 'prop-types';
|
||||
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter';
|
||||
import {coy} from 'react-syntax-highlighter/dist/styles/prism';
|
||||
|
||||
class Code extends PureComponent {
|
||||
render() {
|
||||
let language = this.props.language;
|
||||
if (!language) {
|
||||
language = 'js';
|
||||
}
|
||||
|
||||
return (
|
||||
<SyntaxHighlighter language={language} style={coy}>
|
||||
{this.props.value}
|
||||
</SyntaxHighlighter>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Code.propTypes = {
|
||||
value: string.isRequired,
|
||||
language: string
|
||||
};
|
||||
|
||||
export default Code;
|
||||
29
site/components/Func.js
Normal file
29
site/components/Func.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import {object} from 'prop-types';
|
||||
import React from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import Code from './Code';
|
||||
|
||||
function Func({func, module}) {
|
||||
const exportedName = module.getExportedName(func.name);
|
||||
let importCode;
|
||||
if (exportedName === 'default') {
|
||||
importCode = `import ${func.name} from '${module.id}';`;
|
||||
} else {
|
||||
importCode = `import {${exportedName}} from '${module.id}';`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>{func.name}</h3>
|
||||
<Code value={importCode} />
|
||||
<Markdown source={func.doc.description} renderers={{code: Code}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Func.propTypes = {
|
||||
func: object.isRequired,
|
||||
module: object.isRequired
|
||||
};
|
||||
|
||||
export default Func;
|
||||
25
site/components/Module.js
Normal file
25
site/components/Module.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import {object} from 'prop-types';
|
||||
import React from 'react';
|
||||
import Class from './Class';
|
||||
import Func from './Func';
|
||||
|
||||
function Module({module}) {
|
||||
return (
|
||||
<div>
|
||||
<hr />
|
||||
<h2>{module.id}</h2>
|
||||
{module.classes.map(cls => (
|
||||
<Class key={cls.name} cls={cls} module={module} />
|
||||
))}
|
||||
{module.functions.map(func => (
|
||||
<Func key={func.name} func={func} module={module} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Module.propTypes = {
|
||||
module: object.isRequired
|
||||
};
|
||||
|
||||
export default Module;
|
||||
8
site/components/layout.js
Normal file
8
site/components/layout.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import styled from '@emotion/styled';
|
||||
import {baseSpacingPx} from '../util/typography';
|
||||
|
||||
export const Page = styled.div({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
margin: 2 * baseSpacingPx
|
||||
});
|
||||
26
site/pages/API.js
Normal file
26
site/pages/API.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import {object} from 'prop-types';
|
||||
import React from 'react';
|
||||
import {Page} from '../components/layout';
|
||||
import Module from '../components/Module';
|
||||
import {getHelper} from '../util/api';
|
||||
|
||||
function API({pageContext: {docs}}) {
|
||||
const helper = getHelper(docs);
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<h1>API</h1>
|
||||
{helper.modules
|
||||
.filter(module => module.visible)
|
||||
.map(module => (
|
||||
<Module key={module.id} module={module} />
|
||||
))}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
API.propTypes = {
|
||||
pageContext: object.isRequired
|
||||
};
|
||||
|
||||
export default API;
|
||||
33
site/pages/Info.js
Normal file
33
site/pages/Info.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import {object} from 'prop-types';
|
||||
import React from 'react';
|
||||
import {Page} from '../components/layout';
|
||||
|
||||
function Info({pageContext: {docs}}) {
|
||||
return (
|
||||
<Page>
|
||||
<h1>API</h1>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>kind</th>
|
||||
<th>longname</th>
|
||||
<th>memberof</th>
|
||||
</tr>
|
||||
{docs.map(doc => (
|
||||
<tr key={doc.longname}>
|
||||
<td>{doc.kind}</td>
|
||||
<td>{doc.longname}</td>
|
||||
<td>{doc.memberof}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
Info.propTypes = {
|
||||
pageContext: object.isRequired
|
||||
};
|
||||
|
||||
export default Info;
|
||||
179
site/util/api.js
Normal file
179
site/util/api.js
Normal file
@@ -0,0 +1,179 @@
|
||||
class FunctionDoc {
|
||||
constructor(doc) {
|
||||
this.name = doc.name;
|
||||
this.doc = doc;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassDoc {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
processDoc(doc) {
|
||||
if (doc.kind === 'class') {
|
||||
this.doc = doc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ModuleDoc {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
|
||||
this.classLookup = {};
|
||||
this.classes = [];
|
||||
|
||||
this.functionLookup = {};
|
||||
this.functions = [];
|
||||
}
|
||||
|
||||
processDoc(doc) {
|
||||
if (doc.kind === 'module') {
|
||||
this.doc = doc;
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.kind === 'class') {
|
||||
const name = nameFromLongname(doc.longname);
|
||||
if (!(name in this.classLookup)) {
|
||||
const cls = new ClassDoc(name);
|
||||
this.classLookup[name] = cls;
|
||||
this.classes.push(cls);
|
||||
}
|
||||
|
||||
this.classLookup[name].processDoc(doc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.kind === 'function') {
|
||||
if (nameFromLongname(doc.memberof)) {
|
||||
// belongs to a class or other
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.name in this.functionLookup) {
|
||||
throw new Error(`Duplicate function ${doc.name} in ${this.id}`);
|
||||
}
|
||||
|
||||
const func = new FunctionDoc(doc);
|
||||
this.functionLookup[doc.name] = func;
|
||||
this.functions.push(func);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
finalize() {
|
||||
this.classes.sort(byName);
|
||||
this.functions.sort(byName);
|
||||
this.visible = this.classes.length > 0 || this.functions.length > 0;
|
||||
}
|
||||
|
||||
getExportedName(localName) {
|
||||
if (!this.doc || !this.doc.exportMap) {
|
||||
throw new Error(`Expected to find export map in module doc: ${this.id}`);
|
||||
}
|
||||
|
||||
if (!(localName in this.doc.exportMap)) {
|
||||
throw new Error(
|
||||
`No local name "${localName}" in export map for module: ${this.id}`
|
||||
);
|
||||
}
|
||||
|
||||
return this.doc.exportMap[localName];
|
||||
}
|
||||
}
|
||||
|
||||
const longnameRE = /^module:(?<module>.*?)([~\.](?<name>\w+)(#(?<member>\w+))?(:(?<type>\w+))?)?$/;
|
||||
|
||||
function moduleIDFromLongname(longname) {
|
||||
const match = longname.match(longnameRE);
|
||||
if (!match) {
|
||||
throw new Error(`could not match module id in longname: ${longname}`);
|
||||
}
|
||||
return match.groups.module;
|
||||
}
|
||||
|
||||
function nameFromLongname(longname) {
|
||||
const match = longname.match(longnameRE);
|
||||
if (!match) {
|
||||
throw new Error(`could not match name in longname: ${longname}`);
|
||||
}
|
||||
return match.groups.name;
|
||||
}
|
||||
|
||||
function memberFromLongname(longname) {
|
||||
const match = longname.match(longnameRE);
|
||||
if (!match) {
|
||||
throw new Error(`could not match member in longname: ${longname}`);
|
||||
}
|
||||
return match.groups.member;
|
||||
}
|
||||
|
||||
function byName(a, b) {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function byModuleId(a, b) {
|
||||
const aParts = a.id.split('/');
|
||||
const bParts = b.id.split('/');
|
||||
const len = Math.max(aParts.length, bParts.length);
|
||||
for (let i = 0; i < len; ++i) {
|
||||
if (aParts[i] && bParts[i]) {
|
||||
if (aParts[i] < bParts[i]) {
|
||||
return -1;
|
||||
}
|
||||
if (aParts[i] > bParts[i]) {
|
||||
return 1;
|
||||
}
|
||||
} else if (!aParts[i]) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class DocHelper {
|
||||
constructor(docs) {
|
||||
this.moduleLookup = {};
|
||||
this.modules = [];
|
||||
|
||||
docs.forEach(doc => {
|
||||
const moduleID = moduleIDFromLongname(doc.longname);
|
||||
if (!(moduleID in this.moduleLookup)) {
|
||||
const module = new ModuleDoc(moduleID);
|
||||
this.moduleLookup[moduleID] = module;
|
||||
this.modules.push(module);
|
||||
}
|
||||
|
||||
const module = this.moduleLookup[moduleID];
|
||||
module.processDoc(doc);
|
||||
});
|
||||
|
||||
this.modules.sort(byModuleId);
|
||||
this.modules.forEach(module => module.finalize());
|
||||
}
|
||||
}
|
||||
|
||||
let cachedDocs;
|
||||
let cachedHelper;
|
||||
|
||||
export function getHelper(docs) {
|
||||
if (docs !== cachedDocs) {
|
||||
if (cachedDocs) {
|
||||
console.warn('creating new doc helper'); // eslint-disable-line
|
||||
}
|
||||
cachedHelper = new DocHelper(docs);
|
||||
cachedDocs = docs;
|
||||
}
|
||||
|
||||
return cachedHelper;
|
||||
}
|
||||
9
site/util/typography.js
Normal file
9
site/util/typography.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Typography from 'typography';
|
||||
import theme from 'typography-theme-alton';
|
||||
|
||||
const typography = new Typography(theme);
|
||||
|
||||
export const baseSpacingPx = parseInt(theme.baseFontSize, 10);
|
||||
|
||||
export {theme};
|
||||
export default typography;
|
||||
Reference in New Issue
Block a user