reffy 11.0.2 → 11.0.4

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": "11.0.2",
3
+ "version": "11.0.4",
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",
@@ -46,9 +46,9 @@
46
46
  "chai": "4.3.7",
47
47
  "mocha": "10.1.0",
48
48
  "nock": "13.2.9",
49
- "respec": "32.3.0",
49
+ "respec": "32.4.0",
50
50
  "respec-hljs": "2.1.1",
51
- "rollup": "3.5.0"
51
+ "rollup": "3.5.1"
52
52
  },
53
53
  "scripts": {
54
54
  "test": "mocha --recursive tests/"
@@ -240,21 +240,14 @@ export default function () {
240
240
  // <image> again:
241
241
  // https://drafts.csswg.org/css-images-4/#typedef-image
242
242
  const isAncestorOf = (ancestor, child) => {
243
- let seen = [];
244
- const checkChild = c => {
245
- let res = ancestor === c;
246
- if (!res) {
247
- res = parents[c]
248
- ?.filter(p => !seen.includes(p))
249
- ?.find(p => checkChild(ancestor, p));
250
- }
251
- seen = seen.concat(parents[c]);
252
- return res;
253
- }
254
- return checkChild(child);
243
+ const checkChild = (c, depth) =>
244
+ (depth++ < 10) &&
245
+ (c === ancestor || parents[c]?.find(p => checkChild(p, depth)));
246
+ return checkChild(child, 0);
255
247
  };
256
- const isDeepestConstruct = (name, list) =>
257
- list.every(p => p === name || !isAncestorOf(name, p));
248
+ const isDeepestConstruct = (name, list) => {
249
+ return list.every(p => p === name || !isAncestorOf(name, p));
250
+ }
258
251
 
259
252
  // We may now associate values with dfns
260
253
  for (const value of values) {
@@ -544,6 +537,40 @@ const parseProductionRule = (rule, { res = [], pureSyntax = false }) => {
544
537
  };
545
538
 
546
539
 
540
+ /**
541
+ * Extract the definition name. If multiple linking texts are possible, the
542
+ * function will select the one that is a "syntax" definition. For instance, it
543
+ * will pick up "<identifier>" for "identifiers|<identifier>" in CSS 2:
544
+ * https://drafts.csswg.org/css2/#value-def-identifier
545
+ *
546
+ * The function throws if it cannot extract an obvious name from the definition.
547
+ * We may want to be smarter about that in the future, but this should really
548
+ * never happen in practice (the above "identifiers" example is the only one
549
+ * CSS value definition so far to have multiple linking texts)
550
+ */
551
+ const getDfnName = dfn => {
552
+ if (dfn.getAttribute('data-lt')) {
553
+ const names = dfn.getAttribute('data-lt').split('|').map(normalize);
554
+ let name = names.find(n =>
555
+ n.startsWith('<') || // Looks like a "type"
556
+ n.startsWith('@') || // Looks like an "at-rule"
557
+ n.startsWith(':') || // Looks like a "descriptor"
558
+ n.endsWith('()') || // Looks like a "function"
559
+ n === dfn.textContent.trim()); // Looks like the right term
560
+ if (!name) {
561
+ if (names.length > 1) {
562
+ throw new Error(`Found multiple linking texts for dfn without any obvious one: ${names.join(', ')}`);
563
+ }
564
+ name = names[0];
565
+ }
566
+ return name;
567
+ }
568
+ else {
569
+ return dfn.textContent.trim();
570
+ }
571
+ };
572
+
573
+
547
574
  /**
548
575
  * Extract the given dfn
549
576
  */
@@ -553,6 +580,7 @@ const extractTypedDfn = dfn => {
553
580
  const dfnType = dfn.getAttribute('data-dfn-type');
554
581
  const dfnFor = dfn.getAttribute('data-dfn-for');
555
582
  const parent = dfn.parentNode.cloneNode(true);
583
+ const fnRegExp = /^([a-zA-Z_][a-zA-Z0-9_\-]+)\([^\)]+\)$/;
556
584
 
557
585
  // Remove note references as in:
558
586
  // https://drafts.csswg.org/css-syntax-3/#the-anb-type
@@ -574,8 +602,17 @@ const extractTypedDfn = dfn => {
574
602
  // Don't attempt to parse pre tags at this stage, they are tricky to
575
603
  // split, we'll parse them as text and map them to the right definitions
576
604
  // afterwards.
577
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
578
- res = { name };
605
+ // That said, we may be looking at a function definition on the right hand
606
+ // side of a production rule, as in the definition of "linear()" in
607
+ // css-easing-2: https://drafts.csswg.org/css-easing-2/#funcdef-linear
608
+ // In such a case, we still want to extract the function parameters
609
+ if (dfn.textContent.trim().match(fnRegExp)) {
610
+ const fn = dfn.textContent.trim().match(fnRegExp)[1];
611
+ res = parseProductionRule(`${fn}() = ${dfn.textContent.trim()}`, { pureSyntax: false });
612
+ }
613
+ else {
614
+ res = { name: getDfnName(dfn) };
615
+ }
579
616
  }
580
617
  else if (prod) {
581
618
  res = parseProductionRule(prod, { pureSyntax: true });
@@ -585,13 +622,12 @@ const extractTypedDfn = dfn => {
585
622
  // https://drafts.csswg.org/css-speech-1/#typedef-voice-volume-decibel
586
623
  // It may be worth checking but not an error per se.
587
624
  console.warn('[reffy]', `Found "=" next to definition of ${dfn.textContent.trim()} but no production rule. Did I miss something?`);
588
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
589
- res = { name, prose: text.replace(/\s+/g, ' ') };
625
+ res = { name: getDfnName(dfn), prose: text.replace(/\s+/g, ' ') };
590
626
  }
591
627
  }
592
- else if (dfn.textContent.trim().match(/^[a-zA-Z_][a-zA-Z0-9_\-]+\([^\)]+\)$/)) {
628
+ else if (dfn.textContent.trim().match(fnRegExp)) {
593
629
  // Definition is "prod(foo bar)", create a "prod() = prod(foo bar)" entry
594
- const fn = dfn.textContent.trim().match(/^([a-zA-Z_][a-zA-Z0-9_\-]+)\([^\)]+\)$/)[1];
630
+ const fn = dfn.textContent.trim().match(fnRegExp)[1];
595
631
  res = parseProductionRule(`${fn}() = ${dfn.textContent.trim()}`, { pureSyntax: false });
596
632
  }
597
633
  else if (parent.nodeName === 'DT') {
@@ -625,20 +661,23 @@ const extractTypedDfn = dfn => {
625
661
  }
626
662
  });
627
663
 
628
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
629
- res = { name, prose: dd.textContent.trim().replace(/\s+/g, ' ') };
664
+ res = {
665
+ name: getDfnName(dfn),
666
+ prose: dd.textContent.trim().replace(/\s+/g, ' ')
667
+ };
630
668
  }
