Premier commit déjà bien avancé

This commit is contained in:
2025-11-10 18:33:24 +01:00
commit db4f0508cb
652 changed files with 440521 additions and 0 deletions

297
frontend/node_modules/@lezer/html/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,297 @@
## 1.3.12 (2025-09-26)
### Bug fixes
Emit tokens for `<` characters without tag name, so that autocompletion can work with them.
## 1.3.11 (2025-09-25)
### Bug fixes
Don't parse `<` followed by whitespace as the start of a tag.
## 1.3.10 (2024-05-29)
### Bug fixes
Fix an issue that broke attribute reading when defining a nested language on a tag that isn't a `style`, `textearea`, or `script` tag.
## 1.3.9 (2024-02-21)
### Bug fixes
When using the "selfClosing" dialect, fix parse errors for `<br/>` (tags that implicitly self-close) and `<script/>` (special tags).
## 1.3.8 (2023-12-28)
### Bug fixes
Add `bidiIsolate` node props for tags, comments, and attributes.
Mark tags, attributes, and comments as isolating for bidirectional text.
## 1.3.7 (2023-11-23)
### Bug fixes
Fix a bug that caused the parser to not properly recognize empty HTML comments. Don't emit nested parses for empty elements in configureNesting
Fix an issue where `configureNesting` could emit empty overlay ranges for empty elements.
## 1.3.6 (2023-07-12)
### Bug fixes
Improve parsing of multiple unfinished opening tags by disallowing less-than characters in attribute names.
## 1.3.5 (2023-07-03)
### Bug fixes
Make the package work with new TS resolution styles.
## 1.3.4 (2023-03-27)
### Bug fixes
Fix a bug that caused mixed parsing of non-style/script elements to attach the nested parse tree to the incorrect node.
## 1.3.3 (2023-02-21)
### Bug fixes
Make sure build output is strictly ES6 again.
## 1.3.2 (2023-02-16)
### Bug fixes
Fix a crash caused by nexted parsing of attributes returning invalid ranges for attributes missing their closing quote.
## 1.3.1 (2023-02-11)
### Bug fixes
Always allow self-closing tags in `<svg>` and `<math>` tags.
## 1.3.0 (2022-12-16)
### New features
`configureNesting` now supports targeting tags other than `style`, `script`, and `textarea`.
## 1.2.0 (2022-11-25)
### New features
`configureNesting` now takes a second argument that can be used to specify that the value of some attributes should be parsed with an external parser.
## 1.1.1 (2022-11-18)
### Bug fixes
Align allowed characters in unquoted attribute values with the spec. Replace another unicode escape with a character
## 1.1.0 (2022-11-16)
### Bug fixes
Allow more characters in attribute names, following HTML spec.
### New features
The new `"selfClosing"` dialect allows `/>` syntax to be used to create self-closing tags.
## 1.0.1 (2022-07-27)
### Bug fixes
Continue parsing when an invalid entity reference appears in an attribute value.
## 1.0.0 (2022-06-06)
### New features
First stable version.
## 0.16.1 (2022-05-16)
### Bug fixes
Fix a bug where comment end tokens preceded by dashes were sometimes not recognized.
## 0.16.0 (2022-04-20)
### Breaking changes
Move to 0.16 serialized parser format.
### New features
The parser now includes syntax highlighting information in its node types.
## 0.15.1 (2022-02-16)
### Bug fixes
Make sure the tree for unfinished self-closing tags shows them as self-closing.
## 0.15.0 (2021-08-11)
### Breaking changes
The module's name changed from `lezer-html` to `@lezer/html`.
Upgrade to the 0.15.0 lezer interfaces.
## 0.13.6 (2021-06-16)
### Bug fixes
Add a rule for invalid `&` syntax to avoid error-recovery around stray ampersands.
## 0.13.5 (2021-05-05)
### Bug fixes
Fix a problem where like attributes inappropriately included trailing whitespace.
## 0.13.4 (2021-03-10)
### Bug fixes
Strip quotes from attribute values passed to `attrs` predicates in `configureNesting`.
## 0.13.3 (2021-02-17)
### Bug fixes
Optimize the tokenizer by using a context tracker.
## 0.13.2 (2021-01-22)
### Bug fixes
Make comments consist of multiple tokens, so that huge comments don't freeze the parser.
## 0.13.1 (2020-12-04)
### Bug fixes
Fix versions of lezer packages depended on.
## 0.13.0 (2020-12-04)
### Breaking changes
The nested parser configuration utility is now called `configureNesting`, and returns an object to pass to `Parser.configure`'s `nested` option instead of a new parser.
### New features
The parser can now be given a "noMatch" dialect to not mark mismatched tags.
## 0.12.0 (2020-10-23)
### Breaking changes
Adjust to changed serialized parser format.
### New features
The parser now more effectively matches close and open tags, even in the presence of mismatched tags.
## 0.11.1 (2020-09-26)
### Bug fixes
Fix lezer depencency versions
## 0.11.0 (2020-09-26)
### Breaking changes
Follow change in serialized parser format.
## 0.10.0 (2020-08-07)
### Breaking changes
Upgrade to 0.10 parser serialization
## 0.9.0 (2020-06-08)
### Breaking changes
Upgrade to 0.9 parser serialization
### New features
Tag start/end tokens now have `NodeProp.openedBy`/`closedBy` props.
## 0.8.4 (2020-04-09)
### Bug fixes
Regenerate parser with a fix in lezer-generator so that the top node prop is properly assigned.
## 0.8.3 (2020-04-01)
### Bug fixes
Make the package load as an ES module on node
## 0.8.2 (2020-02-28)
### New features
Provide an ES module file.
## 0.8.1 (2020-02-26)
### Bug fixes
Adds support for single-quoted attribute values.
Don't treat /> tag ends as self-closing, just ignore them instead.
## 0.8.0 (2020-02-03)
### New features
Follow 0.8.0 release of the library.
## 0.7.0 (2020-01-20)
### Breaking changes
Use the lezer 0.7.0 parser format.
## 0.5.2 (2020-01-15)
### Bug fixes
Allow whitespace between the `<` and `/` in a close tag.
## 0.5.1 (2019-10-22)
### Bug fixes
Fix top prop missing from build output.
## 0.5.0 (2019-10-22)
### Breaking changes
Move from `lang` to `top` prop on document node.
## 0.4.0 (2019-09-10)
### Breaking changes
Adjust to 0.4.0 parse table format.
## 0.3.0 (2019-08-22)
### New features
First numbered release.

