Skip to content

Commit

Permalink
Merge branch 'svg:main' into patch-4
Browse files Browse the repository at this point in the history
  • Loading branch information
XhmikosR authored Dec 10, 2023
2 parents ded2fa0 + e6deeca commit 85d1b60
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 119 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v2
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE }}
cache: yarn
Expand All @@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE }}
cache: yarn
Expand All @@ -53,7 +53,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: yarn
Expand Down
2 changes: 1 addition & 1 deletion docs/03-plugins/cleanup-enable-background.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ svgo:

Cleans up [`enable-background`](https://developer.mozilla.org/docs/Web/SVG/Attribute/enable-background), unless the document uses [`<filter>`](https://developer.mozilla.org/docs/Web/SVG/Element/filter) elements.

Only cleans up attribute values and inline-styles, but does not effect stylesheets in [`<style>`](https://developer.mozilla.org/docs/Web/SVG/Element/style) nodes.
Only cleans up attribute values and inline-styles, but does not affect stylesheets in [`<style>`](https://developer.mozilla.org/docs/Web/SVG/Element/style) nodes.

This plugin will:

Expand Down
41 changes: 39 additions & 2 deletions lib/svgo/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
* @typedef {import('../types').DataUri} DataUri
*/

const { attrsGroups } = require('../../plugins/_collections');
const { attrsGroups, referencesProps } = require('../../plugins/_collections');

const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/g;
const regReferencesHref = /^#(.+?)$/;
const regReferencesBegin = /(\w+)\.[a-zA-Z]/;

/**
* Encode plain SVG data string into Data URI string.
Expand Down Expand Up @@ -188,6 +192,39 @@ exports.hasScripts = hasScripts;
* @returns {boolean} If the given string includes a URL reference.
*/
const includesUrlReference = (body) => {
return /\burl\((["'])?#(.+?)\1\)/g.test(body);
return new RegExp(regReferencesUrl).test(body);
};
exports.includesUrlReference = includesUrlReference;

/**
* @param {string} attribute
* @param {string} value
* @returns {string[]}
*/
const findReferences = (attribute, value) => {
const results = [];

if (referencesProps.includes(attribute)) {
const matches = value.matchAll(regReferencesUrl);
for (const match of matches) {
results.push(match[2]);
}
}

if (attribute === 'href' || attribute.endsWith(':href')) {
const match = regReferencesHref.exec(value);
if (match != null) {
results.push(match[1]);
}
}

if (attribute === 'begin') {
const match = regReferencesBegin.exec(value);
if (match != null) {
results.push(match[1]);
}
}

return results.map((body) => decodeURI(body));
};
exports.findReferences = findReferences;
32 changes: 3 additions & 29 deletions plugins/cleanupIds.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@
*/

const { visitSkip } = require('../lib/xast.js');
const { hasScripts } = require('../lib/svgo/tools');
const { referencesProps } = require('./_collections.js');
const { hasScripts, findReferences } = require('../lib/svgo/tools');

exports.name = 'cleanupIds';
exports.description = 'removes unused IDs and minifies used';

const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/g;
const regReferencesHref = /^#(.+?)$/;
const regReferencesBegin = /(\w+)\.[a-zA-Z]/;
const generateIdChars = [
'a',
'b',
Expand Down Expand Up @@ -191,29 +187,7 @@ exports.fn = (_root, params) => {
nodeById.set(id, node);
}
} else {
// collect all references
/**
* @type {string[]}
*/
let ids = [];
if (referencesProps.includes(name)) {
const matches = value.matchAll(regReferencesUrl);
for (const match of matches) {
ids.push(match[2]); // url() reference
}
}
if (name === 'href' || name.endsWith(':href')) {
const match = value.match(regReferencesHref);
if (match != null) {
ids.push(match[1]); // href reference
}
}
if (name === 'begin') {
const match = value.match(regReferencesBegin);
if (match != null) {
ids.push(match[1]); // href reference
}
}
const ids = findReferences(name, value);
for (const id of ids) {
let refs = referencesById.get(id);
if (refs == null) {
Expand Down Expand Up @@ -261,7 +235,7 @@ exports.fn = (_root, params) => {
if (value.includes('#')) {
// replace id in href and url()
element.attributes[name] = value.replace(
`#${id}`,
`#${encodeURI(id)}`,
`#${currentIdString}`
);
} else {
Expand Down
93 changes: 40 additions & 53 deletions plugins/prefixIds.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports.description = 'prefix IDs';
*/
const getBasename = (path) => {
// extract everything after latest slash or backslash
const matched = path.match(/[/\\]?([^/\\]+)$/);
const matched = /[/\\]?([^/\\]+)$/.exec(path);
if (matched) {
return matched[1];
}
Expand Down Expand Up @@ -118,7 +118,6 @@ const generatePrefix = (body, node, info, prefixGenerator, delim, history) => {
* Prefixes identifiers
*
* @author strarsis <[email protected]>
*
* @type {import('./plugins-types').Plugin<'prefixIds'>}
*/
exports.fn = (_root, params, info) => {
Expand Down Expand Up @@ -149,60 +148,48 @@ exports.fn = (_root, params, info) => {
return;
}

// parse styles
let cssText = '';
if (
node.children[0].type === 'text' ||
node.children[0].type === 'cdata'
) {
cssText = node.children[0].value;
}
/**
* @type {?csstree.CssNode}
*/
let cssAst = null;
try {
cssAst = csstree.parse(cssText, {
parseValue: true,
parseCustomProperty: false,
});
} catch {
return;
}
for (const child of node.children) {
if (child.type !== 'text' && child.type !== 'cdata') {
continue;
}

csstree.walk(cssAst, (node) => {
// #ID, .class selectors
if (
(prefixIds && node.type === 'IdSelector') ||
(prefixClassNames && node.type === 'ClassSelector')
) {
node.name = prefixId(prefixGenerator, node.name);
const cssText = child.value;
/** @type {?csstree.CssNode} */
let cssAst = null;
try {
cssAst = csstree.parse(cssText, {
parseValue: true,
parseCustomProperty: false,
});
} catch {
return;
}
// url(...) references
// csstree v2 changed this type
// @ts-ignore
if (node.type === 'Url' && node.value.length > 0) {
const prefixed = prefixReference(
prefixGenerator,
// @ts-ignore
unquote(node.value)
);
if (prefixed != null) {
// @ts-ignore
node.value = prefixed;

csstree.walk(cssAst, (node) => {
if (
(prefixIds && node.type === 'IdSelector') ||
(prefixClassNames && node.type === 'ClassSelector')
) {
node.name = prefixId(prefixGenerator, node.name);
return;
}
}
});
// @ts-ignore csstree v2 changed this type
if (node.type === 'Url' && node.value.length > 0) {
const prefixed = prefixReference(
prefixGenerator,
// @ts-ignore
unquote(node.value)
);
if (prefixed != null) {
// @ts-ignore
node.value = prefixed;
}
}
});

// update styles
if (
node.children[0].type === 'text' ||
node.children[0].type === 'cdata'
) {
node.children[0].value = csstree.generate(cssAst);
child.value = csstree.generate(cssAst);
return;
}
return;
}

// prefix an ID attribute value
Expand Down Expand Up @@ -243,15 +230,15 @@ exports.fn = (_root, params, info) => {
}
}

// prefix an URL attribute value
// prefix a URL attribute value
for (const name of referencesProps) {
if (
node.attributes[name] != null &&
node.attributes[name].length !== 0
) {
node.attributes[name] = node.attributes[name].replace(
/url\((.*?)\)/gi,
(match, url) => {
/\burl\((["'])?(#.+?)\1\)/gi,
(match, _, url) => {
const prefixed = prefixReference(prefixGenerator, url);
if (prefixed == null) {
return match;
Expand Down
Loading

0 comments on commit 85d1b60

Please sign in to comment.