Skip to content

Commit

Permalink
Check for references in <style>.
Browse files Browse the repository at this point in the history
  • Loading branch information
johnkenny54 committed Nov 15, 2024
1 parent 846229c commit 15ba73d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 9 deletions.
39 changes: 39 additions & 0 deletions lib/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ export class CSSRuleSet {
return false;
}

/**
* @param {string} id
* @returns {boolean}
*/
hasIdSelector(id) {
for (const rule of this.#rules) {
if (rule.hasIdSelector(id)) {
return true;
}
}
return false;
}

#initFeatures() {
const features = new Set();
if (this.#atRule) {
Expand Down Expand Up @@ -203,6 +216,13 @@ export class CSSRule {
return this.#selector.hasAttributeSelector(attName);
}

/**
* @param {string} id
*/
hasIdSelector(id) {
return this.#selector.hasIdSelector(id);
}

hasPseudos() {
return this.#selector.hasPseudos();
}
Expand Down Expand Up @@ -350,6 +370,13 @@ export class CSSSelector {
return this.#selectorSequences.some((s) => s.hasAttributeSelector(attName));
}

/**
* @param {string} id
*/
hasIdSelector(id) {
return this.#selectorSequences.some((s) => s.hasIdSelector(id));
}

hasPseudos() {
return this.#strWithoutPseudos !== undefined;
}
Expand Down Expand Up @@ -483,6 +510,18 @@ export class CSSSelectorSequence {
return false;
}

/**
* @param {string} id
*/
hasIdSelector(id) {
for (const selector of this.#simpleSelectors) {
if (selector.type === 'IdSelector') {
return selector.name === id;
}
}
return false;
}

/**
* @param {Map<string,string>} idMap
*/
Expand Down
30 changes: 21 additions & 9 deletions lib/docdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { CSSParseError } from './css.js';
import { cssPropToString, getStyleDeclarations } from './css-tools.js';

/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastParent} XastParent
* @typedef {import('../lib/types.js').XastRoot} XastRoot
*/
Expand All @@ -36,7 +35,7 @@ export class StyleData {
#referencedClasses;

/**
* @param {XastElement[]} styleElements
* @param {import('../lib/types.js').XastElement[]} styleElements
* @param {CSSRuleSet[]} ruleSets
*/
constructor(styleElements, ruleSets) {
Expand Down Expand Up @@ -178,7 +177,7 @@ export class StyleData {
}

/**
* @param {XastElement} element
* @param {import('../lib/types.js').XastElement} element
* @param {{element:XastParent,styles?:Map<string,string|null>}[]} parentInfo
* @param {CSSDeclarationMap} [declarations]
* @returns {Map<string,string|null>}
Expand Down Expand Up @@ -245,7 +244,7 @@ export class StyleData {
}

/**
* @param {XastElement} element
* @param {import('../lib/types.js').XastElement} element
*/
getMatchingRules(element) {
const rules = [];
Expand Down Expand Up @@ -306,6 +305,19 @@ export class StyleData {
return this.getReferencedClasses().has(className);
}

/**
* @param {string} id
* @returns {boolean}
*/
hasIdSelector(id) {
for (const ruleSet of this.#ruleSets) {
if (ruleSet.hasIdSelector(id)) {
return true;
}
}
return false;
}

/**
* @param {CSSFeatures[]} features
*/
Expand Down Expand Up @@ -333,7 +345,7 @@ export class StyleData {

mergeStyles() {
/**
* @param {XastElement} element
* @param {import('../lib/types.js').XastElement} element
*/
function gatherCSS(element) {
let css = getCSS(element);
Expand Down Expand Up @@ -486,7 +498,7 @@ class DocData {
}

/**
* @param {XastElement} styleElement
* @param {import('../lib/types.js').XastElement} styleElement
*/
function getCSS(styleElement) {
let css = '';
Expand All @@ -499,7 +511,7 @@ function getCSS(styleElement) {
}

/**
* @param {XastElement} styleElement
* @param {import('../lib/types.js').XastElement} styleElement
*/
function getRuleSets(styleElement) {
/** @type {CSSRuleSet[]} */
Expand Down Expand Up @@ -537,11 +549,11 @@ function getRuleSets(styleElement) {
* @param {XastRoot} root
*/
export const getDocData = (root) => {
/** @type {XastElement[]} */
/** @type {import('../lib/types.js').XastElement[]} */
const styleElements = [];
/** @type {CSSRuleSet[]} */
const ruleSets = [];
/** @type {Map<XastElement, XastParent>} */
/** @type {Map<import('../lib/types.js').XastElement, XastParent>} */
const parents = new Map();
let styleError = false;
let hasAnimations = false;
Expand Down
1 change: 1 addition & 0 deletions lib/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class StyleData {
getReferencedIds(): Map<string, CSSRule[]>;
hasAttributeSelector(attName?: string): boolean;
hasClassReference(className: string): boolean;
hasIdSelector(id: string): boolean;
hasOnlyFeatures(features: CSSFeatures[]): boolean;
hasStyles(): boolean;
mergeStyles(): void;
Expand Down
8 changes: 8 additions & 0 deletions plugins/mergeGradients.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export const fn = (root, params, info) => {
return;
}

if (styleData.hasIdSelector(gradientId)) {
// Don't merge gradients that are referenced by a selector.
return;
}

const key = getGradientKey(element);
if (!key) {
return;
Expand Down Expand Up @@ -87,6 +92,9 @@ export const fn = (root, params, info) => {
}
}

// Update any ids referenced by <style> properties.
styleData.updateReferencedIds(styleData.getReferencedIds(), idMap);

// Delete merged nodes.
deleteChildren(childrenToDelete);
},
Expand Down
39 changes: 39 additions & 0 deletions test/plugins/mergeGradients.02.svg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Don't merge gradients that are referenced in a <style> selector.

===

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
<style>
#b stop {stop-opacity:.5}
</style>
<defs>
<linearGradient id="a">
<stop style="stop-color:#aaa"/>
<stop style="stop-color:#aaa;stop-opacity:0" offset="1"/>
</linearGradient>
<linearGradient id="b">
<stop style="stop-color:#aaa"/>
<stop style="stop-color:#aaa;stop-opacity:0" offset="1"/>
</linearGradient>
</defs>
<rect width="20" height="10" fill="url(#a)"/>
<rect y="15" width="20" height="10" fill="url(#b)"/>
</svg>

@@@

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
<style>#b stop{stop-opacity:.5}</style>
<defs>
<linearGradient id="a">
<stop style="stop-color:#aaa"/>
<stop style="stop-color:#aaa;stop-opacity:0" offset="1"/>
</linearGradient>
<linearGradient id="b">
<stop style="stop-color:#aaa"/>
<stop style="stop-color:#aaa;stop-opacity:0" offset="1"/>
</linearGradient>
</defs>
<rect width="20" height="10" fill="url(#a)"/>
<rect y="15" width="20" height="10" fill="url(#b)"/>
</svg>
33 changes: 33 additions & 0 deletions test/plugins/mergeGradients.03.svg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Merge gradients that are referenced in a <style> property.

===

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
<style>#c{fill:url(#b)}</style>
<defs>
<linearGradient id="a">
<stop style="stop-color:#aaa"/>
<stop style="stop-color:#aaa;stop-opacity:0" offset="1"/>
</linearGradient>
<linearGradient id="b">
<stop style="stop-color:#aaa"/>
<stop style="stop-color:#aaa;stop-opacity:0" offset="1"/>
</linearGradient>
</defs>
<rect width="20" height="10" fill="url(#a)"/>
<rect id="c" y="15" width="20" height="10"/>
</svg>

@@@

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
<style>#c{fill:url(#a)}</style>
<defs>
<linearGradient id="a">
<stop style="stop-color:#aaa"/>
<stop style="stop-color:#aaa;stop-opacity:0" offset="1"/>
</linearGradient>
</defs>
<rect width="20" height="10" fill="url(#a)"/>
<rect id="c" y="15" width="20" height="10"/>
</svg>

0 comments on commit 15ba73d

Please sign in to comment.