21
frontend/node_modules/@lezer/html/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

37
frontend/node_modules/@lezer/html/README.md generated vendored Normal file
View File

@ -0,0 +1,37 @@
# @lezer/html
This is an HTML grammar for the
[lezer](https://lezer.codemirror.net/) parser system.
The code is licensed under an MIT license.
## Interface
This package exports two bindings:
**`parser`**`: Parser`
The parser instance for the basic HTML grammar. Supports two dialects:
- `"noMatch"` turns off tag matching, creating regular syntax nodes
even for mismatched tags.
- `"selfClosing"` adds support for `/>` self-closing tag syntax.
**`configureNesting`**`(tags?: {`\
`  tag: string,`\
`  attrs?: (attrs: {[attr: string]: string}) => boolean,`\
`  parser: Parser,`\
`}[], attributes?: {`\
`  name: string,`\
`  tagName?: string,`\
`  parser: Parser,`\
`}[]): ParseWrapper`
Create a nested parser config object which overrides the way the
content of some tags or attributes is parsed. Each tag override is an
object with a `tag` property holding the (lower case) tag name to
override, and an optional `attrs` predicate that, if given, has to
return true for the tag's attributes for this override to apply.
The `parser` property describes the way the tag's content is parsed.

354
frontend/node_modules/@lezer/html/dist/index.cjs generated vendored Normal file

File diff suppressed because one or more lines are too long

14
frontend/node_modules/@lezer/html/dist/index.d.cts generated vendored Normal file
View File

@ -0,0 +1,14 @@
import {LRParser} from "@lezer/lr"
import {Input, PartialParse, Parser, TreeCursor, ParseWrapper} from "@lezer/common"
export const parser: LRParser
export function configureNesting(tags?: readonly {
tag: string,
attrs?: (attrs: {[attr: string]: string}) => boolean,
parser: Parser
}[], attributes?: {
name: string,
tagName?: string,
parser: Parser
}[]): ParseWrapper

14
frontend/node_modules/@lezer/html/dist/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,14 @@
import {LRParser} from "@lezer/lr"
import {Input, PartialParse, Parser, TreeCursor, ParseWrapper} from "@lezer/common"
export const parser: LRParser
export function configureNesting(tags?: readonly {
tag: string,
attrs?: (attrs: {[attr: string]: string}) => boolean,
parser: Parser
}[], attributes?: {
name: string,
tagName?: string,
parser: Parser
}[]): ParseWrapper

349
frontend/node_modules/@lezer/html/dist/index.js generated vendored Normal file

File diff suppressed because one or more lines are too long

37
frontend/node_modules/@lezer/html/package.json generated vendored Normal file
View File

@ -0,0 +1,37 @@
{
"name": "@lezer/html",
"version": "1.3.12",
"description": "lezer-based HTML grammar",
"main": "dist/index.cjs",
"type": "module",
"exports": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"module": "dist/index.js",
"types": "dist/index.d.ts",
"author": "Marijn Haverbeke <marijn@haverbeke.berlin>",
"license": "MIT",
"devDependencies": {
"@lezer/javascript": "^1.0.0",
"@lezer/generator": "^1.0.0",
"mocha": "^10.2.0",
"rollup": "^2.52.2",
"@rollup/plugin-node-resolve": "^9.0.0"
},
"dependencies": {
"@lezer/lr": "^1.0.0",
"@lezer/highlight": "^1.0.0",
"@lezer/common": "^1.2.0"
},
"repository": {
"type" : "git",
"url" : "https://github.com/lezer-parser/html.git"
},
"scripts": {
"build": "lezer-generator src/html.grammar -o src/parser && rollup -c",
"build-debug": "lezer-generator src/html.grammar --names -o src/parser && rollup -c",
"prepare": "npm run build",
"test": "mocha test/test-*.js"
}
}

16
frontend/node_modules/@lezer/html/rollup.config.js generated vendored Normal file
View File

@ -0,0 +1,16 @@
import {nodeResolve} from "@rollup/plugin-node-resolve"
export default {
input: "./src/index.js",
output: [{
format: "cjs",
file: "./dist/index.cjs"
}, {
format: "es",
file: "./dist/index.js"
}],
external(id) { return !/^[\.\/]/.test(id) },
plugins: [
nodeResolve()
]
}

87
frontend/node_modules/@lezer/html/src/content.js generated vendored Normal file
View File

@ -0,0 +1,87 @@
import {ScriptText, StyleText, TextareaText,
Element, TagName, Attribute, AttributeName, OpenTag, CloseTag,
AttributeValue, UnquotedAttributeValue} from "./parser.terms.js"
import {parseMixed} from "@lezer/common"
function getAttrs(openTag, input) {
let attrs = Object.create(null)
for (let att of openTag.getChildren(Attribute)) {
let name = att.getChild(AttributeName), value = att.getChild(AttributeValue) || att.getChild(UnquotedAttributeValue)
if (name) attrs[input.read(name.from, name.to)] =
!value ? "" : value.type.id == AttributeValue ? input.read(value.from + 1, value.to - 1) : input.read(value.from, value.to)
}
return attrs
}
function findTagName(openTag, input) {
let tagNameNode = openTag.getChild(TagName)
return tagNameNode ? input.read(tagNameNode.from, tagNameNode.to) : " "
}
function maybeNest(node, input, tags) {
let attrs
for (let tag of tags) {
if (!tag.attrs || tag.attrs(attrs || (attrs = getAttrs(node.node.parent.firstChild, input))))
return {parser: tag.parser}
}
return null
}
// tags?: {
// tag: string,
// attrs?: ({[attr: string]: string}) => boolean,
// parser: Parser
// }[]
// attributes?: {
// name: string,
// tagName?: string,
// parser: Parser
// }[]
export function configureNesting(tags = [], attributes = []) {
let script = [], style = [], textarea = [], other = []
for (let tag of tags) {
let array = tag.tag == "script" ? script : tag.tag == "style" ? style : tag.tag == "textarea" ? textarea : other
array.push(tag)
}
let attrs = attributes.length ? Object.create(null) : null
for (let attr of attributes) (attrs[attr.name] || (attrs[attr.name] = [])).push(attr)
return parseMixed((node, input) => {
let id = node.type.id
if (id == ScriptText) return maybeNest(node, input, script)
if (id == StyleText) return maybeNest(node, input, style)
if (id == TextareaText) return maybeNest(node, input, textarea)
if (id == Element && other.length) {
let n = node.node, open = n.firstChild, tagName = open && findTagName(open, input), attrs
if (tagName) for (let tag of other) {
if (tag.tag == tagName && (!tag.attrs || tag.attrs(attrs || (attrs = getAttrs(open, input))))) {
let close = n.lastChild
let to = close.type.id == CloseTag ? close.from : n.to
if (to > open.to)
return {parser: tag.parser, overlay: [{from: open.to, to}]}
}
}
}
if (attrs && id == Attribute) {
let n = node.node, nameNode
if (nameNode = n.firstChild) {
let matches = attrs[input.read(nameNode.from, nameNode.to)]
if (matches) for (let attr of matches) {
if (attr.tagName && attr.tagName != findTagName(n.parent, input)) continue
let value = n.lastChild
if (value.type.id == AttributeValue) {
let from = value.from + 1
let last = value.lastChild, to = value.to - (last && last.isError ? 0 : 1)
if (to > from) return {parser: attr.parser, overlay: [{from, to}]}
} else if (value.type.id == UnquotedAttributeValue) {
return {parser: attr.parser, overlay: [{from: value.from, to: value.to}]}
}
}
}
}
return null
})
}

15
frontend/node_modules/@lezer/html/src/highlight.js generated vendored Normal file
View File

@ -0,0 +1,15 @@
import {styleTags, tags as t} from "@lezer/highlight"
export const htmlHighlighting = styleTags({
"Text RawText IncompleteTag IncompleteCloseTag": t.content,
"StartTag StartCloseTag SelfClosingEndTag EndTag": t.angleBracket,
TagName: t.tagName,
"MismatchedCloseTag/TagName": [t.tagName, t.invalid],
AttributeName: t.attributeName,
"AttributeValue UnquotedAttributeValue": t.attributeValue,
Is: t.definitionOperator,
"EntityReference CharacterReference": t.character,
Comment: t.blockComment,
ProcessingInst: t.processingInstruction,
DoctypeDecl: t.documentMeta
})

181
frontend/node_modules/@lezer/html/src/html.grammar generated vendored Normal file
View File

@ -0,0 +1,181 @@
@top Document { (entity | DoctypeDecl)+ }
@dialects { noMatch, selfClosing }
entity[@isGroup=Entity] {
Text |
EntityReference |
CharacterReference |
InvalidEntity |
Element |
Comment |
ProcessingInst |
IncompleteTag |
IncompleteCloseTag |
MismatchedCloseTag |
NoMatchCloseTag
}
Element {
OpenScriptTag ScriptText (CloseScriptTag | missingCloseTag) |
OpenStyleTag StyleText (CloseStyleTag | missingCloseTag) |
OpenTextareaTag TextareaText (CloseTextareaTag | missingCloseTag) |
OpenTag entity* (CloseTag | missingCloseTag) |
SelfClosingTag
}
ScriptText[group="TextContent Entity"] { scriptText* }
StyleText[group="TextContent Entity"] { styleText* }
TextareaText[group="TextContent Entity"] { textareaText* }
@skip { space } {
OpenTag[closedBy=CloseTag,isolate=ltr] {
StartTag TagName Attribute* EndTag
}
SelfClosingTag[isolate=ltr] {
StartSelfClosingTag TagName Attribute* (EndTag | SelfClosingEndTag) |
(StartTag | StartScriptTag | StartStyleTag | StartTextareaTag) TagName Attribute* SelfClosingEndTag
}
MismatchedCloseTag[isolate=ltr] {
MismatchedStartCloseTag TagName EndTag
}
NoMatchCloseTag[@name=CloseTag,isolate=ltr] {
NoMatchStartCloseTag TagName EndTag
}
CloseTag[openedBy=OpenTag,isolate=ltr] {
StartCloseTag TagName EndTag
}
OpenScriptTag[@name=OpenTag,closedBy=CloseTag,isolate=ltr] {
StartScriptTag TagName Attribute* EndTag
}
CloseScriptTag[@name=CloseTag,openedBy=OpenTag,isolate=ltr] {
StartCloseScriptTag TagName EndTag
}
OpenStyleTag[@name=OpenTag,closedBy=CloseTag,isolate=ltr] {
StartStyleTag TagName Attribute* EndTag
}
CloseStyleTag[@name=CloseTag,openedBy=OpenTag,isolate=ltr] {
StartCloseStyleTag TagName EndTag
}
OpenTextareaTag[@name=OpenTag,closedBy=CloseTag,isolate=ltr] {
StartTextareaTag TagName Attribute* EndTag
}
CloseTextareaTag[@name=CloseTag,openedBy=OpenTag,isolate=ltr] {
StartCloseTextareaTag TagName EndTag
}
Attribute {
AttributeName (Is (AttributeValue | UnquotedAttributeValue))?
}
}
AttributeValue[isolate] {
"\"" (attributeContentDouble | EntityReference | CharacterReference | InvalidEntity)* "\"" |
"\'" (attributeContentSingle | EntityReference | CharacterReference | InvalidEntity)* "\'"
}
Comment[isolate] { commentStart commentContent* commentEnd }
@context elementContext from "./tokens.js"
@external tokens scriptTokens from "./tokens.js" {
scriptText
StartCloseScriptTag[@name=StartCloseTag,closedBy=EndTag]
}
@external tokens styleTokens from "./tokens.js" {
styleText
StartCloseStyleTag[@name=StartCloseTag,closedBy=EndTag]
}
@external tokens textareaTokens from "./tokens.js" {
textareaText
StartCloseTextareaTag[@name=StartCloseTag,closedBy=EndTag]
}
@external tokens endTag from "./tokens.js" {
EndTag[openedBy="StartTag StartCloseTag"]
SelfClosingEndTag[openedBy=StartTag,@dialect=selfClosing]
}
@external tokens tagStart from "./tokens.js" {
StartTag[closedBy="EndTag SelfClosingEndTag"],
StartScriptTag[@name=StartTag,closedBy=EndTag],
StartStyleTag[@name=StartTag,closedBy=EndTag],
StartTextareaTag[@name=StartTag,closedBy=EndTag],
StartSelfClosingTag[@name=StartTag,closedBy=EndTag],
StartCloseTag[closedBy=EndTag],
NoMatchStartCloseTag[@name=StartCloseTag,closedBy=EndTag]
MismatchedStartCloseTag[@name=StartCloseTag,closedBy=EndTag],
missingCloseTag,
IncompleteTag,
IncompleteCloseTag
}
@external tokens commentContent from "./tokens.js" {
commentContent
}
@tokens {
nameStart {
":" | @asciiLetter | "_" |
$[\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D] |
$[\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u{10000}-\u{EFFFF}]
}
nameChar {
nameStart | "-" | "." | @digit | $[\u00B7\u0300-\u036F\u203F-\u2040]
}
identifier { nameStart nameChar* }
TagName { identifier }
AttributeName { ![\u0000-\u0020\u007F-\u009F"'<>/=\uFDD0-\uFDEF\uFFFE\uFFFF]+ }
UnquotedAttributeValue[isolate] { ![ \t\n\r\u000C=<>"'`]+ }
attributeContentDouble { !["&]+ }
attributeContentSingle { !['&]+ }
Is { "=" }
EntityReference { "&" ![#; ]+ ";" }
CharacterReference { "&#" ![; ]+ ";" }
InvalidEntity { "&" }
@precedence { CharacterReference, EntityReference, InvalidEntity }
Text[group=TextContent] { ![<&]+ }
commentStart { "<!--" }
commentEnd { "-->" }
ProcessingInst { "<?" piContent }
piContent { ![?] piContent | "?" piQuestion }
piQuestion { ![>] piContent | ">" }
DoctypeDecl { "<!" ("doctype" | "DOCTYPE") ![>]* ">" }
@precedence { commentStart, ProcessingInst, DoctypeDecl }
space { (" " | "\t" | "\r" | "\n")+ }
}
@external propSource htmlHighlighting from "./highlight"

2
frontend/node_modules/@lezer/html/src/index.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
export {parser} from "./parser"
export {configureNesting} from "./content"

27
frontend/node_modules/@lezer/html/src/parser.js generated vendored Normal file

File diff suppressed because one or more lines are too long

53
frontend/node_modules/@lezer/html/src/parser.terms.js generated vendored Normal file
View File

@ -0,0 +1,53 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
scriptText = 55,
StartCloseScriptTag = 1,
styleText = 56,
StartCloseStyleTag = 2,
textareaText = 57,
StartCloseTextareaTag = 3,
EndTag = 4,
SelfClosingEndTag = 5,
StartTag = 6,
StartScriptTag = 7,
StartStyleTag = 8,
StartTextareaTag = 9,
StartSelfClosingTag = 10,
StartCloseTag = 11,
NoMatchStartCloseTag = 12,
MismatchedStartCloseTag = 13,
missingCloseTag = 58,
IncompleteTag = 14,
IncompleteCloseTag = 15,
commentContent = 59,
Document = 16,
Text = 17,
EntityReference = 18,
CharacterReference = 19,
InvalidEntity = 20,
Element = 21,
OpenScriptTag = 22,
TagName = 23,
Attribute = 24,
AttributeName = 25,
Is = 26,
AttributeValue = 27,
UnquotedAttributeValue = 28,
ScriptText = 29,
CloseScriptTag = 30,
OpenStyleTag = 31,
StyleText = 32,
CloseStyleTag = 33,
OpenTextareaTag = 34,
TextareaText = 35,
CloseTextareaTag = 36,
OpenTag = 37,
CloseTag = 38,
SelfClosingTag = 39,
Comment = 40,
ProcessingInst = 41,
MismatchedCloseTag = 42,
NoMatchCloseTag = 43,
DoctypeDecl = 44,
Dialect_noMatch = 0,
Dialect_selfClosing = 1

199
frontend/node_modules/@lezer/html/src/tokens.js generated vendored Normal file
View File

@ -0,0 +1,199 @@
/* Hand-written tokenizers for HTML. */
import {ExternalTokenizer, ContextTracker} from "@lezer/lr"
import {StartTag, StartCloseTag, NoMatchStartCloseTag, MismatchedStartCloseTag, missingCloseTag,
StartSelfClosingTag, IncompleteCloseTag, Element, OpenTag, IncompleteTag,
StartScriptTag, scriptText, StartCloseScriptTag,
StartStyleTag, styleText, StartCloseStyleTag,
StartTextareaTag, textareaText, StartCloseTextareaTag,
Dialect_noMatch, Dialect_selfClosing, EndTag, SelfClosingEndTag,
commentContent as cmntContent} from "./parser.terms.js"
const selfClosers = {
area: true, base: true, br: true, col: true, command: true,
embed: true, frame: true, hr: true, img: true, input: true,
keygen: true, link: true, meta: true, param: true, source: true,
track: true, wbr: true, menuitem: true
}
const implicitlyClosed = {
dd: true, li: true, optgroup: true, option: true, p: true,
rp: true, rt: true, tbody: true, td: true, tfoot: true,
th: true, tr: true
}
const closeOnOpen = {
dd: {dd: true, dt: true},
dt: {dd: true, dt: true},
li: {li: true},
option: {option: true, optgroup: true},
optgroup: {optgroup: true},
p: {
address: true, article: true, aside: true, blockquote: true, dir: true,
div: true, dl: true, fieldset: true, footer: true, form: true,
h1: true, h2: true, h3: true, h4: true, h5: true, h6: true,
header: true, hgroup: true, hr: true, menu: true, nav: true, ol: true,
p: true, pre: true, section: true, table: true, ul: true
},
rp: {rp: true, rt: true},
rt: {rp: true, rt: true},
tbody: {tbody: true, tfoot: true},
td: {td: true, th: true},
tfoot: {tbody: true},
th: {td: true, th: true},
thead: {tbody: true, tfoot: true},
tr: {tr: true}
}
function nameChar(ch) {
return ch == 45 || ch == 46 || ch == 58 || ch >= 65 && ch <= 90 || ch == 95 || ch >= 97 && ch <= 122 || ch >= 161
}
function isSpace(ch) {
return ch == 9 || ch == 10 || ch == 13 || ch == 32
}
let cachedName = null, cachedInput = null, cachedPos = 0
function tagNameAfter(input, offset) {
let pos = input.pos + offset
if (cachedPos == pos && cachedInput == input) return cachedName
let next = input.peek(offset), name = ""
for (;;) {
if (!nameChar(next)) break
name += String.fromCharCode(next)
next = input.peek(++offset)
}
// Undefined to signal there's a <? or <!, null for just missing
cachedInput = input; cachedPos = pos
return cachedName = name ? name.toLowerCase() : next == question || next == bang ? undefined : null
}
const lessThan = 60, greaterThan = 62, slash = 47, question = 63, bang = 33, dash = 45
function ElementContext(name, parent) {
this.name = name
this.parent = parent
}
const startTagTerms = [StartTag, StartSelfClosingTag, StartScriptTag, StartStyleTag, StartTextareaTag]
export const elementContext = new ContextTracker({
start: null,
shift(context, term, stack, input) {
return startTagTerms.indexOf(term) > -1 ? new ElementContext(tagNameAfter(input, 1) || "", context) : context
},
reduce(context, term) {
return term == Element && context ? context.parent : context
},
reuse(context, node, stack, input) {
let type = node.type.id
return type == StartTag || type == OpenTag
? new ElementContext(tagNameAfter(input, 1) || "", context) : context
},
strict: false
})
export const tagStart = new ExternalTokenizer((input, stack) => {
if (input.next != lessThan) {
// End of file, close any open tags
if (input.next < 0 && stack.context) input.acceptToken(missingCloseTag)
return
}
input.advance()
let close = input.next == slash
if (close) input.advance()
let name = tagNameAfter(input, 0)
if (name === undefined) return
if (!name) return input.acceptToken(close ? IncompleteCloseTag : IncompleteTag)
let parent = stack.context ? stack.context.name : null
if (close) {
if (name == parent) return input.acceptToken(StartCloseTag)
if (parent && implicitlyClosed[parent]) return input.acceptToken(missingCloseTag, -2)
if (stack.dialectEnabled(Dialect_noMatch)) return input.acceptToken(NoMatchStartCloseTag)
for (let cx = stack.context; cx; cx = cx.parent) if (cx.name == name) return
input.acceptToken(MismatchedStartCloseTag)
} else {
if (name == "script") return input.acceptToken(StartScriptTag)
if (name == "style") return input.acceptToken(StartStyleTag)
if (name == "textarea") return input.acceptToken(StartTextareaTag)
if (selfClosers.hasOwnProperty(name)) return input.acceptToken(StartSelfClosingTag)
if (parent && closeOnOpen[parent] && closeOnOpen[parent][name]) input.acceptToken(missingCloseTag, -1)
else input.acceptToken(StartTag)
}
}, {contextual: true})
export const commentContent = new ExternalTokenizer(input => {
for (let dashes = 0, i = 0;; i++) {
if (input.next < 0) {
if (i) input.acceptToken(cmntContent)
break
}
if (input.next == dash) {
dashes++
} else if (input.next == greaterThan && dashes >= 2) {
if (i >= 3) input.acceptToken(cmntContent, -2)
break
} else {
dashes = 0
}
input.advance()
}
})
function inForeignElement(context) {
for (; context; context = context.parent)
if (context.name == "svg" || context.name == "math") return true
return false
}
export const endTag = new ExternalTokenizer((input, stack) => {
if (input.next == slash && input.peek(1) == greaterThan) {
let selfClosing = stack.dialectEnabled(Dialect_selfClosing) || inForeignElement(stack.context)
input.acceptToken(selfClosing ? SelfClosingEndTag : EndTag, 2)
} else if (input.next == greaterThan) {
input.acceptToken(EndTag, 1)
}
})
function contentTokenizer(tag, textToken, endToken) {
let lastState = 2 + tag.length
return new ExternalTokenizer(input => {
// state means:
// - 0 nothing matched
// - 1 '<' matched
// - 2 '</'
// - 3-(1+tag.length) part of the tag matched
// - lastState whole tag + possibly whitespace matched
for (let state = 0, matchedLen = 0, i = 0;; i++) {
if (input.next < 0) {
if (i) input.acceptToken(textToken)
break
}
if (state == 0 && input.next == lessThan ||
state == 1 && input.next == slash ||
state >= 2 && state < lastState && input.next == tag.charCodeAt(state - 2)) {
state++
matchedLen++
} else if (state == lastState && input.next == greaterThan) {
if (i > matchedLen)
input.acceptToken(textToken, -matchedLen)
else
input.acceptToken(endToken, -(matchedLen - 2))
break
} else if ((input.next == 10 /* '\n' */ || input.next == 13 /* '\r' */) && i) {
input.acceptToken(textToken, 1)
break
} else {
state = matchedLen = 0
}
input.advance()
}
})
}
export const scriptTokens = contentTokenizer("script", scriptText, StartCloseScriptTag)
export const styleTokens = contentTokenizer("style", styleText, StartCloseStyleTag)
export const textareaTokens = contentTokenizer("textarea", textareaText, StartCloseTextareaTag)

69
frontend/node_modules/@lezer/html/test/mixed.txt generated vendored Normal file
View File

@ -0,0 +1,69 @@
# Doesn't parse VB as JS
<script type="text/visualbasic">let something = 20</script>
==>
Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag),
ScriptText,
CloseTag(StartCloseTag,TagName,EndTag)))
# Does parse type-less script tags as JS
<script>/foo/</script>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Script(ExpressionStatement(RegExp)),
CloseTag(StartCloseTag,TagName,EndTag)))
# Still doesn't end script tags on closing tags
<script type=something></foo></script>
==>
Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag),
ScriptText,
CloseTag(StartCloseTag,TagName,EndTag)))
# Missing end tag
<html><script>null
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),
Script(ExpressionStatement(null)))))
# JS with script type
<script type="text/javascript">console.log(2)</script>
==>
Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag),
Script(...),
CloseTag(StartCloseTag,TagName,EndTag)))
# JS with unquoted script type
<script type=module>console.log(2)</script>
==>
Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag),
Script(...),
CloseTag(StartCloseTag,TagName,EndTag)))
# Error in JS
<script>a b</script>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Script(...),
CloseTag(StartCloseTag,TagName,EndTag)))

