feat(cli): allow all CLI options in config (#615)

Also run Prettier on index.js file.

Closes #570
This commit is contained in:
Greg Bergé 2021-11-01 10:06:43 +01:00 committed by GitHub
parent 10638d0dda
commit aa9feb2b1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 70 deletions

View File

@ -3,7 +3,7 @@ const path = require('path')
function indexTemplate(files) {
const exportEntries = files.map(file => {
const basename = path.basename(file, path.extname(file))
return `export { ${basename} } from './${basename}'`
return `export { ${basename} } from './${basename}';`
})
return exportEntries.join('\n')
}

View File

@ -2,7 +2,8 @@
exports[`cli should add Svg prefix to index.js exports staring with number 1`] = `
"export { default as Svg2File } from './2File'
export { default as File } from './File'"
export { default as File } from './File'
"
`;
exports[`cli should not override config with cli defaults 1`] = `
@ -19,7 +20,10 @@ export default SvgFile
"
`;
exports[`cli should support --index-template in cli 1`] = `"export { File } from './File'"`;
exports[`cli should support --index-template in cli 1`] = `
"export { File } from './File'
"
`;
exports[`cli should support --no-index 1`] = `
Array [
@ -92,7 +96,10 @@ Array [
]
`;
exports[`cli should support custom index.js with directory output 1`] = `"export { default as File } from './File'"`;
exports[`cli should support custom index.js with directory output 1`] = `
"export { File } from './File'
"
`;
exports[`cli should support different filename cases with directory output 1`] = `
Array [

View File

@ -3,13 +3,14 @@ import { promises as fs } from 'fs'
import * as path from 'path'
import { grey, white } from 'chalk'
import { loadConfig, Config } from '@svgr/core'
import { format, resolveConfig } from 'prettier'
import {
convertFile,
transformFilename,
politeWrite,
formatExportName,
} from './util'
import type { SvgrCommand } from './index'
import type { Options, SvgrCommand } from './index'
const exists = async (filepath: string) => {
try {
@ -50,20 +51,20 @@ const resolveExtension = (config: Config, ext?: string) =>
ext || (config.typescript ? 'tsx' : 'js')
export const dirCommand: SvgrCommand = async (
{
opts,
_,
filenames,
): Promise<void> => {
const {
ext: extOpt,
filenameCase = 'pascal',
ignoreExisting,
silent,
indexTemplate: indexTemplateOpt,
configFile,
outDir,
},
_,
filenames,
config,
): Promise<void> => {
const ext = resolveExtension(config, extOpt)
} = opts
const ext = resolveExtension(opts, extOpt)
const write = async (src: string, dest: string) => {
if (!isCompilable(src)) {
@ -71,7 +72,7 @@ export const dirCommand: SvgrCommand = async (
}
dest = rename(dest, ext, filenameCase)
const code = await convertFile(src, config)
const code = await convertFile(src, opts)
const cwdRelative = path.relative(process.cwd(), dest)
const logOutput = `${src} -> ${cwdRelative}\n`
@ -86,10 +87,25 @@ export const dirCommand: SvgrCommand = async (
return { transformed: true, dest }
}
const generateIndex = async (dest: string, files: string[]) => {
const indexFile = path.join(dest, `index.${ext}`)
const indexTemplate = indexTemplateOpt || defaultIndexTemplate
await fs.writeFile(indexFile, indexTemplate(files))
const generateIndex = async (
dest: string,
files: string[],
opts: Options,
) => {
const filepath = path.join(dest, `index.${ext}`)
const indexTemplate = opts.indexTemplate || defaultIndexTemplate
const fileContent = indexTemplate(files)
const prettyContent = await (async () => {
if (!opts.prettier) return fileContent
const prettierRcConfig = opts.runtimeConfig
? await resolveConfig(filepath, { editorconfig: true })
: {}
return format(fileContent, {
...prettierRcConfig,
...opts.prettierConfig,
})
})()
await fs.writeFile(filepath, prettyContent)
}
async function handle(filename: string, root: string) {
@ -114,11 +130,11 @@ export const dirCommand: SvgrCommand = async (
path.relative(root, dirname),
)
const resolvedConfig = loadConfig.sync(
{ configFile, ...config },
{ configFile, ...opts },
{ filePath: dest },
)
) as Options
if (resolvedConfig.index) {
await generateIndex(dest, destFiles)
await generateIndex(dest, destFiles, opts)
}
}
return { transformed: false, dest: null }

View File

@ -21,11 +21,10 @@ export const fileCommand: SvgrCommand = async (
opts,
program,
filenames,
config,
): Promise<void> => {
if (opts.stdin || (filenames.length === 0 && !process.stdin.isTTY)) {
const input = await readStdin()
const output = convert(input, config, { filePath: opts.stdinFilepath })
const output = convert(input, opts, { filePath: opts.stdinFilepath })
process.stdout.write(`${output}\n`)
return
}
@ -46,6 +45,6 @@ export const fileCommand: SvgrCommand = async (
exitError('Directory are not supported without `--out-dir` option instead.')
}
const output = await convertFile(filename, config)
const output = await convertFile(filename, opts)
process.stdout.write(`${output}\n`)
}

View File

@ -18,7 +18,7 @@ describe('cli', () => {
it('should work with a simple file', async () => {
const result = await cli('__fixtures__/simple/file.svg')
expect(result).toMatchSnapshot()
}, 10000)
})
it('should not work with a directory without --out-dir option', async () => {
expect.assertions(1)
@ -29,7 +29,7 @@ describe('cli', () => {
'Directory are not supported without `--out-dir` option instead',
)
}
}, 10000)
})
it('should not work with several files without destination', async () => {
expect.assertions(1)
@ -40,19 +40,19 @@ describe('cli', () => {
'Please specify only one filename or use `--out-dir` option',
)
}
}, 10000)
})
it('should work with stdin', async () => {
const result = await cli('< __fixtures__/simple/file.svg')
expect(result).toMatchSnapshot()
}, 10000)
})
it('should support stdin filepath', async () => {
const result = await cli(
'--stdin-filepath __fixtures__/simple/file.svg < __fixtures__/simple/file.svg',
)
expect(result).toMatchSnapshot()
}, 10000)
})
it('should transform a whole directory and output relative destination paths', async () => {
const result = await cli('--out-dir __fixtures_build__/whole __fixtures__')
@ -62,7 +62,7 @@ describe('cli', () => {
.map((x) => x.toLowerCase())
.join('\n')
expect(sorted).toMatchSnapshot()
}, 10000)
})
it('should transform a whole directory with --typescript', async () => {
const result = await cli(
@ -74,7 +74,7 @@ describe('cli', () => {
.map((x) => x.toLowerCase())
.join('\n')
expect(sorted).toMatchSnapshot()
}, 10000)
})
it('should suppress output when transforming a directory with a --silent option', async () => {
const result = await cli(
@ -82,35 +82,35 @@ describe('cli', () => {
)
const sorted = result.split(/\n/).sort().join('\n')
expect(sorted).toMatchSnapshot()
}, 10000)
})
it('should support --prettier-config as json', async () => {
const result = await cli(
`--prettier-config '{"tabWidth": 5}' __fixtures__/simple/file.svg`,
)
expect(result).toMatchSnapshot()
}, 10000)
})
it('should support --prettier-config as file', async () => {
const result = await cli(
`--prettier-config __fixtures__/withPrettierRc/.prettierrc __fixtures__/simple/file.svg`,
)
expect(result).toMatchSnapshot()
}, 10000)
})
it('should support --svgo-config as json', async () => {
const result = await cli(
`--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__/withSvgoConfig/svgo.config.js __fixtures__/simple/file.svg`,
)
expect(result).toMatchSnapshot()
}, 10000)
})
it.each([
['--no-dimensions'],
@ -164,7 +164,7 @@ describe('cli', () => {
await del(outDir)
await cli(`--ext=ts ${inDir} --out-dir=${outDir}`)
expect(await fs.readdir(outDir)).toMatchSnapshot()
}, 10000)
})
it('should support "--ignore-existing"', async () => {
const inDir = '__fixtures__/simple'
@ -172,14 +172,14 @@ describe('cli', () => {
await cli(`${inDir} --out-dir=${outDir} --ignore-existing`)
const content = await fs.readFile(path.join(outDir, 'File.js'), 'utf-8')
expect(content).toBe('// nothing')
}, 10000)
})
it('should not override config with cli defaults', async () => {
const result = await cli(
'__fixtures__/simple/file.svg --config-file=__fixtures__/overrides.config.js',
)
expect(result).toMatchSnapshot()
}, 10000)
})
it('should add Svg prefix to index.js exports staring with number', async () => {
const inDir = '__fixtures__/numeric'
@ -188,7 +188,7 @@ describe('cli', () => {
await cli(`${inDir} --out-dir=${outDir}`)
const content = await fs.readFile(path.join(outDir, 'index.js'), 'utf-8')
expect(content).toMatchSnapshot()
}, 10000)
})
it('should support custom index.js with directory output', async () => {
const inDir = '__fixtures__/simple'
@ -199,7 +199,7 @@ describe('cli', () => {
)
const content = await fs.readFile(path.join(outDir, 'index.js'), 'utf-8')
expect(content).toMatchSnapshot()
}, 10000)
})
it('should support --index-template in cli', async () => {
const inDir = '__fixtures__/simple'
@ -210,7 +210,7 @@ describe('cli', () => {
)
const content = await fs.readFile(path.join(outDir, 'index.js'), 'utf-8')
expect(content).toMatchSnapshot()
}, 10000)
})
it('should support --no-index', async () => {
const inDir = '__fixtures__/simple'
@ -218,5 +218,5 @@ describe('cli', () => {
await del(outDir)
await cli(`--no-index ${inDir} --out-dir=${outDir}`)
expect(await fs.readdir(outDir)).toMatchSnapshot()
}, 10000)
})
})

View File

@ -68,7 +68,7 @@ const parseTemplate = (name: string) => (arg: string) => {
}
}
interface ProgramOpts extends Config {
export interface Options extends Config {
configFile?: string
runtimeConfig?: boolean
outDir?: string
@ -82,12 +82,7 @@ interface ProgramOpts extends Config {
}
export interface SvgrCommand {
(
opts: ProgramOpts,
program: Command,
filenames: string[],
config: Config,
): Promise<void>
(opts: Options, program: Command, filenames: string[]): Promise<void>
}
program
@ -193,28 +188,13 @@ async function run() {
process.exit(2)
}
const opts = noUndefinedKeys(program.opts<ProgramOpts>())
const config = await loadConfig(opts, { filePath: process.cwd() })
if (opts.dimensions === true) {
delete config.dimensions
}
if (opts.svgo === true) {
delete config.svgo
}
if (opts.prettier === true) {
delete config.prettier
}
if (opts.index === false) {
delete config.index
}
const programOpts = noUndefinedKeys(program.opts<Options>())
const opts = (await loadConfig(programOpts, {
filePath: process.cwd(),
})) as Options
const command = opts.outDir ? dirCommand : fileCommand
await command(opts, program, filenames, config)
await command(opts, program, filenames)
}
run().catch((error) => {