| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- /**
- * Copied from https://github.com/MananTank/validate-html-nesting
- * with ISC license
- *
- * To avoid runtime dependency on validate-html-nesting
- * This file should not change very often in the original repo
- * but we may need to keep it up-to-date from time to time.
- */
- /**
- * returns true if given parent-child nesting is valid HTML
- */
- export function isValidHTMLNesting(parent: string, child: string): boolean {
- // if we know the list of children that are the only valid children for the given parent
- if (parent in onlyValidChildren) {
- return onlyValidChildren[parent].has(child)
- }
- // if we know the list of parents that are the only valid parents for the given child
- if (child in onlyValidParents) {
- return onlyValidParents[child].has(parent)
- }
- // if we know the list of children that are NOT valid for the given parent
- if (parent in knownInvalidChildren) {
- // check if the child is in the list of invalid children
- // if so, return false
- if (knownInvalidChildren[parent].has(child)) return false
- }
- // if we know the list of parents that are NOT valid for the given child
- if (child in knownInvalidParents) {
- // check if the parent is in the list of invalid parents
- // if so, return false
- if (knownInvalidParents[child].has(parent)) return false
- }
- return true
- }
- const headings = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
- const emptySet = new Set([])
- /**
- * maps element to set of elements that can be it's children, no other */
- const onlyValidChildren: Record<string, Set<string>> = {
- head: new Set([
- 'base',
- 'basefront',
- 'bgsound',
- 'link',
- 'meta',
- 'title',
- 'noscript',
- 'noframes',
- 'style',
- 'script',
- 'template',
- ]),
- optgroup: new Set(['option']),
- select: new Set(['optgroup', 'option']),
- math: new Set(['mrow']),
- script: new Set(),
- // table
- table: new Set(['caption', 'colgroup', 'tbody', 'tfoot', 'thead']),
- tr: new Set(['td', 'th']),
- colgroup: new Set(['col']),
- tbody: new Set(['tr']),
- thead: new Set(['tr']),
- tfoot: new Set(['tr']),
- // these elements can not have any children elements
- iframe: emptySet,
- option: emptySet,
- textarea: emptySet,
- style: emptySet,
- title: emptySet,
- }
- /** maps elements to set of elements which can be it's parent, no other */
- const onlyValidParents: Record<string, Set<string>> = {
- // sections
- html: emptySet,
- body: new Set(['html']),
- head: new Set(['html']),
- // table
- td: new Set(['tr']),
- colgroup: new Set(['table']),
- caption: new Set(['table']),
- tbody: new Set(['table']),
- tfoot: new Set(['table']),
- col: new Set(['colgroup']),
- th: new Set(['tr']),
- thead: new Set(['table']),
- tr: new Set(['tbody', 'thead', 'tfoot']),
- // data list
- dd: new Set(['dl', 'div']),
- dt: new Set(['dl', 'div']),
- // other
- figcaption: new Set(['figure']),
- // li: new Set(["ul", "ol"]),
- summary: new Set(['details']),
- area: new Set(['map']),
- } as const
- /** maps element to set of elements that can not be it's children, others can */
- const knownInvalidChildren: Record<string, Set<string>> = {
- p: new Set([
- 'address',
- 'article',
- 'aside',
- 'blockquote',
- 'center',
- 'details',
- 'dialog',
- 'dir',
- 'div',
- 'dl',
- 'fieldset',
- 'figure',
- 'footer',
- 'form',
- 'h1',
- 'h2',
- 'h3',
- 'h4',
- 'h5',
- 'h6',
- 'header',
- 'hgroup',
- 'hr',
- 'li',
- 'main',
- 'nav',
- 'menu',
- 'ol',
- 'p',
- 'pre',
- 'section',
- 'table',
- 'ul',
- ]),
- svg: new Set([
- 'b',
- 'blockquote',
- 'br',
- 'code',
- 'dd',
- 'div',
- 'dl',
- 'dt',
- 'em',
- 'embed',
- 'h1',
- 'h2',
- 'h3',
- 'h4',
- 'h5',
- 'h6',
- 'hr',
- 'i',
- 'img',
- 'li',
- 'menu',
- 'meta',
- 'ol',
- 'p',
- 'pre',
- 'ruby',
- 's',
- 'small',
- 'span',
- 'strong',
- 'sub',
- 'sup',
- 'table',
- 'u',
- 'ul',
- 'var',
- ]),
- } as const
- /** maps element to set of elements that can not be it's parent, others can */
- const knownInvalidParents: Record<string, Set<string>> = {
- a: new Set(['a']),
- button: new Set(['button']),
- dd: new Set(['dd', 'dt']),
- dt: new Set(['dd', 'dt']),
- form: new Set(['form']),
- li: new Set(['li']),
- h1: headings,
- h2: headings,
- h3: headings,
- h4: headings,
- h5: headings,
- h6: headings,
- }
|