reffy 18.8.2 → 19.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reffy",
3
- "version": "18.8.2",
3
+ "version": "19.0.1",
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",
@@ -37,15 +37,15 @@
37
37
  "ajv-formats": "3.0.1",
38
38
  "commander": "14.0.0",
39
39
  "fetch-filecache-for-crawling": "5.1.1",
40
- "puppeteer": "24.10.0",
40
+ "puppeteer": "24.10.1",
41
41
  "semver": "^7.3.5",
42
42
  "web-specs": "3.53.0",
43
43
  "webidl2": "24.4.1"
44
44
  },
45
45
  "devDependencies": {
46
- "respec": "35.4.0",
46
+ "respec": "35.4.1",
47
47
  "respec-hljs": "2.1.1",
48
- "rollup": "4.42.0",
48
+ "rollup": "4.43.0",
49
49
  "undici": "^7.0.0"
50
50
  },
51
51
  "overrides": {
@@ -2,6 +2,16 @@
2
2
  "$schema": "http://json-schema.org/schema#",
3
3
  "$id": "https://github.com/w3c/reffy/blob/main/schemas/postprocessing/css.json",
4
4
 
5
+ "$defs": {
6
+ "scopes": {
7
+ "type": "array",
8
+ "items": {
9
+ "type": "string"
10
+ },
11
+ "minItems": 1
12
+ }
13
+ },
14
+
5
15
  "type": "object",
6
16
  "additionalProperties": false,
7
17
  "required": ["atrules", "functions", "properties", "selectors", "types"],
@@ -15,7 +25,7 @@
15
25
  "properties": {
16
26
  "name": { "type": "string", "pattern": "^@" },
17
27
  "href": { "$ref": "../common.json#/$defs/url" },
18
- "value": { "$ref": "../common.json#/$defs/cssValue" },
28
+ "syntax": { "$ref": "../common.json#/$defs/cssValue" },
19
29
  "prose": { "type": "string" },
20
30
  "descriptors": {
21
31
  "type": "array",
@@ -27,7 +37,7 @@
27
37
  "name": { "type": "string" },
28
38
  "for": { "type": "string" },
29
39
  "href": { "$ref": "../common.json#/$defs/url" },
30
- "value": { "$ref": "../common.json#/$defs/cssValue" }
40
+ "syntax": { "$ref": "../common.json#/$defs/cssValue" }
31
41
  }
32
42
  }
33
43
  }
@@ -41,12 +51,12 @@
41
51
  "required": ["name", "type"],
42
52
  "additionalProperties": false,
43
53
  "properties": {
44
- "name": { "type": "string", "pattern": "^<[^>]+>$|^.*()$" },
45
- "for": { "type": "string" },
54
+ "name": { "type": "string", "pattern": "^.*()$" },
55
+ "for": { "$ref": "#/$defs/scopes" },
46
56
  "href": { "$ref": "../common.json#/$defs/url" },
47
57
  "type": { "type": "string", "enum": ["function"] },
48
58
  "prose": { "type": "string" },
49
- "value": { "$ref": "../common.json#/$defs/cssValue" }
59
+ "syntax": { "$ref": "../common.json#/$defs/cssValue" }
50
60
  }
51
61
  }
52
62
  },
@@ -59,7 +69,7 @@
59
69
  "properties": {
60
70
  "name": { "$ref": "../common.json#/$defs/cssPropertyName" },
61
71
  "href": { "$ref": "../common.json#/$defs/url" },
62
- "value": { "$ref": "../common.json#/$defs/cssValue" },
72
+ "syntax": { "$ref": "../common.json#/$defs/cssValue" },
63
73
  "legacyAliasOf": { "$ref": "../common.json#/$defs/cssPropertyName" },
64
74
  "styleDeclaration": {
65
75
  "type": "array",
@@ -79,7 +89,7 @@
79
89
  "name": { "$ref": "../common.json#/$defs/cssPropertyName" },
80
90
  "href": { "$ref": "../common.json#/$defs/url" },
81
91
  "prose": { "type": "string" },
82
- "value": { "$ref": "../common.json#/$defs/cssValue" }
92
+ "syntax": { "$ref": "../common.json#/$defs/cssValue" }
83
93
  }
84
94
  }
85
95
  },
@@ -90,12 +100,12 @@
90
100
  "required": ["name", "type"],
91
101
  "additionalProperties": false,
