svg-icon-baker 1.1.3 → 1.2.1

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 CHANGED
@@ -1,7 +1,3 @@
1
- # svg-icon-baker license
2
-
3
- svg-icon-baker is released under the MIT license:
4
-
5
1
  MIT License
6
2
 
7
3
  Copyright (c) 2025-present, yangxu52
@@ -23,38 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
21
  SOFTWARE.
26
-
27
- # Licenses of bundled dependencies
28
-
29
- The published svg-icon-baker artifact additionally contains code with the following licenses:
30
- MIT
31
-
32
- # Bundled dependencies:
33
-
34
- ## fast-glob
35
-
36
- License: MIT
37
- by: Kir Belevich
38
- Repository: https://github.com/svg/svgo
39
-
40
- > MIT License
41
- >
42
- > Copyright (c) Kir Belevich
43
- >
44
- > Permission is hereby granted, free of charge, to any person obtaining a copy
45
- > of this software and associated documentation files (the "Software"), to deal
46
- > in the Software without restriction, including without limitation the rights
47
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48
- > copies of the Software, and to permit persons to whom the Software is
49
- > furnished to do so, subject to the following conditions:
50
- >
51
- > The above copyright notice and this permission notice shall be included in all
52
- > copies or substantial portions of the Software.
53
- >
54
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
59
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
60
- > SOFTWARE.
package/README.md CHANGED
@@ -32,27 +32,25 @@ Convert multiple SVG inputs with one inferred option set.
32
32
 
33
33
  ## Options
34
34
 
35
- | name | type | default | description |
36
- | ------------------------- | --------- | ------- | -------------------------------------------------- |
37
- | `defaultPreset` | `boolean` | `true` | Enable SVGO `preset-default`. |
38
- | `convertOneStopGradients` | `boolean` | `false` | Convert one-stop gradients. |
39
- | `convertStyleToAttrs` | `boolean` | `false` | Convert style blocks to attributes. |
40
- | `reusePaths` | `boolean` | `false` | Try to reuse identical paths. |
41
- | `removeScripts` | `boolean` | `false` | Drop `<script>` for safety. |
42
- | `removeTitle` | `boolean` | `true` | Remove `<title>` elements from symbols. |
43
- | `removeXMLNS` | `boolean` | `true` | Remove xmlns on root when emitting sprite content. |
44
- | `removeXlink` | `boolean` | `true` | Remove xlink namespace and prefer `href`. |
35
+ | name | type | default | description |
36
+ | ------------- | ------------- | ------- | ------------------------------------------------ |
37
+ | `optimize` | `boolean` | `true` | Enable default safe SVGO optimization preset. |
38
+ | `svgoOptions` | `SvgoOptions` | `{}` | Custom SVGO options into the optimization layer. |
45
39
 
46
40
  Notes:
47
41
 
48
- - Set `true` to use the default optimization set.
49
- - Set `false` to disable optional optimizations. Mandatory conversion steps still run, including `removeDimensions`, `prefixIds`, and SVG-to-symbol rewriting.
50
- - The library prefixes internal ids and URL references via SVGO `prefixIds`, using the icon name as prefix, for example `home-a`.
51
- - Root `width` and `height` are removed. `viewBox` must already exist or be inferable from root dimensions.
42
+ - Behavior matrix:
43
+ - `optimize: true` + no `svgoOptions`: run default safe optimization.
44
+ - `optimize: true` + `svgoOptions`: run default safe optimization, then merge custom options.
45
+ - `optimize: false` + no `svgoOptions`: skip optimization layer.
46
+ - `optimize: false` + `svgoOptions`: run custom optimization settings only.
47
+ - When `svgoOptions.plugins` is provided, custom plugins run after default safe optimization plugins.
52
48
 
53
49
  ## Type Definitions
54
50
 
