From e8941d26e3327998508037ed5e362a2e385d07cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Berg=C3=A9?= Date: Sun, 3 Sep 2017 08:14:53 +0000 Subject: [PATCH] feat: add template option --- README.md | 16 +++++++++++ __fixtures__/template.js | 7 +++++ src/cli/__snapshots__/index.test.js.snap | 15 +++++++++++ src/cli/index.js | 34 +++++++++++++++++++----- src/cli/index.test.js | 7 +++++ src/configToOptions.js | 3 ++- src/index.js | 14 ++++++++++ src/transforms/wrapIntoComponent.js | 19 +++---------- 8 files changed, 91 insertions(+), 24 deletions(-) create mode 100644 __fixtures__/template.js diff --git a/README.md b/README.md index ff4f932a..e03b66b2 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ configurable HTML transpiler. It uses AST (like [Babel](https://github.com/babel -d, --out-dir output files into a directory --no-svgo disable SVGO --no-prettier disable Prettier + --template 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 @@ -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: @@ -189,6 +198,13 @@ Default | CLI Override | API Override --------|--------------|------------- `true` | `--no-prettier` | `prettier: ` +### 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: ` + ### Expand props All properties given to component will be forwarded on SVG tag. diff --git a/__fixtures__/template.js b/__fixtures__/template.js new file mode 100644 index 00000000..cea95208 --- /dev/null +++ b/__fixtures__/template.js @@ -0,0 +1,7 @@ +export default () => (code, state) => ` +import React from 'react' + +export default function ${state.componentName}() { + return ${code} +} +` diff --git a/src/cli/__snapshots__/index.test.js.snap b/src/cli/__snapshots__/index.test.js.snap index 357535b1..e5a33f44 100644 --- a/src/cli/__snapshots__/index.test.js.snap +++ b/src/cli/__snapshots__/index.test.js.snap @@ -143,6 +143,21 @@ export default One; " `; +exports[`cli --template 1`] = ` +"import React from \\"react\\"; + +export default function One() { + return ( + + Rectangle 5 + + + ); +} + +" +`; + exports[`cli should work with a simple file 1`] = ` "import React from \\"react\\"; diff --git a/src/cli/index.js b/src/cli/index.js index a7ed7c09..8b22f2ea 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -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' @@ -20,6 +21,7 @@ program .option('-d, --out-dir ', 'output files into a directory') .option('--no-svgo', 'disable SVGO') .option('--no-prettier', 'disable Prettier') + .option('--template ', 'specify a custom template to use') .option('--no-expand-props', 'disable props expanding') .option('--icon', 'use "1em" as width and height') .option( @@ -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) diff --git a/src/cli/index.test.js b/src/cli/index.test.js index 0bcbcc2e..bb6291e9 100644 --- a/src/cli/index.test.js +++ b/src/cli/index.test.js @@ -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() diff --git a/src/configToOptions.js b/src/configToOptions.js index a34aee70..6dc0ca54 100644 --- a/src/configToOptions.js +++ b/src/configToOptions.js @@ -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 = {}) { @@ -64,7 +65,7 @@ function configToOptions(config = {}) { plugins: getH2xPlugins(), }, prettier: config.prettier ? getPrettierConfig() : null, - template: wrapIntoComponent({ expandProps: config.expandProps }), + template: config.template(config), } } diff --git a/src/index.js b/src/index.js index 1c01e3ab..ce4b2e4c 100644 --- a/src/index.js +++ b/src/index.js @@ -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' @@ -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) diff --git a/src/transforms/wrapIntoComponent.js b/src/transforms/wrapIntoComponent.js index 67a79fc1..02fe9f39 100644 --- a/src/transforms/wrapIntoComponent.js +++ b/src/transforms/wrapIntoComponent.js @@ -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}`