svgo-v2 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/bin/svgo +10 -0
  4. package/dist/svgo.browser.js +1 -0
  5. package/lib/css-tools.js +239 -0
  6. package/lib/parser.js +259 -0
  7. package/lib/path.js +347 -0
  8. package/lib/stringifier.js +326 -0
  9. package/lib/style.js +283 -0
  10. package/lib/svgo/coa.js +517 -0
  11. package/lib/svgo/config.js +138 -0
  12. package/lib/svgo/css-class-list.js +72 -0
  13. package/lib/svgo/css-select-adapter.d.ts +2 -0
  14. package/lib/svgo/css-select-adapter.js +120 -0
  15. package/lib/svgo/css-style-declaration.js +232 -0
  16. package/lib/svgo/jsAPI.d.ts +2 -0
  17. package/lib/svgo/jsAPI.js +443 -0
  18. package/lib/svgo/plugins.js +109 -0
  19. package/lib/svgo/tools.js +137 -0
  20. package/lib/svgo-node.js +106 -0
  21. package/lib/svgo.js +83 -0
  22. package/lib/types.ts +172 -0
  23. package/lib/xast.js +102 -0
  24. package/package.json +130 -0
  25. package/plugins/_applyTransforms.js +335 -0
  26. package/plugins/_collections.js +2168 -0
  27. package/plugins/_path.js +816 -0
  28. package/plugins/_transforms.js +379 -0
  29. package/plugins/addAttributesToSVGElement.js +87 -0
  30. package/plugins/addClassesToSVGElement.js +87 -0
  31. package/plugins/cleanupAttrs.js +55 -0
  32. package/plugins/cleanupEnableBackground.js +75 -0
  33. package/plugins/cleanupIDs.js +297 -0
  34. package/plugins/cleanupListOfValues.js +154 -0
  35. package/plugins/cleanupNumericValues.js +113 -0
  36. package/plugins/collapseGroups.js +135 -0
  37. package/plugins/convertColors.js +152 -0
  38. package/plugins/convertEllipseToCircle.js +39 -0
  39. package/plugins/convertPathData.js +1023 -0
  40. package/plugins/convertShapeToPath.js +175 -0
  41. package/plugins/convertStyleToAttrs.js +132 -0
  42. package/plugins/convertTransform.js +432 -0
  43. package/plugins/inlineStyles.js +379 -0
  44. package/plugins/mergePaths.js +104 -0
  45. package/plugins/mergeStyles.js +93 -0
  46. package/plugins/minifyStyles.js +148 -0
  47. package/plugins/moveElemsAttrsToGroup.js +130 -0
  48. package/plugins/moveGroupAttrsToElems.js +62 -0
  49. package/plugins/plugins.js +56 -0
  50. package/plugins/prefixIds.js +241 -0
  51. package/plugins/preset-default.js +80 -0
  52. package/plugins/removeAttributesBySelector.js +99 -0
  53. package/plugins/removeAttrs.js +159 -0
  54. package/plugins/removeComments.js +31 -0
  55. package/plugins/removeDesc.js +41 -0
  56. package/plugins/removeDimensions.js +43 -0
  57. package/plugins/removeDoctype.js +42 -0
  58. package/plugins/removeEditorsNSData.js +68 -0
  59. package/plugins/removeElementsByAttr.js +78 -0
  60. package/plugins/removeEmptyAttrs.js +33 -0
  61. package/plugins/removeEmptyContainers.js +58 -0
  62. package/plugins/removeEmptyText.js +57 -0
  63. package/plugins/removeHiddenElems.js +318 -0
  64. package/plugins/removeMetadata.js +29 -0
  65. package/plugins/removeNonInheritableGroupAttrs.js +38 -0
  66. package/plugins/removeOffCanvasPaths.js +138 -0
  67. package/plugins/removeRasterImages.js +33 -0
  68. package/plugins/removeScriptElement.js +29 -0
  69. package/plugins/removeStyleElement.js +29 -0
  70. package/plugins/removeTitle.js +29 -0
  71. package/plugins/removeUnknownsAndDefaults.js +218 -0
  72. package/plugins/removeUnusedNS.js +61 -0
  73. package/plugins/removeUselessDefs.js +65 -0
  74. package/plugins/removeUselessStrokeAndFill.js +144 -0
  75. package/plugins/removeViewBox.js +51 -0
  76. package/plugins/removeXMLNS.js +30 -0
  77. package/plugins/removeXMLProcInst.js +30 -0
  78. package/plugins/reusePaths.js +113 -0
  79. package/plugins/sortAttrs.js +113 -0
  80. package/plugins/sortDefsChildren.js +60 -0
