reffy 11.0.2 → 11.0.3

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.3",
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,7 +46,7 @@
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
51
  "rollup": "3.5.0"
52
52
  },
@@ -544,6 +544,39 @@ const parseProductionRule = (rule, { res = [], pureSyntax = false }) => {
544
544
  };
545
545
 
546
546
 
547
+ /**
548
+ * Extract the definition name. If multiple linking texts are possible, the
549
+ * function will select the one that is a "syntax" definition. For instance, it
550
+ * will pick up "<identifier>" for "identifiers|<identifier>" in CSS 2:
551
+ * https://drafts.csswg.org/css2/#value-def-identifier
552
+ *
553
+ * The function throws if it cannot extract an obvious name from the definition.
554
+ * We may want to be smarter about that in the future, but this should really
555
+ * never happen in practice (the above "identifiers" example is the only one
556
+ * CSS value definition so far to have multiple linking texts)
557
+ */
558
+ const getDfnName = dfn => {
559
+ if (dfn.getAttribute('data-lt')) {
560
+ const names = dfn.getAttribute('data-lt').split('|').map(normalize);
561
+ let name = names.find(n =>
562
+ n.startsWith('<') || // Looks like a "type"
563
+ n.startsWith('@') || // Looks like an "at-rule"
564
+ n.startsWith(':') || // Looks like a "descriptor"
565
+ n.endsWith('()')); // Looks like a "function"
566
+ if (!name) {
567
+ if (names.length > 1) {
568
+ throw new Error(`Found multiple linking texts for dfn without any obvious one: ${names.join(', ')}`);
569
+ }
570
+ name = names[0];
571
+ }
572
+ return name;
573
+ }
574
+ else {
575
+ return dfn.textContent.trim();
576
+ }
577
+ };
578
+
579
+
547
580
  /**
548
581
  * Extract the given dfn
549
582
  */
@@ -553,6 +586,7 @@ const extractTypedDfn = dfn => {
553
586
  const dfnType = dfn.getAttribute('data-dfn-type');
554
587
  const dfnFor = dfn.getAttribute('data-dfn-for');
555
588
  const parent = dfn.parentNode.cloneNode(true);
589
+ const fnRegExp = /^([a-zA-Z_][a-zA-Z0-9_\-]+)\([^\)]+\)$/;
556
590
 
557
591
  // Remove note references as in:
558
592
  // https://drafts.csswg.org/css-syntax-3/#the-anb-type
@@ -574,8 +608,17 @@ const extractTypedDfn = dfn => {
574
608
  // Don't attempt to parse pre tags at this stage, they are tricky to
575
609
  // split, we'll parse them as text and map them to the right definitions
576
610
  // afterwards.
577
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
578
- res = { name };
611
+ // That said, we may be looking at a function definition on the right hand
612
+ // side of a production rule, as in the definition of "linear()" in
613
+ // css-easing-2: https://drafts.csswg.org/css-easing-2/#funcdef-linear
614
+ // In such a case, we still want to extract the function parameters
615
+ if (dfn.textContent.trim().match(fnRegExp)) {
616
+ const fn = dfn.textContent.trim().match(fnRegExp)[1];
617
+ res = parseProductionRule(`${fn}() = ${dfn.textContent.trim()}`, { pureSyntax: false });
618
+ }
619
+ else {
620
+ res = { name: getDfnName(dfn) };
621
+ }
579
622
  }
580
623
  else if (prod) {
581
624
  res = parseProductionRule(prod, { pureSyntax: true });
@@ -585,13 +628,12 @@ const extractTypedDfn = dfn => {
585
628
  // https://drafts.csswg.org/css-speech-1/#typedef-voice-volume-decibel
586
629
  // It may be worth checking but not an error per se.
587
630
  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, ' ') };
631
+ res = { name: getDfnName(dfn), prose: text.replace(/\s+/g, ' ') };
590
632
  }
591
633
  }
592
- else if (dfn.textContent.trim().match(/^[a-zA-Z_][a-zA-Z0-9_\-]+\([^\)]+\)$/)) {
634
+ else if (dfn.textContent.trim().match(fnRegExp)) {
593
635
  // 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];
636
+ const fn = dfn.textContent.trim().match(fnRegExp)[1];
595
637
  res = parseProductionRule(`${fn}() = ${dfn.textContent.trim()}`, { pureSyntax: false });
596
638
  }
597
639
  else if (parent.nodeName === 'DT') {
@@ -625,20 +667,23 @@ const extractTypedDfn = dfn => {
625
667
  }
626
668
  });
627
669
 
628
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
629
- res = { name, prose: dd.textContent.trim().replace(/\s+/g, ' ') };
670
+ res = {
671
+ name: getDfnName(dfn),
672
+ prose: dd.textContent.trim().replace(/\s+/g, ' ')
673
+ };
630
674
  }
631
675
  }
632
676
  else if (parent.nodeName === 'P') {
633
677
  // 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, ' ') };
678
+ res = {
679
+ name: getDfnName(dfn),
680
+ prose: parent.textContent.trim().replace(/\s+/g, ' ')
681
+ };
636
682
  }
637
683
  else {
638
684
  // Definition is in a heading or a more complex structure, just list the
639
685
  // name for now.
640
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
641
- res = { name };
686
+ res = { name: getDfnName(dfn) };
642
687
  }
643
688
 
644
689
  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() {