--- /dev/null
+{{
+/* Generated code is not pretty, ignore a lot of this horrorstyle. */
+/* eslint-disable quotes */
+/* eslint-disable vars-on-top */
+/* eslint-disable no-control-regex */
+/* eslint-disable security/detect-object-injection */
+/* eslint-disable vars-on-top */
+/* eslint-disable brace-style */
+/* eslint-disable comma-dangle */
+/* eslint-disable no-unused-vars */
+
+function makeString(o) {
+ return o.join('');
+}
+
+const onlyFirstAttributes = [
+ 'media',
+ 'rel',
+ 'title',
+ 'title*',
+ 'type',
+];
+
+}}
+
+{
+// Per-parsing tracking of attributes which should be ignored after first occurrence.
+let seenAttributes = [];
+}
+
+links 'links'
+ = links:linkValue+ {
+ return links;
+ }
+
+linkValue 'link-value'
+ = uriReference:uriReference OWS attributes:attributes ','? OWS {
+ seenAttributes = [];
+ return {
+ target: uriReference,
+ attributes,
+ };
+ }
+
+uriReference 'uri-reference'
+ = '<' uri:uri '>' ';' {
+ return uri;
+ }
+
+uri 'uri'
+ = uri:[^>]+ {
+ return makeString(uri);
+ }
+
+attributes 'attributes'
+ = attrs:linkParam+ {
+ return attrs.filter((a) => a);
+ }
+
+linkParam 'link-param'
+ = name:name BWS '=' BWS value:value ';'? OWS {
+ if (onlyFirstAttributes.includes(name.name)) {
+ if (seenAttributes.includes(name.name)) {
+ // Repeat of singleton attribute, ignore it.
+ return;
+ }
+ seenAttributes.push(name.name);
+ }
+ return {
+ ...name,
+ ...value,
+ };
+ }
+ / name:name BWS ';'? OWS {
+ return {
+ ...name,
+ value: null,
+ }
+ }
+
+name 'name'
+ = name:[a-zA-Z]+ extended:'*'? {
+ return {
+ name: makeString(name.concat(extended)).toLowerCase(),
+ extended: !!extended,
+ };
+ }
+
+value 'value'
+ = ["] value:[^"]+ ["] {
+ return {
+ value: makeString(value),
+ };
+ }
+ / value:[^";,]+ {
+ return {
+ value: makeString(value),
+ };
+ }
+
+OWS 'whitespace'
+ = spaces:[ ]*
+
+BWS 'bad whitespace'
+ = OWS
+
+// This is also an alternate startRule.
+extendedValue 'extended-value'
+ = encoding:[^']* ['] language:[^']* ['] value:.* {
+ return {
+ encoding: encoding.length ? makeString(encoding) : 'UTF-8',
+ language: language.length ? makeString(language) : null,
+ value: decodeURIComponent(makeString(value)),
+ };
+ }
+ / value:.* {
+ return {
+ encoding: null,
+ language: null,
+ value: makeString(value),
+ };
+ }