1
0
mirror of https://github.com/locomotivemtl/locomotive-boilerplate.git synced 2026-01-15 00:55:08 +08:00

Fix svg-mixer support for sub-directories

By default, svg-mixer only uses the SVG's file name as its ID. If any SVG files are stored in sub-directories, that information is ignored in the assembled spritesheet. This is problematic since context is lost (the sub-directory's name) and risks duplicate symbol IDs.

This commit introduces a new NPM dependency, `common-path` to resolve the longest common base path, and uses a dependency of `svg-mixer` named `url-slug` in order to prefix the directory name.
This commit is contained in:
Chauncey McAskill
2024-04-02 18:12:58 -04:00
parent 27a41aba66
commit 605f30c948
3 changed files with 82 additions and 8 deletions

View File

@@ -1,21 +1,36 @@
import loconfig from '../helpers/config.js'; import loconfig from '../helpers/config.js';
import glob, { supportsGlob } from '../helpers/glob.js';
import message from '../helpers/message.js'; import message from '../helpers/message.js';
import notification from '../helpers/notification.js'; import notification from '../helpers/notification.js';
import resolve from '../helpers/template.js'; import { resolve as resolveTemplate } from '../helpers/template.js';
import { merge } from '../utils/index.js'; import { merge } from '../utils/index.js';
import { basename } from 'node:path'; import {
basename,
dirname,
extname,
resolve,
} from 'node:path';
import commonPath from 'common-path';
import mixer from 'svg-mixer'; import mixer from 'svg-mixer';
import slugify from 'url-slug';
const basePath = loconfig?.paths?.svgs?.src
? resolve(loconfig.paths.svgs.src)
: null;
/** /**
* @const {object} defaultMixerOptions - The default shared Mixer options. * @const {object} defaultMixerOptions - The default shared Mixer options.
* @const {object} developmentMixerOptions - The predefined Mixer options for development.
* @const {object} productionMixerOptions - The predefined Mixer options for production.
*/ */
export const defaultMixerOptions = { export const defaultMixerOptions = {
spriteConfig: { spriteConfig: {
usages: false, usages: false,
}, },
}; };
/**
* @const {object} developmentMixerOptions - The predefined Mixer options for development.
* @const {object} productionMixerOptions - The predefined Mixer options for production.
*/
export const developmentMixerOptions = Object.assign({}, defaultMixerOptions); export const developmentMixerOptions = Object.assign({}, defaultMixerOptions);
export const productionMixerOptions = Object.assign({}, defaultMixerOptions); export const productionMixerOptions = Object.assign({}, defaultMixerOptions);
@@ -74,10 +89,52 @@ export default async function compileSVGs(mixerOptions = null) {
includes = [ includes ]; includes = [ includes ];
} }
includes = resolve(includes); includes = resolveTemplate(includes);
outfile = resolve(outfile); outfile = resolveTemplate(outfile);
const result = await mixer(includes, mixerOptions); if (supportsGlob && basePath) {
includes = await glob(includes);
includes = [ ...new Set(includes) ];
const common = commonPath(includes);
if (common.commonDir) {
common.commonDir = resolve(common.commonDir);
}
/**
* Generates the `<symbol id>` attribute and prefix any
* SVG files in subdirectories according to the paths
* common base path.
*
* Example for SVG source path `./assets/images/sprite`:
*
* | Path | ID |
* | ------------------------------------ | --------- |
* | `./assets/images/sprite/foo.svg` | `foo` |
* | `./assets/images/sprite/baz/qux.svg` | `baz-qux` |
*
* @param {string} path - The absolute path to the file.
* @param {string} [query=''] - A query string.
* @return {string} The symbol ID.
*/
mixerOptions.generateSymbolId = (path, query = '') => {
let dirName = dirname(path)
.replace(common.commonDir ?? basePath, '')
.replace(/^\/|\/$/, '')
.replace('/', '-');
if (dirName) {
dirName += '-';
}
const fileName = basename(path, extname(path));
const decodedQuery = decodeURIComponent(decodeURIComponent(query));
return `${dirName}${fileName}${slugify(decodedQuery)}`;
};
}
const result = await mixer(includes, {
...mixerOptions,
});
await result.write(outfile); await result.write(outfile);

16
package-lock.json generated
View File

@@ -16,6 +16,7 @@
"devDependencies": { "devDependencies": {
"autoprefixer": "^10.4.17", "autoprefixer": "^10.4.17",
"browser-sync": "^3.0.2", "browser-sync": "^3.0.2",
"common-path": "^1.0.1",
"concat": "^1.0.3", "concat": "^1.0.3",
"esbuild": "^0.20.0", "esbuild": "^0.20.0",
"kleur": "^4.1.5", "kleur": "^4.1.5",
@@ -1218,6 +1219,15 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true "dev": true
}, },
"node_modules/common-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/common-path/-/common-path-1.0.1.tgz",
"integrity": "sha512-C8zvr4tVGRIJpbrh7WxeFZPvUkc2PHWx2IvxAUtuJCAiOLx0n6N4Xaab0C7wM+HDfXLqUQg7H9FoIjyxn/4IiA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/component-emitter": { "node_modules/component-emitter": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@@ -5701,6 +5711,12 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true "dev": true
}, },
"common-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/common-path/-/common-path-1.0.1.tgz",
"integrity": "sha512-C8zvr4tVGRIJpbrh7WxeFZPvUkc2PHWx2IvxAUtuJCAiOLx0n6N4Xaab0C7wM+HDfXLqUQg7H9FoIjyxn/4IiA==",
"dev": true
},
"component-emitter": { "component-emitter": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",

View File

@@ -22,6 +22,7 @@
"devDependencies": { "devDependencies": {
"autoprefixer": "^10.4.17", "autoprefixer": "^10.4.17",
"browser-sync": "^3.0.2", "browser-sync": "^3.0.2",
"common-path": "^1.0.1",
"concat": "^1.0.3", "concat": "^1.0.3",
"esbuild": "^0.20.0", "esbuild": "^0.20.0",
"kleur": "^4.1.5", "kleur": "^4.1.5",