From 7a0e488cc42fbe440c1fe2e3758899302b7b7105 Mon Sep 17 00:00:00 2001 From: Francois Daoust Date: Fri, 13 Jun 2025 06:59:27 +0200 Subject: [PATCH 1/2] Add `index()` function to CSS package The function creates an indexed structure more aligned with MDN data. The README documentation was also updated to detail remaining structural nuances, and more importantly differences in content between the two projects. --- packages/css/CHANGELOG.md | 8 +++--- packages/css/README.md | 55 ++++++++++++++++++++++++++++----------- packages/css/index.js | 25 +++++++++++++++++- test/css/consolidated.js | 17 ++++++++++++ 4 files changed, 86 insertions(+), 19 deletions(-) diff --git a/packages/css/CHANGELOG.md b/packages/css/CHANGELOG.md index 5841b98db8b4..56b0b6c55a7c 100644 --- a/packages/css/CHANGELOG.md +++ b/packages/css/CHANGELOG.md @@ -7,7 +7,7 @@ Webref adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), applied to data. A new major version is released whenever a breaking change is made to the data structure. -## v7.0.0-alpha - 2025-06-10 +## v7.0.x - 2025-TBD CSS extracts are now consolidated into a single file. @@ -19,11 +19,13 @@ CSS extracts are now consolidated into a single file. In version 6, `listAll()` resolved with an object where the keys were spec shortnames, and the values mostly matched the structure of the consolidated object that the function now returns. On top of the consolidation itself, main differences are: - Functions and types were merged in a `values` category in version 6, they now appear in separate `functions` and `types` categories. -- Functions and types that were scoped to another CSS feature appeared nested under that CSS feature in version 6. They now appear directly under `functions` and `types` with a `for` key that contains the name of the scoping feature. +- Functions and types that were scoped to another CSS feature appeared nested under that CSS feature in version 6. They now appear directly under `functions` and `types` with a `for` key that contains the list of scoping features for that feature. A scoping feature may be a property, a function or a type. When the scoping feature is a type, its name in the `for` key is enclosed between `<` and `>`. +- Feature syntaxes were stored in a `value` key. That key is now named `syntax`. Actual syntax values are the same as before and can be parsed with CSSTree. - The shortname of the spec (or specs) that defines a feature is no longer readily available (but note the `href` key targets the defining spec). If you need the specs' shortnames, please [raise an issue](https://github.com/w3c/webref/issues) to describe your use case. - The consolidation removes duplicates, merging extended definitions into a single feature. The definition from the latest spec level is used when a feature is defined in more than one level. If you need the definition from earlier levels, please [raise an issue](https://github.com/w3c/webref/issues) to describe your needs! -- Some of the possible values that a CSS feature could take appeared nested under that feature definition in a `values` key in version 6. Such values are no longer reported in the new version: they were confusing in any case because they did not cover the whole range of values that a feature could take, and could contain values that were not atomic keyword values. Values could be re-introduced when CSS specs are more systematic about them. In the meantime, you will need to parse the feature's syntax (the `value` key) to extract keyword values. +- Some of the possible values that a CSS feature could take appeared nested under that feature definition in a `values` key in version 6. Such values are no longer reported in the new version: they were confusing in any case because they did not cover the whole range of values that a feature could take, and could contain values that were not atomic keyword values. Values could be re-introduced when CSS specs are more systematic about them. In the meantime, you will need to parse the feature's syntax (the `syntax` key) to extract keyword values. +Additionally, the package now also exposes a new `index()` async method that resolves with an object similar to that returned by `listAll()`, except that lists of features under each category are indexed by feature names. For scoped features, the feature name is used as identifier when it is unambiguous. If a feature is defined differently for different scopes, the feature name is completed with the name of the first scoping feature to disambiguate. For example, `type() for @function`, `type() for attr()`. ## v6.0.0 - 2022-11-28 diff --git a/packages/css/README.md b/packages/css/README.md index 2ad74381d53a..ee9bae245ae6 100644 --- a/packages/css/README.md +++ b/packages/css/README.md @@ -6,7 +6,11 @@ This package contains a consolidated list of CSS features defined across specs, # API -The async `listAll()` method resolves with an object that contains lists of features grouped by feature type: `atrules`, `functions`, `properties`, `selectors` and `types`. Example: +Two async methods are exposed: `listAll()` and `index()`. They both resolve with an object that lists CSS features by feature type: `atrules`, `functions`, `properties`, `selectors` and `types`. + +The difference between the two functions is that, under each feature type, `listAll()` returns an array of features, whereas `index()` returns an object indexed by feature name to ease direct lookups (and [transition from `mdn/data`](migrating from , see below). Both functions return the same features. + +Example using `listAll()`: ```js const css = require('@webref/css'); @@ -18,43 +22,64 @@ for (const feature of functions) { } ``` +Example using `index()`: + +```js +const css = require('@webref/css'); + +const { atrules, functions, properties, selectors, types } = await css.index(); + +// Do something with the object that describes the abs() function +const abs = functions['abs()']; +``` + Each CSS feature is described by: - a `name` key that contains the name of the feature - an `href` key that contains the URL (with a fragment) of the CSS spec that defines the feature -Many CSS features also have a `value` key that describes the syntax of the feature, as defined in the spec. This syntax can be parsed with the [CSSTree Value Definition Syntax parser](https://github.com/csstree/csstree/blob/master/docs/definition-syntax.md#value-definition-syntax). Example: +Many CSS features also have a `syntax` key that describes the syntax of the feature, as defined in the spec. This syntax can be parsed with the [CSSTree Value Definition Syntax parser](https://github.com/csstree/csstree/blob/master/docs/definition-syntax.md#value-definition-syntax). Example: ```js const css = require('@webref/css'); const { definitionSyntax } = require('css-tree'); -const { properties } = await css.listAll(); -for (const property of properties) { - if (!property.value) { - continue; - } - const ast = definitionSyntax.parse(property.value); - // do something with the abstract syntax tree -} +const { properties } = await css.index(); +const ast = definitionSyntax.parse(properties['flex'].syntax); +// do something with the abstract syntax tree ``` Additional keys may be set depending on the type of the CSS feature. For example: - At-rules have a `descriptors` key that contains the list of descriptors defined for the given at-rule. -- Functions and types that are scoped to a property or other feature have a `for` key that contains the name of the scoping property or other feature. +- Functions and types that are scoped to a property or other feature have a `for` key that contains the list of scoping features for that feature. A scoping feature may be a property, a function or a type. When the scoping feature is a type, its name in the `for` key is enclosed between `<` and `>`. - Properties have a `styleDeclaration` key that contains the list of IDL attribute names that the property generates. A number of other keys may be set to describe the property's initial value, animation type and other parameters. Additional notes: -- Type names are enclosed in `<>`. For example: ``. +- Features returned by `index()` are indexed by their feature name, but there are exceptional cases where a given feature is defined differently (with a different syntax) for different scopes. In such cases the feature is indexed by its name completed with ` for ` and the name of the first scoping feature to which the definition applies. For example, `type() for @function`, `type() for attr()`. As of June 2025, this affects a handful of functions and types. - When a feature is defined across different levels in the same spec series, the definition from the latest level is used. -- When a property is extended with new values in different specs, `href` links to the base definition and `value` is the union (using `|`) of the syntaxes of the base and extended definitions. +- When a property is extended with new values in different specs, `href` links to the base definition and `syntax` is the union (using `|`) of the syntaxes of the base and extended definitions. - When new descriptors are defined for an at-rule in different specs, `descriptors` contains the merged list of known descriptors. -- When specs define the syntax of an at-rule in terms of `` or ``, the `value` key contains an "expanded" syntax that leverages the syntax of the at-rule's descriptors. +- When specs define the syntax of an at-rule in terms of `` or ``, the `syntax` key contains an "expanded" syntax that leverages the syntax of the at-rule's descriptors. + +## Migrating from `mdn/data` + +The structure of the consolidated list of CSS features aligns with CSS data in [`mdn/data`](https://github.com/mdn/data). If you use the `index()` method, the `@webref/css` package can *almost* serve as a drop-in replacement for the `mdn/data` package **for syntaxes**, with the following structural nuances: + +- `mdn/data` reports at-rules under an `atRules` key. `@webref/css` reports them under an `atrules` key. +- `mdn/data` stores syntaxes of functions and types under a separate `syntaxes` key. `@webref/css` stores them in their feature definition. If you need to reproduce the `syntaxes` list, merge the entries under `functions` and `types`. +- `mdn/data` assumes all features are unscoped. `@webref/css` reports scopes where needed in a `for` key. Note that a few functions have a different syntax depending on the scope under which they are used. +- `@webref/css` does not contain information about CSS units. + +Beyond structural nuances, differences in content may also affect transition: +- `mdn/data` contains deprecated features, proprietary features or feature variants that are not defined in specs. `@webref/css` only contains features defined in specs. +- `mdn/data` describes features as implemented in main browsers. `@webref/css` "lives on the edge", listing features and syntaxes from latest levels of CSS specs, regardless of the spec's maturity level and support across browsers. +- `mdn/data` turns specs prose into syntax when needed, `@webref/css` only has syntaxes that are explicitly defined in specs. It should be possible to improve the specs, consider submitting missing syntax reports (against the specs themselves if possible, in [Webref](https://github.com/w3c/webref/issues) otherwise). + # Guarantees The following guarantees are provided by this package: -- All syntax values (the `value` keys) can be parsed by the version of [CSSTree](https://github.com/csstree/csstree) set in `peerDependencies` in `package.json`. +- All syntax values (the `syntax` keys) can be parsed by the version of [CSSTree](https://github.com/csstree/csstree) set in `peerDependencies` in `package.json`. - Feature names (the `name` keys) are unique per type provided that the `for` key is also taken into account for functions and types. - All features have an `href` key that targets the CSS spec that defines the feature. When the feature is extended across CSS specs, this URL targets the base definition. diff --git a/packages/css/index.js b/packages/css/index.js index ee53d6c6bb70..2e31b5e6b70c 100644 --- a/packages/css/index.js +++ b/packages/css/index.js @@ -6,4 +6,27 @@ async function listAll({folder = __dirname} = {}) { return JSON.parse(json); } -module.exports = {listAll}; +async function index({folder = __dirname} = {}) { + const nonIndexed = await listAll(folder); + const indexed = {}; + for (const [category, features] of Object.entries(nonIndexed)) { + indexed[category] = {}; + for (const feature of features) { + // A handful of features have different definitions for different scopes. + // When that happens, the feature identifier needs to be disambiguated. + let id = feature.name; + let dupl = null; + if (feature.for) { + dupl = features.find(f => f !== feature && f.name === feature.name); + } + if (dupl) { + // Note: scopes of different definitions are necessarily disjoint + id += ' for ' + feature.for[0]; + } + indexed[category][id] = feature; + } + } + return indexed; +} + +module.exports = {listAll, index}; diff --git a/test/css/consolidated.js b/test/css/consolidated.js index 528e3bb1d24b..f712ddf1581b 100644 --- a/test/css/consolidated.js +++ b/test/css/consolidated.js @@ -68,4 +68,21 @@ describe(`The consolidated CSS file`, async () => { assert.deepEqual(invalid, []); }); } + + it('can be indexed', async () => { + const indexed = await css.index({ folder: curatedFolder }); + for (const [category, label] of Object.entries(categories)) { + assert(indexed[category], `No ${pluralize(label)} in index`); + assert.equal( + Object.keys(indexed[category]).length, + consolidated[category].length, + `Not the right amount of ${pluralize(label)} in index`); + } + + assert(indexed.atrules['@import'], `No flex property in index`); + assert(indexed.functions['abs()'], `No abs() function in index`); + assert(indexed.properties['flex'], `No flex property in index`); + assert(indexed.selectors[':first-child'], `No :first-child selector in index`); + assert(indexed.types['string'], `No string type in index`); + }); }); From cf892ff749223e469b32e14444ee6e02f7a6f40b Mon Sep 17 00:00:00 2001 From: Francois Daoust Date: Tue, 17 Jun 2025 11:21:18 +0200 Subject: [PATCH 2/2] Fix broken link to transition from mdn/data section --- packages/css/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/css/README.md b/packages/css/README.md index ee9bae245ae6..800161503b14 100644 --- a/packages/css/README.md +++ b/packages/css/README.md @@ -8,7 +8,7 @@ This package contains a consolidated list of CSS features defined across specs, Two async methods are exposed: `listAll()` and `index()`. They both resolve with an object that lists CSS features by feature type: `atrules`, `functions`, `properties`, `selectors` and `types`. -The difference between the two functions is that, under each feature type, `listAll()` returns an array of features, whereas `index()` returns an object indexed by feature name to ease direct lookups (and [transition from `mdn/data`](migrating from , see below). Both functions return the same features. +The difference between the two functions is that, under each feature type, `listAll()` returns an array of features, whereas `index()` returns an object indexed by feature name to ease direct lookups (and [transition from `mdn/data`](#transition-from-mdndata). Both functions return the same features. Example using `listAll()`: