code:
'use strict';
const parser = require('postcss-selector-parser');
const exists = require('../exists');
const isMixin = require('../isMixin');
const BasePlugin = require('../plugin');
const { IE_5_5, IE_6 } = require('../dictionary/browsers');
const { SELECTOR } = require('../dictionary/identifiers');
const { RULE } = require('../dictionary/postcss');
const { HTML } = require('../dictionary/tags');

module.exports = class StarHtml extends BasePlugin {
  /** @param {import('postcss').Result=} result */
  constructor(result) {
    super([IE_5_5, IE_6], [RULE], result);
  }

  /**
   * @param {import('postcss').Rule} rule
   * @return {void}
   */
  detect(rule) {
    if (isMixin(rule)) {
      return;
    }
    parser(this.analyse(rule)).processSync(rule.selector);
  }

  /**
   * @param {import('postcss').Rule} rule
   * @return {parser.SyncProcessor<void>}
   */
  analyse(rule) {
    return (selectors) => {
      selectors.each((selector) => {
        if (
          exists(selector, 0, '*') &&
          exists(selector, 1, ' ') &&
          exists(selector, 2, HTML) &&
          exists(selector, 3, ' ') &&
          selector.at(4)
        ) {
          this.push(rule, {
            identifier: SELECTOR,
            hack: selector.toString(),
          });
        }
      });
    };
  }
};


code:
'use strict';
const BasePlugin = require('../plugin');
const isMixin = require('../isMixin');
const { IE_5_5, IE_6, IE_7 } = require('../dictionary/browsers');
const { SELECTOR } = require('../dictionary/identifiers');
const { RULE } = require('../dictionary/postcss');

module.exports = class TrailingSlashComma extends BasePlugin {
  /** @param {import('postcss').Result=} result */
  constructor(result) {
    super([IE_5_5, IE_6, IE_7], [RULE], result);
  }

  /**
   * @param {import('postcss').Rule} rule
   * @return {void}
   */
  detect(rule) {
    if (isMixin(rule)) {
      return;
    }

    const { selector } = rule;
    const trim = selector.trim();

    if (
      trim.lastIndexOf(',') === selector.length - 1 ||
      trim.lastIndexOf('\\') === selector.length - 1
    ) {
      this.push(rule, {
        identifier: SELECTOR,
        hack: selector,
      });
    }
  }
};

license: mit


code:
export const FF_2: "firefox 2";
export const IE_5_5: "ie 5.5";
export const IE_6: "ie 6";
export const IE_7: "ie 7";
export const IE_8: "ie 8";
export const OP_9: "opera 9";


code:
export const MEDIA_QUERY: "media query";
export const PROPERTY: "property";
export const SELECTOR: "selector";
export const VALUE: "value";


code:
export const ATRULE: "atrule";
export const DECL: "decl";
export const RULE: "rule";


code:
export const BODY: "body";
export const HTML: "html";


code:
declare function _exports(selector: import('postcss-selector-parser').Selector, index: number, value: string): boolean | undefined | '';
export = _exports;


code:
export = pluginCreator;
/** @typedef {{lint?: boolean}} Options */
/**
 * @type {import('postcss').PluginCreator<Options>}
 * @param {Options} opts
 * @return {import('postcss').Plugin}
 */
declare function pluginCreator(opts?: Options): import('postcss').Plugin;
declare namespace pluginCreator {
    export { detect, postcss, Options };
}
type Options = {
    lint?: boolean;
};
declare function detect(node: import('postcss').Node): boolean;
declare var postcss: true;


code:
declare function _exports(node: import('postcss').Rule): boolean;
export = _exports;


code:
export = BasePlugin;
declare class BasePlugin {
    /**
     * @param {string[]} targets
     * @param {string[]} nodeTypes
     * @param {import('postcss').Result=} result
     */
    constructor(targets: string[], nodeTypes: string[], result?: import('postcss').Result | undefined);
    /** @type {NodeWithInfo[]} */
    nodes: NodeWithInfo[];
    targets: Set<string>;
    nodeTypes: Set<string>;
    result: import("postcss").Result | undefined;
    /**
     * @param {import('postcss').Node} node
     * @param {{identifier: string, hack: string}} metadata
     * @return {void}
     */
    push(node: import('postcss').Node, metadata: {
        identifier: string;
        hack: string;
    }): void;
    /**
     * @param {import('postcss').Node} node
     * @return {boolean}
     */
    any(node: import('postcss').Node): boolean;
    /**
     * @param {import('postcss').Node} node
     * @return {void}
     */
    detectAndResolve(node: import('postcss').Node): void;
    /**
     * @param {import('postcss').Node} node
     * @return {void}
     */
    detectAndWarn(node: import('postcss').Node): void;
    /** @param {import('postcss').Node} node */
    detect(node: import('postcss').Node): void;
    /** @return {void} */
    resolve(): void;
    warn(): void;
}
declare namespace BasePlugin {
    export { Plugin, NodeWithInfo };
}
type NodeWithInfo = import('postcss').Node & {
    _stylehacks: {
        message: string;
        browsers: Set<string>;
        identifier: string;
        hack: string;
    };
};
type Plugin = {
    targets: Set<string>;
    nodeTypes: Set<string>;
    detectAndResolve: (node: import('postcss').Node) => void;
    detectAndWarn: (node: import('postcss').Node) => void;
};


code:
export = BodyEmpty;
declare class BodyEmpty extends BasePlugin {
    /** @param {import('postcss').Result} result */
    constructor(result: import('postcss').Result);
    /**
     * @param {import('postcss').Rule} rule
     * @return {void}
     */
    detect(rule: import('postcss').Rule): void;
    /**
     * @param {import('postcss').Rule} rule
     * @return {parser.SyncProcessor<void>}
     */
    analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
}
import BasePlugin = require("../plugin");
import parser = require("postcss-selector-parser");


code:
export = HtmlCombinatorCommentBody;
declare class HtmlCombinatorCommentBody extends BasePlugin {
    /** @param {import('postcss').Result} result */
    constructor(result: import('postcss').Result);
    /**
     * @param {import('postcss').Rule} rule
     * @return {void}
     */
    detect(rule: import('postcss').Rule): void;
    /** @param {import('postcss').Rule} rule
     *  @return {parser.SyncProcessor<void>}
     */
    analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
}
import BasePlugin = require("../plugin");
import parser = require("postcss-selector-parser");


code:
export = HtmlFirstChild;
declare class HtmlFirstChild extends BasePlugin {
    /** @param {import('postcss').Result} result */
    constructor(result: import('postcss').Result);
    /**
     * @param {import('postcss').Rule} rule
     * @return {void}
     */
    detect(rule: import('postcss').Rule): void;
    /**
     * @param {import('postcss').Rule} rule
     * @return {parser.SyncProcessor<void>}
     */
    analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
}
import BasePlugin = require("../plugin");
import parser = require("postcss-selector-parser");


code:
export = Important;
declare class Important extends BasePlugin {
    /** @param {import('postcss').Result=} result */
    constructor(result?: import('postcss').Result | undefined);
    /**
     * @param {import('postcss').Declaration} decl
     * @return {void}
     */
    detect(decl: import('postcss').Declaration): void;
}
import BasePlugin = require("../plugin");


code:
declare const _exports: ({
    new (result?: import("postcss").Result | undefined): important;
} | {
    new (result?: import("postcss").Result | undefined): trailingSlashComma;
})[];
export = _exports;


code:
export = LeadingStar;
declare class LeadingStar extends BasePlugin {
    /** @param {import('postcss').Result=} result */
    constructor(result?: import('postcss').Result | undefined);
    /**
     * @param {import('postcss').Declaration | import('postcss').AtRule} node
     * @return {void}
     */
    detect(node: import('postcss').Declaration | import('postcss').AtRule): void;
}
import BasePlugin = require("../plugin");


code:
export = LeadingUnderscore;
declare class LeadingUnderscore extends BasePlugin {
    /** @param {import('postcss').Result=} result */
    constructor(result?: import('postcss').Result | undefined);
    /**
     * @param {import('postcss').Declaration} decl
     * @return {void}
     */
    detect(decl: import('postcss').Declaration): void;
}
import BasePlugin = require("../plugin");


code:
export = MediaSlash0;
declare class MediaSlash0 extends BasePlugin {
    /** @param {import('postcss').Result} result */
    constructor(result: import('postcss').Result);
    /**
     * @param {import('postcss').AtRule} rule
     * @return {void}
     */
    detect(rule: import('postcss').AtRule): void;
}
import BasePlugin = require("../plugin");


code:
export = MediaSlash0Slash9;
declare class MediaSlash0Slash9 extends BasePlugin {
    /** @param {import('postcss').Result} result */
    constructor(result: import('postcss').Result);
    /**
     * @param {import('postcss').AtRule} rule
     * @return {void}
     */
    detect(rule: import('postcss').AtRule): void;
}
import BasePlugin = require("../plugin");


code:
export = MediaSlash9;
declare class MediaSlash9 extends BasePlugin {
    /** @param {import('postcss').Result} result */
    constructor(result: import('postcss').Result);
    /**
     * @param {import('postcss').AtRule} rule
     * @return {void}
     */
    detect(rule: import('postcss').AtRule): void;
}
import BasePlugin = require("../plugin");


code:
export = Slash9;
declare class Slash9 extends BasePlugin {
    /** @param {import('postcss').Result=} result */
    constructor(result?: import('postcss').Result | undefined);
    /**
     * @param {import('postcss').Declaration} decl
     * @return {void}
     */
    detect(decl: import('postcss').Declaration): void;
}
import BasePlugin = require("../plugin.js");


