| 
									
										
										
										
											2017-08-18 14:48:40 +01:00
										 |  |  | const visit = require('unist-util-visit-parents'); | 
					
						
							| 
									
										
										
										
											2017-05-10 18:34:19 +01:00
										 |  |  | const is = require('hast-util-is-element'); | 
					
						
							|  |  |  | const textContent = require('hast-util-to-string'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |  * Automatically add in a link to the relevant document for code blocks. | 
					
						
							|  |  |  |  * E.g. `<code>MyClass</code>` becomes `<code><a href="path/to/myclass">MyClass</a></code>` | 
					
						
							| 
									
										
										
										
											2017-05-10 18:34:19 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |  * @property docTypes an array of strings. | 
					
						
							|  |  |  |  * Only docs that have one of these docTypes will be linked to. | 
					
						
							| 
									
										
										
										
											2017-05-10 18:34:19 +01:00
										 |  |  |  * Usually set to the API exported docTypes, e.g. "class", "function", "directive", etc. | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-11-15 23:30:09 +00:00
										 |  |  |  * @property customFilters array of functions `(docs, words, wordIndex) => docs` that will filter | 
					
						
							|  |  |  |  * out docs where a word should not link to a doc. | 
					
						
							|  |  |  |  *   - `docs` is the array of docs that match the link `word` | 
					
						
							|  |  |  |  *   - `words` is the collection of words parsed from the code text | 
					
						
							|  |  |  |  *   - `wordIndex` is the index of the current `word` for which we are finding a link | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |  * @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. | 
					
						
							| 
									
										
										
										
											2017-05-10 18:34:19 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | module.exports = function autoLinkCode(getDocFromAlias) { | 
					
						
							|  |  |  |   autoLinkCodeImpl.docTypes = []; | 
					
						
							| 
									
										
										
										
											2017-11-15 23:30:09 +00:00
										 |  |  |   autoLinkCodeImpl.customFilters = []; | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |   autoLinkCodeImpl.codeElements = ['code']; | 
					
						
							| 
									
										
										
										
											2017-05-10 18:34:19 +01:00
										 |  |  |   return autoLinkCodeImpl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function autoLinkCodeImpl()  { | 
					
						
							|  |  |  |     return (ast) => { | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |       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
 | 
					
						
							| 
									
										
										
										
											2018-02-28 14:52:53 +00:00
										 |  |  |                 const nodes = textContent(node).split(/([A-Za-z0-9_.-]+)/) | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |                   .filter(word => word.length) | 
					
						
							| 
									
										
										
										
											2017-11-15 23:30:09 +00:00
										 |  |  |                   .map((word, index, words) => { | 
					
						
							|  |  |  |                     // remove docs that fail the custom filter tests
 | 
					
						
							|  |  |  |                     const filteredDocs = autoLinkCodeImpl.customFilters.reduce((docs, filter) => filter(docs, words, index), getDocFromAlias(word)); | 
					
						
							|  |  |  |                     return foundValidDoc(filteredDocs) ? | 
					
						
							|  |  |  |                               createLinkNode(filteredDocs[0], word) : // Create a link wrapping the text node.
 | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |                               { 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)); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2017-05-10 18:34:19 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |   function foundValidDoc(docs) { | 
					
						
							| 
									
										
										
										
											2018-05-16 17:41:32 +01:00
										 |  |  |     return docs.length === 1 && | 
					
						
							|  |  |  |            !docs[0].internal && | 
					
						
							|  |  |  |            autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1; | 
					
						
							| 
									
										
										
										
											2017-09-09 09:15:08 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function createLinkNode(doc, text) { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       type: 'element', | 
					
						
							|  |  |  |       tagName: 'a', | 
					
						
							|  |  |  |       properties: { href: doc.path, class: 'code-anchor' }, | 
					
						
							|  |  |  |       children: [{ type: 'text', value: text }] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-05-10 18:34:19 +01:00
										 |  |  | }; |