attribute-specifier

56 cases. Source: tests/attribute-specifier.json.

#1 canonical

Source

{#id .class .other key=value key2="quoted value"}

Parse result

{
  "type": "attributes",
  "id": "id",
  "classes": [
    "class",
    "other"
  ],
  "attrs": {
    "key": "value",
    "key2": "quoted value"
  }
}

#2 empty specifier

Source

{}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

#3 id only

Source

{#main}

Parse result

{
  "type": "attributes",
  "id": "main",
  "classes": [],
  "attrs": {}
}

#4 class only

Source

{.lead}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [
    "lead"
  ],
  "attrs": {}
}

#5 several classes keep source order

Source

{.badge .warn .small}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [
    "badge",
    "warn",
    "small"
  ],
  "attrs": {}
}

#6 repeated class is kept, not deduplicated

Source

{.a .a}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [
    "a",
    "a"
  ],
  "attrs": {}
}

#7 bare value

Source

{tone=info}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "tone": "info"
  }
}

#8 quoted value with whitespace

Source

{title="Pricing tiers"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "title": "Pricing tiers"
  }
}

#9 quoted value with escapes

Source

{title="say \"hi\" \\ done"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "title": "say \"hi\" \\ done"
  }
}

#10 quoted empty string

Source

{alt=""}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "alt": ""
  }
}

#11 quoted value may contain braces, hashes, dots, and equals

Source

{q="a } b { #c .d e=f"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "q": "a } b { #c .d e=f"
  }
}

#12 quoted value may contain a single quote

Source

{q="it's"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "q": "it's"
  }
}

#13 repeated id, last wins

Source

{#a #b}

Parse result

{
  "type": "attributes",
  "id": "b",
  "classes": [],
  "attrs": {}
}

#14 repeated key, last wins

Source

{k=1 k=2}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "k": "2"
  }
}

#15 leading, trailing, and repeated whitespace

Source

{  #a 	 .b		k=v  }

Parse result

{
  "type": "attributes",
  "id": "a",
  "classes": [
    "b"
  ],
  "attrs": {
    "k": "v"
  }
}

#16 tab as the only separator

Source

{#a	.b	k=v}

Parse result

{
  "type": "attributes",
  "id": "a",
  "classes": [
    "b"
  ],
  "attrs": {
    "k": "v"
  }
}

#17 identifiers with digits, underscore, and hyphen

Source

{#h2-intro .col_3 data-x=y}

Parse result

{
  "type": "attributes",
  "id": "h2-intro",
  "classes": [
    "col_3"
  ],
  "attrs": {
    "data-x": "y"
  }
}

#18 single-character identifiers

Source

{#a .b c=d}

Parse result

{
  "type": "attributes",
  "id": "a",
  "classes": [
    "b"
  ],
  "attrs": {
    "c": "d"
  }
}

#19 bare values with punctuation

Source

{ratio=2:1 width=60% path=/a/b.c eq=a=b hash=#x dot=.y}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "ratio": "2:1",
    "width": "60%",
    "path": "/a/b.c",
    "eq": "a=b",
    "hash": "#x",
    "dot": ".y"
  }
}

#20 bare value with a backslash is literal

Source

{path=C:\dir}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "path": "C:\\dir"
  }
}

#21 non-ASCII in values

Source

{label=Δ title="Δ revenue"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "label": "Δ",
    "title": "Δ revenue"
  }
}

#22 reserved classes alongside a tone key

Source

{.info .badge tone=warn}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [
    "info",
    "badge"
  ],
  "attrs": {
    "tone": "warn"
  }
}

#23 key that looks like a reserved word but is not

Source

{ids=1 classes=2 identity=3}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "ids": "1",
    "classes": "2",
    "identity": "3"
  }
}

#24 unterminated after items

invalid

Source

{#a .b

Parse result

{
  "type": "attributes",
  "id": "a",
  "classes": [
    "b"
  ],
  "attrs": {}
}

Diagnostics: ATTR_UNTERMINATED

#25 unterminated, empty

invalid

Source

{

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_UNTERMINATED

#26 unterminated quoted value swallows the rest, reported once

invalid

Source

{k="abc

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_UNTERMINATED_QUOTE

#27 unterminated quoted value even when a brace follows

invalid

Source

{k="abc}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_UNTERMINATED_QUOTE

#28 empty id

invalid

Source

{#}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#29 empty class

invalid

Source

{.}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#30 id starting with a digit

invalid

Source

{#1abc}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#31 class starting with a hyphen

invalid

Source

{.-x}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#32 id containing a dot is one bad item, not two

invalid

Source

{#a.b}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#33 class containing an equals sign

invalid

Source

{.a=b}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#34 key starting with a digit

invalid

Source

{9lives=1}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#35 key with an illegal character

invalid

Source

{ke$y=1}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_EXPECTED_EQUALS

#36 non-ASCII letter in an identifier

invalid

Source

{#über}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER

#37 bare word without a value

invalid

Source

{foo}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_EXPECTED_EQUALS

#38 empty bare value

invalid

Source

{key=}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_EMPTY_VALUE

#39 space after equals splits into an empty value and a bare word

invalid

Source

{key= value}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_EMPTY_VALUE ATTR_EXPECTED_EQUALS

#40 space before equals

invalid

Source

{key =value}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_EXPECTED_EQUALS ATTR_UNEXPECTED_CHAR

#41 bare value containing a double quote

invalid

Source

{key=a"b}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_VALUE

#42 bare value containing a single quote

invalid

Source

{key=it's}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_VALUE

#43 single-quoted value is not a quoting form

invalid

Source

{key='v'}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_VALUE

#44 text after the closing quote

invalid

Source

{key="a"b}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_VALUE

#45 unknown escape in a quoted value

invalid

Source

{key="a\nb"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_ESCAPE

#46 escaped brace is not a defined escape

invalid

Source

{key="a\}b"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_ESCAPE

#47 two unknown escapes report twice

invalid

Source

{key="\a\b"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_BAD_ESCAPE ATTR_BAD_ESCAPE

#48 item starting with equals

invalid

Source

{=x}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_UNEXPECTED_CHAR

#49 item starting with a quote

invalid

Source

{"x"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_UNEXPECTED_CHAR

#50 item starting with punctuation

invalid

Source

{@x}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_UNEXPECTED_CHAR

#51 reserved key id

invalid

Source

{id=x}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_RESERVED_KEY

#52 reserved key class

invalid

Source

{class=x}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_RESERVED_KEY

#53 reserved key does not shadow the id item

invalid

Source

{#real id=fake}

Parse result

{
  "type": "attributes",
  "id": "real",
  "classes": [],
  "attrs": {}
}

Diagnostics: ATTR_RESERVED_KEY

#54 several bad items are each reported and the good ones survive

invalid

Source

{# .b bad key= .c}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [
    "b",
    "c"
  ],
  "attrs": {}
}

Diagnostics: ATTR_BAD_IDENTIFIER ATTR_EXPECTED_EQUALS ATTR_EMPTY_VALUE

#55 newline is not whitespace, so it glues items into one bad identifier

invalid

Source

{#a
.b}

Rendered

{#a .b}

Downgrade

{#a
.b}

Diagnostics: ATTR_BAD_IDENTIFIER

#56 bad item followed by good quoted value

invalid

Source

{=x k="a b"}

Parse result

{
  "type": "attributes",
  "id": null,
  "classes": [],
  "attrs": {
    "k": "a b"
  }
}

Diagnostics: ATTR_UNEXPECTED_CHAR