diff --git a/aio/tools/transforms/angular-api-package/index.js b/aio/tools/transforms/angular-api-package/index.js
index 1c7c78c9b9..b54c75dd2a 100644
--- a/aio/tools/transforms/angular-api-package/index.js
+++ b/aio/tools/transforms/angular-api-package/index.js
@@ -128,4 +128,5 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
postProcessHtml.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
autoLinkCode.docTypes = DOCS_TO_CONVERT;
+ autoLinkCode.codeElements = ['code', 'code-example', 'code-pane'];
});
diff --git a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js
index 79b5f31668..3e4982dac3 100644
--- a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js
+++ b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js
@@ -3,34 +3,69 @@ const is = require('hast-util-is-element');
const textContent = require('hast-util-to-string');
/**
- * Automatically add in a link to the relevant document for simple
- * code blocks, e.g. `MyClass
` becomes
- * `MyClass
`
+ * Automatically add in a link to the relevant document for code blocks.
+ * E.g. `MyClass
` becomes `MyClass
`
*
- * @property docTypes an array of strings. Only docs that have one of these docTypes
- * will be linked to.
+ * @property docTypes an array of strings.
+ * 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.
+ *
+ * @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) {
autoLinkCodeImpl.docTypes = [];
+ autoLinkCodeImpl.codeElements = ['code'];
return autoLinkCodeImpl;
function autoLinkCodeImpl() {
return (ast) => {
- visit(ast, (node, ancestors) => {
- if (is(node, 'code') && ancestors.every(ancestor => !is(ancestor, 'a'))) {
- const docs = getDocFromAlias(textContent(node));
- if (docs.length === 1 && autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1) {
- const link = {
- type: 'element',
- tagName: 'a',
- properties: { href: docs[0].path },
- children: node.children
- };
- node.children = [link];
- }
+ visit(ast, 'element', (node, ancestors) => {
+ // Only interested in code elements that are not inside links
+ if (autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType)) &&
+ ancestors.every(ancestor => !is(ancestor, 'a'))) {
+ visit(node, 'text', (node, ancestors) => {
+ // Only interested in text nodes that are not inside links
+ if (ancestors.every(ancestor => !is(ancestor, 'a'))) {
+
+ const parent = ancestors[ancestors.length-1];
+ const index = parent.children.indexOf(node);
+
+ // 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 }]
+ };
+ }
};
diff --git a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js
index eec9b0a930..de96946821 100644
--- a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js
+++ b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js
@@ -9,7 +9,7 @@ describe('autoLinkCode post-processor', () => {
const dgeni = new Dgeni([testPackage]);
const injector = dgeni.configureInjector();
autoLinkCode = injector.get('autoLinkCode');
- autoLinkCode.docTypes = ['class', 'pipe'];
+ autoLinkCode.docTypes = ['class', 'pipe', 'function', 'const'];
aliasMap = injector.get('aliasMap');
processor = injector.get('postProcessHtml');
processor.docTypes = ['test-doc'];
@@ -20,14 +20,14 @@ describe('autoLinkCode post-processor', () => {
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
const doc = { docType: 'test-doc', renderedContent: 'MyClass
' };
processor.$process([doc]);
- expect(doc.renderedContent).toEqual('MyClass
');
+ expect(doc.renderedContent).toEqual('MyClass
');
});
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' });
const doc = { docType: 'test-doc', renderedContent: 'foo.MyClass
' };
processor.$process([doc]);
- expect(doc.renderedContent).toEqual('foo.MyClass
');
+ expect(doc.renderedContent).toEqual('foo.MyClass
');
});
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]);
expect(doc.renderedContent).toEqual('MyClass
');
});
+
+ 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: 'MyClass
' };
+ processor.$process([doc]);
+ expect(doc.renderedContent).toEqual('MyClass
');
+ });
+
+ 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: 'MyClassMyClass
' };
+ processor.$process([doc]);
+ expect(doc.renderedContent).toEqual('MyClassMyClass
');
+ });
+
+ 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: 'myFunc() {\n return new MyClass(MY_CONST);\n}
' };
+ processor.$process([doc]);
+ expect(doc.renderedContent).toEqual('myFunc() {\n return new MyClass(MY_CONST);\n}
');
+ });
+
+ 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: 'MyClass' };
+ processor.$process([doc]);
+ expect(doc.renderedContent).toEqual('MyClass');
+ });
});