var path = require("path"); var getSection = require("mdast-util-heading-range"); var unist = require("../unistHelpers"); var seeAlsoHeading = "See Also"; module.exports = { "initPhase": initPhase, "readPhase": readPhase, "aggPhase": aggPhase, "updatePhase": updatePhase } function initPhase(aggData) { aggData.saGraph = {}; aggData.saUpdateGraph = {}; } function readPhase(tree, pathname, aggData) { var saHeadingOffset = findSeeAlsoSection(tree); var saNode = []; if (saHeadingOffset !== -1) { // Skip over non-list parts. var s; for ( s = saHeadingOffset; (s < tree.children.length) && !unist.isListUnordered(tree.children[s]); s++ ); if ((s < tree.children.length) && unist.isListUnordered(tree.children[s])) { var list = tree.children[s]; for (var i = 0; i < list.children.length; i++) { var itemLink = getItemLinkInfo(list.children[i]); if (itemLink) { saNode.push(itemLink); } } } } aggData.saGraph[path.basename(pathname, ".md")] = saNode; } function aggPhase(aggData) { aggData.saUpdateGraph = tidyGraph(aggData.saGraph); } function updatePhase(tree, pathname, aggData) { var currNodeName = path.basename(pathname, ".md"); var currNodeArcs = aggData.saUpdateGraph[currNodeName]; if (currNodeArcs.length > 0) { var saListItems = []; for (var i = 0; i < currNodeArcs.length; i++) { var linkText = graphKeyToLinkName(currNodeArcs[i]); var linkTarget = currNodeArcs[i] + ".md"; var link = unist.makeLink(unist.makeText(linkText), linkTarget); saListItems.push(unist.makeListItem(link)); } var saHeadingOffset = findSeeAlsoSection(tree); if (saHeadingOffset !== -1) { // Skip over non-list parts. var s; for ( s = saHeadingOffset; (s < tree.children.length) && !unist.isListUnordered(tree.children[s]); s++ ); // Push all elements of the items array as if they were separate elements. Array.prototype.push.apply(tree.children[s].children, saListItems); } else { // This file doesn't currently have a See Also section, so add one at the end. tree.children.push(unist.makeHeading(unist.makeText(seeAlsoHeading), 2)); tree.children.push(unist.makeListUnordered(saListItems)); } } return true; } function graphKeyToLinkName(key) { var mainSections = key.split("."); mainSections[0] = tidyName(mainSections[0]); return mainSections.join(" "); } // Convert an Angular-style name (eg, "card-view") into one with correct spaces and uppercase (eg, "Card view"). function tidyName(name) { var result = name.replace(/-/g, " "); result = result.substr(0, 1).toUpperCase() + result.substr(1); return result; } function makeEmptySAList() { var result = []; } // Makes link symmetrical between items (ie, if A links to B but not the other way // around then it adds the missing link). function tidyGraph(graph) { var nodeNames = Object.keys(graph); var result = {}; for (var n = 0; n < nodeNames.length; n++) { result[nodeNames[n]] = []; } for (var n = 0; n < nodeNames.length; n++) { var currNodeName = nodeNames[n]; var currNodeArcs = graph[currNodeName]; for (var a = 0; a < currNodeArcs.length; a++) { var linkedNode = graph[currNodeArcs[a]]; var resultNode = result[currNodeArcs[a]]; if (!linkedNode) { console.log(`Warning: item '${currNodeArcs[a]}' (in See Also section of '${currNodeName}') has no corresponding file`); } else if (linkedNode.indexOf(currNodeName) === -1) { linkedNode.push(currNodeName); resultNode.push(currNodeName); } } } return result; } function findSeeAlsoSection(tree) { var i; for (i = 0; i < tree.children.length; i++) { var child = tree.children[i]; if (unist.isHeading(child) && (child.children[0].value.toLowerCase() === seeAlsoHeading.toLowerCase())) return i; } return -1; } function getItemLinkInfo(listItem) { var linkTarget = listItem.children[0].children[0].url; if (linkTarget.startsWith("http:") || linkTarget.startsWith("#") || !linkTarget.endsWith(".md")) return null; else return path.basename(linkTarget, ".md"); }