diff --git a/packages/plugin-svgo/src/__snapshots__/index.test.js.snap b/packages/plugin-svgo/src/__snapshots__/index.test.js.snap
index ef9cec5e..f6d891db 100644
--- a/packages/plugin-svgo/src/__snapshots__/index.test.js.snap
+++ b/packages/plugin-svgo/src/__snapshots__/index.test.js.snap
@@ -1,19 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`svgo should be possible to disable id prefixing 1`] = `""`;
+exports[`svgo disables id prefixing using svgo config 1`] = `""`;
-exports[`svgo should not load runtime configuration with \`runtimeConfig: false\` 1`] = `""`;
+exports[`svgo does not load runtime configuration with \`runtimeConfig: false\` 1`] = `""`;
-exports[`svgo should not remove viewBox with icon option 1`] = `""`;
+exports[`svgo does not remove viewBox with \`icon\` option 1`] = `""`;
-exports[`svgo should not remove viewBox with when dimensions is false 1`] = `""`;
+exports[`svgo does not remove viewBox with when \`dimensions\` is false 1`] = `""`;
-exports[`svgo should optimize svg 1`] = `""`;
+exports[`svgo is possible to enable id prefixing as the only optimization 1`] = `""`;
-exports[`svgo should support config.svgoConfig 1`] = `""`;
+exports[`svgo optimizes svg 1`] = `""`;
-exports[`svgo should support icon with config.svgoConfig plugins 1`] = `""`;
+exports[`svgo supports \`config.icon\` with \`config.svgoConfig\` plugins 1`] = `""`;
-exports[`svgo should use state.filePath to detect configuration 1`] = `""`;
+exports[`svgo supports \`config.svgoConfig.multipass\` 1`] = `""`;
-exports[`svgo should be possible to enable id prefixing as the only optimization 1`] = `""`;
+exports[`svgo supports \`config.svgoConfig\` 1`] = `""`;
+
+exports[`svgo users \`state.filePath\` to detect configuration 1`] = `""`;
diff --git a/packages/plugin-svgo/src/config.js b/packages/plugin-svgo/src/config.js
new file mode 100644
index 00000000..0de6c9f0
--- /dev/null
+++ b/packages/plugin-svgo/src/config.js
@@ -0,0 +1,48 @@
+import mergeDeep from 'merge-deep'
+
+export function getFilePath(state) {
+ return state.filePath || process.cwd()
+}
+
+export function getBaseSvgoConfig(config) {
+ const baseSvgoConfig = {
+ plugins: [{ prefixIds: true }],
+ }
+ if (config.icon || config.dimensions === false) {
+ baseSvgoConfig.plugins.push({ removeViewBox: false })
+ }
+ return baseSvgoConfig
+}
+
+export function getPlugins(config) {
+ if (!config || !config.plugins) {
+ return []
+ }
+ if (!Array.isArray(config.plugins)) {
+ throw Error('`svgoConfig.plugins` must be an array')
+ }
+ return config.plugins
+}
+
+function extractPlugins(config) {
+ if (!config) return []
+ if (!config.plugins) return []
+ if (!Array.isArray(config.plugins)) return [config.plugins]
+ return config.plugins
+}
+
+function mergePlugins(configs) {
+ const plugins = configs.reduce(
+ (merged, config) => mergeDeep(merged, ...extractPlugins(config)),
+ {},
+ )
+ return Object.keys(plugins).reduce((array, key) => {
+ array.push({ [key]: plugins[key] })
+ return array
+ }, [])
+}
+
+export function mergeSvgoConfig(...configs) {
+ const plugins = mergePlugins(configs)
+ return { ...mergeDeep(...configs), plugins }
+}
diff --git a/packages/plugin-svgo/src/config.test.js b/packages/plugin-svgo/src/config.test.js
new file mode 100644
index 00000000..bb2946fc
--- /dev/null
+++ b/packages/plugin-svgo/src/config.test.js
@@ -0,0 +1,133 @@
+import { getFilePath, getBaseSvgoConfig, mergeSvgoConfig } from './config'
+
+describe('svgo config', () => {
+ describe('#getFilePath', () => {
+ describe('if `state.filePath` exists', () => {
+ it('returns `state.filePath', () => {
+ expect(getFilePath({ filePath: '/foo/bar' })).toBe('/foo/bar')
+ })
+ })
+ describe('if `state.filePath` does not exists', () => {
+ it('returns current working directory', () => {
+ expect(getFilePath({})).toBe(process.cwd())
+ })
+ })
+ })
+
+ describe('#getBaseSvgoConfig', () => {
+ describe('with no specific config', () => {
+ it('returns config with `prefixIds: true`', () => {
+ expect(getBaseSvgoConfig({})).toEqual({
+ plugins: [{ prefixIds: true }],
+ })
+ })
+ })
+
+ describe('with `config.icons` enabled', () => {
+ it('returns config with `removeViewBox: false`', () => {
+ expect(getBaseSvgoConfig({ icon: true })).toEqual({
+ plugins: [{ prefixIds: true }, { removeViewBox: false }],
+ })
+ })
+ })
+
+ describe('with `config.dimensions` disabled', () => {
+ it('returns config with `removeViewBox: false`', () => {
+ expect(getBaseSvgoConfig({ dimensions: false })).toEqual({
+ plugins: [{ prefixIds: true }, { removeViewBox: false }],
+ })
+ })
+ })
+ })
+
+ describe('#mergeSvgoConfig', () => {
+ it('merges any config format', () => {
+ expect(mergeSvgoConfig({ foo: 'bar' }, { foo: 'rab' })).toEqual({
+ foo: 'rab',
+ plugins: [],
+ })
+ expect(
+ mergeSvgoConfig({ plugins: { removeViewBox: false } }, null),
+ ).toEqual({
+ plugins: [{ removeViewBox: false }],
+ })
+ expect(
+ mergeSvgoConfig({ plugins: { removeViewBox: false } }, {}),
+ ).toEqual({
+ plugins: [{ removeViewBox: false }],
+ })
+ expect(mergeSvgoConfig({ plugins: { removeViewBox: false } })).toEqual({
+ plugins: [{ removeViewBox: false }],
+ })
+ expect(mergeSvgoConfig({ plugins: [{ removeViewBox: false }] })).toEqual({
+ plugins: [{ removeViewBox: false }],
+ })
+ expect(
+ mergeSvgoConfig({
+ plugins: [{ removeViewBox: false }, { removeViewBox: true }],
+ }),
+ ).toEqual({
+ plugins: [{ removeViewBox: true }],
+ })
+ expect(
+ mergeSvgoConfig({
+ plugins: [
+ {
+ convertColors: {
+ currentColor: true,
+ },
+ },
+ {
+ prefixIds: {
+ prefix: 'foo',
+ },
+ },
+ ],
+ }),
+ ).toEqual({
+ plugins: [
+ {
+ convertColors: {
+ currentColor: true,
+ },
+ },
+ {
+ prefixIds: {
+ prefix: 'foo',
+ },
+ },
+ ],
+ })
+ expect(
+ mergeSvgoConfig(
+ {
+ plugins: [
+ {
+ prefixIds: {
+ prefix: 'foo',
+ },
+ },
+ ],
+ },
+ {
+ plugins: [
+ {
+ prefixIds: {
+ prefix: 'bar',
+ },
+ },
+ ],
+ },
+ ),
+ ).toEqual({
+ plugins: [
+ {
+ prefixIds: {
+ prefix: 'bar',
+ },
+ },
+ ],
+ })
+ })
+ })
+})
diff --git a/packages/plugin-svgo/src/index.js b/packages/plugin-svgo/src/index.js
index 5c713b40..4fb9908e 100644
--- a/packages/plugin-svgo/src/index.js
+++ b/packages/plugin-svgo/src/index.js
@@ -1,7 +1,7 @@
/* eslint-disable no-underscore-dangle */
import SVGO from 'svgo'
import { cosmiconfigSync } from 'cosmiconfig'
-import mergeDeep from 'merge-deep'
+import { getFilePath, getBaseSvgoConfig, mergeSvgoConfig } from './config'
const explorer = cosmiconfigSync('svgo', {
searchPlaces: [
@@ -83,54 +83,13 @@ function optimizeSync(svgstr, info) {
return result
}
-function getBaseSvgoConfig(config) {
- const baseSvgoConfig = {
- plugins: [{ prefixIds: true }],
- }
- if (config.icon || config.dimensions === false)
- baseSvgoConfig.plugins.push({ removeViewBox: false })
- return baseSvgoConfig
-}
-
-function getFilePath(state) {
- return state.filePath || process.cwd()
-}
-
-function getPlugins(config) {
- if (!config || !config.plugins) {
- return []
- }
- if (!Array.isArray(config.plugins)) {
- throw Error("`svgoConfig.plugins` must be an array")
- }
- return config.plugins
-}
-
-function extendPlugins(...configs) {
- const init = [];
- let i = configs.length;
-
- while (i-- > 0) {
- const plugins = configs[i];
- for (let j = 0; j < plugins.length; j++) {
- const plugin = plugins[j];
- if (!init.some(item => Object.keys(item)[0] === Object.keys(plugin)[0])) {
- init.push(plugin);
- }
- }
- }
- return init;
-}
-
function createSvgo(config, rcConfig) {
- const baseSvgoConfig = getBaseSvgoConfig(config);
- const plugins = extendPlugins(getPlugins(baseSvgoConfig), getPlugins(rcConfig), getPlugins(config.svgoConfig));
- const mergedConfig = mergeDeep(
+ const baseSvgoConfig = getBaseSvgoConfig(config)
+ const mergedConfig = mergeSvgoConfig(
baseSvgoConfig,
rcConfig,
config.svgoConfig,
)
- mergedConfig.plugins = plugins
return new SVGO(mergedConfig)
}
diff --git a/packages/plugin-svgo/src/index.test.js b/packages/plugin-svgo/src/index.test.js
index 3050fdd1..9491baf9 100644
--- a/packages/plugin-svgo/src/index.test.js
+++ b/packages/plugin-svgo/src/index.test.js
@@ -19,12 +19,12 @@ const baseSvg = `
`
describe('svgo', () => {
- it('should optimize svg', () => {
+ it('optimizes svg', () => {
const result = svgo(baseSvg, { svgo: true, runtimeConfig: true }, {})
expect(result).toMatchSnapshot()
})
- it('should support config.svgoConfig', () => {
+ it('supports `config.svgoConfig`', () => {
const result = svgo(
baseSvg,
{
@@ -38,21 +38,21 @@ describe('svgo', () => {
expect(result).toMatchSnapshot()
})
- it('should throw error for invalid config.svgoConfig', () => {
- const svgoOptions = [
+ it('supports `config.svgoConfig.multipass`', () => {
+ const result = svgo(
baseSvg,
{
svgo: true,
runtimeConfig: true,
- svgoConfig: { plugins: { removeDesc: false } },
+ svgoConfig: { multipass: true },
},
{},
- ]
+ )
- expect(() => svgo(...svgoOptions)).toThrow()
+ expect(result).toMatchSnapshot()
})
- it('should support icon with config.svgoConfig plugins', () => {
+ it('supports `config.icon` with `config.svgoConfig` plugins', () => {
const result = svgo(
baseSvg,
{
@@ -67,7 +67,7 @@ describe('svgo', () => {
expect(result).toMatchSnapshot()
})
- it('should use state.filePath to detect configuration', () => {
+ it('users `state.filePath` to detect configuration', () => {
const result = svgo(
baseSvg,
{ svgo: true, runtimeConfig: true },
@@ -77,7 +77,7 @@ describe('svgo', () => {
expect(result).toMatchSnapshot()
})
- it('should not load runtime configuration with `runtimeConfig: false`', () => {
+ it('does not load runtime configuration with `runtimeConfig: false`', () => {
const result = svgo(
baseSvg,
{ svgo: true, runtimeConfig: false },
@@ -87,7 +87,7 @@ describe('svgo', () => {
expect(result).toMatchSnapshot()
})
- it('should not remove viewBox with icon option', () => {
+ it('does not remove viewBox with `icon` option', () => {
const result = svgo(
baseSvg,
{ svgo: true, icon: true, runtimeConfig: true },
@@ -97,7 +97,7 @@ describe('svgo', () => {
expect(result).toMatchSnapshot()
})
- it('should not remove viewBox with when dimensions is false', () => {
+ it('does not remove viewBox with when `dimensions` is false', () => {
const result = svgo(
baseSvg,
{ svgo: true, dimensions: false, runtimeConfig: true },
@@ -107,7 +107,7 @@ describe('svgo', () => {
expect(result).toMatchSnapshot()
})
- it('should be possible to disable id prefixing', () => {
+ it('disables id prefixing using svgo config', () => {
const result = svgo(
baseSvg,
{
@@ -122,14 +122,19 @@ describe('svgo', () => {
expect(result).toMatchSnapshot()
})
- it('should be possible to enable id prefixing as the only optimization', () => {
+ it('is possible to enable id prefixing as the only optimization', () => {
const result = svgo(
baseSvg,
{
svgo: true,
icon: true,
runtimeConfig: true,
- svgoConfig: { full: true, plugins: [{ prefixIds: {prefixIds: true, prefixClassNames: false} }] },
+ svgoConfig: {
+ full: true,
+ plugins: [
+ { prefixIds: { prefixIds: true, prefixClassNames: false } },
+ ],
+ },
},
{ filePath: path.join(__dirname, '../__fixtures__/svgo') },
)