Skip to content

Commit b59e9fb

Browse files
authored
Merge pull request #225 from microsoft/octogonz/fix-html-name-regexp
Fix an issue where "h1" was not allowed as an HTML element name
2 parents 4b8311f + eba7fa9 commit b59e9fb

File tree

5 files changed

+310
-6
lines changed

5 files changed

+310
-6
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/tsdoc",
5+
"comment": "Fix an issue where \"h1\" was not allowed as an HTML element name",
6+
"type": "patch"
7+
}
8+
],
9+
"packageName": "@microsoft/tsdoc",
10+
"email": "4673363+octogonz@users.noreply.github.com"
11+
}

tsdoc/src/parser/NodeParser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1813,8 +1813,9 @@ export class NodeParser {
18131813
let done: boolean = false;
18141814
while (!done) {
18151815
switch (tokenReader.peekTokenKind()) {
1816-
case TokenKind.AsciiWord:
18171816
case TokenKind.Hyphen:
1817+
case TokenKind.Period:
1818+
case TokenKind.AsciiWord:
18181819
tokenReader.readToken();
18191820
break;
18201821
default:

tsdoc/src/parser/StringChecks.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@ export class StringChecks {
77
private static readonly _urlSchemeRegExp: RegExp = /^[a-z][a-z0-9]*\:\/\//i;
88
private static readonly _urlSchemeAfterRegExp: RegExp = /^[a-z][a-z0-9]*\:\/\/./i;
99

10+
// HTML element definitions:
11+
// https://spec.commonmark.org/0.29/#tag-name
1012
// https://www.w3.org/TR/html5/syntax.html#tag-name
1113
// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
12-
private static readonly _htmlNameRegExp: RegExp = /^[a-z]+(\-[a-z]+)*$/i;
14+
//
15+
// We use the CommonMark spec:
16+
// "A tag name consists of an ASCII letter followed by zero or more ASCII letters, digits, or hyphens (-)."
17+
private static readonly _htmlNameRegExp: RegExp = /^[a-z]+[a-z0-9\-]*$/i;
1318

1419
// Note: In addition to letters, numbers, underscores, and dollar signs, modern ECMAScript
1520
// also allows Unicode categories such as letters, combining marks, digits, and connector punctuation.
@@ -86,7 +91,7 @@ export class StringChecks {
8691
*/
8792
public static explainIfInvalidHtmlName(htmlName: string): string | undefined {
8893
if (!StringChecks._htmlNameRegExp.test(htmlName)) {
89-
return 'An HTML name must be a sequence of letters separated by hyphens';
94+
return 'An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens';
9095
}
9196

9297
return undefined;

tsdoc/src/parser/__tests__/NodeParserHtml.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,23 @@ test('07 Closing tags, negative', () => {
107107
' */'
108108
].join('\n'));
109109
});
110+
111+
test('08 Unusual HTML names, positive', () => {
112+
TestHelpers.parseAndMatchNodeParserSnapshot([
113+
'/**',
114+
' * <a1/>',
115+
' * <a-a>',
116+
' * <a--9->',
117+
' */'
118+
].join('\n'));
119+
});
120+
121+
test('09 Unusual HTML names, negative', () => {
122+
TestHelpers.parseAndMatchNodeParserSnapshot([
123+
'/**',
124+
' * <1a/>',
125+
' * <a.a>',
126+
' * <_a>',
127+
' */'
128+
].join('\n'));
129+
});

tsdoc/src/parser/__tests__/__snapshots__/NodeParserHtml.test.ts.snap

Lines changed: 270 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@ Object {
946946
"logMessages": Array [
947947
"(2,4): The HTML element has an invalid attribute: Expecting \\"=\\" after HTML attribute name",
948948
"(2,28): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag",
949-
"(3,4): The HTML element has an invalid attribute: An HTML name must be a sequence of letters separated by hyphens",
949+
"(3,4): The HTML element has an invalid attribute: Expecting \\"=\\" after HTML attribute name",
950950
"(3,28): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag",
951951
"(4,4): The HTML element has an invalid attribute: The HTML string is missing its closing quote",
952952
"(4,31): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag",
@@ -1011,9 +1011,9 @@ Object {
10111011
],
10121012
},
10131013
Object {
1014-
"errorLocation": "attr-",
1014+
"errorLocation": "two",
10151015
"errorLocationPrecedingToken": " ",
1016-
"errorMessage": "The HTML element has an invalid attribute: An HTML name must be a sequence of letters separated by hyphens",
1016+
"errorMessage": "The HTML element has an invalid attribute: Expecting [q]=[q] after HTML attribute name",
10171017
"kind": "ErrorText",
10181018
"nodes": Array [
10191019
Object {
@@ -1701,3 +1701,270 @@ Object {
17011701
},
17021702
}
17031703
`;
1704+
1705+
exports[`08 Unusual HTML names, positive 1`] = `
1706+
Object {
1707+
"buffer": "/**[n] * [<]a1/[>][n] * [<]a-a[>][n] * [<]a--9-[>][n] */",
1708+
"gaps": Array [],
1709+
"lines": Array [
1710+
"[<]a1/[>]",
1711+
"[<]a-a[>]",
1712+
"[<]a--9-[>]",
1713+
],
1714+
"logMessages": Array [],
1715+
"nodes": Object {
1716+
"kind": "Comment",
1717+
"nodes": Array [
1718+
Object {
1719+
"kind": "Section",
1720+
"nodes": Array [
1721+
Object {
1722+
"kind": "Paragraph",
1723+
"nodes": Array [
1724+
Object {
1725+
"kind": "HtmlStartTag",
1726+
"nodes": Array [
1727+
Object {
1728+
"kind": "Excerpt: HtmlStartTag_OpeningDelimiter",
1729+
"nodeExcerpt": "[<]",
1730+
},
1731+
Object {
1732+
"kind": "Excerpt: HtmlStartTag_Name",
1733+
"nodeExcerpt": "a1",
1734+
},
1735+
Object {
1736+
"kind": "Excerpt: HtmlStartTag_ClosingDelimiter",
1737+
"nodeExcerpt": "/[>]",
1738+
},
1739+
],
1740+
},
1741+
Object {
1742+
"kind": "SoftBreak",
1743+
"nodes": Array [
1744+
Object {
1745+
"kind": "Excerpt: SoftBreak",
1746+
"nodeExcerpt": "[n]",
1747+
},
1748+
],
1749+
},
1750+
Object {
1751+
"kind": "HtmlStartTag",
1752+
"nodes": Array [
1753+
Object {
1754+
"kind": "Excerpt: HtmlStartTag_OpeningDelimiter",
1755+
"nodeExcerpt": "[<]",
1756+
},
1757+
Object {
1758+
"kind": "Excerpt: HtmlStartTag_Name",
1759+
"nodeExcerpt": "a-a",
1760+
},
1761+
Object {
1762+
"kind": "Excerpt: HtmlStartTag_ClosingDelimiter",
1763+
"nodeExcerpt": "[>]",
1764+
},
1765+
],
1766+
},
1767+
Object {
1768+
"kind": "SoftBreak",
1769+
"nodes": Array [
1770+
Object {
1771+
"kind": "Excerpt: SoftBreak",
1772+
"nodeExcerpt": "[n]",
1773+
},
1774+
],
1775+
},
1776+
Object {
1777+
"kind": "HtmlStartTag",
1778+
"nodes": Array [
1779+
Object {
1780+
"kind": "Excerpt: HtmlStartTag_OpeningDelimiter",
1781+
"nodeExcerpt": "[<]",
1782+
},
1783+
Object {
1784+
"kind": "Excerpt: HtmlStartTag_Name",
1785+
"nodeExcerpt": "a--9-",
1786+
},
1787+
Object {
1788+
"kind": "Excerpt: HtmlStartTag_ClosingDelimiter",
1789+
"nodeExcerpt": "[>]",
1790+
},
1791+
],
1792+
},
1793+
Object {
1794+
"kind": "SoftBreak",
1795+
"nodes": Array [
1796+
Object {
1797+
"kind": "Excerpt: SoftBreak",
1798+
"nodeExcerpt": "[n]",
1799+
},
1800+
],
1801+
},
1802+
],
1803+
},
1804+
],
1805+
},
1806+
],
1807+
},
1808+
}
1809+
`;
1810+
1811+
exports[`09 Unusual HTML names, negative 1`] = `
1812+
Object {
1813+
"buffer": "/**[n] * [<]1a/[>][n] * [<]a.a[>][n] * [<]_a[>][n] */",
1814+
"gaps": Array [],
1815+
"lines": Array [
1816+
"[<]1a/[>]",
1817+
"[<]a.a[>]",
1818+
"[<]_a[>]",
1819+
],
1820+
"logMessages": Array [
1821+
"(2,4): Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens",
1822+
"(2,8): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag",
1823+
"(3,4): Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens",
1824+
"(3,8): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag",
1825+
"(4,4): Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens",
1826+
"(4,7): The \\">\\" character should be escaped using a backslash to avoid confusion with an HTML tag",
1827+
],
1828+
"nodes": Object {
1829+
"kind": "Comment",
1830+
"nodes": Array [
1831+
Object {
1832+
"kind": "Section",
1833+
"nodes": Array [
1834+
Object {
1835+
"kind": "Paragraph",
1836+
"nodes": Array [
1837+
Object {
1838+
"errorLocation": "1a",
1839+
"errorLocationPrecedingToken": "<",
1840+
"errorMessage": "Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens",
1841+
"kind": "ErrorText",
1842+
"nodes": Array [
1843+
Object {
1844+
"kind": "Excerpt: ErrorText",
1845+
"nodeExcerpt": "[<]",
1846+
},
1847+
],
1848+
},
1849+
Object {
1850+
"kind": "PlainText",
1851+
"nodes": Array [
1852+
Object {
1853+
"kind": "Excerpt: PlainText",
1854+
"nodeExcerpt": "1a/",
1855+
},
1856+
],
1857+
},
1858+
Object {
1859+
"errorLocation": "[>]",
1860+
"errorLocationPrecedingToken": "/",
1861+
"errorMessage": "The [q][>][q] character should be escaped using a backslash to avoid confusion with an HTML tag",
1862+
"kind": "ErrorText",
1863+
"nodes": Array [
1864+
Object {
1865+
"kind": "Excerpt: ErrorText",
1866+
"nodeExcerpt": "[>]",
1867+
},
1868+
],
1869+
},
1870+
Object {
1871+
"kind": "SoftBreak",
1872+
"nodes": Array [
1873+
Object {
1874+
"kind": "Excerpt: SoftBreak",
1875+
"nodeExcerpt": "[n]",
1876+
},
1877+
],
1878+
},
1879+
Object {
1880+
"errorLocation": "a.a",
1881+
"errorLocationPrecedingToken": "<",
1882+
"errorMessage": "Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens",
1883+
"kind": "ErrorText",
1884+
"nodes": Array [
1885+
Object {
1886+
"kind": "Excerpt: ErrorText",
1887+
"nodeExcerpt": "[<]",
1888+
},
1889+
],
1890+
},
1891+
Object {
1892+
"kind": "PlainText",
1893+
"nodes": Array [
1894+
Object {
1895+
"kind": "Excerpt: PlainText",
1896+
"nodeExcerpt": "a.a",
1897+
},
1898+
],
1899+
},
1900+
Object {
1901+
"errorLocation": "[>]",
1902+
"errorLocationPrecedingToken": "a",
1903+
"errorMessage": "The [q][>][q] character should be escaped using a backslash to avoid confusion with an HTML tag",
1904+
"kind": "ErrorText",
1905+
"nodes": Array [
1906+
Object {
1907+
"kind": "Excerpt: ErrorText",
1908+
"nodeExcerpt": "[>]",
1909+
},
1910+
],
1911+
},
1912+
Object {
1913+
"kind": "SoftBreak",
1914+
"nodes": Array [
1915+
Object {
1916+
"kind": "Excerpt: SoftBreak",
1917+
"nodeExcerpt": "[n]",
1918+
},
1919+
],
1920+
},
1921+
Object {
1922+
"errorLocation": "_a",
1923+
"errorLocationPrecedingToken": "<",
1924+
"errorMessage": "Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens",
1925+
"kind": "ErrorText",
1926+
"nodes": Array [
1927+
Object {
1928+
"kind": "Excerpt: ErrorText",
1929+
"nodeExcerpt": "[<]",
1930+
},
1931+
],
1932+
},
1933+
Object {
1934+
"kind": "PlainText",
1935+
"nodes": Array [
1936+
Object {
1937+
"kind": "Excerpt: PlainText",
1938+
"nodeExcerpt": "_a",
1939+
},
1940+
],
1941+
},
1942+
Object {
1943+
"errorLocation": "[>]",
1944+
"errorLocationPrecedingToken": "_a",
1945+
"errorMessage": "The [q][>][q] character should be escaped using a backslash to avoid confusion with an HTML tag",
1946+
"kind": "ErrorText",
1947+
"nodes": Array [
1948+
Object {
1949+
"kind": "Excerpt: ErrorText",
1950+
"nodeExcerpt": "[>]",
1951+
},
1952+
],
1953+
},
1954+
Object {
1955+
"kind": "SoftBreak",
1956+
"nodes": Array [
1957+
Object {
1958+
"kind": "Excerpt: SoftBreak",
1959+
"nodeExcerpt": "[n]",
1960+
},
1961+
],
1962+
},
1963+
],
1964+
},
1965+
],
1966+
},
1967+
],
1968+
},
1969+
}
1970+
`;

0 commit comments

Comments
 (0)