370
frontend/node_modules/@lezer/html/test/tags.txt generated vendored Normal file
View File

@ -0,0 +1,370 @@
# Regular tag
<foo>bar</foo>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag)))
# Nested tag
<a><b>c</b><br></a>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag)),
Element(SelfClosingTag(StartTag,TagName,EndTag)),
CloseTag(StartCloseTag,TagName,EndTag)))
# Attribute
<br foo="bar">
==>
Document(Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag)))
# Multiple attributes
<a x="one" y="two" z="three"></a>
==>
Document(Element(OpenTag(StartTag,TagName,
Attribute(AttributeName,Is,AttributeValue),
Attribute(AttributeName,Is,AttributeValue),
Attribute(AttributeName,Is,AttributeValue),EndTag),
CloseTag(StartCloseTag,TagName,EndTag)))
# Value-less attributes
<a x y="one" z></a>
==>
Document(Element(OpenTag(StartTag,TagName,
Attribute(AttributeName),
Attribute(AttributeName,Is,AttributeValue),
Attribute(AttributeName),EndTag),
CloseTag(StartCloseTag,TagName,EndTag)))
# Unquoted attributes
<a x=one y z=two></a>
==>
Document(Element(OpenTag(StartTag,TagName,
Attribute(AttributeName,Is,UnquotedAttributeValue),
Attribute(AttributeName),
Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag),
CloseTag(StartCloseTag,TagName,EndTag)))
# Unquoted attributes with slashes
<link as=font crossorigin=anonymous href=/fonts/google-sans/regular/latin.woff2 rel=preload>
==>
Document(Element(SelfClosingTag(StartTag,TagName,
Attribute(AttributeName,Is,UnquotedAttributeValue),
Attribute(AttributeName,Is,UnquotedAttributeValue),
Attribute(AttributeName,Is,UnquotedAttributeValue),
Attribute(AttributeName,Is,UnquotedAttributeValue),
EndTag)))
# Single-quoted attributes
<link x='one' z='two&amp;'>
==>
Document(Element(SelfClosingTag(StartTag, TagName,
Attribute(AttributeName, Is, AttributeValue),
Attribute(AttributeName, Is, AttributeValue(EntityReference)),
EndTag)))
# Entities
<a attr="one&amp;two">&amp;&#67;</a>
==>
Document(Element(OpenTag(StartTag,TagName,
Attribute(AttributeName,Is,AttributeValue(EntityReference)),EndTag),
EntityReference,CharacterReference,
CloseTag(StartCloseTag,TagName,EndTag)))
# Doctype
<!doctype html>
<doc></doc>
==>
Document(DoctypeDecl,Text,Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag)))
# Processing instructions
<?foo?><bar><?baz?></bar>
==>
Document(ProcessingInst,Element(OpenTag(StartTag,TagName,EndTag),ProcessingInst,CloseTag(StartCloseTag,TagName,EndTag)))
# Comments
<!-- top comment -->
<element><!-- inner comment --> text</element>
<!---->
<!--
-->
==>
Document(Comment,Text,Element(OpenTag(StartTag,TagName,EndTag),Comment,Text,CloseTag(StartCloseTag,TagName,EndTag)),Text,Comment,Text,Comment)
# Mismatched tag
<a></b>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),MismatchedCloseTag(StartCloseTag,TagName,EndTag)))
# Unclosed tag
<a>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag)))
# Ignore pseudo-xml self-closers
<br/>
==>
Document(Element(SelfClosingTag(StartTag,TagName,EndTag)))
# Unclosed implicitly closed tag
<p>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag)))
# Nested mismatched tag
<a><b><c></c></x></a>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag)),
MismatchedCloseTag(StartCloseTag,TagName,EndTag),
⚠),
CloseTag(StartCloseTag,TagName,EndTag)))
# Incomplete close tag
<html><body></</html>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag), IncompleteCloseTag, ⚠),
CloseTag(StartCloseTag,TagName,EndTag)))
# Re-synchronize close tags
<a><b><c></x></c></a>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),
MismatchedCloseTag(StartCloseTag,TagName,EndTag),
CloseTag(StartCloseTag,TagName,EndTag)),
⚠),
CloseTag(StartCloseTag,TagName,EndTag)))
# Top-level mismatched close tag
<a></a></a>
==>
Document(
Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag)),
MismatchedCloseTag(StartCloseTag,TagName,EndTag))
# Self-closing tags
<a><img src=blah></a>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag)),
CloseTag(StartCloseTag,TagName,EndTag)))
# Implicitly closed
<dl><dd>Hello</dl>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),Text),
CloseTag(StartCloseTag,TagName,EndTag)))
# Closed by sibling
<div>
<p>Foo
<p>Bar
</div>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Text,
Element(OpenTag(StartTag,TagName,EndTag),Text),
Element(OpenTag(StartTag,TagName,EndTag),Text),
CloseTag(StartCloseTag,TagName,EndTag)))
# Closed by sibling at top
<p>Foo
<p>Bar
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),Text),Element(OpenTag(StartTag,TagName,EndTag),Text))
# Textarea
<p>Enter something: <textarea code-lang=javascript>function foo() {
return "</bar>"
}</textarea>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Text,
Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag),
TextareaText,
CloseTag(StartCloseTag,TagName,EndTag))))
# Script
<script>This is not an entity: &lt;</script>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),ScriptText,CloseTag(StartCloseTag,TagName,EndTag)))
# Doesn't get confused by a stray ampersand
<html>a&b</html>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),Text,InvalidEntity,Text,CloseTag(StartCloseTag,TagName,EndTag)))
# Can ignore mismatches {"dialect": "noMatch"}
<div>foo</p>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag)))
# Can handle lone close tags {"dialect": "noMatch"}
</strong>
==>
Document(CloseTag(StartCloseTag,TagName,EndTag))
# Parses ampersands in attributes
<img src="foo&bar">
==>
Document(Element(SelfClosingTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue(InvalidEntity)), EndTag)))
# Supports self-closing dialect {"dialect": "selfClosing"}
<section><image id=i2 /></section>
==>
Document(Element(
OpenTag(StartTag,TagName,EndTag),
Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),SelfClosingEndTag)),
CloseTag(StartCloseTag,TagName,EndTag)))
# Allows self-closing in foreign elements
<div><svg><circle/></svg></div>
==>
Document(Element(OpenTag(StartTag,TagName,EndTag),
Element(OpenTag(StartTag,TagName,EndTag),
Element(SelfClosingTag(StartTag,TagName,SelfClosingEndTag)),
CloseTag(StartCloseTag,TagName,EndTag)),
CloseTag(StartCloseTag,TagName,EndTag)))
# Parses multiple unfinished tags in a row
<div
<div
<div
==>
Document(Element(OpenTag(StartTag,TagName,⚠),
Element(OpenTag(StartTag,TagName,⚠),
Element(OpenTag(StartTag,TagName,⚠),⚠),⚠),⚠))
# Allows self-closing on special tags {"dialect": "selfClosing"}
<body>
<br/>
<textarea/>
<script/>
<style/>
</body>
==>
Document(Element(
OpenTag(StartTag,TagName,EndTag),
Text,
Element(SelfClosingTag(StartTag,TagName,SelfClosingEndTag)),
Text,
Element(SelfClosingTag(StartTag,TagName,SelfClosingEndTag)),
Text,
Element(SelfClosingTag(StartTag,TagName,SelfClosingEndTag)),
Text,
Element(SelfClosingTag(StartTag,TagName,SelfClosingEndTag)),
Text,
CloseTag(StartCloseTag,TagName,EndTag)))
# Only treats less-than as opening a tag when followed by a name
< div>x
==>
Document(IncompleteTag,Text)

29
frontend/node_modules/@lezer/html/test/test-html.js generated vendored Normal file
View File

@ -0,0 +1,29 @@
import {parser, configureNesting} from "../dist/index.js"
import {parser as jsParser} from "@lezer/javascript"
import {fileTests} from "@lezer/generator/dist/test"
import * as fs from "fs"
import * as path from "path"
import {fileURLToPath} from "url"
let caseDir = path.dirname(fileURLToPath(import.meta.url))
let mixed = parser.configure({
wrap: configureNesting([{
tag: "script",
attrs(attrs) {
return !attrs.type || /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i.test(attrs.type)
},
parser: jsParser
}])
})
for (let file of fs.readdirSync(caseDir)) {
if (!/\.txt$/.test(file)) continue
let name = /^[^\.]*/.exec(file)[0]
describe(name, () => {
let p = name == "mixed" ? mixed : parser
for (let {name, run} of fileTests(fs.readFileSync(path.join(caseDir, file), "utf8"), file))
it(name, () => run(p))
})
}

View File

@ -0,0 +1,97 @@
import {parser as baseParser} from "../dist/index.js"
import {TreeFragment} from "@lezer/common"
let parser = baseParser.configure({bufferLength: 2})
let r = n => Math.floor(Math.random() * n)
let tags = ["p", "ul", "li", "div", "span", "th", "tr", "body", "head", "title", "dd", "code", "em", "strong"]
function randomDoc(size) {
let doc = ""
if (!r(5)) doc += "<!doctype html>"
let scope = []
for (let i = 0; i < size; i++) {
let sel = r(20)
if (sel < 5) {
let tag = tags[r(tags.length)]
doc += `<${tag}${r(2) ? " a=b" : ""}>`
scope.push(tag)
} else if (sel < 10 && scope.length) {
let name = scope.pop()
doc += `</${r(5) ? name : "div"}>`
} else if (sel == 10) {
doc += `<img>`
} else if (sel == 11) {
doc += "<script>a()</script>"
} else if (sel == 12) {
doc += r(2) ? "&amp;" : "<!--@-->"
} else {
for (let i = r(6) + 1; i >= 0; i--)
doc += String.fromCharCode(97 + r(26))
}
}
while (scope.length) {
let name = scope.pop()
if (r(5)) doc += `</${name}>`
}
return doc
}
function check(doc, [tp, pos, txt], prevAST) {
let change = {fromA: pos, toA: pos, fromB: pos, toB: pos}, newDoc
if (tp == "insert") {
newDoc = doc.slice(0, pos) + txt + doc.slice(pos)
change.toA += txt.length
} else if (tp == "del") {
newDoc = doc.slice(0, pos) + doc.slice(pos + 1)
change.toB++
} else {
newDoc = doc.slice(0, pos) + txt + doc.slice(pos + 1)
change.toA += txt.length
change.toB++
}
let fragments = TreeFragment.applyChanges(TreeFragment.addTree(prevAST || parser.parse(doc)), [change], 2)
let ast = parser.parse(newDoc, fragments)
let orig = parser.parse(newDoc)
if (ast.toString() != orig.toString()) {
throw new Error(`Mismatch:\n ${ast}\nvs\n ${orig}\ndocument: ${
JSON.stringify(doc)}\naction: ${JSON.stringify([tp, pos, ch])}`)
}
return [newDoc, ast]
}
// Call this to just run random tests until a failing one is found.
// Not directly called in the tests because there's a bunch of
// circumstances in which uninteresting deviations in error recovery
// will create differing parses, so results have to be manually
// inspected.
function generate() {
for (let count = 0, size = 2;; size = Math.min(40, size + 1)) {
let doc = randomDoc(size), prev = null
for (let i = 0; i < 2; i++) {
console.log("Attempt", ++count)
let action = [["del", "insert", "replace"][r(3)], r(doc.length - 1), "<>/piabc "[r(9)]]
;([doc, prev] = check(doc, action, prev))
}
}
}
describe("Incremental parsing", () => {
it("doesn't get confused by reused opening tags", () => {
check("<code><code>mgnbni</code></code>", ["del", 29])
})
it("can handle a renamed opening tag after a self-closing", () => {
check("<p>one two three four five six seven<p>eight", ["replace", 37, "a"])
})
it("is okay with nameless elements", () => {
check("<body><code><img></code><>body>", ["replace", 14, ">"])
check("abcde<>fghij<", ["replace", 12, ">"])
})
it("doesn't get confused by an invalid close tag receiving a matching open tag", () => {
check("<div><p>foo</body>", ["insert", 0, "<body>"])
})
})

56
frontend/node_modules/@lezer/html/test/vue.txt generated vendored Normal file
View File

@ -0,0 +1,56 @@
# Parses Vue builtin directives
<span v-text="msg"></span>
==>
Document(
Element(
OpenTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue), EndTag),
CloseTag(StartCloseTag, TagName, EndTag)))
# Parses Vue :is shorthand syntax
<Component :is="view"></Component>
==>
Document(
Element(
OpenTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue),EndTag),
CloseTag(StartCloseTag, TagName, EndTag)))
# Parses Vue @click shorthand syntax
<button @click="handler()">Click me</button>
==>
Document(
Element(
OpenTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue), EndTag),
Text,
CloseTag(StartCloseTag, TagName, EndTag)))
# Parses Vue @submit.prevent shorthand syntax
<form @submit.prevent="onSubmit"></form>
==>
Document(
Element(
OpenTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue), EndTag),
CloseTag(StartCloseTag, TagName, EndTag)))
# Parses Vue Dynamic Arguments
<a v-bind:[attributeName]="url">Link</a>
==>
Document(
Element(
OpenTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue), EndTag),
Text,
CloseTag(StartCloseTag, TagName, EndTag)))