|
| 1 | +--- |
| 2 | +title: Migration from v3 to v4 |
| 3 | +slug: 'migration-from-v3-to-v4' |
| 4 | +--- |
| 5 | + |
| 6 | +This is a summary of the changes necessary to migrate from SVGO v3 to SVGO v4. If you want more details or have any questions, please refer to our [release notes for SVGO v4.0.0](https://github.com/svg/svgo/releases/tag/v4.0.0) or related pull requests. You're also encouraged to leave comments in pull requests if the existing content doesn't already answer your question. |
| 7 | + |
| 8 | +## Version Requirements |
| 9 | + |
| 10 | +SVGO now requires [Node.js >=16.0.0](https://nodejs.org/en/blog/release/v16.0.0). |
| 11 | + |
| 12 | +## Default Behavior |
| 13 | + |
| 14 | +The following changes have been made to default plugins, also known as [preset-default](/docs/preset-default/). |
| 15 | + |
| 16 | +- **[removeViewBox](/docs/plugins/removeViewBox/)** is no longer a default plugin, to preserve scalability. |
| 17 | +- **[removeTitle](/docs/plugins/removeTitle)** is no longer a default plugin, to preserve accessibility. |
| 18 | +- **removeScriptElement** has been renamed to **[removeScripts](/docs/plugins/removeScripts)**, as it not only removes the `<script>` element, but also event handlers, and script URIs. |
| 19 | + |
| 20 | +To continue using removeViewBox or removeTitle, configure it in the SVGO config, see the [README](https://github.com/svg/svgo?tab=readme-ov-file#configuration) for more context, but please consider reading the warnings in the respective plugin's documentation first: |
| 21 | + |
| 22 | +```diff |
| 23 | + export default { |
| 24 | + plugins: [ |
| 25 | + 'preset-default', // built-in plugins enabled by default |
| 26 | ++ 'removeViewBox', |
| 27 | ++ 'removeTitle', |
| 28 | + ], |
| 29 | + }; |
| 30 | +``` |
| 31 | + |
| 32 | +If you were using the removeScriptElement plugin, amend your SVGO config to use removeScripts instead: |
| 33 | + |
| 34 | +```diff |
| 35 | + export default { |
| 36 | + plugins: [ |
| 37 | + 'preset-default', // built-in plugins enabled by default |
| 38 | +- 'removeScriptElement', |
| 39 | ++ 'removeScripts', |
| 40 | + ], |
| 41 | + }; |
| 42 | +``` |
| 43 | + |
| 44 | +## Public vs Internal API |
| 45 | + |
| 46 | +We now enforce a boundary between public and internal API. It's no longer possible to arbitrarily import any code declared by SVGO, but rather only the public API we declare as part of our semantic versioning. |
| 47 | + |
| 48 | +There are two ways to import SVGO: |
| 49 | + |
| 50 | +- `svgo` — for normal usage, such as scripts or server-side applications. |
| 51 | +- `svgo/browser` — for browser usage. |
| 52 | + |
| 53 | +### Browser Bundle |
| 54 | + |
| 55 | +If you use the browser bundle, you must amend how you import it: |
| 56 | + |
| 57 | +```diff |
| 58 | +- import { optimize } from 'svgo/dist/svgo.browser.js'; |
| 59 | ++ import { optimize } from 'svgo/browser'; |
| 60 | +``` |
| 61 | + |
| 62 | +### Importing Helpers or Plugins |
| 63 | + |
| 64 | +If you `import`/`require` helpers, the array of built-in plugins, or a single plugin during runtime, this is now a top-level export instead: |
| 65 | + |
| 66 | +```diff |
| 67 | +// import helpers |
| 68 | +- import { querySelector, querySelectorAll } from 'svgo/lib/xast.js'; |
| 69 | ++ import { querySelector, querySelectorAll } from 'svgo'; |
| 70 | + |
| 71 | +// import the array of built-in plugins |
| 72 | +- import { builtin } from 'svgo/lib/builtin'; |
| 73 | ++ import { builtinPlugins } from 'svgo' |
| 74 | + |
| 75 | +// getting a single plugin |
| 76 | +- import presetDefault from 'svgo/plugins/preset-default'; |
| 77 | ++ import { builtinPlugins } from 'svgo'; |
| 78 | ++ const prefixDefault = builtinPlugins.find(plugin => plugin.name === 'preset-default'); |
| 79 | + |
| 80 | +// getting a map of plugin names to implementations |
| 81 | +import { builtinPlugins } from 'svgo'; |
| 82 | +const pluginMap = builtinPlugins.reduce((acc, val) => acc.set(val.name, val), new Map()); |
| 83 | +``` |
| 84 | + |
| 85 | +### Selector Helpers |
| 86 | + |
| 87 | +Xast/CSS helpers for selecting nodes must be imported from `svgo` instead, and have different behavior. This effects custom plugins that use any of the following functions, where the `selector` (2nd) argument could reference parent or sibling nodes (i.e. `div > span`): |
| 88 | + |
| 89 | +- `querySelectorAll` |
| 90 | +- `querySelector` |
| 91 | +- `matches` |
| 92 | + |
| 93 | +If this applies to you, then you need to pass a `Map` of nodes to their parent node as a third argument. |
| 94 | + |
| 95 | +A helper has been provided named `#mapNodesToParents`, which does this for you. This can be used to easily migrate to the new API. If you're not sure if you need it, then it's safer to take this approach. The third argument won't be necessary if `selector` does not traverse nodes, for example querying using one or more attributes of a single node. |
| 96 | + |
| 97 | +```diff |
| 98 | +- import { querySelectorAll } from 'svgo'; |
| 99 | +- const nodes = querySelectorAll(childNode, selector); |
| 100 | + |
| 101 | ++ import { querySelectorAll, mapNodesToParents } from 'svgo'; |
| 102 | ++ const nodes = querySelectorAll(childNode, selector, mapNodesToParents(rootNode)); |
| 103 | +``` |
| 104 | + |
| 105 | +The new API for these functions are as follows: |
| 106 | + |
| 107 | +```js |
| 108 | +// applies `selector` with the context of the `childNode` and its descendants |
| 109 | +const nodes = querySelectorAll(childNode, selector); |
| 110 | + |
| 111 | +// applies `selector` with the context of the entire node tree relative from `childNode` |
| 112 | +// the `rootNode` is required if the result of `selector` may depend on the parent or sibling of `childNode` |
| 113 | +const nodes = querySelectorAll(childNode, selector, rootNode); |
| 114 | + |
| 115 | +// this usage has the same behavior as v3, as `rootNode` is already the entire node tree |
| 116 | +const nodes = querySelectorAll(rootNode, selector); |
| 117 | +``` |
| 118 | + |
| 119 | +## Named vs Default Exports |
| 120 | + |
| 121 | +SVGO now only has named exports, there are no default exports in the public API. |
| 122 | + |
| 123 | +If you use SVGO from the CLI, or have a Common JS project (you import SVGO using `require`), or if you were already using named exports, you don't have to do anything. |
| 124 | + |
| 125 | +If one way or another you're using the `import` keyword to import SVGO, then any instance where you do use our default export, it must be switched for the named equivilent. |
| 126 | + |
| 127 | +```diff |
| 128 | +- import svgo from 'svgo'; |
| 129 | +- svgo.optimize('<svg></svg>'); |
| 130 | + |
| 131 | +// Option 1. Use named exports! |
| 132 | ++ import { optimize } from 'svgo'; |
| 133 | ++ optimize('<svg></svg>'); |
| 134 | + |
| 135 | +// Option 2. Or import everything as svgo! |
| 136 | ++ import * as svgo from 'svgo'; |
| 137 | ++ svgo.optimize('<svg></svg>'); |
| 138 | +``` |
0 commit comments