Skip to content

Commit

Permalink
feat: add template option
Browse files Browse the repository at this point in the history
  • Loading branch information
gregberge committed Sep 3, 2017
1 parent 6390620 commit e8941d2
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 24 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ configurable HTML transpiler. It uses AST (like [Babel](https://github.com/babel
-d, --out-dir <dirname> output files into a directory
--no-svgo disable SVGO
--no-prettier disable Prettier
--template <file> specify a custom template to use
--no-expand-props disable props expanding
--icon use "1em" as width and height
--replace-attr-value [old=new] replace an attribute value
Expand Down Expand Up @@ -142,6 +143,14 @@ To create icons, two options are important:
$ svgr --icon --replace-attr-value "#000000=currentColor" my-icon.svg
```

#### Use a specific template

You can use a specific template, for an example of template, see [the default one](src/transforms/wrapIntoComponent.js).

```
$ svgr --template path/to/template.js my-icon.svg
```

## Node API usage

SVGR can also be used programmatically:
Expand Down Expand Up @@ -189,6 +198,13 @@ Default | CLI Override | API Override
--------|--------------|-------------
`true` | `--no-prettier` | `prettier: <bool>`

### Template
Specify a template file (CLI) or a template function (API) to use. For an example of template, see [the default one](src/transforms/wrapIntoComponent.js).

Default | CLI Override | API Override
--------|--------------|-------------
[`wrapIntoComponent`](src/transforms/wrapIntoComponent.js) | `--template` | `template: <func>`

### Expand props
All properties given to component will be forwarded on SVG tag.

Expand Down
7 changes: 7 additions & 0 deletions __fixtures__/template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default () => (code, state) => `
import React from 'react'
export default function ${state.componentName}() {
return ${code}
}
`
15 changes: 15 additions & 0 deletions src/cli/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ export default One;
"
`;

exports[`cli --template 1`] = `
"import React from \\"react\\";
export default function One() {
return (
<svg width={48} height={1} viewBox=\\"0 0 48 1\\" {...props}>
<title>Rectangle 5</title>
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
</svg>
);
}
"
`;

exports[`cli should work with a simple file 1`] = `
"import React from \\"react\\";
Expand Down
34 changes: 27 additions & 7 deletions src/cli/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
import program from 'commander'
import fs from 'fs'
import path from 'path'
import fs from 'mz/fs'
import glob from 'glob'
import uniq from 'lodash/uniq'
import pkg from '../../package.json'
Expand All @@ -20,6 +21,7 @@ program
.option('-d, --out-dir <dirname>', 'output files into a directory')
.option('--no-svgo', 'disable SVGO')
.option('--no-prettier', 'disable Prettier')
.option('--template <file>', 'specify a custom template to use')
.option('--no-expand-props', 'disable props expanding')
.option('--icon', 'use "1em" as width and height')
.option(
Expand Down Expand Up @@ -74,18 +76,36 @@ async function run() {

filenames = uniq(filenames)

filenames.forEach(filename => {
if (!fs.existsSync(filename)) {
errors.push(`${filename} does not exist`)
}
})
await Promise.all(
filenames.map(async filename => {
if (await !fs.exists(filename)) {
errors.push(`${filename} does not exist`)
}
}),
)

if (errors.length) {
console.error(errors.join('. '))
process.exit(2)
}

const opts = configToOptions(program)
const config = { ...program }

if (config.template) {
try {
const template = require(path.join(process.cwd(), program.template)) // eslint-disable-line global-require, import/no-dynamic-require
if (template.default) config.template = template.default
else config.template = template

if (typeof config.template !== 'function')
throw new Error('Template must be a function')
} catch (error) {
console.error(`Error when loading template: ${program.template}`)
process.exit(2)
}
}

const opts = configToOptions(config)

const command = program.outDir ? dirCommand : fileCommand
await command(program, filenames, opts)
Expand Down
7 changes: 7 additions & 0 deletions src/cli/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ describe('cli', () => {
expect(stdout).toMatchSnapshot()
})

it('--template', async () => {
const [stdout] = await exec(
'babel-node src/cli --template __fixtures__/template.js __fixtures__/one.svg',
)
expect(stdout).toMatchSnapshot()
})

it('should work with stdin', async () => {
const [stdout] = await exec('babel-node src/cli < __fixtures__/one.svg')
expect(stdout).toMatchSnapshot()
Expand Down
3 changes: 2 additions & 1 deletion src/configToOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const defaultConfig = {
trailingComma: undefined, // default to prettier
bracketSpacing: undefined, // default to prettier
jsxBracketSameLine: undefined, // default to prettier
template: wrapIntoComponent,
}

function configToOptions(config = {}) {
Expand Down Expand Up @@ -64,7 +65,7 @@ function configToOptions(config = {}) {
plugins: getH2xPlugins(),
},
prettier: config.prettier ? getPrettierConfig() : null,
template: wrapIntoComponent({ expandProps: config.expandProps }),
template: config.template(config),
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import jsx from 'h2x-plugin-jsx'
import path from 'path'
import svgo from './plugins/svgo'
import h2x from './plugins/h2x'
import prettier from './plugins/prettier'
Expand All @@ -21,7 +22,20 @@ export {
removeComments,
}

const firstUpperCase = str => `${str.charAt(0).toUpperCase()}${str.slice(1)}`
const hyphenToCamelCase = str =>
str.replace(/-(.)/g, (match, chr) => chr.toUpperCase())

function expandState(state) {
const componentName = firstUpperCase(
hyphenToCamelCase(path.parse(state.filePath).name),
)

return { ...state, componentName }
}

export async function rawConvert(code, options, state) {
state = expandState(state)
let result = code
result = options.svgo ? await svgo(result, options.svgo, state) : result
result = await h2x(result, options.h2x, state)
Expand Down
19 changes: 3 additions & 16 deletions src/transforms/wrapIntoComponent.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
import path from 'path'
export default (opts = {}) => (code, state) => `import React from 'react'
const wrapIntoComponent = (opts = {}) => (code, state) => {
const componentName = firstUpperCase(
hyphenToCamelCase(path.parse(state.filePath).name),
)
return `import React from 'react'
const ${state.componentName} = (${opts.expandProps ? 'props' : ''}) => ${code}
const ${componentName} = (${opts.expandProps ? 'props' : ''}) => ${code}
export default ${componentName}`
}

const firstUpperCase = str => `${str.charAt(0).toUpperCase()}${str.slice(1)}`
const hyphenToCamelCase = str =>
str.replace(/-(.)/g, (match, chr) => chr.toUpperCase())

export default wrapIntoComponent
export default ${state.componentName}`

0 comments on commit e8941d2

Please sign in to comment.