Skip to content

Commit

Permalink
feat: upgrade to svgo v2
Browse files Browse the repository at this point in the history
BREAKING CHANGE: config format of SVGO changes & SVGR does not merge SVGO config.
  • Loading branch information
gregberge committed Sep 5, 2021
1 parent 18cd7c8 commit 1f107af
Show file tree
Hide file tree
Showing 18 changed files with 196 additions and 390 deletions.
File renamed without changes
12 changes: 12 additions & 0 deletions __fixtures__/withSvgoConfig/svgo.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeTitle: false,
},
},
},
]
}
1 change: 0 additions & 1 deletion __fixtures__/withSvgoYml/.svgo.json

This file was deleted.

2 changes: 0 additions & 2 deletions __fixtures__/withSvgoYml/.svgo.yml

This file was deleted.

4 changes: 2 additions & 2 deletions packages/cli/src/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ __fixtures__/numeric/2.file.svg -> __fixtures_build__/whole/numeric/2file.js
__fixtures__/numeric/file.svg -> __fixtures_build__/whole/numeric/file.js
__fixtures__/simple/file.svg -> __fixtures_build__/whole/simple/file.js
__fixtures__/withprettierrc/file.svg -> __fixtures_build__/whole/withprettierrc/file.js
__fixtures__/withsvgoyml/file.svg -> __fixtures_build__/whole/withsvgoyml/file.js
__fixtures__/withsvgoconfig/file.svg -> __fixtures_build__/whole/withsvgoconfig/file.js
__fixtures__/withsvgrrc/file.svg -> __fixtures_build__/whole/withsvgrrc/file.js"
`;
Expand All @@ -564,7 +564,7 @@ __fixtures__/numeric/2.file.svg -> __fixtures_build__/whole/numeric/2file.tsx
__fixtures__/numeric/file.svg -> __fixtures_build__/whole/numeric/file.tsx
__fixtures__/simple/file.svg -> __fixtures_build__/whole/simple/file.tsx
__fixtures__/withprettierrc/file.svg -> __fixtures_build__/whole/withprettierrc/file.tsx
__fixtures__/withsvgoyml/file.svg -> __fixtures_build__/whole/withsvgoyml/file.tsx
__fixtures__/withsvgoconfig/file.svg -> __fixtures_build__/whole/withsvgoconfig/file.tsx
__fixtures__/withsvgrrc/file.svg -> __fixtures_build__/whole/withsvgrrc/file.tsx"
`;
Expand Down
25 changes: 13 additions & 12 deletions packages/cli/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,23 @@ function parseObjectList(arg, accumulation = {}) {
return args.reduce((acc, arg) => parseObject(arg, acc), accumulation)
}

function isFile(filePath) {
try {
const stats = fs.statSync(filePath)
return stats.isFile()
} catch (error) {
return false
}
}

