├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist ├── index.d.cts └── index.d.ts ├── package.json ├── rollup.config.js ├── src ├── content.js ├── highlight.js ├── html.grammar ├── index.js └── tokens.js └── test ├── mixed.txt ├── tags.txt ├── test-html.js ├── test-incremental.js └── vue.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /src/parser.* 3 | .tern-* 4 | /dist 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.3.10 (2024-05-29) 2 | 3 | ### Bug fixes 4 | 5 | Fix an issue that broke attribute reading when defining a nested language on a tag that isn't a `style`, `textearea`, or `script` tag. 6 | 7 | ## 1.3.9 (2024-02-21) 8 | 9 | ### Bug fixes 10 | 11 | When using the "selfClosing" dialect, fix parse errors for `
` (tags that implicitly self-close) and ` 4 | 5 | ==> 6 | 7 | Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag), 8 | ScriptText, 9 | CloseTag(StartCloseTag,TagName,EndTag))) 10 | 11 | # Does parse type-less script tags as JS 12 | 13 | 14 | 15 | ==> 16 | 17 | Document(Element(OpenTag(StartTag,TagName,EndTag), 18 | Script(ExpressionStatement(RegExp)), 19 | CloseTag(StartCloseTag,TagName,EndTag))) 20 | 21 | # Still doesn't end script tags on closing tags 22 | 23 | 24 | 25 | ==> 26 | 27 | Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), 28 | ScriptText, 29 | CloseTag(StartCloseTag,TagName,EndTag))) 30 | 31 | # Missing end tag 32 | 33 | 44 | 45 | ==> 46 | 47 | Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag), 48 | Script(...), 49 | CloseTag(StartCloseTag,TagName,EndTag))) 50 | 51 | # JS with unquoted script type 52 | 53 | 54 | 55 | ==> 56 | 57 | Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), 58 | Script(...), 59 | CloseTag(StartCloseTag,TagName,EndTag))) 60 | 61 | # Error in JS 62 | 63 | 64 | 65 | ==> 66 | 67 | Document(Element(OpenTag(StartTag,TagName,EndTag), 68 | Script(...), 69 | CloseTag(StartCloseTag,TagName,EndTag))) 70 | -------------------------------------------------------------------------------- /test/tags.txt: -------------------------------------------------------------------------------- 1 | # Regular tag 2 | 3 | bar 4 | 5 | ==> 6 | 7 | Document(Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag))) 8 | 9 | # Nested tag 10 | 11 | c
12 | 13 | ==> 14 | 15 | Document(Element(OpenTag(StartTag,TagName,EndTag), 16 | Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag)), 17 | Element(SelfClosingTag(StartTag,TagName,EndTag)), 18 | CloseTag(StartCloseTag,TagName,EndTag))) 19 | 20 | # Attribute 21 | 22 |
23 | 24 | ==> 25 | 26 | Document(Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag))) 27 | 28 | # Multiple attributes 29 | 30 | 31 | 32 | ==> 33 | 34 | Document(Element(OpenTag(StartTag,TagName, 35 | Attribute(AttributeName,Is,AttributeValue), 36 | Attribute(AttributeName,Is,AttributeValue), 37 | Attribute(AttributeName,Is,AttributeValue),EndTag), 38 | CloseTag(StartCloseTag,TagName,EndTag))) 39 | 40 | # Value-less attributes 41 | 42 | 43 | 44 | ==> 45 | 46 | Document(Element(OpenTag(StartTag,TagName, 47 | Attribute(AttributeName), 48 | Attribute(AttributeName,Is,AttributeValue), 49 | Attribute(AttributeName),EndTag), 50 | CloseTag(StartCloseTag,TagName,EndTag))) 51 | 52 | # Unquoted attributes 53 | 54 | 55 | 56 | ==> 57 | 58 | Document(Element(OpenTag(StartTag,TagName, 59 | Attribute(AttributeName,Is,UnquotedAttributeValue), 60 | Attribute(AttributeName), 61 | Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), 62 | CloseTag(StartCloseTag,TagName,EndTag))) 63 | 64 | # Unquoted attributes with slashes 65 | 66 | 67 | 68 | ==> 69 | 70 | Document(Element(SelfClosingTag(StartTag,TagName, 71 | Attribute(AttributeName,Is,UnquotedAttributeValue), 72 | Attribute(AttributeName,Is,UnquotedAttributeValue), 73 | Attribute(AttributeName,Is,UnquotedAttributeValue), 74 | Attribute(AttributeName,Is,UnquotedAttributeValue), 75 | EndTag))) 76 | 77 | # Single-quoted attributes 78 | 79 | 80 | 81 | ==> 82 | 83 | Document(Element(SelfClosingTag(StartTag, TagName, 84 | Attribute(AttributeName, Is, AttributeValue), 85 | Attribute(AttributeName, Is, AttributeValue(EntityReference)), 86 | EndTag))) 87 | 88 | # Entities 89 | 90 | &C 91 | 92 | ==> 93 | 94 | Document(Element(OpenTag(StartTag,TagName, 95 | Attribute(AttributeName,Is,AttributeValue(EntityReference)),EndTag), 96 | EntityReference,CharacterReference, 97 | CloseTag(StartCloseTag,TagName,EndTag))) 98 | 99 | # Doctype 100 | 101 | 102 | 103 | 104 | ==> 105 | 106 | Document(DoctypeDecl,Text,Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag))) 107 | 108 | # Processing instructions 109 | 110 | 111 | 112 | ==> 113 | 114 | Document(ProcessingInst,Element(OpenTag(StartTag,TagName,EndTag),ProcessingInst,CloseTag(StartCloseTag,TagName,EndTag))) 115 | 116 | # Comments 117 | 118 | 119 | text 120 | 121 | 123 | 124 | ==> 125 | 126 | Document(Comment,Text,Element(OpenTag(StartTag,TagName,EndTag),Comment,Text,CloseTag(StartCloseTag,TagName,EndTag)),Text,Comment,Text,Comment) 127 | 128 | # Mismatched tag 129 | 130 | 131 | 132 | ==> 133 | 134 | Document(Element(OpenTag(StartTag,TagName,EndTag),MismatchedCloseTag(StartCloseTag,TagName,EndTag))) 135 | 136 | # Unclosed tag 137 | 138 | 139 | 140 | ==> 141 | 142 | Document(Element(OpenTag(StartTag,TagName,EndTag))) 143 | 144 | # Ignore pseudo-xml self-closers 145 | 146 |
147 | 148 | ==> 149 | 150 | Document(Element(SelfClosingTag(StartTag,TagName,EndTag))) 151 | 152 | # Unclosed implicitly closed tag 153 | 154 |

