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:
@@ -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
16
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user