const parseConfig = (name) => (arg) => {
const json = isFile(arg) ? fs.readFileSync(arg) : arg
try {
return JSON.parse(json)
if (arg.endsWith('rc')) {
const content = fs.readFileSync(arg, 'utf-8')
return JSON.parse(content)
}

const ext = path.extname(arg)
if (ext === '.js' || ext === '.json') {
// eslint-disable-next-line import/no-dynamic-require, global-require
return require(path.join(process.cwd(), arg))
}

return JSON.parse(arg)
} catch (error) {
exitError(
`"${name}" is not valid, please specify a file or use inline JSON.`,
`"${name}" is not valid, please specify a valid file or use a inline JSON.`,
)
return null
}
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ describe('cli', () => {

it('should support --svgo-config as json', async () => {
const result = await cli(
`--svgo-config '{"plugins": [{"removeTitle": false}]}' __fixtures__/simple/file.svg`,
`--svgo-config '{"plugins":[{"name":"preset-default","params":{"overrides":{"removeTitle":false}}}]}' __fixtures__/simple/file.svg`,
)
expect(result).toMatchSnapshot()
}, 10000)

it('should support --svgo-config as file', async () => {
const result = await cli(
`--svgo-config __fixtures__/withSvgoYml/.svgo.json __fixtures__/simple/file.svg`,
`--svgo-config __fixtures__/withSvgoConfig/svgo.config.js __fixtures__/simple/file.svg`,
)
expect(result).toMatchSnapshot()
}, 10000)
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/__fixtures__/svgo/.svgo.yml

This file was deleted.

12 changes: 12 additions & 0 deletions packages/core/src/__fixtures__/svgo/svgo.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeDesc: false,
},
},
},
]
}
2 changes: 1 addition & 1 deletion packages/plugin-svgo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@
"dependencies": {
"cosmiconfig": "^7.0.1",
"deepmerge": "^4.2.2",
"svgo": "^1.2.2"
"svgo": "^2.5.0"
}
}
20 changes: 6 additions & 14 deletions packages/plugin-svgo/src/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`svgo disables id prefixing using svgo config 1`] = `"<svg width=\\"88\\" height=\\"88\\" viewBox=\\"0 0 88 88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"Blocks\\" class=\\"blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo does not load runtime configuration with \`runtimeConfig: false\` 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"svgo__Blocks\\" class=\\"svgo__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"svgo__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37 37 51\\" id=\\"svgo__Shape\\"/><path d=\\"M51 51 37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo does not load runtime configuration with \`runtimeConfig: false\` 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"svgo__Blocks\\" class=\\"svgo__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"svgo__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"svgo__Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo does not remove viewBox with \`icon\` option 1`] = `"<svg width=\\"88\\" height=\\"88\\" viewBox=\\"0 0 88 88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"prefix__Blocks\\" class=\\"prefix__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"prefix__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37 37 51\\" id=\\"prefix__Shape\\"/><path d=\\"M51 51 37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo does not remove viewBox with \`icon\` option 1`] = `"<svg width=\\"88\\" height=\\"88\\" viewBox=\\"0 0 88 88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"svgo__Blocks\\" class=\\"svgo__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"svgo__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"svgo__Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo does not remove viewBox with when \`dimensions\` is false 1`] = `"<svg width=\\"88\\" height=\\"88\\" viewBox=\\"0 0 88 88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"prefix__Blocks\\" class=\\"prefix__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"prefix__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37 37 51\\" id=\\"prefix__Shape\\"/><path d=\\"M51 51 37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo does not remove viewBox with when \`dimensions\` is false 1`] = `"<svg width=\\"88\\" height=\\"88\\" viewBox=\\"0 0 88 88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"svgo__Blocks\\" class=\\"svgo__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"svgo__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"svgo__Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo optimizes svg 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"prefix__Blocks\\" class=\\"prefix__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"prefix__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37 37 51\\" id=\\"prefix__Shape\\"/><path d=\\"M51 51 37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo is possible to enable id prefixing as the only optimization 1`] = `"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?><svg width=\\"88px\\" height=\\"88px\\" viewBox=\\"0 0 88 88\\" version=\\"1.1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\"><!--Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch--><title>Dismiss</title><desc>Created with Sketch.</desc><defs/><g id=\\"svgo__Blocks\\" class=\\"blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"svgo__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51,37 L37,51\\" id=\\"svgo__Shape\\"/><path d=\\"M51,51 L37,37\\" id=\\"svgo__Shape\\"/><style>#svgo__Shape{}</style></g></g></svg>"`;
exports[`svgo supports \`config.svgoConfig\` 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><desc>Created with Sketch.</desc><g id=\\"Blocks\\" class=\\"blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37 37 51\\" id=\\"Shape\\"/><path d=\\"M51 51 37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo optimizes svg 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"prefix__Blocks\\" class=\\"prefix__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"prefix__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"prefix__Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo supports \`config.icon\` with \`config.svgoConfig\` plugins 1`] = `"<svg width=\\"88\\" height=\\"88\\" viewBox=\\"0 0 88 88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><desc>Created with Sketch.</desc><g id=\\"prefix__Blocks\\" class=\\"prefix__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"prefix__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"prefix__Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo supports \`config.svgoConfig.multipass\` 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"prefix__prefix__Blocks\\" class=\\"prefix__prefix__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"prefix__prefix__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"prefix__prefix__Shape\\"/><path d=\\"M51 51L37 37\\"/><style/></g></g></svg>"`;
exports[`svgo supports \`config.svgoConfig\` 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><desc>Created with Sketch.</desc><g id=\\"prefix__Blocks\\" class=\\"prefix__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"prefix__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"prefix__Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo users \`state.filePath\` to detect configuration 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"svgo__Blocks\\" class=\\"svgo__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"svgo__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37L37 51\\" id=\\"svgo__Shape\\"/><path d=\\"M51 51L37 37\\"/><style></style></g></g></svg>"`;
exports[`svgo uses \`state.filePath\` to detect configuration 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><g id=\\"svgo__Blocks\\" class=\\"svgo__blocks\\" stroke=\\"none\\" stroke-width=\\"1\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><g id=\\"svgo__Dismiss\\" stroke=\\"#063855\\" stroke-width=\\"2\\"><path d=\\"M51 37 37 51\\" id=\\"svgo__Shape\\"/><path d=\\"M51 51 37 37\\"/><style></style></g></g></svg>"`;
77 changes: 36 additions & 41 deletions packages/plugin-svgo/src/config.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,43 @@
import deepmerge from 'deepmerge'
import { cosmiconfigSync } from 'cosmiconfig'

export function getFilePath(state) {
return state.filePath || process.cwd()
}
const explorer = cosmiconfigSync('svgo', {
searchPlaces: [
'package.json',
'.svgorc',
'.svgorc.js',
'.svgorc.json',
'.svgorc.yaml',
'.svgorc.yml',
'svgo.config.js',
'.svgo.yml',
],
transform: (result) => result && result.config,
cache: true,
})

export function getBaseSvgoConfig(config) {
const baseSvgoConfig = {
plugins: [{ prefixIds: true }],
}
if (config.icon || config.dimensions === false) {
baseSvgoConfig.plugins.push({ removeViewBox: false })
function getSvgoConfigFromSvgrConfig(config) {
const preset =
config.icon || config.dimensions === false
? {
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
}
: 'preset-default'
return {
plugins: [preset, 'prefixIds'],
}
return baseSvgoConfig
}

export function getPlugins(config) {
if (!config || !config.plugins) {
return []
}
if (!Array.isArray(config.plugins)) {
throw Error('`svgoConfig.plugins` must be an array')
export function getSvgoConfig(config, state) {
const cwd = state.filePath || process.cwd()
if (config.svgoConfig) return config.svgoConfig
if (config.runtimeConfig) {
const userConfig = explorer.search(cwd)
if (userConfig) return userConfig
}
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) => deepmerge.all([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 { ...deepmerge.all(configs.filter(Boolean)), plugins }
return getSvgoConfigFromSvgrConfig(config)
}
142 changes: 27 additions & 115 deletions packages/plugin-svgo/src/config.test.js
Original file line number Diff line number Diff line change
@@ -1,131 +1,43 @@
import { getFilePath, getBaseSvgoConfig, mergeSvgoConfig } from './config'
import { getSvgoConfig } 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('#getSvgoConfig', () => {
describe('with no specific config', () => {
it('returns config with `prefixIds: true`', async () => {
const config = {}
const state = {}
expect(await getSvgoConfig(config, state)).toEqual({
plugins: ['preset-default', 'prefixIds'],
})
})
})

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({
describe('with `config.icons` enabled', () => {
it('returns config with `removeViewBox: false`', async () => {
const config = { icon: true }
const state = {}
expect(await getSvgoConfig(config, state)).toEqual({
plugins: [
{
convertColors: {
currentColor: true,
},
},
{
prefixIds: {
prefix: 'foo',
},
name: 'preset-default',
params: { overrides: { removeViewBox: false } },
},
'prefixIds',
],
})
expect(
mergeSvgoConfig(
{
plugins: [
{
prefixIds: {
prefix: 'foo',
},
},
],
},
{
plugins: [
{
prefixIds: {
prefix: 'bar',
},
},
],
},
),
).toEqual({
})
})

describe('with `config.dimensions` disabled', () => {
it('returns config with `removeViewBox: false`', async () => {
const config = { dimensions: false }
const state = {}
expect(await getSvgoConfig(config, state)).toEqual({
plugins: [
{
prefixIds: {
prefix: 'bar',
},
name: 'preset-default',
params: { overrides: { removeViewBox: false } },
},
'prefixIds',
],
})
})
Expand Down
Loading

0 comments on commit 1f107af

Please sign in to comment.