build(aio): map H3 headings into H4 headings for certain templates (#24000)

The sections such as methods and decorator options are already headed
by a H3 heading so we need to map the H3 headings in the API doc source
down to H4 headings.

This commit includes general heading mapping functionality accessible via
the `marked` Nunjucks filter.

PR Close #24000
This commit is contained in:
Pete Bacon Darwin 2018-05-18 15:07:11 +01:00 committed by Miško Hevery
parent e371b226fa
commit 77309e2ea4
6 changed files with 107 additions and 16 deletions

View File

@ -5,5 +5,5 @@ var Package = require('dgeni').Package;
* @description Overrides the renderMarkdown service with an implementation based on remark
*/
module.exports = new Package('remark', ['nunjucks'])
.factory(require('./services/markedNunjucksFilter'))
.factory(require('./services/renderMarkdown'));

View File

@ -0,0 +1,12 @@
/**
* Convert the value, as markdown, into HTML
* @param headingMappings A map of headings to convert (e.g. from h3 to h4).
*/
module.exports = function markedNunjucksFilter(renderMarkdown) {
return {
name: 'marked',
process: function(str, headingMappings) {
return str && renderMarkdown(str, headingMappings);
}
};
};

View File

@ -0,0 +1,31 @@
const visit = require('unist-util-visit');
function headingToLevel(heading) {
const match = /^h(\d+)/.exec(heading);
return match ? match[1] : '0';
}
function parseMappings(mappings) {
const mapping = {};
Object.keys(mappings).forEach(key => mapping[headingToLevel(key)] = headingToLevel(mappings[key]));
return mapping;
}
module.exports = function mapHeadings(mappings) {
const headings = parseMappings(mappings || {});
return () => ast => {
const nodesToFix = [];
Object.keys(headings).forEach(heading => {
visit(ast, 'heading', node => {
if (node.depth === Number(heading)) {
nodesToFix.push(node);
}
});
});
// Update the depth of the matched nodes
nodesToFix.forEach(node => node.depth = headings[node.depth]);
return ast;
};
};

View File

@ -1,25 +1,29 @@
const remark = require('remark');
const html = require('remark-html');
const code = require('./handlers/code');
const mapHeadings = require('./plugins/mapHeadings');
/**
* @dgService renderMarkdown
* @description
* Render the markdown in the given string as HTML.
* @param headingMap A map of headings to convert.
* E.g. `{h3: 'h4'} will map heading 3 level into heading 4.
*/
module.exports = function renderMarkdown() {
const handlers = { code };
const renderer = remark()
.use(inlineTagDefs)
.use(noIndentedCodeBlocks)
.use(plainHTMLBlocks)
// USEFUL DEBUGGING CODE
// .use(() => tree => {
// console.log(require('util').inspect(tree, { colors: true, depth: 4 }));
// })
.use(html, { handlers });
return function renderMarkdownImpl(content, headingMap) {
const renderer = remark()
.use(inlineTagDefs)
.use(noIndentedCodeBlocks)
.use(plainHTMLBlocks)
// USEFUL DEBUGGING CODE
// .use(() => tree => {
// console.log(require('util').inspect(tree, { colors: true, depth: 4 }));
// })
.use(mapHeadings(headingMap))
.use(html, { handlers: { code } });
return function renderMarkdownImpl(content) {
return renderer.processSync(content).toString();
};

View File

@ -95,4 +95,36 @@ describe('remark: renderMarkdown service', () => {
'</code-example>\n'
);
});
it('should map heading levels as specified', () => {
const content =
'# heading 1\n' +
'\n' +
'some paragraph\n' +
'\n' +
'## heading 2a\n' +
'\n' +
'some paragraph\n' +
'\n' +
'### heading 3\n' +
'\n' +
'some paragraph\n' +
'\n' +
'## heading 2b\n' +
'\n' +
'some paragraph\n' +
'\n';
const headingMappings = { h2: 'h3', h3: 'h5' };
const output = renderMarkdown(content, headingMappings);
expect(output).toEqual(
'<h1>heading 1</h1>\n' +
'<p>some paragraph</p>\n' +
'<h3>heading 2a</h3>\n' +
'<p>some paragraph</p>\n' +
'<h5>heading 3</h5>\n' +
'<p>some paragraph</p>\n' +
'<h3>heading 2b</h3>\n' +
'<p>some paragraph</p>\n'
);
});
});

View File

@ -113,12 +113,24 @@
</td>
</tr>
{% endif %}
{% if method.description %}<tr>
{% if method.description -%}
<tr>
<td class="description">
{$ method.description | marked $}
{$ method.description | marked({ h3: 'h4' }) $}
</td>
</tr>{% endif %}
</tbody>
</tr>
{%- endif %}
{% if method.usageNotes -%}
<tr>
<td class="usage-notes">
<h3>Usage Notes</h3>
{$ method.usageNotes | marked({ h3: 'h4' }) $}
</td>
</tr>
{%- endif %}
</tbody>
</table>
{% endmacro -%}