155 | 156 | ==> 157 | 158 | Document(Element(OpenTag(StartTag,TagName,EndTag))) 159 | 160 | # Nested mismatched tag 161 | 162 | 163 | 164 | ==> 165 | 166 | Document(Element(OpenTag(StartTag,TagName,EndTag), 167 | Element(OpenTag(StartTag,TagName,EndTag), 168 | Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag)), 169 | MismatchedCloseTag(StartCloseTag,TagName,EndTag), 170 | ⚠), 171 | CloseTag(StartCloseTag,TagName,EndTag))) 172 | 173 | # Mismatched tag with whitespace 174 | 175 | 176 | < 177 | foo bar="10"> 178 | blah 179 | 180 | 181 | 182 | ==> 183 | 184 | Document(Element(OpenTag(StartTag,TagName,EndTag), 185 | Text, 186 | Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag), 187 | Text, 188 | MismatchedCloseTag(StartCloseTag,TagName,EndTag), 189 | Text, ⚠), 190 | CloseTag(StartCloseTag,TagName,EndTag))) 191 | 192 | # Incomplete close tag 193 | 194 | 195 | 196 | ==> 197 | 198 | Document(Element(OpenTag(StartTag,TagName,EndTag), 199 | Element(OpenTag(StartTag,TagName,EndTag), IncompleteCloseTag, ⚠), 200 | CloseTag(StartCloseTag,TagName,EndTag))) 201 | 202 | # Re-synchronize close tags 203 | 204 | 205 | 206 | ==> 207 | 208 | Document(Element(OpenTag(StartTag,TagName,EndTag), 209 | Element(OpenTag(StartTag,TagName,EndTag), 210 | Element(OpenTag(StartTag,TagName,EndTag), 211 | MismatchedCloseTag(StartCloseTag,TagName,EndTag), 212 | CloseTag(StartCloseTag,TagName,EndTag)), 213 | ⚠), 214 | CloseTag(StartCloseTag,TagName,EndTag))) 215 | 216 | # Top-level mismatched close tag 217 | 218 | 219 | 220 | ==> 221 | 222 | Document( 223 | Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag)), 224 | MismatchedCloseTag(StartCloseTag,TagName,EndTag)) 225 | 226 | # Self-closing tags 227 | 228 | 229 | 230 | ==> 231 | 232 | Document(Element(OpenTag(StartTag,TagName,EndTag), 233 | Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag)), 234 | CloseTag(StartCloseTag,TagName,EndTag))) 235 | 236 | # Implicitly closed 237 | 238 |

