build(aio): auto-link more code items
We now parse all code blocks, after they have been rendered by dgeni and insert links to API docs that match "words" in the code.
This commit is contained in:
parent
5baa069b16
commit
9d93c859d7
@ -128,4 +128,5 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
|
|||||||
convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
||||||
postProcessHtml.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
postProcessHtml.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
||||||
autoLinkCode.docTypes = DOCS_TO_CONVERT;
|
autoLinkCode.docTypes = DOCS_TO_CONVERT;
|
||||||
|
autoLinkCode.codeElements = ['code', 'code-example', 'code-pane'];
|
||||||
});
|
});
|
||||||
|
@ -3,34 +3,69 @@ const is = require('hast-util-is-element');
|
|||||||
const textContent = require('hast-util-to-string');
|
const textContent = require('hast-util-to-string');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically add in a link to the relevant document for simple
|
* Automatically add in a link to the relevant document for code blocks.
|
||||||
* code blocks, e.g. `<code>MyClass</code>` becomes
|
* E.g. `<code>MyClass</code>` becomes `<code><a href="path/to/myclass">MyClass</a></code>`
|
||||||
* `<code><a href="path/to/myclass">MyClass</a></code>`
|
|
||||||
*
|
*
|
||||||
* @property docTypes an array of strings. Only docs that have one of these docTypes
|
* @property docTypes an array of strings.
|
||||||
* will be linked to.
|
* Only docs that have one of these docTypes will be linked to.
|
||||||
* Usually set to the API exported docTypes, e.g. "class", "function", "directive", etc.
|
* Usually set to the API exported docTypes, e.g. "class", "function", "directive", etc.
|
||||||
|
*
|
||||||
|
* @property codeElements an array of strings.
|
||||||
|
* Only text contained in these elements will be linked to.
|
||||||
|
* Usually set to "code" but also "code-example" for angular.io.
|
||||||
*/
|
*/
|
||||||
module.exports = function autoLinkCode(getDocFromAlias) {
|
module.exports = function autoLinkCode(getDocFromAlias) {
|
||||||
autoLinkCodeImpl.docTypes = [];
|
autoLinkCodeImpl.docTypes = [];
|
||||||
|
autoLinkCodeImpl.codeElements = ['code'];
|
||||||
return autoLinkCodeImpl;
|
return autoLinkCodeImpl;
|
||||||
|
|
||||||
function autoLinkCodeImpl() {
|
function autoLinkCodeImpl() {
|
||||||
return (ast) => {
|
return (ast) => {
|
||||||
visit(ast, (node, ancestors) => {
|
visit(ast, 'element', (node, ancestors) => {
|
||||||
if (is(node, 'code') && ancestors.every(ancestor => !is(ancestor, 'a'))) {
|
// Only interested in code elements that are not inside links
|
||||||
const docs = getDocFromAlias(textContent(node));
|
if (autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType)) &&
|
||||||
if (docs.length === 1 && autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1) {
|
ancestors.every(ancestor => !is(ancestor, 'a'))) {
|
||||||
const link = {
|
visit(node, 'text', (node, ancestors) => {
|
||||||
type: 'element',
|
// Only interested in text nodes that are not inside links
|
||||||
tagName: 'a',
|
if (ancestors.every(ancestor => !is(ancestor, 'a'))) {
|
||||||
properties: { href: docs[0].path },
|
|
||||||
children: node.children
|
const parent = ancestors[ancestors.length-1];
|
||||||
};
|
const index = parent.children.indexOf(node);
|
||||||
node.children = [link];
|
|
||||||
|
// Can we convert the whole text node into a doc link?
|
||||||
|
const docs = getDocFromAlias(node.value);
|
||||||
|
if (foundValidDoc(docs)) {
|
||||||
|
parent.children.splice(index, 1, createLinkNode(docs[0], node.value));
|
||||||
|
} else {
|
||||||
|
// Parse the text for words that we can convert to links
|
||||||
|
const nodes = textContent(node).split(/([A-Za-z0-9_]+)/)
|
||||||
|
.filter(word => word.length)
|
||||||
|
.map(word => {
|
||||||
|
const docs = getDocFromAlias(word);
|
||||||
|
return foundValidDoc(docs) ?
|
||||||
|
createLinkNode(docs[0], word) : // Create a link wrapping the text node.
|
||||||
|
{ type: 'text', value: word }; // this is just text so push a new text node
|
||||||
|
});
|
||||||
|
|
||||||
|
// Replace the text node with the links and leftover text nodes
|
||||||
|
Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function foundValidDoc(docs) {
|
||||||
|
return docs.length === 1 && autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLinkNode(doc, text) {
|
||||||
|
return {
|
||||||
|
type: 'element',
|
||||||
|
tagName: 'a',
|
||||||
|
properties: { href: doc.path, class: 'code-anchor' },
|
||||||
|
children: [{ type: 'text', value: text }]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@ describe('autoLinkCode post-processor', () => {
|
|||||||
const dgeni = new Dgeni([testPackage]);
|
const dgeni = new Dgeni([testPackage]);
|
||||||
const injector = dgeni.configureInjector();
|
const injector = dgeni.configureInjector();
|
||||||
autoLinkCode = injector.get('autoLinkCode');
|
autoLinkCode = injector.get('autoLinkCode');
|
||||||
autoLinkCode.docTypes = ['class', 'pipe'];
|
autoLinkCode.docTypes = ['class', 'pipe', 'function', 'const'];
|
||||||
aliasMap = injector.get('aliasMap');
|
aliasMap = injector.get('aliasMap');
|
||||||
processor = injector.get('postProcessHtml');
|
processor = injector.get('postProcessHtml');
|
||||||
processor.docTypes = ['test-doc'];
|
processor.docTypes = ['test-doc'];
|
||||||
@ -20,14 +20,14 @@ describe('autoLinkCode post-processor', () => {
|
|||||||
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
const doc = { docType: 'test-doc', renderedContent: '<code>MyClass</code>' };
|
const doc = { docType: 'test-doc', renderedContent: '<code>MyClass</code>' };
|
||||||
processor.$process([doc]);
|
processor.$process([doc]);
|
||||||
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass">MyClass</a></code>');
|
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass" class="code-anchor">MyClass</a></code>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should insert an anchor into every code item that matches an alias of an API doc', () => {
|
it('should insert an anchor into every code item that matches an alias of an API doc', () => {
|
||||||
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass', 'foo.MyClass'], path: 'a/b/myclass' });
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass', 'foo.MyClass'], path: 'a/b/myclass' });
|
||||||
const doc = { docType: 'test-doc', renderedContent: '<code>foo.MyClass</code>' };
|
const doc = { docType: 'test-doc', renderedContent: '<code>foo.MyClass</code>' };
|
||||||
processor.$process([doc]);
|
processor.$process([doc]);
|
||||||
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass">foo.MyClass</a></code>');
|
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass" class="code-anchor">foo.MyClass</a></code>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore code items that do not match a link to an API doc', () => {
|
it('should ignore code items that do not match a link to an API doc', () => {
|
||||||
@ -43,4 +43,35 @@ describe('autoLinkCode post-processor', () => {
|
|||||||
processor.$process([doc]);
|
processor.$process([doc]);
|
||||||
expect(doc.renderedContent).toEqual('<a href="..."><div><code>MyClass</code></div></a>');
|
expect(doc.renderedContent).toEqual('<a href="..."><div><code>MyClass</code></div></a>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should ignore code items match an API doc but are not in the list of acceptable docTypes', () => {
|
||||||
|
aliasMap.addDoc({ docType: 'directive', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code>MyClass</code>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code>MyClass</code>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert anchors for individual text nodes within a code block', () => {
|
||||||
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code><span>MyClass</span><span>MyClass</span></code>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code><span><a href="a/b/myclass" class="code-anchor">MyClass</a></span><span><a href="a/b/myclass" class="code-anchor">MyClass</a></span></code>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert anchors for words that match within text nodes in a code block', () => {
|
||||||
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
aliasMap.addDoc({ docType: 'function', id: 'myFunc', aliases: ['myFunc'], path: 'ng/myfunc' });
|
||||||
|
aliasMap.addDoc({ docType: 'const', id: 'MY_CONST', aliases: ['MY_CONST'], path: 'ng/my_const' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code>myFunc() {\n return new MyClass(MY_CONST);\n}</code>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code><a href="ng/myfunc" class="code-anchor">myFunc</a>() {\n return new <a href="a/b/myclass" class="code-anchor">MyClass</a>(<a href="ng/my_const" class="code-anchor">MY_CONST</a>);\n}</code>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with custom elements', () => {
|
||||||
|
autoLinkCode.codeElements = ['code-example'];
|
||||||
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code-example>MyClass</code-example>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code-example><a href="a/b/myclass" class="code-anchor">MyClass</a></code-example>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user