code:
export = StarHtml;
declare class StarHtml extends BasePlugin {
    /** @param {import('postcss').Result=} result */
    constructor(result?: import('postcss').Result | undefined);
    /**
     * @param {import('postcss').Rule} rule
     * @return {void}
     */
    detect(rule: import('postcss').Rule): void;
    /**
     * @param {import('postcss').Rule} rule
     * @return {parser.SyncProcessor<void>}
     */
    analyse(rule: import('postcss').Rule): parser.SyncProcessor<void>;
}
import BasePlugin = require("../plugin");
import parser = require("postcss-selector-parser");


code:
export = TrailingSlashComma;
declare class TrailingSlashComma extends BasePlugin {
    /** @param {import('postcss').Result=} result */
    constructor(result?: import('postcss').Result | undefined);
    /**
     * @param {import('postcss').Rule} rule
     * @return {void}
     */
    detect(rule: import('postcss').Rule): void;
}
import BasePlugin = require("../plugin");


code:
'use strict';
module.exports = {
	stdout: false,
	stderr: false
};


code:
'use strict';
const os = require('os');
const hasFlag = require('has-flag');

const env = process.env;

let forceColor;
if (hasFlag('no-color') ||
	hasFlag('no-colors') ||
	hasFlag('color=false')) {
	forceColor = false;
} else if (hasFlag('color') ||
	hasFlag('colors') ||
	hasFlag('color=true') ||
	hasFlag('color=always')) {
	forceColor = true;
}
if ('FORCE_COLOR' in env) {
	forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0;
}

function translateLevel(level) {
	if (level === 0) {
		return false;
	}

	return {
		level,
		hasBasic: true,
		has256: level >= 2,
		has16m: level >= 3
	};
}

function supportsColor(stream) {
	if (forceColor === false) {
		return 0;
	}

	if (hasFlag('color=16m') ||
		hasFlag('color=full') ||
		hasFlag('color=truecolor')) {
		return 3;
	}

	if (hasFlag('color=256')) {
		return 2;
	}

	if (stream && !stream.isTTY && forceColor !== true) {
		return 0;
	}

	const min = forceColor ? 1 : 0;

	if (process.platform === 'win32') {
		// Node.js 7.5.0 is the first version of Node.js to include a patch to
		// libuv that enables 256 color output on Windows. Anything earlier and it
		// won't work. However, here we target Node.js 8 at minimum as it is an LTS
		// release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows
		// release that supports 256 colors. Windows 10 build 14931 is the first release
		// that supports 16m/TrueColor.
		const osRelease = os.release().split('.');
		if (
			Number(process.versions.node.split('.')[0]) >= 8 &&
			Number(osRelease[0]) >= 10 &&
			Number(osRelease[2]) >= 10586
		) {
			return Number(osRelease[2]) >= 14931 ? 3 : 2;
		}

		return 1;
	}

	if ('CI' in env) {
		if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
			return 1;
		}

		return min;
	}

	if ('TEAMCITY_VERSION' in env) {
		return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
	}

	if (env.COLORTERM === 'truecolor') {
		return 3;
	}

	if ('TERM_PROGRAM' in env) {
		const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);

		switch (env.TERM_PROGRAM) {
			case 'iTerm.app':
				return version >= 3 ? 3 : 2;
			case 'Apple_Terminal':
				return 2;
			// No default
		}
	}

	if (/-256(color)?$/i.test(env.TERM)) {
		return 2;
	}

	if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
		return 1;
	}

	if ('COLORTERM' in env) {
		return 1;
	}

	if (env.TERM === 'dumb') {
		return min;
	}

	return min;
}

function getSupportLevel(stream) {
	const level = supportsColor(stream);
	return translateLevel(level);
}

module.exports = {
	supportsColor: getSupportLevel,
	stdout: getSupportLevel(process.stdout),
	stderr: getSupportLevel(process.stderr)
};


supports-color

description: Detect whether a terminal supports color

license: MIT

repository: chalk/supports-color

test: xo && ava

keywords: color colour colors terminal console cli ansi styles tty rgb 256 shell xterm command-line support supports capability detect truecolor 16m


code:
# supports-color [![Build Status](https://travis-ci.org/chalk/supports-color.svg?branch=master)](https://travis-ci.org/chalk/supports-color)

> Detect whether a terminal supports color

## Install

```
$ npm install supports-color
```

## Usage

```js
const supportsColor = require('supports-color');

if (supportsColor.stdout) {
	console.log('Terminal stdout supports color');
}

if (supportsColor.stdout.has256) {
	console.log('Terminal stdout supports 256 colors');
}

if (supportsColor.stderr.has16m) {
	console.log('Terminal stderr supports 16 million colors (truecolor)');
}
```

## API

Returns an `Object` with a `stdout` and `stderr` property for testing either streams. Each property is an `Object`, or `false` if color is not supported.

The `stdout`/`stderr` objects specifies a level of support for color through a `.level` property and a corresponding flag:

- `.level = 1` and `.hasBasic = true`: Basic color support (16 colors)
- `.level = 2` and `.has256 = true`: 256 color support
- `.level = 3` and `.has16m = true`: Truecolor support (16 million colors)

## Info

It obeys the `--color` and `--no-color` CLI flags.

Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, add the environment variable `FORCE_COLOR=1` to forcefully enable color or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks.

Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively.

## Related

