@marcovega/svg-tidy 0.2.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.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/bin/svg-tidy.js +194 -0
- package/lib/config.js +87 -0
- package/lib/svgo-config.js +73 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marco Vega
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# svg-tidy
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@marcovega/svg-tidy)
|
|
4
|
+
|
|
5
|
+
A tiny CLI that optimizes every `.svg` file in a directory using
|
|
6
|
+
[SVGO](https://github.com/svg/svgo) with the same plugin settings as the
|
|
7
|
+
[SVGOMG](https://svgomg.net/) preset.
|
|
8
|
+
|
|
9
|
+
On the **first** run inside a directory it writes a `.svgtidy.json` file with
|
|
10
|
+
the default plugin settings. On subsequent runs it reads that file. Commit it
|
|
11
|
+
to your repo so everyone on the team optimizes icons the same way.
|
|
12
|
+
|
|
13
|
+
It rewrites each `.svg` file **in place**, so the result is ready to drop into
|
|
14
|
+
your assets folder.
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
No install needed — just run it with `npx`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd path/to/your/svgs
|
|
22
|
+
npx @marcovega/svg-tidy
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or install globally (the binary is `svg-tidy`):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @marcovega/svg-tidy
|
|
29
|
+
svg-tidy
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
svg-tidy [paths...] [options]
|
|
36
|
+
|
|
37
|
+
Arguments:
|
|
38
|
+
paths Files or directories to process. Defaults to the current
|
|
39
|
+
working directory.
|
|
40
|
+
|
|
41
|
+
Options:
|
|
42
|
+
-r, --recursive Recurse into subdirectories when a directory is given.
|
|
43
|
+
-n, --dry-run Show what would change without writing files.
|
|
44
|
+
-q, --quiet Only print the final summary.
|
|
45
|
+
--init Write a default .svgtidy.json (if missing) and exit.
|
|
46
|
+
--config PATH Use a specific config file. Defaults to ./.svgtidy.json.
|
|
47
|
+
-h, --help Show this help.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx @marcovega/svg-tidy # optimize every .svg in this dir
|
|
54
|
+
npx @marcovega/svg-tidy -r # ...recursively
|
|
55
|
+
npx @marcovega/svg-tidy ./icons logo.svg # specific paths
|
|
56
|
+
npx @marcovega/svg-tidy -n # preview savings, don't write
|
|
57
|
+
npx @marcovega/svg-tidy --init # just create .svgtidy.json and stop
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Each file is overwritten only when SVGO actually changes its contents.
|
|
61
|
+
|
|
62
|
+
## The `.svgtidy.json` config file
|
|
63
|
+
|
|
64
|
+
The first time you run `svg-tidy` in a directory it creates
|
|
65
|
+
`.svgtidy.json` next to your icons. The file uses the SVGOMG-style boolean
|
|
66
|
+
plugin map:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"multipass": true,
|
|
71
|
+
"js2svg": { "indent": 2, "pretty": false },
|
|
72
|
+
"plugins": {
|
|
73
|
+
"cleanupAttrs": true,
|
|
74
|
+
"cleanupIDs": true,
|
|
75
|
+
"cleanupListOfValues": false,
|
|
76
|
+
"removeViewBox": false,
|
|
77
|
+
"removeXMLNS": false,
|
|
78
|
+
"removeScriptElement": false,
|
|
79
|
+
"removeStyleElement": false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
To customize: edit the file, set a plugin to `false` to disable it, or pass
|
|
85
|
+
custom plugin params with an object value:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"plugins": {
|
|
90
|
+
"removeDesc": { "removeAny": true },
|
|
91
|
+
"cleanupIds": { "minify": false }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Commit `.svgtidy.json` to your repo so collaborators (and CI) get the same
|
|
97
|
+
optimizations every time someone runs `svg-tidy`.
|
|
98
|
+
|
|
99
|
+
### Notes on renamed SVGO plugins
|
|
100
|
+
|
|
101
|
+
SVGO renamed a couple plugins between the SVGOMG UI and modern SVGO. Both
|
|
102
|
+
old and new names are accepted in `.svgtidy.json`:
|
|
103
|
+
|
|
104
|
+
| SVGOMG name | SVGO v4 name |
|
|
105
|
+
| --------------------- | ----------------- |
|
|
106
|
+
| `cleanupIDs` | `cleanupIds` |
|
|
107
|
+
| `removeScriptElement` | `removeScripts` |
|
|
108
|
+
|
|
109
|
+
## How `npx` finds this package
|
|
110
|
+
|
|
111
|
+
`npx some-package` does one of three things, in order:
|
|
112
|
+
|
|
113
|
+
1. Runs `some-package` from the current project's `node_modules/.bin/`.
|
|
114
|
+
2. Runs it from your global `PATH`.
|
|
115
|
+
3. Downloads it from the npm registry to a temp cache and runs it.
|
|
116
|
+
|
|
117
|
+
Because `@marcovega/svg-tidy` is published to npm, option 3 just works — no
|
|
118
|
+
`npm link` required for end users.
|
|
119
|
+
|
|
120
|
+
## Local development
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git clone git@github.com:marcovega/svg-tidy.git
|
|
124
|
+
cd svg-tidy
|
|
125
|
+
npm install
|
|
126
|
+
npm link # makes `svg-tidy` available globally for testing
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
To remove the global symlink later: `npm unlink -g @marcovega/svg-tidy`.
|
|
130
|
+
|
|
131
|
+
## Updating SVGO
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm install svgo@latest
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
[MIT](./LICENSE)
|
package/bin/svg-tidy.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, writeFile, readdir, stat } from 'node:fs/promises';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { optimize } from 'svgo';
|
|
7
|
+
import {
|
|
8
|
+
CONFIG_FILENAME,
|
|
9
|
+
loadOrCreateConfig,
|
|
10
|
+
resolveConfigPath,
|
|
11
|
+
toSvgoOptions,
|
|
12
|
+
} from '../lib/config.js';
|
|
13
|
+
|
|
14
|
+
const HELP = `svg-tidy - optimize SVG files in place using SVGO
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
svg-tidy [paths...] [options]
|
|
18
|
+
|
|
19
|
+
Arguments:
|
|
20
|
+
paths Files or directories to process. Defaults to the current
|
|
21
|
+
working directory.
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
-r, --recursive Recurse into subdirectories when a directory is given.
|
|
25
|
+
-n, --dry-run Show what would change without writing files.
|
|
26
|
+
-q, --quiet Only print the final summary.
|
|
27
|
+
--init Write a default ${CONFIG_FILENAME} (if missing) and exit.
|
|
28
|
+
--config PATH Use a specific config file. Defaults to ./${CONFIG_FILENAME}.
|
|
29
|
+
-h, --help Show this help.
|
|
30
|
+
|
|
31
|
+
Config:
|
|
32
|
+
On first run in a directory svg-tidy writes ${CONFIG_FILENAME} with the
|
|
33
|
+
SVGOMG-style default plugin settings. Edit that file (or commit it) to
|
|
34
|
+
customize how SVGs are optimized. Subsequent runs reuse it.
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
svg-tidy # optimize every .svg in this dir
|
|
38
|
+
svg-tidy -r # ...recursively
|
|
39
|
+
svg-tidy ./icons logo.svg # specific paths
|
|
40
|
+
svg-tidy -n # preview savings, don't write
|
|
41
|
+
svg-tidy --init # just create the default config and stop
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const { values, positionals } = parseArgs({
|
|
45
|
+
allowPositionals: true,
|
|
46
|
+
options: {
|
|
47
|
+
recursive: { type: 'boolean', short: 'r', default: false },
|
|
48
|
+
'dry-run': { type: 'boolean', short: 'n', default: false },
|
|
49
|
+
quiet: { type: 'boolean', short: 'q', default: false },
|
|
50
|
+
init: { type: 'boolean', default: false },
|
|
51
|
+
config: { type: 'string' },
|
|
52
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (values.help) {
|
|
57
|
+
process.stdout.write(HELP);
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const recursive = values.recursive;
|
|
62
|
+
const dryRun = values['dry-run'];
|
|
63
|
+
const quiet = values.quiet;
|
|
64
|
+
const initOnly = values.init;
|
|
65
|
+
const targets = positionals.length > 0 ? positionals : [process.cwd()];
|
|
66
|
+
|
|
67
|
+
function formatBytes(n) {
|
|
68
|
+
if (n < 1024) return `${n} B`;
|
|
69
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
70
|
+
return `${(n / (1024 * 1024)).toFixed(2)} MB`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function pct(before, after) {
|
|
74
|
+
if (before === 0) return '0.0%';
|
|
75
|
+
return `${(((before - after) / before) * 100).toFixed(1)}%`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function collectSvgs(target) {
|
|
79
|
+
const resolved = path.resolve(target);
|
|
80
|
+
let entryStat;
|
|
81
|
+
try {
|
|
82
|
+
entryStat = await stat(resolved);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
throw new Error(`Cannot access ${target}: ${err.message}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (entryStat.isFile()) {
|
|
88
|
+
return resolved.toLowerCase().endsWith('.svg') ? [resolved] : [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!entryStat.isDirectory()) return [];
|
|
92
|
+
|
|
93
|
+
const files = [];
|
|
94
|
+
const entries = await readdir(resolved, { withFileTypes: true });
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
const full = path.join(resolved, entry.name);
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
if (recursive) files.push(...(await collectSvgs(full)));
|
|
99
|
+
} else if (entry.isFile() && entry.name.toLowerCase().endsWith('.svg')) {
|
|
100
|
+
files.push(full);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return files;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function processFile(file, svgoOptions) {
|
|
107
|
+
const original = await readFile(file, 'utf8');
|
|
108
|
+
const result = optimize(original, { path: file, ...svgoOptions });
|
|
109
|
+
|
|
110
|
+
if (result.error) throw new Error(result.error);
|
|
111
|
+
|
|
112
|
+
const optimized = result.data;
|
|
113
|
+
const beforeBytes = Buffer.byteLength(original, 'utf8');
|
|
114
|
+
const afterBytes = Buffer.byteLength(optimized, 'utf8');
|
|
115
|
+
const changed = optimized !== original;
|
|
116
|
+
|
|
117
|
+
if (changed && !dryRun) {
|
|
118
|
+
await writeFile(file, optimized, 'utf8');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { file, beforeBytes, afterBytes, changed };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function main() {
|
|
125
|
+
const cwd = process.cwd();
|
|
126
|
+
const configPath = resolveConfigPath(cwd, values.config);
|
|
127
|
+
|
|
128
|
+
const { config, created } = await loadOrCreateConfig(configPath);
|
|
129
|
+
const svgoOptions = toSvgoOptions(config);
|
|
130
|
+
const relConfig = path.relative(cwd, configPath) || configPath;
|
|
131
|
+
|
|
132
|
+
if (created) {
|
|
133
|
+
console.log(`Wrote default config to ${relConfig}.`);
|
|
134
|
+
console.log('Edit it to customize plugins, then re-run svg-tidy.');
|
|
135
|
+
} else if (!quiet) {
|
|
136
|
+
console.log(`Using config: ${relConfig}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (initOnly) {
|
|
140
|
+
if (!created) console.log(`${relConfig} already exists. No changes made.`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const allFiles = new Set();
|
|
145
|
+
for (const target of targets) {
|
|
146
|
+
const found = await collectSvgs(target);
|
|
147
|
+
for (const f of found) allFiles.add(f);
|
|
148
|
+
}
|
|
149
|
+
const files = [...allFiles].sort();
|
|
150
|
+
|
|
151
|
+
if (files.length === 0) {
|
|
152
|
+
console.error('No .svg files found.');
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let totalBefore = 0;
|
|
157
|
+
let totalAfter = 0;
|
|
158
|
+
let changedCount = 0;
|
|
159
|
+
let failedCount = 0;
|
|
160
|
+
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
try {
|
|
163
|
+
const r = await processFile(file, svgoOptions);
|
|
164
|
+
totalBefore += r.beforeBytes;
|
|
165
|
+
totalAfter += r.afterBytes;
|
|
166
|
+
if (r.changed) changedCount += 1;
|
|
167
|
+
if (!quiet) {
|
|
168
|
+
const rel = path.relative(cwd, r.file) || r.file;
|
|
169
|
+
const tag = r.changed
|
|
170
|
+
? `${formatBytes(r.beforeBytes)} -> ${formatBytes(r.afterBytes)} (${pct(r.beforeBytes, r.afterBytes)})`
|
|
171
|
+
: 'no change';
|
|
172
|
+
console.log(`${dryRun && r.changed ? '[dry] ' : ''}${rel}: ${tag}`);
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
failedCount += 1;
|
|
176
|
+
console.error(`FAILED ${file}: ${err.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const verb = dryRun ? 'Would optimize' : 'Optimized';
|
|
181
|
+
console.log(
|
|
182
|
+
`\n${verb} ${changedCount}/${files.length} file(s). ` +
|
|
183
|
+
`Total: ${formatBytes(totalBefore)} -> ${formatBytes(totalAfter)} ` +
|
|
184
|
+
`(${pct(totalBefore, totalAfter)} smaller)` +
|
|
185
|
+
(failedCount ? `. ${failedCount} failed.` : '.'),
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (failedCount > 0) process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
main().catch((err) => {
|
|
192
|
+
console.error(err.stack || err.message || err);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
});
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DEFAULT_CONFIG, PLUGIN_ALIASES } from './svgo-config.js';
|
|
4
|
+
|
|
5
|
+
export const CONFIG_FILENAME = '.svgtidy.json';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolve where the config file lives.
|
|
9
|
+
*
|
|
10
|
+
* - If `configPath` is provided, use it verbatim.
|
|
11
|
+
* - Otherwise look in `cwd` for `.svgtidy.json`.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveConfigPath(cwd, configPath) {
|
|
14
|
+
if (configPath) return path.resolve(cwd, configPath);
|
|
15
|
+
return path.join(cwd, CONFIG_FILENAME);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Load `.svgtidy.json` from disk. If it doesn't exist, write the default config
|
|
20
|
+
* to that path first.
|
|
21
|
+
*
|
|
22
|
+
* Returns `{ config, path, created }`.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadOrCreateConfig(configPath) {
|
|
25
|
+
let raw;
|
|
26
|
+
let created = false;
|
|
27
|
+
try {
|
|
28
|
+
raw = await readFile(configPath, 'utf8');
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.code !== 'ENOENT') throw err;
|
|
31
|
+
raw = JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n';
|
|
32
|
+
await writeFile(configPath, raw, 'utf8');
|
|
33
|
+
created = true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = JSON.parse(raw);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
throw new Error(`Invalid JSON in ${configPath}: ${err.message}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { config: parsed, path: configPath, created };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Convert a svg-tidy config (with a boolean/params plugin map) into the
|
|
48
|
+
* options object expected by SVGO's `optimize()` function.
|
|
49
|
+
*/
|
|
50
|
+
export function toSvgoOptions(config) {
|
|
51
|
+
if (!config || typeof config !== 'object') {
|
|
52
|
+
throw new Error('Config must be an object.');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const { multipass, js2svg, plugins: pluginMap } = config;
|
|
56
|
+
|
|
57
|
+
if (pluginMap && typeof pluginMap !== 'object') {
|
|
58
|
+
throw new Error('Config "plugins" must be an object map of name -> bool|params.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
const plugins = [];
|
|
63
|
+
|
|
64
|
+
for (const [rawName, value] of Object.entries(pluginMap || {})) {
|
|
65
|
+
if (value === false || value == null) continue;
|
|
66
|
+
|
|
67
|
+
const name = PLUGIN_ALIASES[rawName] || rawName;
|
|
68
|
+
if (seen.has(name)) continue;
|
|
69
|
+
seen.add(name);
|
|
70
|
+
|
|
71
|
+
if (value === true) {
|
|
72
|
+
plugins.push(name);
|
|
73
|
+
} else if (typeof value === 'object') {
|
|
74
|
+
plugins.push({ name, params: value });
|
|
75
|
+
} else {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Config plugin "${rawName}" must be a boolean or an object, got ${typeof value}.`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
multipass: Boolean(multipass),
|
|
84
|
+
js2svg: js2svg || undefined,
|
|
85
|
+
plugins,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default SVGO plugin configuration for svg-tidy.
|
|
3
|
+
*
|
|
4
|
+
* `DEFAULT_CONFIG.plugins` is a SVGOMG-style boolean map (the same shape used
|
|
5
|
+
* in the amazing-facts theme docs). Each key is a plugin name, the value is:
|
|
6
|
+
* - `true` -> enable the plugin with its default params
|
|
7
|
+
* - `false` -> disable it (omitted from SVGO's plugin list)
|
|
8
|
+
* - an `object` (params) -> enable with custom params
|
|
9
|
+
*
|
|
10
|
+
* The persisted `.svgtidy.json` file uses this exact shape so it's easy to
|
|
11
|
+
* read, edit, and diff.
|
|
12
|
+
*
|
|
13
|
+
* SVGO renamed two plugins between the SVGOMG UI and modern SVGO; both old and
|
|
14
|
+
* new names are accepted in the config file and normalized via PLUGIN_ALIASES.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export const PLUGIN_ALIASES = {
|
|
18
|
+
cleanupIDs: 'cleanupIds',
|
|
19
|
+
removeScriptElement: 'removeScripts',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const DEFAULT_CONFIG = {
|
|
23
|
+
multipass: true,
|
|
24
|
+
js2svg: {
|
|
25
|
+
indent: 2,
|
|
26
|
+
pretty: false,
|
|
27
|
+
},
|
|
28
|
+
plugins: {
|
|
29
|
+
cleanupAttrs: true,
|
|
30
|
+
cleanupEnableBackground: true,
|
|
31
|
+
cleanupIDs: true,
|
|
32
|
+
cleanupListOfValues: false,
|
|
33
|
+
cleanupNumericValues: true,
|
|
34
|
+
collapseGroups: true,
|
|
35
|
+
convertColors: true,
|
|
36
|
+
convertEllipseToCircle: true,
|
|
37
|
+
convertPathData: true,
|
|
38
|
+
convertShapeToPath: true,
|
|
39
|
+
convertStyleToAttrs: true,
|
|
40
|
+
convertTransform: true,
|
|
41
|
+
inlineStyles: true,
|
|
42
|
+
mergePaths: true,
|
|
43
|
+
mergeStyles: true,
|
|
44
|
+
minifyStyles: true,
|
|
45
|
+
moveElemsAttrsToGroup: true,
|
|
46
|
+
moveGroupAttrsToElems: true,
|
|
47
|
+
removeComments: true,
|
|
48
|
+
removeDesc: true,
|
|
49
|
+
removeDimensions: true,
|
|
50
|
+
removeDoctype: true,
|
|
51
|
+
removeEditorsNSData: true,
|
|
52
|
+
removeEmptyAttrs: true,
|
|
53
|
+
removeEmptyContainers: true,
|
|
54
|
+
removeEmptyText: true,
|
|
55
|
+
removeHiddenElems: true,
|
|
56
|
+
removeMetadata: true,
|
|
57
|
+
removeNonInheritableGroupAttrs: true,
|
|
58
|
+
removeRasterImages: true,
|
|
59
|
+
removeScriptElement: false,
|
|
60
|
+
removeStyleElement: false,
|
|
61
|
+
removeTitle: true,
|
|
62
|
+
removeUnknownsAndDefaults: true,
|
|
63
|
+
removeUnusedNS: true,
|
|
64
|
+
removeUselessDefs: true,
|
|
65
|
+
removeUselessStrokeAndFill: true,
|
|
66
|
+
removeViewBox: false,
|
|
67
|
+
removeXMLNS: false,
|
|
68
|
+
removeXMLProcInst: true,
|
|
69
|
+
reusePaths: false,
|
|
70
|
+
sortAttrs: false,
|
|
71
|
+
sortDefsChildren: true,
|
|
72
|
+
},
|
|
73
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@marcovega/svg-tidy",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Optimize SVG icons in a directory using SVGO. Writes a .svgtidy.json config on first run so you can customize and commit settings per project.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"svg-tidy": "bin/svg-tidy.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.3.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node bin/svg-tidy.js"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"svgo": "^4.0.1"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"svg",
|
|
26
|
+
"svgo",
|
|
27
|
+
"svgomg",
|
|
28
|
+
"optimize",
|
|
29
|
+
"minify",
|
|
30
|
+
"icons",
|
|
31
|
+
"cli",
|
|
32
|
+
"npx"
|
|
33
|
+
],
|
|
34
|
+
"author": "Marco Vega",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"homepage": "https://github.com/marcovega/svg-tidy#readme",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/marcovega/svg-tidy.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/marcovega/svg-tidy/issues"
|
|
43
|
+
}
|
|
44
|
+
}
|