631
669
  }
632
670
  else if (parent.nodeName === 'P') {
633
671
  // Definition is in regular prose, extract value from prose.
634
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
635
- res = { name, prose: parent.textContent.trim().replace(/\s+/g, ' ') };
672
+ res = {
673
+ name: getDfnName(dfn),
674
+ prose: parent.textContent.trim().replace(/\s+/g, ' ')
675
+ };
636
676
  }
637
677
  else {
638
678
  // Definition is in a heading or a more complex structure, just list the
639
679
  // name for now.
640
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
641
- res = { name };
680
+ res = { name: getDfnName(dfn) };
642
681
  }
643
682
 
644
683
  res.type = dfnType;
@@ -106,17 +106,18 @@ function hasValidType(el) {
106
106
  return isValid;
107
107
  }
108
108
 
109
- // Return true when definition is not already defined in the list,
109
+ // Return true when exported definition is not already defined in the list,
110
110
  // Return false and issue a warning when it is already defined.
111
- function isNotAlreadyDefined(dfn, idx, list) {
111
+ function isNotAlreadyExported(dfn, idx, list) {
112
112
  const first = list.find(d => d === dfn ||
113
- (d.type === dfn.type &&
113
+ (d.access === 'public' && dfn.access === 'public' &&
114
+ d.type === dfn.type &&
114
115
  d.linkingText.length === dfn.linkingText.length &&
115
116
  d.linkingText.every(lt => dfn.linkingText.find(t => t == lt)) &&
116
117
  d.for.length === dfn.for.length &&
117
118
  d.for.every(lt => dfn.for.find(t => t === lt))));
118
119
  if (first !== dfn) {
119
- console.warn('[reffy]', `Duplicate dfn found for "${dfn.linkingText[0]}", type="${dfn.type}", for="${dfn.for[0]}"`);
120
+ console.warn('[reffy]', `Duplicate dfn found for "${dfn.linkingText[0]}", type="${dfn.type}", for="${dfn.for[0]}", dupl=${dfn.href}, first=${first.href}`);
120
121
  }
121
122
  return first === dfn;
122
123
  }
@@ -267,7 +268,7 @@ export default function (spec, idToHeading = {}) {
267
268
  return !link || (node.textContent.trim() !== link.textContent.trim());
268
269
  })
269
270
  .map(node => definitionMapper(node, idToHeading, usesDfnDataModel))
270
- .filter(isNotAlreadyDefined);
271
+ .filter(isNotAlreadyExported);
271
272
  }
272
273
 
273
274
  function preProcessEcmascript() {