Skip to content

Commit

Permalink
fix(inlineStyles): refactor how styles are collected (#1865)
Browse files Browse the repository at this point in the history
  • Loading branch information
SethFalco authored Dec 1, 2023
1 parent 5144936 commit 39e855b
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 41 deletions.
52 changes: 52 additions & 0 deletions plugins/_collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -2237,3 +2237,55 @@ exports.colorsProps = [
'flood-color',
'lighting-color',
];

/** @see https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes */
exports.pseudoClasses = {
displayState: ['fullscreen', 'modal', 'picture-in-picture'],
input: [
'autofill',
'enabled',
'disabled',
'read-only',
'read-write',
'placeholder-shown',
'default',
'checked',
'indetermined',
'blank',
'valid',
'invalid',
'in-range',
'out-of-range',
'required',
'optional',
'user-invalid',
],
linguistic: ['dir', 'lang'],
location: [
'any-link',
'link',
'visited',
'local-link',
'target',
'target-within',
'scope',
],
resourceState: ['playing', 'paused'],
timeDimensional: ['current', 'past', 'future'],
treeStructural: [
'root',
'empty',
'nth-child',
'nth-last-child',
'first-child',
'last-child',
'only-child',
'nth-of-type',
'nth-last-of-type',
'first-of-type',
'last-of-type',
'only-of-type',
],
userAction: ['hover', 'active', 'focus', 'focus-visible', 'focus-within'],
functional: ['is', 'not', 'where', 'has'],
};
101 changes: 60 additions & 41 deletions plugins/inlineStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,26 @@ const {
detachNodeFromParent,
} = require('../lib/xast.js');
const { compareSpecificity, includesAttrSelector } = require('../lib/style');
const { attrsGroups } = require('./_collections');
const { attrsGroups, pseudoClasses } = require('./_collections');

exports.name = 'inlineStyles';
exports.description = 'inline styles (additional options)';

/**
* Some pseudo-classes can only be calculated by clients, like :visited,
* :future, or :hover, but there are other pseudo-classes that we can evaluate
* during optimization.
*
* The list of pseudo-classes that we can evaluate during optimization, and so
* shouldn't be toggled conditionally through the `usePseudos` parameter.
*
* @see https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes
*/
const preservedPseudos = [
...pseudoClasses.functional,
...pseudoClasses.treeStructural,
];

/**
* Merges styles from style nodes into inline styles.
*
Expand Down Expand Up @@ -88,13 +103,9 @@ exports.fn = (root, params) => {

// collect selectors
csstree.walk(cssAst, {
visit: 'Selector',
enter(node, item) {
visit: 'Rule',
enter(node) {
const atrule = this.atrule;
const rule = this.rule;
if (rule == null) {
return;
}

// skip media queries not included into useMqs param
let mediaQuery = '';
Expand All @@ -108,44 +119,52 @@ exports.fn = (root, params) => {
return;
}

/**
* @type {Array<{
* item: csstree.ListItem<csstree.CssNode>,
* list: csstree.List<csstree.CssNode>
* }>}
*/
const pseudos = [];
if (node.type === 'Selector') {
node.children.forEach((childNode, childItem, childList) => {
if (
childNode.type === 'PseudoClassSelector' ||
childNode.type === 'PseudoElementSelector'
) {
pseudos.push({ item: childItem, list: childList });
}
});
}
if (node.prelude.type === 'SelectorList') {
node.prelude.children.forEach((childNode, item) => {
if (childNode.type === 'Selector') {
/**
* @type {Array<{
* item: csstree.ListItem<csstree.CssNode>,
* list: csstree.List<csstree.CssNode>
* }>}
*/
const pseudos = [];

childNode.children.forEach(
(grandchildNode, grandchildItem, grandchildList) => {
const isPseudo =
grandchildNode.type === 'PseudoClassSelector' ||
grandchildNode.type === 'PseudoElementSelector';

if (
isPseudo &&
!preservedPseudos.includes(grandchildNode.name)
) {
pseudos.push({
item: grandchildItem,
list: grandchildList,
});
}
}
);

// skip pseudo classes and pseudo elements not includes into usePseudos param
const pseudoSelectors = csstree.generate({
type: 'Selector',
children: new csstree.List().fromArray(
pseudos.map((pseudo) => pseudo.item.data)
),
});
const pseudoSelectors = csstree.generate({
type: 'Selector',
children: new csstree.List().fromArray(
pseudos.map((pseudo) => pseudo.item.data)
),
});

if (!usePseudos.includes(pseudoSelectors)) {
return;
}
if (usePseudos.includes(pseudoSelectors)) {
for (const pseudo of pseudos) {
pseudo.list.remove(pseudo.item);
}
}

// remove pseudo classes and elements to allow querySelector match elements
// TODO this is not very accurate since some pseudo classes like first-child
// are used for selection
for (const pseudo of pseudos) {
pseudo.list.remove(pseudo.item);
selectors.push({ node: childNode, rule: node, item: item });
}
});
}

selectors.push({ node, item, rule });
},
});
},
Expand Down
21 changes: 21 additions & 0 deletions test/plugins/inlineStyles.27.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions test/plugins/inlineStyles.28.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 39e855b

Please sign in to comment.