Skip to content

Commit

Permalink
feat: support parsing type parameters
Browse files Browse the repository at this point in the history
BREAKING CHANGE: onTagParams event renamed to onTagBodyParams
  • Loading branch information
DylanPiercey committed Nov 22, 2022
1 parent 9788f6e commit 309a8a3
Show file tree
Hide file tree
Showing 31 changed files with 930 additions and 108 deletions.
6 changes: 6 additions & 0 deletions .changeset/long-yaks-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"htmljs-parser": minor
---

Add support for type parameter/argument parsing.
This adds a new `onTagTypeParams`, `onTagTypeArgs` events and a `.typeParams` property on the `AttrMethod` range.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ const parser = createParser({
range.expressions; // A list of placeholder ranges (similar to whats emitted via onPlaceholder).
},

/**
* Called after the type arguments for a tag have been parsed.
*
* @example
* 1╭─ <foo<string>>
* │ │╰─ tagTypeArgs.value "string"
* ╰─ ╰─ tagTypeArgs "<string>"
*/
onTagTypeArgs(range) {
range.value; // Another range that includes only the type arguments themselves and not the angle brackets.
},

/**
* Called after a tag variable has been parsed.
*
Expand All @@ -273,6 +285,18 @@ const parser = createParser({
range.value; // Another range that includes only the args themselves and not the outer parenthesis.
},

/**
* Called after type parameters for the tag parameters have been parsed.
*
* @example
* 1╭─ <tag<T>|input: { name: T }|>
* │ │╰─ tagBodyTypeParams.value
* ╰─ ╰─ tagBodyTypeParams "<T>"
*/
onTagBodyTypeParams(range) {
range.value; // Another range that includes only the type params themselves and not the angle brackets.
},

/**
* Called after tag parameters have been parsed.
*
Expand All @@ -281,7 +305,7 @@ const parser = createParser({
* │ │╰─ tagParams.value "item"
* ╰─ ╰─ tagParams "|item|"
*/
onTagParams(range) {
onTagBodyParams(range) {
range.value; // Another range that includes only the params themselves and not the outer pipes.
},

Expand Down Expand Up @@ -334,6 +358,9 @@ const parser = createParser({
* ╰─ ╰─ attrMethod "(ev) { foo(); }"
*/
onAttrMethod(range) {
range.typeParams; // Another range which includes the type params for the method.
range.typeParams.value; // Another range which includes the type params without outer angle brackets.

range.params; // Another range which includes the params for the method.
range.params.value; // Another range which includes the method params without outer parenthesis.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
1╭─ <foo<A>(event: A){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A"
│ ││ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │╰─ attrMethod.typeParams.value
│ ││ ├─ attrMethod.typeParams "<A>"
│ ││ ├─ attrMethod "<A>(event: A){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
2╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
3╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
4├─
5╭─ <foo<A> (event: A){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A"
│ ││ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │╰─ attrMethod.typeParams.value
│ ││ ├─ attrMethod.typeParams "<A>"
│ ││ ├─ attrMethod "<A> (event: A){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
6╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
7╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
8├─
9╭─ <foo <A>(event: A){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A"
│ ││ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │╰─ attrMethod.typeParams.value
│ ││ ├─ attrMethod.typeParams "<A>"
│ ││ ├─ attrMethod "<A>(event: A){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
10╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
11╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
12├─
13╭─ <foo <A> (event: A){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A"
│ ││ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │╰─ attrMethod.typeParams.value
│ ││ ├─ attrMethod.typeParams "<A>"
│ ││ ├─ attrMethod "<A> (event: A){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
14╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
15╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
16├─
17╭─ <foo<A, B = string>(event: A & B){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ ├─ attrMethod "<A, B = string>(event: A & B){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
18╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
19╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
20├─
21╭─ <foo<A, B = string> (event: A & B){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ ├─ attrMethod "<A, B = string> (event: A & B){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
22╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
23╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
24├─
25╭─ <foo <A, B = string>(event: A & B){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ ├─ attrMethod "<A, B = string>(event: A & B){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
26╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
27╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
28├─
29╭─ <foo <A, B = string> (event: A & B){
│ ││ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ ├─ attrMethod "<A, B = string> (event: A & B){\n console.log(event.type)\n}"
│ ││ ├─ attrName
│ ││ ╰─ attrName
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
30╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
31╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
32╰─
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<foo<A>(event: A){
console.log(event.type)
}/>

<foo<A> (event: A){
console.log(event.type)
}/>

<foo <A>(event: A){
console.log(event.type)
}/>

<foo <A> (event: A){
console.log(event.type)
}/>

<foo<A, B = string>(event: A & B){
console.log(event.type)
}/>

<foo<A, B = string> (event: A & B){
console.log(event.type)
}/>

<foo <A, B = string>(event: A & B){
console.log(event.type)
}/>

<foo <A, B = string> (event: A & B){
console.log(event.type)
}/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
1╭─ <foo onclick<A>(event: A){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A"
│ ││ │ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │ │╰─ attrMethod.typeParams.value
│ ││ │ ├─ attrMethod.typeParams "<A>"
│ ││ │ ╰─ attrMethod "<A>(event: A){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
2╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
3╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
4├─
5╭─ <foo onclick<A> (event: A){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A"
│ ││ │ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │ │╰─ attrMethod.typeParams.value
│ ││ │ ├─ attrMethod.typeParams "<A>"
│ ││ │ ╰─ attrMethod "<A> (event: A){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
6╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
7╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
8├─
9╭─ <foo onclick <A>(event: A){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A"
│ ││ │ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │ │╰─ attrMethod.typeParams.value
│ ││ │ ├─ attrMethod.typeParams "<A>"
│ ││ │ ╰─ attrMethod "<A>(event: A){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
10╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
11╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
12├─
13╭─ <foo onclick <A> (event: A){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A"
│ ││ │ ││ ╰─ attrMethod.params "(event: A)"
│ ││ │ │╰─ attrMethod.typeParams.value
│ ││ │ ├─ attrMethod.typeParams "<A>"
│ ││ │ ╰─ attrMethod "<A> (event: A){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
14╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
15╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
16├─
17╭─ <foo onclick<A, B = string>(event: A & B){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ │ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ │ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ │ ╰─ attrMethod "<A, B = string>(event: A & B){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
18╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
19╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
20├─
21╭─ <foo onclick<A, B = string> (event: A & B){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ │ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ │ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ │ ╰─ attrMethod "<A, B = string> (event: A & B){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
22╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
23╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
24├─
25╭─ <foo onclick <A, B = string>(event: A & B){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ │ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ │ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ │ ╰─ attrMethod "<A, B = string>(event: A & B){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
26╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
27╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
28├─
29╭─ <foo onclick <A, B = string> (event: A & B){
│ ││ │ ││ ││ ╰─ attrMethod.body "{\n console.log(event.type)\n}"
│ ││ │ ││ │╰─ attrMethod.params.value "event: A & B"
│ ││ │ ││ ╰─ attrMethod.params "(event: A & B)"
│ ││ │ │╰─ attrMethod.typeParams.value "A, B = string"
│ ││ │ ├─ attrMethod.typeParams "<A, B = string>"
│ ││ │ ╰─ attrMethod "<A, B = string> (event: A & B){\n console.log(event.type)\n}"
│ ││ ╰─ attrName "onclick"
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
30╭─ console.log(event.type)
╰─ ╰─ attrMethod.body.value "\n console.log(event.type)\n"
31╭─ }/>
╰─ ╰─ openTagEnd:selfClosed "/>"
32╰─
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<foo onclick<A>(event: A){
console.log(event.type)
}/>

<foo onclick<A> (event: A){
console.log(event.type)
}/>

<foo onclick <A>(event: A){
console.log(event.type)
}/>

<foo onclick <A> (event: A){
console.log(event.type)
}/>

<foo onclick<A, B = string>(event: A & B){
console.log(event.type)
}/>

<foo onclick<A, B = string> (event: A & B){
console.log(event.type)
}/>

<foo onclick <A, B = string>(event: A & B){
console.log(event.type)
}/>

<foo onclick <A, B = string> (event: A & B){
console.log(event.type)
}/>
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
1╭─ tagname <span>hello frank</span>
│ │ ╰─ error(INVALID_ATTRIBUTE_NAME:Invalid attribute name. Attribute name cannot begin with the "<" character.)
│ │ ││ │ │ ╰─ error(INVALID_ATTR_TYPE_PARAMS:Attribute cannot contain type parameters unless it is a shorthand method) "/span"
│ │ ││ │ ╰─ attrName "frank"
│ │ ││ ╰─ attrName "hello"
│ │ │╰─ tagTypeArgs.value "span"
│ │ ╰─ tagTypeArgs "<span>"
╰─ ╰─ tagName "tagname"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
1╭─ <foo/, x=1/>
│ ││ ╰─ error(MISSING_TAG_VARIABLE:A slash was found that was not followed by a variable name or lhs expression)
│ │╰─ tagName "foo"
╰─ ╰─ openTagStart
2╰─
1 change: 1 addition & 0 deletions src/__tests__/fixtures/invalid-missing-tag-var/input.marko
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<foo/, x=1/>
Loading

0 comments on commit 309a8a3

Please sign in to comment.