92
102
  "properties": {
93
- "name": { "type": "string", "pattern": "^<[^>]+>$|^.*()$" },
94
- "for": { "type": "string" },
103
+ "name": { "type": "string", "pattern": "^[a-zA-Z0-9-\\+\\(\\)\\[\\]\\{\\}]+$" },
104
+ "for": { "$ref": "#/$defs/scopes" },
95
105
  "href": { "$ref": "../common.json#/$defs/url" },
96
106
  "type": { "type": "string", "enum": ["type"] },
97
107
  "prose": { "type": "string" },
98
- "value": { "$ref": "../common.json#/$defs/cssValue" }
108
+ "syntax": { "$ref": "../common.json#/$defs/cssValue" }
99
109
  }
100
110
  }
101
111
  }
@@ -13,7 +13,7 @@
13
13
  * In CSS extracts, functions and types that are defined for another construct
14
14
  * appear under the `values` key of that construct entry. In the resulting
15
15
  * construct, they get copied to the root lists under `functions` or `types`,
16
- * and get a `for` key that contains the name of the construct that they are
16
+ * and get a `for` key that contains the list of constructs that they are
17
17
  * defined for.
18
18
  *
19
19
  * CSS properties that are defined in one spec and extended in other specs get
@@ -51,12 +51,10 @@
51
51
  * - This code uses arrays for lists, MDN data uses indexed objects.
52
52
  * - This code lists scoped definitions with a `for` key. MDN data only has
53
53
  * unscoped definitions.
54
- * - This code stores syntaxes in a `value` key, MDN data uses a `syntax` key.
55
54
  * - This code stores syntaxes of functions and types directly in the
56
55
  * `functions` and `types` lists. MDN data stores them in a separate `syntaxes`
57
56
  * category. The `syntaxes` view can be built by merging the `functions` and
58
57
  * `types` lists.
59
- * - This code keeps the surrounding `<>` for type names, MDN data does not.
60
58
  *
61
59
  * Module runs at the crawl level to create a `css.json` file.
62
60
  */