- [supports-color-cli](https://github.com/chalk/supports-color-cli) - CLI for this module
- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right

## Maintainers

- [Sindre Sorhus](https://github.com/sindresorhus)
- [Josh Junon](https://github.com/qix-)

## License

MIT


code:
{
	"root": true,

	"extends": "@ljharb",

	"env": {
		"browser": true,
		"node": true,
	},

	"rules": {
		"id-length": "off",
	},
}


code:
# These are supported funding model platforms

github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/supports-preserve-symlink-flag
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


reporter: text-summary text html json

exclude: coverage test


code:
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v1.0.0 - 2022-01-02

### Commits

- Tests [`e2f59ad`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/e2f59ad74e2ae0f5f4899fcde6a6f693ab7cc074)
- Initial commit [`dc222aa`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/dc222aad3c0b940d8d3af1ca9937d108bd2dc4b9)
- [meta] do not publish workflow files [`5ef77f7`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/5ef77f7cb6946d16ee38672be9ec0f1bbdf63262)
- npm init [`992b068`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/992b068503a461f7e8676f40ca2aab255fd8d6ff)
- read me [`6c9afa9`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/6c9afa9fabc8eaf0814aaed6dd01e6df0931b76d)
- Initial implementation [`2f98925`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/2f9892546396d4ab0ad9f1ff83e76c3f01234ae8)
- [meta] add `auto-changelog` [`6c476ae`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/6c476ae1ed7ce68b0480344f090ac2844f35509d)
- [Dev Deps] add `eslint`, `@ljharb/eslint-config` [`d0fffc8`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/d0fffc886d25fba119355520750a909d64da0087)
- Only apps should have lockfiles [`ab318ed`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/ab318ed7ae62f6c2c0e80a50398d40912afd8f69)
- [meta] add `safe-publish-latest` [`2bb23b3`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/2bb23b3ebab02dc4135c4cdf0217db82835b9fca)
- [meta] add `sideEffects` flag [`600223b`](https://github.com/inspect-js/node-supports-preserve-symlinks-flag/commit/600223ba24f30779f209d9097721eff35ed62741)


code:
# node-supports-preserve-symlinks-flag <sup>[![Version Badge][npm-version-svg]][package-url]</sup>

[![github actions][actions-image]][actions-url]
[![coverage][codecov-image]][codecov-url]
[![dependency status][deps-svg]][deps-url]
[![dev dependency status][dev-deps-svg]][dev-deps-url]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]

[![npm badge][npm-badge-png]][package-url]

Determine if the current node version supports the `--preserve-symlinks` flag.

## Example

```js
var supportsPreserveSymlinks = require('node-supports-preserve-symlinks-flag');
var assert = require('assert');

assert.equal(supportsPreserveSymlinks, null); // in a browser
assert.equal(supportsPreserveSymlinks, false); // in node < v6.2
assert.equal(supportsPreserveSymlinks, true); // in node v6.2+
```

## Tests
Simply clone the repo, `npm install`, and run `npm test`

[package-url]: https://npmjs.org/package/node-supports-preserve-symlinks-flag
[npm-version-svg]: https://versionbadg.es/inspect-js/node-supports-preserve-symlinks-flag.svg
[deps-svg]: https://david-dm.org/inspect-js/node-supports-preserve-symlinks-flag.svg
[deps-url]: https://david-dm.org/inspect-js/node-supports-preserve-symlinks-flag
[dev-deps-svg]: https://david-dm.org/inspect-js/node-supports-preserve-symlinks-flag/dev-status.svg
[dev-deps-url]: https://david-dm.org/inspect-js/node-supports-preserve-symlinks-flag#info=devDependencies
[npm-badge-png]: https://nodei.co/npm/node-supports-preserve-symlinks-flag.png?downloads=true&stars=true
[license-image]: https://img.shields.io/npm/l/node-supports-preserve-symlinks-flag.svg
[license-url]: LICENSE
[downloads-image]: https://img.shields.io/npm/dm/node-supports-preserve-symlinks-flag.svg
[downloads-url]: https://npm-stat.com/charts.html?package=node-supports-preserve-symlinks-flag
[codecov-image]: https://codecov.io/gh/inspect-js/node-supports-preserve-symlinks-flag/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/inspect-js/node-supports-preserve-symlinks-flag/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/inspect-js/node-supports-preserve-symlinks-flag
[actions-url]: https://github.com/inspect-js/node-supports-preserve-symlinks-flag/actions


code:
'use strict';

module.exports = (
// node 12+
	process.allowedNodeEnvironmentFlags && process.allowedNodeEnvironmentFlags.has('--preserve-symlinks')
) || (
// node v6.2 - v11
	String(module.constructor._findPath).indexOf('preserveSymlinks') >= 0 // eslint-disable-line no-underscore-dangle
);


supports-preserve-symlinks-flag

description: Determine if the current node version supports the `--preserve-symlinks` flag.

prepublishOnly: safe-publish-latest

prepublish: not-in-publish || npm run prepublishOnly

lint: eslint --ext=js,mjs .

pretest: npm run lint

tests-only: nyc tape 'test/**/*.js'

test: npm run tests-only

posttest: aud --production

version: auto-changelog && git add CHANGELOG.md

postversion: auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f "v$(node -e "console.log(require('./package.json').version)")"

type: git

keywords: node flag symlink symlinks preserve-symlinks

author: Jordan Harband <ljharb@gmail.com>

output: CHANGELOG.md

template: keepachangelog


code:
'use strict';

var test = require('tape');
var semver = require('semver');

var supportsPreserveSymlinks = require('../');
var browser = require('../browser');

test('supportsPreserveSymlinks', function (t) {
	t.equal(typeof supportsPreserveSymlinks, 'boolean', 'is a boolean');

	t.equal(browser, null, 'browser file is `null`');
	t.equal(
		supportsPreserveSymlinks,
		null,
		'in a browser, is null',
		{ skip: typeof window === 'undefined' }
	);

	var expected = semver.satisfies(process.version, '>= 6.2');
	t.equal(
		supportsPreserveSymlinks,
		expected,
		'is true in node v6.2+, false otherwise (actual: ' + supportsPreserveSymlinks + ', expected ' + expected + ')',
		{ skip: typeof window !== 'undefined' }
	);

	t.end();
});


code:
SVG Tags
========
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage][coveralls-image]][coveralls-url] [![Dependencies][dependencies-image]][dependencies-url]

> List of standard SVG tags.

List built from the [SVG 1.1 specification](http://www.w3.org/TR/SVG/eltindex.html).

## Installation

``` bash
$ npm install svg-tags --save
```

## Usage

The module is simply a JSON array, so use as you would a normal JavaScript array.

``` javascript
var tags = require( 'svg-tags' );

console.log( JSON.stringify( tags ) );
/**
* Returns:
*	[ 'a', 'altGlyph', ... ]
*/

console.log( tags.indexOf( 'desc' ) );
// Returns [index]
```

## Examples

To run the example code from the top-level application directory,

``` bash
$ node ./examples/index.js
```

## Tests

### Unit

Unit tests use the [Mocha](http://visionmedia.github.io/mocha) test framework with [Chai](http://chaijs.com) assertions. To run the tests, execute the following command in the top-level application directory:

``` bash
$ make test
```

All new feature development should have corresponding unit tests to validate correct functionality.

### Test Coverage

This repository uses [Istanbul](https://github.com/gotwarlost/istanbul) as its code coverage tool. To generate a test coverage report, execute the following command in the top-level application directory:

``` bash
$ make test-cov
```

Istanbul creates a `./reports/coverage` directory. To access an HTML version of the report,

``` bash
$ open reports/coverage/lcov-report/index.html
```

## License

[MIT license](http://opensource.org/licenses/MIT).

---
## Copyright

Copyright © 2014. Athan Reines.

[npm-image]: http://img.shields.io/npm/v/svg-tags.svg
[npm-url]: https://npmjs.org/package/svg-tags

[travis-image]: http://img.shields.io/travis/element-io/svg-tags/master.svg
[travis-url]: https://travis-ci.org/element-io/svg-tags

[coveralls-image]: https://img.shields.io/coveralls/element-io/svg-tags/master.svg
[coveralls-url]: https://coveralls.io/r/element-io/svg-tags?branch=master

[dependencies-image]: http://img.shields.io/david/element-io/svg-tags.svg
[dependencies-url]: https://david-dm.org/element-io/svg-tags

[dev-dependencies-image]: http://img.shields.io/david/dev/element-io/svg-tags.svg
[dev-dependencies-url]: https://david-dm.org/dev/element-io/svg-tags

[github-issues-image]: http://img.shields.io/github/issues/element-io/svg-tags.svg
[github-issues-url]: https://github.com/element-io/svg-tags/issues


code: module.exports = require( './svg-tags.json' );


code: a altGlyph altGlyphDef altGlyphItem animate animateColor animateMotion animateTransform circle clipPath color-profile cursor defs desc ellipse feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence filter font font-face font-face-format font-face-name font-face-src font-face-uri foreignObject g glyph glyphRef hkern image line linearGradient marker mask metadata missing-glyph mpath path pattern polygon polyline radialGradient rect script set stop style svg switch symbol text textPath title tref tspan use view vkern


svg-tags

description: List of standard SVG tags.

test-cov: ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --dir ./reports/coverage -- -R spec

coveralls: ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --dir ./reports/coveralls/coverage --report lcovonly -- -R spec && cat ./reports/coveralls/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./reports/coveralls

main: ./lib

keywords: svg tags w3c

type: MIT


code:
## SVGO [![npm version](https://img.shields.io/npm/v/svgo)](https://npmjs.org/package/svgo) [![Discord](https://img.shields.io/discord/815166721315831868)](https://discord.gg/z8jX8NYxrE)

**SVG O**ptimizer is a Node.js-based tool for optimizing SVG vector graphics files.

## Why?

SVG files, especially those exported from various editors, usually contain a lot of redundant and useless information. This can include editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting the SVG rendering result.

## Installation

```sh
npm -g install svgo
```

or

```sh
yarn global add svgo
```

## CLI usage

```sh
svgo one.svg two.svg -o one.min.svg two.min.svg
```

Or use the `--folder`/`-f` flag to optimize a whole folder of SVG icons

```sh
svgo -f ./path/to/folder/with/svg/files -o ./path/to/folder/with/svg/output
```

See help for advanced usage

```sh
svgo --help
```

## Configuration

Some options can be configured with CLI though it may be easier to have the configuration in a separate file.
SVGO automatically loads configuration from `svgo.config.js` or module specified with `--config` flag.

```js
module.exports = {
  multipass: true, // boolean. false by default
  datauri: 'enc', // 'base64', 'enc' or 'unenc'. 'base64' by default
  js2svg: {
    indent: 2, // string with spaces or number of spaces. 4 by default
    pretty: true, // boolean, false by default
  },
};
```

SVGO has a plugin-based architecture, so almost every optimization is a separate plugin.
There is a set of [built-in plugins](#built-in-plugins). See how to configure them:

```js
module.exports = {
  plugins: [
    // enable a built-in plugin by name
    'prefixIds',

    // or by expanded version
    {
      name: 'prefixIds',
    },

    // some plugins allow/require to pass options
    {
      name: 'prefixIds',
      params: {
        prefix: 'my-prefix',
      },
    },
  ],
};
```

The default preset of plugins is fully overridden if the `plugins` field is specified.
Use `preset-default` plugin to customize plugins options.

```js
module.exports = {
  plugins: [
    {
      name: 'preset-default',
      params: {
        overrides: {
          // customize options for plugins included in preset
          inlineStyles: {
            onlyMatchedOnce: false,
          },

          // or disable plugins
          removeDoctype: false,
        },
      },
    },

    // enable builtin plugin not included in default preset
    'prefixIds',

    // enable and configure builtin plugin not included in preset
    {
      name: 'sortAttrs',
      params: {
        xmlnsOrder: 'alphabetical',
      },
    },
  ],
};
```

Default preset includes the following list of plugins:

- removeDoctype
- removeXMLProcInst
- removeComments
- removeMetadata
- removeEditorsNSData
- cleanupAttrs
- mergeStyles
- inlineStyles
- minifyStyles
- cleanupIDs
- removeUselessDefs
- cleanupNumericValues
- convertColors
- removeUnknownsAndDefaults
- removeNonInheritableGroupAttrs
- removeUselessStrokeAndFill
- removeViewBox
- cleanupEnableBackground
- removeHiddenElems
- removeEmptyText
- convertShapeToPath
- convertEllipseToCircle
- moveElemsAttrsToGroup
- moveGroupAttrsToElems
- collapseGroups
- convertPathData
- convertTransform
- removeEmptyAttrs
- removeEmptyContainers
- mergePaths
- removeUnusedNS
- sortDefsChildren
- removeTitle
- removeDesc

It's also possible to specify a custom plugin:

```js
const anotherCustomPlugin = require('./another-custom-plugin.js');
module.exports = {
  plugins: [
    {
      name: 'customPluginName',
      type: 'perItem', // 'perItem', 'perItemReverse' or 'full'
      params: {
        optionName: 'optionValue',
      },
      fn: (ast, params, info) => {},
    },
    anotherCustomPlugin,
  ],
};
```

## API usage

SVGO provides a few low level utilities.

### optimize

The core of SVGO is `optimize` function.

```js
const { optimize } = require('svgo');
const result = optimize(svgString, {
  // optional but recommended field
  path: 'path-to.svg',
  // all config fields are also available here
  multipass: true,
});
const optimizedSvgString = result.data;
```

### loadConfig

If you write a tool on top of SVGO you might need a way to load SVGO config.

```js
const { loadConfig } = require('svgo');
const config = await loadConfig();

// you can also specify a relative or absolute path and customize the current working directory
const config = await loadConfig(configFile, cwd);
```

## Built-in plugins

| Plugin                                                                                | Description                                                                                | Default    |
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js)                                     | cleanup attributes from newlines, trailing, and repeating spaces                                                                                | `enabled`  |
| [mergeStyles](https://github.com/svg/svgo/blob/master/plugins/mergeStyles.js)                                       | merge multiple style elements into one                                                                                | `enabled`  |
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js)                                     | move and merge styles from `<style>` elements to element `style` attributes                                                                              | `enabled`  |
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js)                                   | remove `doctype` declaration                                                                                | `enabled`  |
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js)                           | remove XML processing instructions                                                                                | `enabled`  |
| [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js)                                 | remove comments                                                                                | `enabled`  |
| [removeMetadata](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js)                                 | remove `<metadata>`                                                                                | `enabled`  |
| [removeTitle](https://github.com/svg/svgo/blob/master/plugins/removeTitle.js)                                       | remove `<title>`                                                                                | `enabled`  |
| [removeDesc](https://github.com/svg/svgo/blob/master/plugins/removeDesc.js)                                         | remove `<desc>`                                                                                | `enabled`  |
| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js)                           | remove elements of `<defs>` without `id`                                                                                | `enabled`  |
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js)                                       | removes the `xmlns` attribute (for inline SVG)                                                                                | `disabled` |
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js)                       | remove editors namespaces, elements, and attributes                                                                                | `enabled`  |
| [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js)                             | remove empty attributes                                                                                | `enabled`  |
| [removeHiddenElems](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js)                           | remove hidden elements                                                                                | `enabled`  |
| [removeEmptyText](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js)                               | remove empty Text elements                                                                                | `enabled`  |
| [removeEmptyContainers](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js)                   | remove empty Container elements                                                                                | `enabled`  |
| [removeViewBox](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js)                                   | remove `viewBox` attribute when possible                                                                                | `enabled`  |
| [cleanupEnableBackground](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js)               | remove or cleanup `enable-background` attribute when possible                                                                                | `enabled`  |
| [minifyStyles](https://github.com/svg/svgo/blob/master/plugins/minifyStyles.js)                                     | minify `<style>` elements content with [CSSO](https://github.com/css/csso)                                                                               | `enabled`  |
| [convertStyleToAttrs](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js)                       | convert styles into attributes                                                                                | `disabled` |
| [convertColors](https://github.com/svg/svgo/blob/master/plugins/convertColors.js)                                   | convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`)                                                                                | `enabled`  |
| [convertPathData](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js)                               | convert Path data to relative or absolute (whichever is shorter), convert one segment to another, trim useless delimiters, smart rounding, and much more | `enabled`  |
| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js)                             | collapse multiple transforms into one, convert matrices to the short aliases, and much more                                                              | `enabled`  |
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js)           | remove unknown elements content and attributes, remove attributes with default values                                                                    | `enabled`  |
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes                                                                                | `enabled`  |
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js)         | remove useless `stroke` and `fill` attributes                                                                                | `enabled`  |
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js)                                 | remove unused namespaces declaration                                                                                | `enabled`  |
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js)                                           | prefix IDs and classes with the SVG filename or an arbitrary string                                                                                | `disabled` |
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js)                                         | remove unused and minify used IDs                                                                                | `enabled`  |
| [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js)                     | round numeric values to the fixed precision, remove default `px` units                                                                                | `enabled`  |
| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js)                       | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`)                                                   | `disabled` |
| [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js)                   | move elements' attributes to their enclosing group                                                                                | `enabled`  |
| [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/master/plugins/moveGroupAttrsToElems.js)                   | move some group attributes to the contained elements                                                                                | `enabled`  |
| [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js)                                 | collapse useless groups                                                                                | `enabled`  |
| [removeRasterImages](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js)                         | remove raster images                                                                                | `disabled` |
| [mergePaths](https://github.com/svg/svgo/blob/master/plugins/mergePaths.js)                                         | merge multiple Paths into one                                                                                | `enabled`  |
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js)                         | convert some basic shapes to `<path>`                                                                                | `enabled`  |
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js)                 | convert non-eccentric `<ellipse>` to `<circle>`                                                                                | `enabled`  |
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js)                                           | sort element attributes for epic readability                                                                                | `disabled` |
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js)                             | sort children of `<defs>` in order to improve compression                                                                                | `enabled`  |
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js)                             | remove `width`/`height` and add `viewBox` if it's missing (opposite to removeViewBox, disable it first)                                                  | `disabled` |
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js)                                       | remove attributes by pattern                                                                                | `disabled` |
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js)         | removes attributes of elements that match a CSS selector                                                                                | `disabled` |
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js)                     | remove arbitrary elements by `ID` or `className`                                                                                | `disabled` |
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js)                 | add classnames to an outer `<svg>` element                                                                                | `disabled` |
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js)           | adds attributes to an outer `<svg>` element                                                                                | `disabled` |
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js)                     | removes elements that are drawn outside of the viewbox                                                                                | `disabled` |
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js)                         | remove `<style>` elements                                                                                | `disabled` |
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js)                       | remove `<script>` elements                                                                                | `disabled` |
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js)                                         | Find duplicated <path> elements and replace them with <use> links                                                                                | `disabled` |

## Other Ways to Use SVGO

- as a web app – [SVGOMG](https://jakearchibald.github.io/svgomg/)
- as a GitHub Action – [SVGO Action](https://github.com/marketplace/actions/svgo-action)
- as a Grunt task – [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)
- as a Gulp task – [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
- as a Mimosa module – [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg)
- as an OSX Folder Action – [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action)
- as a webpack loader – [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
- as a Telegram Bot – [svgo_bot](https://github.com/maksugr/svgo_bot)
- as a PostCSS plugin – [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
- as an Inkscape plugin – [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
- as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
- as a macOS app - [Image Shrinker](https://image-shrinker.com)
- as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo)
- as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo)
- as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo)
- as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo)
- as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export)
- as a Linux app - [Oh My SVG](https://github.com/sonnyp/OhMySVG)
- as a Browser extension - [SVG Gobbler](https://github.com/rossmoody/svg-gobbler)
- as an API - [Vector Express](https://github.com/smidyo/vectorexpress-api#convertor-svgo)

## Donators

| [](https://sheetjs.com/) | [](https://fontello.com/) |
| :------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------: |
|                       [SheetJS LLC](https://sheetjs.com/)                        |                                               [Fontello](https://fontello.com/)                                               |

## License and Copyright

This software is released under the terms of the [MIT license](https://github.com/svg/svgo/blob/master/LICENSE).

Logo by [André Castillo](https://github.com/DerianAndre).


code:
#!/usr/bin/env node

const colors = require('picocolors');
const { program } = require('commander');
const makeProgram = require('../lib/svgo/coa');
makeProgram(program);
program.parseAsync(process.argv).catch(error => {
  console.error(colors.red(error.stack));
  process.exit(1);
});


code:
'use strict';

var csstree = require('css-tree'),
  List = csstree.List,
  stable = require('stable'),
  specificity = require('csso/lib/restructure/prepare/specificity');

/**
 * Flatten a CSS AST to a selectors list.
 *
 * @param {import('css-tree').CssNode} cssAst css-tree AST to flatten
 * @return {Array} selectors
 */
function flattenToSelectors(cssAst) {
  var selectors = [];

  csstree.walk(cssAst, {
    visit: 'Rule',
    enter: function (node) {
      if (node.type !== 'Rule') {
        return;
      }

      var atrule = this.atrule;
      var rule = node;

      node.prelude.children.each(function (selectorNode, selectorItem) {
        var selector = {
          item: selectorItem,
          atrule: atrule,
          rule: rule,
          pseudos: /** @type {{item: any; list: any[]}[]} */ ([]),
        };

        selectorNode.children.each(function (
          selectorChildNode,
          selectorChildItem,
          selectorChildList
        ) {
          if (
            selectorChildNode.type === 'PseudoClassSelector' ||
            selectorChildNode.type === 'PseudoElementSelector'
          ) {
            selector.pseudos.push({
              item: selectorChildItem,
              list: selectorChildList,
            });
          }
        });

        selectors.push(selector);
      });
    },
  });

  return selectors;
}

/**
 * Filter selectors by Media Query.
 *
 * @param {Array} selectors to filter
 * @param {Array} useMqs Array with strings of media queries that should pass (<name> <expression>)
 * @return {Array} Filtered selectors that match the passed media queries
 */
function filterByMqs(selectors, useMqs) {
  return selectors.filter(function (selector) {
    if (selector.atrule === null) {
      return ~useMqs.indexOf('');
    }

    var mqName = selector.atrule.name;
    var mqStr = mqName;
    if (
      selector.atrule.expression &&
      selector.atrule.expression.children.first().type === 'MediaQueryList'
    ) {
      var mqExpr = csstree.generate(selector.atrule.expression);
      mqStr = [mqName, mqExpr].join(' ');
    }

    return ~useMqs.indexOf(mqStr);
  });
}

/**
 * Filter selectors by the pseudo-elements and/or -classes they contain.
 *
 * @param {Array} selectors to filter
 * @param {Array} usePseudos Array with strings of single or sequence of pseudo-elements and/or -classes that should pass
 * @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
 */
function filterByPseudos(selectors, usePseudos) {
  return selectors.filter(function (selector) {
    var pseudoSelectorsStr = csstree.generate({
      type: 'Selector',
      children: new List().fromArray(
        selector.pseudos.map(function (pseudo) {
          return pseudo.item.data;
        })
      ),
    });
    return ~usePseudos.indexOf(pseudoSelectorsStr);
  });
}

/**
 * Remove pseudo-elements and/or -classes from the selectors for proper matching.
 *
 * @param {Array} selectors to clean
 * @return {void}
 */
function cleanPseudos(selectors) {
  selectors.forEach(function (selector) {
    selector.pseudos.forEach(function (pseudo) {
      pseudo.list.remove(pseudo.item);
    });
  });
}

/**
 * Compares two selector specificities.
 * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
 *
 * @param {Array} aSpecificity Specificity of selector A
 * @param {Array} bSpecificity Specificity of selector B
 * @return {number} Score of selector specificity A compared to selector specificity B
 */
function compareSpecificity(aSpecificity, bSpecificity) {
  for (var i = 0; i < 4; i += 1) {
    if (aSpecificity[i] < bSpecificity[i]) {
      return -1;
    } else if (aSpecificity[i] > bSpecificity[i]) {
      return 1;
    }
  }

  return 0;
}

/**
 * Compare two simple selectors.
 *
 * @param {Object} aSimpleSelectorNode Simple selector A
 * @param {Object} bSimpleSelectorNode Simple selector B
 * @return {number} Score of selector A compared to selector B
 */
function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
  var aSpecificity = specificity(aSimpleSelectorNode),
    bSpecificity = specificity(bSimpleSelectorNode);
  return compareSpecificity(aSpecificity, bSpecificity);
}

function _bySelectorSpecificity(selectorA, selectorB) {
  return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
}

/**
 * Sort selectors stably by their specificity.
 *
 * @param {Array} selectors to be sorted
 * @return {Array} Stable sorted selectors
 */
function sortSelectors(selectors) {
  return stable(selectors, _bySelectorSpecificity);
}

/**
 * Convert a css-tree AST style declaration to CSSStyleDeclaration property.
 *
 * @param {import('css-tree').CssNode} declaration css-tree style declaration
 * @return {Object} CSSStyleDeclaration property
 */
function csstreeToStyleDeclaration(declaration) {
  var propertyName = declaration.property,
    propertyValue = csstree.generate(declaration.value),
    propertyPriority = declaration.important ? 'important' : '';
  return {
    name: propertyName,
    value: propertyValue,
    priority: propertyPriority,
  };
}

/**
 * Gets the CSS string of a style element
 *
 * @param {Object} elem style element
 * @return {string} CSS string or empty array if no styles are set
 */
function getCssStr(elem) {
  if (
    elem.children.length > 0 &&
    (elem.children[0].type === 'text' || elem.children[0].type === 'cdata')
  ) {
    return elem.children[0].value;
  }
  return '';
}

/**
 * Sets the CSS string of a style element
 *
 * @param {Object} elem style element
 * @param {string} css string to be set
 * @return {string} reference to field with CSS
 */
function setCssStr(elem, css) {
  if (elem.children.length === 0) {
    elem.children.push({
      type: 'text',
      value: '',
    });
  }

  if (elem.children[0].type !== 'text' && elem.children[0].type !== 'cdata') {
    return css;
  }

  elem.children[0].value = css;

  return css;
}

module.exports.flattenToSelectors = flattenToSelectors;

module.exports.filterByMqs = filterByMqs;
module.exports.filterByPseudos = filterByPseudos;
module.exports.cleanPseudos = cleanPseudos;

module.exports.compareSpecificity = compareSpecificity;
module.exports.compareSimpleSelectorNode = compareSimpleSelectorNode;

module.exports.sortSelectors = sortSelectors;

module.exports.csstreeToStyleDeclaration = csstreeToStyleDeclaration;

module.exports.getCssStr = getCssStr;
module.exports.setCssStr = setCssStr;


code:
'use strict';

/**
 * @typedef {import('./types').XastNode} XastNode
 * @typedef {import('./types').XastInstruction} XastInstruction
 * @typedef {import('./types').XastDoctype} XastDoctype
 * @typedef {import('./types').XastComment} XastComment
 * @typedef {import('./types').XastRoot} XastRoot
 * @typedef {import('./types').XastElement} XastElement
 * @typedef {import('./types').XastCdata} XastCdata
 * @typedef {import('./types').XastText} XastText
 * @typedef {import('./types').XastParent} XastParent
 */

// @ts-ignore sax will be replaced with something else later
const SAX = require('@trysound/sax');
const JSAPI = require('./svgo/jsAPI.js');
const { textElems } = require('../plugins/_collections.js');

class SvgoParserError extends Error {
  /**
   * @param message {string}
   * @param line {number}
   * @param column {number}
   * @param source {string}
   * @param file {void | string}
   */
  constructor(message, line, column, source, file) {
    super(message);
    this.name = 'SvgoParserError';
    this.message = `${file || '<input>'}:${line}:${column}: ${message}`;
    this.reason = message;
    this.line = line;
    this.column = column;
    this.source = source;
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, SvgoParserError);
    }
  }
  toString() {
    const lines = this.source.split(/\r?\n/);
    const startLine = Math.max(this.line - 3, 0);
    const endLine = Math.min(this.line + 2, lines.length);
    const lineNumberWidth = String(endLine).length;
    const startColumn = Math.max(this.column - 54, 0);
    const endColumn = Math.max(this.column + 20, 80);
    const code = lines
      .slice(startLine, endLine)
      .map((line, index) => {
        const lineSlice = line.slice(startColumn, endColumn);
        let ellipsisPrefix = '';
        let ellipsisSuffix = '';
        if (startColumn !== 0) {
          ellipsisPrefix = startColumn > line.length - 1 ? ' ' : '…';
        }
        if (endColumn < line.length - 1) {
          ellipsisSuffix = '…';
        }
        const number = startLine + 1 + index;
        const gutter = ` ${number.toString().padStart(lineNumberWidth)} | `;
        if (number === this.line) {
          const gutterSpacing = gutter.replace(/[^|]/g, ' ');
          const lineSpacing = (
            ellipsisPrefix + line.slice(startColumn, this.column - 1)
          ).replace(/[^\t]/g, ' ');
          const spacing = gutterSpacing + lineSpacing;
          return `>${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}\n ${spacing}^`;
        }
        return ` ${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}`;
      })
      .join('\n');
    return `${this.name}: ${this.message}\n\n${code}\n`;
  }
}

const entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;

const config = {
  strict: true,
  trim: false,
  normalize: false,
  lowercase: true,
  xmlns: true,
  position: true,
};

/**
 * Convert SVG (XML) string to SVG-as-JS object.
 *
 * @type {(data: string, from?: string) => XastRoot}
 */
const parseSvg = (data, from) => {
  const sax = SAX.parser(config.strict, config);
  /**
   * @type {XastRoot}
   */
  const root = new JSAPI({ type: 'root', children: [] });
  /**
   * @type {XastParent}
   */
  let current = root;
  /**
   * @type {Array<XastParent>}
   */
  const stack = [root];

  /**
   * @type {<T extends XastNode>(node: T) => T}
   */
  const pushToContent = (node) => {
    const wrapped = new JSAPI(node, current);
    current.children.push(wrapped);
    return wrapped;
  };

  /**
   * @type {(doctype: string) => void}
   */
  sax.ondoctype = (doctype) => {
    /**
     * @type {XastDoctype}
     */
    const node = {
      type: 'doctype',
      // TODO parse doctype for name, public and system to match xast
      name: 'svg',
      data: {
        doctype,
      },
    };
    pushToContent(node);
    const subsetStart = doctype.indexOf('[');
    if (subsetStart >= 0) {
      entityDeclaration.lastIndex = subsetStart;
      let entityMatch = entityDeclaration.exec(data);
      while (entityMatch != null) {
        sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
        entityMatch = entityDeclaration.exec(data);
      }
    }
  };

  /**
   * @type {(data: { name: string, body: string }) => void}
   */
  sax.onprocessinginstruction = (data) => {
    /**
     * @type {XastInstruction}
     */
    const node = {
      type: 'instruction',
      name: data.name,
      value: data.body,
    };
    pushToContent(node);
  };

  /**
   * @type {(comment: string) => void}
   */
  sax.oncomment = (comment) => {
    /**
     * @type {XastComment}
     */
    const node = {
      type: 'comment',
      value: comment.trim(),
    };
    pushToContent(node);
  };

  /**
   * @type {(cdata: string) => void}
   */
  sax.oncdata = (cdata) => {
    /**
     * @type {XastCdata}
     */
    const node = {
      type: 'cdata',
      value: cdata,
    };
    pushToContent(node);
  };

  /**
   * @type {(data: { name: string, attributes: Record<string, { value: string }>}) => void}
   */
  sax.onopentag = (data) => {
    /**
     * @type {XastElement}
     */
    let element = {
      type: 'element',
      name: data.name,
      attributes: {},
      children: [],
    };
    for (const [name, attr] of Object.entries(data.attributes)) {
      element.attributes[name] = attr.value;
    }
    element = pushToContent(element);
    current = element;
    stack.push(element);
  };

  /**
   * @type {(text: string) => void}
   */
  sax.ontext = (text) => {
    if (current.type === 'element') {
      // prevent trimming of meaningful whitespace inside textual tags
      if (textElems.includes(current.name)) {
        /**
         * @type {XastText}
         */
        const node = {
          type: 'text',
          value: text,
        };
        pushToContent(node);
      } else if (/\S/.test(text)) {
        /**
         * @type {XastText}
         */
        const node = {
          type: 'text',
          value: text.trim(),
        };
        pushToContent(node);
      }
    }
  };

  sax.onclosetag = () => {
    stack.pop();
    current = stack[stack.length - 1];
  };

  /**
   * @type {(e: any) => void}
   */
  sax.onerror = (e) => {
    const error = new SvgoParserError(
      e.reason,
      e.line + 1,
      e.column,
      data,
      from
    );
    if (e.message.indexOf('Unexpected end') === -1) {
      throw error;
    }
  };

  sax.write(data).close();
  return root;
};
exports.parseSvg = parseSvg;


code:
'use strict';

/**
 * @typedef {import('./types').PathDataItem} PathDataItem
 * @typedef {import('./types').PathDataCommand} PathDataCommand
 */

// Based on https://www.w3.org/TR/SVG11/paths.html#PathDataBNF

const argsCountPerCommand = {
  M: 2,
  m: 2,
  Z: 0,
  z: 0,
  L: 2,
  l: 2,
  H: 1,
  h: 1,
  V: 1,
  v: 1,
  C: 6,
  c: 6,
  S: 4,
  s: 4,
  Q: 4,
  q: 4,
  T: 2,
  t: 2,
  A: 7,
  a: 7,
};

/**
 * @type {(c: string) => c is PathDataCommand}
 */
const isCommand = (c) => {
  return c in argsCountPerCommand;
};

/**
 * @type {(c: string) => boolean}
 */
const isWsp = (c) => {
  const codePoint = c.codePointAt(0);
  return (
    codePoint === 0x20 ||
    codePoint === 0x9 ||
    codePoint === 0xd ||
    codePoint === 0xa
  );
};

/**
 * @type {(c: string) => boolean}
 */
const isDigit = (c) => {
  const codePoint = c.codePointAt(0);
  if (codePoint == null) {
    return false;
  }
  return 48 <= codePoint && codePoint <= 57;
};

/**
 * @typedef {'none' | 'sign' | 'whole' | 'decimal_point' | 'decimal' | 'e' | 'exponent_sign' | 'exponent'} ReadNumberState
 */

/**
 * @type {(string: string, cursor: number) => [number, number | null]}
 */
const readNumber = (string, cursor) => {
  let i = cursor;
  let value = '';
  let state = /** @type {ReadNumberState} */ ('none');
  for (; i < string.length; i += 1) {
    const c = string[i];
    if (c === '+' || c === '-') {
      if (state === 'none') {
        state = 'sign';
        value += c;
        continue;
      }
      if (state === 'e') {
        state = 'exponent_sign';
        value += c;
        continue;
      }
    }
    if (isDigit(c)) {
      if (state === 'none' || state === 'sign' || state === 'whole') {
        state = 'whole';
        value += c;
        continue;
      }
      if (state === 'decimal_point' || state === 'decimal') {
        state = 'decimal';
        value += c;
        continue;
      }
      if (state === 'e' || state === 'exponent_sign' || state === 'exponent') {
        state = 'exponent';
        value += c;
        continue;
      }
    }
    if (c === '.') {
      if (state === 'none' || state === 'sign' || state === 'whole') {
        state = 'decimal_point';
        value += c;
        continue;
      }
    }
    if (c === 'E' || c == 'e') {
      if (
        state === 'whole' ||
        state === 'decimal_point' ||
        state === 'decimal'
      ) {
        state = 'e';
        value += c;
        continue;
      }
    }
    break;
  }
  const number = Number.parseFloat(value);
  if (Number.isNaN(number)) {
    return [cursor, null];
  } else {
    // step back to delegate iteration to parent loop
    return [i - 1, number];
  }
};

/**
 * @type {(string: string) => Array<PathDataItem>}
 */
const parsePathData = (string) => {
  /**
   * @type {Array<PathDataItem>}
   */
  const pathData = [];
  /**
   * @type {null | PathDataCommand}
   */
  let command = null;
  let args = /** @type {number[]} */ ([]);
  let argsCount = 0;
  let canHaveComma = false;
  let hadComma = false;
  for (let i = 0; i < string.length; i += 1) {
    const c = string.charAt(i);
    if (isWsp(c)) {
      continue;
    }
    // allow comma only between arguments
    if (canHaveComma && c === ',') {
      if (hadComma) {
        break;
      }
      hadComma = true;
      continue;
    }
    if (isCommand(c)) {
      if (hadComma) {
        return pathData;
      }
      if (command == null) {
        // moveto should be leading command
        if (c !== 'M' && c !== 'm') {
          return pathData;
        }
      } else {
        // stop if previous command arguments are not flushed
        if (args.length !== 0) {
          return pathData;
        }
      }
      command = c;
      args = [];
      argsCount = argsCountPerCommand[command];
      canHaveComma = false;
      // flush command without arguments
      if (argsCount === 0) {
        pathData.push({ command, args });
      }
      continue;
    }
    // avoid parsing arguments if no command detected
    if (command == null) {
      return pathData;
    }
    // read next argument
    let newCursor = i;
    let number = null;
    if (command === 'A' || command === 'a') {
      const position = args.length;
      if (position === 0 || position === 1) {
        // allow only positive number without sign as first two arguments
        if (c !== '+' && c !== '-') {
          [newCursor, number] = readNumber(string, i);
        }
      }
      if (position === 2 || position === 5 || position === 6) {
        [newCursor, number] = readNumber(string, i);
      }
      if (position === 3 || position === 4) {
        // read flags
        if (c === '0') {
          number = 0;
        }
        if (c === '1') {
          number = 1;
        }
      }
    } else {
      [newCursor, number] = readNumber(string, i);
    }
    if (number == null) {
      return pathData;
    }
    args.push(number);
    canHaveComma = true;
    hadComma = false;
    i = newCursor;
    // flush arguments when necessary count is reached
    if (args.length === argsCount) {
      pathData.push({ command, args });
      // subsequent moveto coordinates are threated as implicit lineto commands
      if (command === 'M') {
        command = 'L';
      }
      if (command === 'm') {
        command = 'l';
      }
      args = [];
    }
  }
  return pathData;
};
exports.parsePathData = parsePathData;

/**
 * @type {(number: number, precision?: number) => string}
 */
const stringifyNumber = (number, precision) => {
  if (precision != null) {
    const ratio = 10 ** precision;
    number = Math.round(number * ratio) / ratio;
  }
  // remove zero whole from decimal number
  return number.toString().replace(/^0\./, '.').replace(/^-0\./, '-.');
};

/**
 * Elliptical arc large-arc and sweep flags are rendered with spaces
 * because many non-browser environments are not able to parse such paths
 *
 * @type {(
 *   command: string,
 *   args: number[],
 *   precision?: number,
 *   disableSpaceAfterFlags?: boolean
 * ) => string}
 */
const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => {
  let result = '';
  let prev = '';
  for (let i = 0; i < args.length; i += 1) {
    const number = args[i];
    const numberString = stringifyNumber(number, precision);
    if (
      disableSpaceAfterFlags &&
      (command === 'A' || command === 'a') &&
      // consider combined arcs
      (i % 7 === 4 || i % 7 === 5)
    ) {
      result += numberString;
    } else if (i === 0 || numberString.startsWith('-')) {
      // avoid space before first and negative numbers
      result += numberString;
    } else if (prev.includes('.') && numberString.startsWith('.')) {
      // remove space before decimal with zero whole
      // only when previous number is also decimal
      result += numberString;
    } else {
      result += ` ${numberString}`;
    }
    prev = numberString;
  }
  return result;
};

/**
 * @typedef {{
 *   pathData: Array<PathDataItem>;
 *   precision?: number;
 *   disableSpaceAfterFlags?: boolean;
 * }} StringifyPathDataOptions
 */

/**
 * @type {(options: StringifyPathDataOptions) => string}
 */
const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => {
  // combine sequence of the same commands
  let combined = [];
  for (let i = 0; i < pathData.length; i += 1) {
    const { command, args } = pathData[i];
    if (i === 0) {
      combined.push({ command, args });
    } else {
      /**
       * @type {PathDataItem}
       */
      const last = combined[combined.length - 1];
      // match leading moveto with following lineto
      if (i === 1) {
        if (command === 'L') {
          last.command = 'M';
        }
        if (command === 'l') {
          last.command = 'm';
        }
      }
      if (
        (last.command === command &&
          last.command !== 'M' &&
          last.command !== 'm') ||
        // combine matching moveto and lineto sequences
        (last.command === 'M' && command === 'L') ||
        (last.command === 'm' && command === 'l')
      ) {
        last.args = [...last.args, ...args];
      } else {
        combined.push({ command, args });
      }
    }
  }
  let result = '';
  for (const { command, args } of combined) {
    result +=
      command + stringifyArgs(command, args, precision, disableSpaceAfterFlags);
  }
  return result;
};
exports.stringifyPathData = stringifyPathData;


code:
'use strict';

/**
 * @typedef {import('./types').XastParent} XastParent
 * @typedef {import('./types').XastRoot} XastRoot
 * @typedef {import('./types').XastElement} XastElement
 * @typedef {import('./types').XastInstruction} XastInstruction
 * @typedef {import('./types').XastDoctype} XastDoctype
 * @typedef {import('./types').XastText} XastText
 * @typedef {import('./types').XastCdata} XastCdata
 * @typedef {import('./types').XastComment} XastComment
 * @typedef {import('./types').StringifyOptions} StringifyOptions
 */

const { textElems } = require('../plugins/_collections.js');

/**
 * @typedef {{
 *   width: void | string,
 *   height: void | string,
 *   indent: string,
 *   textContext: null | XastElement,
 *   indentLevel: number,
 * }} State
 */

/**
 * @typedef {Required<StringifyOptions>} Options
 */

/**
 * @type {(char: string) => string}
 */
const encodeEntity = (char) => {
  return entities[char];
};

/**
 * @type {Options}
 */
const defaults = {
  doctypeStart: '<!DOCTYPE',
  doctypeEnd: '>',
  procInstStart: '<?',
  procInstEnd: '?>',
  tagOpenStart: '<',
  tagOpenEnd: '>',
  tagCloseStart: '</',
  tagCloseEnd: '>',
  tagShortStart: '<',
  tagShortEnd: '/>',
  attrStart: '="',
  attrEnd: '"',
  commentStart: '<!--',
  commentEnd: '-->',
  cdataStart: '<![CDATA[',
  cdataEnd: ']]>',
  textStart: '',
  textEnd: '',
  indent: 4,
  regEntities: /[&'"<>]/g,
  regValEntities: /[&"<>]/g,
  encodeEntity: encodeEntity,
  pretty: false,
  useShortTags: true,
  eol: 'lf',
  finalNewline: false,
};

/**
 * @type {Record<string, string>}
 */
const entities = {
  '&': '&',
  "'": ''',
  '"': '"',
  '>': '>',
  '<': '<',
};

/**
 * convert XAST to SVG string
 *
 * @type {(data: XastRoot, config: StringifyOptions) => {
 *   data: string,
 *   info: {
 *     width: void | string,
 *     height: void | string
 *   }
 * }}
 */
const stringifySvg = (data, userOptions = {}) => {
  /**
   * @type {Options}
   */
  const config = { ...defaults, ...userOptions };
  const indent = config.indent;
  let newIndent = '    ';
  if (typeof indent === 'number' && Number.isNaN(indent) === false) {
    newIndent = indent < 0 ? '\t' : ' '.repeat(indent);
  } else if (typeof indent === 'string') {
    newIndent = indent;
  }
  /**
   * @type {State}
   */
  const state = {
    // TODO remove width and height in v3
    width: undefined,
    height: undefined,
    indent: newIndent,
    textContext: null,
    indentLevel: 0,
  };
  const eol = config.eol === 'crlf' ? '\r\n' : '\n';
  if (config.pretty) {
    config.doctypeEnd += eol;
    config.procInstEnd += eol;
    config.commentEnd += eol;
    config.cdataEnd += eol;
    config.tagShortEnd += eol;
    config.tagOpenEnd += eol;
    config.tagCloseEnd += eol;
    config.textEnd += eol;
  }
  let svg = stringifyNode(data, config, state);
  if (config.finalNewline && svg.length > 0 && svg[svg.length - 1] !== '\n') {
    svg += eol;
  }
  return {
    data: svg,
    info: {
      width: state.width,
      height: state.height,
    },
  };
};
exports.stringifySvg = stringifySvg;

/**
 * @type {(node: XastParent, config: Options, state: State) => string}
 */
const stringifyNode = (data, config, state) => {
  let svg = '';
  state.indentLevel += 1;
  for (const item of data.children) {
    if (item.type === 'element') {
      svg += stringifyElement(item, config, state);
    }
    if (item.type === 'text') {
      svg += stringifyText(item, config, state);
    }
    if (item.type === 'doctype') {
      svg += stringifyDoctype(item, config);
    }
    if (item.type === 'instruction') {
      svg += stringifyInstruction(item, config);
    }
    if (item.type === 'comment') {
      svg += stringifyComment(item, config);
    }
    if (item.type === 'cdata') {
      svg += stringifyCdata(item, config, state);
    }
  }
  state.indentLevel -= 1;
  return svg;
};

/**
 * create indent string in accordance with the current node level.
 *
 * @type {(config: Options, state: State) => string}
 */
const createIndent = (config, state) => {
  let indent = '';
  if (config.pretty && state.textContext == null) {
    indent = state.indent.repeat(state.indentLevel - 1);
  }
  return indent;
};

/**
 * @type {(node: XastDoctype, config: Options) => string}
 */
const stringifyDoctype = (node, config) => {
  return config.doctypeStart + node.data.doctype + config.doctypeEnd;
};

/**
 * @type {(node: XastInstruction, config: Options) => string}
 */
const stringifyInstruction = (node, config) => {
  return (
    config.procInstStart + node.name + ' ' + node.value + config.procInstEnd
  );
};

/**
 * @type {(node: XastComment, config: Options) => string}
 */
const stringifyComment = (node, config) => {
  return config.commentStart + node.value + config.commentEnd;
};

/**
 * @type {(node: XastCdata, config: Options, state: State) => string}
 */
const stringifyCdata = (node, config, state) => {
  return (
    createIndent(config, state) +
    config.cdataStart +
    node.value +
    config.cdataEnd
  );
};

/**
 * @type {(node: XastElement, config: Options, state: State) => string}
 */
const stringifyElement = (node, config, state) => {
  // beautiful injection for obtaining SVG information :)
  if (
    node.name === 'svg' &&
    node.attributes.width != null &&
    node.attributes.height != null
  ) {
    state.width = node.attributes.width;
    state.height = node.attributes.height;
  }

  // empty element and short tag
  if (node.children.length === 0) {
    if (config.useShortTags) {
      return (
        createIndent(config, state) +
        config.tagShortStart +
        node.name +
        stringifyAttributes(node, config) +
        config.tagShortEnd
      );
    } else {
      return (
        createIndent(config, state) +
        config.tagShortStart +
        node.name +
        stringifyAttributes(node, config) +
        config.tagOpenEnd +
        config.tagCloseStart +
        node.name +
        config.tagCloseEnd
      );
    }
    // non-empty element
  } else {
    let tagOpenStart = config.tagOpenStart;
    let tagOpenEnd = config.tagOpenEnd;
    let tagCloseStart = config.tagCloseStart;
    let tagCloseEnd = config.tagCloseEnd;
    let openIndent = createIndent(config, state);
    let closeIndent = createIndent(config, state);

    if (state.textContext) {
      tagOpenStart = defaults.tagOpenStart;
      tagOpenEnd = defaults.tagOpenEnd;
      tagCloseStart = defaults.tagCloseStart;
      tagCloseEnd = defaults.tagCloseEnd;
      openIndent = '';
    } else if (textElems.includes(node.name)) {
      tagOpenEnd = defaults.tagOpenEnd;
      tagCloseStart = defaults.tagCloseStart;
      closeIndent = '';
      state.textContext = node;
    }

    const children = stringifyNode(node, config, state);

    if (state.textContext === node) {
      state.textContext = null;
    }

    return (
      openIndent +
      tagOpenStart +
      node.name +
      stringifyAttributes(node, config) +
      tagOpenEnd +
      children +
      closeIndent +
      tagCloseStart +
      node.name +
      tagCloseEnd
    );
  }
};

/**
 * @type {(node: XastElement, config: Options) => string}
 */
const stringifyAttributes = (node, config) => {
  let attrs = '';
  for (const [name, value] of Object.entries(node.attributes)) {
    // TODO remove attributes without values support in v3
    if (value !== undefined) {
      const encodedValue = value
        .toString()
        .replace(config.regValEntities, config.encodeEntity);
      attrs += ' ' + name + config.attrStart + encodedValue + config.attrEnd;
    } else {
      attrs += ' ' + name;
    }
  }
  return attrs;
};

/**
 * @type {(node: XastText, config: Options, state: State) => string}
 */
const stringifyText = (node, config, state) => {
  return (
    createIndent(config, state) +
    config.textStart +
    node.value.replace(config.regEntities, config.encodeEntity) +
    (state.textContext ? '' : config.textEnd)
  );
};


code:
'use strict';

/**
 * @typedef {import('css-tree').Rule} CsstreeRule
 * @typedef {import('./types').Specificity} Specificity
 * @typedef {import('./types').Stylesheet} Stylesheet
 * @typedef {import('./types').StylesheetRule} StylesheetRule
 * @typedef {import('./types').StylesheetDeclaration} StylesheetDeclaration
 * @typedef {import('./types').ComputedStyles} ComputedStyles
 * @typedef {import('./types').XastRoot} XastRoot
 * @typedef {import('./types').XastElement} XastElement
 * @typedef {import('./types').XastParent} XastParent
 * @typedef {import('./types').XastChild} XastChild
 */

const stable = require('stable');
const csstree = require('css-tree');
// @ts-ignore not defined in @types/csso
const specificity = require('csso/lib/restructure/prepare/specificity');
const { visit, matches } = require('./xast.js');
const {
  attrsGroups,
  inheritableAttrs,
  presentationNonInheritableGroupAttrs,
} = require('../plugins/_collections.js');

// @ts-ignore not defined in @types/csstree
const csstreeWalkSkip = csstree.walk.skip;

/**
 * @type {(ruleNode: CsstreeRule, dynamic: boolean) => StylesheetRule}
 */
const parseRule = (ruleNode, dynamic) => {
  let selectors;
  let selectorsSpecificity;
  /**
   * @type {Array<StylesheetDeclaration>}
   */
  const declarations = [];
  csstree.walk(ruleNode, (cssNode) => {
    if (cssNode.type === 'SelectorList') {
      // compute specificity from original node to consider pseudo classes
      selectorsSpecificity = specificity(cssNode);
      const newSelectorsNode = csstree.clone(cssNode);
      csstree.walk(newSelectorsNode, (pseudoClassNode, item, list) => {
        if (pseudoClassNode.type === 'PseudoClassSelector') {
          dynamic = true;
          list.remove(item);
        }
      });
      selectors = csstree.generate(newSelectorsNode);
      return csstreeWalkSkip;
    }
    if (cssNode.type === 'Declaration') {
      declarations.push({
        name: cssNode.property,
        value: csstree.generate(cssNode.value),
        important: cssNode.important === true,
      });
      return csstreeWalkSkip;
    }
  });
  if (selectors == null || selectorsSpecificity == null) {
    throw Error('assert');
  }
  return {
    dynamic,
    selectors,
    specificity: selectorsSpecificity,
    declarations,
  };
};

/**
 * @type {(css: string, dynamic: boolean) => Array<StylesheetRule>}
 */
const parseStylesheet = (css, dynamic) => {
  /**
   * @type {Array<StylesheetRule>}
   */
  const rules = [];
  const ast = csstree.parse(css, {
    parseValue: false,
    parseAtrulePrelude: false,
  });
  csstree.walk(ast, (cssNode) => {
    if (cssNode.type === 'Rule') {
      rules.push(parseRule(cssNode, dynamic || false));
      return csstreeWalkSkip;
    }
    if (cssNode.type === 'Atrule') {
      if (cssNode.name === 'keyframes') {
        return csstreeWalkSkip;
      }
      csstree.walk(cssNode, (ruleNode) => {
        if (ruleNode.type === 'Rule') {
          rules.push(parseRule(ruleNode, dynamic || true));
          return csstreeWalkSkip;
        }
      });
      return csstreeWalkSkip;
    }
  });
  return rules;
};

/**
 * @type {(css: string) => Array<StylesheetDeclaration>}
 */
const parseStyleDeclarations = (css) => {
  /**
   * @type {Array<StylesheetDeclaration>}
   */
  const declarations = [];
  const ast = csstree.parse(css, {
    context: 'declarationList',
    parseValue: false,
  });
  csstree.walk(ast, (cssNode) => {
    if (cssNode.type === 'Declaration') {
      declarations.push({
        name: cssNode.property,
        value: csstree.generate(cssNode.value),
        important: cssNode.important === true,
      });
    }
  });
  return declarations;
};

/**
 * @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
 */
const computeOwnStyle = (stylesheet, node) => {
  /**
   * @type {ComputedStyles}
   */
  const computedStyle = {};
  const importantStyles = new Map();

  // collect attributes
  for (const [name, value] of Object.entries(node.attributes)) {
    if (attrsGroups.presentation.includes(name)) {
      computedStyle[name] = { type: 'static', inherited: false, value };
      importantStyles.set(name, false);
    }
  }

  // collect matching rules
  for (const { selectors, declarations, dynamic } of stylesheet.rules) {
    if (matches(node, selectors)) {
      for (const { name, value, important } of declarations) {
        const computed = computedStyle[name];
        if (computed && computed.type === 'dynamic') {
          continue;
        }
        if (dynamic) {
          computedStyle[name] = { type: 'dynamic', inherited: false };
          continue;
        }
        if (
          computed == null ||
          important === true ||
          importantStyles.get(name) === false
        ) {
          computedStyle[name] = { type: 'static', inherited: false, value };
          importantStyles.set(name, important);
        }
      }
    }
  }

  // collect inline styles
  const styleDeclarations =
    node.attributes.style == null
      ? []
      : parseStyleDeclarations(node.attributes.style);
  for (const { name, value, important } of styleDeclarations) {
    const computed = computedStyle[name];
    if (computed && computed.type === 'dynamic') {
      continue;
    }
    if (
      computed == null ||
      important === true ||
      importantStyles.get(name) === false
    ) {
      computedStyle[name] = { type: 'static', inherited: false, value };
      importantStyles.set(name, important);
    }
  }

  return computedStyle;
};

/**
 * Compares two selector specificities.
 * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
 *
 * @type {(a: Specificity, b: Specificity) => number}
 */
const compareSpecificity = (a, b) => {
  for (var i = 0; i < 4; i += 1) {
    if (a[i] < b[i]) {
      return -1;
    } else if (a[i] > b[i]) {
      return 1;
    }
  }

  return 0;
};

/**
 * @type {(root: XastRoot) => Stylesheet}
 */
const collectStylesheet = (root) => {
  /**
   * @type {Array<StylesheetRule>}
   */
  const rules = [];
  /**
   * @type {Map<XastElement, XastParent>}
   */
  const parents = new Map();
  visit(root, {
    element: {
      enter: (node, parentNode) => {
        // store parents
        parents.set(node, parentNode);
        // find and parse all styles
        if (node.name === 'style') {
          const dynamic =
            node.attributes.media != null && node.attributes.media !== 'all';
          if (
            node.attributes.type == null ||
            node.attributes.type === '' ||
            node.attributes.type === 'text/css'
          ) {
            const children = node.children;
            for (const child of children) {
              if (child.type === 'text' || child.type === 'cdata') {
                rules.push(...parseStylesheet(child.value, dynamic));
              }
            }
          }
        }
      },
    },
  });
  // sort by selectors specificity
  stable.inplace(rules, (a, b) =>
    compareSpecificity(a.specificity, b.specificity)
  );
  return { rules, parents };
};
exports.collectStylesheet = collectStylesheet;

/**
 * @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
 */
const computeStyle = (stylesheet, node) => {
  const { parents } = stylesheet;
  // collect inherited styles
  const computedStyles = computeOwnStyle(stylesheet, node);
  let parent = parents.get(node);
  while (parent != null && parent.type !== 'root') {
    const inheritedStyles = computeOwnStyle(stylesheet, parent);
    for (const [name, computed] of Object.entries(inheritedStyles)) {
      if (
        computedStyles[name] == null &&
        // ignore not inheritable styles
        inheritableAttrs.includes(name) === true &&
        presentationNonInheritableGroupAttrs.includes(name) === false
      ) {
        computedStyles[name] = { ...computed, inherited: true };
      }
    }
    parent = parents.get(parent);
  }
  return computedStyles;
};
exports.computeStyle = computeStyle;


code:
'use strict';

const os = require('os');
const fs = require('fs');
const { pathToFileURL } = require('url');
const path = require('path');
const {
  extendDefaultPlugins,
  optimize: optimizeAgnostic,
  createContentItem,
} = require('./svgo.js');

exports.extendDefaultPlugins = extendDefaultPlugins;
exports.createContentItem = createContentItem;

const importConfig = async (configFile) => {
  let config;
  // at the moment dynamic import may randomly fail with segfault
  // to workaround this for some users .cjs extension is loaded
  // exclusively with require
  if (configFile.endsWith('.cjs')) {
    config = require(configFile);
  } else {
    try {
      // dynamic import expects file url instead of path and may fail
      // when windows path is provided
      const { default: imported } = await import(pathToFileURL(configFile));
      config = imported;
    } catch (importError) {
      // TODO remove require in v3
      try {
        config = require(configFile);
      } catch (requireError) {
        // throw original error if es module is detected
        if (requireError.code === 'ERR_REQUIRE_ESM') {
          throw importError;
        } else {
          throw requireError;
        }
      }
    }
  }
  if (config == null || typeof config !== 'object' || Array.isArray(config)) {
    throw Error(`Invalid config file "${configFile}"`);
  }
  return config;
};

const isFile = async (file) => {
  try {
    const stats = await fs.promises.stat(file);
    return stats.isFile();
  } catch {
    return false;
  }
};

const loadConfig = async (configFile, cwd = process.cwd()) => {
  if (configFile != null) {
    if (path.isAbsolute(configFile)) {
      return await importConfig(configFile);
    } else {
      return await importConfig(path.join(cwd, configFile));
    }
  }
  let dir = cwd;
  // eslint-disable-next-line no-constant-condition
  while (true) {
    const js = path.join(dir, 'svgo.config.js');
    if (await isFile(js)) {
      return await importConfig(js);
    }
    const mjs = path.join(dir, 'svgo.config.mjs');
    if (await isFile(mjs)) {
      return await importConfig(mjs);
    }
    const cjs = path.join(dir, 'svgo.config.cjs');
    if (await isFile(cjs)) {
      return await importConfig(cjs);
    }
    const parent = path.dirname(dir);
    if (dir === parent) {
      return null;
    }
    dir = parent;
  }
};
exports.loadConfig = loadConfig;

const optimize = (input, config) => {
  if (config == null) {
    config = {};
  }
  if (typeof config !== 'object') {
    throw Error('Config should be an object');
  }
  return optimizeAgnostic(input, {
    ...config,
    js2svg: {
      // platform specific default for end of line
      eol: os.EOL === '\r\n' ? 'crlf' : 'lf',
      ...config.js2svg,
    },
  });
};
exports.optimize = optimize;


code:
'use strict';

const {
  defaultPlugins,
  resolvePluginConfig,
  extendDefaultPlugins,
} = require('./svgo/config.js');
const { parseSvg } = require('./parser.js');
const { stringifySvg } = require('./stringifier.js');
const { invokePlugins } = require('./svgo/plugins.js');
const JSAPI = require('./svgo/jsAPI.js');
const { encodeSVGDatauri } = require('./svgo/tools.js');

exports.extendDefaultPlugins = extendDefaultPlugins;

const optimize = (input, config) => {
  if (config == null) {
    config = {};
  }
  if (typeof config !== 'object') {
    throw Error('Config should be an object');
  }
  const maxPassCount = config.multipass ? 10 : 1;
  let prevResultSize = Number.POSITIVE_INFINITY;
  let svgjs = null;
  const info = {};
  if (config.path != null) {
    info.path = config.path;
  }
  for (let i = 0; i < maxPassCount; i += 1) {
    info.multipassCount = i;
    // TODO throw this error in v3
    try {
      svgjs = parseSvg(input, config.path);
    } catch (error) {
      return { error: error.toString(), modernError: error };
    }
    if (svgjs.error != null) {
      if (config.path != null) {
        svgjs.path = config.path;
      }
      return svgjs;
    }
    const plugins = config.plugins || defaultPlugins;
    if (Array.isArray(plugins) === false) {
      throw Error(
        "Invalid plugins list. Provided 'plugins' in config should be an array."
      );
    }
    const resolvedPlugins = plugins.map(resolvePluginConfig);
    const globalOverrides = {};
    if (config.floatPrecision != null) {
      globalOverrides.floatPrecision = config.floatPrecision;
    }
    svgjs = invokePlugins(svgjs, info, resolvedPlugins, null, globalOverrides);
    svgjs = stringifySvg(svgjs, config.js2svg);
    if (svgjs.data.length < prevResultSize) {
      input = svgjs.data;
      prevResultSize = svgjs.data.length;
    } else {
      if (config.datauri) {
        svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);
      }
      if (config.path != null) {
        svgjs.path = config.path;
      }
      return svgjs;
    }
  }
  return svgjs;
};
exports.optimize = optimize;

/**
 * The factory that creates a content item with the helper methods.
 *
 * @param {Object} data which is passed to jsAPI constructor
 * @returns {JSAPI} content item
 */
const createContentItem = (data) => {
  return new JSAPI(data);
};
exports.createContentItem = createContentItem;