55
51
  ```ts
52
+ import type { Config } from 'svgo'
53
+
56
54
  type BakeSource = {
57
55
  name: string
58
56
  content: string
@@ -63,25 +61,19 @@ type BakeResult = {
63
61
  content: string
64
62
  }
65
63
 
66
- type Options =
67
- | {
68
- defaultPreset?: boolean
69
- convertOneStopGradients?: boolean
70
- convertStyleToAttrs?: boolean
71
- reusePaths?: boolean
72
- removeScripts?: boolean
73
- removeTitle?: boolean
74
- removeXMLNS?: boolean
75
- removeXlink?: boolean
76
- }
77
- | boolean
64
+ type SvgoOptions = Pick<Config, 'multipass' | 'floatPrecision' | 'js2svg' | 'plugins'>
65
+
66
+ type Options = {
67
+ optimize?: boolean
68
+ svgoOptions?: SvgoOptions
69
+ }
78
70
  ```
79
71
 
80
72
  ## Features
81
73
 
82
- - 🎯 Optimization: Reduce file size, and improve efficiency through `SVGO`
83
- - 🔗 Reference Handling: ID and reference prefixing for sprite safety
84
- - 🎨 Size Unify: `viewBox` preservation or inference from root dimensions
74
+ - Optimization: Reduce file size, and improve efficiency through `SVGO`
75
+ - Reference Handling: ID and reference prefixing for sprite safety
76
+ - Size Unify: `viewBox` preservation or inference from root dimensions
85
77
 
86
78
  ## License
87
79
 
