Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/concise-backtick-text.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"htmljs-parser": minor
---

Add backtick text to concise mode. A run of backticks after a tag's attributes (or at the start of a line) begins text just like `--`, either on one line or as an indented block closed by a line of the same backticks. The text is exactly what follows the opening backticks, so leading and trailing whitespace is kept, and a closing run of the same length is optional on a single line and may be followed by a comment. A backtick after whitespace where an attribute could start was previously read as an attribute named by the template literal; it is now text.

`onText` and `onPlaceholder` ranges now have a `delimiter`: the range of the `--` or backtick run that began the concise text block, or `undefined` outside of one.

A concise line that ends its open tag before any tag name, such as a line that starts with a semicolon, is now reported as a parse error instead of emitting an open tag end with no tag name.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ const parser = createParser({
* @example
* 1╭─ <div>Hi</div>
* ╰─ ╰─ text "Hi"
*
* Text within a concise mode `--` or backtick block also has the
* `range.delimiter` that began the block.
*
* @example
* 1╭─ div `Hi`
* │ │╰─ text "Hi"
* ╰─ ╰─ text.delimiter "`"
*/
onText(range) {},

Expand Down
42 changes: 42 additions & 0 deletions src/__tests__/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,48 @@ describe("api", () => {
});
});

describe("text delimiters", () => {
function delimiters(src: string) {
const out: (string | undefined)[] = [];
const read = (range: { start: number; end: number } | undefined) =>
range && src.slice(range.start, range.end);
const parser = createParser({
onText: (range) => out.push(read(range.delimiter)),
onPlaceholder: (range) => out.push(read(range.delimiter)),
});
parser.parse(src);
return out;
}

it("is the hyphens or backticks that begin the text", () => {
assert.deepEqual(delimiters("a -- x"), ["--"]);
assert.deepEqual(delimiters("a ---- x"), ["----"]);
assert.deepEqual(delimiters("a `x`"), ["`"]);
assert.deepEqual(delimiters("a ``x``"), ["``"]);
assert.deepEqual(delimiters("-- x\n`y`"), ["--", "`"]);
});

it("applies to every line of a block and to placeholders", () => {
assert.deepEqual(delimiters("a `\n x ${y}\n z\n `"), [
"`",
"`",
"`",
"`",
]);
assert.deepEqual(delimiters("a --\n x\n z\n --"), ["--", "--"]);
});

it("is absent outside of a block", () => {
assert.deepEqual(delimiters("<a>x ${y}</a>"), [undefined, undefined]);
assert.deepEqual(delimiters("a `x`\n<b>y</b>"), ["`", undefined]);
assert.deepEqual(delimiters("a -- x\nb\n <c>y</c>"), ["--", undefined]);
});

it("covers text until the end of the input", () => {
assert.deepEqual(delimiters("a `\n x"), ["`"]);
});
});

