feat: Implement initial Go backend and React frontend project structure for the gotail application.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../autoprefixer/bin/autoprefixer" "$@"
|
||||
else
|
||||
exec node "$basedir/../autoprefixer/bin/autoprefixer" "$@"
|
||||
fi
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\autoprefixer\bin\autoprefixer" %*
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../autoprefixer/bin/autoprefixer" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../autoprefixer/bin/autoprefixer" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../autoprefixer/bin/autoprefixer" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../autoprefixer/bin/autoprefixer" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
+76
@@ -2054,6 +2054,44 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.22",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz",
|
||||
"integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.27.0",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
"fraction.js": "^5.3.4",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"autoprefixer": "bin/autoprefixer"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
@@ -2839,6 +2877,20 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
|
||||
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@@ -3421,6 +3473,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -3573,6 +3635,13 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -4492,6 +4561,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.17",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
||||
"integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
|
||||
+20
@@ -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.
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Autoprefixer [![Cult Of Martians][cult-img]][cult]
|
||||
|
||||
<img align="right" width="94" height="71"
|
||||
src="https://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 {
|
||||
width: stretch;
|
||||
}
|
||||
```
|
||||
|
||||
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;
|
||||
}
|
||||
::placeholder {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: -webkit-fill-available;
|
||||
width: -moz-available;
|
||||
width: stretch;
|
||||
}
|
||||
```
|
||||
|
||||
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]: https://cultofmartians.com/assets/badges/badge.svg
|
||||
[PostCSS]: https://github.com/postcss/postcss
|
||||
[cult]: https://cultofmartians.com/tasks/autoprefixer-grid.html
|
||||
|
||||
|
||||
## Docs
|
||||
Read full docs **[here](https://github.com/postcss/autoprefixer#readme)**.
|
||||
+22
@@ -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'
|
||||
)
|
||||
}
|
||||
+1139
File diff suppressed because it is too large
Load Diff
+35
@@ -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
|
||||
+95
@@ -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
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
let browserslist = require('browserslist')
|
||||
let { agents } = require('caniuse-lite/dist/unpacker/agents')
|
||||
let pico = require('picocolors')
|
||||
|
||||
let dataPrefixes = require('../data/prefixes')
|
||||
let Browsers = require('./browsers')
|
||||
let getInfo = require('./info')
|
||||
let Prefixes = require('./prefixes')
|
||||
|
||||
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 = {
|
||||
env: options.env,
|
||||
ignoreUnknownVersions: options.ignoreUnknownVersions,
|
||||
stats: options.stats
|
||||
}
|
||||
|
||||
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 {
|
||||
browsers: reqs,
|
||||
|
||||
info(opts) {
|
||||
opts = opts || {}
|
||||
opts.from = opts.from || process.cwd()
|
||||
return getInfo(loadPrefixes(opts))
|
||||
},
|
||||
|
||||
options,
|
||||
|
||||
postcssPlugin: 'autoprefixer',
|
||||
prepare(result) {
|
||||
let prefixes = loadPrefixes({
|
||||
env: options.env,
|
||||
from: result.opts.from
|
||||
})
|
||||
|
||||
return {
|
||||
OnceExit(root) {
|
||||
timeCapsule(result, prefixes)
|
||||
if (options.remove !== false) {
|
||||
prefixes.processor.remove(root, result)
|
||||
}
|
||||
if (options.add !== false) {
|
||||
prefixes.processor.add(root, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugin.postcss = true
|
||||
|
||||
/**
|
||||
* Autoprefixer data
|
||||
*/
|
||||
plugin.data = autoprefixerData
|
||||
|
||||
/**
|
||||
* Autoprefixer default browsers
|
||||
*/
|
||||
plugin.defaults = browserslist.defaults
|
||||
|
||||
/**
|
||||
* Inspect with default Autoprefixer
|
||||
*/
|
||||
plugin.info = () => plugin().info()
|
||||
+51
@@ -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
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
let browserslist = require('browserslist')
|
||||
let { agents } = require('caniuse-lite/dist/unpacker/agents')
|
||||
|
||||
let utils = require('./utils')
|
||||
|
||||
class Browsers {
|
||||
constructor(data, requirements, options, browserslistOpts) {
|
||||
this.data = data
|
||||
this.options = options || {}
|
||||
this.browserslistOpts = browserslistOpts || {}
|
||||
this.selected = this.parse(requirements)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Is browser is selected by requirements
|
||||
*/
|
||||
isSelected(browser) {
|
||||
return this.selected.includes(browser)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}-`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Browsers
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
let Browsers = require('./browsers')
|
||||
let Prefixer = require('./prefixer')
|
||||
let utils = require('./utils')
|
||||
|
||||
class Declaration extends Prefixer {
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Always true, because we already get prefixer by property name
|
||||
*/
|
||||
check(/* decl */) {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 unprefixed version of property
|
||||
*/
|
||||
normalize(prop) {
|
||||
return prop
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of prefixed properties to clean old prefixes
|
||||
*/
|
||||
old(prop, prefix) {
|
||||
return [this.prefixed(prop, prefix)]
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 value.replace(/var\([^)]+\)/, '').includes(other)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Return prefixed version of property
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
return prefix + prop
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
}
|
||||
|
||||
/**
|
||||
* Set prefix to declaration
|
||||
*/
|
||||
set(decl, prefix) {
|
||||
decl.prop = this.prefixed(decl.prop, prefix)
|
||||
return decl
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Declaration
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
class AlignContent extends Declaration {
|
||||
/**
|
||||
* Return property name by final spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'align-content'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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-around': 'distribute',
|
||||
'space-between': 'justify'
|
||||
}
|
||||
|
||||
module.exports = AlignContent
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
class AlignItems extends Declaration {
|
||||
/**
|
||||
* Return property name by final spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'align-items'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
class AlignSelf extends Declaration {
|
||||
check(decl) {
|
||||
return (
|
||||
decl.parent &&
|
||||
!decl.parent.some(i => {
|
||||
return i.prop && i.prop.startsWith('grid-')
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return property name by final spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'align-self'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class Animation extends Declaration {
|
||||
/**
|
||||
* Don’t 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
|
||||
+23
@@ -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
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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(() => '-webkit-'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return different selectors depend on prefix
|
||||
*/
|
||||
prefixed(prefix) {
|
||||
if (prefix === '-webkit-') {
|
||||
return ':-webkit-autofill'
|
||||
}
|
||||
return `:${prefix}autofill`
|
||||
}
|
||||
}
|
||||
|
||||
Autofill.names = [':autofill']
|
||||
|
||||
module.exports = Autofill
|
||||
+20
@@ -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
|
||||
+24
@@ -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
|
||||
+23
@@ -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
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class BlockLogical extends Declaration {
|
||||
/**
|
||||
* Return property name by spec
|
||||
*/
|
||||
normalize(prop) {
|
||||
if (prop.includes('-before')) {
|
||||
return prop.replace('-before', '-block-start')
|
||||
}
|
||||
return prop.replace('-after', '-block-end')
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
+15
@@ -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
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class BorderRadius extends Declaration {
|
||||
/**
|
||||
* Return unprefixed version of property
|
||||
*/
|
||||
normalize(prop) {
|
||||
return BorderRadius.toNormal[prop] || prop
|
||||
}
|
||||
|
||||
/**
|
||||
* Change syntax, when add Mozilla prefix
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
if (prefix === '-moz-') {
|
||||
return prefix + (BorderRadius.toMozilla[prop] || prop)
|
||||
}
|
||||
return super.prefixed(prop, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class BreakProps extends Declaration {
|
||||
/**
|
||||
* Don’t 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 name for -webkit- and -moz- prefix
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
return `${prefix}column-${prop}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
+35
@@ -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
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
let OldValue = require('../old-value')
|
||||
let Value = require('../value')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Change value for old specs
|
||||
*/
|
||||
old(prefix) {
|
||||
let prefixed = this.prefixed(prefix)
|
||||
if (!prefixed) return undefined
|
||||
return new OldValue(this.name, prefixed)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
DisplayFlex.names = ['display-flex', 'inline-flex']
|
||||
|
||||
module.exports = DisplayFlex
|
||||
+21
@@ -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
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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(() => '-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
|
||||
+14
@@ -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
|
||||
+19
@@ -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
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
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
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
class FlexDirection 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 already = decl.parent.some(
|
||||
i =>
|
||||
i.prop === prefix + 'box-orient' || i.prop === prefix + 'box-direction'
|
||||
)
|
||||
if (already) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
let v = decl.value
|
||||
let dir, orient
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return property name by final spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'flex-direction'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
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
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
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
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
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
|
||||
+19
@@ -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]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
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
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
let list = require('postcss').list
|
||||
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
class Flex extends Declaration {
|
||||
/**
|
||||
* Return property name by final spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'flex'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
+20
@@ -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
|
||||
+448
@@ -0,0 +1,448 @@
|
||||
let range = require('normalize-range')
|
||||
let parser = require('postcss-value-parser')
|
||||
|
||||
let OldValue = require('../old-value')
|
||||
let utils = require('../utils')
|
||||
let Value = require('../value')
|
||||
|
||||
let IS_DIRECTION = /top|left|right|bottom/gi
|
||||
|
||||
class Gradient extends Value {
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Get div token from exists parameters
|
||||
*/
|
||||
cloneDiv(params) {
|
||||
for (let i of params) {
|
||||
if (i.type === 'div' && i.value === ',') {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return { after: ' ', type: 'div', value: ',' }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize angle
|
||||
*/
|
||||
normalize(nodes, gradientName) {
|
||||
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 (
|
||||
gradientName === 'linear-gradient' ||
|
||||
gradientName === 'repeating-linear-gradient'
|
||||
) {
|
||||
let direction = nodes[0].value
|
||||
|
||||
// Unitless zero for `<angle>` values are allowed in CSS gradients and transforms.
|
||||
// Spec: https://github.com/w3c/csswg-drafts/commit/602789171429b2231223ab1e5acf8f7f11652eb3
|
||||
if (direction === '0deg' || direction === '0') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'top')
|
||||
} else if (direction === '90deg') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'right')
|
||||
} else if (direction === '180deg') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'bottom') // default value
|
||||
} else if (direction === '270deg') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'left')
|
||||
}
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert angle unit to deg
|
||||
*/
|
||||
normalizeUnit(str, full) {
|
||||
let num = parseFloat(str)
|
||||
let deg = (num / full) * 360
|
||||
return `${deg}deg`
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old WebKit gradient too
|
||||
*/
|
||||
old(prefix) {
|
||||
if (prefix === '-webkit-') {
|
||||
let type
|
||||
if (this.name === 'linear-gradient') {
|
||||
type = 'linear'
|
||||
} else if (this.name === 'repeating-linear-gradient') {
|
||||
type = 'repeating-linear'
|
||||
} else if (this.name === 'repeating-radial-gradient') {
|
||||
type = 'repeating-radial'
|
||||
} else {
|
||||
type = '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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 degrees for webkit prefix
|
||||
*/
|
||||
replace(string, prefix) {
|
||||
let ast = parser(string)
|
||||
for (let node of ast.nodes) {
|
||||
let gradientName = this.name // gradient name
|
||||
if (node.type === 'function' && node.value === gradientName) {
|
||||
node.nodes = this.newDirection(node.nodes)
|
||||
node.nodes = this.normalize(node.nodes, gradientName)
|
||||
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))
|
||||
}
|
||||
|
||||
revertDirection(word) {
|
||||
return Gradient.directions[word.toLowerCase()] || word
|
||||
}
|
||||
|
||||
/**
|
||||
* Round float and save digits under dot
|
||||
*/
|
||||
roundFloat(float, digits) {
|
||||
return parseFloat(float.toFixed(digits))
|
||||
}
|
||||
}
|
||||
|
||||
Gradient.names = [
|
||||
'linear-gradient',
|
||||
'repeating-linear-gradient',
|
||||
'radial-gradient',
|
||||
'repeating-radial-gradient'
|
||||
]
|
||||
|
||||
Gradient.directions = {
|
||||
bottom: 'top',
|
||||
left: 'right',
|
||||
right: 'left',
|
||||
top: 'bottom' // default value
|
||||
}
|
||||
|
||||
// Direction to replace
|
||||
Gradient.oldDirections = {
|
||||
'bottom': 'left top, left bottom',
|
||||
'bottom left': 'right top, left bottom',
|
||||
'bottom right': 'left top, right bottom',
|
||||
'left': 'right top, left top',
|
||||
|
||||
'left bottom': 'right top, left bottom',
|
||||
'left top': 'right bottom, left top',
|
||||
'right': 'left top, right top',
|
||||
'right bottom': 'left top, right bottom',
|
||||
'right top': 'left bottom, right top',
|
||||
'top': 'left bottom, left top',
|
||||
'top left': 'right bottom, left top',
|
||||
'top right': 'left bottom, right top'
|
||||
}
|
||||
|
||||
module.exports = Gradient
|
||||
+34
@@ -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
|
||||
+28
@@ -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 IE property back
|
||||
*/
|
||||
normalize() {
|
||||
return 'justify-self'
|
||||
}
|
||||
|
||||
/**
|
||||
* Change property name for IE
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
return prefix + 'grid-column-align'
|
||||
}
|
||||
}
|
||||
|
||||
GridColumnAlign.names = ['grid-column-align']
|
||||
|
||||
module.exports = GridColumnAlign
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
let Declaration = require('../declaration')
|
||||
let { isPureNumber } = require('../utils')
|
||||
|
||||
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) {
|
||||
if (isPureNumber(startDecl.value)) {
|
||||
let value = Number(decl.value) - Number(startDecl.value) + ''
|
||||
clonedDecl.value = value
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
} 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
|
||||
+28
@@ -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 IE property back
|
||||
*/
|
||||
normalize() {
|
||||
return 'align-self'
|
||||
}
|
||||
|
||||
/**
|
||||
* Change property name for IE
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
return prefix + 'grid-row-align'
|
||||
}
|
||||
}
|
||||
|
||||
GridRowAlign.names = ['grid-row-align']
|
||||
|
||||
module.exports = GridRowAlign
|
||||
+33
@@ -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
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
let Declaration = require('../declaration')
|
||||
let Processor = require('../processor')
|
||||
let {
|
||||
autoplaceGridItems,
|
||||
getGridGap,
|
||||
inheritGridGap,
|
||||
prefixTrackProp,
|
||||
prefixTrackValue
|
||||
} = require('./grid-utils')
|
||||
|
||||
class GridRowsColumns extends Declaration {
|
||||
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({
|
||||
gap: gapValue,
|
||||
value
|
||||
})
|
||||
|
||||
/**
|
||||
* Insert prefixes
|
||||
*/
|
||||
decl.cloneBefore({
|
||||
prop: prefixTrackProp({ prefix, prop }),
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Change IE property back
|
||||
*/
|
||||
normalize(prop) {
|
||||
return prop.replace(/^grid-(rows|columns)/, 'grid-template-$1')
|
||||
}
|
||||
|
||||
/**
|
||||
* Change property name for IE
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
if (prefix === '-ms-') {
|
||||
return prefixTrackProp({ prefix, prop })
|
||||
}
|
||||
return super.prefixed(prop, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
GridRowsColumns.names = [
|
||||
'grid-template-rows',
|
||||
'grid-template-columns',
|
||||
'grid-rows',
|
||||
'grid-columns'
|
||||
]
|
||||
|
||||
module.exports = GridRowsColumns
|
||||
+33
@@ -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
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
let Declaration = require('../declaration')
|
||||
let {
|
||||
getGridGap,
|
||||
inheritGridGap,
|
||||
parseGridAreas,
|
||||
prefixTrackProp,
|
||||
prefixTrackValue,
|
||||
warnGridGap,
|
||||
warnMissedAreas
|
||||
} = 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({ prefix, prop }),
|
||||
value: prefixTrackValue({ gap: gap.row, value })
|
||||
})
|
||||
} else {
|
||||
hasColumns = true
|
||||
}
|
||||
})
|
||||
|
||||
let gridRows = getGridRows(decl.value)
|
||||
|
||||
if (hasColumns && !hasRows && gap.row && gridRows.length > 1) {
|
||||
decl.cloneBefore({
|
||||
prop: '-ms-grid-rows',
|
||||
raws: {},
|
||||
value: prefixTrackValue({
|
||||
gap: gap.row,
|
||||
value: `repeat(${gridRows.length}, auto)`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// warnings
|
||||
warnGridGap({
|
||||
decl,
|
||||
gap,
|
||||
hasColumns,
|
||||
result
|
||||
})
|
||||
|
||||
let areas = parseGridAreas({
|
||||
gap,
|
||||
rows: gridRows
|
||||
})
|
||||
|
||||
warnMissedAreas(areas, decl, result)
|
||||
|
||||
return decl
|
||||
}
|
||||
}
|
||||
|
||||
GridTemplateAreas.names = ['grid-template-areas']
|
||||
|
||||
module.exports = GridTemplateAreas
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
let Declaration = require('../declaration')
|
||||
let {
|
||||
getGridGap,
|
||||
inheritGridGap,
|
||||
parseTemplate,
|
||||
warnGridGap,
|
||||
warnMissedAreas
|
||||
} = 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 { areas, columns, rows } = parseTemplate({
|
||||
decl,
|
||||
gap: inheritedGap || gap
|
||||
})
|
||||
|
||||
let hasAreas = Object.keys(areas).length > 0
|
||||
let hasRows = Boolean(rows)
|
||||
let hasColumns = Boolean(columns)
|
||||
|
||||
warnGridGap({
|
||||
decl,
|
||||
gap,
|
||||
hasColumns,
|
||||
result
|
||||
})
|
||||
|
||||
warnMissedAreas(areas, decl, result)
|
||||
|
||||
if ((hasRows && hasColumns) || hasAreas) {
|
||||
decl.cloneBefore({
|
||||
prop: '-ms-grid-rows',
|
||||
raws: {},
|
||||
value: rows
|
||||
})
|
||||
}
|
||||
|
||||
if (hasColumns) {
|
||||
decl.cloneBefore({
|
||||
prop: '-ms-grid-columns',
|
||||
raws: {},
|
||||
value: columns
|
||||
})
|
||||
}
|
||||
|
||||
return decl
|
||||
}
|
||||
}
|
||||
|
||||
GridTemplate.names = ['grid-template']
|
||||
|
||||
module.exports = GridTemplate
|
||||
+1113
File diff suppressed because it is too large
Load Diff
+48
@@ -0,0 +1,48 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class ImageRendering extends Declaration {
|
||||
/**
|
||||
* Add hack only for crisp-edges
|
||||
*/
|
||||
check(decl) {
|
||||
return decl.value === 'pixelated'
|
||||
}
|
||||
|
||||
/**
|
||||
* Return property name by spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'image-rendering'
|
||||
}
|
||||
|
||||
/**
|
||||
* Change property name for IE
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
if (prefix === '-ms-') {
|
||||
return '-ms-interpolation-mode'
|
||||
}
|
||||
return super.prefixed(prop, prefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn on old value
|
||||
*/
|
||||
process(node, result) {
|
||||
return super.process(node, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
ImageRendering.names = ['image-rendering', 'interpolation-mode']
|
||||
|
||||
module.exports = ImageRendering
|
||||
+18
@@ -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
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class InlineLogical extends Declaration {
|
||||
/**
|
||||
* Return property name by spec
|
||||
*/
|
||||
normalize(prop) {
|
||||
return prop.replace(/(margin|padding|border)-(start|end)/, '$1-inline-$2')
|
||||
}
|
||||
|
||||
/**
|
||||
* Use old syntax for -moz- and -webkit-
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
return prefix + prop.replace('-inline', '')
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
+61
@@ -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 {
|
||||
add(decl, prefix) {
|
||||
if (decl.prop.includes('grid') && prefix !== '-webkit-') {
|
||||
return undefined
|
||||
}
|
||||
return super.add(decl, prefix)
|
||||
}
|
||||
|
||||
isStretch() {
|
||||
return (
|
||||
this.name === 'stretch' ||
|
||||
this.name === 'fill' ||
|
||||
this.name === 'fill-available'
|
||||
)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
regexp() {
|
||||
if (!this.regexpCache) this.regexpCache = regexp(this.name)
|
||||
return this.regexpCache
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Intrinsic.names = [
|
||||
'max-content',
|
||||
'min-content',
|
||||
'fit-content',
|
||||
'fill',
|
||||
'fill-available',
|
||||
'stretch'
|
||||
]
|
||||
|
||||
module.exports = Intrinsic
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
class JustifyContent extends Declaration {
|
||||
/**
|
||||
* Return property name by final spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'justify-content'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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-around': 'distribute',
|
||||
'space-between': 'justify'
|
||||
}
|
||||
|
||||
module.exports = JustifyContent
|
||||
+38
@@ -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
|
||||
+88
@@ -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',
|
||||
exclude: 'xor',
|
||||
intersect: 'source-in',
|
||||
subtract: 'source-out'
|
||||
}
|
||||
|
||||
MaskComposite.regexp = new RegExp(
|
||||
`\\s+(${Object.keys(MaskComposite.oldValues).join(
|
||||
'|'
|
||||
)})\\b(?!\\))\\s*(?=[,])`,
|
||||
'ig'
|
||||
)
|
||||
|
||||
module.exports = MaskComposite
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
let Declaration = require('../declaration')
|
||||
let flexSpec = require('./flex-spec')
|
||||
|
||||
class Order extends Declaration {
|
||||
/**
|
||||
* Return property name by final spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'order'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class OverscrollBehavior extends Declaration {
|
||||
/**
|
||||
* Return property name by spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'overscroll-behavior'
|
||||
}
|
||||
|
||||
/**
|
||||
* Change property name for IE
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
return prefix + 'scroll-chaining'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
let OldValue = require('../old-value')
|
||||
let Value = require('../value')
|
||||
|
||||
class Pixelated extends Value {
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
Pixelated.names = ['pixelated']
|
||||
|
||||
module.exports = Pixelated
|
||||
+32
@@ -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
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
let Selector = require('../selector')
|
||||
|
||||
class PlaceholderShown extends Selector {
|
||||
/**
|
||||
* Return different selectors depend on prefix
|
||||
*/
|
||||
prefixed(prefix) {
|
||||
if (prefix === '-moz-') {
|
||||
return ':-moz-placeholder'
|
||||
} else if (prefix === '-ms-') {
|
||||
return ':-ms-input-placeholder'
|
||||
}
|
||||
return `:${prefix}placeholder-shown`
|
||||
}
|
||||
}
|
||||
|
||||
PlaceholderShown.names = [':placeholder-shown']
|
||||
|
||||
module.exports = PlaceholderShown
|
||||
+33
@@ -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
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class PrintColorAdjust extends Declaration {
|
||||
/**
|
||||
* Return property name by spec
|
||||
*/
|
||||
normalize() {
|
||||
return 'print-color-adjust'
|
||||
}
|
||||
|
||||
/**
|
||||
* Change property name for WebKit-based browsers
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
if (prefix === '-moz-') {
|
||||
return 'color-adjust'
|
||||
} else {
|
||||
return prefix + 'print-color-adjust'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintColorAdjust.names = ['print-color-adjust', 'color-adjust']
|
||||
|
||||
module.exports = PrintColorAdjust
|
||||
+23
@@ -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
|
||||
+25
@@ -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
|
||||
+14
@@ -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
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class TransformDecl extends Declaration {
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
TransformDecl.names = ['transform', 'transform-origin']
|
||||
|
||||
TransformDecl.functions3d = [
|
||||
'matrix3d',
|
||||
'translate3d',
|
||||
'translateZ',
|
||||
'scale3d',
|
||||
'scaleZ',
|
||||
'rotate3d',
|
||||
'rotateX',
|
||||
'rotateY',
|
||||
'perspective'
|
||||
]
|
||||
|
||||
module.exports = TransformDecl
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
let Declaration = require('../declaration')
|
||||
|
||||
class UserSelect extends Declaration {
|
||||
/**
|
||||
* Avoid prefixing all in IE
|
||||
*/
|
||||
insert(decl, prefix, prefixes) {
|
||||
if (decl.value === 'all' && prefix === '-ms-') {
|
||||
return undefined
|
||||
} else if (
|
||||
decl.value === 'contain' &&
|
||||
(prefix === '-moz-' || prefix === '-webkit-')
|
||||
) {
|
||||
return undefined
|
||||
} else {
|
||||
return super.insert(decl, prefix, prefixes)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change prefixed value for IE
|
||||
*/
|
||||
set(decl, prefix) {
|
||||
if (prefix === '-ms-' && decl.value === 'contain') {
|
||||
decl.value = 'element'
|
||||
}
|
||||
return super.set(decl, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
UserSelect.names = ['user-select']
|
||||
|
||||
module.exports = UserSelect
|
||||
+42
@@ -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-lr': 'tb-lr',
|
||||
'vertical-rl': 'tb-rl'
|
||||
},
|
||||
rtl: {
|
||||
'horizontal-tb': 'rl-tb',
|
||||
'vertical-lr': 'bt-lr',
|
||||
'vertical-rl': 'bt-rl'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WritingMode
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
let browserslist = require('browserslist')
|
||||
|
||||
function capitalize(str) {
|
||||
return str.slice(0, 1).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
const NAMES = {
|
||||
and_chr: 'Chrome for Android',
|
||||
and_ff: 'Firefox for Android',
|
||||
and_qq: 'QQ Browser',
|
||||
and_uc: 'UC for Android',
|
||||
baidu: 'Baidu Browser',
|
||||
ie: 'IE',
|
||||
ie_mob: 'IE Mobile',
|
||||
ios_saf: 'iOS Safari',
|
||||
kaios: 'KaiOS Browser',
|
||||
op_mini: 'Opera Mini',
|
||||
op_mob: 'Opera Mobile',
|
||||
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
|
||||
}
|
||||
+67
@@ -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()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OldSelector
|
||||
+22
@@ -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
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
let Browsers = require('./browsers')
|
||||
let utils = require('./utils')
|
||||
let vendor = require('./vendor')
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
constructor(name, prefixes, all) {
|
||||
this.prefixes = prefixes
|
||||
this.name = name
|
||||
this.all = 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for Prefixer.clone
|
||||
*/
|
||||
clone(node, overrides) {
|
||||
return Prefixer.clone(node, overrides)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Prefixer
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
let AtRule = require('./at-rule')
|
||||
let Browsers = require('./browsers')
|
||||
let Declaration = require('./declaration')
|
||||
let hackAlignContent = require('./hacks/align-content')
|
||||
let hackAlignItems = require('./hacks/align-items')
|
||||
let hackAlignSelf = require('./hacks/align-self')
|
||||
let hackAnimation = require('./hacks/animation')
|
||||
let hackAppearance = require('./hacks/appearance')
|
||||
let hackAutofill = require('./hacks/autofill')
|
||||
let hackBackdropFilter = require('./hacks/backdrop-filter')
|
||||
let hackBackgroundClip = require('./hacks/background-clip')
|
||||
let hackBackgroundSize = require('./hacks/background-size')
|
||||
let hackBlockLogical = require('./hacks/block-logical')
|
||||
let hackBorderImage = require('./hacks/border-image')
|
||||
let hackBorderRadius = require('./hacks/border-radius')
|
||||
let hackBreakProps = require('./hacks/break-props')
|
||||
let hackCrossFade = require('./hacks/cross-fade')
|
||||
let hackDisplayFlex = require('./hacks/display-flex')
|
||||
let hackDisplayGrid = require('./hacks/display-grid')
|
||||
let hackFileSelectorButton = require('./hacks/file-selector-button')
|
||||
let hackFilter = require('./hacks/filter')
|
||||
let hackFilterValue = require('./hacks/filter-value')
|
||||
let hackFlex = require('./hacks/flex')
|
||||
let hackFlexBasis = require('./hacks/flex-basis')
|
||||
let hackFlexDirection = require('./hacks/flex-direction')
|
||||
let hackFlexFlow = require('./hacks/flex-flow')
|
||||
let hackFlexGrow = require('./hacks/flex-grow')
|
||||
let hackFlexShrink = require('./hacks/flex-shrink')
|
||||
let hackFlexWrap = require('./hacks/flex-wrap')
|
||||
let hackFullscreen = require('./hacks/fullscreen')
|
||||
let hackGradient = require('./hacks/gradient')
|
||||
let hackGridArea = require('./hacks/grid-area')
|
||||
let hackGridColumnAlign = require('./hacks/grid-column-align')
|
||||
let hackGridEnd = require('./hacks/grid-end')
|
||||
let hackGridRowAlign = require('./hacks/grid-row-align')
|
||||
let hackGridRowColumn = require('./hacks/grid-row-column')
|
||||
let hackGridRowsColumns = require('./hacks/grid-rows-columns')
|
||||
let hackGridStart = require('./hacks/grid-start')
|
||||
let hackGridTemplate = require('./hacks/grid-template')
|
||||
let hackGridTemplateAreas = require('./hacks/grid-template-areas')
|
||||
let hackImageRendering = require('./hacks/image-rendering')
|
||||
let hackImageSet = require('./hacks/image-set')
|
||||
let hackInlineLogical = require('./hacks/inline-logical')
|
||||
let hackIntrinsic = require('./hacks/intrinsic')
|
||||
let hackJustifyContent = require('./hacks/justify-content')
|
||||
let hackMaskBorder = require('./hacks/mask-border')
|
||||
let hackMaskComposite = require('./hacks/mask-composite')
|
||||
let hackOrder = require('./hacks/order')
|
||||
let hackOverscrollBehavior = require('./hacks/overscroll-behavior')
|
||||
let hackPixelated = require('./hacks/pixelated')
|
||||
let hackPlaceSelf = require('./hacks/place-self')
|
||||
let hackPlaceholder = require('./hacks/placeholder')
|
||||
let hackPlaceholderShown = require('./hacks/placeholder-shown')
|
||||
let hackPrintColorAdjust = require('./hacks/print-color-adjust')
|
||||
let hackTextDecoration = require('./hacks/text-decoration')
|
||||
let hackTextDecorationSkipInk = require('./hacks/text-decoration-skip-ink')
|
||||
let hackTextEmphasisPosition = require('./hacks/text-emphasis-position')
|
||||
let hackTransformDecl = require('./hacks/transform-decl')
|
||||
let hackUserSelect = require('./hacks/user-select')
|
||||
let hackWritingMode = require('./hacks/writing-mode')
|
||||
let Processor = require('./processor')
|
||||
let Resolution = require('./resolution')
|
||||
let Selector = require('./selector')
|
||||
let Supports = require('./supports')
|
||||
let Transition = require('./transition')
|
||||
let utils = require('./utils')
|
||||
let Value = require('./value')
|
||||
let vendor = require('./vendor')
|
||||
|
||||
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(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(hackPrintColorAdjust)
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Declaration loader with caching
|
||||
*/
|
||||
decl(prop) {
|
||||
if (!declsCache.has(prop)) {
|
||||
declsCache.set(prop, Declaration.load(prop))
|
||||
}
|
||||
|
||||
return declsCache.get(prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
down(callback) {
|
||||
return checker(+1, callback)
|
||||
},
|
||||
up(callback) {
|
||||
return checker(-1, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache prefixes data to fast CSS processing
|
||||
*/
|
||||
preprocess(selected) {
|
||||
let add = {
|
||||
'@supports': new Supports(Prefixes, this),
|
||||
'selectors': []
|
||||
}
|
||||
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]
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Return unprefixed version of property
|
||||
*/
|
||||
unprefixed(prop) {
|
||||
let value = this.normalize(vendor.unprefixed(prop))
|
||||
if (value === 'flex-direction') {
|
||||
value = 'flex-flow'
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 || []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Prefixes
|
||||
+709
@@ -0,0 +1,709 @@
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
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 === 'color-adjust') {
|
||||
if (parent.every(i => i.prop !== 'print-color-adjust')) {
|
||||
result.warn(
|
||||
'Replace color-adjust to print-color-adjust. ' +
|
||||
'The color-adjust shorthand is currently deprecated.',
|
||||
{ node: decl }
|
||||
)
|
||||
}
|
||||
} else 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 (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)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for grid/flexbox options.
|
||||
*/
|
||||
disabledDecl(node, result) {
|
||||
if (node.type === 'decl' && this.gridStatus(node, result) === false) {
|
||||
if (node.prop.includes('grid') || node.prop === 'justify-items') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (node.type === 'decl' && this.prefixes.options.flexbox === false) {
|
||||
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 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
css.walkRules((rule, i) => {
|
||||
if (this.disabled(rule, result)) return
|
||||
|
||||
for (let checker of this.prefixes.remove.selectors) {
|
||||
if (checker.check(rule)) {
|
||||
rule.parent.removeChild(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
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') ||
|
||||
// Do not remove -webkit-box-orient when -webkit-line-clamp is present.
|
||||
// https://github.com/postcss/autoprefixer/issues/1510
|
||||
(decl.prop === '-webkit-box-orient' &&
|
||||
decl.parent.some(d => d.prop === '-webkit-line-clamp'))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Processor
|
||||
+97
@@ -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 {
|
||||
/**
|
||||
* 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)))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
let { list } = require('postcss')
|
||||
|
||||
let Browsers = require('./browsers')
|
||||
let OldSelector = require('./old-selector')
|
||||
let Prefixer = require('./prefixer')
|
||||
let utils = require('./utils')
|
||||
|
||||
class Selector extends Prefixer {
|
||||
constructor(name, prefixes, all) {
|
||||
super(name, prefixes, all)
|
||||
this.regexpCache = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Is rule selectors need to be prefixed
|
||||
*/
|
||||
check(rule) {
|
||||
if (rule.selector.includes(this.name)) {
|
||||
return !!rule.selector.match(this.regexp())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Return function to fast find prefixed selector
|
||||
*/
|
||||
old(prefix) {
|
||||
return new OldSelector(this, prefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* All possible prefixes
|
||||
*/
|
||||
possible() {
|
||||
return Browsers.prefixes()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return prefixed version of selector
|
||||
*/
|
||||
prefixed(prefix) {
|
||||
return this.name.replace(/^(\W*)/, `$1${prefix}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace selectors by prefixed one
|
||||
*/
|
||||
replace(selector, prefix) {
|
||||
return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Selector
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
let featureQueries = require('caniuse-lite/data/features/css-featurequeries.js')
|
||||
let feature = require('caniuse-lite/dist/unpacker/feature')
|
||||
let { parse } = require('postcss')
|
||||
|
||||
let brackets = require('./brackets')
|
||||
let Browsers = require('./browsers')
|
||||
let utils = require('./utils')
|
||||
let Value = require('./value')
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()]
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Create virtual rule to process it by prefixer
|
||||
*/
|
||||
virtual(str) {
|
||||
let [prop, value] = this.parse(str)
|
||||
let rule = parse('a{}').first
|
||||
rule.append({ prop, raws: { before: '' }, value })
|
||||
return rule
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Supports
|
||||
+329
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does we already have this declaration
|
||||
*/
|
||||
already(decl, prop, value) {
|
||||
return decl.parent.some(i => i.prop === prop && i.value === 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-*'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
cleanOtherPrefixes(params, prefix) {
|
||||
return params.filter(param => {
|
||||
let current = vendor.prefix(this.findProp(param))
|
||||
return current === '' || current === prefix
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Add declaration if it is not exist
|
||||
*/
|
||||
cloneBefore(decl, prop, value) {
|
||||
if (!this.already(decl, prop, value)) {
|
||||
decl.cloneBefore({ prop, value })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 { after: ' ', type: 'div', 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transition
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
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 doesn’t 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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a given value only contains numbers.
|
||||
* @param {*} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
module.exports.isPureNumber = function (value) {
|
||||
if (typeof value === 'number') {
|
||||
return true
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return /^[0-9]+$/.test(value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
let OldValue = require('./old-value')
|
||||
let Prefixer = require('./prefixer')
|
||||
let utils = require('./utils')
|
||||
let vendor = require('./vendor')
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Is declaration need to be prefixed
|
||||
*/
|
||||
check(decl) {
|
||||
let value = decl.value
|
||||
if (!value.includes(this.name)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return !!value.match(this.regexp())
|
||||
}
|
||||
|
||||
/**
|
||||
* Return function to fast find prefixed value
|
||||
*/
|
||||
old(prefix) {
|
||||
return new OldValue(this.name, prefix + this.name)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Value
|
||||
+14
@@ -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+-/, '')
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "autoprefixer",
|
||||
"version": "10.4.22",
|
||||
"description": "Parse CSS and add vendor prefixes to CSS rules using values from the Can I Use website",
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"keywords": [
|
||||
"autoprefixer",
|
||||
"css",
|
||||
"prefix",
|
||||
"postcss",
|
||||
"postcss-plugin"
|
||||
],
|
||||
"main": "lib/autoprefixer.js",
|
||||
"bin": "bin/autoprefixer",
|
||||
"types": "lib/autoprefixer.d.ts",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"author": "Andrey Sitnik <andrey@sitnik.ru>",
|
||||
"license": "MIT",
|
||||
"repository": "postcss/autoprefixer",
|
||||
"bugs": {
|
||||
"url": "https://github.com/postcss/autoprefixer/issues"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"browserslist": "^4.27.0",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
"fraction.js": "^5.3.4",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# CHANGELOG
|
||||
|
||||
v5.2.2
|
||||
- Improved documentation and removed unecessary check
|
||||
|
||||
v5.2.1:
|
||||
- 2bb7b05: Added negative sign check
|
||||
|
||||
v5.2:
|
||||
- 6f9d124: Implemented log and improved simplify
|
||||
- b773e7a: Added named export to TS definition
|
||||
- 70304f9: Fixed merge conflict
|
||||
- 3b940d3: Implemented other comparing functions
|
||||
- 10acdfc: Update README.md
|
||||
- ba41d00: Update README.md
|
||||
- 73ded97: Update README.md
|
||||
- acabc39: Fixed param parsing
|
||||
|
||||
v5.0.5:
|
||||
- 2c9d4c2: Improved roundTo() and param parser
|
||||
|
||||
v5.0.4:
|
||||
- 39e61e7: Fixed bignum param passing
|
||||
|
||||
v5.0.3:
|
||||
- 7d9a3ec: Upgraded bundler for code quality
|
||||
|
||||
v5.0.2:
|
||||
- c64b1d6: fixed esm export
|
||||
|
||||
v5.0.1:
|
||||
- e440f9c: Fixed CJS export
|
||||
- 9bbdd29: Fixed CJS export
|
||||
|
||||
v5.0.0:
|
||||
- ac7cd06: Fixed readme
|
||||
- 33cc9e5: Added crude build
|
||||
- 1adcc76: Release breaking v5.0. Fraction.js now builds on BigInt. The API stays the same as v4, except that the object attributes `n`, `d`, and `s`, are not Number but BigInt and may break code that directly accesses these attributes.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Robert Eisele
|
||||
|
||||
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.
|
||||
+520
@@ -0,0 +1,520 @@
|
||||
# Fraction.js - ℚ in JavaScript
|
||||
|
||||
[](https://npmjs.org/package/fraction.js "View this project on npm")
|
||||
[](http://opensource.org/licenses/MIT)
|
||||
|
||||
Do you find the limitations of floating-point arithmetic frustrating, especially when rational and irrational numbers like π or √2 are stored within the same finite precision? This can lead to avoidable inaccuracies such as:
|
||||
|
||||
```javascript
|
||||
1 / 98 * 98 // Results in 0.9999999999999999
|
||||
```
|
||||
|
||||
For applications requiring higher precision or where working with fractions is preferable, consider incorporating *Fraction.js* into your project.
|
||||
|
||||
The library effectively addresses precision issues, as demonstrated below:
|
||||
|
||||
```javascript
|
||||
Fraction(1).div(98).mul(98) // Returns 1
|
||||
```
|
||||
|
||||
*Fraction.js* uses a `BigInt` representation for both the numerator and denominator, ensuring minimal performance overhead while maximizing accuracy. Its design is optimized for precision, making it an ideal choice as a foundational library for other math tools, such as [Polynomial.js](https://github.com/rawify/Polynomial.js) and [Math.js](https://github.com/josdejong/mathjs).
|
||||
|
||||
## Convert Decimal to Fraction
|
||||
|
||||
One of the core features of *Fraction.js* is its ability to seamlessly convert decimal numbers into fractions.
|
||||
|
||||
```javascript
|
||||
let x = new Fraction(1.88);
|
||||
let res = x.toFraction(true); // Returns "1 22/25" as a string
|
||||
```
|
||||
|
||||
This is particularly useful when you need precise fraction representations instead of dealing with the limitations of floating-point arithmetic. What if you allow some error tolerance?
|
||||
|
||||
```javascript
|
||||
let x = new Fraction(0.33333);
|
||||
let res = x.simplify(0.001) // Error < 0.001
|
||||
.toFraction(); // Returns "1/3" as a string
|
||||
```
|
||||
|
||||
## Precision
|
||||
|
||||
As native `BigInt` support in JavaScript becomes more common, libraries like *Fraction.js* use it to handle calculations with higher precision. This improves the speed and accuracy of math operations with large numbers, providing a better solution for tasks that need more precision than floating-point numbers can offer.
|
||||
|
||||
## Examples / Motivation
|
||||
|
||||
A simple example of using *Fraction.js* might look like this:
|
||||
|
||||
```javascript
|
||||
var f = new Fraction("9.4'31'"); // 9.4313131313131...
|
||||
f.mul([-4, 3]).mod("4.'8'"); // 4.88888888888888...
|
||||
```
|
||||
|
||||
The result can then be displayed as:
|
||||
|
||||
```javascript
|
||||
console.log(f.toFraction()); // -4154 / 1485
|
||||
```
|
||||
|
||||
Additionally, you can access the internal attributes of the fraction, such as the sign (s), numerator (n), and denominator (d). Keep in mind that these values are stored as `BigInt`:
|
||||
|
||||
```javascript
|
||||
Number(f.s) * Number(f.n) / Number(f.d) = -1 * 4154 / 1485 = -2.797306...
|
||||
```
|
||||
|
||||
If you attempted to calculate this manually using floating-point arithmetic, you'd get something like:
|
||||
|
||||
```javascript
|
||||
(9.4313131 * (-4 / 3)) % 4.888888 = -2.797308133...
|
||||
```
|
||||
|
||||
While the result is reasonably close, it’s not as accurate as the fraction-based approach that *Fraction.js* provides, especially when dealing with repeating decimals or complex operations. This highlights the value of precision that the library brings.
|
||||
|
||||
### Laplace Probability
|
||||
|
||||
Here's a straightforward example of using *Fraction.js* to calculate probabilities. Let's determine the probability of rolling a specific outcome on a fair die:
|
||||
|
||||
- **P({3})**: The probability of rolling a 3.
|
||||
- **P({1, 4})**: The probability of rolling either 1 or 4.
|
||||
- **P({2, 4, 6})**: The probability of rolling 2, 4, or 6.
|
||||
|
||||
#### P({3}):
|
||||
|
||||
```javascript
|
||||
var p = new Fraction([3].length, 6).toString(); // "0.1(6)"
|
||||
```
|
||||
|
||||
#### P({1, 4}):
|
||||
|
||||
```javascript
|
||||
var p = new Fraction([1, 4].length, 6).toString(); // "0.(3)"
|
||||
```
|
||||
|
||||
#### P({2, 4, 6}):
|
||||
|
||||
```javascript
|
||||
var p = new Fraction([2, 4, 6].length, 6).toString(); // "0.5"
|
||||
```
|
||||
|
||||
### Convert degrees/minutes/seconds to precise rational representation:
|
||||
|
||||
57+45/60+17/3600
|
||||
|
||||
```javascript
|
||||
var deg = 57; // 57°
|
||||
var min = 45; // 45 Minutes
|
||||
var sec = 17; // 17 Seconds
|
||||
|
||||
new Fraction(deg).add(min, 60).add(sec, 3600).toString() // -> 57.7547(2)
|
||||
```
|
||||
|
||||
|
||||
### Rational approximation of irrational numbers
|
||||
|
||||
To approximate a number like *sqrt(5) - 2* with a numerator and denominator, you can reformat the equation as follows: *pow(n / d + 2, 2) = 5*.
|
||||
|
||||
Then the following algorithm will generate the rational number besides the binary representation.
|
||||
|
||||
```javascript
|
||||
var x = "/", s = "";
|
||||
|
||||
var a = new Fraction(0),
|
||||
b = new Fraction(1);
|
||||
for (var n = 0; n <= 10; n++) {
|
||||
|
||||
var c = a.add(b).div(2);
|
||||
|
||||
console.log(n + "\t" + a + "\t" + b + "\t" + c + "\t" + x);
|
||||
|
||||
if (c.add(2).pow(2).valueOf() < 5) {
|
||||
a = c;
|
||||
x = "1";
|
||||
} else {
|
||||
b = c;
|
||||
x = "0";
|
||||
}
|
||||
s+= x;
|
||||
}
|
||||
console.log(s)
|
||||
```
|
||||
|
||||
The result is
|
||||
|
||||
```
|
||||
n a[n] b[n] c[n] x[n]
|
||||
0 0/1 1/1 1/2 /
|
||||
1 0/1 1/2 1/4 0
|
||||
2 0/1 1/4 1/8 0
|
||||
3 1/8 1/4 3/16 1
|
||||
4 3/16 1/4 7/32 1
|
||||
5 7/32 1/4 15/64 1
|
||||
6 15/64 1/4 31/128 1
|
||||
7 15/64 31/128 61/256 0
|
||||
8 15/64 61/256 121/512 0
|
||||
9 15/64 121/512 241/1024 0
|
||||
10 241/1024 121/512 483/2048 1
|
||||
```
|
||||
|
||||
Thus the approximation after 11 iterations of the bisection method is *483 / 2048* and the binary representation is 0.00111100011 (see [WolframAlpha](http://www.wolframalpha.com/input/?i=sqrt%285%29-2+binary))
|
||||
|
||||
I published another example on how to approximate PI with fraction.js on my [blog](https://raw.org/article/rational-numbers-in-javascript/) (Still not the best idea to approximate irrational numbers, but it illustrates the capabilities of Fraction.js perfectly).
|
||||
|
||||
|
||||
### Get the exact fractional part of a number
|
||||
|
||||
```javascript
|
||||
var f = new Fraction("-6.(3416)");
|
||||
console.log(f.mod(1).abs().toFraction()); // = 3416/9999
|
||||
```
|
||||
|
||||
### Mathematical correct modulo
|
||||
|
||||
The behaviour on negative congruences is different to most modulo implementations in computer science. Even the *mod()* function of Fraction.js behaves in the typical way. To solve the problem of having the mathematical correct modulo with Fraction.js you could come up with this:
|
||||
|
||||
```javascript
|
||||
var a = -1;
|
||||
var b = 10.99;
|
||||
|
||||
console.log(new Fraction(a)
|
||||
.mod(b)); // Not correct, usual Modulo
|
||||
|
||||
console.log(new Fraction(a)
|
||||
.mod(b).add(b).mod(b)); // Correct! Mathematical Modulo
|
||||
```
|
||||
|
||||
fmod() imprecision circumvented
|
||||
---
|
||||
It turns out that Fraction.js outperforms almost any fmod() implementation, including JavaScript itself, [php.js](http://phpjs.org/functions/fmod/), C++, Python, Java and even Wolframalpha due to the fact that numbers like 0.05, 0.1, ... are infinite decimal in base 2.
|
||||
|
||||
The equation *fmod(4.55, 0.05)* gives *0.04999999999999957*, wolframalpha says *1/20*. The correct answer should be **zero**, as 0.05 divides 4.55 without any remainder.
|
||||
|
||||
|
||||
## Parser
|
||||
|
||||
Any function (see below) as well as the constructor of the *Fraction* class parses its input and reduce it to the smallest term.
|
||||
|
||||
You can pass either Arrays, Objects, Integers, Doubles or Strings.
|
||||
|
||||
### Arrays / Objects
|
||||
|
||||
```javascript
|
||||
new Fraction(numerator, denominator);
|
||||
new Fraction([numerator, denominator]);
|
||||
new Fraction({n: numerator, d: denominator});
|
||||
```
|
||||
|
||||
### Integers
|
||||
|
||||
```javascript
|
||||
new Fraction(123);
|
||||
```
|
||||
|
||||
### Doubles
|
||||
|
||||
```javascript
|
||||
new Fraction(55.4);
|
||||
```
|
||||
|
||||
**Note:** If you pass a double as it is, Fraction.js will perform a number analysis based on Farey Sequences. If you concern performance, cache Fraction.js objects and pass arrays/objects.
|
||||
|
||||
The method is really precise, but too large exact numbers, like 1234567.9991829 will result in a wrong approximation. If you want to keep the number as it is, convert it to a string, as the string parser will not perform any further observations. If you have problems with the approximation, in the file `examples/approx.js` is a different approximation algorithm, which might work better in some more specific use-cases.
|
||||
|
||||
|
||||
### Strings
|
||||
|
||||
```javascript
|
||||
new Fraction("123.45");
|
||||
new Fraction("123/45"); // A rational number represented as two decimals, separated by a slash
|
||||
new Fraction("123:45"); // A rational number represented as two decimals, separated by a colon
|
||||
new Fraction("4 123/45"); // A rational number represented as a whole number and a fraction
|
||||
new Fraction("123.'456'"); // Note the quotes, see below!
|
||||
new Fraction("123.(456)"); // Note the brackets, see below!
|
||||
new Fraction("123.45'6'"); // Note the quotes, see below!
|
||||
new Fraction("123.45(6)"); // Note the brackets, see below!
|
||||
```
|
||||
|
||||
### Two arguments
|
||||
|
||||
```javascript
|
||||
new Fraction(3, 2); // 3/2 = 1.5
|
||||
```
|
||||
|
||||
### Repeating decimal places
|
||||
|
||||
*Fraction.js* can easily handle repeating decimal places. For example *1/3* is *0.3333...*. There is only one repeating digit. As you can see in the examples above, you can pass a number like *1/3* as "0.'3'" or "0.(3)", which are synonym. There are no tests to parse something like 0.166666666 to 1/6! If you really want to handle this number, wrap around brackets on your own with the function below for example: 0.1(66666666)
|
||||
|
||||
Assume you want to divide 123.32 / 33.6(567). [WolframAlpha](http://www.wolframalpha.com/input/?i=123.32+%2F+%2812453%2F370%29) states that you'll get a period of 1776 digits. *Fraction.js* comes to the same result. Give it a try:
|
||||
|
||||
```javascript
|
||||
var f = new Fraction("123.32");
|
||||
console.log("Bam: " + f.div("33.6(567)"));
|
||||
```
|
||||
|
||||
To automatically make a number like "0.123123123" to something more Fraction.js friendly like "0.(123)", I hacked this little brute force algorithm in a 10 minutes. Improvements are welcome...
|
||||
|
||||
```javascript
|
||||
function formatDecimal(str) {
|
||||
|
||||
var comma, pre, offset, pad, times, repeat;
|
||||
|
||||
if (-1 === (comma = str.indexOf(".")))
|
||||
return str;
|
||||
|
||||
pre = str.substr(0, comma + 1);
|
||||
str = str.substr(comma + 1);
|
||||
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
|
||||
offset = str.substr(0, i);
|
||||
|
||||
for (var j = 0; j < 5; j++) {
|
||||
|
||||
pad = str.substr(i, j + 1);
|
||||
|
||||
times = Math.ceil((str.length - offset.length) / pad.length);
|
||||
|
||||
repeat = new Array(times + 1).join(pad); // Silly String.repeat hack
|
||||
|
||||
if (0 === (offset + repeat).indexOf(str)) {
|
||||
return pre + offset + "(" + pad + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var f, x = formatDecimal("13.0123123123"); // = 13.0(123)
|
||||
if (x !== null) {
|
||||
f = new Fraction(x);
|
||||
}
|
||||
```
|
||||
|
||||
## Attributes
|
||||
|
||||
|
||||
The Fraction object allows direct access to the numerator, denominator and sign attributes. It is ensured that only the sign-attribute holds sign information so that a sign comparison is only necessary against this attribute.
|
||||
|
||||
```javascript
|
||||
var f = new Fraction('-1/2');
|
||||
console.log(f.n); // Numerator: 1
|
||||
console.log(f.d); // Denominator: 2
|
||||
console.log(f.s); // Sign: -1
|
||||
```
|
||||
|
||||
|
||||
## Functions
|
||||
|
||||
### Fraction abs()
|
||||
|
||||
Returns the actual number without any sign information
|
||||
|
||||
### Fraction neg()
|
||||
|
||||
Returns the actual number with flipped sign in order to get the additive inverse
|
||||
|
||||
### Fraction add(n)
|
||||
|
||||
Returns the sum of the actual number and the parameter n
|
||||
|
||||
### Fraction sub(n)
|
||||
|
||||
Returns the difference of the actual number and the parameter n
|
||||
|
||||
### Fraction mul(n)
|
||||
|
||||
Returns the product of the actual number and the parameter n
|
||||
|
||||
### Fraction div(n)
|
||||
|
||||
Returns the quotient of the actual number and the parameter n
|
||||
|
||||
### Fraction pow(exp)
|
||||
|
||||
Returns the power of the actual number, raised to an possible rational exponent. If the result becomes non-rational the function returns `null`.
|
||||
|
||||
### Fraction log(base)
|
||||
|
||||
Returns the logarithm of the actual number to a given rational base. If the result becomes non-rational the function returns `null`.
|
||||
|
||||
### Fraction mod(n)
|
||||
|
||||
Returns the modulus (rest of the division) of the actual object and n (this % n). It's a much more precise [fmod()](#fmod-impreciseness-circumvented) if you like. Please note that *mod()* is just like the modulo operator of most programming languages. If you want a mathematical correct modulo, see [here](#mathematical-correct-modulo).
|
||||
|
||||
### Fraction mod()
|
||||
|
||||
Returns the modulus (rest of the division) of the actual object (numerator mod denominator)
|
||||
|
||||
### Fraction gcd(n)
|
||||
|
||||
Returns the fractional greatest common divisor
|
||||
|
||||
### Fraction lcm(n)
|
||||
|
||||
Returns the fractional least common multiple
|
||||
|
||||
### Fraction ceil([places=0-16])
|
||||
|
||||
Returns the ceiling of a rational number with Math.ceil
|
||||
|
||||
### Fraction floor([places=0-16])
|
||||
|
||||
Returns the floor of a rational number with Math.floor
|
||||
|
||||
### Fraction round([places=0-16])
|
||||
|
||||
Returns the rational number rounded with Math.round
|
||||
|
||||
### Fraction roundTo(multiple)
|
||||
|
||||
Rounds a fraction to the closest multiple of another fraction.
|
||||
|
||||
### Fraction inverse()
|
||||
|
||||
Returns the multiplicative inverse of the actual number (n / d becomes d / n) in order to get the reciprocal
|
||||
|
||||
### Fraction simplify([eps=0.001])
|
||||
|
||||
Simplifies the rational number under a certain error threshold. Ex. `0.333` will be `1/3` with `eps=0.001`
|
||||
|
||||
### boolean equals(n)
|
||||
|
||||
Check if two rational numbers are equal
|
||||
|
||||
### boolean lt(n)
|
||||
|
||||
Check if this rational number is less than another
|
||||
|
||||
### boolean lte(n)
|
||||
|
||||
Check if this rational number is less than or equal another
|
||||
|
||||
### boolean gt(n)
|
||||
|
||||
Check if this rational number is greater than another
|
||||
|
||||
### boolean gte(n)
|
||||
|
||||
Check if this rational number is greater than or equal another
|
||||
|
||||
### int compare(n)
|
||||
|
||||
Compare two numbers.
|
||||
```
|
||||
result < 0: n is greater than actual number
|
||||
result > 0: n is smaller than actual number
|
||||
result = 0: n is equal to the actual number
|
||||
```
|
||||
|
||||
### boolean divisible(n)
|
||||
|
||||
Check if two numbers are divisible (n divides this)
|
||||
|
||||
### double valueOf()
|
||||
|
||||
Returns a decimal representation of the fraction
|
||||
|
||||
### String toString([decimalPlaces=15])
|
||||
|
||||
Generates an exact string representation of the given object. For repeating decimal places, digits within repeating cycles are enclosed in parentheses, e.g., `1/3 = "0.(3)"`. For other numbers, the string will include up to the specified `decimalPlaces` significant digits, including any trailing zeros if truncation occurs. For example, `1/2` will be represented as `"0.5"`, without additional trailing zeros.
|
||||
|
||||
**Note:** Since both `valueOf()` and `toString()` are provided, `toString()` will only be invoked implicitly when the object is used in a string context. For instance, when using the plus operator like `"123" + new Fraction`, `valueOf()` will be called first, as JavaScript attempts to combine primitives before concatenating them, with the string type taking precedence. However, `alert(new Fraction)` or `String(new Fraction)` will behave as expected. To ensure specific behavior, explicitly call either `toString()` or `valueOf()`.
|
||||
|
||||
### String toLatex(showMixed=false)
|
||||
|
||||
Generates an exact LaTeX representation of the actual object. You can see a [live demo](https://raw.org/article/rational-numbers-in-javascript/) on my blog.
|
||||
|
||||
The optional boolean parameter indicates if you want to show the a mixed fraction. "1 1/3" instead of "4/3"
|
||||
|
||||
### String toFraction(showMixed=false)
|
||||
|
||||
Gets a string representation of the fraction
|
||||
|
||||
The optional boolean parameter indicates if you want to showa mixed fraction. "1 1/3" instead of "4/3"
|
||||
|
||||
### Array toContinued()
|
||||
|
||||
Gets an array of the fraction represented as a continued fraction. The first element always contains the whole part.
|
||||
|
||||
```javascript
|
||||
var f = new Fraction('88/33');
|
||||
var c = f.toContinued(); // [2, 1, 2]
|
||||
```
|
||||
|
||||
### Fraction clone()
|
||||
|
||||
Creates a copy of the actual Fraction object
|
||||
|
||||
|
||||
## Exceptions
|
||||
|
||||
If a really hard error occurs (parsing error, division by zero), *Fraction.js* throws exceptions! Please make sure you handle them correctly.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
You can install `Fraction.js` via npm:
|
||||
|
||||
```bash
|
||||
npm install fraction.js
|
||||
```
|
||||
|
||||
Or with yarn:
|
||||
|
||||
```bash
|
||||
yarn add fraction.js
|
||||
```
|
||||
|
||||
Alternatively, download or clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/rawify/Fraction.js
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Include the `fraction.min.js` file in your project:
|
||||
|
||||
```html
|
||||
<script src="path/to/fraction.min.js"></script>
|
||||
<script>
|
||||
var x = new Fraction("13/4");
|
||||
</script>
|
||||
```
|
||||
|
||||
Or in a Node.js project:
|
||||
|
||||
```javascript
|
||||
const Fraction = require('fraction.js');
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```javascript
|
||||
import Fraction from 'fraction.js';
|
||||
```
|
||||
|
||||
|
||||
## Coding Style
|
||||
|
||||
As every library I publish, Fraction.js is also built to be as small as possible after compressing it with Google Closure Compiler in advanced mode. Thus the coding style orientates a little on maxing-out the compression rate. Please make sure you keep this style if you plan to extend the library.
|
||||
|
||||
## Building the library
|
||||
|
||||
After cloning the Git repository run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Run a test
|
||||
|
||||
Testing the source against the shipped test suite is as easy as
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
## Copyright and Licensing
|
||||
|
||||
Copyright (c) 2025, [Robert Eisele](https://raw.org/)
|
||||
Licensed under the MIT license.
|
||||
+1045
File diff suppressed because it is too large
Load Diff
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Fraction.js v5.3.4 8/22/2025
|
||||
https://raw.org/article/rational-numbers-in-javascript/
|
||||
|
||||
Copyright (c) 2025, Robert Eisele (https://raw.org/)
|
||||
Licensed under the MIT license.
|
||||
*/
|
||||
'use strict';(function(F){function D(){return Error("Parameters must be integer")}function x(){return Error("Invalid argument")}function C(){return Error("Division by Zero")}function q(a,b){var d=g,c=h;let f=h;if(void 0!==a&&null!==a)if(void 0!==b){if("bigint"===typeof a)d=a;else{if(isNaN(a))throw x();if(0!==a%1)throw D();d=BigInt(a)}if("bigint"===typeof b)c=b;else{if(isNaN(b))throw x();if(0!==b%1)throw D();c=BigInt(b)}f=d*c}else if("object"===typeof a){if("d"in a&&"n"in a)d=BigInt(a.n),c=BigInt(a.d),
|
||||
"s"in a&&(d*=BigInt(a.s));else if(0 in a)d=BigInt(a[0]),1 in a&&(c=BigInt(a[1]));else if("bigint"===typeof a)d=a;else throw x();f=d*c}else if("number"===typeof a){if(isNaN(a))throw x();0>a&&(f=-h,a=-a);if(0===a%1)d=BigInt(a);else{b=1;var k=0,l=1,m=1;let r=1;1<=a&&(b=10**Math.floor(1+Math.log10(a)),a/=b);for(;1E7>=l&&1E7>=r;)if(c=(k+m)/(l+r),a===c){1E7>=l+r?(d=k+m,c=l+r):r>l?(d=m,c=r):(d=k,c=l);break}else a>c?(k+=m,l+=r):(m+=k,r+=l),1E7<l?(d=m,c=r):(d=k,c=l);d=BigInt(d)*BigInt(b);c=BigInt(c)}}else if("string"===
|
||||
typeof a){c=0;k=b=d=g;l=m=h;a=a.replace(/_/g,"").match(/\d+|./g);if(null===a)throw x();"-"===a[c]?(f=-h,c++):"+"===a[c]&&c++;if(a.length===c+1)b=w(a[c++],f);else if("."===a[c+1]||"."===a[c]){"."!==a[c]&&(d=w(a[c++],f));c++;if(c+1===a.length||"("===a[c+1]&&")"===a[c+3]||"'"===a[c+1]&&"'"===a[c+3])b=w(a[c],f),m=t**BigInt(a[c].length),c++;if("("===a[c]&&")"===a[c+2]||"'"===a[c]&&"'"===a[c+2])k=w(a[c+1],f),l=t**BigInt(a[c+1].length)-h,c+=3}else"/"===a[c+1]||":"===a[c+1]?(b=w(a[c],f),m=w(a[c+2],h),c+=
|
||||
3):"/"===a[c+3]&&" "===a[c+1]&&(d=w(a[c],f),b=w(a[c+2],f),m=w(a[c+4],h),c+=5);if(a.length<=c)c=m*l,f=d=k+c*d+l*b;else throw x();}else if("bigint"===typeof a)f=d=a,c=h;else throw x();if(c===g)throw C();e.s=f<g?-h:h;e.n=d<g?-d:d;e.d=c<g?-c:c}function w(a,b){try{a=BigInt(a)}catch(d){throw x();}return a*b}function u(a){return"bigint"===typeof a?a:Math.floor(a)}function n(a,b){if(b===g)throw C();const d=Object.create(v.prototype);d.s=a<g?-h:h;a=a<g?-a:a;const c=y(a,b);d.n=a/c;d.d=b/c;return d}function A(a){const b=
|
||||
Object.create(null);if(a<=h)return b[a]=h,b;for(;a%p===g;)b[p]=(b[p]||g)+h,a/=p;for(;a%B===g;)b[B]=(b[B]||g)+h,a/=B;for(;a%z===g;)b[z]=(b[z]||g)+h,a/=z;for(let d=0,c=p+z;c*c<=a;){for(;a%c===g;)b[c]=(b[c]||g)+h,a/=c;c+=G[d];d=d+1&7}a>h&&(b[a]=(b[a]||g)+h);return b}function y(a,b){if(!a)return b;if(!b)return a;for(;;){a%=b;if(!a)return b;b%=a;if(!b)return a}}function v(a,b){q(a,b);if(this instanceof v)a=y(e.d,e.n),this.s=e.s,this.n=e.n/a,this.d=e.d/a;else return n(e.s*e.n,e.d)}"undefined"===typeof BigInt&&
|
||||
(BigInt=function(a){if(isNaN(a))throw Error("");return a});const g=BigInt(0),h=BigInt(1),p=BigInt(2),B=BigInt(3),z=BigInt(5),t=BigInt(10),e={s:h,n:g,d:h},G=[p*p,p,p*p,p,p*p,p*B,p,p*B];v.prototype={s:h,n:g,d:h,abs:function(){return n(this.n,this.d)},neg:function(){return n(-this.s*this.n,this.d)},add:function(a,b){q(a,b);return n(this.s*this.n*e.d+e.s*this.d*e.n,this.d*e.d)},sub:function(a,b){q(a,b);return n(this.s*this.n*e.d-e.s*this.d*e.n,this.d*e.d)},mul:function(a,b){q(a,b);return n(this.s*e.s*
|
||||
this.n*e.n,this.d*e.d)},div:function(a,b){q(a,b);return n(this.s*e.s*this.n*e.d,this.d*e.n)},clone:function(){return n(this.s*this.n,this.d)},mod:function(a,b){if(void 0===a)return n(this.s*this.n%this.d,h);q(a,b);if(g===e.n*this.d)throw C();return n(this.s*e.d*this.n%(e.n*this.d),e.d*this.d)},gcd:function(a,b){q(a,b);return n(y(e.n,this.n)*y(e.d,this.d),e.d*this.d)},lcm:function(a,b){q(a,b);return e.n===g&&this.n===g?n(g,h):n(e.n*this.n,y(e.n,this.n)*y(e.d,this.d))},inverse:function(){return n(this.s*
|
||||
this.d,this.n)},pow:function(a,b){q(a,b);if(e.d===h)return e.s<g?n((this.s*this.d)**e.n,this.n**e.n):n((this.s*this.n)**e.n,this.d**e.n);if(this.s<g)return null;a=A(this.n);b=A(this.d);let d=h,c=h;for(let f in a)if("1"!==f){if("0"===f){d=g;break}a[f]*=e.n;if(a[f]%e.d===g)a[f]/=e.d;else return null;d*=BigInt(f)**a[f]}for(let f in b)if("1"!==f){b[f]*=e.n;if(b[f]%e.d===g)b[f]/=e.d;else return null;c*=BigInt(f)**b[f]}return e.s<g?n(c,d):n(d,c)},log:function(a,b){q(a,b);if(this.s<=g||e.s<=g)return null;
|
||||
var d=Object.create(null);a=A(e.n);const c=A(e.d);b=A(this.n);const f=A(this.d);for(var k in c)a[k]=(a[k]||g)-c[k];for(var l in f)b[l]=(b[l]||g)-f[l];for(var m in a)"1"!==m&&(d[m]=!0);for(var r in b)"1"!==r&&(d[r]=!0);l=k=null;for(const E in d)if(m=a[E]||g,d=b[E]||g,m===g){if(d!==g)return null}else if(r=y(d,m),d/=r,m/=r,null===k&&null===l)k=d,l=m;else if(d*l!==k*m)return null;return null!==k&&null!==l?n(k,l):null},equals:function(a,b){q(a,b);return this.s*this.n*e.d===e.s*e.n*this.d},lt:function(a,
|
||||
b){q(a,b);return this.s*this.n*e.d<e.s*e.n*this.d},lte:function(a,b){q(a,b);return this.s*this.n*e.d<=e.s*e.n*this.d},gt:function(a,b){q(a,b);return this.s*this.n*e.d>e.s*e.n*this.d},gte:function(a,b){q(a,b);return this.s*this.n*e.d>=e.s*e.n*this.d},compare:function(a,b){q(a,b);a=this.s*this.n*e.d-e.s*e.n*this.d;return(g<a)-(a<g)},ceil:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/this.d)+(a*this.n%this.d>g&&this.s>=g?h:g),a)},floor:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/
|
||||
this.d)-(a*this.n%this.d>g&&this.s<g?h:g),a)},round:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/this.d)+this.s*((this.s>=g?h:g)+a*this.n%this.d*p>this.d?h:g),a)},roundTo:function(a,b){q(a,b);var d=this.n*e.d;a=this.d*e.n;b=d%a;d=u(d/a);b+b>=a&&d++;return n(this.s*d*e.n,e.d)},divisible:function(a,b){q(a,b);return e.n===g?!1:this.n*e.d%(e.n*this.d)===g},valueOf:function(){return Number(this.s*this.n)/Number(this.d)},toString:function(a=15){let b=this.n,d=this.d;var c;a:{for(c=d;c%p===g;c/=
|
||||
p);for(;c%z===g;c/=z);if(c===h)c=g;else{for(var f=t%c,k=1;f!==h;k++)if(f=f*t%c,2E3<k){c=g;break a}c=BigInt(k)}}a:{f=h;k=t;var l=c;let m=h;for(;l>g;k=k*k%d,l>>=h)l&h&&(m=m*k%d);k=m;for(l=0;300>l;l++){if(f===k){f=BigInt(l);break a}f=f*t%d;k=k*t%d}f=0}k=f;f=this.s<g?"-":"";f+=u(b/d);(b=b%d*t)&&(f+=".");if(c){for(a=k;a--;)f+=u(b/d),b%=d,b*=t;f+="(";for(a=c;a--;)f+=u(b/d),b%=d,b*=t;f+=")"}else for(;b&&a--;)f+=u(b/d),b%=d,b*=t;return f},toFraction:function(a=!1){let b=this.n,d=this.d,c=this.s<g?"-":"";
|
||||
if(d===h)c+=b;else{const f=u(b/d);a&&f>g&&(c+=f,c+=" ",b%=d);c=c+b+"/"+d}return c},toLatex:function(a=!1){let b=this.n,d=this.d,c=this.s<g?"-":"";if(d===h)c+=b;else{const f=u(b/d);a&&f>g&&(c+=f,b%=d);c=c+"\\frac{"+b+"}{"+d;c+="}"}return c},toContinued:function(){let a=this.n,b=this.d;const d=[];for(;b;){d.push(u(a/b));const c=a%b;a=b;b=c}return d},simplify:function(a=.001){a=BigInt(Math.ceil(1/a));const b=this.abs(),d=b.toContinued();for(let f=1;f<d.length;f++){let k=n(d[f-1],h);for(var c=f-2;0<=
|
||||
c;c--)k=k.inverse().add(d[c]);c=k.sub(b);if(c.n*a<c.d)return k.mul(this.s)}return this}};"function"===typeof define&&define.amd?define([],function(){return v}):"object"===typeof exports?(Object.defineProperty(v,"__esModule",{value:!0}),v["default"]=v,v.Fraction=v,module.exports=v):F.Fraction=v})(this);
|
||||
+1043
File diff suppressed because it is too large
Load Diff
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Fraction.js v5.0.0 10/1/2024
|
||||
https://raw.org/article/rational-numbers-in-javascript/
|
||||
|
||||
Copyright (c) 2024, Robert Eisele (https://raw.org/)
|
||||
Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
// This example generates a list of angles with human readable radians
|
||||
|
||||
var Fraction = require('fraction.js');
|
||||
|
||||
var tab = [];
|
||||
for (var d = 1; d <= 360; d++) {
|
||||
|
||||
var pi = Fraction(2, 360).mul(d);
|
||||
var tau = Fraction(1, 360).mul(d);
|
||||
|
||||
if (pi.d <= 6n && pi.d != 5n)
|
||||
tab.push([
|
||||
d,
|
||||
pi.toFraction() + "pi",
|
||||
tau.toFraction() + "tau"]);
|
||||
}
|
||||
|
||||
console.table(tab);
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Fraction.js v5.0.0 10/1/2024
|
||||
https://raw.org/article/rational-numbers-in-javascript/
|
||||
|
||||
Copyright (c) 2024, Robert Eisele (https://raw.org/)
|
||||
Licensed under the MIT license.
|
||||
*/
|
||||
const Fraction = require('fraction.js');
|
||||
|
||||
// Another rational approximation, not using Farey Sequences but Binary Search using the mediant
|
||||
function approximate(p, precision) {
|
||||
|
||||
var num1 = Math.floor(p);
|
||||
var den1 = 1;
|
||||
|
||||
var num2 = num1 + 1;
|
||||
var den2 = 1;
|
||||
|
||||
if (p !== num1) {
|
||||
|
||||
while (den1 <= precision && den2 <= precision) {
|
||||
|
||||
var m = (num1 + num2) / (den1 + den2);
|
||||
|
||||
if (p === m) {
|
||||
|
||||
if (den1 + den2 <= precision) {
|
||||
den1 += den2;
|
||||
num1 += num2;
|
||||
den2 = precision + 1;
|
||||
} else if (den1 > den2) {
|
||||
den2 = precision + 1;
|
||||
} else {
|
||||
den1 = precision + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
} else if (p < m) {
|
||||
num2 += num1;
|
||||
den2 += den1;
|
||||
} else {
|
||||
num1 += num2;
|
||||
den1 += den2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (den1 > precision) {
|
||||
den1 = den2;
|
||||
num1 = num2;
|
||||
}
|
||||
return new Fraction(num1, den1);
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Fraction.js v5.0.0 10/1/2024
|
||||
https://raw.org/article/rational-numbers-in-javascript/
|
||||
|
||||
Copyright (c) 2024, Robert Eisele (https://raw.org/)
|
||||
Licensed under the MIT license.
|
||||
*/
|
||||
const Fraction = require('fraction.js');
|
||||
|
||||
// Based on http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fractions/egyptian.html
|
||||
function egyptian(a, b) {
|
||||
|
||||
var res = [];
|
||||
|
||||
do {
|
||||
var t = Math.ceil(b / a);
|
||||
var x = new Fraction(a, b).sub(1, t);
|
||||
res.push(t);
|
||||
a = Number(x.n);
|
||||
b = Number(x.d);
|
||||
} while (a !== 0n);
|
||||
return res;
|
||||
}
|
||||
console.log("1 / " + egyptian(521, 1050).join(" + 1 / "));
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Fraction.js v5.0.0 10/1/2024
|
||||
https://raw.org/article/rational-numbers-in-javascript/
|
||||
|
||||
Copyright (c) 2024, Robert Eisele (https://raw.org/)
|
||||
Licensed under the MIT license.
|
||||
*/
|
||||
const Fraction = require('fraction.js');
|
||||
|
||||
/*
|
||||
We have the polynom f(x) = 1/3x_1^2 + x_2^2 + x_1 * x_2 + 3
|
||||
|
||||
The gradient of f(x):
|
||||
|
||||
grad(x) = | x_1^2+x_2 |
|
||||
| 2x_2+x_1 |
|
||||
|
||||
And thus the Hesse-Matrix H:
|
||||
| 2x_1 1 |
|
||||
| 1 2 |
|
||||
|
||||
The inverse Hesse-Matrix H^-1 is
|
||||
| -2 / (1-4x_1) 1 / (1 - 4x_1) |
|
||||
| 1 / (1 - 4x_1) -2x_1 / (1 - 4x_1) |
|
||||
|
||||
We now want to find lim ->oo x[n], with the starting element of (3 2)^T
|
||||
|
||||
*/
|
||||
|
||||
// Get the Hesse Matrix
|
||||
function H(x) {
|
||||
|
||||
var z = Fraction(1).sub(Fraction(4).mul(x[0]));
|
||||
|
||||
return [
|
||||
Fraction(-2).div(z),
|
||||
Fraction(1).div(z),
|
||||
Fraction(1).div(z),
|
||||
Fraction(-2).mul(x[0]).div(z),
|
||||
];
|
||||
}
|
||||
|
||||
// Get the gradient of f(x)
|
||||
function grad(x) {
|
||||
|
||||
return [
|
||||
Fraction(x[0]).mul(x[0]).add(x[1]),
|
||||
Fraction(2).mul(x[1]).add(x[0])
|
||||
];
|
||||
}
|
||||
|
||||
// A simple matrix multiplication helper
|
||||
function matrMult(m, v) {
|
||||
|
||||
return [
|
||||
Fraction(m[0]).mul(v[0]).add(Fraction(m[1]).mul(v[1])),
|
||||
Fraction(m[2]).mul(v[0]).add(Fraction(m[3]).mul(v[1]))
|
||||
];
|
||||
}
|
||||
|
||||
// A simple vector subtraction helper
|
||||
function vecSub(a, b) {
|
||||
|
||||
return [
|
||||
Fraction(a[0]).sub(b[0]),
|
||||
Fraction(a[1]).sub(b[1])
|
||||
];
|
||||
}
|
||||
|
||||
// Main function, gets a vector and the actual index
|
||||
function run(V, j) {
|
||||
|
||||
var t = H(V);
|
||||
//console.log("H(X)");
|
||||
for (var i in t) {
|
||||
|
||||
// console.log(t[i].toFraction());
|
||||
}
|
||||
|
||||
var s = grad(V);
|
||||
//console.log("vf(X)");
|
||||
for (var i in s) {
|
||||
|
||||
// console.log(s[i].toFraction());
|
||||
}
|
||||
|
||||
//console.log("multiplication");
|
||||
var r = matrMult(t, s);
|
||||
for (var i in r) {
|
||||
|
||||
// console.log(r[i].toFraction());
|
||||
}
|
||||
|
||||
var R = (vecSub(V, r));
|
||||
|
||||
console.log("X" + j);
|
||||
console.log(R[0].toFraction(), "= " + R[0].valueOf());
|
||||
console.log(R[1].toFraction(), "= " + R[1].valueOf());
|
||||
console.log("\n");
|
||||
|
||||
return R;
|
||||
}
|
||||
|
||||
|
||||
// Set the starting vector
|
||||
var v = [3, 2];
|
||||
|
||||
for (var i = 0; i < 15; i++) {
|
||||
|
||||
v = run(v, i);
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Fraction.js v5.0.0 10/1/2024
|
||||
https://raw.org/article/rational-numbers-in-javascript/
|
||||
|
||||
Copyright (c) 2024, Robert Eisele (https://raw.org/)
|
||||
Licensed under the MIT license.
|
||||
*/
|
||||
const Fraction = require('fraction.js');
|
||||
|
||||
// NOTE: This is a nice example, but a stable version of this is served with Polynomial.js:
|
||||
// https://github.com/rawify/Polynomial.js
|
||||
|
||||
function integrate(poly) {
|
||||
|
||||
poly = poly.replace(/\s+/g, "");
|
||||
|
||||
var regex = /(\([+-]?[0-9/]+\)|[+-]?[0-9/]+)x(?:\^(\([+-]?[0-9/]+\)|[+-]?[0-9]+))?/g;
|
||||
var arr;
|
||||
var res = {};
|
||||
while (null !== (arr = regex.exec(poly))) {
|
||||
|
||||
var a = (arr[1] || "1").replace("(", "").replace(")", "").split("/");
|
||||
var b = (arr[2] || "1").replace("(", "").replace(")", "").split("/");
|
||||
|
||||
var exp = new Fraction(b).add(1);
|
||||
var key = "" + exp;
|
||||
|
||||
if (res[key] !== undefined) {
|
||||
res[key] = { x: new Fraction(a).div(exp).add(res[key].x), e: exp };
|
||||
} else {
|
||||
res[key] = { x: new Fraction(a).div(exp), e: exp };
|
||||
}
|
||||
}
|
||||
|
||||
var str = "";
|
||||
var c = 0;
|
||||
for (var i in res) {
|
||||
if (res[i].x.s !== -1n && c > 0) {
|
||||
str += "+";
|
||||
} else if (res[i].x.s === -1n) {
|
||||
str += "-";
|
||||
}
|
||||
if (res[i].x.n !== res[i].x.d) {
|
||||
if (res[i].x.d !== 1n) {
|
||||
str += res[i].x.n + "/" + res[i].x.d;
|
||||
} else {
|
||||
str += res[i].x.n;
|
||||
}
|
||||
}
|
||||
str += "x";
|
||||
if (res[i].e.n !== res[i].e.d) {
|
||||
str += "^";
|
||||
if (res[i].e.d !== 1n) {
|
||||
str += "(" + res[i].e.n + "/" + res[i].e.d + ")";
|
||||
} else {
|
||||
str += res[i].e.n;
|
||||
}
|
||||
}
|
||||
c++;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
var poly = "-2/3x^3-2x^2+3x+8x^3-1/3x^(4/8)";
|
||||
|
||||
console.log("f(x): " + poly);
|
||||
console.log("F(x): " + integrate(poly));
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Given the ratio a : b : c = 2 : 3 : 4
|
||||
What is c, given a = 40?
|
||||
|
||||
A general ratio chain is a_1 : a_2 : a_3 : ... : a_n = r_1 : r2 : r_3 : ... : r_n.
|
||||
Now each term can be expressed as a_i = r_i * x for some unknown proportional constant x.
|
||||
If a_k is known it follows that x = a_k / r_k. Substituting x into the first equation yields
|
||||
a_i = r_i / r_k * a_k.
|
||||
|
||||
Given an array r and a given value a_k, the following function calculates all a_i:
|
||||
*/
|
||||
|
||||
function calculateRatios(r, a_k, k) {
|
||||
const x = Fraction(a_k).div(r[k]);
|
||||
return r.map(r_i => x.mul(r_i));
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
const r = [2, 3, 4]; // Ratio array representing a : b : c = 2 : 3 : 4
|
||||
const a_k = 40; // Given value of a (corresponding to r[0])
|
||||
const k = 0; // Index of the known value (a corresponds to r[0])
|
||||
|
||||
const result = calculateRatios(r, a_k, k);
|
||||
console.log(result); // Output: [40, 60, 80]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user