reffy 6.2.0 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +158 -158
  3. package/index.js +11 -11
  4. package/package.json +53 -53
  5. package/reffy.js +248 -248
  6. package/src/browserlib/canonicalize-url.mjs +50 -50
  7. package/src/browserlib/create-outline.mjs +352 -352
  8. package/src/browserlib/extract-cssdfn.mjs +319 -319
  9. package/src/browserlib/extract-dfns.mjs +686 -686
  10. package/src/browserlib/extract-elements.mjs +205 -205
  11. package/src/browserlib/extract-headings.mjs +48 -48
  12. package/src/browserlib/extract-ids.mjs +28 -28
  13. package/src/browserlib/extract-links.mjs +28 -28
  14. package/src/browserlib/extract-references.mjs +203 -203
  15. package/src/browserlib/extract-webidl.mjs +134 -134
  16. package/src/browserlib/get-absolute-url.mjs +21 -21
  17. package/src/browserlib/get-generator.mjs +26 -26
  18. package/src/browserlib/get-lastmodified-date.mjs +13 -13
  19. package/src/browserlib/get-title.mjs +11 -11
  20. package/src/browserlib/informative-selector.mjs +16 -16
  21. package/src/browserlib/map-ids-to-headings.mjs +136 -136
  22. package/src/browserlib/reffy.json +53 -53
  23. package/src/cli/check-missing-dfns.js +609 -609
  24. package/src/cli/generate-idlnames.js +430 -430
  25. package/src/cli/generate-idlparsed.js +139 -139
  26. package/src/cli/merge-crawl-results.js +128 -128
  27. package/src/cli/parse-webidl.js +430 -430
  28. package/src/lib/css-grammar-parse-tree.schema.json +109 -109
  29. package/src/lib/css-grammar-parser.js +440 -440
  30. package/src/lib/fetch.js +55 -55
  31. package/src/lib/nock-server.js +119 -119
  32. package/src/lib/specs-crawler.js +605 -603
  33. package/src/lib/util.js +898 -898
  34. package/src/specs/missing-css-rules.json +197 -197
  35. package/src/specs/spec-equivalents.json +149 -149
  36. package/src/browserlib/extract-editors.mjs~ +0 -14
  37. package/src/browserlib/generate-es-dfn-report.sh~ +0 -4
  38. package/src/cli/csstree-grammar-check.js +0 -28
  39. package/src/cli/csstree-grammar-check.js~ +0 -10
  40. package/src/cli/csstree-grammar-parser.js +0 -11
  41. package/src/cli/csstree-grammar-parser.js~ +0 -1
  42. package/src/cli/extract-editors.js~ +0 -38
  43. package/src/cli/process-specs.js~ +0 -28