@@ -158,14 +156,27 @@ export default {
158
156
  // ... and since we're looping through features, let's get rid
159
157
  // of inner value definitions, which we no longer need
160
158
  // (interesting ones were already copied to the root level)
159
+ // Let's also turn `value` keys into `syntax` keys because that's
160
+ // a better name and that matches what MDN data uses, and drop
161
+ // enclosing `<` and `>` for type names (type names that look like
162
+ // functions should be .
161
163
  if (feature.values) {
162
164
  delete feature.values;
163
165
  }
166
+ if (feature.value) {
167
+ feature.syntax = feature.value;
168
+ delete feature.value;
169
+ }
164
170
  for (const descriptor of feature.descriptors ?? []) {
165
171
  if (descriptor.values) {
166
172
  delete descriptor.values;
167
173
  }
174
+ if (descriptor.value) {
175
+ descriptor.syntax = descriptor.value;
176
+ delete descriptor.value;
177
+ }
168
178
  }
179
+ feature.name = unwrapName(feature.name);
169
180
 
170
181
  const featureId = getFeatureId(feature);
171
182
  if (!featureDfns[featureId]) {
@@ -178,10 +189,10 @@ export default {
178
189
  // (that has some known syntax) in the most recent level. Move that base
179
190
  // definition to the beginning of the array and get rid of other base
180
191
  // definitions.
181
- // (Note: the code throws an error if duplicates of base definitions in
182
- // unrelated specs still exist)
192
+ // (Note: the code chooses one definition if duplicates of base
193
+ // definitions in unrelated specs still exist)
183
194
  for (const [name, dfns] of Object.entries(featureDfns)) {
184
- let actualDfns = dfns.filter(dfn => dfn.value);
195
+ let actualDfns = dfns.filter(dfn => dfn.syntax);
185
196
  if (actualDfns.length === 0) {
186
197
  actualDfns = dfns.filter(dfn => !dfn.newValues);
187
198
  }
@@ -214,7 +225,7 @@ export default {
214
225
  if (dfn === baseDfn) {
215
226
  continue;
216
227
  }
217
- if (baseDfn.value && dfn.newValues) {
228
+ if (baseDfn.syntax && dfn.newValues) {
218
229
  const newerDfn = dfns.find(d =>
219
230
  d !== dfn &&
220
231
  d.newValues === dfn.newValues &&
@@ -224,7 +235,7 @@ export default {
224
235
  // the older one
225
236
  continue;
226
237
  }
227
- baseDfn.value += ' | ' + dfn.newValues;
238
+ baseDfn.syntax += ' | ' + dfn.newValues;
228
239
  }
229
240
  if (baseDfn.descriptors && dfn.descriptors?.length > 0) {
230
241
  baseDfn.descriptors.push(...dfn.descriptors.filter(desc => {
@@ -253,28 +264,28 @@ export default {
253
264
  if (unscoped) {
254
265
  // Only keep the scoped feature if it has a known syntax that
255
266
  // differs from the unscoped feature
256
- return feature.value && feature.value !== unscoped.value;
267
+ return feature.syntax && feature.syntax !== unscoped.syntax;
257
268
  }
258
269
  }
259
270
  return true;
260
271
  })
261
272
  .map(feature => {
262
273
  if (feature.descriptors?.length > 0 &&
263
- feature.value?.match(/{ <declaration-(rule-)?list> }/)) {
274
+ feature.syntax?.match(/{ <declaration-(rule-)?list> }/)) {
264
275
  // Note: More advanced logic would allow to get rid of enclosing
265
276
  // grouping constructs when there's no ambiguity. We'll stick to
266
277
  // simple logic for now.
267
278
  const syntax = feature.descriptors
268
279
  .map(desc => {
269
280
  if (desc.name.startsWith('@')) {
270
- return `[ ${desc.value} ]`;
281
+ return `[ ${desc.syntax} ]`;
271
282
  }
272
283
  else {
273
- return `[ ${desc.name}: [ ${desc.value} ]; ]`;
284
+ return `[ ${desc.name}: [ ${desc.syntax} ]; ]`;
274
285
  }
275
286
  })
276
287
  .join(' ||\n ');
277
- feature.value = feature.value.replace(
288
+ feature.syntax = feature.syntax.replace(
278
289
  /{ <declaration-(rule-)?list> }/,
279
290
  '{\n ' + syntax + '\n}');
280
291
  }
@@ -286,16 +297,42 @@ export default {
286
297
  // Various CSS properties are "legacy aliases of" another property. Use the
287
298
  // syntax of the other property for these.
288
299
  for (const feature of categorized[category]) {
289
- if (feature.legacyAliasOf && !feature.value) {
300
+ if (feature.legacyAliasOf && !feature.syntax) {
290
301
  const target = categorized[category].find(f =>
291
302
  f.name === feature.legacyAliasOf && !f.for);
292
303
  if (!target) {
293
304
  throw new Error(`${feature.name} is a legacy alias of unknown ${f.legacyAliasOf}`);
294
305
  }
295
- feature.value = target.value;
306
+ feature.syntax = target.syntax;
296
307
  }
297
308
  }
298
309
 
310
+ // The same feature may be defined for multiple scopes.
311
+ // To ease indexing and lookup by feature name, let's merge the scopes
312
+ // when possible, turning the `for` key into an array. This will not get
313
+ // rid of all scoping duplicates, but should still make the feature name
314
+ // unique for all but a handful of them.
315
+ categorized[category] = categorized[category]
316
+ .map((feature, idx, list) => {
317
+ const first = list.find(f => f.href === feature.href);
318
+ if (first === feature) {
319
+ if (feature.for) {
320
+ feature.for = [feature.for];
321
+ }
322
+ return feature;
323
+ }
324
+ // Not the first time we see this feature, let's merge scopes.
325
+ // Both scopes should be defined as there is no way to author a
326
+ // single dfn that defines a feature both as scoped and unscoped.
327
+ if (!first.for || !feature.for) {
328
+ throw new Error(`Feature ${feature.name} defined both as unscoped and scoped within the same dfn, see ${feature.href}`);
329
+ }
330
+ first.for.push(feature.for);
331
+ first.for.sort();
332
+ return null;
333
+ })
334
+ .filter(feature => !!feature);
335
+
299
336
  // Let's sort lists before we return to ease human-readability and
300
337
  // avoid non-substantive diff
301
338
  for (const feature of categorized[category]) {
@@ -313,13 +350,14 @@ export default {
313
350
 
314
351
 
315
352
  /**
316
- * Return the identifier of a feature, taking scoping construct into account
353
+ * Return the identifier of a feature, taking scoping construct(s) into account
317
354
  * when needed.
318
355
  */
319
356
  function getFeatureId(feature) {
320
357
  let featureId = feature.name;
321
358
  if (feature.for) {
322
- featureId += ' for ' + feature.for;
359
+ featureId += ' for ' +
360
+ (Array.isArray(feature.for) ? feature.for.join(',') : feature.for);
323
361
  }
324
362
  return featureId;
325
363
  }
@@ -338,3 +376,18 @@ function decorateFeaturesWithSpec(data, spec) {
338
376
  }
339
377
  }
340
378
  }
379
+
380
+
381
+ /**
382
+ * Unwrap (type) name
383
+ *
384
+ * Note: names that appear in other categories are never enclosed in `<`
385
+ * and `>`
386
+ */
387
+ function unwrapName(name) {
388
+ const typeMatch = name.match(/^<([^>]+)>$/);
389
+ if (typeMatch) {
390
+ return typeMatch[1];
391
+ }
392
+ return name;
393
+ }