reffy 15.0.2 → 15.2.0
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reffy",
|
|
3
|
-
"version": "15.0
|
|
3
|
+
"version": "15.2.0",
|
|
4
4
|
"description": "W3C/WHATWG spec dependencies exploration companion. Features a short set of tools to study spec references as well as WebIDL term definitions and references found in W3C specifications.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,19 +33,19 @@
|
|
|
33
33
|
"bin": "./reffy.js",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"ajv": "8.12.0",
|
|
36
|
-
"ajv-formats": "
|
|
36
|
+
"ajv-formats": "3.0.1",
|
|
37
37
|
"commander": "12.0.0",
|
|
38
38
|
"fetch-filecache-for-crawling": "5.1.1",
|
|
39
|
-
"puppeteer": "22.6.
|
|
39
|
+
"puppeteer": "22.6.5",
|
|
40
40
|
"semver": "^7.3.5",
|
|
41
|
-
"web-specs": "3.
|
|
41
|
+
"web-specs": "3.8.0",
|
|
42
42
|
"webidl2": "24.4.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"mocha": "10.4.0",
|
|
46
46
|
"respec": "34.5.0",
|
|
47
47
|
"respec-hljs": "2.1.1",
|
|
48
|
-
"rollup": "4.14.
|
|
48
|
+
"rollup": "4.14.3",
|
|
49
49
|
"undici": "^6.1.0"
|
|
50
50
|
},
|
|
51
51
|
"overrides": {
|
|
@@ -3,10 +3,6 @@ import extractWebIdl from './extract-webidl.mjs';
|
|
|
3
3
|
import {parse} from "../../node_modules/webidl2/index.js";
|
|
4
4
|
import getAbsoluteUrl from './get-absolute-url.mjs';
|
|
5
5
|
|
|
6
|
-
const isSameEvent = (e1, e2) => e1.type === e2.type &&
|
|
7
|
-
((e1.href && e1.href === e2.href ) ||
|
|
8
|
-
(e1.targets?.sort()?.join("|") === e2.targets?.sort()?.join("|")));
|
|
9
|
-
|
|
10
6
|
const singlePage = !document.querySelector('[data-reffy-page]');
|
|
11
7
|
const href = el => el?.getAttribute("id") ? getAbsoluteUrl(el, {singlePage}) : null;
|
|
12
8
|
|
|
@@ -37,6 +33,15 @@ export default function (spec) {
|
|
|
37
33
|
return acc;
|
|
38
34
|
}, {});
|
|
39
35
|
|
|
36
|
+
function isSameEvent(e1, e2) {
|
|
37
|
+
const res = e1.type === e2.type &&
|
|
38
|
+
((e1.href && e1.href === e2.href ) ||
|
|
39
|
+
(e1.targets?.sort()?.join("|") === e2.targets?.sort()?.join("|")));
|
|
40
|
+
if (res && e1.cancelable !== undefined && e2.cancelable !== undefined && e1.cancelable !== e2.cancelable) {
|
|
41
|
+
console.error(`[reffy] Found two occurrences of same event with different "cancelable" properties in ${spec.title}: type=${e1.type} targets=${e1.targets.join(', ')} href=${e1.href}`);
|
|
42
|
+
}
|
|
43
|
+
return res;
|
|
44
|
+
}
|
|
40
45
|
|
|
41
46
|
function fromEventElementToTargetInterfaces(eventEl) {
|
|
42
47
|
if (!eventEl) return;
|
|
@@ -74,6 +79,8 @@ export default function (spec) {
|
|
|
74
79
|
// Useful e.g. for pointerevents
|
|
75
80
|
const bubblingInfoColumn = [...table.querySelectorAll("thead th")]
|
|
76
81
|
.findIndex(n => n.textContent.trim().match(/^bubbl/i));
|
|
82
|
+
const cancelableInfoColumn = [...table.querySelectorAll("thead th")]
|
|
83
|
+
.findIndex(n => n.textContent.trim().match(/^cancel/i));
|
|
77
84
|
const interfaceColumn = [...table.querySelectorAll("thead th")]
|
|
78
85
|
.findIndex(n => n.textContent.trim().match(/^(dom )?interface/i));
|
|
79
86
|
const targetsColumn = [...table.querySelectorAll("thead th")]
|
|
@@ -115,6 +122,9 @@ export default function (spec) {
|
|
|
115
122
|
if (bubblingInfoColumn >= 0) {
|
|
116
123
|
event.bubbles = tr.querySelector(`td:nth-child(${bubblingInfoColumn + 1})`)?.textContent?.trim() === "Yes";
|
|
117
124
|
}
|
|
125
|
+
if (cancelableInfoColumn >= 0) {
|
|
126
|
+
event.cancelable = !!tr.querySelector(`td:nth-child(${cancelableInfoColumn + 1})`)?.textContent?.trim().match(/(yes)|✓|(varies)/i);
|
|
127
|
+
}
|
|
118
128
|
if (interfaceColumn >= 0) {
|
|
119
129
|
event.interface =
|
|
120
130
|
tr.querySelector(`td:nth-child(${interfaceColumn + 1}) a`)?.textContent ??
|
|
@@ -134,14 +144,17 @@ export default function (spec) {
|
|
|
134
144
|
}
|
|
135
145
|
const eventTypeRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim().match(/^type/i));
|
|
136
146
|
const bubblingInfoRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim() === "Bubbles");
|
|
147
|
+
const cancelableInfoRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim() === "Cancelable");
|
|
137
148
|
const interfaceRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim().match(/^interface/i));
|
|
138
149
|
const eventName = table.querySelector(`tr:nth-child(${eventTypeRow + 1}) td:nth-child(2)`)?.textContent?.trim();
|
|
139
150
|
const bubblesCell = table.querySelector(`tr:nth-child(${bubblingInfoRow + 1}) td:nth-child(2)`);
|
|
140
151
|
const bubbles = bubblesCell ? bubblesCell.textContent.trim() === "Yes" : null;
|
|
152
|
+
const cancelableCell = table.querySelector(`tr:nth-child(${cancelableInfoRow + 1}) td:nth-child(2)`);
|
|
153
|
+
const cancelable = cancelableCell ? cancelableCell.textContent.trim() === "Yes" : null;
|
|
141
154
|
const iface = table.querySelector(`tr:nth-child(${interfaceRow + 1}) td:nth-child(2)`)?.textContent?.trim();
|
|
142
155
|
if (eventName) {
|
|
143
156
|
events.push({
|
|
144
|
-
type: eventName, interface: iface, bubbles,
|
|
157
|
+
type: eventName, interface: iface, bubbles, cancelable,
|
|
145
158
|
src: { format: "css definition table", href: href(table.closest('*[id]')) },
|
|
146
159
|
href: href(table.closest('*[id]')) });
|
|
147
160
|
}
|
|
@@ -208,6 +221,7 @@ export default function (spec) {
|
|
|
208
221
|
phrasing = "fire functional event";
|
|
209
222
|
}
|
|
210
223
|
}
|
|
224
|
+
|
|
211
225
|
if (phrasing) {
|
|
212
226
|
const name = m.groups.eventName;
|
|
213
227
|
let newEvent = true;
|
|
@@ -263,6 +277,17 @@ export default function (spec) {
|
|
|
263
277
|
}
|
|
264
278
|
}
|
|
265
279
|
}
|
|
280
|
+
if (event.bubbles === undefined && event.cancelable === undefined) {
|
|
281
|
+
if (parsedText.match(/bubbles and cancelable attributes/)) {
|
|
282
|
+
if (parsedText.match(/true/)) {
|
|
283
|
+
event.bubbles = true;
|
|
284
|
+
event.cancelable = true;
|
|
285
|
+
} else if (parsedText.match(/false/)) {
|
|
286
|
+
event.bubbles = false;
|
|
287
|
+
event.cancelable = false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
266
291
|
if (event.bubbles === undefined) {
|
|
267
292
|
if (parsedText.match(/bubbles attribute/)) {
|
|
268
293
|
if (parsedText.match(/true/)) {
|
|
@@ -276,6 +301,19 @@ export default function (spec) {
|
|
|
276
301
|
event.bubbles = false;
|
|
277
302
|
}
|
|
278
303
|
}
|
|
304
|
+
if (event.cancelable === undefined) {
|
|
305
|
+
if (parsedText.match(/cancelable attribute/)) {
|
|
306
|
+
if (parsedText.match(/true/)) {
|
|
307
|
+
event.cancelable = true;
|
|
308
|
+
} else if (parsedText.match(/false/)) {
|
|
309
|
+
event.cancelable = false;
|
|
310
|
+
}
|
|
311
|
+
} else if (parsedText.match(/not cancelable/) || parsedText.match(/not be cancelable/)) {
|
|
312
|
+
event.cancelable = false;
|
|
313
|
+
} else if (parsedText.match(/cancelable/)) {
|
|
314
|
+
event.cancelable = true;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
279
317
|
if (newEvent) {
|
|
280
318
|
events.push(event);
|
|
281
319
|
}
|
|
@@ -329,13 +367,18 @@ export default function (spec) {
|
|
|
329
367
|
};
|
|
330
368
|
// CSS Animations & Transitions uses dt/dd to describe events
|
|
331
369
|
// and uses a ul in the dd to describe bubbling behavior
|
|
332
|
-
let bubbles, iface;
|
|
370
|
+
let bubbles, iface, cancelable;
|
|
333
371
|
if (container.tagName === "DT") {
|
|
334
372
|
const bubbleItem = [...container.nextElementSibling.querySelectorAll("li")]
|
|
335
373
|
.find(li => li.textContent.startsWith("Bubbles:"));
|
|
336
374
|
if (bubbleItem) {
|
|
337
375
|
bubbles = !!bubbleItem.textContent.match(/yes/i);
|
|
338
376
|
}
|
|
377
|
+
const cancelableItem = [...container.nextElementSibling.querySelectorAll("li")]
|
|
378
|
+
.find(li => li.textContent.startsWith("Cancelable:"));
|
|
379
|
+
if (cancelableItem) {
|
|
380
|
+
cancelable = !!cancelableItem.textContent.match(/yes/i);
|
|
381
|
+
}
|
|
339
382
|
// CSS Animation & Transitions document the event in the heading
|
|
340
383
|
// of the section where the definitions are located
|
|
341
384
|
let currentEl = container.parentNode;
|
|
@@ -356,6 +399,7 @@ export default function (spec) {
|
|
|
356
399
|
event.interface = iface;
|
|
357
400
|
}
|
|
358
401
|
event.bubbles = bubbles;
|
|
402
|
+
event.cancelable = cancelable;
|
|
359
403
|
events.push(event);
|
|
360
404
|
if (!iface) {
|
|
361
405
|
console.error(`[reffy] No interface hint found for event definition ${event.type} in ${spec.title}`);
|
|
@@ -370,6 +414,9 @@ export default function (spec) {
|
|
|
370
414
|
if (bubbles !== undefined) {
|
|
371
415
|
ev.bubbles = bubbles;
|
|
372
416
|
}
|
|
417
|
+
if (cancelable !== undefined) {
|
|
418
|
+
ev.cancelable = cancelable;
|
|
419
|
+
}
|
|
373
420
|
}
|
|
374
421
|
});
|
|
375
422
|
return events
|
|
@@ -65,8 +65,14 @@ module.exports = {
|
|
|
65
65
|
})
|
|
66
66
|
.filter(event => !!event);
|
|
67
67
|
|
|
68
|
+
// Before we clean and sort the result, we'll consolidate events that
|
|
69
|
+
// don't always bubble. We'll call them... "babbling" events. Such events
|
|
70
|
+
// should remain exceptions to the rule, and will likely be artificially
|
|
71
|
+
// created through some patching mechanism (in Webref typically) because
|
|
72
|
+
// the events extraction logic does not (yet?) support this scenario.
|
|
68
73
|
return events
|
|
69
74
|
.filter(event => !eventsToDrop.includes(event))
|
|
75
|
+
.filter(event => consolidateBabblingEvent(event, events))
|
|
70
76
|
.map(event => {
|
|
71
77
|
cleanTargetInterfaces(event, parsedInterfaces);
|
|
72
78
|
delete event.spec;
|
|
@@ -223,3 +229,31 @@ function extendEvent(event, events) {
|
|
|
223
229
|
{ spec: event.spec.series.shortname },
|
|
224
230
|
event.src?.href ? { href: event.src?.href } : {}));
|
|
225
231
|
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Consolidate events that got duplicated in the extract because they bubble
|
|
236
|
+
* or don't bubble depending on the target interface.
|
|
237
|
+
*
|
|
238
|
+
* We'll say that these events "babble" because they don't seem to know whether
|
|
239
|
+
* they bubble or not.
|
|
240
|
+
*/
|
|
241
|
+
function consolidateBabblingEvent(event, events) {
|
|
242
|
+
if (event.mergedIntoAnotherEvent) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
const newTargets = events
|
|
246
|
+
.filter(e =>
|
|
247
|
+
e !== event && !e.isExtension && !e.mergedIntoAnotherEvent &&
|
|
248
|
+
e.href && e.href === event.href && e.cancelable === event.cancelable)
|
|
249
|
+
.map(e => {
|
|
250
|
+
// Flag the event as merged so that we can filter it out afterwards
|
|
251
|
+
e.mergedIntoAnotherEvent = true;
|
|
252
|
+
return e.targets;
|
|
253
|
+
})
|
|
254
|
+
.flat();
|
|
255
|
+
if (newTargets.length > 0) {
|
|
256
|
+
event.targets = (event.targets || []).concat(newTargets);
|
|
257
|
+
}
|
|
258
|
+
return event;
|
|
259
|
+
}
|