Fix bin/publish: copy docs.dist from project root

Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

View File

@@ -0,0 +1,536 @@
# Changes to PostCSS Preset Env
### 7.4.3 (March 19, 2022)
- Fix manually enabling a feature that doesn't meet the required vendor implementations. [#310](https://github.com/csstools/postcss-plugins/issues/310)
- Updated `postcss-clamp` to `4.1.0` (minor)
- Updated `@csstools/postcss-color-function` to `1.0.3` (patch)
- Updated `@csstools/postcss-is-pseudo-class` to `2.0.1` (patch)
- Updated `@csstools/postcss-oklab-function` to `1.0.2` (patch)
- Updated `@csstools/postcss-progressive-custom-properties` to `1.3.0` (minor)
- Updated `autoprefixer` to `10.4.4` (patch)
- Updated `browserslist` to `4.20.2` (patch)
- Updated `cssdb` to `6.5.0` (minor)
- Updated `postcss-custom-properties` to `12.1.5` (patch)
- Updated `postcss-double-position-gradients` to `3.1.1` (patch)
- Updated `postcss-env-function` to `4.0.6` (patch)
- Updated `postcss-lab-function` to `4.1.2` (patch)
- Updated `postcss-nesting` to `10.1.3` (patch)
- Remove internal patch for `postcss-clamp`
- Document all features. [https://github.com/csstools/postcss-preset-env/issues/156](https://github.com/csstools/postcss-preset-env/issues/156)
[see the list of features](https://github.com/csstools/postcss-plugins/blob/main/plugin-packs/postcss-preset-env/FEATURES.md)
### 7.4.2 (March 2, 2022)
- Adding internal patch for `postcss-clamp` to fix issue when `clamp` was being used with any other values along. [#274](https://github.com/csstools/postcss-plugins/issues/274)
### 7.4.1 (February 17, 2022)
- Updated `postcss-clamp` to `4.0.0` (major)
### 7.4.0 (February 16, 2022)
- Added `@csstools/color-function` <br/> [Check the plugin README](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-color-function#readme) for usage details.
- Added `@csstools/oklab-function` <br/> [Check the plugin README](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-oklab-function#readme) for usage details.
- Added `@csstools/ic-unit` <br/> [Check the plugin README](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-ic-unit#readme) for usage details.
- Updated `@csstools/postcss-progressive-custom-properties` to `1.2.0` (minor)
- Updated `cssdb` to `6.3.1` (patch)
- Updated `postcss-double-position-gradients` to `3.1.0` (minor)
- Updated `postcss-lab-function` to `4.1.1` (patch)
### 7.3.3 (February 13, 2022)
- Updated `cssdb` to `6.3.0` (minor)
### 6.7.1 (February 13, 2022)
- Added notice when using preset-env with PostCSS > 7 to prompt to upgrade.
### 7.3.2 (February 12, 2022)
- Updated `postcss-lab-function` to `4.1.0` (minor)
`postcss-lab-function` now supports [wide gamut colors](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-lab-function#displayp3) and [out of gamut color mapping](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-lab-function#displayp3).
- Updated `css-blank-pseudo` to `3.0.3` (patch)
- Updated `css-has-pseudo` to `3.0.4` (patch)
- Updated `cssdb` to `6.2.1` (patch)
- Updated `postcss-color-functional-notation` to `4.2.2` (patch)
- Updated `postcss-color-hex-alpha` to `8.0.3` (patch)
- Updated `postcss-dir-pseudo-class` to `6.0.4` (patch)
- Updated `postcss-double-position-gradients` to `3.0.5` (patch)
- Updated `postcss-env-function` to `4.0.5` (patch)
- Updated `postcss-focus-visible` to `6.0.4` (patch)
- Updated `postcss-focus-within` to `5.0.4` (patch)
- Updated `postcss-gap-properties` to `3.0.3` (patch)
- Updated `postcss-image-set-function` to `4.0.6` (patch)
- Updated `postcss-logical` to `5.0.4` (patch)
- Updated `postcss-overflow-shorthand` to `3.0.3` (patch)
- Updated `postcss-place` to `7.0.4` (patch)
- Updated `postcss-pseudo-class-any-link` to `7.1.1` (patch)
- Updated `@csstools/postcss-progressive-custom-properties` to `1.1.0`.
### 7.3.1 (February 3, 2022)
- Ensured that `debug` option uses a single instance per run, this allows for `debug` to work on parallel runs within CI environments.
- Normalized exports to support both Common JS and ESM within our modules. This allows for the package to be processable via Webpack [#221](https://github.com/csstools/postcss-plugins/issues/221)
- Updated `postcss-opacity-percentage` to `1.1.2` (patch).
### 7.3.0 (January 31, 2022)
- Added `@csstools/postcss-is-pseudo-class` <br/> [Check the plugin README](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-is-pseudo-class#readme) for usage details.
- Added `@csstools/postcss-hwb-function` <br/> [Check the plugin README](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-hwb-function#readme) for usage details.
- Added `postcss-opacity-percentage` <br/> [Check the plugin README](https://github.com/mrcgrtz/postcss-opacity-percentage#readme) for usage details.
- Added `postcss-clamp` <br/> [Check the plugin README](https://github.com/polemius/postcss-clamp#readme) for usage details.
- Added `@csstools/postcss-normalize-display-values` <br/> [Check the plugin README](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-normalize-display-values#readme) for usage details.
- Added `@csstools/postcss-font-format-keywords` <br/> [Check the plugin README](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-font-format-keywords#readme) for usage details.
- Added `debug` [option](https://github.com/csstools/postcss-plugins/tree/main/plugin-packs/postcss-preset-env#debug) that enables extra debugging information while processing the CSS.
- Added `enableClientSidePolyfills` [option](https://github.com/csstools/postcss-plugins/tree/main/plugin-packs/postcss-preset-env#enableclientsidepolyfills) that allows you to control every single plugin that requires a browser library to fully work. Defaults to `true` so they're enabled by default.
- Added `minimumVendorImplementations` [option](https://github.com/csstools/postcss-plugins/tree/main/plugin-packs/postcss-preset-env#minimumvendorimplementations) that allows you to enable/disable plugins based on their implementation status in browsers.
- Fix sourcemaps for `image-set()` function.
- Removed `caniuse-lite` dependency. This results not only in lower package size but also in better feature detection lead by changes on CSSDB.
- Updated `cssdb` to `6.1.0` (major).
- Updated `css-prefers-color-scheme` to `6.0.3` (patch)
- Updated `postcss-custom-properties` to `12.1.4` (patch)
- Updated `postcss-image-set-function` to `4.0.5` (patch)
- Updated `postcss-pseudo-class-any-link` to `7.1.0` (minor)
### 7.2.3 (January 12, 2022)
- Enhanced `importFrom` / `exportTo` so it's harder to cause unexpected issues by different shapes of data.
### 7.2.2 (January 12, 2022)
- Updated `postcss-logical` to `5.0.3` (patch)
- Updated `postcss-custom-properties` to `12.1.2` (patch)
### 7.2.1 (January 12, 2022)
- Always run plugins with side effects if certain options are set. [#140](https://github.com/csstools/postcss-plugins/issues/140)
- `custom-media-queries`
- `custom-properties`
- `environment-variables`
- `custom-selectors`
- Updated `caniuse-lite` to `1.0.30001299` (minor)
- Updated `css-blank-pseudo` to `3.0.2` (minor)
- Updated `css-has-pseudo` to `3.0.3` (minor)
- Updated `postcss-color-rebeccapurple` to `7.0.2` (minor)
- Updated `postcss-custom-properties` to `12.1.0` (minor)
- Updated `postcss-dir-pseudo-class` to `6.0.3` (minor)
- Updated `postcss-nesting` to `10.1.2` (minor)
This will ensure that CSS transforms that are not a browser polyfill are still applied.
⚠️ A future major version `postcss-preset-env` will remove this behavior completely.
### 7.2.0 (January 2, 2022)
- Added warnings and useful messages when a feature that doesn't exist is configured. [156](https://github.com/csstools/postcss-preset-env/issues/156).
When configured with these options for example:
```js
options: {
features: {
"custom-media": true,
"postcss-logical": true,
"postcss-logica": true,
}
}
```
It will yield the following warnings:
```bash
Unknown feature: "custom-media" did you mean: "custom-media-queries"
Unknown feature: "postcss-logical" did you mean: "logical-properties-and-values"
Unknown feature: "postcss-logica" did you mean: "logical-properties-and-values"
```
- Removed Sourcemaps from package tarball.
- Moved CLI to CLI Package. See [announcement](https://github.com/csstools/postcss-plugins/discussions/121).
- Updated `postcss` to 8.4 (minor)
- Updated `autoprefixer` to 10.4.1 (patch)
- Updated `caniuse-lite` to 1.0.30001295 (patch)
- Updated `css-blank-pseudo` to 3.0.1 (patch)
- Updated `css-has-pseudo` to 3.0.2 (patch)
- Updated `css-prefers-color-scheme` to 6.0.2 (patch)
- Updated `postcss-color-functional-notation` to 4.2.1 (minor)
- Updated `postcss-color-hex-alpha` to 8.0.2 (patch)
- Updated `postcss-custom-properties` to 12.0.2 (patch)
- Updated `postcss-dir-pseudo-class` to 6.0.2 (patch)
- Updated `postcss-double-position-gradients` to 3.0.4 (patch)
- Updated `postcss-env-function` to 4.0.4 (patch)
- Updated `postcss-focus-visible` to 6.0.3 (patch)
- Updated `postcss-focus-within` to 5.0.3 (patch)
- Updated `postcss-gap-properties` to 3.0.2 (patch)
- Updated `postcss-image-set-function` to 4.0.4 (patch)
- Updated `postcss-lab-function` to 4.0.3 (patch)
- Updated `postcss-logical` to 5.0.2 (patch)
- Updated `postcss-nesting` to 10.1.1 (minor)
- Updated `postcss-overflow-shorthand` to 3.0.2 (patch)
- Updated `postcss-place` to 7.0.3 (patch)
- Updated `postcss-pseudo-class-any-link` to 7.0.2 (patch)
### 7.1.0 (December 22, 2021)
- Updated [`postcss-nesting` to `10.1.0` (minor)](https://github.com/csstools/postcss-plugins/blob/main/plugins/postcss-nesting/CHANGELOG.md#changes-to-postcss-nesting)
### 7.0.2 (December 16, 2021)
- Fixed unexpected behaviours when using different versions of `postcss-values-parser` across the plugins [228](https://github.com/csstools/postcss-preset-env/issues/228)
- Updated `browserlist` to 4.19.1 (minor)
- Updated `caniuse-lite` to `1.0.30001287` (minor)
- Updated `css-blank-pseudo` to `3.0.0` (major)
- Updated `css-has-pseudo` to `3.0.0` (major)
- Updated `css-prefers-color-scheme` to `6.0.0` (major)
- Updated `postcss-color-functional-notation` to `4.1.0` (minor)
- Updated `postcss-color-hex-alpha` to `8.0.1` (patch)
- Updated `postcss-color-rebeccapurple` to `7.0.1` (patch)
- Updated `postcss-custom-properties` to `12.0.1` (patch)
- Updated `postcss-dir-pseudo-class` to `6.0.1` (patch)
- Updated `postcss-double-position-gradients` to `3.0.3` (patch)
- Updated `postcss-env-function` to `4.0.3` (patch)
- Updated `postcss-focus-visible` to `6.0.2` (patch)
- Updated `postcss-focus-within` to `5.0.2` (patch)
- Updated `postcss-gap-properties` to `3.0.1` (patch)
- Updated `postcss-image-set-function` to `4.0.3` (patch)
- Updated `postcss-lab-function` to `4.0.2` (patch)
- Updated `postcss-logical` to `5.0.1` (patch)
- Updated `postcss-nesting` to `10.0.3` (patch)
- Updated `postcss-overflow-shorthand` to `3.0.1` (patch)
- Updated `postcss-place` to `7.0.2` (patch)
- Updated `postcss-pseudo-class-any-link` to `7.0.1` (patch)
- Updated documentation
- Updated `postcss` to be a peer dependency.
### 7.0.1 (November 19, 2021)
- Fixed infinite loop in double-position-gradients [223](https://github.com/csstools/postcss-preset-env/issues/223)
- Fixed "Unknown word" errors in when parsing CSS values [224](https://github.com/csstools/postcss-preset-env/issues/224)
- Fixed "undefined" CSS values after transforms with postcss-place [225](https://github.com/csstools/postcss-preset-env/issues/225)
- Updated `postcss-color-functional-notation` to 4.0.1 (patch)
- Updated `postcss-double-position-gradients` to 3.0.1 (patch)
- Updated `postcss-env-function` to 4.0.2 (patch)
- Updated `postcss-image-set-function` to 4.0.2 (patch)
- Updated `postcss-lab-function` to 4.0.1 (patch)
- Updated `postcss-nesting` to 10.0.2 (patch)
- Updated `postcss-place` to 7.0.1 (patch)
### 7.0.0 (November 16, 2021)
- Updated `autoprefixer` to 10.4.0 (major)
- Updated `browserslist` to 4.17.5 (minor)
- Updated `caniuse-lite` to 1.0.30001272 (patch)
- Updated `css-blank-pseudo` to 2.0.0 (major)
- Updated `css-has-pseudo` to 2.0.0 (major)
- Updated `css-prefers-color-scheme` to 5.0.0 (major)
- Updated `cssdb` to 5.0.0 (major)
- Updated `postcss` to 8.3.0 (major)
- Updated `postcss-attribute-case-insensitive` to 5.0.0 (major)
- Updated `postcss-color-functional-notation` to 4.0.0 (major)
- Updated `postcss-color-hex-alpha` to 8.0.0 (major)
- Updated `postcss-color-rebeccapurple` to 7.0.0 (major)
- Updated `postcss-custom-media` to 8.0.0 (major)
- Updated `postcss-custom-properties` to 12.0.0 (major)
- Updated `postcss-custom-selectors` to 6.0.0 (major)
- Updated `postcss-dir-pseudo-class` to 6.0.0 (major)
- Updated `postcss-double-position-gradients` to 3.0.0 (major)
- Updated `postcss-env-function` to 4.0.1 (major)
- Updated `postcss-focus-visible` to 6.0.1 (major)
- Updated `postcss-focus-within` to 5.0.1 (major)
- Updated `postcss-font-variant` to 5.0.0 (major)
- Updated `postcss-gap-properties` to 3.0.0 (major)
- Updated `postcss-image-set-function` to 4.0.0 (major)
- Updated `postcss-initial` to 3.0.4 (patch)
- Updated `postcss-lab-function` to 4.0.0 (major)
- Updated `postcss-logical` to 5.0.0 (major)
- Updated `postcss-media-minmax` to 5.0.0 (major)
- Updated `postcss-nesting` to 10.0.0 (major)
- Updated `postcss-overflow-shorthand` to 3.0.0 (major)
- Updated `postcss-page-break` to 3.0.4 (major)
- Updated `postcss-place` to 7.0.0 (major)
- Updated `postcss-pseudo-class-any-link` to 7.0.0 (major)
- Updated `postcss-replace-overflow-wrap` to 4.0.0 (major)
- Removed `postcss-selector-matches`
- Removed `postcss-color-gray`
- Updated support for Node 12+ (major)
### 6.7.0 (July 8, 2019)
- Fixed the issue of autoprefixer alerting an upcoming change to the API
- Updated `autoprefixer` to 9.6.1 (minor)
- Updated `browserslist` to 4.6.4 (minor)
- Updated `cssdb` to 4.4.0 (minor)
- Updated `caniuse-lite` to 1.0.30000981 (patch)
- Updated `postcss` to 7.0.17 (patch)
- Updated `postcss-color-hex-alpha` to 5.0.3 (patch)
- Updated `postcss-custom-media` to 7.0.8 (patch)
- Updated `postcss-custom-properties` to 8.0.11 (patch)
### 6.6.0 (February 28, 2019)
- Moved browserslist detection from using each input file per process to using
the working directory on intialization, as was implied by the documentation.
If fixing this previously undocumented behavior causes any harm to existing
projects, it can be easily rolled back in a subsequent patch. For the
majority of projects — those with a singular browserslist configuration and
potentially many individually processed CSS files — we should expect reported
build times around 35 seconds to drop to less than 2 seconds.
- Updated `browserslist` to 4.4.2 (minor)
- Updated `autoprefixer` to 9.4.9 (patch)
- Updated `caniuse-lite` to 1.0.30000939 (patch)
- Updated `postcss` to 7.0.14 (patch)
- Updated `postcss-attribute-case-insensitive` to 4.0.1 (patch)
### 6.5.0 (December 12, 2018)
- Added `css-blank-pseudo` polyfill
- Added `css-has-pseudo` polyfill
- Updated `autoprefixer` to 9.4.2 (minor)
- Updated `browserslist` to 4.3.5 (minor)
- Updated `caniuse-lite` to 1.0.30000918 (patch)
- Updated `css-prefers-color-scheme` to 3.1.1 (minor, patch for this project)
- Updated `cssdb` to 4.3.0 (minor)
- Updated `postcss` to 7.0.6 (patch)
### 6.4.0 (November 6, 2018)
- Fixed `exportTo` option to export Custom Media, Custom Properties, and Custom
Selectors all to the same function, object, or file
- Added `css-prefers-color-scheme` 3.0.0 (major, non-breaking for this project)
- Updated `cssdb` to 4.2.0 (minor)
### 6.3.1 (November 5, 2018)
- Updated `caniuse-lite` to 1.0.30000905 (patch)
- Updated `postcss-custom-properties` to 8.0.9 (patch)
### 6.3.0 (October 28, 2018)
- Added `postcss-double-position-gradients` 1.0.0 (major, non-breaking for this project)
- Updated `autoprefixer` to 9.3.1 (minor)
- Updated `browserslist` to 4.3.4 (patch)
- Updated `caniuse-lite` to 1.0.30000899 (patch)
- Updated `cssdb` to 4.1.0 (major, non-breaking for this project)
### 6.2.0 (October 22, 2018)
- Updated `autoprefixer` to 9.2.1 (minor)
- Updated `browserslist` to 4.3.1 (minor)
### 6.1.2 (October 19, 2018)
- Updated `browserslist` to 4.2.1 (patch)
- Updated `caniuse-lite` to 1.0.30000893 (patch)
- Updated `postcss-custom-media` to 7.0.7 (patch)
### 6.1.1 (October 12, 2018)
- Updated: `postcss-custom-media` to 7.0.6 (patch)
### 6.1.0 (October 10, 2018)
- Added: `postcss-color-gray`
- Added: Passing `autoprefixer: false` disables autoprefixer
- Updated: `browserslist` to 4.2.0 (minor)
- Updated: `caniuse-lite` to 1.0.30000890 (patch)
### 6.0.10 (October 2, 2018)
- Updated: `postcss-custom-properties` to 8.0.8 (patch)
### 6.0.9 (October 2, 2018)
- Updated: `browserslist` to 4.1.2 (patch)
- Updated: `postcss` to 7.0.5 (patch)
- Updated: `postcss-custom-properties` to 8.0.7 (patch)
### 6.0.8 (October 1, 2018)
- Updated: `caniuse-lite` to 1.0.30000888 (patch)
- Updated: `postcss` to 7.0.4 (patch)
**Did you hear? PostCSS Preset Env is now part of Create React App!** 🎉
### 6.0.7 (September 23, 2018)
- Updated: `postcss` to 7.0.3 (patch)
- Updated: `postcss-custom-properties` to 8.0.6 (patch)
### 6.0.6 (September 23, 2018)
- Updated: `postcss-custom-media` to 7.0.4 (patch)
### 6.0.5 (September 23, 2018)
- Updated: `postcss-color-mod-function` to 3.0.3 (patch)
### 6.0.4 (September 23, 2018)
- Updated: `caniuse-lite` to 1.0.30000887 (patch)
- Updated: `postcss-color-mod-function` to 3.0.2 (patch)
### 6.0.3 (September 21, 2018)
- Updated: `caniuse-lite` to 1.0.30000885 (patch)
- Updated: `postcss-custom-properties` to 8.0.5 (patch)
### 6.0.2 (September 20, 2018)
- Fixed: Do not break on an empty `importFrom` object
- Fixed: Actually run `postcss-env-function`
### 6.0.1 (September 20, 2018)
- Fixed: Issue with the `system-ui` font family polyfill by replacing
`postcss-font-family-system-ui` with an internal polyfill, at least until the
problem with the original plugin is resolved.
### 6.0.0 (September 20, 2018)
- Added: Support for PostCSS 7+
- Added: Support for PostCSS Values Parser 2+
- Added: Support for PostCSS Selector Parser 5+
- Added: Support for Node 6+
- Updated: All 28 plugins
### 5.4.0 (July 25, 2018)
- Added: `toggle` option to override which features are enabled or disabled
- Deprecated: toggle features with `toggle`, not `features`
### 5.3.0 (July 24, 2018)
- Updated: `postcss-lab-function` to v1.1.0 (minor update)
### 5.2.3 (July 21, 2018)
- Updated: `postcss-color-mod-function` to v2.4.3 (patch update)
### 5.2.2 (July 13, 2018)
- Updated: `autoprefixer` to v8.6.5 (patch update)
- Updated: `caniuse-lite` to v1.0.30000865 (patch update)
- Updated: `postcss-color-functional-notation` to v1.0.2 (patch update)
### 5.2.1 (June 26, 2018)
- Updated: `caniuse-lite` to v1.0.30000859 (patch update)
- Updated: `postcss-attribute-case-insensitive` to v3.0.1 (patch update)
### 5.2.0 (June 25, 2018)
- Updated: `autoprefixer` to v8.6.3 (minor update)
- Updated: `caniuse-lite` to v1.0.30000858 (patch update)
- Updated: `postcss` to 6.0.23 (patch update)
- Updated: `postcss-nesting` to v6.0.0 (major internal update, non-breaking for this project)
### 5.1.0 (May 21, 2018)
- Added: `autoprefixer` option to pass options into autoprefixer
- Updated: `autoprefixer` to v8.5.0 (minor update)
- Updated: `browserslist` to v3.2.8 (patch update)
- Updated: `caniuse-lite` to v1.0.30000844 (patch update)
- Updated: `postcss-color-functional-notation` to v1.0.1 (patch update)
### 5.0.0 (May 11, 2018)
- Added: `autoprefixer`
- Added: `postcss-color-functional-notation`
- Added: `postcss-env-function`
- Added: `postcss-lab-function`
- Added: `postcss-place`
- Added: `postcss-gap-properties`
- Added: `postcss-overflow-shorthand`
- Updated: `cssdb` to v3.1.0 (major update)
- Updated: In conformance with cssdb v3, the default stage is now 2
- Updated: `postcss-attribute-case-insensitive` to v3.0.0 (major update)
- Updated: `postcss-pseudo-class-any-link` to v5.0.0 (major update)
- Updated: `postcss-image-set-function` to v2.0.0 (major update)
- Updated: `postcss-dir-pseudo-class` to v4.0.0 (major update)
- Updated: `postcss-color-rebeccapurple` to v3.1.0 (minor update)
- Updated: `postcss` to v6.0.22 (patch update)
- Updated: `browserslist` to v3.2.7 (patch update)
- Updated: `caniuse-lite` to v1.0.30000839 (patch update)
All plugins now conform to the latest stable releases of `postcss-value-parser`
v1.5.0 and `postcss-selector-parser` v4.0.0.
### 4.1.0 (April 23, 2018)
- Updated: `browserslist` to v3.2.5 (patch update)
- Updated: `caniuse-lite` to v1.0.30000830 (patch update)
- Updated: `postcss-apply` to v0.10.0 (minor update)
- Updated: `postcss-nesting` to v5.0.0 (major update, non-breaking for this project)
### 4.0.0 (April 7, 2018)
- Added: `postcss-focus-within`
- Updated: `postcss-focus-visible` to v3.0.0 (major update)
- Updated: `caniuse-lite` to v1.0.30000824 (patch update)
- Updated: `cssdb` to v2.0.0 (major update)
- Changed: All `specificationId` names to new `id` names for the `cssdb` update.
### 3.5.0 (April 5, 2018)
- Fixed: `selectors-matches-pseudo` mapping to allow `:matches` polyfilling
- Updated: `postcss-dir-pseudo-class` to v3.0.0 (major update, non-breaking for this project)
- Updated: `postcss-logical` to v1.1.1 (minor update)
- Updated: `postcss` to v6.0.21 (patch update)
- Updated: `browserslist` to v3.2.4 (patch update)
- Updated: `caniuse-lite` to v1.0.30000823 (patch update)
### 3.4.0 (March 18, 2018)
- Updated: `browserslist` to v3.2.0 (minor update)
- Updated: `postcss` to v6.0.20 (patch update)
- Updated: `postcss-image-set-polyfill` to `@csstools/postcss-image-set-function` (hopefully temporarily)
### 3.3.0 (March 16, 2018)
- Updated: `postcss-apply` to v0.9.0 (minor update)
- Updated: `browserslist` to v3.1.2 (patch update)
- Updated: `caniuse-lite` to v1.0.30000815 (patch update)
- Updated: distribution to cjs and es bundles
### 3.2.2 (February 27, 2018)
- Updated: `postcss-color-mod-function` to v2.4.2 (patch update)
### 3.2.1 (February 21, 2018)
- Updated: Use the latest tested version of all dependencies
### 3.2.0 (February 18, 2018)
- Added: `postcss-page-break` which has moved here from Autoprefixer
### 3.1.0 (February 17, 2018)
- Added: `postcss-focus-visible`
### 3.0.0 (February 16, 2018)
- Updated: `postcss-color-mod-function` to v2.4 (minor update)
- Updated: `postcss-custom-properties` to v7.0 (major update)
### 2.2.0 (February 14, 2018)
- Updated: `browserslist` to v3.1 (major update)
- Updated: `postcss-color-mod-function` to v2.3 (minor update)
- Improved: cleaned up one reusable variable and added a few tests
### 2.1.0 (January 22, 2018)
- Updated: `cssdb` to v1.5 (minor update)
- Updated: `postcss-color-mod-function` to v2.2 (major update)
- Updated: `postcss-font-family-system-ui` to v3.0 (repo update)
### 2.0.0 (January 16, 2018)
- Initial version
### 1.0.0 (December 20, 2017)
- Unsupported version accidentally published by a member of the community

View File

@@ -0,0 +1,108 @@
# CC0 1.0 Universal
## Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an “owner”) of an original work of
authorship and/or a database (each, a “Work”).
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific works
(“Commons”) that the public can reliably and without fear of later claims of
infringement build upon, modify, incorporate in other works, reuse and
redistribute as freely as possible in any form whatsoever and for any purposes,
including without limitation commercial purposes. These owners may contribute
to the Commons to promote the ideal of a free culture and the further
production of creative, cultural and scientific works, or to gain reputation or
greater distribution for their Work in part through the use and efforts of
others.
For these and/or other purposes and motivations, and without any expectation of
additional consideration or compensation, the person associating CC0 with a
Work (the “Affirmer”), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and
publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights (“Copyright and
Related Rights”). Copyright and Related Rights include, but are not limited
to, the following:
1. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
2. moral rights retained by the original author(s) and/or performer(s);
3. publicity and privacy rights pertaining to a persons image or likeness
depicted in a Work;
4. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(i), below;
5. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
6. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
7. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations
thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmers Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the “Waiver”). Affirmer
makes the Waiver for the benefit of each member of the public at large and
to the detriment of Affirmers heirs and successors, fully intending that
such Waiver shall not be subject to revocation, rescission, cancellation,
termination, or any other legal or equitable action to disrupt the quiet
enjoyment of the Work by the public as contemplated by Affirmers express
Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmers express Statement of Purpose. In addition, to the extent the
Waiver is so judged Affirmer hereby grants to each affected person a
royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmers Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the “License”). The License
shall be deemed effective as of the date CC0 was applied by Affirmer to the
Work. Should any part of the License for any reason be judged legally
invalid or ineffective under applicable law, such partial invalidity or
ineffectiveness shall not invalidate the remainder of the License, and in
such case Affirmer hereby affirms that he or she will not (i) exercise any
of his or her remaining Copyright and Related Rights in the Work or (ii)
assert any associated claims and causes of action with respect to the Work,
in either case contrary to Affirmers express Statement of Purpose.
4. Limitations and Disclaimers.
1. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
2. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or
otherwise, including without limitation warranties of title,
merchantability, fitness for a particular purpose, non infringement, or
the absence of latent or other defects, accuracy, or the present or
absence of errors, whether or not discoverable, all to the greatest
extent permissible under applicable law.
3. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any persons Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the Work.
4. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
http://creativecommons.org/publicdomain/zero/1.0/.

View File

@@ -0,0 +1,522 @@
# PostCSS Preset Env [<img src="https://postcss.github.io/postcss/logo.svg" alt="PostCSS" width="90" height="90" align="right">][postcss]
[<img alt="npm version" src="https://img.shields.io/npm/v/postcss-preset-env.svg" height="20">][npm-url]
[<img alt="build status" src="https://github.com/csstools/postcss-plugins/workflows/test/badge.svg" height="20">][cli-url]
[![install size][package-phobia-badge]][package-phobia]
[<img alt="Discord" src="https://shields.io/badge/Discord-5865F2?logo=discord&logoColor=white">][discord]
[PostCSS Preset Env] lets you convert modern CSS into something most browsers
can understand, determining the polyfills you need based on your targeted
browsers or runtime environments.
## Quick start
[PostCSS Preset Env] is a [PostCSS] plugin.<br>
If you are already using [PostCSS] to build your CSS, you can simply add [PostCSS Preset Env] to your configuration.
- Install `postcss-preset-env` from npm.
- Add `postcss-preset-env` to your configuration:
```js
const postcssPresetEnv = require('postcss-preset-env');
const yourConfig = {
plugins: [
postcssPresetEnv(/* pluginOptions */)
]
}
```
_[Read more on how to use and install PostCSS Preset Env.](#usage)_
## How does it work?
[PostCSS Preset Env] is a Plugin Pack for [PostCSS]. It leverages the list of the features we keep an eye from [CSSDB][cssdb] and applies plugins, so you can use those new features without having to worry about browser support.
CSSDB exposes the browser support that each feature has which can come from [Can I Use](https://caniuse.com/css-all) or from [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/all) (through [mdn/browser-compat-data](https://github.com/mdn/browser-compat-data)).
By providing a list of browser targets for your project, plugins that aren't needed will be skipped. Over time your targets might change and by updating the settings your CSS bundle will only ever contain the needed fallbacks.
What [PostCSS Preset Env] does is to take the support data that comes from MDN and Can I Use and determine from a [browserlist](https://github.com/browserslist/browserslist) whether those transformations are needed. It also packs [Autoprefixer](https://github.com/postcss/autoprefixer) within and shares the list with it, so prefixes are only applied when you're going to need them given your browser support list.
### Glossary:
* **Browser list option**: [Browserlist](https://github.com/browserslist/browserslist) is a package that gives you a list of browsers for a given query. For example, `chrome < 42` will give you a list of every Chrome version that has been released up to, but not including, 42.
* **Browser support stats**: Features get introduced on browsers at certain versions. They're often visible on [MDN](https://developer.mozilla.org/en-US/) and [Can I Use](https://caniuse.com/). Comparing these stats with the needed _support_ for your project tells you if it's safe to use a feature or not.
* **CSS Feature**: A CSS feature is often part of some spec that enables a specific feature. For example, `hwb` functional notation lets you express a given color according to its hue, whiteness, and blackness. This is part of the CSS Color 4 Spec.
* **CSS Spec**: A Spec is a document that collects new features, what problems are they trying to solve and how it's intended to be solved (generally). This is usually an evolving document that goes over lengthy discussions between several people from different companies.
* **Plugin**: A plugin is package that's intended (usually) to enable a new CSS Feature by leveraging PostCSS. This doesn't need to be part of any spec. An example of the latter is [PostCSS Mixins](https://github.com/postcss/postcss-mixins), a concept that existed on Less or Sass, but it's not part of any spec. This plugin plack **only** packs plugins that enable features acknowledged by the World Wide Web Consortium (W3C) which will then be implemented by browsers later.
* **Polyfill**: A polyfill is a piece of code (usually JavaScript on the Web) used to provide modern functionality on older browsers that do not natively support it. A polyfill _should_ be indistinguishable from the native behaviour.
Here's a quick example of the syntax you can leverage by using [PostCSS Preset Env].
```pcss
@custom-media --viewport-medium (width <= 50rem);
@custom-selector :--heading h1, h2, h3, h4, h5, h6;
:root {
--mainColor: #12345678;
}
body {
color: var(--mainColor);
font-family: system-ui;
overflow-wrap: break-word;
}
:--heading {
background-image: image-set(url(img/heading.png) 1x, url(img/heading@2x.png) 2x);
@media (--viewport-medium) {
margin-block: 0;
}
}
a {
color: rgb(0 0 100% / 90%);
&:hover {
color: rebeccapurple;
}
}
/* becomes */
:root {
--mainColor: rgba(18, 52, 86, 0.47059);
}
body {
color: rgba(18, 52, 86, 0.47059);
color: var(--mainColor);
font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Droid Sans, Helvetica Neue;
word-wrap: break-word;
}
h1, h2, h3, h4, h5, h6 {
background-image: url(img/heading.png);
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
h1, h2, h3, h4, h5, h6 {
background-image: url(img/heading@2x.png)
}
}
@media (max-width: 50rem) {
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0;
}
}
a {
color: rgba(0, 0, 255, 0.9)
}
a:hover {
color: #639;
}
```
Without any configuration options, [PostCSS Preset Env] enables **Stage 2**
features and supports **all** browsers.
[![Transform with Preset Env][readme-transform-with-preset-env-img]][readme-transform-with-preset-env-url]
[![Style with Preset Env][readme-style-with-preset-env-img]][readme-style-with-preset-env-url]
⚠️ Please note that some features need a companion library that makes
the feature work. While we try to avoid this requirement, there are instances
in which this isn't possible to polyfill a new behaviour with _just_ CSS.
[See the list below](#plugins-that-need-client-library).
## Usage
Add [PostCSS Preset Env] to your project:
```bash
npm install postcss-preset-env --save-dev
```
Use [PostCSS Preset Env] as a [PostCSS] plugin:
```js
const postcss = require('postcss');
const postcssPresetEnv = require('postcss-preset-env');
postcss([
postcssPresetEnv(/* pluginOptions */)
]).process(YOUR_CSS /*, processOptions */);
```
[PostCSS Preset Env] runs in all Node environments, with special instructions for:
| [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) | [Rollup](INSTALL.md#rollup) |
| --- | --- | --- | --- | --- | --- | --- |
## Options
### stage
The `stage` option determines which CSS features to polyfill, based upon their
stability in the process of becoming implemented web standards.
```js
postcssPresetEnv({ stage: 0 })
```
The `stage` can be `0` (experimental) through `4` (stable), or `false`. Setting
`stage` to `false` will disable every polyfill. Doing this would only be useful
if you intended to exclusively use the [`features`](#features) option.
Default: `2`
### minimumVendorImplementations
The `minimumVendorImplementations` option determines which CSS features to polyfill, based their implementation status.
This can be used to enable plugins that are available in browsers regardless of the [spec status](#stage).
```js
postcssPresetEnv({ minimumVendorImplementations: 2 })
```
`minimumVendorImplementations` can be `0` (no vendor has implemented it) through `3` (all major vendors).<br>
Default: `0`
**Note:**
When a feature has not yet been implemented by any vendor it can be considered experimental.<br>
Even with a single implementation it might still change in the future.<br>
Sometimes issues with a feature/specification are only discovered after it becomes available.
A value of `2` is recommended when you want to use only those features that should be [stable](#stability-and-portability).
Having 2 independent implementations is [a critical step in proposals becoming standards](https://www.w3.org/2021/Process-20211102/#implementation-experience) and a good indicator of a feature's stability.
### features
The `features` option enables or disables specific polyfills by ID. Passing
`true` to a specific feature ID will enable its polyfill, while passing `false`
will disable it. [List of Features](https://github.com/csstools/postcss-plugins/blob/main/plugin-packs/postcss-preset-env/FEATURES.md)
```js
postcssPresetEnv({
/* use stage 3 features + css nesting rules */
stage: 3,
features: {
'nesting-rules': true
}
})
```
Passing an object to a specific feature ID will both enable and configure it.
```js
postcssPresetEnv({
/* use stage 3 features + css color-mod (warning on unresolved) */
stage: 3,
features: {
'color-mod-function': { unresolved: 'warn' }
}
})
```
Any polyfills not explicitly enabled or disabled through `features` are
determined by the [`stage`](#stage) option.
### browsers
The `browsers` option determines which polyfills are required based upon the
browsers you are supporting.
[PostCSS Preset Env] supports any standard [browserslist] configuration, which
can be a `.browserslistrc` file, a `browserslist` key in `package.json`, or
`browserslist` environment variables.
The `browsers` option should only be used when a standard browserslist
configuration is not available.
```js
postcssPresetEnv({ browsers: 'last 2 versions' })
```
If no valid browserslist configuration is specified, the
[default browserslist query](https://github.com/browserslist/browserslist#queries)
will be used.
### insertBefore / insertAfter
The `insertBefore` and `insertAfter` keys allow you to insert other PostCSS
plugins into the chain. This is only useful if you are also using sugary
PostCSS plugins that must execute before or after certain polyfills.
Both `insertBefore` and `insertAfter` support chaining one or multiple plugins.
```js
import postcssSimpleVars from 'postcss-simple-vars';
postcssPresetEnv({
insertBefore: {
'all-property': postcssSimpleVars
}
})
```
### autoprefixer
[PostCSS Preset Env] includes [autoprefixer] and [`browsers`](#browsers) option
will be passed to it automatically.
Specifying the `autoprefixer` option enables passing
[additional options](https://github.com/postcss/autoprefixer#options)
into [autoprefixer].
```js
postcssPresetEnv({
autoprefixer: { grid: true }
})
```
Passing `autoprefixer: false` disables autoprefixer.
⚠️ [autoprefixer] has [complex logic to fix CSS Grid in IE en older Edge](https://github.com/postcss/autoprefixer#grid-autoplacement-support-in-ie).
This can have unexpected results with certain features and when [`preserve: true`](#preserve) is used. (defaults to `true`)
```pcss
:root {
--grid-gap: 15px;
}
.test-grid {
grid-gap: var(--grid-gap);
grid-template-columns: repeat(2, 1fr);
}
```
Becomes :
```
.test-grid {
grid-gap: 15px;
grid-gap: var(--grid-gap);
-ms-grid-columns: 1fr var(--grid-gap) 1fr;
grid-template-columns: repeat(2, 1fr);
}
```
The prefixed `-ms-grid-columns` still has a custom property: `1fr var(--grid-gap) 1fr;` which won't work.<br />
This example shows issues with custom properties but other transforms might have similar issues.
If you target IE or older Edge it is best to avoid using other modern features in grid property values.<br />
As a last resort you can set [`preserve: false`](#preserve), we do not advice this as doing so purely to fix issues with CSS grid.
_older Edge is any version before chromium (<79)_
### preserve
The `preserve` option determines whether all plugins should receive a
`preserve` option, which may preserve or remove otherwise-polyfilled CSS. By
default, this option is not configured.
```js
postcssPresetEnv({
preserve: false // instruct all plugins to omit pre-polyfilled CSS
});
```
### importFrom
The `importFrom` option specifies sources where variables like Custom Media,
Custom Properties, Custom Selectors, and Environment Variables can be imported
from, which might be CSS, JS, and JSON files, functions, and directly passed
objects.
```js
postcssPresetEnv({
/*
@custom-media --small-viewport (max-width: 30em);
@custom-selector :--heading h1, h2, h3;
:root { --color: red; }
*/
importFrom: 'path/to/file.css'
});
```
Multiple sources can be passed into this option, and they will be parsed in the
order they are received. JavaScript files, JSON files, functions, and objects
will use different namespaces to import different kinds of variables.
```js
postcssPresetEnv({
importFrom: [
/*
@custom-media --small-viewport (max-width: 30em);
@custom-selector :--heading h1, h2, h3;
:root { --color: red; }
*/
'path/to/file.css',
/* module.exports = {
customMedia: { '--small-viewport': '(max-width: 30em)' },
customProperties: { '--color': 'red' },
customSelectors: { ':--heading': 'h1, h2, h3' },
environmentVariables: { '--branding-padding': '20px' }
} */
'and/then/this.js',
/* {
"custom-media": { "--small-viewport": "(max-width: 30em)" }
"custom-properties": { "--color": "red" },
"custom-selectors": { ":--heading": "h1, h2, h3" },
"environment-variables": { "--branding-padding": "20px" }
} */
'and/then/that.json',
{
customMedia: { '--small-viewport': '(max-width: 30em)' },
customProperties: { '--color': 'red' },
customSelectors: { ':--heading': 'h1, h2, h3' },
environmentVariables: { '--branding-padding': '20px' }
},
() => {
const customMedia = { '--small-viewport': '(max-width: 30em)' };
const customProperties = { '--color': 'red' };
const customSelectors = { ':--heading': 'h1, h2, h3' };
const environmentVariables = { '--branding-padding': '20px' };
return { customMedia, customProperties, customSelectors, environmentVariables };
}
]
});
```
### exportTo
The `exportTo` option specifies destinations where variables like Custom Media,
Custom Properties, Custom Selectors, and Environment Variables can be exported
to, which might be CSS, JS, and JSON files, functions, and directly passed
objects.
```js
postcssPresetEnv({
/*
@custom-media --small-viewport (max-width: 30em);
@custom-selector :--heading h1, h2, h3;
:root { --color: red; }
*/
exportTo: 'path/to/file.css'
});
```
Multiple destinations can be passed into this option as well, and they will be
parsed in the order they are received. JavaScript files, JSON files, and
objects will use different namespaces to import different kinds of variables.
```js
const cachedObject = {};
postcssPresetEnv({
exportTo: [
/*
@custom-media --small-viewport (max-width: 30em);
@custom-selector :--heading h1, h2, h3;
:root { --color: red; }
*/
'path/to/file.css',
/* module.exports = {
customMedia: { '--small-viewport': '(max-width: 30em)' },
customProperties: { '--color': 'red' },
customSelectors: { ':--heading': 'h1, h2, h3' },
environmentVariables: { '--branding-padding': '20px' }
} */
'and/then/this.js',
/* {
"custom-media": { "--small-viewport": "(max-width: 30em)" }
"custom-properties": { "--color": "red" },
"custom-selectors": { ":--heading": "h1, h2, h3" },
"environment-variables": { "--branding-padding": "20px" }
} */
'and/then/that.json',
cachedObject,
variables => {
if ('customProperties' in variables) {
// do something special with customProperties
}
Object.assign(cachedObject, variables);
}
]
});
```
### debug
The `debug` option enables debugging messages to stdout which should be useful to help you debug which features have been enabled/disabled and why.
### enableClientSidePolyfills
The `enableClientSidePolyfills` enables any feature that would need an extra browser library to be loaded into the page for it to work. Defaults to `true`.
Note that manually enabling/disabling features via the "feature" option overrides this flag.
## Stability and Portability
[PostCSS Preset Env] will often include very modern CSS features that are not fully ready yet.
This gives users the chance to play around with these features and provide feedback.
If the specification changes or is abandoned a new major version of the plugin will be released.
This will require you to update your source code so that everything works as expected.
To have more stability between updates of [PostCSS Preset Env] you may set `stage: 3` and/or `minimumVendorImplementations: 2`.
A side effect of staying close to the standard is that you can more easily migrate your project to other tooling all together.
## Plugins list
### Plugins that need client library
This is the current list of features that need a client library with a link
to the polyfill's library.
* `blank-pseudo-class`: [Plugin](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-blank-pseudo) / [Polyfill](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-blank-pseudo/README-BROWSER.md)
* `focus-visible-pseudo-class`: [Plugin](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-focus-visible) / [Polyfill](https://github.com/WICG/focus-visible)
* `focus-within-pseudo-class`: [Plugin](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-focus-within) / [Library](https://github.com/jsxtools/focus-within) / [Polyfill](https://github.com/jsxtools/focus-within/blob/master/README-BROWSER.md)
* `has-pseudo-class`: [Plugin](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-has-pseudo) / [Polyfill](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-has-pseudo/README-BROWSER.md)
* `prefers-color-scheme-query`: [Plugin](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-prefers-color-scheme) / [Polyfill](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-prefers-color-scheme/README-BROWSER.md)
If you want to disable these types of features, please check the [`enableClientSidePolyfills` option](#enableclientsidepolyfills).
### Plugins not affected by Browser Support
Given they have no support they will always be enabled if they match by Stage:
* `blank-pseudo-class`: [Plugin](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-blank-pseudo) / [Polyfill](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-blank-pseudo/README-BROWSER.md)
* `custom-media-queries`: [Plugin](https://github.com/postcss/postcss-custom-media)
* `has-pseudo-class`: [Plugin](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-has-pseudo) / [Polyfill](https://github.com/csstools/postcss-plugins/blob/main/plugins/css-has-pseudo/README-BROWSER.md)
* `image-set-function`: [Plugin](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-image-set-function)
* `media-query-ranges`: [Plugin](https://github.com/postcss/postcss-media-minmax)
* `nesting-rules`: [Plugin](https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting)
[cli-img]: https://github.com/csstools/postcss-plugins/workflows/test/badge.svg
[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
[discord]: https://discord.gg/bUadyRwkJS
[npm-img]: https://img.shields.io/npm/v/postcss-preset-env.svg
[npm-url]: https://www.npmjs.com/package/postcss-preset-env
[package-phobia-badge]: https://packagephobia.com/badge?p=postcss-preset-env
[package-phobia]: https://packagephobia.com/result?p=postcss-preset-env
[autoprefixer]: https://github.com/postcss/autoprefixer
[browserslist]: https://github.com/browserslist/browserslist#readme
[caniuse]: https://caniuse.com/
[cssdb]: https://cssdb.org/
[PostCSS]: https://github.com/postcss/postcss
[PostCSS Preset Env]: https://github.com/csstools/postcss-plugins/tree/main/plugin-packs/postcss-preset-env
[readme-style-with-preset-env-img]: https://csstools.github.io/postcss-preset-env/readme-style-with-preset-env.svg
[readme-style-with-preset-env-url]: https://codepen.io/pen?template=OZRovK
[readme-transform-with-preset-env-img]: https://csstools.github.io/postcss-preset-env/readme-transform-with-preset-env.svg
[readme-transform-with-preset-env-url]: https://csstools.github.io/postcss-preset-env/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
../autoprefixer/bin/autoprefixer

View File

@@ -0,0 +1 @@
../browserslist/cli.js

View File

@@ -0,0 +1 @@
../../../css-blank-pseudo/dist/cli.cjs

View File

@@ -0,0 +1 @@
../../../css-has-pseudo/dist/cli.cjs

View File

@@ -0,0 +1 @@
../../../css-prefers-color-scheme/dist/cli.cjs

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2013 Andrey Sitnik <andrey@sitnik.ru>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,69 @@
# Autoprefixer [![Cult Of Martians][cult-img]][cult]
<img align="right" width="94" height="71"
src="http://postcss.github.io/autoprefixer/logo.svg"
title="Autoprefixer logo by Anton Lovchikov">
[PostCSS] plugin to parse CSS and add vendor prefixes to CSS rules using values
from [Can I Use]. It is recommended by Google and used in Twitter and Alibaba.
Write your CSS rules without vendor prefixes (in fact, forget about them
entirely):
```css
::placeholder {
color: gray;
}
.image {
background-image: url(image@1x.png);
}
@media (min-resolution: 2dppx) {
.image {
background-image: url(image@2x.png);
}
}
```
Autoprefixer will use the data based on current browser popularity and property
support to apply prefixes for you. You can try the [interactive demo]
of Autoprefixer.
```css
::-moz-placeholder {
color: gray;
}
:-ms-input-placeholder {
color: gray;
}
::placeholder {
color: gray;
}
.image {
background-image: url(image@1x.png);
}
@media (-webkit-min-device-pixel-ratio: 2),
(min-resolution: 2dppx) {
.image {
background-image: url(image@2x.png);
}
}
```
Twitter account for news and releases: [@autoprefixer].
<a href="https://evilmartians.com/?utm_source=autoprefixer">
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
</a>
[interactive demo]: https://autoprefixer.github.io/
[@autoprefixer]: https://twitter.com/autoprefixer
[Can I Use]: https://caniuse.com/
[cult-img]: http://cultofmartians.com/assets/badges/badge.svg
[PostCSS]: https://github.com/postcss/postcss
[cult]: http://cultofmartians.com/tasks/autoprefixer-grid.html
## Docs
Read **[full docs](https://github.com/postcss/autoprefixer#readme)** on GitHub.

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env node
let mode = process.argv[2]
if (mode === '--info') {
process.stdout.write(require('../')().info() + '\n')
} else if (mode === '--version') {
process.stdout.write(
'autoprefixer ' + require('../package.json').version + '\n'
)
} else {
process.stdout.write(
'autoprefix\n' +
'\n' +
'Options:\n' +
' --info Show target browsers and used prefixes\n' +
' --version Show version number\n' +
' --help Show help\n' +
'\n' +
'Usage:\n' +
' autoprefixer --info\n'
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
let Prefixer = require('./prefixer')
class AtRule extends Prefixer {
/**
* Clone and add prefixes for at-rule
*/
add(rule, prefix) {
let prefixed = prefix + rule.name
let already = rule.parent.some(
i => i.name === prefixed && i.params === rule.params
)
if (already) {
return undefined
}
let cloned = this.clone(rule, { name: prefixed })
return rule.parent.insertBefore(rule, cloned)
}
/**
* Clone node with prefixes
*/
process(node) {
let parent = this.parentPrefix(node)
for (let prefix of this.prefixes) {
if (!parent || parent === prefix) {
this.add(node, prefix)
}
}
}
}
module.exports = AtRule

View File

@@ -0,0 +1,95 @@
import { Plugin } from 'postcss'
import { Stats } from 'browserslist'
declare function autoprefixer<T extends string[]>(
...args: [...T, autoprefixer.Options]
): Plugin & autoprefixer.ExportedAPI
declare function autoprefixer(
browsers: string[],
options?: autoprefixer.Options
): Plugin & autoprefixer.ExportedAPI
declare function autoprefixer(
options?: autoprefixer.Options
): Plugin & autoprefixer.ExportedAPI
declare namespace autoprefixer {
type GridValue = 'autoplace' | 'no-autoplace'
interface Options {
/** environment for `Browserslist` */
env?: string
/** should Autoprefixer use Visual Cascade, if CSS is uncompressed */
cascade?: boolean
/** should Autoprefixer add prefixes. */
add?: boolean
/** should Autoprefixer [remove outdated] prefixes */
remove?: boolean
/** should Autoprefixer add prefixes for @supports parameters. */
supports?: boolean
/** should Autoprefixer add prefixes for flexbox properties */
flexbox?: boolean | 'no-2009'
/** should Autoprefixer add IE 10-11 prefixes for Grid Layout properties */
grid?: boolean | GridValue
/** custom usage statistics for > 10% in my stats browsers query */
stats?: Stats
/**
* list of queries for target browsers.
* Try to not use it.
* The best practice is to use `.browserslistrc` config or `browserslist` key in `package.json`
* to share target browsers with Babel, ESLint and Stylelint
*/
overrideBrowserslist?: string | string[]
/** do not raise error on unknown browser version in `Browserslist` config. */
ignoreUnknownVersions?: boolean
}
interface ExportedAPI {
/** Autoprefixer data */
data: {
browsers: { [browser: string]: object | undefined }
prefixes: { [prefixName: string]: object | undefined }
}
/** Autoprefixer default browsers */
defaults: string[]
/** Inspect with default Autoprefixer */
info(options?: { from?: string }): string
options: Options
browsers: string | string[]
}
/** Autoprefixer data */
let data: ExportedAPI['data']
/** Autoprefixer default browsers */
let defaults: ExportedAPI['defaults']
/** Inspect with default Autoprefixer */
let info: ExportedAPI['info']
let postcss: true
}
declare global {
namespace NodeJS {
interface ProcessEnv {
AUTOPREFIXER_GRID?: autoprefixer.GridValue
}
}
}
export = autoprefixer

View File

@@ -0,0 +1,164 @@
let browserslist = require('browserslist')
let { agents } = require('caniuse-lite')
let pico = require('picocolors')
let Browsers = require('./browsers')
let Prefixes = require('./prefixes')
let dataPrefixes = require('../data/prefixes')
let getInfo = require('./info')
let autoprefixerData = { browsers: agents, prefixes: dataPrefixes }
const WARNING =
'\n' +
' Replace Autoprefixer `browsers` option to Browserslist config.\n' +
' Use `browserslist` key in `package.json` or `.browserslistrc` file.\n' +
'\n' +
' Using `browsers` option can cause errors. Browserslist config can\n' +
' be used for Babel, Autoprefixer, postcss-normalize and other tools.\n' +
'\n' +
' If you really need to use option, rename it to `overrideBrowserslist`.\n' +
'\n' +
' Learn more at:\n' +
' https://github.com/browserslist/browserslist#readme\n' +
' https://twitter.com/browserslist\n' +
'\n'
function isPlainObject(obj) {
return Object.prototype.toString.apply(obj) === '[object Object]'
}
let cache = new Map()
function timeCapsule(result, prefixes) {
if (prefixes.browsers.selected.length === 0) {
return
}
if (prefixes.add.selectors.length > 0) {
return
}
if (Object.keys(prefixes.add).length > 2) {
return
}
/* c8 ignore next 11 */
result.warn(
'Autoprefixer target browsers do not need any prefixes.' +
'You do not need Autoprefixer anymore.\n' +
'Check your Browserslist config to be sure that your targets ' +
'are set up correctly.\n' +
'\n' +
' Learn more at:\n' +
' https://github.com/postcss/autoprefixer#readme\n' +
' https://github.com/browserslist/browserslist#readme\n' +
'\n'
)
}
module.exports = plugin
function plugin(...reqs) {
let options
if (reqs.length === 1 && isPlainObject(reqs[0])) {
options = reqs[0]
reqs = undefined
} else if (reqs.length === 0 || (reqs.length === 1 && !reqs[0])) {
reqs = undefined
} else if (reqs.length <= 2 && (Array.isArray(reqs[0]) || !reqs[0])) {
options = reqs[1]
reqs = reqs[0]
} else if (typeof reqs[reqs.length - 1] === 'object') {
options = reqs.pop()
}
if (!options) {
options = {}
}
if (options.browser) {
throw new Error(
'Change `browser` option to `overrideBrowserslist` in Autoprefixer'
)
} else if (options.browserslist) {
throw new Error(
'Change `browserslist` option to `overrideBrowserslist` in Autoprefixer'
)
}
if (options.overrideBrowserslist) {
reqs = options.overrideBrowserslist
} else if (options.browsers) {
if (typeof console !== 'undefined' && console.warn) {
console.warn(
pico.red(WARNING.replace(/`[^`]+`/g, i => pico.yellow(i.slice(1, -1))))
)
}
reqs = options.browsers
}
let brwlstOpts = {
ignoreUnknownVersions: options.ignoreUnknownVersions,
stats: options.stats,
env: options.env
}
function loadPrefixes(opts) {
let d = autoprefixerData
let browsers = new Browsers(d.browsers, reqs, opts, brwlstOpts)
let key = browsers.selected.join(', ') + JSON.stringify(options)
if (!cache.has(key)) {
cache.set(key, new Prefixes(d.prefixes, browsers, options))
}
return cache.get(key)
}
return {
postcssPlugin: 'autoprefixer',
prepare(result) {
let prefixes = loadPrefixes({
from: result.opts.from,
env: options.env
})
return {
OnceExit(root) {
timeCapsule(result, prefixes)
if (options.remove !== false) {
prefixes.processor.remove(root, result)
}
if (options.add !== false) {
prefixes.processor.add(root, result)
}
}
}
},
info(opts) {
opts = opts || {}
opts.from = opts.from || process.cwd()
return getInfo(loadPrefixes(opts))
},
options,
browsers: reqs
}
}
plugin.postcss = true
/**
* Autoprefixer data
*/
plugin.data = autoprefixerData
/**
* Autoprefixer default browsers
*/
plugin.defaults = browserslist.defaults
/**
* Inspect with default Autoprefixer
*/
plugin.info = () => plugin().info()

View File

@@ -0,0 +1,51 @@
function last(array) {
return array[array.length - 1]
}
let brackets = {
/**
* Parse string to nodes tree
*/
parse(str) {
let current = ['']
let stack = [current]
for (let sym of str) {
if (sym === '(') {
current = ['']
last(stack).push(current)
stack.push(current)
continue
}
if (sym === ')') {
stack.pop()
current = last(stack)
current.push('')
continue
}
current[current.length - 1] += sym
}
return stack[0]
},
/**
* Generate output string by nodes tree
*/
stringify(ast) {
let result = ''
for (let i of ast) {
if (typeof i === 'object') {
result += `(${brackets.stringify(i)})`
continue
}
result += i
}
return result
}
}
module.exports = brackets

View File

@@ -0,0 +1,79 @@
let browserslist = require('browserslist')
let agents = require('caniuse-lite').agents
let utils = require('./utils')
class Browsers {
/**
* Return all prefixes for default browser data
*/
static prefixes() {
if (this.prefixesCache) {
return this.prefixesCache
}
this.prefixesCache = []
for (let name in agents) {
this.prefixesCache.push(`-${agents[name].prefix}-`)
}
this.prefixesCache = utils
.uniq(this.prefixesCache)
.sort((a, b) => b.length - a.length)
return this.prefixesCache
}
/**
* Check is value contain any possible prefix
*/
static withPrefix(value) {
if (!this.prefixesRegexp) {
this.prefixesRegexp = new RegExp(this.prefixes().join('|'))
}
return this.prefixesRegexp.test(value)
}
constructor(data, requirements, options, browserslistOpts) {
this.data = data
this.options = options || {}
this.browserslistOpts = browserslistOpts || {}
this.selected = this.parse(requirements)
}
/**
* Return browsers selected by requirements
*/
parse(requirements) {
let opts = {}
for (let i in this.browserslistOpts) {
opts[i] = this.browserslistOpts[i]
}
opts.path = this.options.from
return browserslist(requirements, opts)
}
/**
* Return prefix for selected browser
*/
prefix(browser) {
let [name, version] = browser.split(' ')
let data = this.data[name]
let prefix = data.prefix_exceptions && data.prefix_exceptions[version]
if (!prefix) {
prefix = data.prefix
}
return `-${prefix}-`
}
/**
* Is browser is selected by requirements
*/
isSelected(browser) {
return this.selected.includes(browser)
}
}
module.exports = Browsers

View File

@@ -0,0 +1,187 @@
let Prefixer = require('./prefixer')
let Browsers = require('./browsers')
let utils = require('./utils')
class Declaration extends Prefixer {
/**
* Always true, because we already get prefixer by property name
*/
check(/* decl */) {
return true
}
/**
* Return prefixed version of property
*/
prefixed(prop, prefix) {
return prefix + prop
}
/**
* Return unprefixed version of property
*/
normalize(prop) {
return prop
}
/**
* Check `value`, that it contain other prefixes, rather than `prefix`
*/
otherPrefixes(value, prefix) {
for (let other of Browsers.prefixes()) {
if (other === prefix) {
continue
}
if (value.includes(other)) {
return true
}
}
return false
}
/**
* Set prefix to declaration
*/
set(decl, prefix) {
decl.prop = this.prefixed(decl.prop, prefix)
return decl
}
/**
* Should we use visual cascade for prefixes
*/
needCascade(decl) {
if (!decl._autoprefixerCascade) {
decl._autoprefixerCascade =
this.all.options.cascade !== false && decl.raw('before').includes('\n')
}
return decl._autoprefixerCascade
}
/**
* Return maximum length of possible prefixed property
*/
maxPrefixed(prefixes, decl) {
if (decl._autoprefixerMax) {
return decl._autoprefixerMax
}
let max = 0
for (let prefix of prefixes) {
prefix = utils.removeNote(prefix)
if (prefix.length > max) {
max = prefix.length
}
}
decl._autoprefixerMax = max
return decl._autoprefixerMax
}
/**
* Calculate indentation to create visual cascade
*/
calcBefore(prefixes, decl, prefix = '') {
let max = this.maxPrefixed(prefixes, decl)
let diff = max - utils.removeNote(prefix).length
let before = decl.raw('before')
if (diff > 0) {
before += Array(diff).fill(' ').join('')
}
return before
}
/**
* Remove visual cascade
*/
restoreBefore(decl) {
let lines = decl.raw('before').split('\n')
let min = lines[lines.length - 1]
this.all.group(decl).up(prefixed => {
let array = prefixed.raw('before').split('\n')
let last = array[array.length - 1]
if (last.length < min.length) {
min = last
}
})
lines[lines.length - 1] = min
decl.raws.before = lines.join('\n')
}
/**
* Clone and insert new declaration
*/
insert(decl, prefix, prefixes) {
let cloned = this.set(this.clone(decl), prefix)
if (!cloned) return undefined
let already = decl.parent.some(
i => i.prop === cloned.prop && i.value === cloned.value
)
if (already) {
return undefined
}
if (this.needCascade(decl)) {
cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
}
return decl.parent.insertBefore(decl, cloned)
}
/**
* Did this declaration has this prefix above
*/
isAlready(decl, prefixed) {
let already = this.all.group(decl).up(i => i.prop === prefixed)
if (!already) {
already = this.all.group(decl).down(i => i.prop === prefixed)
}
return already
}
/**
* Clone and add prefixes for declaration
*/
add(decl, prefix, prefixes, result) {
let prefixed = this.prefixed(decl.prop, prefix)
if (
this.isAlready(decl, prefixed) ||
this.otherPrefixes(decl.value, prefix)
) {
return undefined
}
return this.insert(decl, prefix, prefixes, result)
}
/**
* Add spaces for visual cascade
*/
process(decl, result) {
if (!this.needCascade(decl)) {
super.process(decl, result)
return
}
let prefixes = super.process(decl, result)
if (!prefixes || !prefixes.length) {
return
}
this.restoreBefore(decl)
decl.raws.before = this.calcBefore(prefixes, decl)
}
/**
* Return list of prefixed properties to clean old prefixes
*/
old(prop, prefix) {
return [this.prefixed(prop, prefix)]
}
}
module.exports = Declaration

View File

@@ -0,0 +1,49 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class AlignContent extends Declaration {
/**
* Change property name for 2012 spec
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2012) {
return prefix + 'flex-line-pack'
}
return super.prefixed(prop, prefix)
}
/**
* Return property name by final spec
*/
normalize() {
return 'align-content'
}
/**
* Change value for 2012 spec and ignore prefix for 2009
*/
set(decl, prefix) {
let spec = flexSpec(prefix)[0]
if (spec === 2012) {
decl.value = AlignContent.oldValues[decl.value] || decl.value
return super.set(decl, prefix)
}
if (spec === 'final') {
return super.set(decl, prefix)
}
return undefined
}
}
AlignContent.names = ['align-content', 'flex-line-pack']
AlignContent.oldValues = {
'flex-end': 'end',
'flex-start': 'start',
'space-between': 'justify',
'space-around': 'distribute'
}
module.exports = AlignContent

View File

@@ -0,0 +1,46 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class AlignItems extends Declaration {
/**
* Change property name for 2009 and 2012 specs
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2009) {
return prefix + 'box-align'
}
if (spec === 2012) {
return prefix + 'flex-align'
}
return super.prefixed(prop, prefix)
}
/**
* Return property name by final spec
*/
normalize() {
return 'align-items'
}
/**
* Change value for 2009 and 2012 specs
*/
set(decl, prefix) {
let spec = flexSpec(prefix)[0]
if (spec === 2009 || spec === 2012) {
decl.value = AlignItems.oldValues[decl.value] || decl.value
}
return super.set(decl, prefix)
}
}
AlignItems.names = ['align-items', 'flex-align', 'box-align']
AlignItems.oldValues = {
'flex-end': 'end',
'flex-start': 'start'
}
module.exports = AlignItems

View File

@@ -0,0 +1,56 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class AlignSelf extends Declaration {
check(decl) {
return (
decl.parent &&
!decl.parent.some(i => {
return i.prop && i.prop.startsWith('grid-')
})
)
}
/**
* Change property name for 2012 specs
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2012) {
return prefix + 'flex-item-align'
}
return super.prefixed(prop, prefix)
}
/**
* Return property name by final spec
*/
normalize() {
return 'align-self'
}
/**
* Change value for 2012 spec and ignore prefix for 2009
*/
set(decl, prefix) {
let spec = flexSpec(prefix)[0]
if (spec === 2012) {
decl.value = AlignSelf.oldValues[decl.value] || decl.value
return super.set(decl, prefix)
}
if (spec === 'final') {
return super.set(decl, prefix)
}
return undefined
}
}
AlignSelf.names = ['align-self', 'flex-item-align']
AlignSelf.oldValues = {
'flex-end': 'end',
'flex-start': 'start'
}
module.exports = AlignSelf

View File

@@ -0,0 +1,17 @@
let Declaration = require('../declaration')
class Animation extends Declaration {
/**
* Dont add prefixes for modern values.
*/
check(decl) {
return !decl.value.split(/\s+/).some(i => {
let lower = i.toLowerCase()
return lower === 'reverse' || lower === 'alternate-reverse'
})
}
}
Animation.names = ['animation', 'animation-direction']
module.exports = Animation

View File

@@ -0,0 +1,23 @@
let Declaration = require('../declaration')
let utils = require('../utils')
class Appearance extends Declaration {
constructor(name, prefixes, all) {
super(name, prefixes, all)
if (this.prefixes) {
this.prefixes = utils.uniq(
this.prefixes.map(i => {
if (i === '-ms-') {
return '-webkit-'
}
return i
})
)
}
}
}
Appearance.names = ['appearance']
module.exports = Appearance

View File

@@ -0,0 +1,30 @@
let Selector = require('../selector')
let utils = require('../utils')
class Autofill extends Selector {
constructor(name, prefixes, all) {
super(name, prefixes, all)
if (this.prefixes) {
this.prefixes = utils.uniq(
this.prefixes.map(i => {
return '-webkit-'
})
)
}
}
/**
* Return different selectors depend on prefix
*/
prefixed(prefix) {
if (prefix === '-webkit-') {
return ':-webkit-autofill'
}
return `:${prefix}autofill`
}
}
Autofill.names = [':autofill']
module.exports = Autofill

View File

@@ -0,0 +1,20 @@
let Declaration = require('../declaration')
let utils = require('../utils')
class BackdropFilter extends Declaration {
constructor(name, prefixes, all) {
super(name, prefixes, all)
if (this.prefixes) {
this.prefixes = utils.uniq(
this.prefixes.map(i => {
return i === '-ms-' ? '-webkit-' : i
})
)
}
}
}
BackdropFilter.names = ['backdrop-filter']
module.exports = BackdropFilter

View File

@@ -0,0 +1,24 @@
let Declaration = require('../declaration')
let utils = require('../utils')
class BackgroundClip extends Declaration {
constructor(name, prefixes, all) {
super(name, prefixes, all)
if (this.prefixes) {
this.prefixes = utils.uniq(
this.prefixes.map(i => {
return i === '-ms-' ? '-webkit-' : i
})
)
}
}
check(decl) {
return decl.value.toLowerCase() === 'text'
}
}
BackgroundClip.names = ['background-clip']
module.exports = BackgroundClip

View File

@@ -0,0 +1,23 @@
let Declaration = require('../declaration')
class BackgroundSize extends Declaration {
/**
* Duplication parameter for -webkit- browsers
*/
set(decl, prefix) {
let value = decl.value.toLowerCase()
if (
prefix === '-webkit-' &&
!value.includes(' ') &&
value !== 'contain' &&
value !== 'cover'
) {
decl.value = decl.value + ' ' + decl.value
}
return super.set(decl, prefix)
}
}
BackgroundSize.names = ['background-size']
module.exports = BackgroundSize

View File

@@ -0,0 +1,40 @@
let Declaration = require('../declaration')
class BlockLogical extends Declaration {
/**
* Use old syntax for -moz- and -webkit-
*/
prefixed(prop, prefix) {
if (prop.includes('-start')) {
return prefix + prop.replace('-block-start', '-before')
}
return prefix + prop.replace('-block-end', '-after')
}
/**
* Return property name by spec
*/
normalize(prop) {
if (prop.includes('-before')) {
return prop.replace('-before', '-block-start')
}
return prop.replace('-after', '-block-end')
}
}
BlockLogical.names = [
'border-block-start',
'border-block-end',
'margin-block-start',
'margin-block-end',
'padding-block-start',
'padding-block-end',
'border-before',
'border-after',
'margin-before',
'margin-after',
'padding-before',
'padding-after'
]
module.exports = BlockLogical

View File

@@ -0,0 +1,15 @@
let Declaration = require('../declaration')
class BorderImage extends Declaration {
/**
* Remove fill parameter for prefixed declarations
*/
set(decl, prefix) {
decl.value = decl.value.replace(/\s+fill(\s)/, '$1')
return super.set(decl, prefix)
}
}
BorderImage.names = ['border-image']
module.exports = BorderImage

View File

@@ -0,0 +1,40 @@
let Declaration = require('../declaration')
class BorderRadius extends Declaration {
/**
* Change syntax, when add Mozilla prefix
*/
prefixed(prop, prefix) {
if (prefix === '-moz-') {
return prefix + (BorderRadius.toMozilla[prop] || prop)
}
return super.prefixed(prop, prefix)
}
/**
* Return unprefixed version of property
*/
normalize(prop) {
return BorderRadius.toNormal[prop] || prop
}
}
BorderRadius.names = ['border-radius']
BorderRadius.toMozilla = {}
BorderRadius.toNormal = {}
for (let ver of ['top', 'bottom']) {
for (let hor of ['left', 'right']) {
let normal = `border-${ver}-${hor}-radius`
let mozilla = `border-radius-${ver}${hor}`
BorderRadius.names.push(normal)
BorderRadius.names.push(mozilla)
BorderRadius.toMozilla[normal] = mozilla
BorderRadius.toNormal[mozilla] = normal
}
}
module.exports = BorderRadius

View File

@@ -0,0 +1,63 @@
let Declaration = require('../declaration')
class BreakProps extends Declaration {
/**
* Change name for -webkit- and -moz- prefix
*/
prefixed(prop, prefix) {
return `${prefix}column-${prop}`
}
/**
* Return property name by final spec
*/
normalize(prop) {
if (prop.includes('inside')) {
return 'break-inside'
}
if (prop.includes('before')) {
return 'break-before'
}
return 'break-after'
}
/**
* Change prefixed value for avoid-column and avoid-page
*/
set(decl, prefix) {
if (
(decl.prop === 'break-inside' && decl.value === 'avoid-column') ||
decl.value === 'avoid-page'
) {
decl.value = 'avoid'
}
return super.set(decl, prefix)
}
/**
* Dont prefix some values
*/
insert(decl, prefix, prefixes) {
if (decl.prop !== 'break-inside') {
return super.insert(decl, prefix, prefixes)
}
if (/region/i.test(decl.value) || /page/i.test(decl.value)) {
return undefined
}
return super.insert(decl, prefix, prefixes)
}
}
BreakProps.names = [
'break-inside',
'page-break-inside',
'column-break-inside',
'break-before',
'page-break-before',
'column-break-before',
'break-after',
'page-break-after',
'column-break-after'
]
module.exports = BreakProps

View File

@@ -0,0 +1,21 @@
let Declaration = require('../declaration')
class ColorAdjust extends Declaration {
/**
* Change property name for WebKit-based browsers
*/
prefixed(prop, prefix) {
return prefix + 'print-color-adjust'
}
/**
* Return property name by spec
*/
normalize() {
return 'color-adjust'
}
}
ColorAdjust.names = ['color-adjust', 'print-color-adjust']
module.exports = ColorAdjust

View File

@@ -0,0 +1,35 @@
let list = require('postcss').list
let Value = require('../value')
class CrossFade extends Value {
replace(string, prefix) {
return list
.space(string)
.map(value => {
if (value.slice(0, +this.name.length + 1) !== this.name + '(') {
return value
}
let close = value.lastIndexOf(')')
let after = value.slice(close + 1)
let args = value.slice(this.name.length + 1, close)
if (prefix === '-webkit-') {
let match = args.match(/\d*.?\d+%?/)
if (match) {
args = args.slice(match[0].length).trim()
args += `, ${match[0]}`
} else {
args += ', 0.5'
}
}
return prefix + this.name + '(' + args + ')' + after
})
.join(' ')
}
}
CrossFade.names = ['cross-fade']
module.exports = CrossFade

View File

@@ -0,0 +1,65 @@
let flexSpec = require('./flex-spec')
let OldValue = require('../old-value')
let Value = require('../value')
class DisplayFlex extends Value {
constructor(name, prefixes) {
super(name, prefixes)
if (name === 'display-flex') {
this.name = 'flex'
}
}
/**
* Faster check for flex value
*/
check(decl) {
return decl.prop === 'display' && decl.value === this.name
}
/**
* Return value by spec
*/
prefixed(prefix) {
let spec, value
;[spec, prefix] = flexSpec(prefix)
if (spec === 2009) {
if (this.name === 'flex') {
value = 'box'
} else {
value = 'inline-box'
}
} else if (spec === 2012) {
if (this.name === 'flex') {
value = 'flexbox'
} else {
value = 'inline-flexbox'
}
} else if (spec === 'final') {
value = this.name
}
return prefix + value
}
/**
* Add prefix to value depend on flebox spec version
*/
replace(string, prefix) {
return this.prefixed(prefix)
}
/**
* Change value for old specs
*/
old(prefix) {
let prefixed = this.prefixed(prefix)
if (!prefixed) return undefined
return new OldValue(this.name, prefixed)
}
}
DisplayFlex.names = ['display-flex', 'inline-flex']
module.exports = DisplayFlex

View File

@@ -0,0 +1,21 @@
let Value = require('../value')
class DisplayGrid extends Value {
constructor(name, prefixes) {
super(name, prefixes)
if (name === 'display-grid') {
this.name = 'grid'
}
}
/**
* Faster check for flex value
*/
check(decl) {
return decl.prop === 'display' && decl.value === this.name
}
}
DisplayGrid.names = ['display-grid', 'inline-grid']
module.exports = DisplayGrid

View File

@@ -0,0 +1,30 @@
let Selector = require('../selector')
let utils = require('../utils')
class FileSelectorButton extends Selector {
constructor(name, prefixes, all) {
super(name, prefixes, all)
if (this.prefixes) {
this.prefixes = utils.uniq(
this.prefixes.map(i => {
return '-webkit-'
})
)
}
}
/**
* Return different selectors depend on prefix
*/
prefixed(prefix) {
if (prefix === '-webkit-') {
return '::-webkit-file-upload-button'
}
return `::${prefix}file-selector-button`
}
}
FileSelectorButton.names = ['::file-selector-button']
module.exports = FileSelectorButton

View File

@@ -0,0 +1,14 @@
let Value = require('../value')
class FilterValue extends Value {
constructor(name, prefixes) {
super(name, prefixes)
if (name === 'filter-function') {
this.name = 'filter'
}
}
}
FilterValue.names = ['filter', 'filter-function']
module.exports = FilterValue

View File

@@ -0,0 +1,19 @@
let Declaration = require('../declaration')
class Filter extends Declaration {
/**
* Check is it Internet Explorer filter
*/
check(decl) {
let v = decl.value
return (
!v.toLowerCase().includes('alpha(') &&
!v.includes('DXImageTransform.Microsoft') &&
!v.includes('data:image/svg+xml')
)
}
}
Filter.names = ['filter']
module.exports = Filter

View File

@@ -0,0 +1,39 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class FlexBasis extends Declaration {
/**
* Return property name by final spec
*/
normalize() {
return 'flex-basis'
}
/**
* Return flex property for 2012 spec
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2012) {
return prefix + 'flex-preferred-size'
}
return super.prefixed(prop, prefix)
}
/**
* Ignore 2009 spec and use flex property for 2012
*/
set(decl, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2012 || spec === 'final') {
return super.set(decl, prefix)
}
return undefined
}
}
FlexBasis.names = ['flex-basis', 'flex-preferred-size']
module.exports = FlexBasis

View File

@@ -0,0 +1,72 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class FlexDirection extends Declaration {
/**
* Return property name by final spec
*/
normalize() {
return 'flex-direction'
}
/**
* Use two properties for 2009 spec
*/
insert(decl, prefix, prefixes) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec !== 2009) {
return super.insert(decl, prefix, prefixes)
}
let already = decl.parent.some(
i =>
i.prop === prefix + 'box-orient' || i.prop === prefix + 'box-direction'
)
if (already) {
return undefined
}
let v = decl.value
let orient, dir
if (v === 'inherit' || v === 'initial' || v === 'unset') {
orient = v
dir = v
} else {
orient = v.includes('row') ? 'horizontal' : 'vertical'
dir = v.includes('reverse') ? 'reverse' : 'normal'
}
let cloned = this.clone(decl)
cloned.prop = prefix + 'box-orient'
cloned.value = orient
if (this.needCascade(decl)) {
cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
}
decl.parent.insertBefore(decl, cloned)
cloned = this.clone(decl)
cloned.prop = prefix + 'box-direction'
cloned.value = dir
if (this.needCascade(decl)) {
cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
}
return decl.parent.insertBefore(decl, cloned)
}
/**
* Clean two properties for 2009 spec
*/
old(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2009) {
return [prefix + 'box-orient', prefix + 'box-direction']
} else {
return super.old(prop, prefix)
}
}
}
FlexDirection.names = ['flex-direction', 'box-direction', 'box-orient']
module.exports = FlexDirection

View File

@@ -0,0 +1,53 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class FlexFlow extends Declaration {
/**
* Use two properties for 2009 spec
*/
insert(decl, prefix, prefixes) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec !== 2009) {
return super.insert(decl, prefix, prefixes)
}
let values = decl.value
.split(/\s+/)
.filter(i => i !== 'wrap' && i !== 'nowrap' && 'wrap-reverse')
if (values.length === 0) {
return undefined
}
let already = decl.parent.some(
i =>
i.prop === prefix + 'box-orient' || i.prop === prefix + 'box-direction'
)
if (already) {
return undefined
}
let value = values[0]
let orient = value.includes('row') ? 'horizontal' : 'vertical'
let dir = value.includes('reverse') ? 'reverse' : 'normal'
let cloned = this.clone(decl)
cloned.prop = prefix + 'box-orient'
cloned.value = orient
if (this.needCascade(decl)) {
cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
}
decl.parent.insertBefore(decl, cloned)
cloned = this.clone(decl)
cloned.prop = prefix + 'box-direction'
cloned.value = dir
if (this.needCascade(decl)) {
cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
}
return decl.parent.insertBefore(decl, cloned)
}
}
FlexFlow.names = ['flex-flow', 'box-direction', 'box-orient']
module.exports = FlexFlow

View File

@@ -0,0 +1,30 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class Flex extends Declaration {
/**
* Return property name by final spec
*/
normalize() {
return 'flex'
}
/**
* Return flex property for 2009 and 2012 specs
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2009) {
return prefix + 'box-flex'
}
if (spec === 2012) {
return prefix + 'flex-positive'
}
return super.prefixed(prop, prefix)
}
}
Flex.names = ['flex-grow', 'flex-positive']
module.exports = Flex

View File

@@ -0,0 +1,39 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class FlexShrink extends Declaration {
/**
* Return property name by final spec
*/
normalize() {
return 'flex-shrink'
}
/**
* Return flex property for 2012 spec
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2012) {
return prefix + 'flex-negative'
}
return super.prefixed(prop, prefix)
}
/**
* Ignore 2009 spec and use flex property for 2012
*/
set(decl, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2012 || spec === 'final') {
return super.set(decl, prefix)
}
return undefined
}
}
FlexShrink.names = ['flex-shrink', 'flex-negative']
module.exports = FlexShrink

View File

@@ -0,0 +1,19 @@
/**
* Return flexbox spec versions by prefix
*/
module.exports = function (prefix) {
let spec
if (prefix === '-webkit- 2009' || prefix === '-moz-') {
spec = 2009
} else if (prefix === '-ms-') {
spec = 2012
} else if (prefix === '-webkit-') {
spec = 'final'
}
if (prefix === '-webkit- 2009') {
prefix = '-webkit-'
}
return [spec, prefix]
}

View File

@@ -0,0 +1,19 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class FlexWrap extends Declaration {
/**
* Don't add prefix for 2009 spec
*/
set(decl, prefix) {
let spec = flexSpec(prefix)[0]
if (spec !== 2009) {
return super.set(decl, prefix)
}
return undefined
}
}
FlexWrap.names = ['flex-wrap']
module.exports = FlexWrap

View File

@@ -0,0 +1,54 @@
let list = require('postcss').list
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class Flex extends Declaration {
/**
* Change property name for 2009 spec
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2009) {
return prefix + 'box-flex'
}
return super.prefixed(prop, prefix)
}
/**
* Return property name by final spec
*/
normalize() {
return 'flex'
}
/**
* Spec 2009 supports only first argument
* Spec 2012 disallows unitless basis
*/
set(decl, prefix) {
let spec = flexSpec(prefix)[0]
if (spec === 2009) {
decl.value = list.space(decl.value)[0]
decl.value = Flex.oldValues[decl.value] || decl.value
return super.set(decl, prefix)
}
if (spec === 2012) {
let components = list.space(decl.value)
if (components.length === 3 && components[2] === '0') {
decl.value = components.slice(0, 2).concat('0px').join(' ')
}
}
return super.set(decl, prefix)
}
}
Flex.names = ['flex', 'box-flex']
Flex.oldValues = {
auto: '1',
none: '0'
}
module.exports = Flex

View File

@@ -0,0 +1,20 @@
let Selector = require('../selector')
class Fullscreen extends Selector {
/**
* Return different selectors depend on prefix
*/
prefixed(prefix) {
if (prefix === '-webkit-') {
return ':-webkit-full-screen'
}
if (prefix === '-moz-') {
return ':-moz-full-screen'
}
return `:${prefix}fullscreen`
}
}
Fullscreen.names = [':fullscreen']
module.exports = Fullscreen

View File

@@ -0,0 +1,429 @@
let parser = require('postcss-value-parser')
let range = require('normalize-range')
let OldValue = require('../old-value')
let Value = require('../value')
let utils = require('../utils')
let IS_DIRECTION = /top|left|right|bottom/gi
class Gradient extends Value {
/**
* Change degrees for webkit prefix
*/
replace(string, prefix) {
let ast = parser(string)
for (let node of ast.nodes) {
if (node.type === 'function' && node.value === this.name) {
node.nodes = this.newDirection(node.nodes)
node.nodes = this.normalize(node.nodes)
if (prefix === '-webkit- old') {
let changes = this.oldWebkit(node)
if (!changes) {
return false
}
} else {
node.nodes = this.convertDirection(node.nodes)
node.value = prefix + node.value
}
}
}
return ast.toString()
}
/**
* Replace first token
*/
replaceFirst(params, ...words) {
let prefix = words.map(i => {
if (i === ' ') {
return { type: 'space', value: i }
}
return { type: 'word', value: i }
})
return prefix.concat(params.slice(1))
}
/**
* Convert angle unit to deg
*/
normalizeUnit(str, full) {
let num = parseFloat(str)
let deg = (num / full) * 360
return `${deg}deg`
}
/**
* Normalize angle
*/
normalize(nodes) {
if (!nodes[0]) return nodes
if (/-?\d+(.\d+)?grad/.test(nodes[0].value)) {
nodes[0].value = this.normalizeUnit(nodes[0].value, 400)
} else if (/-?\d+(.\d+)?rad/.test(nodes[0].value)) {
nodes[0].value = this.normalizeUnit(nodes[0].value, 2 * Math.PI)
} else if (/-?\d+(.\d+)?turn/.test(nodes[0].value)) {
nodes[0].value = this.normalizeUnit(nodes[0].value, 1)
} else if (nodes[0].value.includes('deg')) {
let num = parseFloat(nodes[0].value)
num = range.wrap(0, 360, num)
nodes[0].value = `${num}deg`
}
if (nodes[0].value === '0deg') {
nodes = this.replaceFirst(nodes, 'to', ' ', 'top')
} else if (nodes[0].value === '90deg') {
nodes = this.replaceFirst(nodes, 'to', ' ', 'right')
} else if (nodes[0].value === '180deg') {
nodes = this.replaceFirst(nodes, 'to', ' ', 'bottom')
} else if (nodes[0].value === '270deg') {
nodes = this.replaceFirst(nodes, 'to', ' ', 'left')
}
return nodes
}
/**
* Replace old direction to new
*/
newDirection(params) {
if (params[0].value === 'to') {
return params
}
IS_DIRECTION.lastIndex = 0 // reset search index of global regexp
if (!IS_DIRECTION.test(params[0].value)) {
return params
}
params.unshift(
{
type: 'word',
value: 'to'
},
{
type: 'space',
value: ' '
}
)
for (let i = 2; i < params.length; i++) {
if (params[i].type === 'div') {
break
}
if (params[i].type === 'word') {
params[i].value = this.revertDirection(params[i].value)
}
}
return params
}
/**
* Look for at word
*/
isRadial(params) {
let state = 'before'
for (let param of params) {
if (state === 'before' && param.type === 'space') {
state = 'at'
} else if (state === 'at' && param.value === 'at') {
state = 'after'
} else if (state === 'after' && param.type === 'space') {
return true
} else if (param.type === 'div') {
break
} else {
state = 'before'
}
}
return false
}
/**
* Change new direction to old
*/
convertDirection(params) {
if (params.length > 0) {
if (params[0].value === 'to') {
this.fixDirection(params)
} else if (params[0].value.includes('deg')) {
this.fixAngle(params)
} else if (this.isRadial(params)) {
this.fixRadial(params)
}
}
return params
}
/**
* Replace `to top left` to `bottom right`
*/
fixDirection(params) {
params.splice(0, 2)
for (let param of params) {
if (param.type === 'div') {
break
}
if (param.type === 'word') {
param.value = this.revertDirection(param.value)
}
}
}
/**
* Add 90 degrees
*/
fixAngle(params) {
let first = params[0].value
first = parseFloat(first)
first = Math.abs(450 - first) % 360
first = this.roundFloat(first, 3)
params[0].value = `${first}deg`
}
/**
* Fix radial direction syntax
*/
fixRadial(params) {
let first = []
let second = []
let a, b, c, i, next
for (i = 0; i < params.length - 2; i++) {
a = params[i]
b = params[i + 1]
c = params[i + 2]
if (a.type === 'space' && b.value === 'at' && c.type === 'space') {
next = i + 3
break
} else {
first.push(a)
}
}
let div
for (i = next; i < params.length; i++) {
if (params[i].type === 'div') {
div = params[i]
break
} else {
second.push(params[i])
}
}
params.splice(0, i, ...second, div, ...first)
}
revertDirection(word) {
return Gradient.directions[word.toLowerCase()] || word
}
/**
* Round float and save digits under dot
*/
roundFloat(float, digits) {
return parseFloat(float.toFixed(digits))
}
/**
* Convert to old webkit syntax
*/
oldWebkit(node) {
let { nodes } = node
let string = parser.stringify(node.nodes)
if (this.name !== 'linear-gradient') {
return false
}
if (nodes[0] && nodes[0].value.includes('deg')) {
return false
}
if (
string.includes('px') ||
string.includes('-corner') ||
string.includes('-side')
) {
return false
}
let params = [[]]
for (let i of nodes) {
params[params.length - 1].push(i)
if (i.type === 'div' && i.value === ',') {
params.push([])
}
}
this.oldDirection(params)
this.colorStops(params)
node.nodes = []
for (let param of params) {
node.nodes = node.nodes.concat(param)
}
node.nodes.unshift(
{ type: 'word', value: 'linear' },
this.cloneDiv(node.nodes)
)
node.value = '-webkit-gradient'
return true
}
/**
* Change direction syntax to old webkit
*/
oldDirection(params) {
let div = this.cloneDiv(params[0])
if (params[0][0].value !== 'to') {
return params.unshift([
{ type: 'word', value: Gradient.oldDirections.bottom },
div
])
} else {
let words = []
for (let node of params[0].slice(2)) {
if (node.type === 'word') {
words.push(node.value.toLowerCase())
}
}
words = words.join(' ')
let old = Gradient.oldDirections[words] || words
params[0] = [{ type: 'word', value: old }, div]
return params[0]
}
}
/**
* Get div token from exists parameters
*/
cloneDiv(params) {
for (let i of params) {
if (i.type === 'div' && i.value === ',') {
return i
}
}
return { type: 'div', value: ',', after: ' ' }
}
/**
* Change colors syntax to old webkit
*/
colorStops(params) {
let result = []
for (let i = 0; i < params.length; i++) {
let pos
let param = params[i]
let item
if (i === 0) {
continue
}
let color = parser.stringify(param[0])
if (param[1] && param[1].type === 'word') {
pos = param[1].value
} else if (param[2] && param[2].type === 'word') {
pos = param[2].value
}
let stop
if (i === 1 && (!pos || pos === '0%')) {
stop = `from(${color})`
} else if (i === params.length - 1 && (!pos || pos === '100%')) {
stop = `to(${color})`
} else if (pos) {
stop = `color-stop(${pos}, ${color})`
} else {
stop = `color-stop(${color})`
}
let div = param[param.length - 1]
params[i] = [{ type: 'word', value: stop }]
if (div.type === 'div' && div.value === ',') {
item = params[i].push(div)
}
result.push(item)
}
return result
}
/**
* Remove old WebKit gradient too
*/
old(prefix) {
if (prefix === '-webkit-') {
let type = this.name === 'linear-gradient' ? 'linear' : 'radial'
let string = '-gradient'
let regexp = utils.regexp(
`-webkit-(${type}-gradient|gradient\\(\\s*${type})`,
false
)
return new OldValue(this.name, prefix + this.name, string, regexp)
} else {
return super.old(prefix)
}
}
/**
* Do not add non-webkit prefixes for list-style and object
*/
add(decl, prefix) {
let p = decl.prop
if (p.includes('mask')) {
if (prefix === '-webkit-' || prefix === '-webkit- old') {
return super.add(decl, prefix)
}
} else if (
p === 'list-style' ||
p === 'list-style-image' ||
p === 'content'
) {
if (prefix === '-webkit-' || prefix === '-webkit- old') {
return super.add(decl, prefix)
}
} else {
return super.add(decl, prefix)
}
return undefined
}
}
Gradient.names = [
'linear-gradient',
'repeating-linear-gradient',
'radial-gradient',
'repeating-radial-gradient'
]
Gradient.directions = {
top: 'bottom',
left: 'right',
bottom: 'top',
right: 'left'
}
// Direction to replace
Gradient.oldDirections = {
'top': 'left bottom, left top',
'left': 'right top, left top',
'bottom': 'left top, left bottom',
'right': 'left top, right top',
'top right': 'left bottom, right top',
'top left': 'right bottom, left top',
'right top': 'left bottom, right top',
'right bottom': 'left top, right bottom',
'bottom right': 'left top, right bottom',
'bottom left': 'right top, left bottom',
'left top': 'right bottom, left top',
'left bottom': 'right top, left bottom'
}
module.exports = Gradient

View File

@@ -0,0 +1,34 @@
let Declaration = require('../declaration')
let utils = require('./grid-utils')
class GridArea extends Declaration {
/**
* Translate grid-area to separate -ms- prefixed properties
*/
insert(decl, prefix, prefixes, result) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
let values = utils.parse(decl)
let [rowStart, rowSpan] = utils.translate(values, 0, 2)
let [columnStart, columnSpan] = utils.translate(values, 1, 3)
;[
['grid-row', rowStart],
['grid-row-span', rowSpan],
['grid-column', columnStart],
['grid-column-span', columnSpan]
].forEach(([prop, value]) => {
utils.insertDecl(decl, prop, value)
})
utils.warnTemplateSelectorNotFound(decl, result)
utils.warnIfGridRowColumnExists(decl, result)
return undefined
}
}
GridArea.names = ['grid-area']
module.exports = GridArea

View File

@@ -0,0 +1,28 @@
let Declaration = require('../declaration')
class GridColumnAlign extends Declaration {
/**
* Do not prefix flexbox values
*/
check(decl) {
return !decl.value.includes('flex-') && decl.value !== 'baseline'
}
/**
* Change property name for IE
*/
prefixed(prop, prefix) {
return prefix + 'grid-column-align'
}
/**
* Change IE property back
*/
normalize() {
return 'justify-self'
}
}
GridColumnAlign.names = ['grid-column-align']
module.exports = GridColumnAlign

View File

@@ -0,0 +1,47 @@
let Declaration = require('../declaration')
class GridEnd extends Declaration {
/**
* Change repeating syntax for IE
*/
insert(decl, prefix, prefixes, result) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
let clonedDecl = this.clone(decl)
let startProp = decl.prop.replace(/end$/, 'start')
let spanProp = prefix + decl.prop.replace(/end$/, 'span')
if (decl.parent.some(i => i.prop === spanProp)) {
return undefined
}
clonedDecl.prop = spanProp
if (decl.value.includes('span')) {
clonedDecl.value = decl.value.replace(/span\s/i, '')
} else {
let startDecl
decl.parent.walkDecls(startProp, d => {
startDecl = d
})
if (startDecl) {
let value = Number(decl.value) - Number(startDecl.value) + ''
clonedDecl.value = value
} else {
decl.warn(
result,
`Can not prefix ${decl.prop} (${startProp} is not found)`
)
}
}
decl.cloneBefore(clonedDecl)
return undefined
}
}
GridEnd.names = ['grid-row-end', 'grid-column-end']
module.exports = GridEnd

View File

@@ -0,0 +1,28 @@
let Declaration = require('../declaration')
class GridRowAlign extends Declaration {
/**
* Do not prefix flexbox values
*/
check(decl) {
return !decl.value.includes('flex-') && decl.value !== 'baseline'
}
/**
* Change property name for IE
*/
prefixed(prop, prefix) {
return prefix + 'grid-row-align'
}
/**
* Change IE property back
*/
normalize() {
return 'align-self'
}
}
GridRowAlign.names = ['grid-row-align']
module.exports = GridRowAlign

View File

@@ -0,0 +1,33 @@
let Declaration = require('../declaration')
let utils = require('./grid-utils')
class GridRowColumn extends Declaration {
/**
* Translate grid-row / grid-column to separate -ms- prefixed properties
*/
insert(decl, prefix, prefixes) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
let values = utils.parse(decl)
let [start, span] = utils.translate(values, 0, 1)
let hasStartValueSpan = values[0] && values[0].includes('span')
if (hasStartValueSpan) {
span = values[0].join('').replace(/\D/g, '')
}
;[
[decl.prop, start],
[`${decl.prop}-span`, span]
].forEach(([prop, value]) => {
utils.insertDecl(decl, prop, value)
})
return undefined
}
}
GridRowColumn.names = ['grid-row', 'grid-column']
module.exports = GridRowColumn

View File

@@ -0,0 +1,125 @@
let Declaration = require('../declaration')
let {
prefixTrackProp,
prefixTrackValue,
autoplaceGridItems,
getGridGap,
inheritGridGap
} = require('./grid-utils')
let Processor = require('../processor')
class GridRowsColumns extends Declaration {
/**
* Change property name for IE
*/
prefixed(prop, prefix) {
if (prefix === '-ms-') {
return prefixTrackProp({ prop, prefix })
}
return super.prefixed(prop, prefix)
}
/**
* Change IE property back
*/
normalize(prop) {
return prop.replace(/^grid-(rows|columns)/, 'grid-template-$1')
}
insert(decl, prefix, prefixes, result) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
let { parent, prop, value } = decl
let isRowProp = prop.includes('rows')
let isColumnProp = prop.includes('columns')
let hasGridTemplate = parent.some(
i => i.prop === 'grid-template' || i.prop === 'grid-template-areas'
)
/**
* Not to prefix rows declaration if grid-template(-areas) is present
*/
if (hasGridTemplate && isRowProp) {
return false
}
let processor = new Processor({ options: {} })
let status = processor.gridStatus(parent, result)
let gap = getGridGap(decl)
gap = inheritGridGap(decl, gap) || gap
let gapValue = isRowProp ? gap.row : gap.column
if ((status === 'no-autoplace' || status === true) && !hasGridTemplate) {
gapValue = null
}
let prefixValue = prefixTrackValue({
value,
gap: gapValue
})
/**
* Insert prefixes
*/
decl.cloneBefore({
prop: prefixTrackProp({ prop, prefix }),
value: prefixValue
})
let autoflow = parent.nodes.find(i => i.prop === 'grid-auto-flow')
let autoflowValue = 'row'
if (autoflow && !processor.disabled(autoflow, result)) {
autoflowValue = autoflow.value.trim()
}
if (status === 'autoplace') {
/**
* Show warning if grid-template-rows decl is not found
*/
let rowDecl = parent.nodes.find(i => i.prop === 'grid-template-rows')
if (!rowDecl && hasGridTemplate) {
return undefined
} else if (!rowDecl && !hasGridTemplate) {
decl.warn(
result,
'Autoplacement does not work without grid-template-rows property'
)
return undefined
}
/**
* Show warning if grid-template-columns decl is not found
*/
let columnDecl = parent.nodes.find(i => {
return i.prop === 'grid-template-columns'
})
if (!columnDecl && !hasGridTemplate) {
decl.warn(
result,
'Autoplacement does not work without grid-template-columns property'
)
}
/**
* Autoplace grid items
*/
if (isColumnProp && !hasGridTemplate) {
autoplaceGridItems(decl, result, gap, autoflowValue)
}
}
return undefined
}
}
GridRowsColumns.names = [
'grid-template-rows',
'grid-template-columns',
'grid-rows',
'grid-columns'
]
module.exports = GridRowsColumns

View File

@@ -0,0 +1,33 @@
let Declaration = require('../declaration')
class GridStart extends Declaration {
/**
* Do not add prefix for unsupported value in IE
*/
check(decl) {
let value = decl.value
return !value.includes('/') || value.includes('span')
}
/**
* Return a final spec property
*/
normalize(prop) {
return prop.replace('-start', '')
}
/**
* Change property name for IE
*/
prefixed(prop, prefix) {
let result = super.prefixed(prop, prefix)
if (prefix === '-ms-') {
result = result.replace('-start', '')
}
return result
}
}
GridStart.names = ['grid-row-start', 'grid-column-start']
module.exports = GridStart

View File

@@ -0,0 +1,84 @@
let Declaration = require('../declaration')
let {
parseGridAreas,
warnMissedAreas,
prefixTrackProp,
prefixTrackValue,
getGridGap,
warnGridGap,
inheritGridGap
} = require('./grid-utils')
function getGridRows(tpl) {
return tpl
.trim()
.slice(1, -1)
.split(/["']\s*["']?/g)
}
class GridTemplateAreas extends Declaration {
/**
* Translate grid-template-areas to separate -ms- prefixed properties
*/
insert(decl, prefix, prefixes, result) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
let hasColumns = false
let hasRows = false
let parent = decl.parent
let gap = getGridGap(decl)
gap = inheritGridGap(decl, gap) || gap
// remove already prefixed rows
// to prevent doubling prefixes
parent.walkDecls(/-ms-grid-rows/, i => i.remove())
// add empty tracks to rows
parent.walkDecls(/grid-template-(rows|columns)/, trackDecl => {
if (trackDecl.prop === 'grid-template-rows') {
hasRows = true
let { prop, value } = trackDecl
trackDecl.cloneBefore({
prop: prefixTrackProp({ prop, prefix }),
value: prefixTrackValue({ value, gap: gap.row })
})
} else {
hasColumns = true
}
})
let gridRows = getGridRows(decl.value)
if (hasColumns && !hasRows && gap.row && gridRows.length > 1) {
decl.cloneBefore({
prop: '-ms-grid-rows',
value: prefixTrackValue({
value: `repeat(${gridRows.length}, auto)`,
gap: gap.row
}),
raws: {}
})
}
// warnings
warnGridGap({
gap,
hasColumns,
decl,
result
})
let areas = parseGridAreas({
rows: gridRows,
gap
})
warnMissedAreas(areas, decl, result)
return decl
}
}
GridTemplateAreas.names = ['grid-template-areas']
module.exports = GridTemplateAreas

View File

@@ -0,0 +1,69 @@
let Declaration = require('../declaration')
let {
parseTemplate,
warnMissedAreas,
getGridGap,
warnGridGap,
inheritGridGap
} = require('./grid-utils')
class GridTemplate extends Declaration {
/**
* Translate grid-template to separate -ms- prefixed properties
*/
insert(decl, prefix, prefixes, result) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
if (decl.parent.some(i => i.prop === '-ms-grid-rows')) {
return undefined
}
let gap = getGridGap(decl)
/**
* we must insert inherited gap values in some cases:
* if we are inside media query && if we have no grid-gap value
*/
let inheritedGap = inheritGridGap(decl, gap)
let { rows, columns, areas } = parseTemplate({
decl,
gap: inheritedGap || gap
})
let hasAreas = Object.keys(areas).length > 0
let hasRows = Boolean(rows)
let hasColumns = Boolean(columns)
warnGridGap({
gap,
hasColumns,
decl,
result
})
warnMissedAreas(areas, decl, result)
if ((hasRows && hasColumns) || hasAreas) {
decl.cloneBefore({
prop: '-ms-grid-rows',
value: rows,
raws: {}
})
}
if (hasColumns) {
decl.cloneBefore({
prop: '-ms-grid-columns',
value: columns,
raws: {}
})
}
return decl
}
}
GridTemplate.names = ['grid-template']
module.exports = GridTemplate

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
let Declaration = require('../declaration')
class ImageRendering extends Declaration {
/**
* Add hack only for crisp-edges
*/
check(decl) {
return decl.value === 'pixelated'
}
/**
* Change property name for IE
*/
prefixed(prop, prefix) {
if (prefix === '-ms-') {
return '-ms-interpolation-mode'
}
return super.prefixed(prop, prefix)
}
/**
* Change property and value for IE
*/
set(decl, prefix) {
if (prefix !== '-ms-') return super.set(decl, prefix)
decl.prop = '-ms-interpolation-mode'
decl.value = 'nearest-neighbor'
return decl
}
/**
* Return property name by spec
*/
normalize() {
return 'image-rendering'
}
/**
* Warn on old value
*/
process(node, result) {
return super.process(node, result)
}
}
ImageRendering.names = ['image-rendering', 'interpolation-mode']
module.exports = ImageRendering

View File

@@ -0,0 +1,18 @@
let Value = require('../value')
class ImageSet extends Value {
/**
* Use non-standard name for WebKit and Firefox
*/
replace(string, prefix) {
let fixed = super.replace(string, prefix)
if (prefix === '-webkit-') {
fixed = fixed.replace(/("[^"]+"|'[^']+')(\s+\d+\w)/gi, 'url($1)$2')
}
return fixed
}
}
ImageSet.names = ['image-set']
module.exports = ImageSet

View File

@@ -0,0 +1,34 @@
let Declaration = require('../declaration')
class InlineLogical extends Declaration {
/**
* Use old syntax for -moz- and -webkit-
*/
prefixed(prop, prefix) {
return prefix + prop.replace('-inline', '')
}
/**
* Return property name by spec
*/
normalize(prop) {
return prop.replace(/(margin|padding|border)-(start|end)/, '$1-inline-$2')
}
}
InlineLogical.names = [
'border-inline-start',
'border-inline-end',
'margin-inline-start',
'margin-inline-end',
'padding-inline-start',
'padding-inline-end',
'border-start',
'border-end',
'margin-start',
'margin-end',
'padding-start',
'padding-end'
]
module.exports = InlineLogical

View File

@@ -0,0 +1,61 @@
let OldValue = require('../old-value')
let Value = require('../value')
function regexp(name) {
return new RegExp(`(^|[\\s,(])(${name}($|[\\s),]))`, 'gi')
}
class Intrinsic extends Value {
regexp() {
if (!this.regexpCache) this.regexpCache = regexp(this.name)
return this.regexpCache
}
isStretch() {
return (
this.name === 'stretch' ||
this.name === 'fill' ||
this.name === 'fill-available'
)
}
replace(string, prefix) {
if (prefix === '-moz-' && this.isStretch()) {
return string.replace(this.regexp(), '$1-moz-available$3')
}
if (prefix === '-webkit-' && this.isStretch()) {
return string.replace(this.regexp(), '$1-webkit-fill-available$3')
}
return super.replace(string, prefix)
}
old(prefix) {
let prefixed = prefix + this.name
if (this.isStretch()) {
if (prefix === '-moz-') {
prefixed = '-moz-available'
} else if (prefix === '-webkit-') {
prefixed = '-webkit-fill-available'
}
}
return new OldValue(this.name, prefixed, prefixed, regexp(prefixed))
}
add(decl, prefix) {
if (decl.prop.includes('grid') && prefix !== '-webkit-') {
return undefined
}
return super.add(decl, prefix)
}
}
Intrinsic.names = [
'max-content',
'min-content',
'fit-content',
'fill',
'fill-available',
'stretch'
]
module.exports = Intrinsic

View File

@@ -0,0 +1,54 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class JustifyContent extends Declaration {
/**
* Change property name for 2009 and 2012 specs
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2009) {
return prefix + 'box-pack'
}
if (spec === 2012) {
return prefix + 'flex-pack'
}
return super.prefixed(prop, prefix)
}
/**
* Return property name by final spec
*/
normalize() {
return 'justify-content'
}
/**
* Change value for 2009 and 2012 specs
*/
set(decl, prefix) {
let spec = flexSpec(prefix)[0]
if (spec === 2009 || spec === 2012) {
let value = JustifyContent.oldValues[decl.value] || decl.value
decl.value = value
if (spec !== 2009 || value !== 'distribute') {
return super.set(decl, prefix)
}
} else if (spec === 'final') {
return super.set(decl, prefix)
}
return undefined
}
}
JustifyContent.names = ['justify-content', 'flex-pack', 'box-pack']
JustifyContent.oldValues = {
'flex-end': 'end',
'flex-start': 'start',
'space-between': 'justify',
'space-around': 'distribute'
}
module.exports = JustifyContent

View File

@@ -0,0 +1,38 @@
let Declaration = require('../declaration')
class MaskBorder extends Declaration {
/**
* Return property name by final spec
*/
normalize() {
return this.name.replace('box-image', 'border')
}
/**
* Return flex property for 2012 spec
*/
prefixed(prop, prefix) {
let result = super.prefixed(prop, prefix)
if (prefix === '-webkit-') {
result = result.replace('border', 'box-image')
}
return result
}
}
MaskBorder.names = [
'mask-border',
'mask-border-source',
'mask-border-slice',
'mask-border-width',
'mask-border-outset',
'mask-border-repeat',
'mask-box-image',
'mask-box-image-source',
'mask-box-image-slice',
'mask-box-image-width',
'mask-box-image-outset',
'mask-box-image-repeat'
]
module.exports = MaskBorder

View File

@@ -0,0 +1,88 @@
let Declaration = require('../declaration')
class MaskComposite extends Declaration {
/**
* Prefix mask-composite for webkit
*/
insert(decl, prefix, prefixes) {
let isCompositeProp = decl.prop === 'mask-composite'
let compositeValues
if (isCompositeProp) {
compositeValues = decl.value.split(',')
} else {
compositeValues = decl.value.match(MaskComposite.regexp) || []
}
compositeValues = compositeValues.map(el => el.trim()).filter(el => el)
let hasCompositeValues = compositeValues.length
let compositeDecl
if (hasCompositeValues) {
compositeDecl = this.clone(decl)
compositeDecl.value = compositeValues
.map(value => MaskComposite.oldValues[value] || value)
.join(', ')
if (compositeValues.includes('intersect')) {
compositeDecl.value += ', xor'
}
compositeDecl.prop = prefix + 'mask-composite'
}
if (isCompositeProp) {
if (!hasCompositeValues) {
return undefined
}
if (this.needCascade(decl)) {
compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix)
}
return decl.parent.insertBefore(decl, compositeDecl)
}
let cloned = this.clone(decl)
cloned.prop = prefix + cloned.prop
if (hasCompositeValues) {
cloned.value = cloned.value.replace(MaskComposite.regexp, '')
}
if (this.needCascade(decl)) {
cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
}
decl.parent.insertBefore(decl, cloned)
if (!hasCompositeValues) {
return decl
}
if (this.needCascade(decl)) {
compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix)
}
return decl.parent.insertBefore(decl, compositeDecl)
}
}
MaskComposite.names = ['mask', 'mask-composite']
MaskComposite.oldValues = {
add: 'source-over',
subtract: 'source-out',
intersect: 'source-in',
exclude: 'xor'
}
MaskComposite.regexp = new RegExp(
`\\s+(${Object.keys(MaskComposite.oldValues).join(
'|'
)})\\b(?!\\))\\s*(?=[,])`,
'ig'
)
module.exports = MaskComposite

View File

@@ -0,0 +1,42 @@
let flexSpec = require('./flex-spec')
let Declaration = require('../declaration')
class Order extends Declaration {
/**
* Change property name for 2009 and 2012 specs
*/
prefixed(prop, prefix) {
let spec
;[spec, prefix] = flexSpec(prefix)
if (spec === 2009) {
return prefix + 'box-ordinal-group'
}
if (spec === 2012) {
return prefix + 'flex-order'
}
return super.prefixed(prop, prefix)
}
/**
* Return property name by final spec
*/
normalize() {
return 'order'
}
/**
* Fix value for 2009 spec
*/
set(decl, prefix) {
let spec = flexSpec(prefix)[0]
if (spec === 2009 && /\d/.test(decl.value)) {
decl.value = (parseInt(decl.value) + 1).toString()
return super.set(decl, prefix)
}
return super.set(decl, prefix)
}
}
Order.names = ['order', 'flex-order', 'box-ordinal-group']
module.exports = Order

View File

@@ -0,0 +1,33 @@
let Declaration = require('../declaration')
class OverscrollBehavior extends Declaration {
/**
* Change property name for IE
*/
prefixed(prop, prefix) {
return prefix + 'scroll-chaining'
}
/**
* Return property name by spec
*/
normalize() {
return 'overscroll-behavior'
}
/**
* Change value for IE
*/
set(decl, prefix) {
if (decl.value === 'auto') {
decl.value = 'chained'
} else if (decl.value === 'none' || decl.value === 'contain') {
decl.value = 'none'
}
return super.set(decl, prefix)
}
}
OverscrollBehavior.names = ['overscroll-behavior', 'scroll-chaining']
module.exports = OverscrollBehavior

View File

@@ -0,0 +1,34 @@
let OldValue = require('../old-value')
let Value = require('../value')
class Pixelated extends Value {
/**
* Use non-standard name for WebKit and Firefox
*/
replace(string, prefix) {
if (prefix === '-webkit-') {
return string.replace(this.regexp(), '$1-webkit-optimize-contrast')
}
if (prefix === '-moz-') {
return string.replace(this.regexp(), '$1-moz-crisp-edges')
}
return super.replace(string, prefix)
}
/**
* Different name for WebKit and Firefox
*/
old(prefix) {
if (prefix === '-webkit-') {
return new OldValue(this.name, '-webkit-optimize-contrast')
}
if (prefix === '-moz-') {
return new OldValue(this.name, '-moz-crisp-edges')
}
return super.old(prefix)
}
}
Pixelated.names = ['pixelated']
module.exports = Pixelated

View File

@@ -0,0 +1,32 @@
let Declaration = require('../declaration')
let utils = require('./grid-utils')
class PlaceSelf extends Declaration {
/**
* Translate place-self to separate -ms- prefixed properties
*/
insert(decl, prefix, prefixes) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
// prevent doubling of prefixes
if (decl.parent.some(i => i.prop === '-ms-grid-row-align')) {
return undefined
}
let [[first, second]] = utils.parse(decl)
if (second) {
utils.insertDecl(decl, 'grid-row-align', first)
utils.insertDecl(decl, 'grid-column-align', second)
} else {
utils.insertDecl(decl, 'grid-row-align', first)
utils.insertDecl(decl, 'grid-column-align', first)
}
return undefined
}
}
PlaceSelf.names = ['place-self']
module.exports = PlaceSelf

View File

@@ -0,0 +1,17 @@
let Selector = require('../selector')
class PlaceholderShown extends Selector {
/**
* Return different selectors depend on prefix
*/
prefixed(prefix) {
if (prefix === '-ms-') {
return ':-ms-input-placeholder'
}
return `:${prefix}placeholder-shown`
}
}
PlaceholderShown.names = [':placeholder-shown']
module.exports = PlaceholderShown

View File

@@ -0,0 +1,33 @@
let Selector = require('../selector')
class Placeholder extends Selector {
/**
* Add old mozilla to possible prefixes
*/
possible() {
return super.possible().concat(['-moz- old', '-ms- old'])
}
/**
* Return different selectors depend on prefix
*/
prefixed(prefix) {
if (prefix === '-webkit-') {
return '::-webkit-input-placeholder'
}
if (prefix === '-ms-') {
return '::-ms-input-placeholder'
}
if (prefix === '-ms- old') {
return ':-ms-input-placeholder'
}
if (prefix === '-moz- old') {
return ':-moz-placeholder'
}
return `::${prefix}placeholder`
}
}
Placeholder.names = ['::placeholder']
module.exports = Placeholder

View File

@@ -0,0 +1,23 @@
let Declaration = require('../declaration')
class TextDecorationSkipInk extends Declaration {
/**
* Change prefix for ink value
*/
set(decl, prefix) {
if (decl.prop === 'text-decoration-skip-ink' && decl.value === 'auto') {
decl.prop = prefix + 'text-decoration-skip'
decl.value = 'ink'
return decl
} else {
return super.set(decl, prefix)
}
}
}
TextDecorationSkipInk.names = [
'text-decoration-skip-ink',
'text-decoration-skip'
]
module.exports = TextDecorationSkipInk

View File

@@ -0,0 +1,25 @@
let Declaration = require('../declaration')
const BASIC = [
'none',
'underline',
'overline',
'line-through',
'blink',
'inherit',
'initial',
'unset'
]
class TextDecoration extends Declaration {
/**
* Do not add prefixes for basic values.
*/
check(decl) {
return decl.value.split(/\s+/).some(i => !BASIC.includes(i))
}
}
TextDecoration.names = ['text-decoration']
module.exports = TextDecoration

View File

@@ -0,0 +1,14 @@
let Declaration = require('../declaration')
class TextEmphasisPosition extends Declaration {
set(decl, prefix) {
if (prefix === '-webkit-') {
decl.value = decl.value.replace(/\s*(right|left)\s*/i, '')
}
return super.set(decl, prefix)
}
}
TextEmphasisPosition.names = ['text-emphasis-position']
module.exports = TextEmphasisPosition

View File

@@ -0,0 +1,79 @@
let Declaration = require('../declaration')
class TransformDecl extends Declaration {
/**
* Recursively check all parents for @keyframes
*/
keyframeParents(decl) {
let { parent } = decl
while (parent) {
if (parent.type === 'atrule' && parent.name === 'keyframes') {
return true
}
;({ parent } = parent)
}
return false
}
/**
* Is transform contain 3D commands
*/
contain3d(decl) {
if (decl.prop === 'transform-origin') {
return false
}
for (let func of TransformDecl.functions3d) {
if (decl.value.includes(`${func}(`)) {
return true
}
}
return false
}
/**
* Replace rotateZ to rotate for IE 9
*/
set(decl, prefix) {
decl = super.set(decl, prefix)
if (prefix === '-ms-') {
decl.value = decl.value.replace(/rotatez/gi, 'rotate')
}
return decl
}
/**
* Don't add prefix for IE in keyframes
*/
insert(decl, prefix, prefixes) {
if (prefix === '-ms-') {
if (!this.contain3d(decl) && !this.keyframeParents(decl)) {
return super.insert(decl, prefix, prefixes)
}
} else if (prefix === '-o-') {
if (!this.contain3d(decl)) {
return super.insert(decl, prefix, prefixes)
}
} else {
return super.insert(decl, prefix, prefixes)
}
return undefined
}
}
TransformDecl.names = ['transform', 'transform-origin']
TransformDecl.functions3d = [
'matrix3d',
'translate3d',
'translateZ',
'scale3d',
'scaleZ',
'rotate3d',
'rotateX',
'rotateY',
'perspective'
]
module.exports = TransformDecl

View File

@@ -0,0 +1,28 @@
let Declaration = require('../declaration')
class UserSelect extends Declaration {
/**
* Change prefixed value for IE
*/
set(decl, prefix) {
if (prefix === '-ms-' && decl.value === 'contain') {
decl.value = 'element'
}
return super.set(decl, prefix)
}
/**
* Avoid prefixing all in IE
*/
insert(decl, prefix, prefixes) {
if (decl.value === 'all' && prefix === '-ms-') {
return undefined
} else {
return super.insert(decl, prefix, prefixes)
}
}
}
UserSelect.names = ['user-select']
module.exports = UserSelect

View File

@@ -0,0 +1,42 @@
let Declaration = require('../declaration')
class WritingMode extends Declaration {
insert(decl, prefix, prefixes) {
if (prefix === '-ms-') {
let cloned = this.set(this.clone(decl), prefix)
if (this.needCascade(decl)) {
cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
}
let direction = 'ltr'
decl.parent.nodes.forEach(i => {
if (i.prop === 'direction') {
if (i.value === 'rtl' || i.value === 'ltr') direction = i.value
}
})
cloned.value = WritingMode.msValues[direction][decl.value] || decl.value
return decl.parent.insertBefore(decl, cloned)
}
return super.insert(decl, prefix, prefixes)
}
}
WritingMode.names = ['writing-mode']
WritingMode.msValues = {
ltr: {
'horizontal-tb': 'lr-tb',
'vertical-rl': 'tb-rl',
'vertical-lr': 'tb-lr'
},
rtl: {
'horizontal-tb': 'rl-tb',
'vertical-rl': 'bt-rl',
'vertical-lr': 'bt-lr'
}
}
module.exports = WritingMode

View File

@@ -0,0 +1,123 @@
let browserslist = require('browserslist')
function capitalize(str) {
return str.slice(0, 1).toUpperCase() + str.slice(1)
}
const NAMES = {
ie: 'IE',
ie_mob: 'IE Mobile',
ios_saf: 'iOS Safari',
op_mini: 'Opera Mini',
op_mob: 'Opera Mobile',
and_chr: 'Chrome for Android',
and_ff: 'Firefox for Android',
and_uc: 'UC for Android',
and_qq: 'QQ Browser',
kaios: 'KaiOS Browser',
baidu: 'Baidu Browser',
samsung: 'Samsung Internet'
}
function prefix(name, prefixes, note) {
let out = ` ${name}`
if (note) out += ' *'
out += ': '
out += prefixes.map(i => i.replace(/^-(.*)-$/g, '$1')).join(', ')
out += '\n'
return out
}
module.exports = function (prefixes) {
if (prefixes.browsers.selected.length === 0) {
return 'No browsers selected'
}
let versions = {}
for (let browser of prefixes.browsers.selected) {
let parts = browser.split(' ')
let name = parts[0]
let version = parts[1]
name = NAMES[name] || capitalize(name)
if (versions[name]) {
versions[name].push(version)
} else {
versions[name] = [version]
}
}
let out = 'Browsers:\n'
for (let browser in versions) {
let list = versions[browser]
list = list.sort((a, b) => parseFloat(b) - parseFloat(a))
out += ` ${browser}: ${list.join(', ')}\n`
}
let coverage = browserslist.coverage(prefixes.browsers.selected)
let round = Math.round(coverage * 100) / 100.0
out += `\nThese browsers account for ${round}% of all users globally\n`
let atrules = []
for (let name in prefixes.add) {
let data = prefixes.add[name]
if (name[0] === '@' && data.prefixes) {
atrules.push(prefix(name, data.prefixes))
}
}
if (atrules.length > 0) {
out += `\nAt-Rules:\n${atrules.sort().join('')}`
}
let selectors = []
for (let selector of prefixes.add.selectors) {
if (selector.prefixes) {
selectors.push(prefix(selector.name, selector.prefixes))
}
}
if (selectors.length > 0) {
out += `\nSelectors:\n${selectors.sort().join('')}`
}
let values = []
let props = []
let hadGrid = false
for (let name in prefixes.add) {
let data = prefixes.add[name]
if (name[0] !== '@' && data.prefixes) {
let grid = name.indexOf('grid-') === 0
if (grid) hadGrid = true
props.push(prefix(name, data.prefixes, grid))
}
if (!Array.isArray(data.values)) {
continue
}
for (let value of data.values) {
let grid = value.name.includes('grid')
if (grid) hadGrid = true
let string = prefix(value.name, value.prefixes, grid)
if (!values.includes(string)) {
values.push(string)
}
}
}
if (props.length > 0) {
out += `\nProperties:\n${props.sort().join('')}`
}
if (values.length > 0) {
out += `\nValues:\n${values.sort().join('')}`
}
if (hadGrid) {
out += '\n* - Prefixes will be added only on grid: true option.\n'
}
if (!atrules.length && !selectors.length && !props.length && !values.length) {
out +=
"\nAwesome! Your browsers don't require any vendor prefixes." +
'\nNow you can remove Autoprefixer from build steps.'
}
return out
}

View File

@@ -0,0 +1,67 @@
class OldSelector {
constructor(selector, prefix) {
this.prefix = prefix
this.prefixed = selector.prefixed(this.prefix)
this.regexp = selector.regexp(this.prefix)
this.prefixeds = selector
.possible()
.map(x => [selector.prefixed(x), selector.regexp(x)])
this.unprefixed = selector.name
this.nameRegexp = selector.regexp()
}
/**
* Is rule a hack without unprefixed version bottom
*/
isHack(rule) {
let index = rule.parent.index(rule) + 1
let rules = rule.parent.nodes
while (index < rules.length) {
let before = rules[index].selector
if (!before) {
return true
}
if (before.includes(this.unprefixed) && before.match(this.nameRegexp)) {
return false
}
let some = false
for (let [string, regexp] of this.prefixeds) {
if (before.includes(string) && before.match(regexp)) {
some = true
break
}
}
if (!some) {
return true
}
index += 1
}
return true
}
/**
* Does rule contain an unnecessary prefixed selector
*/
check(rule) {
if (!rule.selector.includes(this.prefixed)) {
return false
}
if (!rule.selector.match(this.regexp)) {
return false
}
if (this.isHack(rule)) {
return false
}
return true
}
}
module.exports = OldSelector

View File

@@ -0,0 +1,22 @@
let utils = require('./utils')
class OldValue {
constructor(unprefixed, prefixed, string, regexp) {
this.unprefixed = unprefixed
this.prefixed = prefixed
this.string = string || prefixed
this.regexp = regexp || utils.regexp(prefixed)
}
/**
* Check, that value contain old value
*/
check(value) {
if (value.includes(this.string)) {
return !!value.match(this.regexp)
}
return false
}
}
module.exports = OldValue

View File

@@ -0,0 +1,144 @@
let Browsers = require('./browsers')
let vendor = require('./vendor')
let utils = require('./utils')
/**
* Recursively clone objects
*/
function clone(obj, parent) {
let cloned = new obj.constructor()
for (let i of Object.keys(obj || {})) {
let value = obj[i]
if (i === 'parent' && typeof value === 'object') {
if (parent) {
cloned[i] = parent
}
} else if (i === 'source' || i === null) {
cloned[i] = value
} else if (Array.isArray(value)) {
cloned[i] = value.map(x => clone(x, cloned))
} else if (
i !== '_autoprefixerPrefix' &&
i !== '_autoprefixerValues' &&
i !== 'proxyCache'
) {
if (typeof value === 'object' && value !== null) {
value = clone(value, cloned)
}
cloned[i] = value
}
}
return cloned
}
class Prefixer {
/**
* Add hack to selected names
*/
static hack(klass) {
if (!this.hacks) {
this.hacks = {}
}
return klass.names.map(name => {
this.hacks[name] = klass
return this.hacks[name]
})
}
/**
* Load hacks for some names
*/
static load(name, prefixes, all) {
let Klass = this.hacks && this.hacks[name]
if (Klass) {
return new Klass(name, prefixes, all)
} else {
return new this(name, prefixes, all)
}
}
/**
* Clone node and clean autprefixer custom caches
*/
static clone(node, overrides) {
let cloned = clone(node)
for (let name in overrides) {
cloned[name] = overrides[name]
}
return cloned
}
constructor(name, prefixes, all) {
this.prefixes = prefixes
this.name = name
this.all = all
}
/**
* Find prefix in node parents
*/
parentPrefix(node) {
let prefix
if (typeof node._autoprefixerPrefix !== 'undefined') {
prefix = node._autoprefixerPrefix
} else if (node.type === 'decl' && node.prop[0] === '-') {
prefix = vendor.prefix(node.prop)
} else if (node.type === 'root') {
prefix = false
} else if (
node.type === 'rule' &&
node.selector.includes(':-') &&
/:(-\w+-)/.test(node.selector)
) {
prefix = node.selector.match(/:(-\w+-)/)[1]
} else if (node.type === 'atrule' && node.name[0] === '-') {
prefix = vendor.prefix(node.name)
} else {
prefix = this.parentPrefix(node.parent)
}
if (!Browsers.prefixes().includes(prefix)) {
prefix = false
}
node._autoprefixerPrefix = prefix
return node._autoprefixerPrefix
}
/**
* Clone node with prefixes
*/
process(node, result) {
if (!this.check(node)) {
return undefined
}
let parent = this.parentPrefix(node)
let prefixes = this.prefixes.filter(
prefix => !parent || parent === utils.removeNote(prefix)
)
let added = []
for (let prefix of prefixes) {
if (this.add(node, prefix, added.concat([prefix]), result)) {
added.push(prefix)
}
}
return added
}
/**
* Shortcut for Prefixer.clone
*/
clone(node, overrides) {
return Prefixer.clone(node, overrides)
}
}
module.exports = Prefixer

View File

@@ -0,0 +1,428 @@
let vendor = require('./vendor')
let Declaration = require('./declaration')
let Resolution = require('./resolution')
let Transition = require('./transition')
let Processor = require('./processor')
let Supports = require('./supports')
let Browsers = require('./browsers')
let Selector = require('./selector')
let AtRule = require('./at-rule')
let Value = require('./value')
let utils = require('./utils')
let hackFullscreen = require('./hacks/fullscreen')
let hackPlaceholder = require('./hacks/placeholder')
let hackPlaceholderShown = require('./hacks/placeholder-shown')
let hackFileSelectorButton = require('./hacks/file-selector-button')
let hackFlex = require('./hacks/flex')
let hackOrder = require('./hacks/order')
let hackFilter = require('./hacks/filter')
let hackGridEnd = require('./hacks/grid-end')
let hackAnimation = require('./hacks/animation')
let hackFlexFlow = require('./hacks/flex-flow')
let hackFlexGrow = require('./hacks/flex-grow')
let hackFlexWrap = require('./hacks/flex-wrap')
let hackGridArea = require('./hacks/grid-area')
let hackPlaceSelf = require('./hacks/place-self')
let hackGridStart = require('./hacks/grid-start')
let hackAlignSelf = require('./hacks/align-self')
let hackAppearance = require('./hacks/appearance')
let hackFlexBasis = require('./hacks/flex-basis')
let hackMaskBorder = require('./hacks/mask-border')
let hackMaskComposite = require('./hacks/mask-composite')
let hackAlignItems = require('./hacks/align-items')
let hackUserSelect = require('./hacks/user-select')
let hackFlexShrink = require('./hacks/flex-shrink')
let hackBreakProps = require('./hacks/break-props')
let hackColorAdjust = require('./hacks/color-adjust')
let hackWritingMode = require('./hacks/writing-mode')
let hackBorderImage = require('./hacks/border-image')
let hackAlignContent = require('./hacks/align-content')
let hackBorderRadius = require('./hacks/border-radius')
let hackBlockLogical = require('./hacks/block-logical')
let hackGridTemplate = require('./hacks/grid-template')
let hackInlineLogical = require('./hacks/inline-logical')
let hackGridRowAlign = require('./hacks/grid-row-align')
let hackTransformDecl = require('./hacks/transform-decl')
let hackFlexDirection = require('./hacks/flex-direction')
let hackImageRendering = require('./hacks/image-rendering')
let hackBackdropFilter = require('./hacks/backdrop-filter')
let hackBackgroundClip = require('./hacks/background-clip')
let hackTextDecoration = require('./hacks/text-decoration')
let hackJustifyContent = require('./hacks/justify-content')
let hackBackgroundSize = require('./hacks/background-size')
let hackGridRowColumn = require('./hacks/grid-row-column')
let hackGridRowsColumns = require('./hacks/grid-rows-columns')
let hackGridColumnAlign = require('./hacks/grid-column-align')
let hackOverscrollBehavior = require('./hacks/overscroll-behavior')
let hackGridTemplateAreas = require('./hacks/grid-template-areas')
let hackTextEmphasisPosition = require('./hacks/text-emphasis-position')
let hackTextDecorationSkipInk = require('./hacks/text-decoration-skip-ink')
let hackGradient = require('./hacks/gradient')
let hackIntrinsic = require('./hacks/intrinsic')
let hackPixelated = require('./hacks/pixelated')
let hackImageSet = require('./hacks/image-set')
let hackCrossFade = require('./hacks/cross-fade')
let hackDisplayFlex = require('./hacks/display-flex')
let hackDisplayGrid = require('./hacks/display-grid')
let hackFilterValue = require('./hacks/filter-value')
let hackAutofill = require('./hacks/autofill')
Selector.hack(hackAutofill)
Selector.hack(hackFullscreen)
Selector.hack(hackPlaceholder)
Selector.hack(hackPlaceholderShown)
Selector.hack(hackFileSelectorButton)
Declaration.hack(hackFlex)
Declaration.hack(hackOrder)
Declaration.hack(hackFilter)
Declaration.hack(hackGridEnd)
Declaration.hack(hackAnimation)
Declaration.hack(hackFlexFlow)
Declaration.hack(hackFlexGrow)
Declaration.hack(hackFlexWrap)
Declaration.hack(hackGridArea)
Declaration.hack(hackPlaceSelf)
Declaration.hack(hackGridStart)
Declaration.hack(hackAlignSelf)
Declaration.hack(hackAppearance)
Declaration.hack(hackFlexBasis)
Declaration.hack(hackMaskBorder)
Declaration.hack(hackMaskComposite)
Declaration.hack(hackAlignItems)
Declaration.hack(hackUserSelect)
Declaration.hack(hackFlexShrink)
Declaration.hack(hackBreakProps)
Declaration.hack(hackColorAdjust)
Declaration.hack(hackWritingMode)
Declaration.hack(hackBorderImage)
Declaration.hack(hackAlignContent)
Declaration.hack(hackBorderRadius)
Declaration.hack(hackBlockLogical)
Declaration.hack(hackGridTemplate)
Declaration.hack(hackInlineLogical)
Declaration.hack(hackGridRowAlign)
Declaration.hack(hackTransformDecl)
Declaration.hack(hackFlexDirection)
Declaration.hack(hackImageRendering)
Declaration.hack(hackBackdropFilter)
Declaration.hack(hackBackgroundClip)
Declaration.hack(hackTextDecoration)
Declaration.hack(hackJustifyContent)
Declaration.hack(hackBackgroundSize)
Declaration.hack(hackGridRowColumn)
Declaration.hack(hackGridRowsColumns)
Declaration.hack(hackGridColumnAlign)
Declaration.hack(hackOverscrollBehavior)
Declaration.hack(hackGridTemplateAreas)
Declaration.hack(hackTextEmphasisPosition)
Declaration.hack(hackTextDecorationSkipInk)
Value.hack(hackGradient)
Value.hack(hackIntrinsic)
Value.hack(hackPixelated)
Value.hack(hackImageSet)
Value.hack(hackCrossFade)
Value.hack(hackDisplayFlex)
Value.hack(hackDisplayGrid)
Value.hack(hackFilterValue)
let declsCache = new Map()
class Prefixes {
constructor(data, browsers, options = {}) {
this.data = data
this.browsers = browsers
this.options = options
;[this.add, this.remove] = this.preprocess(this.select(this.data))
this.transition = new Transition(this)
this.processor = new Processor(this)
}
/**
* Return clone instance to remove all prefixes
*/
cleaner() {
if (this.cleanerCache) {
return this.cleanerCache
}
if (this.browsers.selected.length) {
let empty = new Browsers(this.browsers.data, [])
this.cleanerCache = new Prefixes(this.data, empty, this.options)
} else {
return this
}
return this.cleanerCache
}
/**
* Select prefixes from data, which is necessary for selected browsers
*/
select(list) {
let selected = { add: {}, remove: {} }
for (let name in list) {
let data = list[name]
let add = data.browsers.map(i => {
let params = i.split(' ')
return {
browser: `${params[0]} ${params[1]}`,
note: params[2]
}
})
let notes = add
.filter(i => i.note)
.map(i => `${this.browsers.prefix(i.browser)} ${i.note}`)
notes = utils.uniq(notes)
add = add
.filter(i => this.browsers.isSelected(i.browser))
.map(i => {
let prefix = this.browsers.prefix(i.browser)
if (i.note) {
return `${prefix} ${i.note}`
} else {
return prefix
}
})
add = this.sort(utils.uniq(add))
if (this.options.flexbox === 'no-2009') {
add = add.filter(i => !i.includes('2009'))
}
let all = data.browsers.map(i => this.browsers.prefix(i))
if (data.mistakes) {
all = all.concat(data.mistakes)
}
all = all.concat(notes)
all = utils.uniq(all)
if (add.length) {
selected.add[name] = add
if (add.length < all.length) {
selected.remove[name] = all.filter(i => !add.includes(i))
}
} else {
selected.remove[name] = all
}
}
return selected
}
/**
* Sort vendor prefixes
*/
sort(prefixes) {
return prefixes.sort((a, b) => {
let aLength = utils.removeNote(a).length
let bLength = utils.removeNote(b).length
if (aLength === bLength) {
return b.length - a.length
} else {
return bLength - aLength
}
})
}
/**
* Cache prefixes data to fast CSS processing
*/
preprocess(selected) {
let add = {
'selectors': [],
'@supports': new Supports(Prefixes, this)
}
for (let name in selected.add) {
let prefixes = selected.add[name]
if (name === '@keyframes' || name === '@viewport') {
add[name] = new AtRule(name, prefixes, this)
} else if (name === '@resolution') {
add[name] = new Resolution(name, prefixes, this)
} else if (this.data[name].selector) {
add.selectors.push(Selector.load(name, prefixes, this))
} else {
let props = this.data[name].props
if (props) {
let value = Value.load(name, prefixes, this)
for (let prop of props) {
if (!add[prop]) {
add[prop] = { values: [] }
}
add[prop].values.push(value)
}
} else {
let values = (add[name] && add[name].values) || []
add[name] = Declaration.load(name, prefixes, this)
add[name].values = values
}
}
}
let remove = { selectors: [] }
for (let name in selected.remove) {
let prefixes = selected.remove[name]
if (this.data[name].selector) {
let selector = Selector.load(name, prefixes)
for (let prefix of prefixes) {
remove.selectors.push(selector.old(prefix))
}
} else if (name === '@keyframes' || name === '@viewport') {
for (let prefix of prefixes) {
let prefixed = `@${prefix}${name.slice(1)}`
remove[prefixed] = { remove: true }
}
} else if (name === '@resolution') {
remove[name] = new Resolution(name, prefixes, this)
} else {
let props = this.data[name].props
if (props) {
let value = Value.load(name, [], this)
for (let prefix of prefixes) {
let old = value.old(prefix)
if (old) {
for (let prop of props) {
if (!remove[prop]) {
remove[prop] = {}
}
if (!remove[prop].values) {
remove[prop].values = []
}
remove[prop].values.push(old)
}
}
}
} else {
for (let p of prefixes) {
let olds = this.decl(name).old(name, p)
if (name === 'align-self') {
let a = add[name] && add[name].prefixes
if (a) {
if (p === '-webkit- 2009' && a.includes('-webkit-')) {
continue
} else if (p === '-webkit-' && a.includes('-webkit- 2009')) {
continue
}
}
}
for (let prefixed of olds) {
if (!remove[prefixed]) {
remove[prefixed] = {}
}
remove[prefixed].remove = true
}
}
}
}
}
return [add, remove]
}
/**
* Declaration loader with caching
*/
decl(prop) {
if (!declsCache.has(prop)) {
declsCache.set(prop, Declaration.load(prop))
}
return declsCache.get(prop)
}
/**
* Return unprefixed version of property
*/
unprefixed(prop) {
let value = this.normalize(vendor.unprefixed(prop))
if (value === 'flex-direction') {
value = 'flex-flow'
}
return value
}
/**
* Normalize prefix for remover
*/
normalize(prop) {
return this.decl(prop).normalize(prop)
}
/**
* Return prefixed version of property
*/
prefixed(prop, prefix) {
prop = vendor.unprefixed(prop)
return this.decl(prop).prefixed(prop, prefix)
}
/**
* Return values, which must be prefixed in selected property
*/
values(type, prop) {
let data = this[type]
let global = data['*'] && data['*'].values
let values = data[prop] && data[prop].values
if (global && values) {
return utils.uniq(global.concat(values))
} else {
return global || values || []
}
}
/**
* Group declaration by unprefixed property to check them
*/
group(decl) {
let rule = decl.parent
let index = rule.index(decl)
let { length } = rule.nodes
let unprefixed = this.unprefixed(decl.prop)
let checker = (step, callback) => {
index += step
while (index >= 0 && index < length) {
let other = rule.nodes[index]
if (other.type === 'decl') {
if (step === -1 && other.prop === unprefixed) {
if (!Browsers.withPrefix(other.value)) {
break
}
}
if (this.unprefixed(other.prop) !== unprefixed) {
break
} else if (callback(other) === true) {
return true
}
if (step === +1 && other.prop === unprefixed) {
if (!Browsers.withPrefix(other.value)) {
break
}
}
}
index += step
}
return false
}
return {
up(callback) {
return checker(-1, callback)
},
down(callback) {
return checker(+1, callback)
}
}
}
}
module.exports = Prefixes

View File

@@ -0,0 +1,710 @@
let parser = require('postcss-value-parser')
let Value = require('./value')
let insertAreas = require('./hacks/grid-utils').insertAreas
const OLD_LINEAR = /(^|[^-])linear-gradient\(\s*(top|left|right|bottom)/i
const OLD_RADIAL = /(^|[^-])radial-gradient\(\s*\d+(\w*|%)\s+\d+(\w*|%)\s*,/i
const IGNORE_NEXT = /(!\s*)?autoprefixer:\s*ignore\s+next/i
const GRID_REGEX = /(!\s*)?autoprefixer\s*grid:\s*(on|off|(no-)?autoplace)/i
const SIZES = [
'width',
'height',
'min-width',
'max-width',
'min-height',
'max-height',
'inline-size',
'min-inline-size',
'max-inline-size',
'block-size',
'min-block-size',
'max-block-size'
]
function hasGridTemplate(decl) {
return decl.parent.some(
i => i.prop === 'grid-template' || i.prop === 'grid-template-areas'
)
}
function hasRowsAndColumns(decl) {
let hasRows = decl.parent.some(i => i.prop === 'grid-template-rows')
let hasColumns = decl.parent.some(i => i.prop === 'grid-template-columns')
return hasRows && hasColumns
}
class Processor {
constructor(prefixes) {
this.prefixes = prefixes
}
/**
* Add necessary prefixes
*/
add(css, result) {
// At-rules
let resolution = this.prefixes.add['@resolution']
let keyframes = this.prefixes.add['@keyframes']
let viewport = this.prefixes.add['@viewport']
let supports = this.prefixes.add['@supports']
css.walkAtRules(rule => {
if (rule.name === 'keyframes') {
if (!this.disabled(rule, result)) {
return keyframes && keyframes.process(rule)
}
} else if (rule.name === 'viewport') {
if (!this.disabled(rule, result)) {
return viewport && viewport.process(rule)
}
} else if (rule.name === 'supports') {
if (
this.prefixes.options.supports !== false &&
!this.disabled(rule, result)
) {
return supports.process(rule)
}
} else if (rule.name === 'media' && rule.params.includes('-resolution')) {
if (!this.disabled(rule, result)) {
return resolution && resolution.process(rule)
}
}
return undefined
})
// Selectors
css.walkRules(rule => {
if (this.disabled(rule, result)) return undefined
return this.prefixes.add.selectors.map(selector => {
return selector.process(rule, result)
})
})
function insideGrid(decl) {
return decl.parent.nodes.some(node => {
if (node.type !== 'decl') return false
let displayGrid =
node.prop === 'display' && /(inline-)?grid/.test(node.value)
let gridTemplate = node.prop.startsWith('grid-template')
let gridGap = /^grid-([A-z]+-)?gap/.test(node.prop)
return displayGrid || gridTemplate || gridGap
})
}
function insideFlex(decl) {
return decl.parent.some(node => {
return node.prop === 'display' && /(inline-)?flex/.test(node.value)
})
}
let gridPrefixes =
this.gridStatus(css, result) &&
this.prefixes.add['grid-area'] &&
this.prefixes.add['grid-area'].prefixes
css.walkDecls(decl => {
if (this.disabledDecl(decl, result)) return undefined
let parent = decl.parent
let prop = decl.prop
let value = decl.value
if (prop === 'grid-row-span') {
result.warn(
'grid-row-span is not part of final Grid Layout. Use grid-row.',
{ node: decl }
)
return undefined
} else if (prop === 'grid-column-span') {
result.warn(
'grid-column-span is not part of final Grid Layout. Use grid-column.',
{ node: decl }
)
return undefined
} else if (prop === 'display' && value === 'box') {
result.warn(
'You should write display: flex by final spec ' +
'instead of display: box',
{ node: decl }
)
return undefined
} else if (prop === 'text-emphasis-position') {
if (value === 'under' || value === 'over') {
result.warn(
'You should use 2 values for text-emphasis-position ' +
'For example, `under left` instead of just `under`.',
{ node: decl }
)
}
} else if (
/^(align|justify|place)-(items|content)$/.test(prop) &&
insideFlex(decl)
) {
if (value === 'start' || value === 'end') {
result.warn(
`${value} value has mixed support, consider using ` +
`flex-${value} instead`,
{ node: decl }
)
}
} else if (prop === 'text-decoration-skip' && value === 'ink') {
result.warn(
'Replace text-decoration-skip: ink to ' +
'text-decoration-skip-ink: auto, because spec had been changed',
{ node: decl }
)
} else {
if (gridPrefixes && this.gridStatus(decl, result)) {
if (decl.value === 'subgrid') {
result.warn('IE does not support subgrid', { node: decl })
}
if (/^(align|justify|place)-items$/.test(prop) && insideGrid(decl)) {
let fixed = prop.replace('-items', '-self')
result.warn(
`IE does not support ${prop} on grid containers. ` +
`Try using ${fixed} on child elements instead: ` +
`${decl.parent.selector} > * { ${fixed}: ${decl.value} }`,
{ node: decl }
)
} else if (
/^(align|justify|place)-content$/.test(prop) &&
insideGrid(decl)
) {
result.warn(`IE does not support ${decl.prop} on grid containers`, {
node: decl
})
} else if (prop === 'display' && decl.value === 'contents') {
result.warn(
'Please do not use display: contents; ' +
'if you have grid setting enabled',
{ node: decl }
)
return undefined
} else if (decl.prop === 'grid-gap') {
let status = this.gridStatus(decl, result)
if (
status === 'autoplace' &&
!hasRowsAndColumns(decl) &&
!hasGridTemplate(decl)
) {
result.warn(
'grid-gap only works if grid-template(-areas) is being ' +
'used or both rows and columns have been declared ' +
'and cells have not been manually ' +
'placed inside the explicit grid',
{ node: decl }
)
} else if (
(status === true || status === 'no-autoplace') &&
!hasGridTemplate(decl)
) {
result.warn(
'grid-gap only works if grid-template(-areas) is being used',
{ node: decl }
)
}
} else if (prop === 'grid-auto-columns') {
result.warn('grid-auto-columns is not supported by IE', {
node: decl
})
return undefined
} else if (prop === 'grid-auto-rows') {
result.warn('grid-auto-rows is not supported by IE', { node: decl })
return undefined
} else if (prop === 'grid-auto-flow') {
let hasRows = parent.some(i => i.prop === 'grid-template-rows')
let hasCols = parent.some(i => i.prop === 'grid-template-columns')
if (hasGridTemplate(decl)) {
result.warn('grid-auto-flow is not supported by IE', {
node: decl
})
} else if (value.includes('dense')) {
result.warn('grid-auto-flow: dense is not supported by IE', {
node: decl
})
} else if (!hasRows && !hasCols) {
result.warn(
'grid-auto-flow works only if grid-template-rows and ' +
'grid-template-columns are present in the same rule',
{ node: decl }
)
}
return undefined
} else if (value.includes('auto-fit')) {
result.warn('auto-fit value is not supported by IE', {
node: decl,
word: 'auto-fit'
})
return undefined
} else if (value.includes('auto-fill')) {
result.warn('auto-fill value is not supported by IE', {
node: decl,
word: 'auto-fill'
})
return undefined
} else if (prop.startsWith('grid-template') && value.includes('[')) {
result.warn(
'Autoprefixer currently does not support line names. ' +
'Try using grid-template-areas instead.',
{ node: decl, word: '[' }
)
}
}
if (value.includes('radial-gradient')) {
if (OLD_RADIAL.test(decl.value)) {
result.warn(
'Gradient has outdated direction syntax. ' +
'New syntax is like `closest-side at 0 0` ' +
'instead of `0 0, closest-side`.',
{ node: decl }
)
} else {
let ast = parser(value)
for (let i of ast.nodes) {
if (i.type === 'function' && i.value === 'radial-gradient') {
for (let word of i.nodes) {
if (word.type === 'word') {
if (word.value === 'cover') {
result.warn(
'Gradient has outdated direction syntax. ' +
'Replace `cover` to `farthest-corner`.',
{ node: decl }
)
} else if (word.value === 'contain') {
result.warn(
'Gradient has outdated direction syntax. ' +
'Replace `contain` to `closest-side`.',
{ node: decl }
)
}
}
}
}
}
}
}
if (value.includes('linear-gradient')) {
if (OLD_LINEAR.test(value)) {
result.warn(
'Gradient has outdated direction syntax. ' +
'New syntax is like `to left` instead of `right`.',
{ node: decl }
)
}
}
}
if (SIZES.includes(decl.prop)) {
if (!decl.value.includes('-fill-available')) {
if (decl.value.includes('fill-available')) {
result.warn(
'Replace fill-available to stretch, ' +
'because spec had been changed',
{ node: decl }
)
} else if (decl.value.includes('fill')) {
let ast = parser(value)
if (ast.nodes.some(i => i.type === 'word' && i.value === 'fill')) {
result.warn(
'Replace fill to stretch, because spec had been changed',
{ node: decl }
)
}
}
}
}
let prefixer
if (decl.prop === 'transition' || decl.prop === 'transition-property') {
// Transition
return this.prefixes.transition.add(decl, result)
} else if (decl.prop === 'align-self') {
// align-self flexbox or grid
let display = this.displayType(decl)
if (display !== 'grid' && this.prefixes.options.flexbox !== false) {
prefixer = this.prefixes.add['align-self']
if (prefixer && prefixer.prefixes) {
prefixer.process(decl)
}
}
if (this.gridStatus(decl, result) !== false) {
prefixer = this.prefixes.add['grid-row-align']
if (prefixer && prefixer.prefixes) {
return prefixer.process(decl, result)
}
}
} else if (decl.prop === 'justify-self') {
// justify-self flexbox or grid
if (this.gridStatus(decl, result) !== false) {
prefixer = this.prefixes.add['grid-column-align']
if (prefixer && prefixer.prefixes) {
return prefixer.process(decl, result)
}
}
} else if (decl.prop === 'place-self') {
prefixer = this.prefixes.add['place-self']
if (
prefixer &&
prefixer.prefixes &&
this.gridStatus(decl, result) !== false
) {
return prefixer.process(decl, result)
}
} else {
// Properties
prefixer = this.prefixes.add[decl.prop]
if (prefixer && prefixer.prefixes) {
return prefixer.process(decl, result)
}
}
return undefined
})
// Insert grid-area prefixes. We need to be able to store the different
// rules as a data and hack API is not enough for this
if (this.gridStatus(css, result)) {
insertAreas(css, this.disabled)
}
// Values
return css.walkDecls(decl => {
if (this.disabledValue(decl, result)) return
let unprefixed = this.prefixes.unprefixed(decl.prop)
let list = this.prefixes.values('add', unprefixed)
if (Array.isArray(list)) {
for (let value of list) {
if (value.process) value.process(decl, result)
}
}
Value.save(this.prefixes, decl)
})
}
/**
* Remove unnecessary pefixes
*/
remove(css, result) {
// At-rules
let resolution = this.prefixes.remove['@resolution']
css.walkAtRules((rule, i) => {
if (this.prefixes.remove[`@${rule.name}`]) {
if (!this.disabled(rule, result)) {
rule.parent.removeChild(i)
}
} else if (
rule.name === 'media' &&
rule.params.includes('-resolution') &&
resolution
) {
resolution.clean(rule)
}
})
// Selectors
for (let checker of this.prefixes.remove.selectors) {
css.walkRules((rule, i) => {
if (checker.check(rule)) {
if (!this.disabled(rule, result)) {
rule.parent.removeChild(i)
}
}
})
}
return css.walkDecls((decl, i) => {
if (this.disabled(decl, result)) return
let rule = decl.parent
let unprefixed = this.prefixes.unprefixed(decl.prop)
// Transition
if (decl.prop === 'transition' || decl.prop === 'transition-property') {
this.prefixes.transition.remove(decl)
}
// Properties
if (
this.prefixes.remove[decl.prop] &&
this.prefixes.remove[decl.prop].remove
) {
let notHack = this.prefixes.group(decl).down(other => {
return this.prefixes.normalize(other.prop) === unprefixed
})
if (unprefixed === 'flex-flow') {
notHack = true
}
if (decl.prop === '-webkit-box-orient') {
let hacks = { 'flex-direction': true, 'flex-flow': true }
if (!decl.parent.some(j => hacks[j.prop])) return
}
if (notHack && !this.withHackValue(decl)) {
if (decl.raw('before').includes('\n')) {
this.reduceSpaces(decl)
}
rule.removeChild(i)
return
}
}
// Values
for (let checker of this.prefixes.values('remove', unprefixed)) {
if (!checker.check) continue
if (!checker.check(decl.value)) continue
unprefixed = checker.unprefixed
let notHack = this.prefixes.group(decl).down(other => {
return other.value.includes(unprefixed)
})
if (notHack) {
rule.removeChild(i)
return
}
}
})
}
/**
* Some rare old values, which is not in standard
*/
withHackValue(decl) {
return decl.prop === '-webkit-background-clip' && decl.value === 'text'
}
/**
* Check for grid/flexbox options.
*/
disabledValue(node, result) {
if (this.gridStatus(node, result) === false && node.type === 'decl') {
if (node.prop === 'display' && node.value.includes('grid')) {
return true
}
}
if (this.prefixes.options.flexbox === false && node.type === 'decl') {
if (node.prop === 'display' && node.value.includes('flex')) {
return true
}
}
if (node.type === 'decl' && node.prop === 'content') {
return true
}
return this.disabled(node, result)
}
/**
* Check for grid/flexbox options.
*/
disabledDecl(node, result) {
if (this.gridStatus(node, result) === false && node.type === 'decl') {
if (node.prop.includes('grid') || node.prop === 'justify-items') {
return true
}
}
if (this.prefixes.options.flexbox === false && node.type === 'decl') {
let other = ['order', 'justify-content', 'align-items', 'align-content']
if (node.prop.includes('flex') || other.includes(node.prop)) {
return true
}
}
return this.disabled(node, result)
}
/**
* Check for control comment and global options
*/
disabled(node, result) {
if (!node) return false
if (node._autoprefixerDisabled !== undefined) {
return node._autoprefixerDisabled
}
if (node.parent) {
let p = node.prev()
if (p && p.type === 'comment' && IGNORE_NEXT.test(p.text)) {
node._autoprefixerDisabled = true
node._autoprefixerSelfDisabled = true
return true
}
}
let value = null
if (node.nodes) {
let status
node.each(i => {
if (i.type !== 'comment') return
if (/(!\s*)?autoprefixer:\s*(off|on)/i.test(i.text)) {
if (typeof status !== 'undefined') {
result.warn(
'Second Autoprefixer control comment ' +
'was ignored. Autoprefixer applies control ' +
'comment to whole block, not to next rules.',
{ node: i }
)
} else {
status = /on/i.test(i.text)
}
}
})
if (status !== undefined) {
value = !status
}
}
if (!node.nodes || value === null) {
if (node.parent) {
let isParentDisabled = this.disabled(node.parent, result)
if (node.parent._autoprefixerSelfDisabled === true) {
value = false
} else {
value = isParentDisabled
}
} else {
value = false
}
}
node._autoprefixerDisabled = value
return value
}
/**
* Normalize spaces in cascade declaration group
*/
reduceSpaces(decl) {
let stop = false
this.prefixes.group(decl).up(() => {
stop = true
return true
})
if (stop) {
return
}
let parts = decl.raw('before').split('\n')
let prevMin = parts[parts.length - 1].length
let diff = false
this.prefixes.group(decl).down(other => {
parts = other.raw('before').split('\n')
let last = parts.length - 1
if (parts[last].length > prevMin) {
if (diff === false) {
diff = parts[last].length - prevMin
}
parts[last] = parts[last].slice(0, -diff)
other.raws.before = parts.join('\n')
}
})
}
/**
* Is it flebox or grid rule
*/
displayType(decl) {
for (let i of decl.parent.nodes) {
if (i.prop !== 'display') {
continue
}
if (i.value.includes('flex')) {
return 'flex'
}
if (i.value.includes('grid')) {
return 'grid'
}
}
return false
}
/**
* Set grid option via control comment
*/
gridStatus(node, result) {
if (!node) return false
if (node._autoprefixerGridStatus !== undefined) {
return node._autoprefixerGridStatus
}
let value = null
if (node.nodes) {
let status
node.each(i => {
if (i.type !== 'comment') return
if (GRID_REGEX.test(i.text)) {
let hasAutoplace = /:\s*autoplace/i.test(i.text)
let noAutoplace = /no-autoplace/i.test(i.text)
if (typeof status !== 'undefined') {
result.warn(
'Second Autoprefixer grid control comment was ' +
'ignored. Autoprefixer applies control comments to the whole ' +
'block, not to the next rules.',
{ node: i }
)
} else if (hasAutoplace) {
status = 'autoplace'
} else if (noAutoplace) {
status = true
} else {
status = /on/i.test(i.text)
}
}
})
if (status !== undefined) {
value = status
}
}
if (node.type === 'atrule' && node.name === 'supports') {
let params = node.params
if (params.includes('grid') && params.includes('auto')) {
value = false
}
}
if (!node.nodes || value === null) {
if (node.parent) {
let isParentGrid = this.gridStatus(node.parent, result)
if (node.parent._autoprefixerSelfDisabled === true) {
value = false
} else {
value = isParentGrid
}
} else if (typeof this.prefixes.options.grid !== 'undefined') {
value = this.prefixes.options.grid
} else if (typeof process.env.AUTOPREFIXER_GRID !== 'undefined') {
if (process.env.AUTOPREFIXER_GRID === 'autoplace') {
value = 'autoplace'
} else {
value = true
}
} else {
value = false
}
}
node._autoprefixerGridStatus = value
return value
}
}
module.exports = Processor

View File

@@ -0,0 +1,97 @@
let FractionJs = require('fraction.js')
let Prefixer = require('./prefixer')
let utils = require('./utils')
const REGEXP = /(min|max)-resolution\s*:\s*\d*\.?\d+(dppx|dpcm|dpi|x)/gi
const SPLIT = /(min|max)-resolution(\s*:\s*)(\d*\.?\d+)(dppx|dpcm|dpi|x)/i
class Resolution extends Prefixer {
/**
* Return prefixed query name
*/
prefixName(prefix, name) {
if (prefix === '-moz-') {
return name + '--moz-device-pixel-ratio'
} else {
return prefix + name + '-device-pixel-ratio'
}
}
/**
* Return prefixed query
*/
prefixQuery(prefix, name, colon, value, units) {
value = new FractionJs(value)
// 1dpcm = 2.54dpi
// 1dppx = 96dpi
if (units === 'dpi') {
value = value.div(96)
} else if (units === 'dpcm') {
value = value.mul(2.54).div(96)
}
value = value.simplify()
if (prefix === '-o-') {
value = value.n + '/' + value.d
}
return this.prefixName(prefix, name) + colon + value
}
/**
* Remove prefixed queries
*/
clean(rule) {
if (!this.bad) {
this.bad = []
for (let prefix of this.prefixes) {
this.bad.push(this.prefixName(prefix, 'min'))
this.bad.push(this.prefixName(prefix, 'max'))
}
}
rule.params = utils.editList(rule.params, queries => {
return queries.filter(query => this.bad.every(i => !query.includes(i)))
})
}
/**
* Add prefixed queries
*/
process(rule) {
let parent = this.parentPrefix(rule)
let prefixes = parent ? [parent] : this.prefixes
rule.params = utils.editList(rule.params, (origin, prefixed) => {
for (let query of origin) {
if (
!query.includes('min-resolution') &&
!query.includes('max-resolution')
) {
prefixed.push(query)
continue
}
for (let prefix of prefixes) {
let processed = query.replace(REGEXP, str => {
let parts = str.match(SPLIT)
return this.prefixQuery(
prefix,
parts[1],
parts[2],
parts[3],
parts[4]
)
})
prefixed.push(processed)
}
prefixed.push(query)
}
return utils.uniq(prefixed)
})
}
}
module.exports = Resolution

View File

@@ -0,0 +1,150 @@
let { list } = require('postcss')
let OldSelector = require('./old-selector')
let Prefixer = require('./prefixer')
let Browsers = require('./browsers')
let utils = require('./utils')
class Selector extends Prefixer {
constructor(name, prefixes, all) {
super(name, prefixes, all)
this.regexpCache = new Map()
}
/**
* Is rule selectors need to be prefixed
*/
check(rule) {
if (rule.selector.includes(this.name)) {
return !!rule.selector.match(this.regexp())
}
return false
}
/**
* Return prefixed version of selector
*/
prefixed(prefix) {
return this.name.replace(/^(\W*)/, `$1${prefix}`)
}
/**
* Lazy loadRegExp for name
*/
regexp(prefix) {
if (!this.regexpCache.has(prefix)) {
let name = prefix ? this.prefixed(prefix) : this.name
this.regexpCache.set(
prefix,
new RegExp(`(^|[^:"'=])${utils.escapeRegexp(name)}`, 'gi')
)
}
return this.regexpCache.get(prefix)
}
/**
* All possible prefixes
*/
possible() {
return Browsers.prefixes()
}
/**
* Return all possible selector prefixes
*/
prefixeds(rule) {
if (rule._autoprefixerPrefixeds) {
if (rule._autoprefixerPrefixeds[this.name]) {
return rule._autoprefixerPrefixeds
}
} else {
rule._autoprefixerPrefixeds = {}
}
let prefixeds = {}
if (rule.selector.includes(',')) {
let ruleParts = list.comma(rule.selector)
let toProcess = ruleParts.filter(el => el.includes(this.name))
for (let prefix of this.possible()) {
prefixeds[prefix] = toProcess
.map(el => this.replace(el, prefix))
.join(', ')
}
} else {
for (let prefix of this.possible()) {
prefixeds[prefix] = this.replace(rule.selector, prefix)
}
}
rule._autoprefixerPrefixeds[this.name] = prefixeds
return rule._autoprefixerPrefixeds
}
/**
* Is rule already prefixed before
*/
already(rule, prefixeds, prefix) {
let index = rule.parent.index(rule) - 1
while (index >= 0) {
let before = rule.parent.nodes[index]
if (before.type !== 'rule') {
return false
}
let some = false
for (let key in prefixeds[this.name]) {
let prefixed = prefixeds[this.name][key]
if (before.selector === prefixed) {
if (prefix === key) {
return true
} else {
some = true
break
}
}
}
if (!some) {
return false
}
index -= 1
}
return false
}
/**
* Replace selectors by prefixed one
*/
replace(selector, prefix) {
return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`)
}
/**
* Clone and add prefixes for at-rule
*/
add(rule, prefix) {
let prefixeds = this.prefixeds(rule)
if (this.already(rule, prefixeds, prefix)) {
return
}
let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] })
rule.parent.insertBefore(rule, cloned)
}
/**
* Return function to fast find prefixed selector
*/
old(prefix) {
return new OldSelector(this, prefix)
}
}
module.exports = Selector

View File

@@ -0,0 +1,302 @@
let featureQueries = require('caniuse-lite/data/features/css-featurequeries.js')
let { feature } = require('caniuse-lite')
let { parse } = require('postcss')
let Browsers = require('./browsers')
let brackets = require('./brackets')
let Value = require('./value')
let utils = require('./utils')
let data = feature(featureQueries)
let supported = []
for (let browser in data.stats) {
let versions = data.stats[browser]
for (let version in versions) {
let support = versions[version]
if (/y/.test(support)) {
supported.push(browser + ' ' + version)
}
}
}
class Supports {
constructor(Prefixes, all) {
this.Prefixes = Prefixes
this.all = all
}
/**
* Return prefixer only with @supports supported browsers
*/
prefixer() {
if (this.prefixerCache) {
return this.prefixerCache
}
let filtered = this.all.browsers.selected.filter(i => {
return supported.includes(i)
})
let browsers = new Browsers(
this.all.browsers.data,
filtered,
this.all.options
)
this.prefixerCache = new this.Prefixes(
this.all.data,
browsers,
this.all.options
)
return this.prefixerCache
}
/**
* Parse string into declaration property and value
*/
parse(str) {
let parts = str.split(':')
let prop = parts[0]
let value = parts[1]
if (!value) value = ''
return [prop.trim(), value.trim()]
}
/**
* Create virtual rule to process it by prefixer
*/
virtual(str) {
let [prop, value] = this.parse(str)
let rule = parse('a{}').first
rule.append({ prop, value, raws: { before: '' } })
return rule
}
/**
* Return array of Declaration with all necessary prefixes
*/
prefixed(str) {
let rule = this.virtual(str)
if (this.disabled(rule.first)) {
return rule.nodes
}
let result = { warn: () => null }
let prefixer = this.prefixer().add[rule.first.prop]
prefixer && prefixer.process && prefixer.process(rule.first, result)
for (let decl of rule.nodes) {
for (let value of this.prefixer().values('add', rule.first.prop)) {
value.process(decl)
}
Value.save(this.all, decl)
}
return rule.nodes
}
/**
* Return true if brackets node is "not" word
*/
isNot(node) {
return typeof node === 'string' && /not\s*/i.test(node)
}
/**
* Return true if brackets node is "or" word
*/
isOr(node) {
return typeof node === 'string' && /\s*or\s*/i.test(node)
}
/**
* Return true if brackets node is (prop: value)
*/
isProp(node) {
return (
typeof node === 'object' &&
node.length === 1 &&
typeof node[0] === 'string'
)
}
/**
* Return true if prefixed property has no unprefixed
*/
isHack(all, unprefixed) {
let check = new RegExp(`(\\(|\\s)${utils.escapeRegexp(unprefixed)}:`)
return !check.test(all)
}
/**
* Return true if we need to remove node
*/
toRemove(str, all) {
let [prop, value] = this.parse(str)
let unprefixed = this.all.unprefixed(prop)
let cleaner = this.all.cleaner()
if (
cleaner.remove[prop] &&
cleaner.remove[prop].remove &&
!this.isHack(all, unprefixed)
) {
return true
}
for (let checker of cleaner.values('remove', unprefixed)) {
if (checker.check(value)) {
return true
}
}
return false
}
/**
* Remove all unnecessary prefixes
*/
remove(nodes, all) {
let i = 0
while (i < nodes.length) {
if (
!this.isNot(nodes[i - 1]) &&
this.isProp(nodes[i]) &&
this.isOr(nodes[i + 1])
) {
if (this.toRemove(nodes[i][0], all)) {
nodes.splice(i, 2)
continue
}
i += 2
continue
}
if (typeof nodes[i] === 'object') {
nodes[i] = this.remove(nodes[i], all)
}
i += 1
}
return nodes
}
/**
* Clean brackets with one child
*/
cleanBrackets(nodes) {
return nodes.map(i => {
if (typeof i !== 'object') {
return i
}
if (i.length === 1 && typeof i[0] === 'object') {
return this.cleanBrackets(i[0])
}
return this.cleanBrackets(i)
})
}
/**
* Add " or " between properties and convert it to brackets format
*/
convert(progress) {
let result = ['']
for (let i of progress) {
result.push([`${i.prop}: ${i.value}`])
result.push(' or ')
}
result[result.length - 1] = ''
return result
}
/**
* Compress value functions into a string nodes
*/
normalize(nodes) {
if (typeof nodes !== 'object') {
return nodes
}
nodes = nodes.filter(i => i !== '')
if (typeof nodes[0] === 'string') {
let firstNode = nodes[0].trim()
if (
firstNode.includes(':') ||
firstNode === 'selector' ||
firstNode === 'not selector'
) {
return [brackets.stringify(nodes)]
}
}
return nodes.map(i => this.normalize(i))
}
/**
* Add prefixes
*/
add(nodes, all) {
return nodes.map(i => {
if (this.isProp(i)) {
let prefixed = this.prefixed(i[0])
if (prefixed.length > 1) {
return this.convert(prefixed)
}
return i
}
if (typeof i === 'object') {
return this.add(i, all)
}
return i
})
}
/**
* Add prefixed declaration
*/
process(rule) {
let ast = brackets.parse(rule.params)
ast = this.normalize(ast)
ast = this.remove(ast, rule.params)
ast = this.add(ast, rule.params)
ast = this.cleanBrackets(ast)
rule.params = brackets.stringify(ast)
}
/**
* Check global options
*/
disabled(node) {
if (!this.all.options.grid) {
if (node.prop === 'display' && node.value.includes('grid')) {
return true
}
if (node.prop.includes('grid') || node.prop === 'justify-items') {
return true
}
}
if (this.all.options.flexbox === false) {
if (node.prop === 'display' && node.value.includes('flex')) {
return true
}
let other = ['order', 'justify-content', 'align-items', 'align-content']
if (node.prop.includes('flex') || other.includes(node.prop)) {
return true
}
}
return false
}
}
module.exports = Supports

View File

@@ -0,0 +1,329 @@
let { list } = require('postcss')
let parser = require('postcss-value-parser')
let Browsers = require('./browsers')
let vendor = require('./vendor')
class Transition {
constructor(prefixes) {
this.props = ['transition', 'transition-property']
this.prefixes = prefixes
}
/**
* Process transition and add prefixes for all necessary properties
*/
add(decl, result) {
let prefix, prop
let add = this.prefixes.add[decl.prop]
let vendorPrefixes = this.ruleVendorPrefixes(decl)
let declPrefixes = vendorPrefixes || (add && add.prefixes) || []
let params = this.parse(decl.value)
let names = params.map(i => this.findProp(i))
let added = []
if (names.some(i => i[0] === '-')) {
return
}
for (let param of params) {
prop = this.findProp(param)
if (prop[0] === '-') continue
let prefixer = this.prefixes.add[prop]
if (!prefixer || !prefixer.prefixes) continue
for (prefix of prefixer.prefixes) {
if (vendorPrefixes && !vendorPrefixes.some(p => prefix.includes(p))) {
continue
}
let prefixed = this.prefixes.prefixed(prop, prefix)
if (prefixed !== '-ms-transform' && !names.includes(prefixed)) {
if (!this.disabled(prop, prefix)) {
added.push(this.clone(prop, prefixed, param))
}
}
}
}
params = params.concat(added)
let value = this.stringify(params)
let webkitClean = this.stringify(
this.cleanFromUnprefixed(params, '-webkit-')
)
if (declPrefixes.includes('-webkit-')) {
this.cloneBefore(decl, `-webkit-${decl.prop}`, webkitClean)
}
this.cloneBefore(decl, decl.prop, webkitClean)
if (declPrefixes.includes('-o-')) {
let operaClean = this.stringify(this.cleanFromUnprefixed(params, '-o-'))
this.cloneBefore(decl, `-o-${decl.prop}`, operaClean)
}
for (prefix of declPrefixes) {
if (prefix !== '-webkit-' && prefix !== '-o-') {
let prefixValue = this.stringify(
this.cleanOtherPrefixes(params, prefix)
)
this.cloneBefore(decl, prefix + decl.prop, prefixValue)
}
}
if (value !== decl.value && !this.already(decl, decl.prop, value)) {
this.checkForWarning(result, decl)
decl.cloneBefore()
decl.value = value
}
}
/**
* Find property name
*/
findProp(param) {
let prop = param[0].value
if (/^\d/.test(prop)) {
for (let [i, token] of param.entries()) {
if (i !== 0 && token.type === 'word') {
return token.value
}
}
}
return prop
}
/**
* Does we already have this declaration
*/
already(decl, prop, value) {
return decl.parent.some(i => i.prop === prop && i.value === value)
}
/**
* Add declaration if it is not exist
*/
cloneBefore(decl, prop, value) {
if (!this.already(decl, prop, value)) {
decl.cloneBefore({ prop, value })
}
}
/**
* Show transition-property warning
*/
checkForWarning(result, decl) {
if (decl.prop !== 'transition-property') {
return
}
let isPrefixed = false
let hasAssociatedProp = false
decl.parent.each(i => {
if (i.type !== 'decl') {
return undefined
}
if (i.prop.indexOf('transition-') !== 0) {
return undefined
}
let values = list.comma(i.value)
// check if current Rule's transition-property comma separated value list needs prefixes
if (i.prop === 'transition-property') {
values.forEach(value => {
let lookup = this.prefixes.add[value]
if (lookup && lookup.prefixes && lookup.prefixes.length > 0) {
isPrefixed = true
}
})
return undefined
}
// check if another transition-* prop in current Rule has comma separated value list
hasAssociatedProp = hasAssociatedProp || values.length > 1
return false
})
if (isPrefixed && hasAssociatedProp) {
decl.warn(
result,
'Replace transition-property to transition, ' +
'because Autoprefixer could not support ' +
'any cases of transition-property ' +
'and other transition-*'
)
}
}
/**
* Process transition and remove all unnecessary properties
*/
remove(decl) {
let params = this.parse(decl.value)
params = params.filter(i => {
let prop = this.prefixes.remove[this.findProp(i)]
return !prop || !prop.remove
})
let value = this.stringify(params)
if (decl.value === value) {
return
}
if (params.length === 0) {
decl.remove()
return
}
let double = decl.parent.some(i => {
return i.prop === decl.prop && i.value === value
})
let smaller = decl.parent.some(i => {
return i !== decl && i.prop === decl.prop && i.value.length > value.length
})
if (double || smaller) {
decl.remove()
return
}
decl.value = value
}
/**
* Parse properties list to array
*/
parse(value) {
let ast = parser(value)
let result = []
let param = []
for (let node of ast.nodes) {
param.push(node)
if (node.type === 'div' && node.value === ',') {
result.push(param)
param = []
}
}
result.push(param)
return result.filter(i => i.length > 0)
}
/**
* Return properties string from array
*/
stringify(params) {
if (params.length === 0) {
return ''
}
let nodes = []
for (let param of params) {
if (param[param.length - 1].type !== 'div') {
param.push(this.div(params))
}
nodes = nodes.concat(param)
}
if (nodes[0].type === 'div') {
nodes = nodes.slice(1)
}
if (nodes[nodes.length - 1].type === 'div') {
nodes = nodes.slice(0, +-2 + 1 || undefined)
}
return parser.stringify({ nodes })
}
/**
* Return new param array with different name
*/
clone(origin, name, param) {
let result = []
let changed = false
for (let i of param) {
if (!changed && i.type === 'word' && i.value === origin) {
result.push({ type: 'word', value: name })
changed = true
} else {
result.push(i)
}
}
return result
}
/**
* Find or create separator
*/
div(params) {
for (let param of params) {
for (let node of param) {
if (node.type === 'div' && node.value === ',') {
return node
}
}
}
return { type: 'div', value: ',', after: ' ' }
}
cleanOtherPrefixes(params, prefix) {
return params.filter(param => {
let current = vendor.prefix(this.findProp(param))
return current === '' || current === prefix
})
}
/**
* Remove all non-webkit prefixes and unprefixed params if we have prefixed
*/
cleanFromUnprefixed(params, prefix) {
let remove = params
.map(i => this.findProp(i))
.filter(i => i.slice(0, prefix.length) === prefix)
.map(i => this.prefixes.unprefixed(i))
let result = []
for (let param of params) {
let prop = this.findProp(param)
let p = vendor.prefix(prop)
if (!remove.includes(prop) && (p === prefix || p === '')) {
result.push(param)
}
}
return result
}
/**
* Check property for disabled by option
*/
disabled(prop, prefix) {
let other = ['order', 'justify-content', 'align-self', 'align-content']
if (prop.includes('flex') || other.includes(prop)) {
if (this.prefixes.options.flexbox === false) {
return true
}
if (this.prefixes.options.flexbox === 'no-2009') {
return prefix.includes('2009')
}
}
return undefined
}
/**
* Check if transition prop is inside vendor specific rule
*/
ruleVendorPrefixes(decl) {
let { parent } = decl
if (parent.type !== 'rule') {
return false
} else if (!parent.selector.includes(':-')) {
return false
}
let selectors = Browsers.prefixes().filter(s =>
parent.selector.includes(':' + s)
)
return selectors.length > 0 ? selectors : false
}
}
module.exports = Transition

View File

@@ -0,0 +1,78 @@
let { list } = require('postcss')
/**
* Throw special error, to tell beniary,
* that this error is from Autoprefixer.
*/
module.exports.error = function (text) {
let err = new Error(text)
err.autoprefixer = true
throw err
}
/**
* Return array, that doesnt contain duplicates.
*/
module.exports.uniq = function (array) {
return [...new Set(array)]
}
/**
* Return "-webkit-" on "-webkit- old"
*/
module.exports.removeNote = function (string) {
if (!string.includes(' ')) {
return string
}
return string.split(' ')[0]
}
/**
* Escape RegExp symbols
*/
module.exports.escapeRegexp = function (string) {
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&')
}
/**
* Return regexp to check, that CSS string contain word
*/
module.exports.regexp = function (word, escape = true) {
if (escape) {
word = this.escapeRegexp(word)
}
return new RegExp(`(^|[\\s,(])(${word}($|[\\s(,]))`, 'gi')
}
/**
* Change comma list
*/
module.exports.editList = function (value, callback) {
let origin = list.comma(value)
let changed = callback(origin, [])
if (origin === changed) {
return value
}
let join = value.match(/,\s*/)
join = join ? join[0] : ', '
return changed.join(join)
}
/**
* Split the selector into parts.
* It returns 3 level deep array because selectors can be comma
* separated (1), space separated (2), and combined (3)
* @param {String} selector selector string
* @return {Array<Array<Array>>} 3 level deep array of split selector
* @see utils.test.js for examples
*/
module.exports.splitSelector = function (selector) {
return list.comma(selector).map(i => {
return list.space(i).map(k => {
return k.split(/(?=\.|#)/g)
})
})
}

View File

@@ -0,0 +1,125 @@
let Prefixer = require('./prefixer')
let OldValue = require('./old-value')
let vendor = require('./vendor')
let utils = require('./utils')
class Value extends Prefixer {
/**
* Clone decl for each prefixed values
*/
static save(prefixes, decl) {
let prop = decl.prop
let result = []
for (let prefix in decl._autoprefixerValues) {
let value = decl._autoprefixerValues[prefix]
if (value === decl.value) {
continue
}
let item
let propPrefix = vendor.prefix(prop)
if (propPrefix === '-pie-') {
continue
}
if (propPrefix === prefix) {
item = decl.value = value
result.push(item)
continue
}
let prefixed = prefixes.prefixed(prop, prefix)
let rule = decl.parent
if (!rule.every(i => i.prop !== prefixed)) {
result.push(item)
continue
}
let trimmed = value.replace(/\s+/, ' ')
let already = rule.some(
i => i.prop === decl.prop && i.value.replace(/\s+/, ' ') === trimmed
)
if (already) {
result.push(item)
continue
}
let cloned = this.clone(decl, { value })
item = decl.parent.insertBefore(decl, cloned)
result.push(item)
}
return result
}
/**
* Is declaration need to be prefixed
*/
check(decl) {
let value = decl.value
if (!value.includes(this.name)) {
return false
}
return !!value.match(this.regexp())
}
/**
* Lazy regexp loading
*/
regexp() {
return this.regexpCache || (this.regexpCache = utils.regexp(this.name))
}
/**
* Add prefix to values in string
*/
replace(string, prefix) {
return string.replace(this.regexp(), `$1${prefix}$2`)
}
/**
* Get value with comments if it was not changed
*/
value(decl) {
if (decl.raws.value && decl.raws.value.value === decl.value) {
return decl.raws.value.raw
} else {
return decl.value
}
}
/**
* Save values with next prefixed token
*/
add(decl, prefix) {
if (!decl._autoprefixerValues) {
decl._autoprefixerValues = {}
}
let value = decl._autoprefixerValues[prefix] || this.value(decl)
let before
do {
before = value
value = this.replace(value, prefix)
if (value === false) return
} while (value !== before)
decl._autoprefixerValues[prefix] = value
}
/**
* Return function to fast find prefixed value
*/
old(prefix) {
return new OldValue(this.name, prefix + this.name)
}
}
module.exports = Value

View File

@@ -0,0 +1,14 @@
module.exports = {
prefix(prop) {
let match = prop.match(/^(-\w+-)/)
if (match) {
return match[0]
}
return ''
},
unprefixed(prop) {
return prop.replace(/^-\w+-/, '')
}
}

View File

@@ -0,0 +1 @@
../../../browserslist/cli.js

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2014 Andrey Sitnik <andrey@sitnik.ru> and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,66 @@
# Browserslist [![Cult Of Martians][cult-img]][cult]
<img width="120" height="120" alt="Browserslist logo by Anton Lovchikov"
src="https://browserslist.github.io/browserslist/logo.svg" align="right">
The config to share target browsers and Node.js versions between different
front-end tools. It is used in:
* [Autoprefixer]
* [Babel]
* [postcss-preset-env]
* [eslint-plugin-compat]
* [stylelint-no-unsupported-browser-features]
* [postcss-normalize]
* [obsolete-webpack-plugin]
All tools will find target browsers automatically,
when you add the following to `package.json`:
```json
"browserslist": [
"defaults",
"not IE 11",
"maintained node versions"
]
```
Or in `.browserslistrc` config:
```yaml
# Browsers that we support
defaults
not IE 11
maintained node versions
```
Developers set their version lists using queries like `last 2 versions`
to be free from updating versions manually.
Browserslist will use [`caniuse-lite`] with [Can I Use] data for this queries.
Browserslist will take queries from tool option,
`browserslist` config, `.browserslistrc` config,
`browserslist` section in `package.json` or environment variables.
[cult-img]: https://cultofmartians.com/assets/badges/badge.svg
[cult]: https://cultofmartians.com/done.html
<a href="https://evilmartians.com/?utm_source=browserslist">
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg"
alt="Sponsored by Evil Martians" width="236" height="54">
</a>
[stylelint-no-unsupported-browser-features]: https://github.com/ismay/stylelint-no-unsupported-browser-features
[eslint-plugin-compat]: https://github.com/amilajack/eslint-plugin-compat
[Browserslist Example]: https://github.com/browserslist/browserslist-example
[postcss-preset-env]: https://github.com/jonathantneal/postcss-preset-env
[postcss-normalize]: https://github.com/jonathantneal/postcss-normalize
[`caniuse-lite`]: https://github.com/ben-eb/caniuse-lite
[Autoprefixer]: https://github.com/postcss/autoprefixer
[Can I Use]: https://caniuse.com/
[Babel]: https://github.com/babel/babel/tree/master/packages/babel-preset-env
[obsolete-webpack-plugin]: https://github.com/ElemeFE/obsolete-webpack-plugin
## Docs
Read **[full docs](https://github.com/browserslist/browserslist#readme)** on GitHub.

View File

@@ -0,0 +1,50 @@
var BrowserslistError = require('./error')
function noop() {}
module.exports = {
loadQueries: function loadQueries() {
throw new BrowserslistError(
'Sharable configs are not supported in client-side build of Browserslist'
)
},
getStat: function getStat(opts) {
return opts.stats
},
loadConfig: function loadConfig(opts) {
if (opts.config) {
throw new BrowserslistError(
'Browserslist config are not supported in client-side build'
)
}
},
loadCountry: function loadCountry() {
throw new BrowserslistError(
'Country statistics are not supported ' +
'in client-side build of Browserslist'
)
},
loadFeature: function loadFeature() {
throw new BrowserslistError(
'Supports queries are not available in client-side build of Browserslist'
)
},
currentNode: function currentNode(resolve, context) {
return resolve(['maintained node versions'], context)[0]
},
parseConfig: noop,
readConfig: noop,
findConfig: noop,
clearCaches: noop,
oldDataWarning: noop
}

View File

@@ -0,0 +1,152 @@
#!/usr/bin/env node
var fs = require('fs')
var browserslist = require('./')
var updateDb = require('./update-db')
var pkg = require('./package.json')
var args = process.argv.slice(2)
var USAGE =
'Usage:\n' +
' npx browserslist\n' +
' npx browserslist "QUERIES"\n' +
' npx browserslist --json "QUERIES"\n' +
' npx browserslist --config="path/to/browserlist/file"\n' +
' npx browserslist --coverage "QUERIES"\n' +
' npx browserslist --coverage=US "QUERIES"\n' +
' npx browserslist --coverage=US,RU,global "QUERIES"\n' +
' npx browserslist --env="environment name defined in config"\n' +
' npx browserslist --stats="path/to/browserlist/stats/file"\n' +
' npx browserslist --mobile-to-desktop\n' +
' npx browserslist --ignore-unknown-versions\n' +
' npx browserslist --update-db'
function isArg(arg) {
return args.some(function (str) {
return str === arg || str.indexOf(arg + '=') === 0
})
}
function error(msg) {
process.stderr.write('browserslist: ' + msg + '\n')
process.exit(1)
}
if (isArg('--help') || isArg('-h')) {
process.stdout.write(pkg.description + '.\n\n' + USAGE + '\n')
} else if (isArg('--version') || isArg('-v')) {
process.stdout.write('browserslist ' + pkg.version + '\n')
} else if (isArg('--update-db')) {
/* c8 ignore next 3 */
updateDb(function (str) {
process.stdout.write(str)
})
} else {
var mode = 'browsers'
var opts = {}
var queries
var areas
for (var i = 0; i < args.length; i++) {
if (args[i][0] !== '-') {
queries = args[i].replace(/^["']|["']$/g, '')
continue
}
var arg = args[i].split('=')
var name = arg[0]
var value = arg[1]
if (value) value = value.replace(/^["']|["']$/g, '')
if (name === '--config' || name === '-b') {
opts.config = value
} else if (name === '--env' || name === '-e') {
opts.env = value
} else if (name === '--stats' || name === '-s') {
opts.stats = value
} else if (name === '--coverage' || name === '-c') {
if (mode !== 'json') mode = 'coverage'
if (value) {
areas = value.split(',')
} else {
areas = ['global']
}
} else if (name === '--json') {
mode = 'json'
} else if (name === '--mobile-to-desktop') {
/* c8 ignore next */
opts.mobileToDesktop = true
} else if (name === '--ignore-unknown-versions') {
/* c8 ignore next */
opts.ignoreUnknownVersions = true
} else {
error('Unknown arguments ' + args[i] + '.\n\n' + USAGE)
}
}
var browsers
try {
browsers = browserslist(queries, opts)
} catch (e) {
if (e.name === 'BrowserslistError') {
error(e.message)
} else /* c8 ignore start */ {
throw e
} /* c8 ignore end */
}
var coverage
if (mode === 'browsers') {
browsers.forEach(function (browser) {
process.stdout.write(browser + '\n')
})
} else if (areas) {
coverage = areas.map(function (area) {
var stats
if (area !== 'global') {
stats = area
} else if (opts.stats) {
stats = JSON.parse(fs.readFileSync(opts.stats))
}
var result = browserslist.coverage(browsers, stats)
var round = Math.round(result * 100) / 100.0
return [area, round]
})
if (mode === 'coverage') {
var prefix = 'These browsers account for '
process.stdout.write(prefix)
coverage.forEach(function (data, index) {
var area = data[0]
var round = data[1]
var end = 'globally'
if (area && area !== 'global') {
end = 'in the ' + area.toUpperCase()
} else if (opts.stats) {
end = 'in custom statistics'
}
if (index !== 0) {
process.stdout.write(prefix.replace(/./g, ' '))
}
process.stdout.write(round + '% of all users ' + end + '\n')
})
}
}
if (mode === 'json') {
var data = { browsers: browsers }
if (coverage) {
data.coverage = coverage.reduce(function (object, j) {
object[j[0]] = j[1]
return object
}, {})
}
process.stdout.write(JSON.stringify(data, null, ' ') + '\n')
}
}

View File

@@ -0,0 +1,7 @@
declare class BrowserslistError extends Error {
constructor(message: any)
name: 'BrowserslistError'
browserslist: true
}
export = BrowserslistError

View File

@@ -0,0 +1,12 @@
function BrowserslistError(message) {
this.name = 'BrowserslistError'
this.message = message
this.browserslist = true
if (Error.captureStackTrace) {
Error.captureStackTrace(this, BrowserslistError)
}
}
BrowserslistError.prototype = Error.prototype
module.exports = BrowserslistError

View File

@@ -0,0 +1,178 @@
/**
* Return array of browsers by selection queries.
*
* ```js
* browserslist('IE >= 10, IE 8') //=> ['ie 11', 'ie 10', 'ie 8']
* ```
*
* @param queries Browser queries.
* @returns Array with browser names in Can I Use.
*/
declare function browserslist(
queries?: string | readonly string[] | null,
opts?: browserslist.Options
): string[]
declare namespace browserslist {
interface Options {
/**
* Path to processed file. It will be used to find config files.
*/
path?: string | false
/**
* Processing environment. It will be used to take right queries
* from config file.
*/
env?: string
/**
* Custom browser usage statistics for "> 1% in my stats" query.
*/
stats?: Stats | string
/**
* Path to config file with queries.
*/
config?: string
/**
* Do not throw on unknown version in direct query.
*/
ignoreUnknownVersions?: boolean
/**
* Throw a error if env is not found.
*/
throwOnMissing?: boolean
/**
* Disable security checks for extend query.
*/
dangerousExtend?: boolean
/**
* Alias mobile browsers to the desktop version when Can I Use
* doesnt have data about the specified version.
*/
mobileToDesktop?: boolean
}
type Config = {
defaults: string[]
[section: string]: string[] | undefined
}
interface Stats {
[browser: string]: {
[version: string]: number
}
}
/**
* Browser names aliases.
*/
let aliases: {
[alias: string]: string | undefined
}
/**
* Aliases to work with joined versions like `ios_saf 7.0-7.1`.
*/
let versionAliases: {
[browser: string]:
| {
[version: string]: string | undefined
}
| undefined
}
/**
* Can I Use only provides a few versions for some browsers (e.g. `and_chr`).
*
* Fallback to a similar browser for unknown versions.
*/
let desktopNames: {
[browser: string]: string | undefined
}
let data: {
[browser: string]:
| {
name: string
versions: string[]
released: string[]
releaseDate: {
[version: string]: number | undefined | null
}
}
| undefined
}
let nodeVersions: string[]
interface Usage {
[version: string]: number
}
let usage: {
global?: Usage
custom?: Usage | null
[country: string]: Usage | undefined | null
}
let cache: {
[feature: string]: {
[name: string]: 'y' | 'n'
}
}
/**
* Default browsers query
*/
let defaults: readonly string[]
/**
* Which statistics should be used. Country code or custom statistics.
* Pass `"my stats"` to load statistics from `Browserslist` files.
*/
type StatsOptions = string | 'my stats' | Stats | { dataByBrowser: Stats }
/**
* Return browsers market coverage.
*
* ```js
* browserslist.coverage(browserslist('> 1% in US'), 'US') //=> 83.1
* ```
*
* @param browsers Browsers names in Can I Use.
* @param stats Which statistics should be used.
* @returns Total market coverage for all selected browsers.
*/
function coverage(browsers: readonly string[], stats?: StatsOptions): number
function clearCaches(): void
function parseConfig(string: string): Config
function readConfig(file: string): Config
function findConfig(...pathSegments: string[]): Config | undefined
interface LoadConfigOptions {
config?: string
path?: string
env?: string
}
function loadConfig(options: LoadConfigOptions): string[] | undefined
}
declare global {
namespace NodeJS {
interface ProcessEnv {
BROWSERSLIST?: string
BROWSERSLIST_CONFIG?: string
BROWSERSLIST_DANGEROUS_EXTEND?: string
BROWSERSLIST_DISABLE_CACHE?: string
BROWSERSLIST_ENV?: string
BROWSERSLIST_IGNORE_OLD_DATA?: string
BROWSERSLIST_STATS?: string
}
}
}
export = browserslist

Some files were not shown because too many files have changed in this diff Show More