build(aio): append information about links in and out of docs (#19583)
Closes #19560 PR Close #19583
This commit is contained in:
parent
b0befd7376
commit
8b571309ed
|
@ -29,6 +29,7 @@ module.exports = new Package('angular-base', [
|
|||
.processor(require('./processors/convertToJson'))
|
||||
.processor(require('./processors/fixInternalDocumentLinks'))
|
||||
.processor(require('./processors/copyContentAssets'))
|
||||
.processor(require('./processors/renderLinkInfo'))
|
||||
|
||||
// overrides base packageInfo and returns the one for the 'angular/angular' repo.
|
||||
.factory('packageInfo', function() { return require(path.resolve(PROJECT_ROOT, 'package.json')); })
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @dgProcessor renderLinkInfo
|
||||
* @description For each doc that has one of the specified docTypes,
|
||||
* add HTML comments that describe the links to and from the doc.
|
||||
*/
|
||||
module.exports = function renderLinkInfo(extractLinks) {
|
||||
return {
|
||||
docTypes: [],
|
||||
$runBefore: ['convertToJsonProcessor'],
|
||||
$runAfter: ['fixInternalDocumentLinks'],
|
||||
$process(docs) {
|
||||
const toLinks = {};
|
||||
const fromLinks = {};
|
||||
const docsToCheck = docs.filter(doc => this.docTypes.indexOf(doc.docType) !== -1);
|
||||
|
||||
// Extract and store all links found in each doc in hashes
|
||||
docsToCheck.forEach(doc => {
|
||||
const linksFromDoc = extractLinks(doc.renderedContent).hrefs;
|
||||
// Update the hashes
|
||||
fromLinks[doc.path] = linksFromDoc;
|
||||
linksFromDoc.forEach(linkPath => {
|
||||
linkPath = linkPath.match(/^[^#?]+/)[0]; // remove the query and hash from the link
|
||||
(toLinks[linkPath] = toLinks[linkPath] || []).push(doc.path);
|
||||
});
|
||||
});
|
||||
|
||||
// Add HTML comments to the end of the rendered content that list the links found above
|
||||
docsToCheck.forEach(doc => {
|
||||
const linksFromDoc = getLinks(fromLinks, doc.path);
|
||||
const linksToDoc = getLinks(toLinks, doc.path);
|
||||
doc.renderedContent +=
|
||||
`\n<!-- links to this doc:${linksToDoc.map(link => `\n - ${link}`).join('')}\n-->\n` +
|
||||
`<!-- links from this doc:${linksFromDoc.map(link => `\n - ${link}`).join('')}\n-->`;
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function getLinks(hash, docPath) {
|
||||
const links = (hash[docPath] || []).filter(link => link !== docPath);
|
||||
const internal = {};
|
||||
const external = {};
|
||||
links.forEach(link => {
|
||||
if (/^[^:/#?]+:/.test(link)) {
|
||||
external[link] = true;
|
||||
} else {
|
||||
internal[link] = true;
|
||||
}
|
||||
});
|
||||
return Object.keys(internal).sort()
|
||||
.concat(Object.keys(external).sort());
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
const testPackage = require('../../helpers/test-package');
|
||||
const processorFactory = require('./renderLinkInfo');
|
||||
const extractLinks = require('dgeni-packages/base/services/extractLinks')();
|
||||
const Dgeni = require('dgeni');
|
||||
|
||||
describe('renderLinkInfo processor', () => {
|
||||
|
||||
it('should be available on the injector', () => {
|
||||
const dgeni = new Dgeni([testPackage('angular-base-package')]);
|
||||
const injector = dgeni.configureInjector();
|
||||
const processor = injector.get('renderLinkInfo');
|
||||
expect(processor.$process).toBeDefined();
|
||||
});
|
||||
|
||||
it('should run before the correct processor', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
expect(processor.$runBefore).toEqual(['convertToJsonProcessor']);
|
||||
});
|
||||
|
||||
it('should run after the correct processor', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
expect(processor.$runAfter).toEqual(['fixInternalDocumentLinks']);
|
||||
});
|
||||
|
||||
it('should add HTML comments for links out of docs', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="a/b/c"></a><a href="x/y/z"></a>' },
|
||||
{ path: 'test-2', docType: 'test', renderedContent: '<a href="foo"></a><a href="bar"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="a/b/c"></a><a href="x/y/z"></a>\n' +
|
||||
'<!-- links to this doc:\n-->\n' +
|
||||
'<!-- links from this doc:\n - a/b/c\n - x/y/z\n-->'
|
||||
},
|
||||
{
|
||||
path: 'test-2',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="foo"></a><a href="bar"></a>\n' +
|
||||
'<!-- links to this doc:\n-->\n' +
|
||||
'<!-- links from this doc:\n - bar\n - foo\n-->'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should order links alphabetically', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="orange"></a><a href="apple"></a><a href="banana"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="orange"></a><a href="apple"></a><a href="banana"></a>\n' +
|
||||
'<!-- links to this doc:\n-->\n' +
|
||||
'<!-- links from this doc:\n - apple\n - banana\n - orange\n-->'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should list repeated links only once', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="banana"></a><a href="apple"></a><a href="banana"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="banana"></a><a href="apple"></a><a href="banana"></a>\n' +
|
||||
'<!-- links to this doc:\n-->\n' +
|
||||
'<!-- links from this doc:\n - apple\n - banana\n-->'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should list internal links before external', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="https://www.google.com"></a><a href="apple"></a><a href="ftp://myfile.org"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="https://www.google.com"></a><a href="apple"></a><a href="ftp://myfile.org"></a>\n' +
|
||||
'<!-- links to this doc:\n-->\n' +
|
||||
'<!-- links from this doc:\n - apple\n - ftp://myfile.org\n - https://www.google.com\n-->'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should ignore docs that do not have the specified docType', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="a/b/c"></a><a href="x/y/z"></a>' },
|
||||
{ path: 'test-2', docType: 'test2', renderedContent: '<a href="foo"></a><a href="bar"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="a/b/c"></a><a href="x/y/z"></a>\n' +
|
||||
'<!-- links to this doc:\n-->\n' +
|
||||
'<!-- links from this doc:\n - a/b/c\n - x/y/z\n-->'
|
||||
},
|
||||
{
|
||||
path: 'test-2',
|
||||
docType: 'test2',
|
||||
renderedContent: '<a href="foo"></a><a href="bar"></a>'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add HTML comments for links into docs', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="test-2"></a>' },
|
||||
{ path: 'test-2', docType: 'test', renderedContent: '<a href="test-1"></a><a href="test-3"></a>' },
|
||||
{ path: 'test-3', docType: 'test', renderedContent: '<a href="test-1"></a><a href="test-2"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-2"></a>\n' +
|
||||
'<!-- links to this doc:\n - test-2\n - test-3\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-2\n-->'
|
||||
},
|
||||
{
|
||||
path: 'test-2',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-1"></a><a href="test-3"></a>\n' +
|
||||
'<!-- links to this doc:\n - test-1\n - test-3\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-1\n - test-3\n-->'
|
||||
},
|
||||
{
|
||||
path: 'test-3',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-1"></a><a href="test-2"></a>\n' +
|
||||
'<!-- links to this doc:\n - test-2\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-1\n - test-2\n-->'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not include links to themselves', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="test-2"></a>' },
|
||||
{ path: 'test-2', docType: 'test', renderedContent: '<a href="test-1"></a><a href="test-2"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-2"></a>\n' +
|
||||
'<!-- links to this doc:\n - test-2\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-2\n-->'
|
||||
},
|
||||
{
|
||||
path: 'test-2',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-1"></a><a href="test-2"></a>\n' +
|
||||
'<!-- links to this doc:\n - test-1\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-1\n-->'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should match links that contain fragments or queries', () => {
|
||||
const processor = processorFactory(extractLinks);
|
||||
processor.docTypes = ['test'];
|
||||
const docs = [
|
||||
{ path: 'test-1', docType: 'test', renderedContent: '<a href="test-2#foo"></a>' },
|
||||
{ path: 'test-2', docType: 'test', renderedContent: '<a href="test-1?some-query"></a>' },
|
||||
{ path: 'test-3', docType: 'test', renderedContent: '<a href="test-1?some-query#foo"></a>' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
expect(docs).toEqual([
|
||||
{
|
||||
path: 'test-1',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-2#foo"></a>\n' +
|
||||
'<!-- links to this doc:\n - test-2\n - test-3\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-2#foo\n-->'
|
||||
},
|
||||
{
|
||||
path: 'test-2',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-1?some-query"></a>\n' +
|
||||
'<!-- links to this doc:\n - test-1\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-1?some-query\n-->'
|
||||
},
|
||||
{
|
||||
path: 'test-3',
|
||||
docType: 'test',
|
||||
renderedContent: '<a href="test-1?some-query#foo"></a>\n' +
|
||||
'<!-- links to this doc:\n-->\n' +
|
||||
'<!-- links from this doc:\n - test-1?some-query#foo\n-->'
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -48,4 +48,8 @@ module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPacka
|
|||
});
|
||||
checkAnchorLinksProcessor.pathVariants = ['', '/', '.html', '/index.html', '#top-of-page'];
|
||||
checkAnchorLinksProcessor.errorOnUnmatchedLinks = true;
|
||||
})
|
||||
|
||||
.config(function(renderLinkInfo, postProcessHtml) {
|
||||
renderLinkInfo.docTypes = postProcessHtml.docTypes;
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue