diff --git a/aio/tools/transforms/angular-base-package/index.js b/aio/tools/transforms/angular-base-package/index.js index 5f1f6e5fc8..4fcb0c4631 100644 --- a/aio/tools/transforms/angular-base-package/index.js +++ b/aio/tools/transforms/angular-base-package/index.js @@ -140,6 +140,7 @@ module.exports = new Package('angular-base', [ .config(function(postProcessHtml, addImageDimensions, autoLinkCode, filterPipes, filterAmbiguousDirectiveAliases, ignoreHttpInUrls, ignoreGenericWords) { addImageDimensions.basePath = path.resolve(AIO_PATH, 'src'); autoLinkCode.customFilters = [ignoreGenericWords, ignoreHttpInUrls, filterPipes, filterAmbiguousDirectiveAliases]; + autoLinkCode.failOnMissingDocPath = true; postProcessHtml.plugins = [ require('./post-processors/autolink-headings'), addImageDimensions, diff --git a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js index 278205eef7..91bfbf6330 100644 --- a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js +++ b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js @@ -19,12 +19,23 @@ const textContent = require('hast-util-to-string'); * @property codeElements an array of strings. * Only text contained in these elements will be linked to. * Usually set to "code" but also "code-example" for angular.io. + * + * @property ignoredLanguages an array of languages that should not be auto-linked + * + * @property ignoredLanguages an array of languages that should not be auto-linked + * + * @property failOnMissingDocPath if set to true then this post-processor will cause the doc-gen + * to fail when it attempts to auto-link to a doc that has no `doc.path` property, which implies + * that it exists but is not public (nor rendered). + * */ module.exports = function autoLinkCode(getDocFromAlias) { autoLinkCodeImpl.docTypes = []; autoLinkCodeImpl.customFilters = []; autoLinkCodeImpl.codeElements = ['code']; autoLinkCodeImpl.ignoredLanguages = ['bash', 'sh', 'shell', 'json', 'markdown']; + autoLinkCodeImpl.failOnMissingDocPath = false; + return autoLinkCodeImpl; function autoLinkCodeImpl() { @@ -64,8 +75,10 @@ module.exports = function autoLinkCode(getDocFromAlias) { // * do not have an ignored language // * are not inside links const isCodeElement = autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType)); - const hasNoAutoLink = node.properties.className && node.properties.className.includes('no-auto-link'); - const isLanguageSupported = !autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language); + const hasNoAutoLink = + node.properties.className && node.properties.className.includes('no-auto-link'); + const isLanguageSupported = + !autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language); const isInLink = isInsideLink(ancestors); return isCodeElement && !hasNoAutoLink && isLanguageSupported && !isInLink; } @@ -76,19 +89,19 @@ module.exports = function autoLinkCode(getDocFromAlias) { function getNodes(node, file) { return textContent(node) - .split(/([A-Za-z0-9_.-]+)/) - .filter(word => word.length) - .map((word, index, words) => { - // remove docs that fail the custom filter tests - const filteredDocs = autoLinkCodeImpl.customFilters.reduce( - (docs, filter) => filter(docs, words, index), getDocFromAlias(word)); + .split(/([A-Za-z0-9_.-]+)/) + .filter(word => word.length) + .map((word, index, words) => { + // remove docs that fail the custom filter tests + const filteredDocs = autoLinkCodeImpl.customFilters.reduce( + (docs, filter) => filter(docs, words, index), getDocFromAlias(word)); - return foundValidDoc(filteredDocs, word, file) ? - // Create a link wrapping the text node. - createLinkNode(filteredDocs[0], word) : - // this is just text so push a new text node - {type: 'text', value: word}; - }); + return foundValidDoc(filteredDocs, word, file) ? + // Create a link wrapping the text node. + createLinkNode(filteredDocs[0], word) : + // this is just text so push a new text node + {type: 'text', value: word}; + }); } /** @@ -112,12 +125,16 @@ module.exports = function autoLinkCode(getDocFromAlias) { return false; } - if (doc.path === '') { + if (!doc.path) { var message = ` autoLinkCode: Doc path is empty for "${doc.id}" - link will not be generated for "${keyword}". Please make sure if the doc should be public. If not, it should probably not be referenced in the docs.`; - file.message(message); + if (autoLinkCodeImpl.failOnMissingDocPath) { + file.fail(message); + } else { + file.message(message); + } return false; } diff --git a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js index 5f18756f63..e36203a5af 100644 --- a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js +++ b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js @@ -2,7 +2,7 @@ var createTestPackage = require('../../helpers/test-package'); var Dgeni = require('dgeni'); describe('autoLinkCode post-processor', () => { - let processor, autoLinkCode, aliasMap, filterPipes; + let processor, autoLinkCode, aliasMap, filterPipes, log; beforeEach(() => { const testPackage = createTestPackage('angular-base-package'); @@ -15,6 +15,7 @@ describe('autoLinkCode post-processor', () => { processor.docTypes = ['test-doc']; processor.plugins = [autoLinkCode]; filterPipes = injector.get('filterPipes'); + log = injector.get('log'); }); it('should insert an anchor into every code item that matches the id of an API doc', () => { @@ -126,19 +127,9 @@ describe('autoLinkCode post-processor', () => { expect(doc.renderedContent).toEqual('MyClass'); }); - it('should ignore code items that match an API doc but have no path set', - () => { - aliasMap.addDoc( - {docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: ''}); - const doc = {docType: 'test-doc', renderedContent: 'MyClass'}; - processor.$process([doc]); - expect(doc.renderedContent).toEqual('MyClass'); - }); - it('should ignore documents when the `docType` is set to `member` and the keyword doesn\'t include `.`', () => { - aliasMap.addDoc( - {docType: 'member', id: 'MyEnum', aliases: ['MyEnum'], path: 'a/b/c'}); + aliasMap.addDoc({docType: 'member', id: 'MyEnum', aliases: ['MyEnum'], path: 'a/b/c'}); const doc = {docType: 'test-doc', renderedContent: 'MyEnum'}; processor.$process([doc]); expect(doc.renderedContent).toEqual('MyEnum'); @@ -193,4 +184,25 @@ describe('autoLinkCode post-processor', () => { processor.$process([doc]); expect(doc.renderedContent).toEqual('MyClass'); }); + + it('should record a warning if the autolinked doc has no `path` and `failOnMissingDocPath` is false', + () => { + aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass']}); + const doc = {docType: 'test-doc', renderedContent: 'MyClass'}; + autoLinkCode.failOnMissingDocPath = false; + processor.$process([doc]); + + expect(log.warn).toHaveBeenCalledWith(` + autoLinkCode: Doc path is empty for "MyClass" - link will not be generated for "MyClass". + Please make sure if the doc should be public. If not, it should probably not be referenced in the docs. - doc (test-doc) `); + }); + + it('should fail if the autolinked doc has no `path` and `failOnMissingDocPath` is true', () => { + aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass']}); + const doc = {docType: 'test-doc', renderedContent: 'MyClass'}; + autoLinkCode.failOnMissingDocPath = true; + expect(() => processor.$process([doc])).toThrowError(` + autoLinkCode: Doc path is empty for "MyClass" - link will not be generated for "MyClass". + Please make sure if the doc should be public. If not, it should probably not be referenced in the docs. - doc (test-doc) `); + }); });