From c6041078e272a4ebf791b20521106fa0b52f3122 Mon Sep 17 00:00:00 2001 From: John Kenny Date: Tue, 17 Sep 2024 08:18:44 -0700 Subject: [PATCH] Add --enable command line option. --- lib/svgo.d.ts | 1 + lib/svgo.js | 32 ++++- lib/svgo/coa.js | 10 +- test/coa/_index.test.js | 132 +++++++++++++++--- .../{testPreset => testPluginOpts}/config1.js | 0 .../{testPreset => testPluginOpts}/test1.svg | 0 6 files changed, 148 insertions(+), 27 deletions(-) rename test/coa/{testPreset => testPluginOpts}/config1.js (100%) rename test/coa/{testPreset => testPluginOpts}/test1.svg (100%) diff --git a/lib/svgo.d.ts b/lib/svgo.d.ts index 319c282..cd02a95 100644 --- a/lib/svgo.d.ts +++ b/lib/svgo.d.ts @@ -73,6 +73,7 @@ export type Config = { */ plugins?: PluginConfig[]; preset?: 'default' | 'none'; + enable?: string[]; /** Options for rendering optimized SVG from AST. */ js2svg?: StringifyOptions; /** Output as Data URI string. */ diff --git a/lib/svgo.js b/lib/svgo.js index 11c5d2f..854d9f7 100644 --- a/lib/svgo.js +++ b/lib/svgo.js @@ -82,16 +82,34 @@ export const optimize = (input, config) => { * @returns {import('svgo-ll').PluginConfig[]} */ function getPlugins(config) { - if (config.plugins) { - return config.plugins; + /** + * @returns {import('svgo-ll').PluginConfig[]} + */ + function getPreset() { + if (config.plugins) { + return config.plugins; + } + if (config.preset) { + if (builtinPresets.has(config.preset)) { + return [`preset-${config.preset}`]; + } + console.warn(`invalid preset "${config.preset}"; using preset-default`); + } + return ['preset-default']; } - if (config.preset) { - if (builtinPresets.has(config.preset)) { - return [`preset-${config.preset}`]; + + const presets = getPreset(); + if (config.enable) { + for (const builtinName of config.enable) { + const builtin = pluginsMap.get(builtinName); + if (builtin) { + presets.push(builtin); + } else { + console.warn(`plugin "${builtinName}" not found`); + } } - console.warn(`invalid preset "${config.preset}"; using preset-default`); } - return ['preset-default']; + return presets; } if (!config) { diff --git a/lib/svgo/coa.js b/lib/svgo/coa.js index 8b45167..2d8e726 100644 --- a/lib/svgo/coa.js +++ b/lib/svgo/coa.js @@ -56,9 +56,13 @@ export default function makeProgram(program) { ) .option( '--preset ', - 'Specify which set of predefined plugins to use.', + 'Specify which set of predefined plugins to use', 'default', ) + .option( + '--enable ', + 'Specify one or more builtin plugins to run in addition to the presets', + ) .option( '--config ', 'Custom config file, only .js, .mjs, and .cjs is supported', @@ -240,6 +244,10 @@ async function action(args, opts, command) { } } + if (opts.enable) { + config.enable = opts.enable; + } + // --pretty if (opts.pretty) { config.js2svg = config.js2svg || {}; diff --git a/test/coa/_index.test.js b/test/coa/_index.test.js index bdf6a18..803a854 100644 --- a/test/coa/_index.test.js +++ b/test/coa/_index.test.js @@ -180,34 +180,45 @@ describe('coa', function () { }); }); +const PLUGINOPT_DIR = path.resolve(__dirname, 'testPluginOpts'); +const PLUGINOPT_FILE1 = path.resolve(PLUGINOPT_DIR, 'test1.svg'); +const PLUGINOPT_FILE1_OPT = path.resolve(tempFolder, 'test1.svg'); + +const EXPECT_TRANS = + ''; +const EXPECT_TRANS_PATH = + ''; +const EXPECT_TRANS_PATH_SORTED = + ''; + describe('test preset option', function () { afterAll(() => { fs.rmSync(tempFolder, { force: true, recursive: true }); }); - const dirName = path.resolve(__dirname, 'testPreset'); - const svg1 = path.resolve(dirName, 'test1.svg'); - const svg1Opt = path.resolve(tempFolder, 'test1.svg'); - it('should use default preset when option not specified', async () => { - await runProgram(['-i', svg1, '-o', svg1Opt, '--quiet']); - const opt = fs.readFileSync(svg1Opt, { encoding: 'utf8' }); - expect(opt).toBe( - '', - ); + await runProgram([ + '-i', + PLUGINOPT_FILE1, + '-o', + PLUGINOPT_FILE1_OPT, + '--quiet', + ]); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); + expect(opt).toBe(EXPECT_TRANS_PATH); }); it('should only remove whitespace when "none" specified', async () => { await runProgram([ '-i', - svg1, + PLUGINOPT_FILE1, '-o', - svg1Opt, + PLUGINOPT_FILE1_OPT, '--quiet', '--preset', 'none', ]); - const opt = fs.readFileSync(svg1Opt, { encoding: 'utf8' }); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); expect(opt).toBe( '', ); @@ -216,18 +227,101 @@ describe('test preset option', function () { it('should only minify transform when "none" specified, but custom config is used', async () => { await runProgram([ '-i', - svg1, + PLUGINOPT_FILE1, '-o', - svg1Opt, + PLUGINOPT_FILE1_OPT, '--quiet', '--preset', 'none', '--config', - path.resolve(dirName, 'config1.js'), + path.resolve(PLUGINOPT_DIR, 'config1.js'), ]); - const opt = fs.readFileSync(svg1Opt, { encoding: 'utf8' }); - expect(opt).toBe( - '', - ); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); + expect(opt).toBe(EXPECT_TRANS); + }); +}); + +describe('test --enable option', function () { + afterAll(() => { + fs.rmSync(tempFolder, { force: true, recursive: true }); + }); + + it('should only run one plugin with --preset=none and --enable=minifyTransforms', async () => { + await runProgram([ + '-i', + PLUGINOPT_FILE1, + '-o', + PLUGINOPT_FILE1_OPT, + '--quiet', + '--preset=none', + '--enable=minifyTransforms', + ]); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); + expect(opt).toBe(EXPECT_TRANS); + }); + + it('should run two plugins with --preset=none and --enable minifyTransforms convertShapeToPath minifyPathData', async () => { + await runProgram([ + '-i', + PLUGINOPT_FILE1, + '-o', + PLUGINOPT_FILE1_OPT, + '--quiet', + '--preset=none', + '--enable', + 'minifyTransforms', + 'convertShapeToPath', + 'minifyPathData', + ]); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); + expect(opt).toBe(EXPECT_TRANS_PATH); + }); + + it('should ignore invalid plugin names', async () => { + await runProgram([ + '-i', + PLUGINOPT_FILE1, + '-o', + PLUGINOPT_FILE1_OPT, + '--quiet', + '--preset=none', + '--enable', + 'x', + 'minifyTransforms', + ]); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); + expect(opt).toBe(EXPECT_TRANS); + }); + + it('should work when plugins are specified in custom config', async () => { + await runProgram([ + '-i', + PLUGINOPT_FILE1, + '-o', + PLUGINOPT_FILE1_OPT, + '--quiet', + '--preset=none', + '--enable', + 'convertShapeToPath', + 'minifyPathData', + '--config', + path.resolve(PLUGINOPT_DIR, 'config1.js'), + ]); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); + expect(opt).toBe(EXPECT_TRANS_PATH); + }); + + it('should work with preset-default', async () => { + await runProgram([ + '-i', + PLUGINOPT_FILE1, + '-o', + PLUGINOPT_FILE1_OPT, + '--quiet', + '--enable', + 'sortAttrs', + ]); + const opt = fs.readFileSync(PLUGINOPT_FILE1_OPT, { encoding: 'utf8' }); + expect(opt).toBe(EXPECT_TRANS_PATH_SORTED); }); }); diff --git a/test/coa/testPreset/config1.js b/test/coa/testPluginOpts/config1.js similarity index 100% rename from test/coa/testPreset/config1.js rename to test/coa/testPluginOpts/config1.js diff --git a/test/coa/testPreset/test1.svg b/test/coa/testPluginOpts/test1.svg similarity index 100% rename from test/coa/testPreset/test1.svg rename to test/coa/testPluginOpts/test1.svg