Hello
239 | 240 | ==> 241 | 242 | Document(Element(OpenTag(StartTag,TagName,EndTag), 243 | Element(OpenTag(StartTag,TagName,EndTag),Text), 244 | CloseTag(StartCloseTag,TagName,EndTag))) 245 | 246 | # Closed by sibling 247 | 248 |
249 |

Foo 250 |

Bar 251 |

252 | 253 | ==> 254 | 255 | Document(Element(OpenTag(StartTag,TagName,EndTag), 256 | Text, 257 | Element(OpenTag(StartTag,TagName,EndTag),Text), 258 | Element(OpenTag(StartTag,TagName,EndTag),Text), 259 | CloseTag(StartCloseTag,TagName,EndTag))) 260 | 261 | # Closed by sibling at top 262 | 263 |

Foo 264 |

Bar 265 | 266 | ==> 267 | 268 | Document(Element(OpenTag(StartTag,TagName,EndTag),Text),Element(OpenTag(StartTag,TagName,EndTag),Text)) 269 | 270 | # Textarea 271 | 272 |

Enter something: 275 | 276 | ==> 277 | 278 | Document(Element(OpenTag(StartTag,TagName,EndTag), 279 | Text, 280 | Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), 281 | TextareaText, 282 | CloseTag(StartCloseTag,TagName,EndTag)))) 283 | 284 | # Script 285 | 286 | 287 | 288 | ==> 289 | 290 | Document(Element(OpenTag(StartTag,TagName,EndTag),ScriptText,CloseTag(StartCloseTag,TagName,EndTag))) 291 | 292 | # Allows whitespace in tags 293 | 294 | < 295 | body 296 | foo=bar 297 | >hi 300 | 301 | ==> 302 | 303 | Document(Element( 304 | OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), 305 | Text, 306 | CloseTag(StartCloseTag,TagName,EndTag))) 307 | 308 | # Doesn't get confused by a stray ampersand 309 | 310 | a&b 311 | 312 | ==> 313 | 314 | Document(Element(OpenTag(StartTag,TagName,EndTag),Text,InvalidEntity,Text,CloseTag(StartCloseTag,TagName,EndTag))) 315 | 316 | # Can ignore mismatches {"dialect": "noMatch"} 317 | 318 |

foo

319 | 320 | ==> 321 | 322 | Document(Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag))) 323 | 324 | # Can handle lone close tags {"dialect": "noMatch"} 325 | 326 | 327 | 328 | ==> 329 | 330 | Document(CloseTag(StartCloseTag,TagName,EndTag)) 331 | 332 | # Parses ampersands in attributes 333 | 334 | 335 | 336 | ==> 337 | 338 | Document(Element(SelfClosingTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue(InvalidEntity)), EndTag))) 339 | 340 | # Supports self-closing dialect {"dialect": "selfClosing"} 341 | 342 |
343 | 344 | ==> 345 | 346 | Document(Element( 347 | OpenTag(StartTag,TagName,EndTag), 348 | Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),SelfClosingEndTag)), 349 | CloseTag(StartCloseTag,TagName,EndTag))) 350 | 351 | # Allows self-closing in foreign elements 352 | 353 |
354 | 355 | ==> 356 | 357 | Document(Element(OpenTag(StartTag,TagName,EndTag), 358 | Element(OpenTag(StartTag,TagName,EndTag), 359 | Element(SelfClosingTag(StartTag,TagName,SelfClosingEndTag)), 360 | CloseTag(StartCloseTag,TagName,EndTag)), 361 | CloseTag(StartCloseTag,TagName,EndTag))) 362 | 363 | # Parses multiple unfinished tags in a row 364 | 365 |
370 | 371 | Document(Element(OpenTag(StartTag,TagName,⚠), 372 | Element(OpenTag(StartTag,TagName,⚠), 373 | Element(OpenTag(StartTag,TagName,⚠),⚠),⚠),⚠)) 374 | 375 | # Allows self-closing on special tags {"dialect": "selfClosing"} 376 | 377 | 378 |
379 |