Skip to content

Commit 9ef6e23

Browse files
addaleaxMyles Borins
authored and
Myles Borins
committed
tools: make sure doctool anchors respect includes
Previously, output files which were created using includes (notably, the single-page all.html) had basically broken internal links all over the place because references like `errors.html#errors_class_error` are being used, yet `id` attributes were generated that looked like `all_class_error`. This PR adds generation of comments from the include preprocessor that indicate from which file the current markdown bits come and lets the HTML output generation take advantage of that so that more appropriate `id` attributes can be generated. PR-URL: #6943 Reviewed-By: Robert Jefe Lindstaedt <[email protected]> Reviewed-By: Daniel Wang <[email protected]>
1 parent f9f85a0 commit 9ef6e23

File tree

6 files changed

+59
-18
lines changed

6 files changed

+59
-18
lines changed

test/doctool/test-doctool-html.js

+30-16
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const assert = require('assert');
55
const fs = require('fs');
66
const path = require('path');
77

8+
const processIncludes = require('../../tools/doc/preprocess.js');
89
const html = require('../../tools/doc/html.js');
910

1011
// Test data is a list of objects with two properties.
@@ -53,30 +54,43 @@ const testData = [
5354
'<p>Describe <code>Something</code> in more detail here. ' +
5455
'</p>'
5556
},
57+
{
58+
file: path.join(common.fixturesDir, 'doc_with_includes.md'),
59+
html: '<!-- [start-include:doc_inc_1.md] -->' +
60+
'<p>Look <a href="doc_inc_2.html#doc_inc_2_foobar">here</a>!</p>' +
61+
'<!-- [end-include:doc_inc_1.md] -->' +
62+
'<!-- [start-include:doc_inc_2.md] -->' +
63+
'<h1>foobar<span><a class="mark" href="#doc_inc_2_foobar" ' +
64+
'id="doc_inc_2_foobar">#</a></span></h1>' +
65+
'<p>I exist and am being linked to.</p>' +
66+
'<!-- [end-include:doc_inc_2.md] -->'
67+
},
5668
];
5769

5870
testData.forEach(function(item) {
5971
// Normalize expected data by stripping whitespace
6072
const expected = item.html.replace(/\s/g, '');
6173

62-
fs.readFile(item.file, 'utf8', common.mustCall(function(err, input) {
74+
fs.readFile(item.file, 'utf8', common.mustCall((err, input) => {
6375
assert.ifError(err);
64-
html(
65-
{
66-
input: input,
67-
filename: 'foo',
68-
template: 'doc/template.html',
69-
nodeVersion: process.version,
70-
},
76+
processIncludes(item.file, input, common.mustCall((err, preprocessed) => {
77+
assert.ifError(err);
7178

72-
common.mustCall(function(err, output) {
73-
assert.ifError(err);
79+
html(
80+
{
81+
input: preprocessed,
82+
filename: 'foo',
83+
template: 'doc/template.html',
84+
nodeVersion: process.version,
85+
},
86+
common.mustCall((err, output) => {
87+
assert.ifError(err);
7488

75-
const actual = output.replace(/\s/g, '');
76-
// Assert that the input stripped of all whitespace contains the
77-
// expected list
78-
assert.notEqual(actual.indexOf(expected), -1);
79-
})
80-
);
89+
const actual = output.replace(/\s/g, '');
90+
// Assert that the input stripped of all whitespace contains the
91+
// expected list
92+
assert.notEqual(actual.indexOf(expected), -1);
93+
}));
94+
}));
8195
}));
8296
});

test/fixtures/doc_inc_1.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Look [here][]!
2+
3+
[here]: doc_inc_2.html#doc_inc_2_foobar

test/fixtures/doc_inc_2.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# foobar
2+
3+
I exist and am being linked to.

test/fixtures/doc_with_includes.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@include doc_inc_1
2+
@include doc_inc_2.md

tools/doc/html.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,30 @@ function getSection(lexed) {
283283
function buildToc(lexed, filename, cb) {
284284
var toc = [];
285285
var depth = 0;
286+
287+
const startIncludeRefRE = /^\s*<!-- \[start-include:(.+)\] -->\s*$/;
288+
const endIncludeRefRE = /^\s*<!-- \[end-include:(.+)\] -->\s*$/;
289+
const realFilenames = [filename];
290+
286291
lexed.forEach(function(tok) {
292+
// Keep track of the current filename along @include directives.
293+
if (tok.type === 'html') {
294+
let match;
295+
if ((match = tok.text.match(startIncludeRefRE)) !== null)
296+
realFilenames.unshift(match[1]);
297+
else if (tok.text.match(endIncludeRefRE))
298+
realFilenames.shift();
299+
}
300+
287301
if (tok.type !== 'heading') return;
288302
if (tok.depth - depth > 1) {
289303
return cb(new Error('Inappropriate heading level\n' +
290304
JSON.stringify(tok)));
291305
}
292306

293307
depth = tok.depth;
294-
var id = getId(filename + '_' + tok.text.trim());
308+
const realFilename = path.basename(realFilenames[0], '.md');
309+
const id = getId(realFilename + '_' + tok.text.trim());
295310
toc.push(new Array((depth - 1) * 2 + 1).join(' ') +
296311
'* <a href="#' + id + '">' +
297312
tok.text + '</a>');

tools/doc/preprocess.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ function processIncludes(inputFile, input, cb) {
4848
if (errState) return;
4949
if (er) return cb(errState = er);
5050
incCount--;
51-
includeData[fname] = inc;
51+
52+
// Add comments to let the HTML generator know how the anchors for
53+
// headings should look like.
54+
includeData[fname] = `<!-- [start-include:${fname}] -->\n` +
55+
inc + `\n<!-- [end-include:${fname}] -->\n`;
5256
input = input.split(include + '\n').join(includeData[fname] + '\n');
5357
if (incCount === 0) {
5458
return cb(null, input);

0 commit comments

Comments
 (0)