describe("validation internals", () => {
it("rejects doubled operators followed by a terminator", () => {
assert.equal(isValidAttrValue(" ++ ,", true), 0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
1╭─ div [a `t`]
│ │ │ ╰─ attrName "`t`"
│ │ ╰─ attrName
╰─ ╰─ tagName "div"
2╭─ div [a, `t`] `text`
│ │ │ │ │╰─ text "text"
│ │ │ │ ╰─ openTagEnd
│ │ │ ╰─ attrName "`t`"
│ │ ╰─ attrName
│ ├─ closeTagEnd(div)
│ ├─ openTagEnd
╰─ ╰─ tagName "div"
3╭─
╰─ ╰─ closeTagEnd(div)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
div [a `t`]
div [a, `t`] `text`
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
1╭─ div a=b` hi`
│ │ ││╰─ attrValue.value "b` hi`"
│ │ │╰─ attrValue "=b` hi`"
│ │ ╰─ attrName
╰─ ╰─ tagName "div"
2╭─ div a=b`hi` `text`
│ │ │││ │╰─ text "text"
│ │ │││ ╰─ openTagEnd
│ │ ││╰─ attrValue.value "b`hi`"
│ │ │╰─ attrValue "=b`hi`"
│ │ ╰─ attrName
│ ├─ closeTagEnd(div)
│ ├─ openTagEnd
╰─ ╰─ tagName "div"
3╭─
╰─ ╰─ closeTagEnd(div)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
div a=b` hi`
div a=b`hi` `text`
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
1╭─ div a=`x` b=`y` `text`
│ │ │││ │││ │╰─ text "text"
│ │ │││ │││ ╰─ openTagEnd
│ │ │││ ││╰─ attrValue.value "`y`"
│ │ │││ │╰─ attrValue "=`y`"
│ │ │││ ╰─ attrName
│ │ ││╰─ attrValue.value "`x`"
│ │ │╰─ attrValue "=`x`"
│ │ ╰─ attrName
╰─ ╰─ tagName "div"
2╭─ div a=`x ${y} z`
│ │ ││╰─ attrValue.value "`x ${y} z`"
│ │ │╰─ attrValue "=`x ${y} z`"
│ │ ╰─ attrName
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
3╭─
│ ├─ openTagEnd
╰─ ╰─ closeTagEnd(div)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
div a=`x` b=`y` `text`
div a=`x ${y} z`
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
1╭─ div class=a ? `x` : `y`
│ │ │ │╰─ attrValue.value "a ? `x` : `y`"
│ │ │ ╰─ attrValue "=a ? `x` : `y`"
│ │ ╰─ attrName "class"
╰─ ╰─ tagName "div"
2╭─ div class=a ?
│ │ │ │╰─ attrValue.value "a ?\n `x` : `y`"
│ │ │ ╰─ attrValue "=a ?\n `x` : `y`"
│ │ ╰─ attrName "class"
│ ├─ closeTagEnd(div)
│ ├─ openTagEnd
╰─ ╰─ tagName "div"
3├─ `x` : `y`
4╭─
│ ├─ openTagEnd
╰─ ╰─ closeTagEnd(div)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
div class=a ? `x` : `y`
div class=a ?
`x` : `y`
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
1╭─ div(`x`) `text`
│ │ ││ │╰─ text "text"
│ │ ││ ╰─ openTagEnd
│ │ │╰─ tagArgs.value "`x`"
│ │ ╰─ tagArgs "(`x`)"
╰─ ╰─ tagName "div"
2╭─ div(`x`)
│ │ │╰─ tagArgs.value "`x`"
│ │ ╰─ tagArgs "(`x`)"
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
3╭─
│ ├─ openTagEnd
╰─ ╰─ closeTagEnd(div)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
div(`x`) `text`
div(`x`)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
1╭─ div a=1, `t`
│ │ │││ │╰─ text
│ │ │││ ╰─ openTagEnd
│ │ ││╰─ attrValue.value
│ │ │╰─ attrValue "=1"
│ │ ╰─ attrName
╰─ ╰─ tagName "div"
2╭─ div a=1,
│ │ ││╰─ attrValue.value
│ │ │╰─ attrValue "=1"
│ │ ╰─ attrName
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
3╭─ `t`
│ │╰─ text
╰─ ╰─ openTagEnd
4╭─
╰─ ╰─ closeTagEnd(div)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
div a=1, `t`
div a=1,
`t`
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
1╭─ div; `x`
│ │ │╰─ error(INVALID_CODE_AFTER_SEMICOLON:A semicolon indicates the end of a line. Only comments may follow it.)
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
2╰─
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
div; `x`
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
1╭─ div
╰─ ╰─ tagName "div"
2╭─ `a`
│ │ ╰─ text
╰─ ╰─ openTagEnd
3╭─ `b`
╰─ ╰─ error(INVALID_INDENTATION:Line indentation does match indentation of previous line)
4╰─
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
div
`a`
`b`
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
1╭─ div `hello` /* note */ more
│ │ ││ │ │ ╰─ error(INVALID_CHARACTER:In concise mode a javascript comment block can only be followed by whitespace characters and a newline.)
│ │ ││ │ ╰─ comment.value " note "
│ │ ││ ╰─ comment "/* note */"
│ │ │╰─ text "hello"
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
2╰─
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
div `hello` /* note */ more
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
1╭─ div `a`
│ │ │╰─ text
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
2╭─ span `
│ │ ╰─ openTagEnd
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "span"
3╭─ b
╰─ ╰─ text "b\n"
4╭─ c
╰─ ╰─ text
5├─ `
6╭─ div
│ ├─ closeTagEnd(span)
╰─ ╰─ tagName "div"
7╭─ ``
╰─ ╰─ openTagEnd
8╭─ d
╰─ ╰─ text " d"
9├─ ``
10╭─
╰─ ╰─ closeTagEnd(div)
9 changes: 9 additions & 0 deletions src/__tests__/fixtures/concise-backtick-text-crlf/input.marko
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
div `a`
span `
b
c
`
div
``
d
``
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
1╭─ div ``
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
2╭─ a
╰─ ╰─ text
3├─ ``
4╭─ div ` `
│ │ │╰─ text " "
│ │ ╰─ openTagEnd
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
5╭─ div `` x ``
│ │ │ ╰─ text " x "
│ │ ╰─ openTagEnd
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
6╭─ span
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "span"
7╭─
│ ├─ openTagEnd
╰─ ╰─ closeTagEnd(span)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
div ``
a
``
div ` `
div `` x ``
span
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
1╭─ div `
│ │ │╰─ closeTagEnd(div)
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
div `
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
1╭─ div `
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
2╭─ hello
│ │ ╰─ closeTagEnd(div)
╰─ ╰─ text "hello"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
div `
hello
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
1╭─ div `a ${b
│ │ ││ ╰─ error(MALFORMED_PLACEHOLDER:EOF reached while parsing placeholder)
│ │ │╰─ text "a "
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
div `a ${b
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
1╭─ div ``a`b``
│ │ │ ╰─ text "a`b"
│ │ ╰─ openTagEnd
╰─ ╰─ tagName "div"
2╭─ div ``a ` b``
│ │ │ ╰─ text "a ` b"
│ │ ╰─ openTagEnd
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
3╭─ div ```a``b```
│ │ │ ╰─ text "a``b"
│ │ ╰─ openTagEnd
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
4╭─ div `a``b`
│ │ │╰─ text "a``b"
│ │ ╰─ openTagEnd
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
5╭─ div ``a`
│ │ │ ╰─ text "a`"
│ │ ╰─ openTagEnd
│ ├─ closeTagEnd(div)
╰─ ╰─ tagName "div"
6╭─
╰─ ╰─ closeTagEnd(div)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
div ``a`b``
div ``a ` b``
div ```a``b```
div `a``b`
div ``a`
Loading
Loading