@@ -0,0 +1,517 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const colors = require('picocolors');
6
+ const { loadConfig, optimize } = require('../svgo-node.js');
7
+ const pluginsMap = require('../../plugins/plugins.js');
8
+ const PKG = require('../../package.json');
9
+ const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');
10
+
11
+ const regSVGFile = /\.svg$/i;
12
+
13
+ /**
14
+ * Synchronously check if path is a directory. Tolerant to errors like ENOENT.
15
+ * @param {string} path
16
+ */
17
+ function checkIsDir(path) {
18
+ try {
19
+ return fs.lstatSync(path).isDirectory();
20
+ } catch (e) {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ module.exports = function makeProgram(program) {
26
+ program
27
+ .name(PKG.name)
28
+ .description(PKG.description, {
29
+ INPUT: 'Alias to --input',
30
+ })
31
+ .version(PKG.version, '-v, --version')
32
+ .arguments('[INPUT...]')
33
+ .option('-i, --input <INPUT...>', 'Input files, "-" for STDIN')
34
+ .option('-s, --string <STRING>', 'Input SVG data string')
35
+ .option(
36
+ '-f, --folder <FOLDER>',
37
+ 'Input folder, optimize and rewrite all *.svg files'
38
+ )
39
+ .option(
40
+ '-o, --output <OUTPUT...>',
41
+ 'Output file or folder (by default the same as the input), "-" for STDOUT'
42
+ )
43
+ .option(
44
+ '-p, --precision <INTEGER>',
45
+ 'Set number of digits in the fractional part, overrides plugins params'
46
+ )
47
+ .option('--config <CONFIG>', 'Custom config file, only .js is supported')
48
+ .option(
49
+ '--datauri <FORMAT>',
50
+ 'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)'
51
+ )
52
+ .option(
53
+ '--multipass',
54
+ 'Pass over SVGs multiple times to ensure all optimizations are applied'
55
+ )
56
+ .option('--pretty', 'Make SVG pretty printed')
57
+ .option('--indent <INTEGER>', 'Indent number when pretty printing SVGs')
58
+ .option(
59
+ '--eol <EOL>',
60
+ 'Line break to use when outputting SVG: lf, crlf. If unspecified, uses platform default.'
61
+ )
62
+ .option('--final-newline', 'Ensure SVG ends with a line break')
63
+ .option(
64
+ '-r, --recursive',
65
+ "Use with '--folder'. Optimizes *.svg files in folders recursively."
66
+ )
67
+ .option(
68
+ '--exclude <PATTERN...>',
69
+ "Use with '--folder'. Exclude files matching regular expression pattern."
70
+ )
71
+ .option(
72
+ '-q, --quiet',
73
+ 'Only output error messages, not regular status messages'
74
+ )
75
+ .option('--show-plugins', 'Show available plugins and exit')
76
+ // used by picocolors internally
77
+ .option('--no-color', 'Output plain text without color')
78
+ .action(action);
79
+ };
80
+
81
+ async function action(args, opts, command) {
82
+ var input = opts.input || args;
83
+ var output = opts.output;
84
+ var config = {};
85
+
86
+ if (opts.precision != null) {
87
+ const number = Number.parseInt(opts.precision, 10);
88
+ if (Number.isNaN(number)) {
89
+ console.error(
90
+ "error: option '-p, --precision' argument must be an integer number"
91
+ );
92
+ process.exit(1);
93
+ } else {
94
+ opts.precision = number;
95
+ }
96
+ }
97
+
98
+ if (opts.datauri != null) {
99
+ if (
100
+ opts.datauri !== 'base64' &&
101
+ opts.datauri !== 'enc' &&
102
+ opts.datauri !== 'unenc'
103
+ ) {
104
+ console.error(
105
+ "error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'"
106
+ );
107
+ process.exit(1);
108
+ }
109
+ }
110
+
111
+ if (opts.indent != null) {
112
+ const number = Number.parseInt(opts.indent, 10);
113
+ if (Number.isNaN(number)) {
114
+ console.error(
115
+ "error: option '--indent' argument must be an integer number"
116
+ );
117
+ process.exit(1);
118
+ } else {
119
+ opts.indent = number;
120
+ }
121
+ }
122
+
123
+ if (opts.eol != null && opts.eol !== 'lf' && opts.eol !== 'crlf') {
124
+ console.error(
125
+ "error: option '--eol' must have one of the following values: 'lf' or 'crlf'"
126
+ );
127
+ process.exit(1);
128
+ }
129
+
130
+ // --show-plugins
131
+ if (opts.showPlugins) {
132
+ showAvailablePlugins();
133
+ return;
134
+ }
135
+
136
+ // w/o anything
137
+ if (
138
+ (input.length === 0 || input[0] === '-') &&
139
+ !opts.string &&
140
+ !opts.stdin &&
141
+ !opts.folder &&
142
+ process.stdin.isTTY === true
143
+ ) {
144
+ return command.help();
145
+ }
146
+
147
+ if (
148
+ typeof process == 'object' &&
149
+ process.versions &&
150
+ process.versions.node &&
151
+ PKG &&
152
+ PKG.engines.node
153
+ ) {
154
+ var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
155
+ if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
156
+ throw Error(
157
+ `${PKG.name} requires Node.js version ${nodeVersion} or higher.`
158
+ );
159
+ }
160
+ }
161
+
162
+ // --config
163
+ const loadedConfig = await loadConfig(opts.config);
164
+ if (loadedConfig != null) {
165
+ config = loadedConfig;
166
+ }
167
+
168
+ // --quiet
169
+ if (opts.quiet) {
170
+ config.quiet = opts.quiet;
171
+ }
172
+
173
+ // --recursive
174
+ if (opts.recursive) {
175
+ config.recursive = opts.recursive;
176
+ }
177
+
178
+ // --exclude
179
+ config.exclude = opts.exclude
180
+ ? opts.exclude.map((pattern) => RegExp(pattern))
181
+ : [];
182
+
183
+ // --precision
184
+ if (opts.precision != null) {
185
+ var precision = Math.min(Math.max(0, opts.precision), 20);
186
+ config.floatPrecision = precision;
187
+ }
188
+
189
+ // --multipass
190
+ if (opts.multipass) {
191
+ config.multipass = true;
192
+ }
193
+
194
+ // --pretty
195
+ if (opts.pretty) {
196
+ config.js2svg = config.js2svg || {};
197
+ config.js2svg.pretty = true;
198
+ if (opts.indent != null) {
199
+ config.js2svg.indent = opts.indent;
200
+ }
201
+ }
202
+
203
+ // --eol
204
+ if (opts.eol) {
205
+ config.js2svg = config.js2svg || {};
206
+ config.js2svg.eol = opts.eol;
207
+ }
208
+
209
+ // --final-newline
210
+ if (opts.finalNewline) {
211
+ config.js2svg = config.js2svg || {};
212
+ config.js2svg.finalNewline = true;
213
+ }
214
+
215
+ // --output
216
+ if (output) {
217
+ if (input.length && input[0] != '-') {
218
+ if (output.length == 1 && checkIsDir(output[0])) {
219
+ var dir = output[0];
220
+ for (var i = 0; i < input.length; i++) {
221
+ output[i] = checkIsDir(input[i])
222
+ ? input[i]
223
+ : path.resolve(dir, path.basename(input[i]));
224
+ }
225
+ } else if (output.length < input.length) {
226
+ output = output.concat(input.slice(output.length));
227
+ }
228
+ }
229
+ } else if (input.length) {
230
+ output = input;
231
+ } else if (opts.string) {
232
+ output = '-';
233
+ }
234
+
235
+ if (opts.datauri) {
236
+ config.datauri = opts.datauri;
237
+ }
238
+
239
+ // --folder
240
+ if (opts.folder) {
241
+ var ouputFolder = (output && output[0]) || opts.folder;
242
+ await optimizeFolder(config, opts.folder, ouputFolder);
243
+ }
244
+
245
+ // --input
246
+ if (input.length !== 0) {
247
+ // STDIN
248
+ if (input[0] === '-') {
249
+ return new Promise((resolve, reject) => {
250
+ var data = '',
251
+ file = output[0];
252
+
253
+ process.stdin
254
+ .on('data', (chunk) => (data += chunk))
255
+ .once('end', () =>
256
+ processSVGData(config, { input: 'string' }, data, file).then(
257
+ resolve,
258
+ reject
259
+ )
260
+ );
261
+ });
262
+ // file
263
+ } else {
264
+ await Promise.all(
265
+ input.map((file, n) => optimizeFile(config, file, output[n]))
266
+ );
267
+ }
268
+
269
+ // --string
270
+ } else if (opts.string) {
271
+ var data = decodeSVGDatauri(opts.string);
272
+
273
+ return processSVGData(config, { input: 'string' }, data, output[0]);
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Optimize SVG files in a directory.
279
+ * @param {Object} config options
280
+ * @param {string} dir input directory
281
+ * @param {string} output output directory
282
+ * @return {Promise}
283
+ */
284
+ function optimizeFolder(config, dir, output) {
285
+ if (!config.quiet) {
286
+ console.log(`Processing directory '${dir}':\n`);
287
+ }
288
+ return fs.promises
289
+ .readdir(dir)
290
+ .then((files) => processDirectory(config, dir, files, output));
291
+ }
292
+
293
+ /**
294
+ * Process given files, take only SVG.
295
+ * @param {Object} config options
296
+ * @param {string} dir input directory
297
+ * @param {Array} files list of file names in the directory
298
+ * @param {string} output output directory
299
+ * @return {Promise}
300
+ */
301
+ function processDirectory(config, dir, files, output) {
302
+ // take only *.svg files, recursively if necessary
303
+ var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
304
+
305
+ return svgFilesDescriptions.length
306
+ ? Promise.all(
307
+ svgFilesDescriptions.map((fileDescription) =>
308
+ optimizeFile(
309
+ config,
310
+ fileDescription.inputPath,
311
+ fileDescription.outputPath
312
+ )
313
+ )
314
+ )
315
+ : Promise.reject(
316
+ new Error(`No SVG files have been found in '${dir}' directory.`)
317
+ );
318
+ }
319
+
320
+ /**
321
+ * Get svg files descriptions
322
+ * @param {Object} config options
323
+ * @param {string} dir input directory
324
+ * @param {Array} files list of file names in the directory
325
+ * @param {string} output output directory
326
+ * @return {Array}
327
+ */
328
+ function getFilesDescriptions(config, dir, files, output) {
329
+ const filesInThisFolder = files
330
+ .filter(
331
+ (name) =>
332
+ regSVGFile.test(name) &&
333
+ !config.exclude.some((regExclude) => regExclude.test(name))
334
+ )
335
+ .map((name) => ({
336
+ inputPath: path.resolve(dir, name),
337
+ outputPath: path.resolve(output, name),
338
+ }));
339
+
340
+ return config.recursive
341
+ ? [].concat(
342
+ filesInThisFolder,
343
+ files
344
+ .filter((name) => checkIsDir(path.resolve(dir, name)))
345
+ .map((subFolderName) => {
346
+ const subFolderPath = path.resolve(dir, subFolderName);
347
+ const subFolderFiles = fs.readdirSync(subFolderPath);
348
+ const subFolderOutput = path.resolve(output, subFolderName);
349
+ return getFilesDescriptions(
350
+ config,
351
+ subFolderPath,
352
+ subFolderFiles,
353
+ subFolderOutput
354
+ );
355
+ })
356
+ .reduce((a, b) => [].concat(a, b), [])
357
+ )
358
+ : filesInThisFolder;
359
+ }
360
+
361
+ /**
362
+ * Read SVG file and pass to processing.
363
+ * @param {Object} config options
364
+ * @param {string} file
365
+ * @param {string} output
366
+ * @return {Promise}
367
+ */
368
+ function optimizeFile(config, file, output) {
369
+ return fs.promises.readFile(file, 'utf8').then(
370
+ (data) =>
371
+ processSVGData(config, { input: 'file', path: file }, data, output, file),
372
+ (error) => checkOptimizeFileError(config, file, output, error)
373
+ );
374
+ }
375
+
376
+ /**
377
+ * Optimize SVG data.
378
+ * @param {Object} config options
379
+ * @param {string} data SVG content to optimize
380
+ * @param {string} output where to write optimized file
381
+ * @param {string} [input] input file name (being used if output is a directory)
382
+ * @return {Promise}
383
+ */
384
+ function processSVGData(config, info, data, output, input) {
385
+ var startTime = Date.now(),
386
+ prevFileSize = Buffer.byteLength(data, 'utf8');
387
+
388
+ const result = optimize(data, { ...config, ...info });
389
+ if (result.modernError) {
390
+ console.error(colors.red(result.modernError.toString()));
391
+ process.exit(1);
392
+ }
393
+ if (config.datauri) {
394
+ result.data = encodeSVGDatauri(result.data, config.datauri);
395
+ }
396
+ var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
397
+ processingTime = Date.now() - startTime;
398
+
399
+ return writeOutput(input, output, result.data).then(
400
+ function () {
401
+ if (!config.quiet && output != '-') {
402
+ if (input) {
403
+ console.log(`\n${path.basename(input)}:`);
404
+ }
405
+ printTimeInfo(processingTime);
406
+ printProfitInfo(prevFileSize, resultFileSize);
407
+ }
408
+ },
409
+ (error) =>
410
+ Promise.reject(
411
+ new Error(
412
+ error.code === 'ENOTDIR'
413
+ ? `Error: output '${output}' is not a directory.`
414
+ : error
415
+ )
416
+ )
417
+ );
418
+ }
419
+
420
+ /**
421
+ * Write result of an optimization.
422
+ * @param {string} input
423
+ * @param {string} output output file name. '-' for stdout
424
+ * @param {string} data data to write
425
+ * @return {Promise}
426
+ */
427
+ function writeOutput(input, output, data) {
428
+ if (output == '-') {
429
+ console.log(data);
430
+ return Promise.resolve();
431
+ }
432
+
433
+ fs.mkdirSync(path.dirname(output), { recursive: true });
434
+
435
+ return fs.promises
436
+ .writeFile(output, data, 'utf8')
437
+ .catch((error) => checkWriteFileError(input, output, data, error));
438
+ }
439
+
440
+ /**
441
+ * Write a time taken by optimization.
442
+ * @param {number} time time in milliseconds.
443
+ */
444
+ function printTimeInfo(time) {
445
+ console.log(`Done in ${time} ms!`);
446
+ }
447
+
448
+ /**
449
+ * Write optimizing information in human readable format.
450
+ * @param {number} inBytes size before optimization.
451
+ * @param {number} outBytes size after optimization.
452
+ */
453
+ function printProfitInfo(inBytes, outBytes) {
454
+ var profitPercents = 100 - (outBytes * 100) / inBytes;
455
+
456
+ console.log(
457
+ Math.round((inBytes / 1024) * 1000) / 1000 +
458
+ ' KiB' +
459
+ (profitPercents < 0 ? ' + ' : ' - ') +
460
+ colors.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
461
+ ' = ' +
462
+ Math.round((outBytes / 1024) * 1000) / 1000 +
463
+ ' KiB'
464
+ );
465
+ }
466
+
467
+ /**
468
+ * Check for errors, if it's a dir optimize the dir.
469
+ * @param {Object} config
470
+ * @param {string} input
471
+ * @param {string} output
472
+ * @param {Error} error
473
+ * @return {Promise}
474
+ */
475
+ function checkOptimizeFileError(config, input, output, error) {
476
+ if (error.code == 'EISDIR') {
477
+ return optimizeFolder(config, input, output);
478
+ } else if (error.code == 'ENOENT') {
479
+ return Promise.reject(
480
+ new Error(`Error: no such file or directory '${error.path}'.`)
481
+ );
482
+ }
483
+ return Promise.reject(error);
484
+ }
485
+
486
+ /**
487
+ * Check for saving file error. If the output is a dir, then write file there.
488
+ * @param {string} input
489
+ * @param {string} output
490
+ * @param {string} data
491
+ * @param {Error} error
492
+ * @return {Promise}
493
+ */
494
+ function checkWriteFileError(input, output, data, error) {
495
+ if (error.code == 'EISDIR' && input) {
496
+ return fs.promises.writeFile(
497
+ path.resolve(output, path.basename(input)),
498
+ data,
499
+ 'utf8'
500
+ );
501
+ } else {
502
+ return Promise.reject(error);
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Show list of available plugins with short description.
508
+ */
509
+ function showAvailablePlugins() {
510
+ const list = Object.entries(pluginsMap)
511
+ .sort(([a], [b]) => a.localeCompare(b))
512
+ .map(([name, plugin]) => ` [ ${colors.green(name)} ] ${plugin.description}`)
513
+ .join('\n');
514
+ console.log('Currently available plugins:\n' + list);
515
+ }
516
+
517
+ module.exports.checkIsDir = checkIsDir;
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+
3
+ const pluginsMap = require('../../plugins/plugins.js');
4
+
5
+ const pluginsOrder = [
6
+ 'removeDoctype',
7
+ 'removeXMLProcInst',
8
+ 'removeComments',
9
+ 'removeMetadata',
10
+ 'removeXMLNS',
11
+ 'removeEditorsNSData',
12
+ 'cleanupAttrs',
13
+ 'mergeStyles',
14
+ 'inlineStyles',
15
+ 'minifyStyles',
16
+ 'convertStyleToAttrs',
17
+ 'cleanupIDs',
18
+ 'prefixIds',
19
+ 'removeRasterImages',
20
+ 'removeUselessDefs',
21
+ 'cleanupNumericValues',
22
+ 'cleanupListOfValues',
23
+ 'convertColors',
24
+ 'removeUnknownsAndDefaults',
25
+ 'removeNonInheritableGroupAttrs',
26
+ 'removeUselessStrokeAndFill',
27
+ 'removeViewBox',
28
+ 'cleanupEnableBackground',
29
+ 'removeHiddenElems',
30
+ 'removeEmptyText',
31
+ 'convertShapeToPath',
32
+ 'convertEllipseToCircle',
33
+ 'moveElemsAttrsToGroup',
34
+ 'moveGroupAttrsToElems',
35
+ 'collapseGroups',
36
+ 'convertPathData',
37
+ 'convertTransform',
38
+ 'removeEmptyAttrs',
39
+ 'removeEmptyContainers',
40
+ 'mergePaths',
41
+ 'removeUnusedNS',
42
+ 'sortAttrs',
43
+ 'sortDefsChildren',
44
+ 'removeTitle',
45
+ 'removeDesc',
46
+ 'removeDimensions',
47
+ 'removeAttrs',
48
+ 'removeAttributesBySelector',
49
+ 'removeElementsByAttr',
50
+ 'addClassesToSVGElement',
51
+ 'removeStyleElement',
52
+ 'removeScriptElement',
53
+ 'addAttributesToSVGElement',
54
+ 'removeOffCanvasPaths',
55
+ 'reusePaths',
56
+ ];
57
+ const defaultPlugins = pluginsOrder.filter((name) => pluginsMap[name].active);
58
+ exports.defaultPlugins = defaultPlugins;
59
+
60
+ const extendDefaultPlugins = (plugins) => {
61
+ console.warn(
62
+ '\n"extendDefaultPlugins" utility is deprecated.\n' +
63
+ 'Use "preset-default" plugin with overrides instead.\n' +
64
+ 'For example:\n' +
65
+ `{\n` +
66
+ ` name: 'preset-default',\n` +
67
+ ` params: {\n` +
68
+ ` overrides: {\n` +
69
+ ` // customize plugin options\n` +
70
+ ` convertShapeToPath: {\n` +
71
+ ` convertArcs: true\n` +
72
+ ` },\n` +
73
+ ` // disable plugins\n` +
74
+ ` convertPathData: false\n` +
75
+ ` }\n` +
76
+ ` }\n` +
77
+ `}\n`
78
+ );
79
+ const extendedPlugins = pluginsOrder.map((name) => ({
80
+ name,
81
+ active: pluginsMap[name].active,
82
+ }));
83
+ for (const plugin of plugins) {
84
+ const resolvedPlugin = resolvePluginConfig(plugin);
85
+ const index = pluginsOrder.indexOf(resolvedPlugin.name);
86
+ if (index === -1) {
87
+ extendedPlugins.push(plugin);
88
+ } else {
89
+ extendedPlugins[index] = plugin;
90
+ }
91
+ }
92
+ return extendedPlugins;
93
+ };
94
+ exports.extendDefaultPlugins = extendDefaultPlugins;
95
+
96
+ const resolvePluginConfig = (plugin) => {
97
+ let configParams = {};
98
+ if (typeof plugin === 'string') {
99
+ // resolve builtin plugin specified as string
100
+ const pluginConfig = pluginsMap[plugin];
101
+ if (pluginConfig == null) {
102
+ throw Error(`Unknown builtin plugin "${plugin}" specified.`);
103
+ }
104
+ return {
105
+ ...pluginConfig,
106
+ name: plugin,
107
+ active: true,
108
+ params: { ...pluginConfig.params, ...configParams },
109
+ };
110
+ }
111
+ if (typeof plugin === 'object' && plugin != null) {
112
+ if (plugin.name == null) {
113
+ throw Error(`Plugin name should be specified`);
114
+ }
115
+ if (plugin.fn) {
116
+ // resolve custom plugin with implementation
117
+ return {
118
+ active: true,
119
+ ...plugin,
120
+ params: { ...configParams, ...plugin.params },
121
+ };
122
+ } else {
123
+ // resolve builtin plugin specified as object without implementation
124
+ const pluginConfig = pluginsMap[plugin.name];
125
+ if (pluginConfig == null) {
126
+ throw Error(`Unknown builtin plugin "${plugin.name}" specified.`);
127
+ }
128
+ return {
129
+ ...pluginConfig,
130
+ active: true,
131
+ ...plugin,
132
+ params: { ...pluginConfig.params, ...configParams, ...plugin.params },
133
+ };
134
+ }
135
+ }
136
+ return null;
137
+ };
138
+ exports.resolvePluginConfig = resolvePluginConfig;