diff --git a/common/changes/@microsoft/tsdoc/octogonz-fix-html-name-regexp_2020-03-27-05-15.json b/common/changes/@microsoft/tsdoc/octogonz-fix-html-name-regexp_2020-03-27-05-15.json new file mode 100644 index 00000000..6dc935cc --- /dev/null +++ b/common/changes/@microsoft/tsdoc/octogonz-fix-html-name-regexp_2020-03-27-05-15.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/tsdoc", + "comment": "Fix an issue where \"h1\" was not allowed as an HTML element name", + "type": "patch" + } + ], + "packageName": "@microsoft/tsdoc", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file diff --git a/tsdoc/src/parser/NodeParser.ts b/tsdoc/src/parser/NodeParser.ts index cd9e0fef..764611c1 100644 --- a/tsdoc/src/parser/NodeParser.ts +++ b/tsdoc/src/parser/NodeParser.ts @@ -1813,8 +1813,9 @@ export class NodeParser { let done: boolean = false; while (!done) { switch (tokenReader.peekTokenKind()) { - case TokenKind.AsciiWord: case TokenKind.Hyphen: + case TokenKind.Period: + case TokenKind.AsciiWord: tokenReader.readToken(); break; default: diff --git a/tsdoc/src/parser/StringChecks.ts b/tsdoc/src/parser/StringChecks.ts index e55e8357..a0cc0419 100644 --- a/tsdoc/src/parser/StringChecks.ts +++ b/tsdoc/src/parser/StringChecks.ts @@ -7,9 +7,14 @@ export class StringChecks { private static readonly _urlSchemeRegExp: RegExp = /^[a-z][a-z0-9]*\:\/\//i; private static readonly _urlSchemeAfterRegExp: RegExp = /^[a-z][a-z0-9]*\:\/\/./i; + // HTML element definitions: + // https://spec.commonmark.org/0.29/#tag-name // https://www.w3.org/TR/html5/syntax.html#tag-name // https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name - private static readonly _htmlNameRegExp: RegExp = /^[a-z]+(\-[a-z]+)*$/i; + // + // We use the CommonMark spec: + // "A tag name consists of an ASCII letter followed by zero or more ASCII letters, digits, or hyphens (-)." + private static readonly _htmlNameRegExp: RegExp = /^[a-z]+[a-z0-9\-]*$/i; // Note: In addition to letters, numbers, underscores, and dollar signs, modern ECMAScript // also allows Unicode categories such as letters, combining marks, digits, and connector punctuation. @@ -86,7 +91,7 @@ export class StringChecks { */ public static explainIfInvalidHtmlName(htmlName: string): string | undefined { if (!StringChecks._htmlNameRegExp.test(htmlName)) { - return 'An HTML name must be a sequence of letters separated by hyphens'; + return 'An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens'; } return undefined; diff --git a/tsdoc/src/parser/__tests__/NodeParserHtml.test.ts b/tsdoc/src/parser/__tests__/NodeParserHtml.test.ts index 8a6cd276..e1e5fdf8 100644 --- a/tsdoc/src/parser/__tests__/NodeParserHtml.test.ts +++ b/tsdoc/src/parser/__tests__/NodeParserHtml.test.ts @@ -107,3 +107,23 @@ test('07 Closing tags, negative', () => { ' */' ].join('\n')); }); + +test('08 Unusual HTML names, positive', () => { + TestHelpers.parseAndMatchNodeParserSnapshot([ + '/**', + ' * ', + ' * ', + ' * ', + ' */' + ].join('\n')); +}); + +test('09 Unusual HTML names, negative', () => { + TestHelpers.parseAndMatchNodeParserSnapshot([ + '/**', + ' * <1a/>', + ' * ', + ' * <_a>', + ' */' + ].join('\n')); +}); diff --git a/tsdoc/src/parser/__tests__/__snapshots__/NodeParserHtml.test.ts.snap b/tsdoc/src/parser/__tests__/__snapshots__/NodeParserHtml.test.ts.snap index aa7a15a9..0c01fd32 100644 --- a/tsdoc/src/parser/__tests__/__snapshots__/NodeParserHtml.test.ts.snap +++ b/tsdoc/src/parser/__tests__/__snapshots__/NodeParserHtml.test.ts.snap @@ -946,7 +946,7 @@ Object { "logMessages": Array [ "(2,4): The HTML element has an invalid attribute: Expecting \\"=\\" after HTML attribute name", "(2,28): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag", - "(3,4): The HTML element has an invalid attribute: An HTML name must be a sequence of letters separated by hyphens", + "(3,4): The HTML element has an invalid attribute: Expecting \\"=\\" after HTML attribute name", "(3,28): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag", "(4,4): The HTML element has an invalid attribute: The HTML string is missing its closing quote", "(4,31): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag", @@ -1011,9 +1011,9 @@ Object { ], }, Object { - "errorLocation": "attr-", + "errorLocation": "two", "errorLocationPrecedingToken": " ", - "errorMessage": "The HTML element has an invalid attribute: An HTML name must be a sequence of letters separated by hyphens", + "errorMessage": "The HTML element has an invalid attribute: Expecting [q]=[q] after HTML attribute name", "kind": "ErrorText", "nodes": Array [ Object { @@ -1701,3 +1701,270 @@ Object { }, } `; + +exports[`08 Unusual HTML names, positive 1`] = ` +Object { + "buffer": "/**[n] * [<]a1/[>][n] * [<]a-a[>][n] * [<]a--9-[>][n] */", + "gaps": Array [], + "lines": Array [ + "[<]a1/[>]", + "[<]a-a[>]", + "[<]a--9-[>]", + ], + "logMessages": Array [], + "nodes": Object { + "kind": "Comment", + "nodes": Array [ + Object { + "kind": "Section", + "nodes": Array [ + Object { + "kind": "Paragraph", + "nodes": Array [ + Object { + "kind": "HtmlStartTag", + "nodes": Array [ + Object { + "kind": "Excerpt: HtmlStartTag_OpeningDelimiter", + "nodeExcerpt": "[<]", + }, + Object { + "kind": "Excerpt: HtmlStartTag_Name", + "nodeExcerpt": "a1", + }, + Object { + "kind": "Excerpt: HtmlStartTag_ClosingDelimiter", + "nodeExcerpt": "/[>]", + }, + ], + }, + Object { + "kind": "SoftBreak", + "nodes": Array [ + Object { + "kind": "Excerpt: SoftBreak", + "nodeExcerpt": "[n]", + }, + ], + }, + Object { + "kind": "HtmlStartTag", + "nodes": Array [ + Object { + "kind": "Excerpt: HtmlStartTag_OpeningDelimiter", + "nodeExcerpt": "[<]", + }, + Object { + "kind": "Excerpt: HtmlStartTag_Name", + "nodeExcerpt": "a-a", + }, + Object { + "kind": "Excerpt: HtmlStartTag_ClosingDelimiter", + "nodeExcerpt": "[>]", + }, + ], + }, + Object { + "kind": "SoftBreak", + "nodes": Array [ + Object { + "kind": "Excerpt: SoftBreak", + "nodeExcerpt": "[n]", + }, + ], + }, + Object { + "kind": "HtmlStartTag", + "nodes": Array [ + Object { + "kind": "Excerpt: HtmlStartTag_OpeningDelimiter", + "nodeExcerpt": "[<]", + }, + Object { + "kind": "Excerpt: HtmlStartTag_Name", + "nodeExcerpt": "a--9-", + }, + Object { + "kind": "Excerpt: HtmlStartTag_ClosingDelimiter", + "nodeExcerpt": "[>]", + }, + ], + }, + Object { + "kind": "SoftBreak", + "nodes": Array [ + Object { + "kind": "Excerpt: SoftBreak", + "nodeExcerpt": "[n]", + }, + ], + }, + ], + }, + ], + }, + ], + }, +} +`; + +exports[`09 Unusual HTML names, negative 1`] = ` +Object { + "buffer": "/**[n] * [<]1a/[>][n] * [<]a.a[>][n] * [<]_a[>][n] */", + "gaps": Array [], + "lines": Array [ + "[<]1a/[>]", + "[<]a.a[>]", + "[<]_a[>]", + ], + "logMessages": Array [ + "(2,4): Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens", + "(2,8): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag", + "(3,4): Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens", + "(3,8): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag", + "(4,4): Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens", + "(4,7): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag", + ], + "nodes": Object { + "kind": "Comment", + "nodes": Array [ + Object { + "kind": "Section", + "nodes": Array [ + Object { + "kind": "Paragraph", + "nodes": Array [ + Object { + "errorLocation": "1a", + "errorLocationPrecedingToken": "<", + "errorMessage": "Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens", + "kind": "ErrorText", + "nodes": Array [ + Object { + "kind": "Excerpt: ErrorText", + "nodeExcerpt": "[<]", + }, + ], + }, + Object { + "kind": "PlainText", + "nodes": Array [ + Object { + "kind": "Excerpt: PlainText", + "nodeExcerpt": "1a/", + }, + ], + }, + Object { + "errorLocation": "[>]", + "errorLocationPrecedingToken": "/", + "errorMessage": "The [q][>][q] character should be escaped using a backslash to avoid confusion with an HTML tag", + "kind": "ErrorText", + "nodes": Array [ + Object { + "kind": "Excerpt: ErrorText", + "nodeExcerpt": "[>]", + }, + ], + }, + Object { + "kind": "SoftBreak", + "nodes": Array [ + Object { + "kind": "Excerpt: SoftBreak", + "nodeExcerpt": "[n]", + }, + ], + }, + Object { + "errorLocation": "a.a", + "errorLocationPrecedingToken": "<", + "errorMessage": "Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens", + "kind": "ErrorText", + "nodes": Array [ + Object { + "kind": "Excerpt: ErrorText", + "nodeExcerpt": "[<]", + }, + ], + }, + Object { + "kind": "PlainText", + "nodes": Array [ + Object { + "kind": "Excerpt: PlainText", + "nodeExcerpt": "a.a", + }, + ], + }, + Object { + "errorLocation": "[>]", + "errorLocationPrecedingToken": "a", + "errorMessage": "The [q][>][q] character should be escaped using a backslash to avoid confusion with an HTML tag", + "kind": "ErrorText", + "nodes": Array [ + Object { + "kind": "Excerpt: ErrorText", + "nodeExcerpt": "[>]", + }, + ], + }, + Object { + "kind": "SoftBreak", + "nodes": Array [ + Object { + "kind": "Excerpt: SoftBreak", + "nodeExcerpt": "[n]", + }, + ], + }, + Object { + "errorLocation": "_a", + "errorLocationPrecedingToken": "<", + "errorMessage": "Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens", + "kind": "ErrorText", + "nodes": Array [ + Object { + "kind": "Excerpt: ErrorText", + "nodeExcerpt": "[<]", + }, + ], + }, + Object { + "kind": "PlainText", + "nodes": Array [ + Object { + "kind": "Excerpt: PlainText", + "nodeExcerpt": "_a", + }, + ], + }, + Object { + "errorLocation": "[>]", + "errorLocationPrecedingToken": "_a", + "errorMessage": "The [q][>][q] character should be escaped using a backslash to avoid confusion with an HTML tag", + "kind": "ErrorText", + "nodes": Array [ + Object { + "kind": "Excerpt: ErrorText", + "nodeExcerpt": "[>]", + }, + ], + }, + Object { + "kind": "SoftBreak", + "nodes": Array [ + Object { + "kind": "Excerpt: SoftBreak", + "nodeExcerpt": "[n]", + }, + ], + }, + ], + }, + ], + }, + ], + }, +} +`;