Skip to content

Commit

Permalink
feat(config): improve runtime config
Browse files Browse the repository at this point in the history
- Add "runtimeConfig" option to enable or disable runtime configuration

BREAKING CHANGE:

- Runtime configuration is always loaded (even with Node API `convert`)
- In CLI, "--config" is now "--config-file"; this new option can be used
everywhere

Closes #192
  • Loading branch information
gregberge committed Sep 30, 2018
1 parent b687c9e commit e52cdce
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 30 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ npx @svgr/cli
## Supporting SVGR

SVGR is an MIT-licensed open source project. It's an independent project with ongoing development made possible thanks to the support of these awesome [backers](/BACKERS.md). If you'd like to join them, please consider:

- [Become a backer or sponsor on OpenCollective](https://opencollective.com/svgr).

### Gold Sponsors
Expand Down Expand Up @@ -93,12 +94,13 @@ powerful and configurable HTML transpiler. It uses AST (like
## Command line usage

```
Usage: npx @svgr/cli [options] <file|directory>
Usage: svgr [options] <file|directory>
Options:
-V, --version output the version number
--config <file> specify the path of the svgr config
--config-file <file> specify the path of the svgr config
--no-runtime-config disable runtime config (.svgrrc, .svgo.yml, .prettierrc)
-d, --out-dir <dirname> output files into a directory
--ext <ext> specify a custom file extension (default: "js")
--filename-case <case> specify filename case (pascal, kebab, camel) (default: "pascal")
Expand Down Expand Up @@ -265,6 +267,22 @@ Even if it is not recommended, you can also use `svgoConfig` option to specify y
SVGR ships with a handful of customizable options, usable in both the CLI and
API.

### Config file

Specify a custom config file.

| Default | CLI Override | API Override |
| ------- | --------------- | ---------------------- |
| `null` | `--config-file` | `configFile: <string>` |

### Runtime config

Disable runtime config (`.svgrrc`, `.svgo.yml`, `.prettierrc`).

| Default | CLI Override | API Override |
| ------- | --------------- | ---------------------- |
| `null` | `--config-file` | `configFile: <string>` |

### File extension

Specify a custom extension for generated files.
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Usage: svgr [options] <file|directory>
Options:
-V, --version output the version number
--config <file> specify the path of the svgr config
--config-file <file> specify the path of the svgr config
--no-runtime-config disable runtime config (.svgrrc, .svgo.yml, .prettierrc)
-d, --out-dir <dirname> output files into a directory
--ext <ext> specify a custom file extension (default: "js")
--filename-case <case> specify filename case (pascal, kebab, camel) (default: "pascal")
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/dirCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async function dirCommand(
relative = rename(relative, ext, filenameCase)

const dest = path.join(program.outDir, relative)
const code = await convertFile(src, options, { filePath: dest })
const code = await convertFile(src, options)

outputFileSync(dest, code)
console.info(`${src} -> ${dest}`)
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ const parseConfig = name => arg => {
program
.version(pkg.version)
.usage('[options] <file|directory>')
.option('--config <file>', 'specify the path of the svgr config')
.option('--config-file <file>', 'specify the path of the svgr config')
.option(
'--no-runtime-config',
'disable runtime config (.svgrrc, .svgo.yml, .prettierrc)',
)
.option('-d, --out-dir <dirname>', 'output files into a directory')
.option('--ext <ext>', 'specify a custom file extension (default: "js")')
.option(
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 @@ -12,7 +12,7 @@ const babelNode = path.join(__dirname, '../../../node_modules/.bin/babel-node')

describe('cli', () => {
const cli = async args => {
const { stdout } = await exec(`${babelNode} ${svgr} ${args}`)
const { stdout } = await exec(`${babelNode} -- ${svgr} ${args}`)
return stdout
}

Expand Down Expand Up @@ -173,7 +173,7 @@ describe('cli', () => {
'should not override config with cli defaults',
async () => {
const result = await cli(
'__fixtures__/simple/file.svg --config=__fixtures__/overrides.config.js',
'__fixtures__/simple/file.svg --config-file=__fixtures__/overrides.config.js',
)
expect(result).toMatchSnapshot()
},
Expand Down
7 changes: 3 additions & 4 deletions packages/cli/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
import fs from 'fs'
import chalk from 'chalk'
import util from 'util'
import convert, { resolveConfig } from '@svgr/core'
import convert from '@svgr/core'

export const readFile = util.promisify(fs.readFile)
export const stat = util.promisify(fs.stat)

export async function convertFile(filePath, { config, ...options } = {}) {
export async function convertFile(filePath, config = {}) {
const code = await readFile(filePath, 'utf-8')
const rcConfig = await resolveConfig(filePath, config)
return convert(code, { ...rcConfig, ...options }, { filePath })
return convert(code, config, { filePath })
}

export function exitError(error) {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('util', () => {
it('should support a custom config path', async () => {
const file = path.join(FIXTURES, 'simple/file.svg')
const result = await convertFile(file, {
config: '__fixtures__/withSvgrRc/.svgrrc',
configFile: '__fixtures__/withSvgrRc/.svgrrc',
})
expect(result).toMatchSnapshot()
})
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/__fixtures__/svgr/icon.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions packages/core/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const DEFAULT_CONFIG = {
svgoConfig: null,
template: null,
titleProp: false,
runtimeConfig: true,
}

const explorer = cosmiconfig('svgr', {
Expand All @@ -37,3 +38,11 @@ export async function resolveConfigFile(filePath) {
const result = await explorer.search(filePath)
return result ? result.filepath : null
}

export async function loadConfig({ configFile, ...baseConfig }, state = {}) {
const rcConfig =
state.filePath && baseConfig.runtimeConfig !== false
? await resolveConfig(state.filePath, configFile)
: {}
return { ...DEFAULT_CONFIG, ...rcConfig, ...baseConfig }
}
128 changes: 127 additions & 1 deletion packages/core/src/config.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'path'
import { resolveConfig, resolveConfigFile } from './config'
import { resolveConfig, resolveConfigFile, loadConfig } from './config'

describe('config', () => {
describe('#resolveConfig', () => {
Expand Down Expand Up @@ -33,4 +33,130 @@ describe('config', () => {
expect(config).toMatch(/__fixtures__(\/|\\)svgr(\/|\\)\.svgrrc$/)
})
})

describe('#loadConfig', () => {
it('should use default config without state.filePath', async () => {
const config = await loadConfig({ dimensions: false })
expect(config).toMatchInlineSnapshot(`
Object {
"dimensions": false,
"expandProps": true,
"h2xConfig": null,
"icon": false,
"native": false,
"prettier": true,
"prettierConfig": null,
"ref": false,
"replaceAttrValues": null,
"runtimeConfig": true,
"svgAttributes": null,
"svgProps": null,
"svgo": true,
"svgoConfig": null,
"template": null,
"titleProp": false,
}
`)
})

it('should load config using filePath', async () => {
const config = await loadConfig(
{},
{ filePath: path.join(__dirname, '__fixtures__/svgr/icon.svg') },
)
expect(config).toMatchInlineSnapshot(`
Object {
"dimensions": true,
"expandProps": true,
"h2xConfig": null,
"icon": true,
"native": false,
"noSemi": true,
"prettier": true,
"prettierConfig": null,
"ref": false,
"replaceAttrValues": Array [
Array [
"#063855",
"currentColor",
],
],
"runtimeConfig": true,
"svgAttributes": null,
"svgProps": null,
"svgo": true,
"svgoConfig": null,
"template": null,
"titleProp": false,
}
`)
})

it('should not load config with "runtimeConfig: false', async () => {
const config = await loadConfig(
{ useRuntimeConfig: false },
{ filePath: path.join(__dirname, '__fixtures__/svgr/icon.svg') },
)
expect(config).toMatchInlineSnapshot(`
Object {
"dimensions": true,
"expandProps": true,
"h2xConfig": null,
"icon": true,
"native": false,
"noSemi": true,
"prettier": true,
"prettierConfig": null,
"ref": false,
"replaceAttrValues": Array [
Array [
"#063855",
"currentColor",
],
],
"runtimeConfig": true,
"svgAttributes": null,
"svgProps": null,
"svgo": true,
"svgoConfig": null,
"template": null,
"titleProp": false,
"useRuntimeConfig": false,
}
`)
})

it('should work with custom config path', async () => {
const config = await loadConfig(
{ configFile: path.join(__dirname, '__fixtures__/svgr/.svgrrc') },
{ filePath: __dirname },
)
expect(config).toMatchInlineSnapshot(`
Object {
"dimensions": true,
"expandProps": true,
"h2xConfig": null,
"icon": true,
"native": false,
"noSemi": true,
"prettier": true,
"prettierConfig": null,
"ref": false,
"replaceAttrValues": Array [
Array [
"#063855",
"currentColor",
],
],
"runtimeConfig": true,
"svgAttributes": null,
"svgProps": null,
"svgo": true,
"svgoConfig": null,
"template": null,
"titleProp": false,
}
`)
})
})
})
8 changes: 4 additions & 4 deletions packages/core/src/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import h2x from './plugins/h2x'
import prettier from './plugins/prettier'
import transform from './plugins/transform'
import { expandState } from './util'
import { DEFAULT_CONFIG } from './config'
import { loadConfig } from './config'

async function convert(code, config = {}, state = {}) {
config = { ...DEFAULT_CONFIG, ...config }
state = expandState(state)
async function convert(code, baseConfig = {}, baseState = {}) {
const state = expandState(baseState)
const config = await loadConfig(baseConfig, baseState)
let result = code
// Remove null-byte character (copy/paste from Illustrator)
result = String(result).replace('\0', '')
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/plugins/__snapshots__/svgo.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,3 @@ exports[`svgo should optimize svg 1`] = `"<svg width=\\"88\\" height=\\"88\\" xm
exports[`svgo should support config.svgoConfig 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><desc>Created with Sketch.</desc><g stroke=\\"#063855\\" stroke-width=\\"2\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><path d=\\"M51 37L37 51M51 51L37 37\\"/></g></svg>"`;
exports[`svgo should support 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 stroke=\\"#063855\\" stroke-width=\\"2\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><path d=\\"M51 37L37 51M51 51L37 37\\"/></g></svg>"`;
exports[`svgo should use state.filePath to detect configuration 1`] = `"<svg width=\\"88\\" height=\\"88\\" xmlns=\\"http://www.w3.org/2000/svg\\"><desc>Created with Sketch.</desc><g stroke=\\"#063855\\" stroke-width=\\"2\\" fill=\\"none\\" fill-rule=\\"evenodd\\" stroke-linecap=\\"square\\"><path d=\\"M51 37L37 51M51 51L37 37\\"/></g></svg>"`;
8 changes: 5 additions & 3 deletions packages/core/src/plugins/prettier.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import mergeDeep from 'merge-deep'
export default async (code, config = {}, state = {}) => {
if (!config.prettier) return code
const filePath = state.filePath || process.cwd()
const prettierRcConfig = await prettier.resolveConfig(filePath, {
editorconfig: true,
})
const prettierRcConfig = config.runtimeConfig
? await prettier.resolveConfig(filePath, {
editorconfig: true,
})
: {}
return prettier.format(
code,
mergeDeep(
Expand Down
28 changes: 25 additions & 3 deletions packages/core/src/plugins/prettier.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import prettier from './prettier'

describe('prettier', () => {
it('should prettify code', async () => {
const result = await prettier(`const foo = <div></div>`, { prettier: true })
const result = await prettier(`const foo = <div></div>`, {
prettier: true,
runtimeConfig: true,
})
expect(result).toBe('const foo = <div />\n')
})

it('should support config.prettierConfig', async () => {
const result = await prettier(`const foo = <div></div>`, {
prettier: true,
runtimeConfig: true,
prettierConfig: { semi: true },
})
expect(result).toBe('const foo = <div />;\n')
Expand All @@ -17,7 +21,7 @@ describe('prettier', () => {
it('should use state.filePath to detect configuration', async () => {
const result = await prettier(
`const foo = <div></div>`,
{ prettier: true },
{ prettier: true, runtimeConfig: true },
{ filePath: '/tmp' },
)
expect(result).toBe('const foo = <div />;\n')
Expand All @@ -31,9 +35,27 @@ describe('prettier', () => {
const { resolveConfig } = require('prettier')
/* eslint-enable global-require */

await prettierPlugin(`const foo = <div></div>`, { prettier: true })
await prettierPlugin(`const foo = <div></div>`, {
prettier: true,
runtimeConfig: true,
})
expect(resolveConfig).toHaveBeenCalledWith(expect.any(String), {
editorconfig: true,
})
})

it('should not load runtime configuration with `runtimeConfig: false`', async () => {
jest.resetModules()
jest.doMock('prettier')
/* eslint-disable global-require */
const prettierPlugin = require('./prettier').default
const { resolveConfig } = require('prettier')
/* eslint-enable global-require */

await prettierPlugin(`const foo = <div></div>`, {
prettier: true,
runtimeConfig: false,
})
expect(resolveConfig).not.toHaveBeenCalled()
})
})

0 comments on commit e52cdce

Please sign in to comment.