@@ -1,205 +1,205 @@
1
- /**
2
- * Extract the list of markup elements that the spec defines
3
- *
4
- * Extraction requires spec to follow a structure similar to the one used in
5
- * the HTML spec:
6
- * https://html.spec.whatwg.org/multipage/dom.html#element-definitions
7
- *
8
- * Important (2021-05-31): Extraction code is incomplete and only extracts the
9
- * mapping between each element and its associated IDL interface for now. The
10
- * rest of the extraction logic is currently disabled because it does not yet
11
- * produce fully machine-readable results, see inline comments prefixed with
12
- * "TODO" for details.
13
- *
14
- * @function
15
- * @public
16
- */
17
- export default function () {
18
- function getText(el) {
19
- return el.textContent.trim().replace(/\s+/g, ' ');
20
- }
21
-
22
- // Extract HTML elements
23
- const htmlElements = [...document.querySelectorAll('dl.element')]
24
- .map(el => {
25
- // Get back to heading that defines the element(s)
26
- let heading = el.previousElementSibling;
27
- while (heading && !heading.nodeName.match(/^H\d$/)) {
28
- heading = heading.previousElementSibling;
29
- }
30
- if (!heading) {
31
- throw new Error('Could not locate heading associated with element');
32
- }
33
-
34
- const dfns = [...heading.querySelectorAll('dfn')];
35
- if (dfns.length === 0) {
36
- // Ignore the definition of "Custom elements" in HTML
37
- if (getText(heading).match(/Core concepts/)) {
38
- return null;
39
- }
40
- throw new Error('No dfn found in heading element: ' + heading.textContent);
41
- }
42
-
43
- // In most cases, there will be only one element, but some elements are
44
- // defined together, typically h1-h6 or sub and sup
45
- return dfns.map(dfn => {
46
- const res = { name: getText(dfn) };
47
- const dts = [...el.querySelectorAll('dt')];
48
- dts.forEach(dt => {
49
- const prop = ({
50
- // TODO: Properties other than "interface" follow common patterns
51
- // most of the time... but not always, making it hard to create
52
- // fully machine-readable info. Uncomment following lines to extract
53
- // more info and improve the code
54
- // 'Categories': 'categories',
55
- // 'Contexts in which this element can be used': 'contexts',
56
- // 'Content model': 'contents',
57
- // 'Tag omission in text/html': 'omission',
58
- // 'Content attributes': 'attributes',
59
- // 'Accessibility considerations': 'accessibility',
60
- 'DOM interface': 'interface'
61
- })[getText(dt).replace(/:$/, '')];
62
-
63
- if (prop === 'omission') {
64
- // Tag omission can be a complex paragraph but it is never a list.
65
- let dd = dt.nextElementSibling;
66
- while (dd && dd.nodeName !== 'DD') {
67
- dd = dd.nextElementSibling;
68
- }
69
- if (dd) {
70
- res[prop] = getText(dd);
71
- }
72
- }
73
- else if (prop === 'accessibility') {
74
- // TODO: Accessibility considerations links to ARIA and AAM. The
75
- // code does not yet handle more complex associations that currently
76
- // appear in the definitions of the img, sub and sup, input and
77
- // select elements.
78
- res[prop] = [];
79
- let dd = dt.nextElementSibling;
80
- while (dd && dd.nodeName !== 'DT') {
81
- if (dd.nodeName === 'DD') {
82
- const link = dd.querySelector('a');
83
- if (link) {
84
- res[prop].push({
85
- title: getText(link),
86
- href: link.getAttribute('href')
87
- });
88
- }
89
- }
90
- dd = dd.nextElementSibling;
91
- }
92
- }
93
- else if (prop === 'interface') {
94
- let dd = dt.nextElementSibling;
95
- while (dd && dd.nodeName !== 'DD') {
96
- dd = dd.nextElementSibling;
97
- }
98
- if (dd) {
99
- let match = dd.textContent.match(/^interface (.*?) /m);
100
- if (match) {
101
- res[prop] = match[1];
102
- }
103
- else {
104
- // NB: The sub/sup table uses a plural form for "Use", hence
105
- // the "?" for the final "s" in "Uses".
106
- match = dd.textContent.match(/^Uses? (.*?)[,\.\s]/);
107
- if (match) {
108
- res[prop] = match[1];
109
- }
110
- else {
111
- throw new Error('Could not link element to interface: ' + getText(dd));
112
- }
113
- }
114
- }
115
- else {
116
- throw new Error('Could not link element to interface, missing dd for ' + res.name);
117
- }
118
- }
119
- else if (prop) {
120
- res[prop] = [];
121
- let dd = dt.nextElementSibling;
122
- while (dd && dd.nodeName !== 'DT') {
123
- if (dd.nodeName === 'DD') {
124
- res[prop].push(getText(dd).replace(/\.$/, ''));
125
- }
126
- dd = dd.nextElementSibling;
127
- }
128
- }
129
- });
130
- return res;
131
- });
132
- })
133
- .filter(el => !!el)
134
- .flat();
135
-
136
-
137
- // Extract SVG elements that use the "element-summary" pattern
138
- const svgSummaryElements = [...document.querySelectorAll('div.element-summary')]
139
- .map(el => {
140
- const name = el.querySelector('.element-name');
141
- if (!name) {
142
- throw new Error('Could not extract name from element-summary element');
143
- }
144
-
145
- const res = { name: getText(name).replace(/‘|’/g, '') };
146
- const dts = [...el.querySelectorAll('dt')];
147
- dts.forEach(dt => {
148
- const prop = ({
149
- 'DOM Interfaces': 'interface'
150
- })[getText(dt).replace(/:$/, '')];
151
-
152
- if (prop === 'interface') {
153
- let dd = dt.nextElementSibling;
154
- while (dd && dd.nodeName !== 'DD') {
155
- dd = dd.nextElementSibling;
156
- }
157
- if (dd) {
158
- // For some reason, the "discard" element has no interface:
159
- // https://svgwg.org/specs/animations/#DiscardElement
160
- if (getText(dd)) {
161
- res[prop] = getText(dd);
162
- }
163
- }
164
- else {
165
- throw new Error('Could not link element to interface, missing dd for ' + res.name);
166
- }
167
- }
168
- });
169
- return res;
170
- });
171
-
172
-
173
- // Extract SVG elements that use the "definition-table" pattern
174
- const svgTableElements = [...document.querySelectorAll('table.definition-table')]
175
- .map(el => {
176
- const dfn = el.querySelector('dfn');
177
- if (!dfn) {
178
- throw new Error('Could not extract name from definition-table element');
179
- }
180
-
181
- const res = { name: getText(dfn) };
182
- const ths = [...el.querySelectorAll('th')];
183
- ths.forEach(th => {
184
- const prop = ({
185
- 'DOM Interfaces': 'interface'
186
- })[getText(th).replace(/:$/, '')];
187
-
188
- if (prop === 'interface') {
189
- let td = th.nextElementSibling;
190
- while (td && td.nodeName !== 'TD') {
191
- td = td.nextElementSibling;
192
- }
193
- if (td) {
194
- res[prop] = getText(td);
195
- }
196
- else {
197
- throw new Error('Could not link element to interface, missing cell for ' + res.name);
198
- }
199
- }
200
- });
201
- return res;
202
- });
203
-
204
- return htmlElements.concat(svgSummaryElements, svgTableElements);
205
- }
1
+ /**
2
+ * Extract the list of markup elements that the spec defines
3
+ *
4
+ * Extraction requires spec to follow a structure similar to the one used in
5
+ * the HTML spec:
6
+ * https://html.spec.whatwg.org/multipage/dom.html#element-definitions
7
+ *
8
+ * Important (2021-05-31): Extraction code is incomplete and only extracts the
9
+ * mapping between each element and its associated IDL interface for now. The
10
+ * rest of the extraction logic is currently disabled because it does not yet
11
+ * produce fully machine-readable results, see inline comments prefixed with
12
+ * "TODO" for details.
13
+ *
14
+ * @function
15
+ * @public
16
+ */
17
+ export default function () {
18
+ function getText(el) {
19
+ return el.textContent.trim().replace(/\s+/g, ' ');
20
+ }
21
+
22
+ // Extract HTML elements
23
+ const htmlElements = [...document.querySelectorAll('dl.element')]
24
+ .map(el => {
25
+ // Get back to heading that defines the element(s)
26
+ let heading = el.previousElementSibling;
27
+ while (heading && !heading.nodeName.match(/^H\d$/)) {
28
+ heading = heading.previousElementSibling;
29
+ }
30
+ if (!heading) {
31
+ throw new Error('Could not locate heading associated with element');
32
+ }
33
+
34
+ const dfns = [...heading.querySelectorAll('dfn')];
35
+ if (dfns.length === 0) {
36
+ // Ignore the definition of "Custom elements" in HTML
37
+ if (getText(heading).match(/Core concepts/)) {
38
+ return null;
39
+ }
40
+ throw new Error('No dfn found in heading element: ' + heading.textContent);
41
+ }
42
+
43
+ // In most cases, there will be only one element, but some elements are
44
+ // defined together, typically h1-h6 or sub and sup
45
+ return dfns.map(dfn => {
46
+ const res = { name: getText(dfn) };
47
+ const dts = [...el.querySelectorAll('dt')];
48
+ dts.forEach(dt => {
49
+ const prop = ({
50
+ // TODO: Properties other than "interface" follow common patterns
51
+ // most of the time... but not always, making it hard to create
52
+ // fully machine-readable info. Uncomment following lines to extract
53
+ // more info and improve the code
54
+ // 'Categories': 'categories',
55
+ // 'Contexts in which this element can be used': 'contexts',
56
+ // 'Content model': 'contents',
57
+ // 'Tag omission in text/html': 'omission',
58
+ // 'Content attributes': 'attributes',
59
+ // 'Accessibility considerations': 'accessibility',
60
+ 'DOM interface': 'interface'
61
+ })[getText(dt).replace(/:$/, '')];
62
+
63
+ if (prop === 'omission') {
64
+ // Tag omission can be a complex paragraph but it is never a list.
65
+ let dd = dt.nextElementSibling;
66
+ while (dd && dd.nodeName !== 'DD') {
67
+ dd = dd.nextElementSibling;
68
+ }
69
+ if (dd) {
70
+ res[prop] = getText(dd);
71
+ }
72
+ }
73
+ else if (prop === 'accessibility') {
74
+ // TODO: Accessibility considerations links to ARIA and AAM. The
75
+ // code does not yet handle more complex associations that currently
76
+ // appear in the definitions of the img, sub and sup, input and
77
+ // select elements.
78
+ res[prop] = [];
79
+ let dd = dt.nextElementSibling;
80
+ while (dd && dd.nodeName !== 'DT') {
81
+ if (dd.nodeName === 'DD') {
82
+ const link = dd.querySelector('a');
83
+ if (link) {
84
+ res[prop].push({
85
+ title: getText(link),
86
+ href: link.getAttribute('href')
87
+ });
88
+ }
89
+ }
90
+ dd = dd.nextElementSibling;
91
+ }
92
+ }
93
+ else if (prop === 'interface') {
94
+ let dd = dt.nextElementSibling;
95
+ while (dd && dd.nodeName !== 'DD') {
96
+ dd = dd.nextElementSibling;
97
+ }
98
+ if (dd) {
99
+ let match = dd.textContent.match(/^interface (.*?) /m);
100
+ if (match) {
101
+ res[prop] = match[1];
102
+ }
103
+ else {
104
+ // NB: The sub/sup table uses a plural form for "Use", hence
105
+ // the "?" for the final "s" in "Uses".
106
+ match = dd.textContent.match(/^Uses? (.*?)[,\.\s]/);
107
+ if (match) {
108
+ res[prop] = match[1];
109
+ }
110
+ else {
111
+ throw new Error('Could not link element to interface: ' + getText(dd));
112
+ }
113
+ }
114
+ }
115
+ else {
116
+ throw new Error('Could not link element to interface, missing dd for ' + res.name);
117
+ }
118
+ }
119
+ else if (prop) {
120
+ res[prop] = [];
121
+ let dd = dt.nextElementSibling;
122
+ while (dd && dd.nodeName !== 'DT') {
123
+ if (dd.nodeName === 'DD') {
124
+ res[prop].push(getText(dd).replace(/\.$/, ''));
125
+ }
126
+ dd = dd.nextElementSibling;
127
+ }
128
+ }
129
+ });
130
+ return res;
131
+ });
132
+ })
133
+ .filter(el => !!el)
134
+ .flat();
135
+
136
+
137
+ // Extract SVG elements that use the "element-summary" pattern
138
+ const svgSummaryElements = [...document.querySelectorAll('div.element-summary')]
139
+ .map(el => {
140
+ const name = el.querySelector('.element-name');
141
+ if (!name) {
142
+ throw new Error('Could not extract name from element-summary element');
143
+ }
144
+
145
+ const res = { name: getText(name).replace(/‘|’/g, '') };
146
+ const dts = [...el.querySelectorAll('dt')];
147
+ dts.forEach(dt => {
148
+ const prop = ({
149
+ 'DOM Interfaces': 'interface'
150
+ })[getText(dt).replace(/:$/, '')];
151
+
152
+ if (prop === 'interface') {
153
+ let dd = dt.nextElementSibling;
154
+ while (dd && dd.nodeName !== 'DD') {
155
+ dd = dd.nextElementSibling;
156
+ }
157
+ if (dd) {
158
+ // For some reason, the "discard" element has no interface:
159
+ // https://svgwg.org/specs/animations/#DiscardElement
160
+ if (getText(dd)) {
161
+ res[prop] = getText(dd);
162
+ }
163
+ }
164
+ else {
165
+ throw new Error('Could not link element to interface, missing dd for ' + res.name);
166
+ }
167
+ }
168
+ });
169
+ return res;
170
+ });
171
+
172
+
173
+ // Extract SVG elements that use the "definition-table" pattern
174
+ const svgTableElements = [...document.querySelectorAll('table.definition-table')]
175
+ .map(el => {
176
+ const dfn = el.querySelector('dfn');
177
+ if (!dfn) {
178
+ throw new Error('Could not extract name from definition-table element');
179
+ }
180
+
181
+ const res = { name: getText(dfn) };
182
+ const ths = [...el.querySelectorAll('th')];
183
+ ths.forEach(th => {
184
+ const prop = ({
185
+ 'DOM Interfaces': 'interface'
186
+ })[getText(th).replace(/:$/, '')];
187
+
188
+ if (prop === 'interface') {
189
+ let td = th.nextElementSibling;
190
+ while (td && td.nodeName !== 'TD') {
191
+ td = td.nextElementSibling;
192
+ }
193
+ if (td) {
194
+ res[prop] = getText(td);
195
+ }
196
+ else {
197
+ throw new Error('Could not link element to interface, missing cell for ' + res.name);
198
+ }
199
+ }
200
+ });
201
+ return res;
202
+ });
203
+
204
+ return htmlElements.concat(svgSummaryElements, svgTableElements);
205
+ }
@@ -1,48 +1,48 @@
1
- import getAbsoluteUrl from './get-absolute-url.mjs';
2
-
3
- /**
4
- * Extract headings data from documents
5
- */
6
- export default function (spec, idToHeading) {
7
- // Compute once whether we created a single page version out of multiple pages
8
- const singlePage = !document.querySelector('[data-reffy-page]');
9
-
10
- // Headings using the markup convention of the EcmaScript spec
11
- const esHeadings = [...document.querySelectorAll('emu-clause[id] > h1')].map(n => {
12
- const headingNumber = n.querySelector(".secnum")?.textContent;
13
- const headingLevel = headingNumber ? headingNumber.split(".").length : undefined;
14
- return {
15
- id: n.parentNode.id,
16
- title: n.textContent.replace(headingNumber, '').trim(),
17
- level: headingLevel,
18
- number: headingNumber
19
- };
20
- });
21
- return esHeadings.concat([...document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id] ,h6[id]')].map(n => {
22
- // Note: In theory, all <hX> heading elements that have an ID are associated
23
- // with a heading in idToHeading. One exception to the rule: when the
24
- // heading element appears in a <hgroup> element, the mapping is not
25
- // properly done (the outline creation algorithm explicitly skips these
26
- // headings not to create a mess in the outline). In practice, this only
27
- // really happens so far for WHATWG spec titles that (correctly) group the
28
- // title and subtitle headings in a <hgroup>.
29
- const href = getAbsoluteUrl(n, { singlePage });
30
- const heading = idToHeading[href] || {
31
- id: n.id,
32
- href,
33
- title: n.textContent.trim()
34
- };
35
-
36
- const res = {
37
- id: heading.id,
38
- href: heading.href,
39
- level: parseInt(n.tagName.slice(1), 10),
40
- title: heading.title
41
- };
42
- if (heading.number) {
43
- res.number = heading.number;
44
- }
45
-
46
- return res;
47
- }));
48
- }
1
+ import getAbsoluteUrl from './get-absolute-url.mjs';
2
+
3
+ /**
4
+ * Extract headings data from documents
5
+ */
6
+ export default function (spec, idToHeading) {
7
+ // Compute once whether we created a single page version out of multiple pages
8
+ const singlePage = !document.querySelector('[data-reffy-page]');
9
+
10
+ // Headings using the markup convention of the EcmaScript spec
11
+ const esHeadings = [...document.querySelectorAll('emu-clause[id] > h1')].map(n => {
12
+ const headingNumber = n.querySelector(".secnum")?.textContent;
13
+ const headingLevel = headingNumber ? headingNumber.split(".").length : undefined;
14
+ return {
15
+ id: n.parentNode.id,
16
+ title: n.textContent.replace(headingNumber, '').trim(),
17
+ level: headingLevel,
18
+ number: headingNumber
19
+ };
20
+ });
21
+ return esHeadings.concat([...document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id] ,h6[id]')].map(n => {
22
+ // Note: In theory, all <hX> heading elements that have an ID are associated
23
+ // with a heading in idToHeading. One exception to the rule: when the
24
+ // heading element appears in a <hgroup> element, the mapping is not
25
+ // properly done (the outline creation algorithm explicitly skips these
26
+ // headings not to create a mess in the outline). In practice, this only
27
+ // really happens so far for WHATWG spec titles that (correctly) group the
28
+ // title and subtitle headings in a <hgroup>.
29
+ const href = getAbsoluteUrl(n, { singlePage });
30
+ const heading = idToHeading[href] || {
31
+ id: n.id,
32
+ href,
33
+ title: n.textContent.trim()
34
+ };
35
+
36
+ const res = {
37
+ id: heading.id,
38
+ href: heading.href,
39
+ level: parseInt(n.tagName.slice(1), 10),
40
+ title: heading.title
41
+ };
42
+ if (heading.number) {
43
+ res.number = heading.number;
44
+ }
45
+
46
+ return res;
47
+ }));
48
+ }
@@ -1,28 +1,28 @@
1
- import getAbsoluteUrl from './get-absolute-url.mjs';
2
-
3
- /**
4
- * Extract absolute ids from documents
5
- */
6
- export default function () {
7
- // Compute once whether we created a single page version out of multiple pages
8
- const singlePage = !document.querySelector('[data-reffy-page]');
9
-
10
- return [...document.querySelectorAll('*[id]')]
11
- // Ignore respec- prefixed ids to avoid keeping track of their evolution
12
- // They're clearly not meant to be link target in any case
13
- .filter(n => !n.id.startsWith('respec-'))
14
-
15
- // Ignore dfn-panel- prefixed ids that ReSpec generates to avoid keeping
16
- // track of their evolution. They're clearly not meant to be link target
17
- // either.
18
- .filter(n => !n.id.startsWith('dfn-panel-'))
19
-
20
- // Convert IDs to absolute URLs (needed to handle multipage specs)
21
- .map(n => getAbsoluteUrl(n, { singlePage }))
22
-
23
- // Capture anchors set in <a name> when they're not dup of ids
24
- .concat([...document.querySelectorAll('a[name]')]
25
- .filter(n => !n.id || n.id !== n.name)
26
- .map(n => getAbsoluteUrl(n, { singlePage, attribute: 'name' }))
27
- );
28
- }
1
+ import getAbsoluteUrl from './get-absolute-url.mjs';
2
+
3
+ /**
4
+ * Extract absolute ids from documents
5
+ */
6
+ export default function () {
7
+ // Compute once whether we created a single page version out of multiple pages
8
+ const singlePage = !document.querySelector('[data-reffy-page]');
9
+
10
+ return [...document.querySelectorAll('*[id]')]
11
+ // Ignore respec- prefixed ids to avoid keeping track of their evolution
12
+ // They're clearly not meant to be link target in any case
13
+ .filter(n => !n.id.startsWith('respec-'))
14
+
15
+ // Ignore dfn-panel- prefixed ids that ReSpec generates to avoid keeping
16
+ // track of their evolution. They're clearly not meant to be link target
17
+ // either.
18
+ .filter(n => !n.id.startsWith('dfn-panel-'))
19
+
20
+ // Convert IDs to absolute URLs (needed to handle multipage specs)
21
+ .map(n => getAbsoluteUrl(n, { singlePage }))
22
+
23
+ // Capture anchors set in <a name> when they're not dup of ids
24
+ .concat([...document.querySelectorAll('a[name]')]
25
+ .filter(n => !n.id || n.id !== n.name)
26
+ .map(n => getAbsoluteUrl(n, { singlePage, attribute: 'name' }))
27
+ );
28
+ }
@@ -1,28 +1,28 @@
1
- import { canonicalizeUrl } from './canonicalize-url.mjs';
2
-
3
- /**
4
- * Extract and canonicalize absolute links of the document and their fragments
5
- * FIXME: ⚠ Modify the DOM
6
- */
7
- export default function () {
8
- // Ignore links from the "head" section, which either link to
9
- // self, the GitHub repo, the implementation report, and other
10
- // documents that don't need to appear in the list of references.
11
- const links = {};
12
- [...document.querySelectorAll('.head a[href]')].forEach(n => n.href = '');
13
- document.querySelectorAll('a[href^=http]').forEach(n => {
14
- const url = canonicalizeUrl(n.href);
15
- if (!links[url]) {
16
- links[url] = new Set();
17
- }
18
- if (n.href.includes('#') && n.href.split('#')[1]) {
19
- links[url].add(n.href.split('#')[1]);
20
- }
21
- });
22
- return Object.keys(links)
23
- // turning sets into arrays
24
- .reduce((acc, u) => {
25
- acc[u] = [...links[u]];
26
- return acc;
27
- }, {});
28
- }
1
+ import { canonicalizeUrl } from './canonicalize-url.mjs';
2
+
3
+ /**
4
+ * Extract and canonicalize absolute links of the document and their fragments
5
+ * FIXME: ⚠ Modify the DOM
6
+ */
7
+ export default function () {
8
+ // Ignore links from the "head" section, which either link to
9
+ // self, the GitHub repo, the implementation report, and other
10
+ // documents that don't need to appear in the list of references.
11
+ const links = {};
12
+ [...document.querySelectorAll('.head a[href]')].forEach(n => n.href = '');
13
+ document.querySelectorAll('a[href^=http]').forEach(n => {
14
+ const url = canonicalizeUrl(n.href);
15
+ if (!links[url]) {
16
+ links[url] = new Set();
17
+ }
18
+ if (n.href.includes('#') && n.href.split('#')[1]) {
19
+ links[url].add(n.href.split('#')[1]);
20
+ }
21
+ });
22
+ return Object.keys(links)
23
+ // turning sets into arrays
24
+ .reduce((acc, u) => {
25
+ acc[u] = [...links[u]];
26
+ return acc;
27
+ }, {});
28
+ }