[ADF-2979] Updated doc tools to avoid erasing MD docs with blank JSDocs in services (#3347)

* [ADF-2979] Update to ignore blank JSDocs for method signature

* [ADF-2979] Finished adding blank JSDoc check for services
This commit is contained in:
Andy Stark
2018-05-18 14:30:26 +01:00
committed by Eugenio Romano
parent 592ad185e7
commit 73bc62ae8f
6 changed files with 390 additions and 21 deletions

View File

@@ -17,8 +17,7 @@
"toc"
],
"dev": [
"tsInfo",
"typeLinker"
"tsInfo"
]
}
}

View File

@@ -26,6 +26,34 @@ var MDNav = /** @class */ (function () {
}
return new MDNav(this.root, this.root.children.length);
};
MDNav.prototype.findAll = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
if (!this.root || !this.root.children) {
return [];
}
var result = [];
var currIndex = 0;
for (var i = this.pos; i < this.root.children.length; i++) {
var child = this.root.children[i];
if (test(child)) {
if (currIndex === index) {
result.push(new MDNav(this.root, i));
}
else {
currIndex++;
}
}
}
return result;
};
MDNav.prototype.emph = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "emphasis" && test(h);
}, index);
};
MDNav.prototype.heading = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
@@ -33,6 +61,48 @@ var MDNav = /** @class */ (function () {
return h.type === "heading" && test(h);
}, index);
};
MDNav.prototype.html = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "html" && test(h);
}, index);
};
MDNav.prototype.list = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "list" && test(h);
}, index);
};
MDNav.prototype.listItem = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "listItem" && test(h);
}, index);
};
MDNav.prototype.listItems = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.findAll(function (h) {
return h.type === "listItem" && test(h);
}, index);
};
MDNav.prototype.paragraph = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "paragraph" && test(h);
}, index);
};
MDNav.prototype.strong = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "strong" && test(h);
}, index);
};
MDNav.prototype.table = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
@@ -40,13 +110,6 @@ var MDNav = /** @class */ (function () {
return h.type === "table" && test(h);
}, index);
};
MDNav.prototype.text = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "text" && test(h);
}, index);
};
MDNav.prototype.tableRow = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
@@ -61,6 +124,13 @@ var MDNav = /** @class */ (function () {
return h.type === "tableCell" && test(h);
}, index);
};
MDNav.prototype.text = function (test, index) {
if (test === void 0) { test = function () { return true; }; }
if (index === void 0) { index = 0; }
return this.find(function (h) {
return h.type === "text" && test(h);
}, index);
};
Object.defineProperty(MDNav.prototype, "item", {
get: function () {
if (!this.root || !this.root.children) {
@@ -89,6 +159,18 @@ var MDNav = /** @class */ (function () {
enumerable: true,
configurable: true
});
Object.defineProperty(MDNav.prototype, "value", {
get: function () {
if (this.item && this.item["value"]) {
return this.item.value;
}
else {
return undefined;
}
},
enumerable: true,
configurable: true
});
return MDNav;
}());
exports.MDNav = MDNav;

View File

@@ -26,6 +26,38 @@ export class MDNav {
}
findAll(test: (element: any) => boolean = () => true, index: number = 0): MDNav[] {
if (!this.root || !this.root.children) {
return [];
}
let result = [];
let currIndex = 0;
for (let i = this.pos; i < this.root.children.length; i++) {
let child = this.root.children[i];
if (test(child)) {
if (currIndex === index) {
result.push(new MDNav(this.root, i));
} else {
currIndex++;
}
}
}
return result;
}
emph(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "emphasis" && test(h);
}, index);
}
heading(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "heading" && test(h);
@@ -33,16 +65,49 @@ export class MDNav {
}
table(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
html(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "table" && test(h);
return h.type === "html" && test(h);
}, index);
}
text(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
list(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "text" && test(h);
return h.type === "list" && test(h);
}, index);
}
listItem(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "listItem" && test(h);
}, index);
}
listItems(test: (element: any) => boolean = () => true, index: number = 0): MDNav[] {
return this.findAll((h) => {
return h.type === "listItem" && test(h);
}, index);
}
paragraph(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "paragraph" && test(h);
}, index);
}
strong(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "strong" && test(h);
}, index);
}
table(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "table" && test(h);
}, index);
}
@@ -60,6 +125,14 @@ export class MDNav {
}, index);
}
text(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
return this.find((h) => {
return h.type === "text" && test(h);
}, index);
}
get item(): any {
if (!this.root || !this.root.children) {
return undefined;
@@ -75,7 +148,16 @@ export class MDNav {
}
get childNav() {
get childNav(): MDNav {
return new MDNav(this.item);
}
get value(): any {
if (this.item && this.item["value"]) {
return this.item.value;
} else {
return undefined;
}
}
}

View File

@@ -2,10 +2,10 @@
{% if hasMethods %}
### Methods
{% each methods as meth %}- `{{meth.name}}{{{meth.signature}}{% if meth.returnsSomething %}: {{{meth.returnType}}}{% endif %}`<br/>
{% each methods as meth %}- **{{meth.name}}**{{{meth.signature}}}{% if meth.returnsSomething %}: `{{{meth.returnType}}}`{% endif %}<br/>
{{meth.docText}}
{% each meth.params as param %}
- `{{{param.combined}}}` - {% if param.isOptional %}(Optional){% endif %}{{{param.docText}}}
- *{{{param.name}}}:* `{{{param.type}}}` - {% if param.isOptional %}(Optional){% endif %}{{{param.docText}}}
{% endeach %}
{% if meth.returnsSomething %}
- **Returns** `{{{meth.returnType}}}` - {{{meth.returnDocText}}}

View File

@@ -196,8 +196,6 @@ function aggPhase(aggData) {
}
exports.aggPhase = aggPhase;
function updatePhase(tree, pathname, aggData, errorMessages) {
var inputMD = getPropDocsFromMD(tree, "Properties", 3);
var outputMD = getPropDocsFromMD(tree, "Events", 2);
var compName = angNameToClassName(path.basename(pathname, ".md"));
var classRef = aggData.projData.findReflectionByName(compName);
if (!classRef) {
@@ -208,7 +206,13 @@ function updatePhase(tree, pathname, aggData, errorMessages) {
var classType = compName.match(/component|directive|service/i);
if (classType) {
// Copy docs back from the .md file when the JSDocs are empty.
var inputMD = getPropDocsFromMD(tree, "Properties", 3);
var outputMD = getPropDocsFromMD(tree, "Events", 2);
updatePropDocsFromMD(compData, inputMD, outputMD, errorMessages);
if (classType === "service") {
var methodMD = getMethodDocsFromMD(tree);
updateMethodDocsFromMD(compData, methodMD, errorMessages);
}
var templateName = path.resolve(templateFolder, classType + ".combyne");
var templateSource = fs.readFileSync(templateName, "utf8");
var template = combyne(templateSource);
@@ -277,6 +281,74 @@ function getPropDocsFromMD(tree, sectionHeading, docsColumn) {
}
return result;
}
function getMethodDocsFromMD(tree) {
var result = {};
var nav = new mdNav_1.MDNav(tree);
var classMemHeading = nav
.heading(function (h) {
return (h.children[0].type === "text") && (h.children[0].value === "Class members");
});
var methListItems = classMemHeading
.heading(function (h) {
return (h.children[0].type === "text") && (h.children[0].value === "Methods");
}).list().childNav;
var methItem = methListItems
.listItem();
var i = 0;
while (!methItem.empty) {
var methNameSection = methItem.childNav
.paragraph().childNav
.strong().childNav;
var methName = '';
// Method docs must be in "new" format with names and types styled separately.
if (!methNameSection.empty) {
methName = methNameSection.text().item.value;
var methDoc = methItem.childNav
.paragraph().childNav
.html()
.text().item.value;
var params = getMDMethodParams(methItem);
result[methName] = {
"docText": methDoc.replace(/^\n/, ""),
"params": params
};
}
i++;
methItem = methListItems
.listItem(function (l) { return true; }, i);
}
/*
let newRoot = unist.makeRoot([methList.item]);
console.log(remark().use(frontMatter, {type: 'yaml', fence: '---'}).data("settings", {paddedTable: false, gfm: false}).stringify(tree));
*/
return result;
}
function getMDMethodParams(methItem) {
var result = {};
var paramList = methItem.childNav.list().childNav;
var paramListItems = paramList
.listItems();
paramListItems.forEach(function (paramListItem) {
var paramNameNode = paramListItem.childNav
.paragraph().childNav
.emph().childNav;
var paramName;
if (!paramNameNode.empty) {
paramName = paramNameNode.text().item.value.replace(/:/, "");
}
else {
paramName = paramListItem.childNav
.paragraph().childNav
.strong().childNav
.text().item.value;
}
var paramDoc = paramListItem.childNav
.paragraph().childNav
.text(function (t) { return true; }, 1).item.value;
result[paramName] = paramDoc.replace(/^[ -]+/, "");
});
return result;
}
function updatePropDocsFromMD(comp, inputDocs, outputDocs, errorMessages) {
comp.properties.forEach(function (prop) {
var propMDDoc;
@@ -293,3 +365,19 @@ function updatePropDocsFromMD(comp, inputDocs, outputDocs, errorMessages) {
}
});
}
function updateMethodDocsFromMD(comp, methodDocs, errorMessages) {
comp.methods.forEach(function (meth) {
var currMethMD = methodDocs[meth.name];
// If JSDocs are empty but MD docs aren't then the Markdown is presumably more up-to-date.
if (!meth.docText && currMethMD.docText) {
meth.docText = currMethMD.docText;
errorMessages.push("Warning: empty JSDocs for method sig \"" + meth.name + "\" may need sync with the .md file.");
}
meth.params.forEach(function (param) {
if (!param.docText && currMethMD.params[param.name]) {
param.docText = currMethMD.params[param.name];
errorMessages.push("Warning: empty JSDocs for parameter \"" + param.name + " (" + meth.name + ")\" may need sync with the .md file.");
}
});
});
}

View File

@@ -285,9 +285,6 @@ export function aggPhase(aggData) {
export function updatePhase(tree, pathname, aggData, errorMessages) {
let inputMD = getPropDocsFromMD(tree, "Properties", 3);
let outputMD = getPropDocsFromMD(tree, "Events", 2);
let compName = angNameToClassName(path.basename(pathname, ".md"));
let classRef = aggData.projData.findReflectionByName(compName);
@@ -301,7 +298,14 @@ export function updatePhase(tree, pathname, aggData, errorMessages) {
if (classType) {
// Copy docs back from the .md file when the JSDocs are empty.
let inputMD = getPropDocsFromMD(tree, "Properties", 3);
let outputMD = getPropDocsFromMD(tree, "Events", 2);
updatePropDocsFromMD(compData, inputMD, outputMD, errorMessages);
if (classType === "service") {
let methodMD = getMethodDocsFromMD(tree);
updateMethodDocsFromMD(compData, methodMD, errorMessages);
}
let templateName = path.resolve(templateFolder, classType + ".combyne");
let templateSource = fs.readFileSync(templateName, "utf8");
@@ -401,6 +405,99 @@ function getPropDocsFromMD(tree, sectionHeading, docsColumn) {
}
function getMethodDocsFromMD(tree) {
let result = {}
let nav = new MDNav(tree);
let classMemHeading = nav
.heading(h => {
return (h.children[0].type === "text") && (h.children[0].value === "Class members");
});
let methListItems = classMemHeading
.heading(h => {
return (h.children[0].type === "text") && (h.children[0].value === "Methods");
}).list().childNav;
let methItem = methListItems
.listItem();
let i = 0;
while (!methItem.empty) {
let methNameSection = methItem.childNav
.paragraph().childNav
.strong().childNav;
let methName = '';
// Method docs must be in "new" format with names and types styled separately.
if (!methNameSection.empty) {
methName = methNameSection.text().item.value;
let methDoc = methItem.childNav
.paragraph().childNav
.html()
.text().item.value;
let params = getMDMethodParams(methItem);
result[methName] = {
"docText": methDoc.replace(/^\n/, ""),
"params": params
};
}
i++;
methItem = methListItems
.listItem(l=>true, i);
}
/*
let newRoot = unist.makeRoot([methList.item]);
console.log(remark().use(frontMatter, {type: 'yaml', fence: '---'}).data("settings", {paddedTable: false, gfm: false}).stringify(tree));
*/
return result;
}
function getMDMethodParams(methItem: MDNav) {
let result = {};
let paramList = methItem.childNav.list().childNav;
let paramListItems = paramList
.listItems();
paramListItems.forEach(paramListItem => {
let paramNameNode = paramListItem.childNav
.paragraph().childNav
.emph().childNav;
let paramName;
if (!paramNameNode.empty) {
paramName = paramNameNode.text().item.value.replace(/:/, "");
} else {
paramName = paramListItem.childNav
.paragraph().childNav
.strong().childNav
.text().item.value;
}
let paramDoc = paramListItem.childNav
.paragraph().childNav
.text(t=>true, 1).item.value;
result[paramName] = paramDoc.replace(/^[ -]+/, "");
});
return result;
}
function updatePropDocsFromMD(comp: ComponentInfo, inputDocs, outputDocs, errorMessages) {
comp.properties.forEach(prop => {
let propMDDoc: string;
@@ -417,4 +514,25 @@ function updatePropDocsFromMD(comp: ComponentInfo, inputDocs, outputDocs, errorM
errorMessages.push(`Warning: empty JSDocs for property "${prop.name}" may need sync with the .md file.`);
}
});
}
function updateMethodDocsFromMD(comp: ComponentInfo, methodDocs, errorMessages) {
comp.methods.forEach(meth => {
let currMethMD = methodDocs[meth.name]
// If JSDocs are empty but MD docs aren't then the Markdown is presumably more up-to-date.
if (!meth.docText && currMethMD.docText) {
meth.docText = currMethMD.docText;
errorMessages.push(`Warning: empty JSDocs for method sig "${meth.name}" may need sync with the .md file.`);
}
meth.params.forEach(param => {
if (!param.docText && currMethMD.params[param.name])
{
param.docText = currMethMD.params[param.name];
errorMessages.push(`Warning: empty JSDocs for parameter "${param.name} (${meth.name})" may need sync with the .md file.`);
}
});
});
}