Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions tsdoc/src/configuration/TSDocConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class TSDocConfiguration {
private readonly _validation: TSDocValidationConfiguration;
private readonly _docNodeManager: DocNodeManager;
private readonly _supportedHtmlElements: Set<string>;
private readonly _linkUrlPrefixes: Set<string>;

public constructor() {
this._tagDefinitions = [];
Expand All @@ -26,6 +27,7 @@ export class TSDocConfiguration {
this._validation = new TSDocValidationConfiguration();
this._docNodeManager = new DocNodeManager();
this._supportedHtmlElements = new Set();
this._linkUrlPrefixes = new Set();

this.clear(false);

Expand All @@ -46,6 +48,7 @@ export class TSDocConfiguration {
this._validation.reportUnsupportedTags = false;
this._validation.reportUnsupportedHtmlElements = false;
this._supportedHtmlElements.clear();
this._linkUrlPrefixes.clear();

if (!noStandardTags) {
// Define all the standard tags
Expand Down Expand Up @@ -89,6 +92,29 @@ export class TSDocConfiguration {
return Array.from(this._supportedHtmlElements.values());
}

/**
* URI-style prefixes that should be treated as URL destinations in `{@link ...}` tags.
*/
public get linkUrlPrefixes(): string[] {
return Array.from(this._linkUrlPrefixes.values());
}

public addLinkUrlPrefix(prefix: string): void {
if (!/^[A-Za-z][A-Za-z0-9+.-]*$/.test(prefix)) {
throw new Error(`Invalid link URL prefix: "${prefix}"`);
}

this._linkUrlPrefixes.add(prefix.toLowerCase());
}

public clearLinkUrlPrefixes(): void {
this._linkUrlPrefixes.clear();
}

public isLinkUrlPrefixAllowed(prefix: string): boolean {
return this._linkUrlPrefixes.has(prefix.toLowerCase());
}

/**
* Register custom DocNode subclasses.
*/
Expand Down
14 changes: 14 additions & 0 deletions tsdoc/src/parser/NodeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ export class NodeParser {
this._currentSection = parserContext.docComment.summarySection;
}

private _isConfiguredLinkUrlDestination(linkDestination: string): boolean {
const colonIndex: number = linkDestination.indexOf(':');
if (colonIndex <= 0) {
return false;
}

if (linkDestination.indexOf('://') > 0) {
return false;
}

const prefix: string = linkDestination.substring(0, colonIndex);
return this._configuration.isLinkUrlPrefixAllowed(prefix);
}

public parse(): void {
const tokenReader: TokenReader = new TokenReader(this._parserContext);

Expand Down
17 changes: 17 additions & 0 deletions tsdoc/src/parser/__tests__/NodeParserLinkTag.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { TSDocConfiguration } from '../../configuration/TSDocConfiguration';
import { TestHelpers } from './TestHelpers';

test('00 Link text: positive examples', () => {
Expand Down Expand Up @@ -54,6 +55,22 @@ test('03 URL destination: negative examples', () => {
);
});

test('03b URL destination: configured URI prefixes', () => {
const configuration: TSDocConfiguration = new TSDocConfiguration();
configuration.addLinkUrlPrefix('mailto');
configuration.addLinkUrlPrefix('xref');

TestHelpers.parseAndMatchNodeParserSnapshot(
[
'/**',
' * {@link mailto:bob@example.com}',
' * {@link xref:MyNamespace.MySymbol|link text}',
' */'
].join('\n'),
configuration
);
});

test('04 Declaration reference with package name: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot(
[
Expand Down
Loading