reffy 11.0.1 → 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.1",
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
  },
@@ -48,7 +48,8 @@
48
48
  "values": { "$ref": "../common.json#/$defs/cssValues" }
49
49
  }
50
50
  }
51
- }
51
+ },
52
+ "values": { "$ref": "../common.json#/$defs/cssValues" }
52
53
  }
53
54
  }
54
55
  },
@@ -43,11 +43,11 @@
43
43
  "type": "array",
44
44
  "items": {
45
45
  "type": "object",
46
- "required": ["name", "type", "value"],
46
+ "required": ["name", "type"],
47
47
  "additionalProperties": false,
48
48
  "properties": {
49
49
  "name": { "$ref": "#/$defs/cssValue" },
50
- "type": { "type": "string", "enum": ["type", "function", "value"] },
50
+ "type": { "type": "string", "enum": ["type", "function", "value", "selector"] },
51
51
  "prose": { "type": "string" },
52
52
  "value": { "$ref": "#/$defs/cssValue" },
53
53
  "legacyValue": { "$ref": "#/$defs/cssValue" },
@@ -36,7 +36,9 @@ export default function () {
36
36
  warnings
37
37
  }),
38
38
  selectors: extractDfns({
39
- selector: 'dfn[data-dfn-type=selector][data-export]',
39
+ selector: ['dfn[data-dfn-type=selector][data-export]:not([data-dfn-for])',
40
+ 'dfn[data-dfn-type=selector][data-export][data-dfn-for=""]'
41
+ ].join(','),
40
42
  extractor: extractTypedDfn,
41
43
  duplicates: 'reject',
42
44
  warnings
@@ -125,7 +127,8 @@ export default function () {
125
127
  const values = extractDfns({
126
128
  selector: ['dfn[data-dfn-type=value][data-dfn-for]:not([data-dfn-for=""])',
127
129
  'dfn[data-dfn-type=function][data-dfn-for]:not([data-dfn-for=""])',
128
- 'dfn[data-dfn-type=type][data-dfn-for]:not([data-dfn-for=""])'
130
+ 'dfn[data-dfn-type=type][data-dfn-for]:not([data-dfn-for=""])',
131
+ 'dfn[data-dfn-type=selector][data-dfn-for]:not([data-dfn-for=""])'
129
132
  ].join(','),
130
133
  extractor: extractTypedDfn,
131
134
  duplicates: 'push',
@@ -541,6 +544,39 @@ const parseProductionRule = (rule, { res = [], pureSyntax = false }) => {
541
544
  };
542
545
 
543
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
+
544
580
  /**
545
581
  * Extract the given dfn
546
582
  */
@@ -550,6 +586,7 @@ const extractTypedDfn = dfn => {
550
586
  const dfnType = dfn.getAttribute('data-dfn-type');
551
587
  const dfnFor = dfn.getAttribute('data-dfn-for');
552
588
  const parent = dfn.parentNode.cloneNode(true);
589
+ const fnRegExp = /^([a-zA-Z_][a-zA-Z0-9_\-]+)\([^\)]+\)$/;
553
590
 
554
591
  // Remove note references as in:
555
592
  // https://drafts.csswg.org/css-syntax-3/#the-anb-type
@@ -571,8 +608,17 @@ const extractTypedDfn = dfn => {
571
608
  // Don't attempt to parse pre tags at this stage, they are tricky to
572
609
  // split, we'll parse them as text and map them to the right definitions
573
610
  // afterwards.
574
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
575
- 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
+ }
576
622
  }
577
623
  else if (prod) {
578
624
  res = parseProductionRule(prod, { pureSyntax: true });
@@ -582,13 +628,12 @@ const extractTypedDfn = dfn => {
582
628
  // https://drafts.csswg.org/css-speech-1/#typedef-voice-volume-decibel
583
629
  // It may be worth checking but not an error per se.
584
630
  console.warn('[reffy]', `Found "=" next to definition of ${dfn.textContent.trim()} but no production rule. Did I miss something?`);
585
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
586
- res = { name, prose: text.replace(/\s+/g, ' ') };
631
+ res = { name: getDfnName(dfn), prose: text.replace(/\s+/g, ' ') };
587
632
  }
588
633
  }
589
- else if (dfn.textContent.trim().match(/^[a-zA-Z_][a-zA-Z0-9_\-]+\([^\)]+\)$/)) {
634
+ else if (dfn.textContent.trim().match(fnRegExp)) {
590
635
  // Definition is "prod(foo bar)", create a "prod() = prod(foo bar)" entry
591
- const fn = dfn.textContent.trim().match(/^([a-zA-Z_][a-zA-Z0-9_\-]+)\([^\)]+\)$/)[1];
636
+ const fn = dfn.textContent.trim().match(fnRegExp)[1];
592
637
  res = parseProductionRule(`${fn}() = ${dfn.textContent.trim()}`, { pureSyntax: false });
593
638
  }
594
639
  else if (parent.nodeName === 'DT') {
@@ -622,20 +667,23 @@ const extractTypedDfn = dfn => {
622
667
  }
623
668
  });
624
669
 
625
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
626
- res = { name, prose: dd.textContent.trim().replace(/\s+/g, ' ') };
670
+ res = {
671
+ name: getDfnName(dfn),
672
+ prose: dd.textContent.trim().replace(/\s+/g, ' ')
673
+ };
627
674
  }
628
675
  }
629
676
  else if (parent.nodeName === 'P') {
630
677
  // Definition is in regular prose, extract value from prose.
631
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
632
- res = { name, prose: parent.textContent.trim().replace(/\s+/g, ' ') };
678
+ res = {
679
+ name: getDfnName(dfn),
680
+ prose: parent.textContent.trim().replace(/\s+/g, ' ')
681
+ };
633
682
  }
634
683
  else {
635
684
  // Definition is in a heading or a more complex structure, just list the
636
685
  // name for now.
637
- const name = (dfn.getAttribute('data-lt') ?? dfn.textContent).trim();
638
- res = { name };
686
+ res = { name: getDfnName(dfn) };
639
687
  }
640
688
 
641
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() {