package/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Config } from 'svgo';
2
+
1
3
  type BakeSource = {
2
4
  name: string;
3
5
  content: string;
@@ -6,52 +8,21 @@ type BakeResult = {
6
8
  name: string;
7
9
  content: string;
8
10
  };
9
- type ManualOptions = {
10
- /**
11
- * default plugin preset
12
- * @default true
13
- */
14
- defaultPreset?: boolean;
15
- /**
16
- * convert one stop gradients to inline styles
17
- * @default false
18
- */
19
- convertOneStopGradients?: boolean;
20
- /**
21
- * convert style to attrs
22
- * @default false
23
- */
24
- convertStyleToAttrs?: boolean;
11
+ type SvgoOptions = Pick<Config, 'multipass' | 'floatPrecision' | 'js2svg' | 'plugins'>;
12
+ type Options = {
25
13
  /**
26
- * reuse paths
27
- * @default false
28
- */
29
- reusePaths?: boolean;
30
- /**
31
- * remove scripts
32
- * @default false
33
- */
34
- removeScripts?: boolean;
35
- /**
36
- * remove title
14
+ * enable/disable default safe optimization preset
37
15
  * @default true
38
16
  */
39
- removeTitle?: boolean;
17
+ optimize?: boolean;
40
18
  /**
41
- * remove xmlns
42
- * @default true
43
- */
44
- removeXMLNS?: boolean;
45
- /**
46
- * remove xlink
47
- * @default true
19
+ * custom svgo options merged into optimizer
48
20
  */
49
- removeXlink?: boolean;
21
+ svgoOptions?: SvgoOptions;
50
22
  };
51
- type Options = boolean | ManualOptions;
52
23
 
53
24
  declare function bakeIcon(source: BakeSource, options?: Options): BakeResult;
54
25
  declare function bakeIcons(sources: BakeSource[], options?: Options): BakeResult[];
55
26
 
56
27
  export { bakeIcon, bakeIcons };
57
- export type { BakeResult, BakeSource, Options };
28
+ export type { BakeResult, BakeSource, Options, SvgoOptions };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Config } from 'svgo';
2
+
1
3
  type BakeSource = {
2
4
  name: string;
3
5
  content: string;
@@ -6,52 +8,21 @@ type BakeResult = {
6
8
  name: string;
7
9
  content: string;
8
10
  };
9
- type ManualOptions = {
10
- /**
11
- * default plugin preset
12
- * @default true
13
- */
14
- defaultPreset?: boolean;
15
- /**
16
- * convert one stop gradients to inline styles
17
- * @default false
18
- */
19
- convertOneStopGradients?: boolean;
20
- /**
21
- * convert style to attrs
22
- * @default false
23
- */
24
- convertStyleToAttrs?: boolean;
11
+ type SvgoOptions = Pick<Config, 'multipass' | 'floatPrecision' | 'js2svg' | 'plugins'>;
12
+ type Options = {
25
13
  /**
26
- * reuse paths
27
- * @default false
28
- */
29
- reusePaths?: boolean;
30
- /**
31
- * remove scripts
32
- * @default false
33
- */
34
- removeScripts?: boolean;
35
- /**
36
- * remove title
14
+ * enable/disable default safe optimization preset
37
15
  * @default true
38
16
  */
39
- removeTitle?: boolean;
17
+ optimize?: boolean;
40
18
  /**
41
- * remove xmlns
42
- * @default true
43
- */
44
- removeXMLNS?: boolean;
45
- /**
46
- * remove xlink
47
- * @default true
19
+ * custom svgo options merged into optimizer
48
20
  */
49
- removeXlink?: boolean;
21
+ svgoOptions?: SvgoOptions;
50
22
  };
51
- type Options = boolean | ManualOptions;
52
23
 
53
24
  declare function bakeIcon(source: BakeSource, options?: Options): BakeResult;
54
25
  declare function bakeIcons(sources: BakeSource[], options?: Options): BakeResult[];
55
26
 
56
27
  export { bakeIcon, bakeIcons };
57
- export type { BakeResult, BakeSource, Options };
28
+ export type { BakeResult, BakeSource, Options, SvgoOptions };
package/dist/index.mjs CHANGED
@@ -1 +1,113 @@
1
- import{optimize as m}from"svgo";function c(e,r){const t=i(r),n=l(t);return{name:e.name,content:o(e,n)}}function p(e,r){const t=i(r),n=l(t);return e.map(s=>({name:s.name,content:o(s,n)}))}function o(e,r){if(!e||!e.name||!e.content)throw new TypeError("Property name and content are required.");if(!/^[A-Za-z][A-Za-z0-9_-]*$/.test(e.name))throw new TypeError("Invalid name. Use letters, numbers, dash, or underscore, starting with a letter.");r.push({name:"prefixIds",params:{prefix:`${e.name}-`,delim:""}});let t;try{t=m(e.content,{plugins:r})}catch(a){throw new Error(`Parsing failed. ${String(a)}`)}const n=t.data.match(/viewBox="([^"]+)"/)?.[1];if(!n)throw new Error("Cannot determine viewBox. Provide an SVG with viewBox or width/height attributes.");const s=t.data.replace(/^\s*<\?xml[^>]*\?>\s*/i,"");return u(s,e.name,n)}function u(e,r,t){const n=e.match(/^\s*<svg\b[^>]*>/i)[0].replace(/^\s*<svg\b/i,"").replace(/>\s*$/i,"").replace(/\s+id=(['"])[^'"]*\1/gi,"").replace(/\s+viewBox=(['"])[^'"]*\1/gi,"").replace(/\s+width=(['"])[^'"]*\1/gi,"").replace(/\s+height=(['"])[^'"]*\1/gi,"").trim(),s=n?` ${n}`:"",a=`<symbol id="${r}" viewBox="${t}"${s}>`;return e.replace(/^\s*<svg\b[^>]*>/i,a).replace(/<\/svg>\s*$/i,"</symbol>").trim()}function i(e){const r={defaultPreset:!0,convertOneStopGradients:!1,convertStyleToAttrs:!1,reusePaths:!1,removeScripts:!1,removeTitle:!0,removeXMLNS:!0,removeXlink:!0};return typeof e=="boolean"?e?r:{defaultPreset:!1,convertOneStopGradients:!1,convertStyleToAttrs:!1,reusePaths:!1,removeScripts:!1,removeTitle:!1,removeXMLNS:!1,removeXlink:!1}:{...r,...e||{}}}function l(e){const r=[];return e.defaultPreset&&r.push({name:"preset-default",params:{overrides:{removeUselessDefs:!1,removeHiddenElems:!1,removeUnknownsAndDefaults:!1,collapseGroups:!1,mergePaths:!1,convertShapeToPath:!1}}}),e.convertOneStopGradients&&r.push({name:"convertOneStopGradients"}),e.convertStyleToAttrs&&r.push({name:"convertStyleToAttrs"}),e.reusePaths&&r.push({name:"reusePaths"}),e.removeScripts&&r.push({name:"removeScripts"}),e.removeTitle&&r.push({name:"removeTitle"}),e.removeXMLNS&&r.push({name:"removeXMLNS"}),e.removeXlink&&r.push({name:"removeXlink"}),r.push({name:"removeDimensions"}),r}export{c as bakeIcon,p as bakeIcons};
1
+ import { optimize } from 'svgo';
2
+
3
+ const DEFAULT_SAFE_PRESET = {
4
+ name: "preset-default",
5
+ params: {
6
+ overrides: {
7
+ removeUselessDefs: false,
8
+ removeHiddenElems: false,
9
+ removeUnknownsAndDefaults: false,
10
+ collapseGroups: false,
11
+ mergePaths: false,
12
+ convertShapeToPath: false
13
+ }
14
+ }
15
+ };
16
+ const DEFAULT_SAFE_PLUGINS = [{ name: "removeTitle" }, { name: "removeXMLNS" }, { name: "removeXlink" }];
17
+ const CORE_PLUGIN_BLOCKLIST = /* @__PURE__ */ new Set(["prefixIds"]);
18
+ function resolveOptions(userOption) {
19
+ const userObject = userOption ?? {};
20
+ return {
21
+ optimize: userObject.optimize ?? true,
22
+ svgoOptions: userObject.svgoOptions ?? {}
23
+ };
24
+ }
25
+ function createSvgoConfig(sourceName, options) {
26
+ const plugins = [];
27
+ if (options.optimize) {
28
+ plugins.push(DEFAULT_SAFE_PRESET);
29
+ plugins.push(...DEFAULT_SAFE_PLUGINS);
30
+ }
31
+ if (options.svgoOptions.plugins != null) {
32
+ plugins.push(...filterPlugins(options.svgoOptions.plugins));
33
+ }
34
+ plugins.push(...createCorePlugins(sourceName));
35
+ return {
36
+ multipass: options.svgoOptions.multipass,
37
+ floatPrecision: options.svgoOptions.floatPrecision,
38
+ js2svg: options.svgoOptions.js2svg,
39
+ plugins
40
+ };
41
+ }
42
+ function filterPlugins(plugins) {
43
+ return plugins.filter((plugin) => {
44
+ const name = resolvePluginName(plugin);
45
+ if (name == null) {
46
+ return true;
47
+ }
48
+ return !CORE_PLUGIN_BLOCKLIST.has(name);
49
+ });
50
+ }
51
+ function resolvePluginName(plugin) {
52
+ if (typeof plugin === "string") {
53
+ return plugin;
54
+ }
55
+ if (plugin && typeof plugin === "object" && "name" in plugin && typeof plugin.name === "string") {
56
+ return plugin.name;
57
+ }
58
+ return null;
59
+ }
60
+ function createCorePlugins(sourceName) {
61
+ return [{ name: "removeDimensions" }, { name: "prefixIds", params: { prefix: `${sourceName}-`, delim: "" } }];
62
+ }
63
+
64
+ function bakeIcon(source, options) {
65
+ const resolvedOptions = resolveOptions(options);
66
+ return {
67
+ name: source.name,
68
+ content: convertToSymbol(source, resolvedOptions)
69
+ };
70
+ }
71
+ function bakeIcons(sources, options) {
72
+ const resolvedOptions = resolveOptions(options);
73
+ return sources.map((source) => ({
74
+ name: source.name,
75
+ content: convertToSymbol(source, resolvedOptions)
76
+ }));
77
+ }
78
+ function convertToSymbol(source, options) {
79
+ if (!source || !source.name || !source.content) {
80
+ throw new TypeError("Property name and content are required.");
81
+ }
82
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(source.name)) {
83
+ throw new TypeError("Invalid name. Use letters, numbers, dash, or underscore, starting with a letter.");
84
+ }
85
+ const normalizedSource = stripLeadingSvgPreamble(source.content);
86
+ if (!/^\s*<svg\b/i.test(normalizedSource)) {
87
+ throw new Error("Parsing failed. Input must start with an <svg> root element.");
88
+ }
89
+ let result;
90
+ try {
91
+ result = optimize(normalizedSource, createSvgoConfig(source.name, options));
92
+ } catch (err) {
93
+ throw new Error(`Parsing failed. ${String(err)}`);
94
+ }
95
+ const viewBox = result.data.match(/viewBox="([^"]+)"/)?.[1];
96
+ if (!viewBox) {
97
+ throw new Error("Cannot determine viewBox. Provide an SVG with viewBox or width/height attributes.");
98
+ }
99
+ const cleanedSvg = stripLeadingSvgPreamble(result.data);
100
+ return toSymbolRootTag(cleanedSvg, source.name, viewBox);
101
+ }
102
+ function stripLeadingSvgPreamble(content) {
103
+ return content.replace(/^(?:\uFEFF|\s|<\?xml[\s\S]*?\?>|<!--[\s\S]*?-->|<!DOCTYPE[\s\S]*?>)+/i, "");
104
+ }
105
+ function toSymbolRootTag(svg, symbolId, viewBox) {
106
+ const rootOpenTag = svg.match(/^\s*<svg\b[^>]*>/i)[0];
107
+ const preservedAttrs = rootOpenTag.replace(/^\s*<svg\b/i, "").replace(/>\s*$/i, "").replace(/\s+id=(['"])[^'"]*\1/gi, "").replace(/\s+viewBox=(['"])[^'"]*\1/gi, "").replace(/\s+width=(['"])[^'"]*\1/gi, "").replace(/\s+height=(['"])[^'"]*\1/gi, "").trim();
108
+ const attrs = preservedAttrs ? ` ${preservedAttrs}` : "";
109
+ const symbolOpenTag = `<symbol id="${symbolId}" viewBox="${viewBox}"${attrs}>`;
110
+ return svg.replace(/^\s*<svg\b[^>]*>/i, symbolOpenTag).replace(/<\/svg>\s*$/i, "</symbol>").trim();
111
+ }
112
+
113
+ export { bakeIcon, bakeIcons };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svg-icon-baker",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "A delightful toolkit for baking raw SVG icons into delicious SVG symbol sprites",
5
5
  "keywords": [
6
6
  "svg",
@@ -17,7 +17,8 @@
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/yangxu52/svg-icon-baker.git"
20
+ "url": "git+https://github.com/yangxu52/vite-plugin-svg-icons-ng.git",
21
+ "directory": "packages/svg-icon-baker"
21
22
  },
22
23
  "license": "MIT",
23
24
  "author": "yangxu52",
@@ -40,17 +41,19 @@
40
41
  },
41
42
  "devDependencies": {
42
43
  "@vitest/coverage-v8": "^4.1.0",
44
+ "rimraf": "^6.1.3",
43
45
  "unbuild": "^3.6.1",
44
46
  "vitest": "^4.1.0"
45
47
  },
46
48
  "engines": {
47
- "node": "^20.0.0 || ^22.0.0 || >=24.0.0",
48
- "pnpm": ">=10.0.0"
49
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
49
50
  },
50
51
  "scripts": {
51
- "build": "unbuild",
52
52
  "dev": "unbuild --stub",
53
- "test": "vitest run --coverage",
54
- "test:watch": "vitest"
53
+ "build": "unbuild",
54
+ "test": "vitest run",
55
+ "coverage": "vitest run --coverage",
56
+ "clean": "rimraf dist",
57
+ "log": "conventional-changelog -p angular --commit-path . -t svg-icon-baker@v"
55
58
  }
56
59
  }