autoprefixer
Advanced tools
Comparing version 10.2.5 to 10.4.16
@@ -1,4 +0,4 @@ | ||
let unpack = require('caniuse-lite').feature | ||
let unpack = require('caniuse-lite/dist/unpacker/feature') | ||
function browsersSort (a, b) { | ||
function browsersSort(a, b) { | ||
a = a.split(' ') | ||
@@ -16,3 +16,3 @@ b = b.split(' ') | ||
// Convert Can I Use data | ||
function f (data, opts, callback) { | ||
function f(data, opts, callback) { | ||
data = unpack(data) | ||
@@ -43,3 +43,3 @@ | ||
function prefix (names, data) { | ||
function prefix(names, data) { | ||
for (let name of names) { | ||
@@ -50,3 +50,3 @@ result[name] = Object.assign({}, data) | ||
function add (names, data) { | ||
function add(names, data) { | ||
for (let name of names) { | ||
@@ -62,3 +62,5 @@ result[name].browsers = result[name].browsers | ||
// Border Radius | ||
f(require('caniuse-lite/data/features/border-radius'), browsers => | ||
let prefixBorderRadius = require('caniuse-lite/data/features/border-radius') | ||
f(prefixBorderRadius, browsers => | ||
prefix( | ||
@@ -73,5 +75,5 @@ [ | ||
{ | ||
mistakes: ['-khtml-', '-ms-', '-o-'], | ||
browsers, | ||
feature: 'border-radius', | ||
browsers | ||
mistakes: ['-khtml-', '-ms-', '-o-'] | ||
} | ||
@@ -82,7 +84,9 @@ ) | ||
// Box Shadow | ||
f(require('caniuse-lite/data/features/css-boxshadow'), browsers => | ||
let prefixBoxshadow = require('caniuse-lite/data/features/css-boxshadow') | ||
f(prefixBoxshadow, browsers => | ||
prefix(['box-shadow'], { | ||
mistakes: ['-khtml-'], | ||
browsers, | ||
feature: 'css-boxshadow', | ||
browsers | ||
mistakes: ['-khtml-'] | ||
}) | ||
@@ -92,3 +96,5 @@ ) | ||
// Animation | ||
f(require('caniuse-lite/data/features/css-animation'), browsers => | ||
let prefixAnimation = require('caniuse-lite/data/features/css-animation') | ||
f(prefixAnimation, browsers => | ||
prefix( | ||
@@ -108,5 +114,5 @@ [ | ||
{ | ||
mistakes: ['-khtml-', '-ms-'], | ||
browsers, | ||
feature: 'css-animation', | ||
browsers | ||
mistakes: ['-khtml-', '-ms-'] | ||
} | ||
@@ -117,3 +123,5 @@ ) | ||
// Transition | ||
f(require('caniuse-lite/data/features/css-transitions'), browsers => | ||
let prefixTransition = require('caniuse-lite/data/features/css-transitions') | ||
f(prefixTransition, browsers => | ||
prefix( | ||
@@ -128,5 +136,5 @@ [ | ||
{ | ||
mistakes: ['-khtml-', '-ms-'], | ||
browsers, | ||
feature: 'css-transitions' | ||
feature: 'css-transitions', | ||
mistakes: ['-khtml-', '-ms-'] | ||
} | ||
@@ -137,6 +145,8 @@ ) | ||
// Transform 2D | ||
f(require('caniuse-lite/data/features/transforms2d'), browsers => | ||
let prefixTransform2d = require('caniuse-lite/data/features/transforms2d') | ||
f(prefixTransform2d, browsers => | ||
prefix(['transform', 'transform-origin'], { | ||
feature: 'transforms2d', | ||
browsers | ||
browsers, | ||
feature: 'transforms2d' | ||
}) | ||
@@ -146,21 +156,21 @@ ) | ||
// Transform 3D | ||
let transforms3d = require('caniuse-lite/data/features/transforms3d') | ||
let prefixTransforms3d = require('caniuse-lite/data/features/transforms3d') | ||
f(transforms3d, browsers => { | ||
f(prefixTransforms3d, browsers => { | ||
prefix(['perspective', 'perspective-origin'], { | ||
feature: 'transforms3d', | ||
browsers | ||
browsers, | ||
feature: 'transforms3d' | ||
}) | ||
return prefix(['transform-style'], { | ||
mistakes: ['-ms-', '-o-'], | ||
browsers, | ||
feature: 'transforms3d' | ||
feature: 'transforms3d', | ||
mistakes: ['-ms-', '-o-'] | ||
}) | ||
}) | ||
f(transforms3d, { match: /y\sx|y\s#2/ }, browsers => | ||
f(prefixTransforms3d, { match: /y\sx|y\s#2/ }, browsers => | ||
prefix(['backface-visibility'], { | ||
mistakes: ['-ms-', '-o-'], | ||
browsers, | ||
feature: 'transforms3d', | ||
browsers | ||
mistakes: ['-ms-', '-o-'] | ||
}) | ||
@@ -170,5 +180,5 @@ ) | ||
// Gradients | ||
let gradients = require('caniuse-lite/data/features/css-gradients') | ||
let prefixGradients = require('caniuse-lite/data/features/css-gradients') | ||
f(gradients, { match: /y\sx/ }, browsers => | ||
f(prefixGradients, { match: /y\sx/ }, browsers => | ||
prefix( | ||
@@ -182,2 +192,5 @@ [ | ||
{ | ||
browsers, | ||
feature: 'css-gradients', | ||
mistakes: ['-ms-'], | ||
props: [ | ||
@@ -192,6 +205,3 @@ 'background', | ||
'mask-image' | ||
], | ||
mistakes: ['-ms-'], | ||
feature: 'css-gradients', | ||
browsers | ||
] | ||
} | ||
@@ -201,3 +211,3 @@ ) | ||
f(gradients, { match: /a\sx/ }, browsers => { | ||
f(prefixGradients, { match: /a\sx/ }, browsers => { | ||
browsers = browsers.map(i => { | ||
@@ -218,4 +228,4 @@ if (/firefox|op/.test(i)) { | ||
{ | ||
feature: 'css-gradients', | ||
browsers | ||
browsers, | ||
feature: 'css-gradients' | ||
} | ||
@@ -226,6 +236,8 @@ ) | ||
// Box sizing | ||
f(require('caniuse-lite/data/features/css3-boxsizing'), browsers => | ||
let prefixBoxsizing = require('caniuse-lite/data/features/css3-boxsizing') | ||
f(prefixBoxsizing, browsers => | ||
prefix(['box-sizing'], { | ||
feature: 'css3-boxsizing', | ||
browsers | ||
browsers, | ||
feature: 'css3-boxsizing' | ||
}) | ||
@@ -235,6 +247,8 @@ ) | ||
// Filter Effects | ||
f(require('caniuse-lite/data/features/css-filters'), browsers => | ||
let prefixFilters = require('caniuse-lite/data/features/css-filters') | ||
f(prefixFilters, browsers => | ||
prefix(['filter'], { | ||
feature: 'css-filters', | ||
browsers | ||
browsers, | ||
feature: 'css-filters' | ||
}) | ||
@@ -244,4 +258,8 @@ ) | ||
// filter() function | ||
f(require('caniuse-lite/data/features/css-filter-function'), browsers => | ||
let prefixFilterFunction = require('caniuse-lite/data/features/css-filter-function') | ||
f(prefixFilterFunction, browsers => | ||
prefix(['filter-function'], { | ||
browsers, | ||
feature: 'css-filter-function', | ||
props: [ | ||
@@ -256,5 +274,3 @@ 'background', | ||
'mask-image' | ||
], | ||
feature: 'css-filter-function', | ||
browsers | ||
] | ||
}) | ||
@@ -264,8 +280,8 @@ ) | ||
// Backdrop-filter | ||
let backdrop = require('caniuse-lite/data/features/css-backdrop-filter') | ||
let prefixBackdropFilter = require('caniuse-lite/data/features/css-backdrop-filter') | ||
f(backdrop, { match: /y\sx|y\s#2/ }, browsers => | ||
f(prefixBackdropFilter, { match: /y\sx|y\s#2/ }, browsers => | ||
prefix(['backdrop-filter'], { | ||
feature: 'css-backdrop-filter', | ||
browsers | ||
browsers, | ||
feature: 'css-backdrop-filter' | ||
}) | ||
@@ -275,4 +291,8 @@ ) | ||
// element() function | ||
f(require('caniuse-lite/data/features/css-element-function'), browsers => | ||
let prefixElementFunction = require('caniuse-lite/data/features/css-element-function') | ||
f(prefixElementFunction, browsers => | ||
prefix(['element'], { | ||
browsers, | ||
feature: 'css-element-function', | ||
props: [ | ||
@@ -287,5 +307,3 @@ 'background', | ||
'mask-image' | ||
], | ||
feature: 'css-element-function', | ||
browsers | ||
] | ||
}) | ||
@@ -295,3 +313,5 @@ ) | ||
// Multicolumns | ||
f(require('caniuse-lite/data/features/multicolumn'), browsers => { | ||
let prefixMulticolumns = require('caniuse-lite/data/features/multicolumn') | ||
f(prefixMulticolumns, browsers => { | ||
prefix( | ||
@@ -311,4 +331,4 @@ [ | ||
{ | ||
feature: 'multicolumn', | ||
browsers | ||
browsers, | ||
feature: 'multicolumn' | ||
} | ||
@@ -319,4 +339,4 @@ ) | ||
prefix(['break-before', 'break-after', 'break-inside'], { | ||
feature: 'multicolumn', | ||
browsers: noff | ||
browsers: noff, | ||
feature: 'multicolumn' | ||
}) | ||
@@ -326,7 +346,9 @@ }) | ||
// User select | ||
f(require('caniuse-lite/data/features/user-select-none'), browsers => | ||
let prefixUserSelect = require('caniuse-lite/data/features/user-select-none') | ||
f(prefixUserSelect, browsers => | ||
prefix(['user-select'], { | ||
mistakes: ['-khtml-'], | ||
browsers, | ||
feature: 'user-select-none', | ||
browsers | ||
mistakes: ['-khtml-'] | ||
}) | ||
@@ -336,5 +358,5 @@ ) | ||
// Flexible Box Layout | ||
let flexbox = require('caniuse-lite/data/features/flexbox') | ||
let prefixFlexbox = require('caniuse-lite/data/features/flexbox') | ||
f(flexbox, { match: /a\sx/ }, browsers => { | ||
f(prefixFlexbox, { match: /a\sx/ }, browsers => { | ||
browsers = browsers.map(i => { | ||
@@ -348,9 +370,9 @@ if (/ie|firefox/.test(i)) { | ||
prefix(['display-flex', 'inline-flex'], { | ||
props: ['display'], | ||
browsers, | ||
feature: 'flexbox', | ||
browsers | ||
props: ['display'] | ||
}) | ||
prefix(['flex', 'flex-grow', 'flex-shrink', 'flex-basis'], { | ||
feature: 'flexbox', | ||
browsers | ||
browsers, | ||
feature: 'flexbox' | ||
}) | ||
@@ -369,4 +391,4 @@ prefix( | ||
{ | ||
feature: 'flexbox', | ||
browsers | ||
browsers, | ||
feature: 'flexbox' | ||
} | ||
@@ -376,10 +398,10 @@ ) | ||
f(flexbox, { match: /y\sx/ }, browsers => { | ||
f(prefixFlexbox, { match: /y\sx/ }, browsers => { | ||
add(['display-flex', 'inline-flex'], { | ||
feature: 'flexbox', | ||
browsers | ||
browsers, | ||
feature: 'flexbox' | ||
}) | ||
add(['flex', 'flex-grow', 'flex-shrink', 'flex-basis'], { | ||
feature: 'flexbox', | ||
browsers | ||
browsers, | ||
feature: 'flexbox' | ||
}) | ||
@@ -398,4 +420,4 @@ add( | ||
{ | ||
feature: 'flexbox', | ||
browsers | ||
browsers, | ||
feature: 'flexbox' | ||
} | ||
@@ -406,7 +428,9 @@ ) | ||
// calc() unit | ||
f(require('caniuse-lite/data/features/calc'), browsers => | ||
let prefixCalc = require('caniuse-lite/data/features/calc') | ||
f(prefixCalc, browsers => | ||
prefix(['calc'], { | ||
props: ['*'], | ||
browsers, | ||
feature: 'calc', | ||
browsers | ||
props: ['*'] | ||
}) | ||
@@ -416,6 +440,8 @@ ) | ||
// Background options | ||
f(require('caniuse-lite/data/features/background-img-opts'), browsers => | ||
let prefixBackgroundOptions = require('caniuse-lite/data/features/background-img-opts') | ||
f(prefixBackgroundOptions, browsers => | ||
prefix(['background-origin', 'background-size'], { | ||
feature: 'background-img-opts', | ||
browsers | ||
browsers, | ||
feature: 'background-img-opts' | ||
}) | ||
@@ -425,6 +451,8 @@ ) | ||
// background-clip: text | ||
f(require('caniuse-lite/data/features/background-clip-text'), browsers => | ||
let prefixBackgroundClipText = require('caniuse-lite/data/features/background-clip-text') | ||
f(prefixBackgroundClipText, browsers => | ||
prefix(['background-clip'], { | ||
feature: 'background-clip-text', | ||
browsers | ||
browsers, | ||
feature: 'background-clip-text' | ||
}) | ||
@@ -434,3 +462,5 @@ ) | ||
// Font feature settings | ||
f(require('caniuse-lite/data/features/font-feature'), browsers => | ||
let prefixFontFeature = require('caniuse-lite/data/features/font-feature') | ||
f(prefixFontFeature, browsers => | ||
prefix( | ||
@@ -443,4 +473,4 @@ [ | ||
{ | ||
feature: 'font-feature', | ||
browsers | ||
browsers, | ||
feature: 'font-feature' | ||
} | ||
@@ -451,6 +481,8 @@ ) | ||
// CSS font-kerning property | ||
f(require('caniuse-lite/data/features/font-kerning'), browsers => | ||
let prefixFontKerning = require('caniuse-lite/data/features/font-kerning') | ||
f(prefixFontKerning, browsers => | ||
prefix(['font-kerning'], { | ||
feature: 'font-kerning', | ||
browsers | ||
browsers, | ||
feature: 'font-kerning' | ||
}) | ||
@@ -460,6 +492,8 @@ ) | ||
// Border image | ||
f(require('caniuse-lite/data/features/border-image'), browsers => | ||
let prefixBorderImage = require('caniuse-lite/data/features/border-image') | ||
f(prefixBorderImage, browsers => | ||
prefix(['border-image'], { | ||
feature: 'border-image', | ||
browsers | ||
browsers, | ||
feature: 'border-image' | ||
}) | ||
@@ -469,7 +503,9 @@ ) | ||
// Selection selector | ||
f(require('caniuse-lite/data/features/css-selection'), browsers => | ||
let prefixSelection = require('caniuse-lite/data/features/css-selection') | ||
f(prefixSelection, browsers => | ||
prefix(['::selection'], { | ||
selector: true, | ||
browsers, | ||
feature: 'css-selection', | ||
browsers | ||
selector: true | ||
}) | ||
@@ -479,7 +515,9 @@ ) | ||
// Placeholder selector | ||
f(require('caniuse-lite/data/features/css-placeholder'), browsers => { | ||
let prefixPlaceholder = require('caniuse-lite/data/features/css-placeholder') | ||
f(prefixPlaceholder, browsers => { | ||
prefix(['::placeholder'], { | ||
selector: true, | ||
browsers: browsers.concat(['ie 10 old', 'ie 11 old', 'firefox 18 old']), | ||
feature: 'css-placeholder', | ||
browsers: browsers.concat(['ie 10 old', 'ie 11 old', 'firefox 18 old']) | ||
selector: true | ||
}) | ||
@@ -489,7 +527,9 @@ }) | ||
// Placeholder-shown selector | ||
f(require('caniuse-lite/data/features/css-placeholder-shown'), browsers => { | ||
let prefixPlaceholderShown = require('caniuse-lite/data/features/css-placeholder-shown') | ||
f(prefixPlaceholderShown, browsers => { | ||
prefix([':placeholder-shown'], { | ||
selector: true, | ||
browsers, | ||
feature: 'css-placeholder-shown', | ||
browsers | ||
selector: true | ||
}) | ||
@@ -499,6 +539,8 @@ }) | ||
// Hyphenation | ||
f(require('caniuse-lite/data/features/css-hyphens'), browsers => | ||
let prefixHyphens = require('caniuse-lite/data/features/css-hyphens') | ||
f(prefixHyphens, browsers => | ||
prefix(['hyphens'], { | ||
feature: 'css-hyphens', | ||
browsers | ||
browsers, | ||
feature: 'css-hyphens' | ||
}) | ||
@@ -508,25 +550,53 @@ ) | ||
// Fullscreen selector | ||
let fullscreen = require('caniuse-lite/data/features/fullscreen') | ||
let prefixFullscreen = require('caniuse-lite/data/features/fullscreen') | ||
f(fullscreen, browsers => | ||
f(prefixFullscreen, browsers => | ||
prefix([':fullscreen'], { | ||
selector: true, | ||
browsers, | ||
feature: 'fullscreen', | ||
browsers | ||
selector: true | ||
}) | ||
) | ||
f(fullscreen, { match: /x(\s#2|$)/ }, browsers => | ||
// ::backdrop pseudo-element | ||
// https://caniuse.com/mdn-css_selectors_backdrop | ||
let prefixBackdrop = require('caniuse-lite/data/features/mdn-css-backdrop-pseudo-element') | ||
f(prefixBackdrop, browsers => | ||
prefix(['::backdrop'], { | ||
selector: true, | ||
feature: 'fullscreen', | ||
browsers | ||
browsers, | ||
feature: 'backdrop', | ||
selector: true | ||
}) | ||
) | ||
// File selector button | ||
let prefixFileSelectorButton = require('caniuse-lite/data/features/css-file-selector-button') | ||
f(prefixFileSelectorButton, browsers => | ||
prefix(['::file-selector-button'], { | ||
browsers, | ||
feature: 'file-selector-button', | ||
selector: true | ||
}) | ||
) | ||
// :autofill | ||
let prefixAutofill = require('caniuse-lite/data/features/css-autofill') | ||
f(prefixAutofill, browsers => | ||
prefix([':autofill'], { | ||
browsers, | ||
feature: 'css-autofill', | ||
selector: true | ||
}) | ||
) | ||
// Tab size | ||
f(require('caniuse-lite/data/features/css3-tabsize'), browsers => | ||
let prefixTabsize = require('caniuse-lite/data/features/css3-tabsize') | ||
f(prefixTabsize, browsers => | ||
prefix(['tab-size'], { | ||
feature: 'css3-tabsize', | ||
browsers | ||
browsers, | ||
feature: 'css3-tabsize' | ||
}) | ||
@@ -536,3 +606,3 @@ ) | ||
// Intrinsic & extrinsic sizing | ||
let intrinsic = require('caniuse-lite/data/features/intrinsic-width') | ||
let prefixIntrinsic = require('caniuse-lite/data/features/intrinsic-width') | ||
@@ -560,32 +630,46 @@ let sizeProps = [ | ||
f(intrinsic, browsers => | ||
f(prefixIntrinsic, browsers => | ||
prefix(['max-content', 'min-content'], { | ||
props: sizeProps, | ||
browsers, | ||
feature: 'intrinsic-width', | ||
browsers | ||
props: sizeProps | ||
}) | ||
) | ||
f(intrinsic, { match: /x|\s#4/ }, browsers => | ||
prefix(['fill', 'fill-available', 'stretch'], { | ||
props: sizeProps, | ||
f(prefixIntrinsic, { match: /x|\s#4/ }, browsers => | ||
prefix(['fill', 'fill-available'], { | ||
browsers, | ||
feature: 'intrinsic-width', | ||
browsers | ||
props: sizeProps | ||
}) | ||
) | ||
f(intrinsic, { match: /x|\s#5/ }, browsers => | ||
f(prefixIntrinsic, { match: /x|\s#5/ }, browsers => | ||
prefix(['fit-content'], { | ||
props: sizeProps, | ||
browsers, | ||
feature: 'intrinsic-width', | ||
browsers | ||
props: sizeProps | ||
}) | ||
) | ||
// Stretch value | ||
let prefixStretch = require('caniuse-lite/data/features/css-width-stretch') | ||
f(prefixStretch, browsers => | ||
prefix(['stretch'], { | ||
browsers, | ||
feature: 'css-width-stretch', | ||
props: sizeProps | ||
}) | ||
) | ||
// Zoom cursors | ||
f(require('caniuse-lite/data/features/css3-cursors-newer'), browsers => | ||
let prefixCursorsNewer = require('caniuse-lite/data/features/css3-cursors-newer') | ||
f(prefixCursorsNewer, browsers => | ||
prefix(['zoom-in', 'zoom-out'], { | ||
props: ['cursor'], | ||
browsers, | ||
feature: 'css3-cursors-newer', | ||
browsers | ||
props: ['cursor'] | ||
}) | ||
@@ -595,7 +679,9 @@ ) | ||
// Grab cursors | ||
f(require('caniuse-lite/data/features/css3-cursors-grab'), browsers => | ||
let prefixCursorsGrab = require('caniuse-lite/data/features/css3-cursors-grab') | ||
f(prefixCursorsGrab, browsers => | ||
prefix(['grab', 'grabbing'], { | ||
props: ['cursor'], | ||
browsers, | ||
feature: 'css3-cursors-grab', | ||
browsers | ||
props: ['cursor'] | ||
}) | ||
@@ -605,7 +691,9 @@ ) | ||
// Sticky position | ||
f(require('caniuse-lite/data/features/css-sticky'), browsers => | ||
let prefixSticky = require('caniuse-lite/data/features/css-sticky') | ||
f(prefixSticky, browsers => | ||
prefix(['sticky'], { | ||
props: ['position'], | ||
browsers, | ||
feature: 'css-sticky', | ||
browsers | ||
props: ['position'] | ||
}) | ||
@@ -615,6 +703,8 @@ ) | ||
// Pointer Events | ||
f(require('caniuse-lite/data/features/pointer'), browsers => | ||
let prefixPointer = require('caniuse-lite/data/features/pointer') | ||
f(prefixPointer, browsers => | ||
prefix(['touch-action'], { | ||
feature: 'pointer', | ||
browsers | ||
browsers, | ||
feature: 'pointer' | ||
}) | ||
@@ -624,31 +714,54 @@ ) | ||
// Text decoration | ||
let decoration = require('caniuse-lite/data/features/text-decoration') | ||
let prefixDecoration = require('caniuse-lite/data/features/text-decoration') | ||
f(decoration, browsers => | ||
prefix( | ||
[ | ||
'text-decoration-style', | ||
'text-decoration-color', | ||
'text-decoration-line', | ||
'text-decoration' | ||
], | ||
{ | ||
feature: 'text-decoration', | ||
browsers | ||
} | ||
) | ||
f(prefixDecoration, { match: /x.*#[235]/ }, browsers => | ||
prefix(['text-decoration-skip', 'text-decoration-skip-ink'], { | ||
browsers, | ||
feature: 'text-decoration' | ||
}) | ||
) | ||
f(decoration, { match: /x.*#[235]/ }, browsers => | ||
prefix(['text-decoration-skip', 'text-decoration-skip-ink'], { | ||
feature: 'text-decoration', | ||
browsers | ||
let prefixDecorationShorthand = require('caniuse-lite/data/features/mdn-text-decoration-shorthand') | ||
f(prefixDecorationShorthand, browsers => | ||
prefix(['text-decoration'], { | ||
browsers, | ||
feature: 'text-decoration' | ||
}) | ||
) | ||
let prefixDecorationColor = require('caniuse-lite/data/features/mdn-text-decoration-color') | ||
f(prefixDecorationColor, browsers => | ||
prefix(['text-decoration-color'], { | ||
browsers, | ||
feature: 'text-decoration' | ||
}) | ||
) | ||
let prefixDecorationLine = require('caniuse-lite/data/features/mdn-text-decoration-line') | ||
f(prefixDecorationLine, browsers => | ||
prefix(['text-decoration-line'], { | ||
browsers, | ||
feature: 'text-decoration' | ||
}) | ||
) | ||
let prefixDecorationStyle = require('caniuse-lite/data/features/mdn-text-decoration-style') | ||
f(prefixDecorationStyle, browsers => | ||
prefix(['text-decoration-style'], { | ||
browsers, | ||
feature: 'text-decoration' | ||
}) | ||
) | ||
// Text Size Adjust | ||
f(require('caniuse-lite/data/features/text-size-adjust'), browsers => | ||
let prefixTextSizeAdjust = require('caniuse-lite/data/features/text-size-adjust') | ||
f(prefixTextSizeAdjust, browsers => | ||
prefix(['text-size-adjust'], { | ||
feature: 'text-size-adjust', | ||
browsers | ||
browsers, | ||
feature: 'text-size-adjust' | ||
}) | ||
@@ -658,3 +771,5 @@ ) | ||
// CSS Masks | ||
f(require('caniuse-lite/data/features/css-masks'), browsers => { | ||
let prefixCssMasks = require('caniuse-lite/data/features/css-masks') | ||
f(prefixCssMasks, browsers => { | ||
prefix( | ||
@@ -671,4 +786,4 @@ [ | ||
{ | ||
feature: 'css-masks', | ||
browsers | ||
browsers, | ||
feature: 'css-masks' | ||
} | ||
@@ -687,4 +802,4 @@ ) | ||
{ | ||
feature: 'css-masks', | ||
browsers | ||
browsers, | ||
feature: 'css-masks' | ||
} | ||
@@ -695,6 +810,8 @@ ) | ||
// CSS clip-path property | ||
f(require('caniuse-lite/data/features/css-clip-path'), browsers => | ||
let prefixClipPath = require('caniuse-lite/data/features/css-clip-path') | ||
f(prefixClipPath, browsers => | ||
prefix(['clip-path'], { | ||
feature: 'css-clip-path', | ||
browsers | ||
browsers, | ||
feature: 'css-clip-path' | ||
}) | ||
@@ -704,6 +821,8 @@ ) | ||
// Fragmented Borders and Backgrounds | ||
f(require('caniuse-lite/data/features/css-boxdecorationbreak'), browsers => | ||
let prefixBoxdecoration = require('caniuse-lite/data/features/css-boxdecorationbreak') | ||
f(prefixBoxdecoration, browsers => | ||
prefix(['box-decoration-break'], { | ||
feature: 'css-boxdecorationbreak', | ||
browsers | ||
browsers, | ||
feature: 'css-boxdecorationbreak' | ||
}) | ||
@@ -713,6 +832,8 @@ ) | ||
// CSS3 object-fit/object-position | ||
f(require('caniuse-lite/data/features/object-fit'), browsers => | ||
let prefixObjectFit = require('caniuse-lite/data/features/object-fit') | ||
f(prefixObjectFit, browsers => | ||
prefix(['object-fit', 'object-position'], { | ||
feature: 'object-fit', | ||
browsers | ||
browsers, | ||
feature: 'object-fit' | ||
}) | ||
@@ -722,6 +843,8 @@ ) | ||
// CSS Shapes | ||
f(require('caniuse-lite/data/features/css-shapes'), browsers => | ||
let prefixShapes = require('caniuse-lite/data/features/css-shapes') | ||
f(prefixShapes, browsers => | ||
prefix(['shape-margin', 'shape-outside', 'shape-image-threshold'], { | ||
feature: 'css-shapes', | ||
browsers | ||
browsers, | ||
feature: 'css-shapes' | ||
}) | ||
@@ -731,6 +854,8 @@ ) | ||
// CSS3 text-overflow | ||
f(require('caniuse-lite/data/features/text-overflow'), browsers => | ||
let prefixTextOverflow = require('caniuse-lite/data/features/text-overflow') | ||
f(prefixTextOverflow, browsers => | ||
prefix(['text-overflow'], { | ||
feature: 'text-overflow', | ||
browsers | ||
browsers, | ||
feature: 'text-overflow' | ||
}) | ||
@@ -740,6 +865,8 @@ ) | ||
// Viewport at-rule | ||
f(require('caniuse-lite/data/features/css-deviceadaptation'), browsers => | ||
let prefixDeviceadaptation = require('caniuse-lite/data/features/css-deviceadaptation') | ||
f(prefixDeviceadaptation, browsers => | ||
prefix(['@viewport'], { | ||
feature: 'css-deviceadaptation', | ||
browsers | ||
browsers, | ||
feature: 'css-deviceadaptation' | ||
}) | ||
@@ -749,8 +876,8 @@ ) | ||
// Resolution Media Queries | ||
let resolut = require('caniuse-lite/data/features/css-media-resolution') | ||
let prefixResolut = require('caniuse-lite/data/features/css-media-resolution') | ||
f(resolut, { match: /( x($| )|a #2)/ }, browsers => | ||
f(prefixResolut, { match: /( x($| )|a #2)/ }, browsers => | ||
prefix(['@resolution'], { | ||
feature: 'css-media-resolution', | ||
browsers | ||
browsers, | ||
feature: 'css-media-resolution' | ||
}) | ||
@@ -760,6 +887,8 @@ ) | ||
// CSS text-align-last | ||
f(require('caniuse-lite/data/features/css-text-align-last'), browsers => | ||
let prefixTextAlignLast = require('caniuse-lite/data/features/css-text-align-last') | ||
f(prefixTextAlignLast, browsers => | ||
prefix(['text-align-last'], { | ||
feature: 'css-text-align-last', | ||
browsers | ||
browsers, | ||
feature: 'css-text-align-last' | ||
}) | ||
@@ -769,16 +898,16 @@ ) | ||
// Crisp Edges Image Rendering Algorithm | ||
let crispedges = require('caniuse-lite/data/features/css-crisp-edges') | ||
let prefixCrispedges = require('caniuse-lite/data/features/css-crisp-edges') | ||
f(crispedges, { match: /y x|a x #1/ }, browsers => | ||
f(prefixCrispedges, { match: /y x|a x #1/ }, browsers => | ||
prefix(['pixelated'], { | ||
props: ['image-rendering'], | ||
browsers, | ||
feature: 'css-crisp-edges', | ||
browsers | ||
props: ['image-rendering'] | ||
}) | ||
) | ||
f(crispedges, { match: /a x #2/ }, browsers => | ||
f(prefixCrispedges, { match: /a x #2/ }, browsers => | ||
prefix(['image-rendering'], { | ||
feature: 'css-crisp-edges', | ||
browsers | ||
browsers, | ||
feature: 'css-crisp-edges' | ||
}) | ||
@@ -788,5 +917,5 @@ ) | ||
// Logical Properties | ||
let logicalProps = require('caniuse-lite/data/features/css-logical-props') | ||
let prefixLogicalProps = require('caniuse-lite/data/features/css-logical-props') | ||
f(logicalProps, browsers => | ||
f(prefixLogicalProps, browsers => | ||
prefix( | ||
@@ -802,4 +931,4 @@ [ | ||
{ | ||
feature: 'css-logical-props', | ||
browsers | ||
browsers, | ||
feature: 'css-logical-props' | ||
} | ||
@@ -809,3 +938,3 @@ ) | ||
f(logicalProps, { match: /x\s#2/ }, browsers => | ||
f(prefixLogicalProps, { match: /x\s#2/ }, browsers => | ||
prefix( | ||
@@ -821,4 +950,4 @@ [ | ||
{ | ||
feature: 'css-logical-props', | ||
browsers | ||
browsers, | ||
feature: 'css-logical-props' | ||
} | ||
@@ -829,8 +958,8 @@ ) | ||
// CSS appearance | ||
let appearance = require('caniuse-lite/data/features/css-appearance') | ||
let prefixAppearance = require('caniuse-lite/data/features/css-appearance') | ||
f(appearance, { match: /#2|x/ }, browsers => | ||
f(prefixAppearance, { match: /#2|x/ }, browsers => | ||
prefix(['appearance'], { | ||
feature: 'css-appearance', | ||
browsers | ||
browsers, | ||
feature: 'css-appearance' | ||
}) | ||
@@ -840,3 +969,5 @@ ) | ||
// CSS Scroll snap points | ||
f(require('caniuse-lite/data/features/css-snappoints'), browsers => | ||
let prefixSnappoints = require('caniuse-lite/data/features/css-snappoints') | ||
f(prefixSnappoints, browsers => | ||
prefix( | ||
@@ -851,4 +982,4 @@ [ | ||
{ | ||
feature: 'css-snappoints', | ||
browsers | ||
browsers, | ||
feature: 'css-snappoints' | ||
} | ||
@@ -859,6 +990,8 @@ ) | ||
// CSS Regions | ||
f(require('caniuse-lite/data/features/css-regions'), browsers => | ||
let prefixRegions = require('caniuse-lite/data/features/css-regions') | ||
f(prefixRegions, browsers => | ||
prefix(['flow-into', 'flow-from', 'region-fragment'], { | ||
feature: 'css-regions', | ||
browsers | ||
browsers, | ||
feature: 'css-regions' | ||
}) | ||
@@ -868,4 +1001,8 @@ ) | ||
// CSS image-set | ||
f(require('caniuse-lite/data/features/css-image-set'), browsers => | ||
let prefixImageSet = require('caniuse-lite/data/features/css-image-set') | ||
f(prefixImageSet, browsers => | ||
prefix(['image-set'], { | ||
browsers, | ||
feature: 'css-image-set', | ||
props: [ | ||
@@ -881,5 +1018,3 @@ 'background', | ||
'content' | ||
], | ||
feature: 'css-image-set', | ||
browsers | ||
] | ||
}) | ||
@@ -889,8 +1024,8 @@ ) | ||
// Writing Mode | ||
let writingMode = require('caniuse-lite/data/features/css-writing-mode') | ||
let prefixWritingMode = require('caniuse-lite/data/features/css-writing-mode') | ||
f(writingMode, { match: /a|x/ }, browsers => | ||
f(prefixWritingMode, { match: /a|x/ }, browsers => | ||
prefix(['writing-mode'], { | ||
feature: 'css-writing-mode', | ||
browsers | ||
browsers, | ||
feature: 'css-writing-mode' | ||
}) | ||
@@ -900,4 +1035,8 @@ ) | ||
// Cross-Fade Function | ||
f(require('caniuse-lite/data/features/css-cross-fade'), browsers => | ||
let prefixCrossFade = require('caniuse-lite/data/features/css-cross-fade') | ||
f(prefixCrossFade, browsers => | ||
prefix(['cross-fade'], { | ||
browsers, | ||
feature: 'css-cross-fade', | ||
props: [ | ||
@@ -912,5 +1051,3 @@ 'background', | ||
'mask-image' | ||
], | ||
feature: 'css-cross-fade', | ||
browsers | ||
] | ||
}) | ||
@@ -920,7 +1057,9 @@ ) | ||
// Read Only selector | ||
f(require('caniuse-lite/data/features/css-read-only-write'), browsers => | ||
let prefixReadOnly = require('caniuse-lite/data/features/css-read-only-write') | ||
f(prefixReadOnly, browsers => | ||
prefix([':read-only', ':read-write'], { | ||
selector: true, | ||
browsers, | ||
feature: 'css-read-only-write', | ||
browsers | ||
selector: true | ||
}) | ||
@@ -930,3 +1069,5 @@ ) | ||
// Text Emphasize | ||
f(require('caniuse-lite/data/features/text-emphasis'), browsers => | ||
let prefixTextEmphasis = require('caniuse-lite/data/features/text-emphasis') | ||
f(prefixTextEmphasis, browsers => | ||
prefix( | ||
@@ -940,4 +1081,4 @@ [ | ||
{ | ||
feature: 'text-emphasis', | ||
browsers | ||
browsers, | ||
feature: 'text-emphasis' | ||
} | ||
@@ -948,9 +1089,9 @@ ) | ||
// CSS Grid Layout | ||
let grid = require('caniuse-lite/data/features/css-grid') | ||
let prefixGrid = require('caniuse-lite/data/features/css-grid') | ||
f(grid, browsers => { | ||
f(prefixGrid, browsers => { | ||
prefix(['display-grid', 'inline-grid'], { | ||
props: ['display'], | ||
browsers, | ||
feature: 'css-grid', | ||
browsers | ||
props: ['display'] | ||
}) | ||
@@ -973,4 +1114,4 @@ prefix( | ||
{ | ||
feature: 'css-grid', | ||
browsers | ||
browsers, | ||
feature: 'css-grid' | ||
} | ||
@@ -980,6 +1121,6 @@ ) | ||
f(grid, { match: /a x/ }, browsers => | ||
f(prefixGrid, { match: /a x/ }, browsers => | ||
prefix(['grid-column-align', 'grid-row-align'], { | ||
feature: 'css-grid', | ||
browsers | ||
browsers, | ||
feature: 'css-grid' | ||
}) | ||
@@ -989,6 +1130,8 @@ ) | ||
// CSS text-spacing | ||
f(require('caniuse-lite/data/features/css-text-spacing'), browsers => | ||
let prefixTextSpacing = require('caniuse-lite/data/features/css-text-spacing') | ||
f(prefixTextSpacing, browsers => | ||
prefix(['text-spacing'], { | ||
feature: 'css-text-spacing', | ||
browsers | ||
browsers, | ||
feature: 'css-text-spacing' | ||
}) | ||
@@ -998,7 +1141,9 @@ ) | ||
// :any-link selector | ||
f(require('caniuse-lite/data/features/css-any-link'), browsers => | ||
let prefixAnyLink = require('caniuse-lite/data/features/css-any-link') | ||
f(prefixAnyLink, browsers => | ||
prefix([':any-link'], { | ||
selector: true, | ||
browsers, | ||
feature: 'css-any-link', | ||
browsers | ||
selector: true | ||
}) | ||
@@ -1008,25 +1153,30 @@ ) | ||
// unicode-bidi | ||
let bidi = require('caniuse-lite/data/features/css-unicode-bidi') | ||
f(bidi, browsers => | ||
let bidiIsolate = require('caniuse-lite/data/features/mdn-css-unicode-bidi-isolate') | ||
f(bidiIsolate, browsers => | ||
prefix(['isolate'], { | ||
props: ['unicode-bidi'], | ||
browsers, | ||
feature: 'css-unicode-bidi', | ||
browsers | ||
props: ['unicode-bidi'] | ||
}) | ||
) | ||
f(bidi, { match: /y x|a x #2/ }, browsers => | ||
let bidiPlaintext = require('caniuse-lite/data/features/mdn-css-unicode-bidi-plaintext') | ||
f(bidiPlaintext, browsers => | ||
prefix(['plaintext'], { | ||
props: ['unicode-bidi'], | ||
browsers, | ||
feature: 'css-unicode-bidi', | ||
browsers | ||
props: ['unicode-bidi'] | ||
}) | ||
) | ||
f(bidi, { match: /y x/ }, browsers => | ||
let bidiOverride = require('caniuse-lite/data/features/mdn-css-unicode-bidi-isolate-override') | ||
f(bidiOverride, { match: /y x/ }, browsers => | ||
prefix(['isolate-override'], { | ||
props: ['unicode-bidi'], | ||
browsers, | ||
feature: 'css-unicode-bidi', | ||
browsers | ||
props: ['unicode-bidi'] | ||
}) | ||
@@ -1036,25 +1186,29 @@ ) | ||
// overscroll-behavior selector | ||
let over = require('caniuse-lite/data/features/css-overscroll-behavior') | ||
let prefixOverscroll = require('caniuse-lite/data/features/css-overscroll-behavior') | ||
f(over, { match: /a #1/ }, browsers => | ||
f(prefixOverscroll, { match: /a #1/ }, browsers => | ||
prefix(['overscroll-behavior'], { | ||
feature: 'css-overscroll-behavior', | ||
browsers | ||
browsers, | ||
feature: 'css-overscroll-behavior' | ||
}) | ||
) | ||
// color-adjust | ||
f(require('caniuse-lite/data/features/css-color-adjust'), browsers => | ||
prefix(['color-adjust'], { | ||
feature: 'css-color-adjust', | ||
browsers | ||
// text-orientation | ||
let prefixTextOrientation = require('caniuse-lite/data/features/css-text-orientation') | ||
f(prefixTextOrientation, browsers => | ||
prefix(['text-orientation'], { | ||
browsers, | ||
feature: 'css-text-orientation' | ||
}) | ||
) | ||
// text-orientation | ||
f(require('caniuse-lite/data/features/css-text-orientation'), browsers => | ||
prefix(['text-orientation'], { | ||
feature: 'css-text-orientation', | ||
browsers | ||
// print-color-adjust | ||
let prefixPrintAdjust = require('caniuse-lite/data/features/css-print-color-adjust') | ||
f(prefixPrintAdjust, browsers => | ||
prefix(['print-color-adjust', 'color-adjust'], { | ||
browsers, | ||
feature: 'css-print-color-adjust' | ||
}) | ||
) |
@@ -7,3 +7,3 @@ let Prefixer = require('./prefixer') | ||
*/ | ||
add (rule, prefix) { | ||
add(rule, prefix) { | ||
let prefixed = prefix + rule.name | ||
@@ -25,3 +25,3 @@ | ||
*/ | ||
process (node) { | ||
process(node) { | ||
let parent = this.parentPrefix(node) | ||
@@ -28,0 +28,0 @@ |
import { Plugin } from 'postcss' | ||
import { Stats } from 'browserslist' | ||
declare function autoprefixer<T extends string[]> ( | ||
declare function autoprefixer<T extends string[]>( | ||
...args: [...T, autoprefixer.Options] | ||
): Plugin & autoprefixer.ExportedAPI | ||
declare function autoprefixer ( | ||
declare function autoprefixer( | ||
browsers: string[], | ||
@@ -13,3 +13,3 @@ options?: autoprefixer.Options | ||
declare function autoprefixer ( | ||
declare function autoprefixer( | ||
options?: autoprefixer.Options | ||
@@ -16,0 +16,0 @@ ): Plugin & autoprefixer.ExportedAPI |
let browserslist = require('browserslist') | ||
let { agents } = require('caniuse-lite') | ||
let colorette = require('colorette') | ||
let { agents } = require('caniuse-lite/dist/unpacker/agents') | ||
let pico = require('picocolors') | ||
let Browsers = require('./browsers') | ||
let Prefixes = require('./prefixes') | ||
let data = require('../data/prefixes') | ||
let info = require('./info') | ||
let dataPrefixes = require('../data/prefixes') | ||
let getInfo = require('./info') | ||
let autoprefixerData = { browsers: agents, prefixes: dataPrefixes } | ||
const WARNING = | ||
@@ -25,3 +27,3 @@ '\n' + | ||
function isPlainObject (obj) { | ||
function isPlainObject(obj) { | ||
return Object.prototype.toString.apply(obj) === '[object Object]' | ||
@@ -32,3 +34,3 @@ } | ||
function timeCapsule (result, prefixes) { | ||
function timeCapsule(result, prefixes) { | ||
if (prefixes.browsers.selected.length === 0) { | ||
@@ -43,12 +45,19 @@ return | ||
} | ||
/* istanbul ignore next */ | ||
/* c8 ignore next 11 */ | ||
result.warn( | ||
'Greetings, time traveller. ' + | ||
'We are in the golden age of prefix-less CSS, ' + | ||
'where Autoprefixer is no longer needed for your stylesheet.' | ||
'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 = (...reqs) => { | ||
module.exports = plugin | ||
function plugin(...reqs) { | ||
let options | ||
@@ -85,11 +94,5 @@ if (reqs.length === 1 && isPlainObject(reqs[0])) { | ||
if (typeof console !== 'undefined' && console.warn) { | ||
if (colorette.red) { | ||
console.warn( | ||
colorette.red( | ||
WARNING.replace(/`[^`]+`/g, i => colorette.yellow(i.slice(1, -1))) | ||
) | ||
) | ||
} else { | ||
console.warn(WARNING) | ||
} | ||
console.warn( | ||
pico.red(WARNING.replace(/`[^`]+`/g, i => pico.yellow(i.slice(1, -1)))) | ||
) | ||
} | ||
@@ -100,9 +103,9 @@ reqs = options.browsers | ||
let brwlstOpts = { | ||
env: options.env, | ||
ignoreUnknownVersions: options.ignoreUnknownVersions, | ||
stats: options.stats, | ||
env: options.env | ||
stats: options.stats | ||
} | ||
function loadPrefixes (opts) { | ||
let d = module.exports.data | ||
function loadPrefixes(opts) { | ||
let d = autoprefixerData | ||
let browsers = new Browsers(d.browsers, reqs, opts, brwlstOpts) | ||
@@ -119,12 +122,21 @@ let key = browsers.selected.join(', ') + JSON.stringify(options) | ||
return { | ||
browsers: reqs, | ||
info(opts) { | ||
opts = opts || {} | ||
opts.from = opts.from || process.cwd() | ||
return getInfo(loadPrefixes(opts)) | ||
}, | ||
options, | ||
postcssPlugin: 'autoprefixer', | ||
prepare (result) { | ||
prepare(result) { | ||
let prefixes = loadPrefixes({ | ||
from: result.opts.from, | ||
env: options.env | ||
env: options.env, | ||
from: result.opts.from | ||
}) | ||
return { | ||
OnceExit (root) { | ||
OnceExit(root) { | ||
timeCapsule(result, prefixes) | ||
@@ -139,16 +151,7 @@ if (options.remove !== false) { | ||
} | ||
}, | ||
info (opts) { | ||
opts = opts || {} | ||
opts.from = opts.from || process.cwd() | ||
return info(loadPrefixes(opts)) | ||
}, | ||
options, | ||
browsers: reqs | ||
} | ||
} | ||
} | ||
module.exports.postcss = true | ||
plugin.postcss = true | ||
@@ -158,3 +161,3 @@ /** | ||
*/ | ||
module.exports.data = { browsers: agents, prefixes: data } | ||
plugin.data = autoprefixerData | ||
@@ -164,3 +167,3 @@ /** | ||
*/ | ||
module.exports.defaults = browserslist.defaults | ||
plugin.defaults = browserslist.defaults | ||
@@ -170,2 +173,2 @@ /** | ||
*/ | ||
module.exports.info = () => module.exports().info() | ||
plugin.info = () => plugin().info() |
@@ -1,2 +0,2 @@ | ||
function last (array) { | ||
function last(array) { | ||
return array[array.length - 1] | ||
@@ -9,3 +9,3 @@ } | ||
*/ | ||
parse (str) { | ||
parse(str) { | ||
let current = [''] | ||
@@ -38,3 +38,3 @@ let stack = [current] | ||
*/ | ||
stringify (ast) { | ||
stringify(ast) { | ||
let result = '' | ||
@@ -41,0 +41,0 @@ for (let i of ast) { |
let browserslist = require('browserslist') | ||
let agents = require('caniuse-lite').agents | ||
let { agents } = require('caniuse-lite/dist/unpacker/agents') | ||
@@ -7,6 +7,13 @@ 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 () { | ||
static prefixes() { | ||
if (this.prefixesCache) { | ||
@@ -31,3 +38,3 @@ return this.prefixesCache | ||
*/ | ||
static withPrefix (value) { | ||
static withPrefix(value) { | ||
if (!this.prefixesRegexp) { | ||
@@ -40,7 +47,7 @@ this.prefixesRegexp = new RegExp(this.prefixes().join('|')) | ||
constructor (data, requirements, options, browserslistOpts) { | ||
this.data = data | ||
this.options = options || {} | ||
this.browserslistOpts = browserslistOpts || {} | ||
this.selected = this.parse(requirements) | ||
/** | ||
* Is browser is selected by requirements | ||
*/ | ||
isSelected(browser) { | ||
return this.selected.includes(browser) | ||
} | ||
@@ -51,3 +58,3 @@ | ||
*/ | ||
parse (requirements) { | ||
parse(requirements) { | ||
let opts = {} | ||
@@ -64,3 +71,3 @@ for (let i in this.browserslistOpts) { | ||
*/ | ||
prefix (browser) { | ||
prefix(browser) { | ||
let [name, version] = browser.split(' ') | ||
@@ -75,11 +82,4 @@ let data = this.data[name] | ||
} | ||
/** | ||
* Is browser is selected by requirements | ||
*/ | ||
isSelected (browser) { | ||
return this.selected.includes(browser) | ||
} | ||
} | ||
module.exports = Browsers |
@@ -7,54 +7,66 @@ let Prefixer = require('./prefixer') | ||
/** | ||
* Always true, because we already get prefixer by property name | ||
* Clone and add prefixes for declaration | ||
*/ | ||
check (/* decl */) { | ||
return true | ||
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) | ||
} | ||
/** | ||
* Return prefixed version of property | ||
* Calculate indentation to create visual cascade | ||
*/ | ||
prefixed (prop, prefix) { | ||
return prefix + prop | ||
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 | ||
} | ||
/** | ||
* Return unprefixed version of property | ||
* Always true, because we already get prefixer by property name | ||
*/ | ||
normalize (prop) { | ||
return prop | ||
check(/* decl */) { | ||
return true | ||
} | ||
/** | ||
* Check `value`, that it contain other prefixes, rather than `prefix` | ||
* Clone and insert new declaration | ||
*/ | ||
otherPrefixes (value, prefix) { | ||
for (let other of Browsers.prefixes()) { | ||
if (other === prefix) { | ||
continue | ||
} | ||
if (value.includes(other)) { | ||
return true | ||
} | ||
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 | ||
} | ||
return false | ||
} | ||
/** | ||
* Set prefix to declaration | ||
*/ | ||
set (decl, prefix) { | ||
decl.prop = this.prefixed(decl.prop, prefix) | ||
return decl | ||
if (this.needCascade(decl)) { | ||
cloned.raws.before = this.calcBefore(prefixes, decl, prefix) | ||
} | ||
return decl.parent.insertBefore(decl, cloned) | ||
} | ||
/** | ||
* Should we use visual cascade for prefixes | ||
* Did this declaration has this prefix above | ||
*/ | ||
needCascade (decl) { | ||
if (!decl._autoprefixerCascade) { | ||
decl._autoprefixerCascade = | ||
this.all.options.cascade !== false && decl.raw('before').includes('\n') | ||
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 decl._autoprefixerCascade | ||
return already | ||
} | ||
@@ -65,3 +77,3 @@ | ||
*/ | ||
maxPrefixed (prefixes, decl) { | ||
maxPrefixed(prefixes, decl) { | ||
if (decl._autoprefixerMax) { | ||
@@ -84,78 +96,46 @@ return decl._autoprefixerMax | ||
/** | ||
* Calculate indentation to create visual cascade | ||
* Should we use visual cascade for prefixes | ||
*/ | ||
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('') | ||
needCascade(decl) { | ||
if (!decl._autoprefixerCascade) { | ||
decl._autoprefixerCascade = | ||
this.all.options.cascade !== false && decl.raw('before').includes('\n') | ||
} | ||
return before | ||
return decl._autoprefixerCascade | ||
} | ||
/** | ||
* Remove visual cascade | ||
* Return unprefixed version of property | ||
*/ | ||
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') | ||
normalize(prop) { | ||
return prop | ||
} | ||
/** | ||
* Clone and insert new declaration | ||
* Return list of prefixed properties to clean old prefixes | ||
*/ | ||
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) | ||
old(prop, prefix) { | ||
return [this.prefixed(prop, prefix)] | ||
} | ||
/** | ||
* Did this declaration has this prefix above | ||
* Check `value`, that it contain other prefixes, rather than `prefix` | ||
*/ | ||
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) | ||
otherPrefixes(value, prefix) { | ||
for (let other of Browsers.prefixes()) { | ||
if (other === prefix) { | ||
continue | ||
} | ||
if (value.includes(other)) { | ||
return value.replace(/var\([^)]+\)/, '').includes(other) | ||
} | ||
} | ||
return already | ||
return false | ||
} | ||
/** | ||
* Clone and add prefixes for declaration | ||
* Return prefixed version of property | ||
*/ | ||
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) | ||
prefixed(prop, prefix) { | ||
return prefix + prop | ||
} | ||
@@ -166,3 +146,3 @@ | ||
*/ | ||
process (decl, result) { | ||
process(decl, result) { | ||
if (!this.needCascade(decl)) { | ||
@@ -184,9 +164,29 @@ super.process(decl, result) | ||
/** | ||
* Return list of prefixed properties to clean old prefixes | ||
* Remove visual cascade | ||
*/ | ||
old (prop, prefix) { | ||
return [this.prefixed(prop, prefix)] | ||
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 |
@@ -6,5 +6,12 @@ let flexSpec = require('./flex-spec') | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize() { | ||
return 'align-content' | ||
} | ||
/** | ||
* Change property name for 2012 spec | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -19,12 +26,5 @@ ;[spec, prefix] = flexSpec(prefix) | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize () { | ||
return 'align-content' | ||
} | ||
/** | ||
* Change value for 2012 spec and ignore prefix for 2009 | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec = flexSpec(prefix)[0] | ||
@@ -47,6 +47,6 @@ if (spec === 2012) { | ||
'flex-start': 'start', | ||
'space-between': 'justify', | ||
'space-around': 'distribute' | ||
'space-around': 'distribute', | ||
'space-between': 'justify' | ||
} | ||
module.exports = AlignContent |
@@ -6,5 +6,12 @@ let flexSpec = require('./flex-spec') | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize() { | ||
return 'align-items' | ||
} | ||
/** | ||
* Change property name for 2009 and 2012 specs | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -22,12 +29,5 @@ ;[spec, prefix] = flexSpec(prefix) | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize () { | ||
return 'align-items' | ||
} | ||
/** | ||
* Change value for 2009 and 2012 specs | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec = flexSpec(prefix)[0] | ||
@@ -34,0 +34,0 @@ if (spec === 2009 || spec === 2012) { |
@@ -5,3 +5,3 @@ let flexSpec = require('./flex-spec') | ||
class AlignSelf extends Declaration { | ||
check (decl) { | ||
check(decl) { | ||
return ( | ||
@@ -16,5 +16,12 @@ decl.parent && | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize() { | ||
return 'align-self' | ||
} | ||
/** | ||
* Change property name for 2012 specs | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -29,12 +36,5 @@ ;[spec, prefix] = flexSpec(prefix) | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize () { | ||
return 'align-self' | ||
} | ||
/** | ||
* Change value for 2012 spec and ignore prefix for 2009 | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec = flexSpec(prefix)[0] | ||
@@ -41,0 +41,0 @@ if (spec === 2012) { |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
return !decl.value.split(/\s+/).some(i => { | ||
@@ -10,0 +10,0 @@ let lower = i.toLowerCase() |
@@ -5,3 +5,3 @@ let Declaration = require('../declaration') | ||
class Appearance extends Declaration { | ||
constructor (name, prefixes, all) { | ||
constructor(name, prefixes, all) { | ||
super(name, prefixes, all) | ||
@@ -8,0 +8,0 @@ |
@@ -5,3 +5,3 @@ let Declaration = require('../declaration') | ||
class BackdropFilter extends Declaration { | ||
constructor (name, prefixes, all) { | ||
constructor(name, prefixes, all) { | ||
super(name, prefixes, all) | ||
@@ -8,0 +8,0 @@ |
@@ -5,3 +5,3 @@ let Declaration = require('../declaration') | ||
class BackgroundClip extends Declaration { | ||
constructor (name, prefixes, all) { | ||
constructor(name, prefixes, all) { | ||
super(name, prefixes, all) | ||
@@ -18,3 +18,3 @@ | ||
check (decl) { | ||
check(decl) { | ||
return decl.value.toLowerCase() === 'text' | ||
@@ -21,0 +21,0 @@ } |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let value = decl.value.toLowerCase() | ||
@@ -10,0 +10,0 @@ if ( |
@@ -5,19 +5,19 @@ let Declaration = require('../declaration') | ||
/** | ||
* Use old syntax for -moz- and -webkit- | ||
* Return property name by spec | ||
*/ | ||
prefixed (prop, prefix) { | ||
if (prop.includes('-start')) { | ||
return prefix + prop.replace('-block-start', '-before') | ||
normalize(prop) { | ||
if (prop.includes('-before')) { | ||
return prop.replace('-before', '-block-start') | ||
} | ||
return prefix + prop.replace('-block-end', '-after') | ||
return prop.replace('-after', '-block-end') | ||
} | ||
/** | ||
* Return property name by spec | ||
* Use old syntax for -moz- and -webkit- | ||
*/ | ||
normalize (prop) { | ||
if (prop.includes('-before')) { | ||
return prop.replace('-before', '-block-start') | ||
prefixed(prop, prefix) { | ||
if (prop.includes('-start')) { | ||
return prefix + prop.replace('-block-start', '-before') | ||
} | ||
return prop.replace('-after', '-block-end') | ||
return prefix + prop.replace('-block-end', '-after') | ||
} | ||
@@ -24,0 +24,0 @@ } |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
decl.value = decl.value.replace(/\s+fill(\s)/, '$1') | ||
@@ -10,0 +10,0 @@ return super.set(decl, prefix) |
@@ -5,5 +5,12 @@ let Declaration = require('../declaration') | ||
/** | ||
* Return unprefixed version of property | ||
*/ | ||
normalize(prop) { | ||
return BorderRadius.toNormal[prop] || prop | ||
} | ||
/** | ||
* Change syntax, when add Mozilla prefix | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
if (prefix === '-moz-') { | ||
@@ -14,9 +21,2 @@ return prefix + (BorderRadius.toMozilla[prop] || prop) | ||
} | ||
/** | ||
* Return unprefixed version of property | ||
*/ | ||
normalize (prop) { | ||
return BorderRadius.toNormal[prop] || prop | ||
} | ||
} | ||
@@ -23,0 +23,0 @@ |
@@ -5,6 +5,12 @@ let Declaration = require('../declaration') | ||
/** | ||
* Change name for -webkit- and -moz- prefix | ||
* Don’t prefix some values | ||
*/ | ||
prefixed (prop, prefix) { | ||
return `${prefix}column-${prop}` | ||
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) | ||
} | ||
@@ -15,3 +21,3 @@ | ||
*/ | ||
normalize (prop) { | ||
normalize(prop) { | ||
if (prop.includes('inside')) { | ||
@@ -27,5 +33,12 @@ return 'break-inside' | ||
/** | ||
* 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) { | ||
set(decl, prefix) { | ||
if ( | ||
@@ -39,15 +52,2 @@ (decl.prop === 'break-inside' && decl.value === 'avoid-column') || | ||
} | ||
/** | ||
* 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) | ||
} | ||
} | ||
@@ -54,0 +54,0 @@ |
@@ -6,3 +6,3 @@ let list = require('postcss').list | ||
class CrossFade extends Value { | ||
replace (string, prefix) { | ||
replace(string, prefix) { | ||
return list | ||
@@ -9,0 +9,0 @@ .space(string) |
@@ -6,3 +6,3 @@ let flexSpec = require('./flex-spec') | ||
class DisplayFlex extends Value { | ||
constructor (name, prefixes) { | ||
constructor(name, prefixes) { | ||
super(name, prefixes) | ||
@@ -17,3 +17,3 @@ if (name === 'display-flex') { | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
return decl.prop === 'display' && decl.value === this.name | ||
@@ -23,5 +23,14 @@ } | ||
/** | ||
* 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) { | ||
prefixed(prefix) { | ||
let spec, value | ||
@@ -52,14 +61,5 @@ ;[spec, prefix] = flexSpec(prefix) | ||
*/ | ||
replace (string, prefix) { | ||
replace(string, prefix) { | ||
return this.prefixed(prefix) | ||
} | ||
/** | ||
* Change value for old specs | ||
*/ | ||
old (prefix) { | ||
let prefixed = this.prefixed(prefix) | ||
if (!prefixed) return undefined | ||
return new OldValue(this.name, prefixed) | ||
} | ||
} | ||
@@ -66,0 +66,0 @@ |
let Value = require('../value') | ||
class DisplayGrid extends Value { | ||
constructor (name, prefixes) { | ||
constructor(name, prefixes) { | ||
super(name, prefixes) | ||
@@ -14,3 +14,3 @@ if (name === 'display-grid') { | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
return decl.prop === 'display' && decl.value === this.name | ||
@@ -17,0 +17,0 @@ } |
let Value = require('../value') | ||
class FilterValue extends Value { | ||
constructor (name, prefixes) { | ||
constructor(name, prefixes) { | ||
super(name, prefixes) | ||
@@ -6,0 +6,0 @@ if (name === 'filter-function') { |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
let v = decl.value | ||
@@ -10,0 +10,0 @@ return ( |
@@ -8,3 +8,3 @@ let flexSpec = require('./flex-spec') | ||
*/ | ||
normalize () { | ||
normalize() { | ||
return 'flex-basis' | ||
@@ -16,3 +16,3 @@ } | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -29,3 +29,3 @@ ;[spec, prefix] = flexSpec(prefix) | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec | ||
@@ -32,0 +32,0 @@ ;[spec, prefix] = flexSpec(prefix) |
@@ -6,12 +6,5 @@ let flexSpec = require('./flex-spec') | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize () { | ||
return 'flex-direction' | ||
} | ||
/** | ||
* Use two properties for 2009 spec | ||
*/ | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
let spec | ||
@@ -58,5 +51,12 @@ ;[spec, prefix] = flexSpec(prefix) | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize() { | ||
return 'flex-direction' | ||
} | ||
/** | ||
* Clean two properties for 2009 spec | ||
*/ | ||
old (prop, prefix) { | ||
old(prop, prefix) { | ||
let spec | ||
@@ -63,0 +63,0 @@ ;[spec, prefix] = flexSpec(prefix) |
@@ -8,3 +8,3 @@ let flexSpec = require('./flex-spec') | ||
*/ | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
let spec | ||
@@ -11,0 +11,0 @@ ;[spec, prefix] = flexSpec(prefix) |
@@ -8,3 +8,3 @@ let flexSpec = require('./flex-spec') | ||
*/ | ||
normalize () { | ||
normalize() { | ||
return 'flex' | ||
@@ -16,3 +16,3 @@ } | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -19,0 +19,0 @@ ;[spec, prefix] = flexSpec(prefix) |
@@ -8,3 +8,3 @@ let flexSpec = require('./flex-spec') | ||
*/ | ||
normalize () { | ||
normalize() { | ||
return 'flex-shrink' | ||
@@ -16,3 +16,3 @@ } | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -29,3 +29,3 @@ ;[spec, prefix] = flexSpec(prefix) | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec | ||
@@ -32,0 +32,0 @@ ;[spec, prefix] = flexSpec(prefix) |
@@ -8,3 +8,3 @@ let flexSpec = require('./flex-spec') | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec = flexSpec(prefix)[0] | ||
@@ -11,0 +11,0 @@ if (spec !== 2009) { |
@@ -8,5 +8,12 @@ let list = require('postcss').list | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize() { | ||
return 'flex' | ||
} | ||
/** | ||
* Change property name for 2009 spec | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -21,13 +28,6 @@ ;[spec, prefix] = flexSpec(prefix) | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize () { | ||
return 'flex' | ||
} | ||
/** | ||
* Spec 2009 supports only first argument | ||
* Spec 2012 disallows unitless basis | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec = flexSpec(prefix)[0] | ||
@@ -34,0 +34,0 @@ if (spec === 2009) { |
@@ -7,3 +7,3 @@ let Selector = require('../selector') | ||
*/ | ||
prefixed (prefix) { | ||
prefixed(prefix) { | ||
if (prefix === '-webkit-') { | ||
@@ -10,0 +10,0 @@ return ':-webkit-full-screen' |
@@ -12,131 +12,75 @@ let parser = require('postcss-value-parser') | ||
/** | ||
* Change degrees for webkit prefix | ||
* Do not add non-webkit prefixes for list-style and object | ||
*/ | ||
replace (string, prefix) { | ||
let ast = parser(string) | ||
for (let node of ast.nodes) { | ||
if (node.type === 'function' && node.value === this.name) { | ||
node.nodes = this.newDirection(node.nodes) | ||
node.nodes = this.normalize(node.nodes) | ||
if (prefix === '-webkit- old') { | ||
let changes = this.oldWebkit(node) | ||
if (!changes) { | ||
return false | ||
} | ||
} else { | ||
node.nodes = this.convertDirection(node.nodes) | ||
node.value = prefix + node.value | ||
} | ||
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 ast.toString() | ||
return undefined | ||
} | ||
/** | ||
* Replace first token | ||
* Get div token from exists parameters | ||
*/ | ||
replaceFirst (params, ...words) { | ||
let prefix = words.map(i => { | ||
if (i === ' ') { | ||
return { type: 'space', value: i } | ||
cloneDiv(params) { | ||
for (let i of params) { | ||
if (i.type === 'div' && i.value === ',') { | ||
return i | ||
} | ||
return { type: 'word', value: i } | ||
}) | ||
return prefix.concat(params.slice(1)) | ||
} | ||
/** | ||
* Convert angle unit to deg | ||
*/ | ||
normalizeUnit (str, full) { | ||
let num = parseFloat(str) | ||
let deg = (num / full) * 360 | ||
return `${deg}deg` | ||
} | ||
/** | ||
* Normalize angle | ||
*/ | ||
normalize (nodes) { | ||
if (!nodes[0]) return nodes | ||
if (/-?\d+(.\d+)?grad/.test(nodes[0].value)) { | ||
nodes[0].value = this.normalizeUnit(nodes[0].value, 400) | ||
} else if (/-?\d+(.\d+)?rad/.test(nodes[0].value)) { | ||
nodes[0].value = this.normalizeUnit(nodes[0].value, 2 * Math.PI) | ||
} else if (/-?\d+(.\d+)?turn/.test(nodes[0].value)) { | ||
nodes[0].value = this.normalizeUnit(nodes[0].value, 1) | ||
} else if (nodes[0].value.includes('deg')) { | ||
let num = parseFloat(nodes[0].value) | ||
num = range.wrap(0, 360, num) | ||
nodes[0].value = `${num}deg` | ||
} | ||
if (nodes[0].value === '0deg') { | ||
nodes = this.replaceFirst(nodes, 'to', ' ', 'top') | ||
} else if (nodes[0].value === '90deg') { | ||
nodes = this.replaceFirst(nodes, 'to', ' ', 'right') | ||
} else if (nodes[0].value === '180deg') { | ||
nodes = this.replaceFirst(nodes, 'to', ' ', 'bottom') | ||
} else if (nodes[0].value === '270deg') { | ||
nodes = this.replaceFirst(nodes, 'to', ' ', 'left') | ||
} | ||
return nodes | ||
return { after: ' ', type: 'div', value: ',' } | ||
} | ||
/** | ||
* Replace old direction to new | ||
* Change colors syntax to old webkit | ||
*/ | ||
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: ' ' | ||
colorStops(params) { | ||
let result = [] | ||
for (let i = 0; i < params.length; i++) { | ||
let pos | ||
let param = params[i] | ||
let item | ||
if (i === 0) { | ||
continue | ||
} | ||
) | ||
for (let i = 2; i < params.length; i++) { | ||
if (params[i].type === 'div') { | ||
break | ||
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 | ||
} | ||
if (params[i].type === 'word') { | ||
params[i].value = this.revertDirection(params[i].value) | ||
} | ||
} | ||
return params | ||
} | ||
/** | ||
* Look for at word | ||
*/ | ||
isRadial (params) { | ||
let state = 'before' | ||
for (let param of params) { | ||
if (state === 'before' && param.type === 'space') { | ||
state = 'at' | ||
} else if (state === 'at' && param.value === 'at') { | ||
state = 'after' | ||
} else if (state === 'after' && param.type === 'space') { | ||
return true | ||
} else if (param.type === 'div') { | ||
break | ||
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 { | ||
state = 'before' | ||
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 false | ||
return result | ||
} | ||
@@ -147,3 +91,3 @@ | ||
*/ | ||
convertDirection (params) { | ||
convertDirection(params) { | ||
if (params.length > 0) { | ||
@@ -162,5 +106,16 @@ if (params[0].value === 'to') { | ||
/** | ||
* 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) { | ||
fixDirection(params) { | ||
params.splice(0, 2) | ||
@@ -179,16 +134,5 @@ | ||
/** | ||
* Add 90 degrees | ||
*/ | ||
fixAngle (params) { | ||
let first = params[0].value | ||
first = parseFloat(first) | ||
first = Math.abs(450 - first) % 360 | ||
first = this.roundFloat(first, 3) | ||
params[0].value = `${first}deg` | ||
} | ||
/** | ||
* Fix radial direction syntax | ||
*/ | ||
fixRadial (params) { | ||
fixRadial(params) { | ||
let first = [] | ||
@@ -223,17 +167,165 @@ let second = [] | ||
revertDirection (word) { | ||
return Gradient.directions[word.toLowerCase()] || word | ||
/** | ||
* 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 | ||
} | ||
/** | ||
* Round float and save digits under dot | ||
* Replace old direction to new | ||
*/ | ||
roundFloat (float, digits) { | ||
return parseFloat(float.toFixed(digits)) | ||
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) { | ||
oldWebkit(node) { | ||
let { nodes } = node | ||
@@ -282,121 +374,48 @@ let string = parser.stringify(node.nodes) | ||
/** | ||
* Change direction syntax to old webkit | ||
* Change degrees for webkit prefix | ||
*/ | ||
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()) | ||
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 | ||
} | ||
} | ||
words = words.join(' ') | ||
let old = Gradient.oldDirections[words] || words | ||
params[0] = [{ type: 'word', value: old }, div] | ||
return params[0] | ||
} | ||
return ast.toString() | ||
} | ||
/** | ||
* Get div token from exists parameters | ||
* Replace first token | ||
*/ | ||
cloneDiv (params) { | ||
for (let i of params) { | ||
if (i.type === 'div' && i.value === ',') { | ||
return i | ||
replaceFirst(params, ...words) { | ||
let prefix = words.map(i => { | ||
if (i === ' ') { | ||
return { type: 'space', value: i } | ||
} | ||
} | ||
return { type: 'div', value: ',', after: ' ' } | ||
return { type: 'word', value: i } | ||
}) | ||
return prefix.concat(params.slice(1)) | ||
} | ||
/** | ||
* 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 | ||
revertDirection(word) { | ||
return Gradient.directions[word.toLowerCase()] || word | ||
} | ||
/** | ||
* Remove old WebKit gradient too | ||
* Round float and save digits under dot | ||
*/ | ||
old (prefix) { | ||
if (prefix === '-webkit-') { | ||
let type = this.name === 'linear-gradient' ? 'linear' : 'radial' | ||
let string = '-gradient' | ||
let regexp = utils.regexp( | ||
`-webkit-(${type}-gradient|gradient\\(\\s*${type})`, | ||
false | ||
) | ||
return new OldValue(this.name, prefix + this.name, string, regexp) | ||
} else { | ||
return super.old(prefix) | ||
} | ||
roundFloat(float, digits) { | ||
return parseFloat(float.toFixed(digits)) | ||
} | ||
/** | ||
* 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 | ||
} | ||
} | ||
@@ -412,6 +431,6 @@ | ||
Gradient.directions = { | ||
top: 'bottom', | ||
bottom: 'top', | ||
left: 'right', | ||
bottom: 'top', | ||
right: 'left' | ||
right: 'left', | ||
top: 'bottom' // default value | ||
} | ||
@@ -421,17 +440,17 @@ | ||
Gradient.oldDirections = { | ||
'top': 'left bottom, left top', | ||
'bottom': 'left top, left bottom', | ||
'bottom left': 'right top, left bottom', | ||
'bottom right': 'left top, right bottom', | ||
'left': 'right top, left top', | ||
'bottom': 'left top, left bottom', | ||
'left bottom': 'right top, left bottom', | ||
'left top': 'right bottom, left top', | ||
'right': 'left top, right top', | ||
'top right': 'left bottom, 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', | ||
'right top': 'left bottom, right top', | ||
'right bottom': 'left top, right bottom', | ||
'bottom right': 'left top, right bottom', | ||
'bottom left': 'right top, left bottom', | ||
'left top': 'right bottom, left top', | ||
'left bottom': 'right top, left bottom' | ||
'top right': 'left bottom, right top' | ||
} | ||
module.exports = Gradient |
@@ -8,3 +8,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
insert (decl, prefix, prefixes, result) { | ||
insert(decl, prefix, prefixes, result) { | ||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) | ||
@@ -11,0 +11,0 @@ |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
return !decl.value.includes('flex-') && decl.value !== 'baseline' | ||
@@ -13,13 +13,13 @@ } | ||
/** | ||
* Change property name for IE | ||
* Change IE property back | ||
*/ | ||
prefixed (prop, prefix) { | ||
return prefix + 'grid-column-align' | ||
normalize() { | ||
return 'justify-self' | ||
} | ||
/** | ||
* Change IE property back | ||
* Change property name for IE | ||
*/ | ||
normalize () { | ||
return 'justify-self' | ||
prefixed(prop, prefix) { | ||
return prefix + 'grid-column-align' | ||
} | ||
@@ -26,0 +26,0 @@ } |
let Declaration = require('../declaration') | ||
let { isPureNumber } = require('../utils') | ||
@@ -7,3 +8,3 @@ class GridEnd extends Declaration { | ||
*/ | ||
insert (decl, prefix, prefixes, result) { | ||
insert(decl, prefix, prefixes, result) { | ||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) | ||
@@ -30,4 +31,8 @@ | ||
if (startDecl) { | ||
let value = Number(decl.value) - Number(startDecl.value) + '' | ||
clonedDecl.value = value | ||
if (isPureNumber(startDecl.value)) { | ||
let value = Number(decl.value) - Number(startDecl.value) + '' | ||
clonedDecl.value = value | ||
} else { | ||
return undefined | ||
} | ||
} else { | ||
@@ -34,0 +39,0 @@ decl.warn( |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
return !decl.value.includes('flex-') && decl.value !== 'baseline' | ||
@@ -13,13 +13,13 @@ } | ||
/** | ||
* Change property name for IE | ||
* Change IE property back | ||
*/ | ||
prefixed (prop, prefix) { | ||
return prefix + 'grid-row-align' | ||
normalize() { | ||
return 'align-self' | ||
} | ||
/** | ||
* Change IE property back | ||
* Change property name for IE | ||
*/ | ||
normalize () { | ||
return 'align-self' | ||
prefixed(prop, prefix) { | ||
return prefix + 'grid-row-align' | ||
} | ||
@@ -26,0 +26,0 @@ } |
@@ -8,3 +8,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) | ||
@@ -11,0 +11,0 @@ |
let Declaration = require('../declaration') | ||
let { | ||
prefixTrackProp, | ||
prefixTrackValue, | ||
autoplaceGridItems, | ||
getGridGap, | ||
inheritGridGap | ||
inheritGridGap, | ||
prefixTrackProp, | ||
prefixTrackValue | ||
} = require('./grid-utils') | ||
@@ -12,20 +12,3 @@ let Processor = require('../processor') | ||
class GridRowsColumns extends Declaration { | ||
/** | ||
* Change property name for IE | ||
*/ | ||
prefixed (prop, prefix) { | ||
if (prefix === '-ms-') { | ||
return prefixTrackProp({ prop, prefix }) | ||
} | ||
return super.prefixed(prop, prefix) | ||
} | ||
/** | ||
* Change IE property back | ||
*/ | ||
normalize (prop) { | ||
return prop.replace(/^grid-(rows|columns)/, 'grid-template-$1') | ||
} | ||
insert (decl, prefix, prefixes, result) { | ||
insert(decl, prefix, prefixes, result) { | ||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) | ||
@@ -60,4 +43,4 @@ | ||
let prefixValue = prefixTrackValue({ | ||
value, | ||
gap: gapValue | ||
gap: gapValue, | ||
value | ||
}) | ||
@@ -69,3 +52,3 @@ | ||
decl.cloneBefore({ | ||
prop: prefixTrackProp({ prop, prefix }), | ||
prop: prefixTrackProp({ prefix, prop }), | ||
value: prefixValue | ||
@@ -119,2 +102,19 @@ }) | ||
} | ||
/** | ||
* 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) | ||
} | ||
} | ||
@@ -121,0 +121,0 @@ |
@@ -7,5 +7,5 @@ let Declaration = require('../declaration') | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
let value = decl.value | ||
return !value.includes('/') || value.includes('span') | ||
return !value.includes('/') && !value.includes('span') | ||
} | ||
@@ -16,3 +16,3 @@ | ||
*/ | ||
normalize (prop) { | ||
normalize(prop) { | ||
return prop.replace('-start', '') | ||
@@ -24,3 +24,3 @@ } | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let result = super.prefixed(prop, prefix) | ||
@@ -27,0 +27,0 @@ if (prefix === '-ms-') { |
let Declaration = require('../declaration') | ||
let { | ||
getGridGap, | ||
inheritGridGap, | ||
parseGridAreas, | ||
warnMissedAreas, | ||
prefixTrackProp, | ||
prefixTrackValue, | ||
getGridGap, | ||
warnGridGap, | ||
inheritGridGap | ||
warnMissedAreas | ||
} = require('./grid-utils') | ||
function getGridRows (tpl) { | ||
function getGridRows(tpl) { | ||
return tpl | ||
@@ -23,3 +23,3 @@ .trim() | ||
*/ | ||
insert (decl, prefix, prefixes, result) { | ||
insert(decl, prefix, prefixes, result) { | ||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) | ||
@@ -43,4 +43,4 @@ | ||
trackDecl.cloneBefore({ | ||
prop: prefixTrackProp({ prop, prefix }), | ||
value: prefixTrackValue({ value, gap: gap.row }) | ||
prop: prefixTrackProp({ prefix, prop }), | ||
value: prefixTrackValue({ gap: gap.row, value }) | ||
}) | ||
@@ -57,7 +57,7 @@ } else { | ||
prop: '-ms-grid-rows', | ||
raws: {}, | ||
value: prefixTrackValue({ | ||
value: `repeat(${gridRows.length}, auto)`, | ||
gap: gap.row | ||
}), | ||
raws: {} | ||
gap: gap.row, | ||
value: `repeat(${gridRows.length}, auto)` | ||
}) | ||
}) | ||
@@ -68,5 +68,5 @@ } | ||
warnGridGap({ | ||
decl, | ||
gap, | ||
hasColumns, | ||
decl, | ||
result | ||
@@ -76,4 +76,4 @@ }) | ||
let areas = parseGridAreas({ | ||
rows: gridRows, | ||
gap | ||
gap, | ||
rows: gridRows | ||
}) | ||
@@ -80,0 +80,0 @@ |
let Declaration = require('../declaration') | ||
let { | ||
getGridGap, | ||
inheritGridGap, | ||
parseTemplate, | ||
warnMissedAreas, | ||
getGridGap, | ||
warnGridGap, | ||
inheritGridGap | ||
warnMissedAreas | ||
} = require('./grid-utils') | ||
@@ -14,3 +14,3 @@ | ||
*/ | ||
insert (decl, prefix, prefixes, result) { | ||
insert(decl, prefix, prefixes, result) { | ||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) | ||
@@ -30,3 +30,3 @@ | ||
let { rows, columns, areas } = parseTemplate({ | ||
let { areas, columns, rows } = parseTemplate({ | ||
decl, | ||
@@ -41,5 +41,5 @@ gap: inheritedGap || gap | ||
warnGridGap({ | ||
decl, | ||
gap, | ||
hasColumns, | ||
decl, | ||
result | ||
@@ -53,4 +53,4 @@ }) | ||
prop: '-ms-grid-rows', | ||
value: rows, | ||
raws: {} | ||
raws: {}, | ||
value: rows | ||
}) | ||
@@ -62,4 +62,4 @@ } | ||
prop: '-ms-grid-columns', | ||
value: columns, | ||
raws: {} | ||
raws: {}, | ||
value: columns | ||
}) | ||
@@ -66,0 +66,0 @@ } |
@@ -8,3 +8,3 @@ let parser = require('postcss-value-parser') | ||
function convert (value) { | ||
function convert(value) { | ||
if ( | ||
@@ -26,3 +26,5 @@ value && | ||
function translate (values, startIndex, endIndex) { | ||
exports.translate = translate | ||
function translate(values, startIndex, endIndex) { | ||
let startValue = values[startIndex] | ||
@@ -57,3 +59,5 @@ let endValue = values[endIndex] | ||
function parse (decl) { | ||
exports.parse = parse | ||
function parse(decl) { | ||
let node = parser(decl.value) | ||
@@ -77,3 +81,5 @@ | ||
function insertDecl (decl, prop, value) { | ||
exports.insertDecl = insertDecl | ||
function insertDecl(decl, prop, value) { | ||
if (value && !decl.parent.some(i => i.prop === `-ms-${prop}`)) { | ||
@@ -89,7 +95,9 @@ decl.cloneBefore({ | ||
function prefixTrackProp ({ prop, prefix }) { | ||
exports.prefixTrackProp = prefixTrackProp | ||
function prefixTrackProp({ prefix, prop }) { | ||
return prefix + prop.replace('template-', '') | ||
} | ||
function transformRepeat ({ nodes }, { gap }) { | ||
function transformRepeat({ nodes }, { gap }) { | ||
let { count, size } = nodes.reduce( | ||
@@ -105,5 +113,5 @@ (result, node) => { | ||
{ | ||
count: [], | ||
key: 'count', | ||
size: [], | ||
count: [] | ||
size: [] | ||
} | ||
@@ -131,3 +139,5 @@ ) | ||
function prefixTrackValue ({ value, gap }) { | ||
exports.prefixTrackValue = prefixTrackValue | ||
function prefixTrackValue({ gap, value }) { | ||
let result = parser(value).nodes.reduce((nodes, node) => { | ||
@@ -163,11 +173,13 @@ if (node.type === 'function' && node.value === 'repeat') { | ||
function track (start, end) { | ||
return { start, end, span: end - start } | ||
function track(start, end) { | ||
return { end, span: end - start, start } | ||
} | ||
function getColumns (line) { | ||
function getColumns(line) { | ||
return line.trim().split(/\s+/g) | ||
} | ||
function parseGridAreas ({ rows, gap }) { | ||
exports.parseGridAreas = parseGridAreas | ||
function parseGridAreas({ gap, rows }) { | ||
return rows.reduce((areas, line, rowIndex) => { | ||
@@ -207,7 +219,7 @@ if (gap.row) rowIndex *= 2 | ||
function testTrack (node) { | ||
function testTrack(node) { | ||
return node.type === 'word' && /^\[.+]$/.test(node.value) | ||
} | ||
function verifyRowSize (result) { | ||
function verifyRowSize(result) { | ||
if (result.areas.length > result.rows.length) { | ||
@@ -219,3 +231,5 @@ result.rows.push('auto') | ||
function parseTemplate ({ decl, gap }) { | ||
exports.parseTemplate = parseTemplate | ||
function parseTemplate({ decl, gap }) { | ||
let gridTemplate = parser(decl.value).nodes.reduce( | ||
@@ -247,6 +261,6 @@ (result, node) => { | ||
{ | ||
areas: [], | ||
columns: [], | ||
key: 'rows', | ||
columns: [], | ||
rows: [], | ||
areas: [] | ||
rows: [] | ||
} | ||
@@ -257,12 +271,12 @@ ) | ||
areas: parseGridAreas({ | ||
rows: gridTemplate.areas, | ||
gap | ||
gap, | ||
rows: gridTemplate.areas | ||
}), | ||
columns: prefixTrackValue({ | ||
value: gridTemplate.columns.join(' '), | ||
gap: gap.column | ||
gap: gap.column, | ||
value: gridTemplate.columns.join(' ') | ||
}), | ||
rows: prefixTrackValue({ | ||
value: gridTemplate.rows.join(' '), | ||
gap: gap.row | ||
gap: gap.row, | ||
value: gridTemplate.rows.join(' ') | ||
}) | ||
@@ -281,3 +295,3 @@ } | ||
*/ | ||
function getMSDecls (area, addRowSpan = false, addColumnSpan = false) { | ||
function getMSDecls(area, addRowSpan = false, addColumnSpan = false) { | ||
let result = [ | ||
@@ -308,3 +322,3 @@ { | ||
function getParentMedia (parent) { | ||
function getParentMedia(parent) { | ||
if (parent.type === 'atrule' && parent.name === 'media') { | ||
@@ -325,3 +339,3 @@ return parent | ||
*/ | ||
function changeDuplicateAreaSelectors (ruleSelectors, templateSelectors) { | ||
function changeDuplicateAreaSelectors(ruleSelectors, templateSelectors) { | ||
ruleSelectors = ruleSelectors.map(selector => { | ||
@@ -353,3 +367,3 @@ let selectorBySpace = list.space(selector) | ||
*/ | ||
function selectorsEqual (ruleA, ruleB) { | ||
function selectorsEqual(ruleA, ruleB) { | ||
return ruleA.selectors.some(sel => { | ||
@@ -365,3 +379,3 @@ return ruleB.selectors.includes(sel) | ||
*/ | ||
function parseGridTemplatesData (css) { | ||
function parseGridTemplatesData(css) { | ||
let parsed = [] | ||
@@ -435,8 +449,8 @@ | ||
parsed[index].rules.push({ | ||
areas, | ||
duplicateAreaNames, | ||
hasDuplicates: !hasNoDuplicates, | ||
node: rule, | ||
params: media.params, | ||
selectors: rule.selectors, | ||
node: rule, | ||
duplicateAreaNames, | ||
areas | ||
selectors: rule.selectors | ||
}) | ||
@@ -450,9 +464,9 @@ } else { | ||
{ | ||
areas, | ||
duplicateAreaNames: [], | ||
duplicateRules: [], | ||
hasDuplicates: false, | ||
duplicateRules: [], | ||
node: rule, | ||
params: media.params, | ||
selectors: rule.selectors, | ||
node: rule, | ||
duplicateAreaNames: [], | ||
areas | ||
selectors: rule.selectors | ||
} | ||
@@ -475,3 +489,5 @@ ] | ||
*/ | ||
function insertAreas (css, isDisabled) { | ||
exports.insertAreas = insertAreas | ||
function insertAreas(css, isDisabled) { | ||
// parse grid-template declarations | ||
@@ -539,3 +555,3 @@ let gridTemplatesData = parseGridTemplatesData(css) | ||
} else { | ||
/* istanbul ignore next */ | ||
/* c8 ignore next 2 */ | ||
lastRuleIndex = -1 | ||
@@ -694,3 +710,5 @@ } | ||
*/ | ||
function warnMissedAreas (areas, decl, result) { | ||
exports.warnMissedAreas = warnMissedAreas | ||
function warnMissedAreas(areas, decl, result) { | ||
let missed = Object.keys(areas) | ||
@@ -717,3 +735,5 @@ | ||
*/ | ||
function warnTemplateSelectorNotFound (decl, result) { | ||
exports.warnTemplateSelectorNotFound = warnTemplateSelectorNotFound | ||
function warnTemplateSelectorNotFound(decl, result) { | ||
let rule = decl.parent | ||
@@ -789,3 +809,5 @@ let root = decl.root() | ||
*/ | ||
function warnIfGridRowColumnExists (decl, result) { | ||
exports.warnIfGridRowColumnExists = warnIfGridRowColumnExists | ||
function warnIfGridRowColumnExists(decl, result) { | ||
let rule = decl.parent | ||
@@ -817,3 +839,5 @@ let decls = [] | ||
function getGridGap (decl) { | ||
exports.getGridGap = getGridGap | ||
function getGridGap(decl) { | ||
let gap = {} | ||
@@ -842,3 +866,3 @@ | ||
*/ | ||
function parseMediaParams (params) { | ||
function parseMediaParams(params) { | ||
if (!params) { | ||
@@ -869,3 +893,3 @@ return [] | ||
*/ | ||
function shouldInheritGap (selA, selB) { | ||
function shouldInheritGap(selA, selB) { | ||
let result | ||
@@ -924,3 +948,5 @@ | ||
*/ | ||
function inheritGridGap (decl, gap) { | ||
exports.inheritGridGap = inheritGridGap | ||
function inheritGridGap(decl, gap) { | ||
let rule = decl.parent | ||
@@ -996,3 +1022,5 @@ let mediaRule = getParentMedia(rule) | ||
function warnGridGap ({ gap, hasColumns, decl, result }) { | ||
exports.warnGridGap = warnGridGap | ||
function warnGridGap({ decl, gap, hasColumns, result }) { | ||
let hasBothGaps = gap.row && gap.column | ||
@@ -1016,3 +1044,3 @@ if (!hasColumns && (hasBothGaps || (gap.column && !gap.row))) { | ||
*/ | ||
function normalizeRowColumn (str) { | ||
function normalizeRowColumn(str) { | ||
let normalized = parser(str).nodes.reduce((result, node) => { | ||
@@ -1057,2 +1085,4 @@ if (node.type === 'function' && node.value === 'repeat') { | ||
exports.autoplaceGridItems = autoplaceGridItems | ||
/** | ||
@@ -1067,3 +1097,3 @@ * Autoplace grid items | ||
*/ | ||
function autoplaceGridItems (decl, result, gap, autoflowValue = 'row') { | ||
function autoplaceGridItems(decl, result, gap, autoflowValue = 'row') { | ||
let { parent } = decl | ||
@@ -1084,3 +1114,3 @@ | ||
let areas = parseGridAreas({ rows: filledRows, gap }) | ||
let areas = parseGridAreas({ gap, rows: filledRows }) | ||
let keys = Object.keys(areas) | ||
@@ -1117,19 +1147,1 @@ let items = keys.map(i => areas[i]) | ||
} | ||
module.exports = { | ||
parse, | ||
translate, | ||
parseTemplate, | ||
parseGridAreas, | ||
warnMissedAreas, | ||
insertAreas, | ||
insertDecl, | ||
prefixTrackProp, | ||
prefixTrackValue, | ||
getGridGap, | ||
warnGridGap, | ||
warnTemplateSelectorNotFound, | ||
warnIfGridRowColumnExists, | ||
inheritGridGap, | ||
autoplaceGridItems | ||
} |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
return decl.value === 'pixelated' | ||
@@ -13,5 +13,12 @@ } | ||
/** | ||
* Return property name by spec | ||
*/ | ||
normalize() { | ||
return 'image-rendering' | ||
} | ||
/** | ||
* Change property name for IE | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
if (prefix === '-ms-') { | ||
@@ -24,5 +31,12 @@ return '-ms-interpolation-mode' | ||
/** | ||
* Warn on old value | ||
*/ | ||
process(node, result) { | ||
return super.process(node, result) | ||
} | ||
/** | ||
* Change property and value for IE | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
if (prefix !== '-ms-') return super.set(decl, prefix) | ||
@@ -33,16 +47,2 @@ decl.prop = '-ms-interpolation-mode' | ||
} | ||
/** | ||
* Return property name by spec | ||
*/ | ||
normalize () { | ||
return 'image-rendering' | ||
} | ||
/** | ||
* Warn on old value | ||
*/ | ||
process (node, result) { | ||
return super.process(node, result) | ||
} | ||
} | ||
@@ -49,0 +49,0 @@ |
@@ -7,3 +7,3 @@ let Value = require('../value') | ||
*/ | ||
replace (string, prefix) { | ||
replace(string, prefix) { | ||
let fixed = super.replace(string, prefix) | ||
@@ -10,0 +10,0 @@ if (prefix === '-webkit-') { |
@@ -5,13 +5,13 @@ let Declaration = require('../declaration') | ||
/** | ||
* Use old syntax for -moz- and -webkit- | ||
* Return property name by spec | ||
*/ | ||
prefixed (prop, prefix) { | ||
return prefix + prop.replace('-inline', '') | ||
normalize(prop) { | ||
return prop.replace(/(margin|padding|border)-(start|end)/, '$1-inline-$2') | ||
} | ||
/** | ||
* Return property name by spec | ||
* Use old syntax for -moz- and -webkit- | ||
*/ | ||
normalize (prop) { | ||
return prop.replace(/(margin|padding|border)-(start|end)/, '$1-inline-$2') | ||
prefixed(prop, prefix) { | ||
return prefix + prop.replace('-inline', '') | ||
} | ||
@@ -18,0 +18,0 @@ } |
let OldValue = require('../old-value') | ||
let Value = require('../value') | ||
function regexp (name) { | ||
function regexp(name) { | ||
return new RegExp(`(^|[\\s,(])(${name}($|[\\s),]))`, 'gi') | ||
@@ -9,8 +9,10 @@ } | ||
class Intrinsic extends Value { | ||
regexp () { | ||
if (!this.regexpCache) this.regexpCache = regexp(this.name) | ||
return this.regexpCache | ||
add(decl, prefix) { | ||
if (decl.prop.includes('grid') && prefix !== '-webkit-') { | ||
return undefined | ||
} | ||
return super.add(decl, prefix) | ||
} | ||
isStretch () { | ||
isStretch() { | ||
return ( | ||
@@ -23,13 +25,3 @@ this.name === 'stretch' || | ||
replace (string, prefix) { | ||
if (prefix === '-moz-' && this.isStretch()) { | ||
return string.replace(this.regexp(), '$1-moz-available$3') | ||
} | ||
if (prefix === '-webkit-' && this.isStretch()) { | ||
return string.replace(this.regexp(), '$1-webkit-fill-available$3') | ||
} | ||
return super.replace(string, prefix) | ||
} | ||
old (prefix) { | ||
old(prefix) { | ||
let prefixed = prefix + this.name | ||
@@ -46,7 +38,15 @@ if (this.isStretch()) { | ||
add (decl, prefix) { | ||
if (decl.prop.includes('grid') && prefix !== '-webkit-') { | ||
return undefined | ||
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') | ||
} | ||
return super.add(decl, prefix) | ||
if (prefix === '-webkit-' && this.isStretch()) { | ||
return string.replace(this.regexp(), '$1-webkit-fill-available$3') | ||
} | ||
return super.replace(string, prefix) | ||
} | ||
@@ -53,0 +53,0 @@ } |
@@ -6,5 +6,12 @@ let flexSpec = require('./flex-spec') | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize() { | ||
return 'justify-content' | ||
} | ||
/** | ||
* Change property name for 2009 and 2012 specs | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -22,12 +29,5 @@ ;[spec, prefix] = flexSpec(prefix) | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize () { | ||
return 'justify-content' | ||
} | ||
/** | ||
* Change value for 2009 and 2012 specs | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec = flexSpec(prefix)[0] | ||
@@ -52,6 +52,6 @@ if (spec === 2009 || spec === 2012) { | ||
'flex-start': 'start', | ||
'space-between': 'justify', | ||
'space-around': 'distribute' | ||
'space-around': 'distribute', | ||
'space-between': 'justify' | ||
} | ||
module.exports = JustifyContent |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
normalize () { | ||
normalize() { | ||
return this.name.replace('box-image', 'border') | ||
@@ -15,3 +15,3 @@ } | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let result = super.prefixed(prop, prefix) | ||
@@ -18,0 +18,0 @@ if (prefix === '-webkit-') { |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
let isCompositeProp = decl.prop === 'mask-composite' | ||
@@ -77,5 +77,5 @@ | ||
add: 'source-over', | ||
subtract: 'source-out', | ||
exclude: 'xor', | ||
intersect: 'source-in', | ||
exclude: 'xor' | ||
subtract: 'source-out' | ||
} | ||
@@ -82,0 +82,0 @@ |
@@ -6,5 +6,12 @@ let flexSpec = require('./flex-spec') | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize() { | ||
return 'order' | ||
} | ||
/** | ||
* Change property name for 2009 and 2012 specs | ||
*/ | ||
prefixed (prop, prefix) { | ||
prefixed(prop, prefix) { | ||
let spec | ||
@@ -22,12 +29,5 @@ ;[spec, prefix] = flexSpec(prefix) | ||
/** | ||
* Return property name by final spec | ||
*/ | ||
normalize () { | ||
return 'order' | ||
} | ||
/** | ||
* Fix value for 2009 spec | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
let spec = flexSpec(prefix)[0] | ||
@@ -34,0 +34,0 @@ if (spec === 2009 && /\d/.test(decl.value)) { |
@@ -5,13 +5,13 @@ let Declaration = require('../declaration') | ||
/** | ||
* Change property name for IE | ||
* Return property name by spec | ||
*/ | ||
prefixed (prop, prefix) { | ||
return prefix + 'scroll-chaining' | ||
normalize() { | ||
return 'overscroll-behavior' | ||
} | ||
/** | ||
* Return property name by spec | ||
* Change property name for IE | ||
*/ | ||
normalize () { | ||
return 'overscroll-behavior' | ||
prefixed(prop, prefix) { | ||
return prefix + 'scroll-chaining' | ||
} | ||
@@ -22,3 +22,3 @@ | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
if (decl.value === 'auto') { | ||
@@ -25,0 +25,0 @@ decl.value = 'chained' |
@@ -6,25 +6,25 @@ let OldValue = require('../old-value') | ||
/** | ||
* Use non-standard name for WebKit and Firefox | ||
* Different name for WebKit and Firefox | ||
*/ | ||
replace (string, prefix) { | ||
old(prefix) { | ||
if (prefix === '-webkit-') { | ||
return string.replace(this.regexp(), '$1-webkit-optimize-contrast') | ||
return new OldValue(this.name, '-webkit-optimize-contrast') | ||
} | ||
if (prefix === '-moz-') { | ||
return string.replace(this.regexp(), '$1-moz-crisp-edges') | ||
return new OldValue(this.name, '-moz-crisp-edges') | ||
} | ||
return super.replace(string, prefix) | ||
return super.old(prefix) | ||
} | ||
/** | ||
* Different name for WebKit and Firefox | ||
* Use non-standard name for WebKit and Firefox | ||
*/ | ||
old (prefix) { | ||
replace(string, prefix) { | ||
if (prefix === '-webkit-') { | ||
return new OldValue(this.name, '-webkit-optimize-contrast') | ||
return string.replace(this.regexp(), '$1-webkit-optimize-contrast') | ||
} | ||
if (prefix === '-moz-') { | ||
return new OldValue(this.name, '-moz-crisp-edges') | ||
return string.replace(this.regexp(), '$1-moz-crisp-edges') | ||
} | ||
return super.old(prefix) | ||
return super.replace(string, prefix) | ||
} | ||
@@ -31,0 +31,0 @@ } |
@@ -8,3 +8,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) | ||
@@ -11,0 +11,0 @@ |
@@ -7,3 +7,3 @@ let Selector = require('../selector') | ||
*/ | ||
prefixed (prefix) { | ||
prefixed(prefix) { | ||
if (prefix === '-ms-') { | ||
@@ -10,0 +10,0 @@ return ':-ms-input-placeholder' |
@@ -7,3 +7,3 @@ let Selector = require('../selector') | ||
*/ | ||
possible () { | ||
possible() { | ||
return super.possible().concat(['-moz- old', '-ms- old']) | ||
@@ -15,3 +15,3 @@ } | ||
*/ | ||
prefixed (prefix) { | ||
prefixed(prefix) { | ||
if (prefix === '-webkit-') { | ||
@@ -18,0 +18,0 @@ return '::-webkit-input-placeholder' |
@@ -7,3 +7,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
if (decl.prop === 'text-decoration-skip-ink' && decl.value === 'auto') { | ||
@@ -10,0 +10,0 @@ decl.prop = prefix + 'text-decoration-skip' |
@@ -18,3 +18,3 @@ let Declaration = require('../declaration') | ||
*/ | ||
check (decl) { | ||
check(decl) { | ||
return decl.value.split(/\s+/).some(i => !BASIC.includes(i)) | ||
@@ -21,0 +21,0 @@ } |
let Declaration = require('../declaration') | ||
class TextEmphasisPosition extends Declaration { | ||
set (decl, prefix) { | ||
set(decl, prefix) { | ||
if (prefix === '-webkit-') { | ||
@@ -6,0 +6,0 @@ decl.value = decl.value.replace(/\s*(right|left)\s*/i, '') |
@@ -5,19 +5,5 @@ let Declaration = require('../declaration') | ||
/** | ||
* Recursively check all parents for @keyframes | ||
*/ | ||
keyframeParents (decl) { | ||
let { parent } = decl | ||
while (parent) { | ||
if (parent.type === 'atrule' && parent.name === 'keyframes') { | ||
return true | ||
} | ||
;({ parent } = parent) | ||
} | ||
return false | ||
} | ||
/** | ||
* Is transform contain 3D commands | ||
*/ | ||
contain3d (decl) { | ||
contain3d(decl) { | ||
if (decl.prop === 'transform-origin') { | ||
@@ -37,16 +23,5 @@ return false | ||
/** | ||
* Replace rotateZ to rotate for IE 9 | ||
*/ | ||
set (decl, prefix) { | ||
decl = super.set(decl, prefix) | ||
if (prefix === '-ms-') { | ||
decl.value = decl.value.replace(/rotatez/gi, 'rotate') | ||
} | ||
return decl | ||
} | ||
/** | ||
* Don't add prefix for IE in keyframes | ||
*/ | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
if (prefix === '-ms-') { | ||
@@ -65,2 +40,27 @@ if (!this.contain3d(decl) && !this.keyframeParents(decl)) { | ||
} | ||
/** | ||
* 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 | ||
} | ||
} | ||
@@ -67,0 +67,0 @@ |
@@ -5,15 +5,5 @@ let Declaration = require('../declaration') | ||
/** | ||
* Change prefixed value for IE | ||
*/ | ||
set (decl, prefix) { | ||
if (prefix === '-ms-' && decl.value === 'contain') { | ||
decl.value = 'element' | ||
} | ||
return super.set(decl, prefix) | ||
} | ||
/** | ||
* Avoid prefixing all in IE | ||
*/ | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
if (decl.value === 'all' && prefix === '-ms-') { | ||
@@ -25,2 +15,12 @@ return undefined | ||
} | ||
/** | ||
* Change prefixed value for IE | ||
*/ | ||
set(decl, prefix) { | ||
if (prefix === '-ms-' && decl.value === 'contain') { | ||
decl.value = 'element' | ||
} | ||
return super.set(decl, prefix) | ||
} | ||
} | ||
@@ -27,0 +27,0 @@ |
let Declaration = require('../declaration') | ||
class WritingMode extends Declaration { | ||
insert (decl, prefix, prefixes) { | ||
insert(decl, prefix, prefixes) { | ||
if (prefix === '-ms-') { | ||
@@ -32,9 +32,9 @@ let cloned = this.set(this.clone(decl), prefix) | ||
'horizontal-tb': 'lr-tb', | ||
'vertical-rl': 'tb-rl', | ||
'vertical-lr': 'tb-lr' | ||
'vertical-lr': 'tb-lr', | ||
'vertical-rl': 'tb-rl' | ||
}, | ||
rtl: { | ||
'horizontal-tb': 'rl-tb', | ||
'vertical-rl': 'bt-rl', | ||
'vertical-lr': 'bt-lr' | ||
'vertical-lr': 'bt-lr', | ||
'vertical-rl': 'bt-rl' | ||
} | ||
@@ -41,0 +41,0 @@ } |
let browserslist = require('browserslist') | ||
function capitalize (str) { | ||
function capitalize(str) { | ||
return str.slice(0, 1).toUpperCase() + str.slice(1) | ||
@@ -8,17 +8,17 @@ } | ||
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', | ||
and_chr: 'Chrome for Android', | ||
and_ff: 'Firefox for Android', | ||
and_uc: 'UC for Android', | ||
and_qq: 'QQ Browser', | ||
kaios: 'KaiOS Browser', | ||
baidu: 'Baidu Browser', | ||
samsung: 'Samsung Internet' | ||
} | ||
function prefix (name, prefixes, note) { | ||
function prefix(name, prefixes, note) { | ||
let out = ` ${name}` | ||
@@ -25,0 +25,0 @@ if (note) out += ' *' |
class OldSelector { | ||
constructor (selector, prefix) { | ||
constructor(selector, prefix) { | ||
this.prefix = prefix | ||
@@ -16,5 +16,21 @@ this.prefixed = selector.prefixed(this.prefix) | ||
/** | ||
* 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) { | ||
isHack(rule) { | ||
let index = rule.parent.index(rule) + 1 | ||
@@ -50,20 +66,4 @@ let rules = rule.parent.nodes | ||
} | ||
/** | ||
* Does rule contain an unnecessary prefixed selector | ||
*/ | ||
check (rule) { | ||
if (!rule.selector.includes(this.prefixed)) { | ||
return false | ||
} | ||
if (!rule.selector.match(this.regexp)) { | ||
return false | ||
} | ||
if (this.isHack(rule)) { | ||
return false | ||
} | ||
return true | ||
} | ||
} | ||
module.exports = OldSelector |
let utils = require('./utils') | ||
class OldValue { | ||
constructor (unprefixed, prefixed, string, regexp) { | ||
constructor(unprefixed, prefixed, string, regexp) { | ||
this.unprefixed = unprefixed | ||
@@ -14,3 +14,3 @@ this.prefixed = prefixed | ||
*/ | ||
check (value) { | ||
check(value) { | ||
if (value.includes(this.string)) { | ||
@@ -17,0 +17,0 @@ return !!value.match(this.regexp) |
@@ -8,3 +8,3 @@ let Browsers = require('./browsers') | ||
*/ | ||
function clone (obj, parent) { | ||
function clone(obj, parent) { | ||
let cloned = new obj.constructor() | ||
@@ -38,6 +38,23 @@ | ||
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) { | ||
static hack(klass) { | ||
if (!this.hacks) { | ||
@@ -55,3 +72,3 @@ this.hacks = {} | ||
*/ | ||
static load (name, prefixes, all) { | ||
static load(name, prefixes, all) { | ||
let Klass = this.hacks && this.hacks[name] | ||
@@ -66,22 +83,12 @@ if (Klass) { | ||
/** | ||
* Clone node and clean autprefixer custom caches | ||
* Shortcut for Prefixer.clone | ||
*/ | ||
static clone (node, overrides) { | ||
let cloned = clone(node) | ||
for (let name in overrides) { | ||
cloned[name] = overrides[name] | ||
} | ||
return cloned | ||
clone(node, overrides) { | ||
return Prefixer.clone(node, overrides) | ||
} | ||
constructor (name, prefixes, all) { | ||
this.prefixes = prefixes | ||
this.name = name | ||
this.all = all | ||
} | ||
/** | ||
* Find prefix in node parents | ||
*/ | ||
parentPrefix (node) { | ||
parentPrefix(node) { | ||
let prefix | ||
@@ -119,3 +126,3 @@ | ||
*/ | ||
process (node, result) { | ||
process(node, result) { | ||
if (!this.check(node)) { | ||
@@ -140,11 +147,4 @@ return undefined | ||
} | ||
/** | ||
* Shortcut for Prefixer.clone | ||
*/ | ||
clone (node, overrides) { | ||
return Prefixer.clone(node, overrides) | ||
} | ||
} | ||
module.exports = Prefixer |
@@ -12,62 +12,122 @@ let vendor = require('./vendor') | ||
let utils = require('./utils') | ||
Selector.hack(require('./hacks/fullscreen')) | ||
Selector.hack(require('./hacks/placeholder')) | ||
Selector.hack(require('./hacks/placeholder-shown')) | ||
Declaration.hack(require('./hacks/flex')) | ||
Declaration.hack(require('./hacks/order')) | ||
Declaration.hack(require('./hacks/filter')) | ||
Declaration.hack(require('./hacks/grid-end')) | ||
Declaration.hack(require('./hacks/animation')) | ||
Declaration.hack(require('./hacks/flex-flow')) | ||
Declaration.hack(require('./hacks/flex-grow')) | ||
Declaration.hack(require('./hacks/flex-wrap')) | ||
Declaration.hack(require('./hacks/grid-area')) | ||
Declaration.hack(require('./hacks/place-self')) | ||
Declaration.hack(require('./hacks/grid-start')) | ||
Declaration.hack(require('./hacks/align-self')) | ||
Declaration.hack(require('./hacks/appearance')) | ||
Declaration.hack(require('./hacks/flex-basis')) | ||
Declaration.hack(require('./hacks/mask-border')) | ||
Declaration.hack(require('./hacks/mask-composite')) | ||
Declaration.hack(require('./hacks/align-items')) | ||
Declaration.hack(require('./hacks/user-select')) | ||
Declaration.hack(require('./hacks/flex-shrink')) | ||
Declaration.hack(require('./hacks/break-props')) | ||
Declaration.hack(require('./hacks/color-adjust')) | ||
Declaration.hack(require('./hacks/writing-mode')) | ||
Declaration.hack(require('./hacks/border-image')) | ||
Declaration.hack(require('./hacks/align-content')) | ||
Declaration.hack(require('./hacks/border-radius')) | ||
Declaration.hack(require('./hacks/block-logical')) | ||
Declaration.hack(require('./hacks/grid-template')) | ||
Declaration.hack(require('./hacks/inline-logical')) | ||
Declaration.hack(require('./hacks/grid-row-align')) | ||
Declaration.hack(require('./hacks/transform-decl')) | ||
Declaration.hack(require('./hacks/flex-direction')) | ||
Declaration.hack(require('./hacks/image-rendering')) | ||
Declaration.hack(require('./hacks/backdrop-filter')) | ||
Declaration.hack(require('./hacks/background-clip')) | ||
Declaration.hack(require('./hacks/text-decoration')) | ||
Declaration.hack(require('./hacks/justify-content')) | ||
Declaration.hack(require('./hacks/background-size')) | ||
Declaration.hack(require('./hacks/grid-row-column')) | ||
Declaration.hack(require('./hacks/grid-rows-columns')) | ||
Declaration.hack(require('./hacks/grid-column-align')) | ||
Declaration.hack(require('./hacks/overscroll-behavior')) | ||
Declaration.hack(require('./hacks/grid-template-areas')) | ||
Declaration.hack(require('./hacks/text-emphasis-position')) | ||
Declaration.hack(require('./hacks/text-decoration-skip-ink')) | ||
Value.hack(require('./hacks/gradient')) | ||
Value.hack(require('./hacks/intrinsic')) | ||
Value.hack(require('./hacks/pixelated')) | ||
Value.hack(require('./hacks/image-set')) | ||
Value.hack(require('./hacks/cross-fade')) | ||
Value.hack(require('./hacks/display-flex')) | ||
Value.hack(require('./hacks/display-grid')) | ||
Value.hack(require('./hacks/filter-value')) | ||
let hackFullscreen = require('./hacks/fullscreen') | ||
let hackPlaceholder = require('./hacks/placeholder') | ||
let hackPlaceholderShown = require('./hacks/placeholder-shown') | ||
let hackFileSelectorButton = require('./hacks/file-selector-button') | ||
let hackFlex = require('./hacks/flex') | ||
let hackOrder = require('./hacks/order') | ||
let hackFilter = require('./hacks/filter') | ||
let hackGridEnd = require('./hacks/grid-end') | ||
let hackAnimation = require('./hacks/animation') | ||
let hackFlexFlow = require('./hacks/flex-flow') | ||
let hackFlexGrow = require('./hacks/flex-grow') | ||
let hackFlexWrap = require('./hacks/flex-wrap') | ||
let hackGridArea = require('./hacks/grid-area') | ||
let hackPlaceSelf = require('./hacks/place-self') | ||
let hackGridStart = require('./hacks/grid-start') | ||
let hackAlignSelf = require('./hacks/align-self') | ||
let hackAppearance = require('./hacks/appearance') | ||
let hackFlexBasis = require('./hacks/flex-basis') | ||
let hackMaskBorder = require('./hacks/mask-border') | ||
let hackMaskComposite = require('./hacks/mask-composite') | ||
let hackAlignItems = require('./hacks/align-items') | ||
let hackUserSelect = require('./hacks/user-select') | ||
let hackFlexShrink = require('./hacks/flex-shrink') | ||
let hackBreakProps = require('./hacks/break-props') | ||
let hackWritingMode = require('./hacks/writing-mode') | ||
let hackBorderImage = require('./hacks/border-image') | ||
let hackAlignContent = require('./hacks/align-content') | ||
let hackBorderRadius = require('./hacks/border-radius') | ||
let hackBlockLogical = require('./hacks/block-logical') | ||
let hackGridTemplate = require('./hacks/grid-template') | ||
let hackInlineLogical = require('./hacks/inline-logical') | ||
let hackGridRowAlign = require('./hacks/grid-row-align') | ||
let hackTransformDecl = require('./hacks/transform-decl') | ||
let hackFlexDirection = require('./hacks/flex-direction') | ||
let hackImageRendering = require('./hacks/image-rendering') | ||
let hackBackdropFilter = require('./hacks/backdrop-filter') | ||
let hackBackgroundClip = require('./hacks/background-clip') | ||
let hackTextDecoration = require('./hacks/text-decoration') | ||
let hackJustifyContent = require('./hacks/justify-content') | ||
let hackBackgroundSize = require('./hacks/background-size') | ||
let hackGridRowColumn = require('./hacks/grid-row-column') | ||
let hackGridRowsColumns = require('./hacks/grid-rows-columns') | ||
let hackGridColumnAlign = require('./hacks/grid-column-align') | ||
let hackPrintColorAdjust = require('./hacks/print-color-adjust') | ||
let hackOverscrollBehavior = require('./hacks/overscroll-behavior') | ||
let hackGridTemplateAreas = require('./hacks/grid-template-areas') | ||
let hackTextEmphasisPosition = require('./hacks/text-emphasis-position') | ||
let hackTextDecorationSkipInk = require('./hacks/text-decoration-skip-ink') | ||
let hackGradient = require('./hacks/gradient') | ||
let hackIntrinsic = require('./hacks/intrinsic') | ||
let hackPixelated = require('./hacks/pixelated') | ||
let hackImageSet = require('./hacks/image-set') | ||
let hackCrossFade = require('./hacks/cross-fade') | ||
let hackDisplayFlex = require('./hacks/display-flex') | ||
let hackDisplayGrid = require('./hacks/display-grid') | ||
let hackFilterValue = require('./hacks/filter-value') | ||
let hackAutofill = require('./hacks/autofill') | ||
Selector.hack(hackAutofill) | ||
Selector.hack(hackFullscreen) | ||
Selector.hack(hackPlaceholder) | ||
Selector.hack(hackPlaceholderShown) | ||
Selector.hack(hackFileSelectorButton) | ||
Declaration.hack(hackFlex) | ||
Declaration.hack(hackOrder) | ||
Declaration.hack(hackFilter) | ||
Declaration.hack(hackGridEnd) | ||
Declaration.hack(hackAnimation) | ||
Declaration.hack(hackFlexFlow) | ||
Declaration.hack(hackFlexGrow) | ||
Declaration.hack(hackFlexWrap) | ||
Declaration.hack(hackGridArea) | ||
Declaration.hack(hackPlaceSelf) | ||
Declaration.hack(hackGridStart) | ||
Declaration.hack(hackAlignSelf) | ||
Declaration.hack(hackAppearance) | ||
Declaration.hack(hackFlexBasis) | ||
Declaration.hack(hackMaskBorder) | ||
Declaration.hack(hackMaskComposite) | ||
Declaration.hack(hackAlignItems) | ||
Declaration.hack(hackUserSelect) | ||
Declaration.hack(hackFlexShrink) | ||
Declaration.hack(hackBreakProps) | ||
Declaration.hack(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 = {}) { | ||
constructor(data, browsers, options = {}) { | ||
this.data = data | ||
@@ -84,3 +144,3 @@ this.browsers = browsers | ||
*/ | ||
cleaner () { | ||
cleaner() { | ||
if (this.cleanerCache) { | ||
@@ -101,72 +161,73 @@ return this.cleanerCache | ||
/** | ||
* Select prefixes from data, which is necessary for selected browsers | ||
* Declaration loader with caching | ||
*/ | ||
select (list) { | ||
let selected = { add: {}, remove: {} } | ||
decl(prop) { | ||
if (!declsCache.has(prop)) { | ||
declsCache.set(prop, Declaration.load(prop)) | ||
} | ||
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] | ||
} | ||
}) | ||
return declsCache.get(prop) | ||
} | ||
let notes = add | ||
.filter(i => i.note) | ||
.map(i => `${this.browsers.prefix(i.browser)} ${i.note}`) | ||
notes = utils.uniq(notes) | ||
/** | ||
* 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) | ||
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 | ||
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 | ||
} | ||
} | ||
}) | ||
add = this.sort(utils.uniq(add)) | ||
if (this.options.flexbox === 'no-2009') { | ||
add = add.filter(i => !i.includes('2009')) | ||
} | ||
if (this.unprefixed(other.prop) !== unprefixed) { | ||
break | ||
} else if (callback(other) === true) { | ||
return true | ||
} | ||
let all = data.browsers.map(i => this.browsers.prefix(i)) | ||
if (data.mistakes) { | ||
all = all.concat(data.mistakes) | ||
if (step === +1 && other.prop === unprefixed) { | ||
if (!Browsers.withPrefix(other.value)) { | ||
break | ||
} | ||
} | ||
} | ||
index += step | ||
} | ||
all = all.concat(notes) | ||
all = utils.uniq(all) | ||
return false | ||
} | ||
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 { | ||
down(callback) { | ||
return checker(+1, callback) | ||
}, | ||
up(callback) { | ||
return checker(-1, callback) | ||
} | ||
} | ||
} | ||
return selected | ||
/** | ||
* Normalize prefix for remover | ||
*/ | ||
normalize(prop) { | ||
return this.decl(prop).normalize(prop) | ||
} | ||
/** | ||
* Sort vendor prefixes | ||
* Return prefixed version of property | ||
*/ | ||
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 | ||
} | ||
}) | ||
prefixed(prop, prefix) { | ||
prop = vendor.unprefixed(prop) | ||
return this.decl(prop).prefixed(prop, prefix) | ||
} | ||
@@ -177,6 +238,6 @@ | ||
*/ | ||
preprocess (selected) { | ||
preprocess(selected) { | ||
let add = { | ||
'selectors': [], | ||
'@supports': new Supports(Prefixes, this) | ||
'@supports': new Supports(Prefixes, this), | ||
'selectors': [] | ||
} | ||
@@ -271,16 +332,78 @@ for (let name in selected.add) { | ||
/** | ||
* Declaration loader with caching | ||
* Select prefixes from data, which is necessary for selected browsers | ||
*/ | ||
decl (prop) { | ||
if (!declsCache.has(prop)) { | ||
declsCache.set(prop, Declaration.load(prop)) | ||
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 declsCache.get(prop) | ||
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) { | ||
unprefixed(prop) { | ||
let value = this.normalize(vendor.unprefixed(prop)) | ||
@@ -294,20 +417,5 @@ if (value === 'flex-direction') { | ||
/** | ||
* Normalize prefix for remover | ||
*/ | ||
normalize (prop) { | ||
return this.decl(prop).normalize(prop) | ||
} | ||
/** | ||
* Return prefixed version of property | ||
*/ | ||
prefixed (prop, prefix) { | ||
prop = vendor.unprefixed(prop) | ||
return this.decl(prop).prefixed(prop, prefix) | ||
} | ||
/** | ||
* Return values, which must be prefixed in selected property | ||
*/ | ||
values (type, prop) { | ||
values(type, prop) { | ||
let data = this[type] | ||
@@ -324,52 +432,4 @@ | ||
} | ||
/** | ||
* Group declaration by unprefixed property to check them | ||
*/ | ||
group (decl) { | ||
let rule = decl.parent | ||
let index = rule.index(decl) | ||
let { length } = rule.nodes | ||
let unprefixed = this.unprefixed(decl.prop) | ||
let checker = (step, callback) => { | ||
index += step | ||
while (index >= 0 && index < length) { | ||
let other = rule.nodes[index] | ||
if (other.type === 'decl') { | ||
if (step === -1 && other.prop === unprefixed) { | ||
if (!Browsers.withPrefix(other.value)) { | ||
break | ||
} | ||
} | ||
if (this.unprefixed(other.prop) !== unprefixed) { | ||
break | ||
} else if (callback(other) === true) { | ||
return true | ||
} | ||
if (step === +1 && other.prop === unprefixed) { | ||
if (!Browsers.withPrefix(other.value)) { | ||
break | ||
} | ||
} | ||
} | ||
index += step | ||
} | ||
return false | ||
} | ||
return { | ||
up (callback) { | ||
return checker(-1, callback) | ||
}, | ||
down (callback) { | ||
return checker(+1, callback) | ||
} | ||
} | ||
} | ||
} | ||
module.exports = Prefixes |
@@ -26,3 +26,3 @@ let parser = require('postcss-value-parser') | ||
function hasGridTemplate (decl) { | ||
function hasGridTemplate(decl) { | ||
return decl.parent.some( | ||
@@ -33,3 +33,3 @@ i => i.prop === 'grid-template' || i.prop === 'grid-template-areas' | ||
function hasRowsAndColumns (decl) { | ||
function hasRowsAndColumns(decl) { | ||
let hasRows = decl.parent.some(i => i.prop === 'grid-template-rows') | ||
@@ -41,3 +41,3 @@ let hasColumns = decl.parent.some(i => i.prop === 'grid-template-columns') | ||
class Processor { | ||
constructor (prefixes) { | ||
constructor(prefixes) { | ||
this.prefixes = prefixes | ||
@@ -49,3 +49,3 @@ } | ||
*/ | ||
add (css, result) { | ||
add(css, result) { | ||
// At-rules | ||
@@ -91,3 +91,3 @@ let resolution = this.prefixes.add['@resolution'] | ||
function insideGrid (decl) { | ||
function insideGrid(decl) { | ||
return decl.parent.nodes.some(node => { | ||
@@ -102,3 +102,3 @@ if (node.type !== 'decl') return false | ||
} | ||
function insideFlex (decl) { | ||
function insideFlex(decl) { | ||
return decl.parent.some(node => { | ||
@@ -121,3 +121,11 @@ return node.prop === 'display' && /(inline-)?flex/.test(node.value) | ||
if (prop === 'grid-row-span') { | ||
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( | ||
@@ -399,137 +407,5 @@ 'grid-row-span is not part of final Grid Layout. Use grid-row.', | ||
/** | ||
* Remove unnecessary pefixes | ||
*/ | ||
remove (css, result) { | ||
// At-rules | ||
let resolution = this.prefixes.remove['@resolution'] | ||
css.walkAtRules((rule, i) => { | ||
if (this.prefixes.remove[`@${rule.name}`]) { | ||
if (!this.disabled(rule, result)) { | ||
rule.parent.removeChild(i) | ||
} | ||
} else if ( | ||
rule.name === 'media' && | ||
rule.params.includes('-resolution') && | ||
resolution | ||
) { | ||
resolution.clean(rule) | ||
} | ||
}) | ||
// Selectors | ||
for (let checker of this.prefixes.remove.selectors) { | ||
css.walkRules((rule, i) => { | ||
if (checker.check(rule)) { | ||
if (!this.disabled(rule, result)) { | ||
rule.parent.removeChild(i) | ||
} | ||
} | ||
}) | ||
} | ||
return css.walkDecls((decl, i) => { | ||
if (this.disabled(decl, result)) return | ||
let rule = decl.parent | ||
let unprefixed = this.prefixes.unprefixed(decl.prop) | ||
// Transition | ||
if (decl.prop === 'transition' || decl.prop === 'transition-property') { | ||
this.prefixes.transition.remove(decl) | ||
} | ||
// Properties | ||
if ( | ||
this.prefixes.remove[decl.prop] && | ||
this.prefixes.remove[decl.prop].remove | ||
) { | ||
let notHack = this.prefixes.group(decl).down(other => { | ||
return this.prefixes.normalize(other.prop) === unprefixed | ||
}) | ||
if (unprefixed === 'flex-flow') { | ||
notHack = true | ||
} | ||
if (decl.prop === '-webkit-box-orient') { | ||
let hacks = { 'flex-direction': true, 'flex-flow': true } | ||
if (!decl.parent.some(j => hacks[j.prop])) return | ||
} | ||
if (notHack && !this.withHackValue(decl)) { | ||
if (decl.raw('before').includes('\n')) { | ||
this.reduceSpaces(decl) | ||
} | ||
rule.removeChild(i) | ||
return | ||
} | ||
} | ||
// Values | ||
for (let checker of this.prefixes.values('remove', unprefixed)) { | ||
if (!checker.check) continue | ||
if (!checker.check(decl.value)) continue | ||
unprefixed = checker.unprefixed | ||
let notHack = this.prefixes.group(decl).down(other => { | ||
return other.value.includes(unprefixed) | ||
}) | ||
if (notHack) { | ||
rule.removeChild(i) | ||
return | ||
} | ||
} | ||
}) | ||
} | ||
/** | ||
* Some rare old values, which is not in standard | ||
*/ | ||
withHackValue (decl) { | ||
return decl.prop === '-webkit-background-clip' && decl.value === 'text' | ||
} | ||
/** | ||
* Check for grid/flexbox options. | ||
*/ | ||
disabledValue (node, result) { | ||
if (this.gridStatus(node, result) === false && node.type === 'decl') { | ||
if (node.prop === 'display' && node.value.includes('grid')) { | ||
return true | ||
} | ||
} | ||
if (this.prefixes.options.flexbox === false && node.type === 'decl') { | ||
if (node.prop === 'display' && node.value.includes('flex')) { | ||
return true | ||
} | ||
} | ||
return this.disabled(node, result) | ||
} | ||
/** | ||
* Check for grid/flexbox options. | ||
*/ | ||
disabledDecl (node, result) { | ||
if (this.gridStatus(node, result) === false && node.type === 'decl') { | ||
if (node.prop.includes('grid') || node.prop === 'justify-items') { | ||
return true | ||
} | ||
} | ||
if (this.prefixes.options.flexbox === false && node.type === 'decl') { | ||
let other = ['order', 'justify-content', 'align-items', 'align-content'] | ||
if (node.prop.includes('flex') || other.includes(node.prop)) { | ||
return true | ||
} | ||
} | ||
return this.disabled(node, result) | ||
} | ||
/** | ||
* Check for control comment and global options | ||
*/ | ||
disabled (node, result) { | ||
disabled(node, result) { | ||
if (!node) return false | ||
@@ -590,31 +466,39 @@ | ||
/** | ||
* Normalize spaces in cascade declaration group | ||
* Check for grid/flexbox options. | ||
*/ | ||
reduceSpaces (decl) { | ||
let stop = false | ||
this.prefixes.group(decl).up(() => { | ||
stop = true | ||
return true | ||
}) | ||
if (stop) { | ||
return | ||
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 | ||
} | ||
} | ||
let parts = decl.raw('before').split('\n') | ||
let prevMin = parts[parts.length - 1].length | ||
let diff = false | ||
return this.disabled(node, result) | ||
} | ||
this.prefixes.group(decl).down(other => { | ||
parts = other.raw('before').split('\n') | ||
let last = parts.length - 1 | ||
/** | ||
* 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 | ||
} | ||
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') | ||
} | ||
}) | ||
return this.disabled(node, result) | ||
} | ||
@@ -625,3 +509,3 @@ | ||
*/ | ||
displayType (decl) { | ||
displayType(decl) { | ||
for (let i of decl.parent.nodes) { | ||
@@ -647,3 +531,3 @@ if (i.prop !== 'display') { | ||
*/ | ||
gridStatus (node, result) { | ||
gridStatus(node, result) { | ||
if (!node) return false | ||
@@ -716,4 +600,132 @@ | ||
} | ||
/** | ||
* 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' | ||
} | ||
} | ||
module.exports = Processor |
@@ -1,2 +0,2 @@ | ||
let Fraction = require('fraction.js') | ||
let FractionJs = require('fraction.js') | ||
@@ -11,5 +11,22 @@ let Prefixer = require('./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) { | ||
prefixName(prefix, name) { | ||
if (prefix === '-moz-') { | ||
@@ -25,4 +42,4 @@ return name + '--moz-device-pixel-ratio' | ||
*/ | ||
prefixQuery (prefix, name, colon, value, units) { | ||
value = new Fraction(value) | ||
prefixQuery(prefix, name, colon, value, units) { | ||
value = new FractionJs(value) | ||
@@ -45,22 +62,5 @@ // 1dpcm = 2.54dpi | ||
/** | ||
* Remove prefixed queries | ||
*/ | ||
clean (rule) { | ||
if (!this.bad) { | ||
this.bad = [] | ||
for (let prefix of this.prefixes) { | ||
this.bad.push(this.prefixName(prefix, 'min')) | ||
this.bad.push(this.prefixName(prefix, 'max')) | ||
} | ||
} | ||
rule.params = utils.editList(rule.params, queries => { | ||
return queries.filter(query => this.bad.every(i => !query.includes(i))) | ||
}) | ||
} | ||
/** | ||
* Add prefixed queries | ||
*/ | ||
process (rule) { | ||
process(rule) { | ||
let parent = this.parentPrefix(rule) | ||
@@ -67,0 +67,0 @@ let prefixes = parent ? [parent] : this.prefixes |
@@ -9,3 +9,3 @@ let { list } = require('postcss') | ||
class Selector extends Prefixer { | ||
constructor (name, prefixes, all) { | ||
constructor(name, prefixes, all) { | ||
super(name, prefixes, all) | ||
@@ -16,38 +16,72 @@ this.regexpCache = new Map() | ||
/** | ||
* Is rule selectors need to be prefixed | ||
* Clone and add prefixes for at-rule | ||
*/ | ||
check (rule) { | ||
if (rule.selector.includes(this.name)) { | ||
return !!rule.selector.match(this.regexp()) | ||
add(rule, prefix) { | ||
let prefixeds = this.prefixeds(rule) | ||
if (this.already(rule, prefixeds, prefix)) { | ||
return | ||
} | ||
return false | ||
let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] }) | ||
rule.parent.insertBefore(rule, cloned) | ||
} | ||
/** | ||
* Return prefixed version of selector | ||
* Is rule already prefixed before | ||
*/ | ||
prefixed (prefix) { | ||
return this.name.replace(/^(\W*)/, `$1${prefix}`) | ||
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 | ||
} | ||
/** | ||
* Lazy loadRegExp for name | ||
* Is rule selectors need to be prefixed | ||
*/ | ||
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') | ||
) | ||
check(rule) { | ||
if (rule.selector.includes(this.name)) { | ||
return !!rule.selector.match(this.regexp()) | ||
} | ||
return this.regexpCache.get(prefix) | ||
return false | ||
} | ||
/** | ||
* Return function to fast find prefixed selector | ||
*/ | ||
old(prefix) { | ||
return new OldSelector(this, prefix) | ||
} | ||
/** | ||
* All possible prefixes | ||
*/ | ||
possible () { | ||
possible() { | ||
return Browsers.prefixes() | ||
@@ -57,5 +91,12 @@ } | ||
/** | ||
* Return prefixed version of selector | ||
*/ | ||
prefixed(prefix) { | ||
return this.name.replace(/^(\W*)/, `$1${prefix}`) | ||
} | ||
/** | ||
* Return all possible selector prefixes | ||
*/ | ||
prefixeds (rule) { | ||
prefixeds(rule) { | ||
if (rule._autoprefixerPrefixeds) { | ||
@@ -90,34 +131,14 @@ if (rule._autoprefixerPrefixeds[this.name]) { | ||
/** | ||
* Is rule already prefixed before | ||
* Lazy loadRegExp for name | ||
*/ | ||
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 | ||
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 false | ||
return this.regexpCache.get(prefix) | ||
} | ||
@@ -128,28 +149,7 @@ | ||
*/ | ||
replace (selector, prefix) { | ||
replace(selector, prefix) { | ||
return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`) | ||
} | ||
/** | ||
* Clone and add prefixes for at-rule | ||
*/ | ||
add (rule, prefix) { | ||
let prefixeds = this.prefixeds(rule) | ||
if (this.already(rule, prefixeds, prefix)) { | ||
return | ||
} | ||
let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] }) | ||
rule.parent.insertBefore(rule, cloned) | ||
} | ||
/** | ||
* Return function to fast find prefixed selector | ||
*/ | ||
old (prefix) { | ||
return new OldSelector(this, prefix) | ||
} | ||
} | ||
module.exports = Selector |
let featureQueries = require('caniuse-lite/data/features/css-featurequeries.js') | ||
let { feature } = require('caniuse-lite') | ||
let feature = require('caniuse-lite/dist/unpacker/feature') | ||
let { parse } = require('postcss') | ||
@@ -24,3 +24,3 @@ | ||
class Supports { | ||
constructor (Prefixes, all) { | ||
constructor(Prefixes, all) { | ||
this.Prefixes = Prefixes | ||
@@ -31,75 +31,91 @@ this.all = all | ||
/** | ||
* Return prefixer only with @supports supported browsers | ||
* Add prefixes | ||
*/ | ||
prefixer () { | ||
if (this.prefixerCache) { | ||
return this.prefixerCache | ||
} | ||
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) | ||
} | ||
let filtered = this.all.browsers.selected.filter(i => { | ||
return supported.includes(i) | ||
return i | ||
} | ||
if (typeof i === 'object') { | ||
return this.add(i, all) | ||
} | ||
return i | ||
}) | ||
let browsers = new Browsers( | ||
this.all.browsers.data, | ||
filtered, | ||
this.all.options | ||
) | ||
this.prefixerCache = new this.Prefixes( | ||
this.all.data, | ||
browsers, | ||
this.all.options | ||
) | ||
return this.prefixerCache | ||
} | ||
/** | ||
* Parse string into declaration property and value | ||
* Clean brackets with one child | ||
*/ | ||
parse (str) { | ||
let parts = str.split(':') | ||
let prop = parts[0] | ||
let value = parts[1] | ||
if (!value) value = '' | ||
return [prop.trim(), value.trim()] | ||
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) | ||
}) | ||
} | ||
/** | ||
* Create virtual rule to process it by prefixer | ||
* Add " or " between properties and convert it to brackets format | ||
*/ | ||
virtual (str) { | ||
let [prop, value] = this.parse(str) | ||
let rule = parse('a{}').first | ||
rule.append({ prop, value, raws: { before: '' } }) | ||
return rule | ||
convert(progress) { | ||
let result = [''] | ||
for (let i of progress) { | ||
result.push([`${i.prop}: ${i.value}`]) | ||
result.push(' or ') | ||
} | ||
result[result.length - 1] = '' | ||
return result | ||
} | ||
/** | ||
* Return array of Declaration with all necessary prefixes | ||
* Check global options | ||
*/ | ||
prefixed (str) { | ||
let rule = this.virtual(str) | ||
if (this.disabled(rule.first)) { | ||
return rule.nodes | ||
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 | ||
} | ||
} | ||
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) | ||
if (this.all.options.flexbox === false) { | ||
if (node.prop === 'display' && node.value.includes('flex')) { | ||
return true | ||
} | ||
Value.save(this.all, decl) | ||
let other = ['order', 'justify-content', 'align-items', 'align-content'] | ||
if (node.prop.includes('flex') || other.includes(node.prop)) { | ||
return true | ||
} | ||
} | ||
return rule.nodes | ||
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) { | ||
isNot(node) { | ||
return typeof node === 'string' && /not\s*/i.test(node) | ||
@@ -111,3 +127,3 @@ } | ||
*/ | ||
isOr (node) { | ||
isOr(node) { | ||
return typeof node === 'string' && /\s*or\s*/i.test(node) | ||
@@ -119,3 +135,3 @@ } | ||
*/ | ||
isProp (node) { | ||
isProp(node) { | ||
return ( | ||
@@ -129,39 +145,101 @@ typeof node === 'object' && | ||
/** | ||
* Return true if prefixed property has no unprefixed | ||
* Compress value functions into a string nodes | ||
*/ | ||
isHack (all, unprefixed) { | ||
let check = new RegExp(`(\\(|\\s)${utils.escapeRegexp(unprefixed)}:`) | ||
return !check.test(all) | ||
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)) | ||
} | ||
/** | ||
* Return true if we need to remove node | ||
* Parse string into declaration property and value | ||
*/ | ||
toRemove (str, all) { | ||
let [prop, value] = this.parse(str) | ||
let unprefixed = this.all.unprefixed(prop) | ||
parse(str) { | ||
let parts = str.split(':') | ||
let prop = parts[0] | ||
let value = parts[1] | ||
if (!value) value = '' | ||
return [prop.trim(), value.trim()] | ||
} | ||
let cleaner = this.all.cleaner() | ||
if ( | ||
cleaner.remove[prop] && | ||
cleaner.remove[prop].remove && | ||
!this.isHack(all, unprefixed) | ||
) { | ||
return true | ||
/** | ||
* Return array of Declaration with all necessary prefixes | ||
*/ | ||
prefixed(str) { | ||
let rule = this.virtual(str) | ||
if (this.disabled(rule.first)) { | ||
return rule.nodes | ||
} | ||
for (let checker of cleaner.values('remove', unprefixed)) { | ||
if (checker.check(value)) { | ||
return true | ||
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 false | ||
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) { | ||
remove(nodes, all) { | ||
let i = 0 | ||
@@ -193,116 +271,38 @@ while (i < nodes.length) { | ||
/** | ||
* Clean brackets with one child | ||
* Return true if we need to remove node | ||
*/ | ||
cleanBrackets (nodes) { | ||
return nodes.map(i => { | ||
if (typeof i !== 'object') { | ||
return i | ||
} | ||
toRemove(str, all) { | ||
let [prop, value] = this.parse(str) | ||
let unprefixed = this.all.unprefixed(prop) | ||
if (i.length === 1 && typeof i[0] === 'object') { | ||
return this.cleanBrackets(i[0]) | ||
} | ||
let cleaner = this.all.cleaner() | ||
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 ') | ||
if ( | ||
cleaner.remove[prop] && | ||
cleaner.remove[prop].remove && | ||
!this.isHack(all, unprefixed) | ||
) { | ||
return true | ||
} | ||
result[result.length - 1] = '' | ||
return result | ||
} | ||
/** | ||
* Compress value functions into a string nodes | ||
*/ | ||
normalize (nodes) { | ||
if (typeof nodes !== 'object') { | ||
return nodes | ||
} | ||
nodes = nodes.filter(i => i !== '') | ||
if (typeof nodes[0] === 'string') { | ||
let firstNode = nodes[0].trim() | ||
if ( | ||
firstNode.includes(':') || | ||
firstNode === 'selector' || | ||
firstNode === 'not selector' | ||
) { | ||
return [brackets.stringify(nodes)] | ||
for (let checker of cleaner.values('remove', unprefixed)) { | ||
if (checker.check(value)) { | ||
return true | ||
} | ||
} | ||
return nodes.map(i => this.normalize(i)) | ||
} | ||
/** | ||
* Add prefixes | ||
*/ | ||
add (nodes, all) { | ||
return nodes.map(i => { | ||
if (this.isProp(i)) { | ||
let prefixed = this.prefixed(i[0]) | ||
if (prefixed.length > 1) { | ||
return this.convert(prefixed) | ||
} | ||
return i | ||
} | ||
if (typeof i === 'object') { | ||
return this.add(i, all) | ||
} | ||
return i | ||
}) | ||
return false | ||
} | ||
/** | ||
* Add prefixed declaration | ||
* Create virtual rule to process it by prefixer | ||
*/ | ||
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) | ||
virtual(str) { | ||
let [prop, value] = this.parse(str) | ||
let rule = parse('a{}').first | ||
rule.append({ prop, raws: { before: '' }, value }) | ||
return rule | ||
} | ||
/** | ||
* Check global options | ||
*/ | ||
disabled (node) { | ||
if (!this.all.options.grid) { | ||
if (node.prop === 'display' && node.value.includes('grid')) { | ||
return true | ||
} | ||
if (node.prop.includes('grid') || node.prop === 'justify-items') { | ||
return true | ||
} | ||
} | ||
if (this.all.options.flexbox === false) { | ||
if (node.prop === 'display' && node.value.includes('flex')) { | ||
return true | ||
} | ||
let other = ['order', 'justify-content', 'align-items', 'align-content'] | ||
if (node.prop.includes('flex') || other.includes(node.prop)) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
} | ||
module.exports = Supports |
@@ -8,3 +8,3 @@ let { list } = require('postcss') | ||
class Transition { | ||
constructor (prefixes) { | ||
constructor(prefixes) { | ||
this.props = ['transition', 'transition-property'] | ||
@@ -17,3 +17,3 @@ this.prefixes = prefixes | ||
*/ | ||
add (decl, result) { | ||
add(decl, result) { | ||
let prefix, prop | ||
@@ -85,20 +85,5 @@ let add = this.prefixes.add[decl.prop] | ||
/** | ||
* Find property name | ||
*/ | ||
findProp (param) { | ||
let prop = param[0].value | ||
if (/^\d/.test(prop)) { | ||
for (let [i, token] of param.entries()) { | ||
if (i !== 0 && token.type === 'word') { | ||
return token.value | ||
} | ||
} | ||
} | ||
return prop | ||
} | ||
/** | ||
* Does we already have this declaration | ||
*/ | ||
already (decl, prop, value) { | ||
already(decl, prop, value) { | ||
return decl.parent.some(i => i.prop === prop && i.value === value) | ||
@@ -108,14 +93,5 @@ } | ||
/** | ||
* Add declaration if it is not exist | ||
*/ | ||
cloneBefore (decl, prop, value) { | ||
if (!this.already(decl, prop, value)) { | ||
decl.cloneBefore({ prop, value }) | ||
} | ||
} | ||
/** | ||
* Show transition-property warning | ||
*/ | ||
checkForWarning (result, decl) { | ||
checkForWarning(result, decl) { | ||
if (decl.prop !== 'transition-property') { | ||
@@ -163,75 +139,26 @@ return | ||
/** | ||
* Process transition and remove all unnecessary properties | ||
* Remove all non-webkit prefixes and unprefixed params if we have prefixed | ||
*/ | ||
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) | ||
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)) | ||
if (decl.value === value) { | ||
return | ||
} | ||
if (params.length === 0) { | ||
decl.remove() | ||
return | ||
} | ||
let double = decl.parent.some(i => { | ||
return i.prop === decl.prop && i.value === value | ||
}) | ||
let smaller = decl.parent.some(i => { | ||
return i !== decl && i.prop === decl.prop && i.value.length > value.length | ||
}) | ||
if (double || smaller) { | ||
decl.remove() | ||
return | ||
} | ||
decl.value = value | ||
} | ||
/** | ||
* Parse properties list to array | ||
*/ | ||
parse (value) { | ||
let ast = parser(value) | ||
let result = [] | ||
let param = [] | ||
for (let node of ast.nodes) { | ||
param.push(node) | ||
if (node.type === 'div' && node.value === ',') { | ||
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) | ||
param = [] | ||
} | ||
} | ||
result.push(param) | ||
return result.filter(i => i.length > 0) | ||
return result | ||
} | ||
/** | ||
* 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 }) | ||
cleanOtherPrefixes(params, prefix) { | ||
return params.filter(param => { | ||
let current = vendor.prefix(this.findProp(param)) | ||
return current === '' || current === prefix | ||
}) | ||
} | ||
@@ -242,3 +169,3 @@ | ||
*/ | ||
clone (origin, name, param) { | ||
clone(origin, name, param) { | ||
let result = [] | ||
@@ -258,5 +185,31 @@ let changed = false | ||
/** | ||
* 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) { | ||
div(params) { | ||
for (let param of params) { | ||
@@ -269,47 +222,71 @@ for (let node of param) { | ||
} | ||
return { type: 'div', value: ',', after: ' ' } | ||
return { after: ' ', type: 'div', value: ',' } | ||
} | ||
cleanOtherPrefixes (params, prefix) { | ||
return params.filter(param => { | ||
let current = vendor.prefix(this.findProp(param)) | ||
return current === '' || current === prefix | ||
}) | ||
/** | ||
* 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 | ||
} | ||
/** | ||
* Remove all non-webkit prefixes and unprefixed params if we have prefixed | ||
* Parse properties list to array | ||
*/ | ||
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)) | ||
parse(value) { | ||
let ast = parser(value) | ||
let result = [] | ||
for (let param of params) { | ||
let prop = this.findProp(param) | ||
let p = vendor.prefix(prop) | ||
if (!remove.includes(prop) && (p === prefix || p === '')) { | ||
let param = [] | ||
for (let node of ast.nodes) { | ||
param.push(node) | ||
if (node.type === 'div' && node.value === ',') { | ||
result.push(param) | ||
param = [] | ||
} | ||
} | ||
return result | ||
result.push(param) | ||
return result.filter(i => i.length > 0) | ||
} | ||
/** | ||
* Check property for disabled by option | ||
* Process transition and remove all unnecessary properties | ||
*/ | ||
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 | ||
} | ||
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 (this.prefixes.options.flexbox === 'no-2009') { | ||
return prefix.includes('2009') | ||
} | ||
if (decl.value === value) { | ||
return | ||
} | ||
return undefined | ||
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 | ||
} | ||
@@ -320,3 +297,3 @@ | ||
*/ | ||
ruleVendorPrefixes (decl) { | ||
ruleVendorPrefixes(decl) { | ||
let { parent } = decl | ||
@@ -336,4 +313,27 @@ | ||
} | ||
/** | ||
* 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 |
145
lib/utils.js
let { list } = require('postcss') | ||
module.exports = { | ||
/** | ||
* Throw special error, to tell beniary, | ||
* that this error is from Autoprefixer. | ||
*/ | ||
error (text) { | ||
let err = new Error(text) | ||
err.autoprefixer = true | ||
throw err | ||
}, | ||
/** | ||
* 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. | ||
*/ | ||
uniq (array) { | ||
return [...new Set(array)] | ||
}, | ||
/** | ||
* Return array, that doesn’t contain duplicates. | ||
*/ | ||
module.exports.uniq = function (array) { | ||
return [...new Set(array)] | ||
} | ||
/** | ||
* Return "-webkit-" on "-webkit- old" | ||
*/ | ||
removeNote (string) { | ||
if (!string.includes(' ')) { | ||
return string | ||
} | ||
/** | ||
* Return "-webkit-" on "-webkit- old" | ||
*/ | ||
module.exports.removeNote = function (string) { | ||
if (!string.includes(' ')) { | ||
return string | ||
} | ||
return string.split(' ')[0] | ||
}, | ||
return string.split(' ')[0] | ||
} | ||
/** | ||
* Escape RegExp symbols | ||
*/ | ||
escapeRegexp (string) { | ||
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&') | ||
}, | ||
/** | ||
* Escape RegExp symbols | ||
*/ | ||
module.exports.escapeRegexp = function (string) { | ||
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&') | ||
} | ||
/** | ||
* Return regexp to check, that CSS string contain word | ||
*/ | ||
regexp (word, escape = true) { | ||
if (escape) { | ||
word = this.escapeRegexp(word) | ||
} | ||
return new RegExp(`(^|[\\s,(])(${word}($|[\\s(,]))`, 'gi') | ||
}, | ||
/** | ||
* 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 | ||
*/ | ||
editList (value, callback) { | ||
let origin = list.comma(value) | ||
let changed = callback(origin, []) | ||
/** | ||
* Change comma list | ||
*/ | ||
module.exports.editList = function (value, callback) { | ||
let origin = list.comma(value) | ||
let changed = callback(origin, []) | ||
if (origin === changed) { | ||
return value | ||
} | ||
if (origin === changed) { | ||
return value | ||
} | ||
let join = value.match(/,\s*/) | ||
join = join ? join[0] : ', ' | ||
return changed.join(join) | ||
}, | ||
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 | ||
*/ | ||
splitSelector (selector) { | ||
return list.comma(selector).map(i => { | ||
return list.space(i).map(k => { | ||
return k.split(/(?=\.|#)/g) | ||
}) | ||
/** | ||
* 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 | ||
} |
@@ -10,3 +10,3 @@ let Prefixer = require('./prefixer') | ||
*/ | ||
static save (prefixes, decl) { | ||
static save(prefixes, decl) { | ||
let prop = decl.prop | ||
@@ -63,5 +63,24 @@ let 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) { | ||
check(decl) { | ||
let value = decl.value | ||
@@ -76,5 +95,12 @@ if (!value.includes(this.name)) { | ||
/** | ||
* Return function to fast find prefixed value | ||
*/ | ||
old(prefix) { | ||
return new OldValue(this.name, prefix + this.name) | ||
} | ||
/** | ||
* Lazy regexp loading | ||
*/ | ||
regexp () { | ||
regexp() { | ||
return this.regexpCache || (this.regexpCache = utils.regexp(this.name)) | ||
@@ -86,3 +112,3 @@ } | ||
*/ | ||
replace (string, prefix) { | ||
replace(string, prefix) { | ||
return string.replace(this.regexp(), `$1${prefix}$2`) | ||
@@ -94,3 +120,3 @@ } | ||
*/ | ||
value (decl) { | ||
value(decl) { | ||
if (decl.raws.value && decl.raws.value.value === decl.value) { | ||
@@ -102,30 +128,4 @@ return decl.raws.value.raw | ||
} | ||
/** | ||
* Save values with next prefixed token | ||
*/ | ||
add (decl, prefix) { | ||
if (!decl._autoprefixerValues) { | ||
decl._autoprefixerValues = {} | ||
} | ||
let value = decl._autoprefixerValues[prefix] || this.value(decl) | ||
let before | ||
do { | ||
before = value | ||
value = this.replace(value, prefix) | ||
if (value === false) return | ||
} while (value !== before) | ||
decl._autoprefixerValues[prefix] = value | ||
} | ||
/** | ||
* Return function to fast find prefixed value | ||
*/ | ||
old (prefix) { | ||
return new OldValue(this.name, prefix + this.name) | ||
} | ||
} | ||
module.exports = Value |
module.exports = { | ||
prefix (prop) { | ||
prefix(prop) { | ||
let match = prop.match(/^(-\w+-)/) | ||
@@ -11,5 +11,5 @@ if (match) { | ||
unprefixed (prop) { | ||
unprefixed(prop) { | ||
return prop.replace(/^-\w+-/, '') | ||
} | ||
} |
{ | ||
"name": "autoprefixer", | ||
"version": "10.2.5", | ||
"version": "10.4.16", | ||
"description": "Parse CSS and add vendor prefixes to CSS rules using values from the Can I Use website", | ||
@@ -18,9 +18,22 @@ "engines": { | ||
"types": "lib/autoprefixer.d.ts", | ||
"funding": { | ||
"type": "opencollective", | ||
"url": "https://opencollective.com/postcss/" | ||
}, | ||
"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": { | ||
@@ -30,13 +43,9 @@ "postcss": "^8.1.0" | ||
"dependencies": { | ||
"browserslist": "^4.16.3", | ||
"caniuse-lite": "^1.0.30001196", | ||
"colorette": "^1.2.2", | ||
"fraction.js": "^4.0.13", | ||
"browserslist": "^4.21.10", | ||
"caniuse-lite": "^1.0.30001538", | ||
"fraction.js": "^4.3.6", | ||
"normalize-range": "^0.1.2", | ||
"postcss-value-parser": "^4.1.0" | ||
}, | ||
"browser": { | ||
"colorette": false, | ||
"chalk": false | ||
"picocolors": "^1.0.0", | ||
"postcss-value-parser": "^4.2.0" | ||
} | ||
} |
1062
README.md
# Autoprefixer [![Cult Of Martians][cult-img]][cult] | ||
<img align="right" width="94" height="71" | ||
src="http://postcss.github.io/autoprefixer/logo.svg" | ||
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. | ||
from [Can I Use]. It is recommended by Google and used in Twitter and Alibaba. | ||
@@ -36,5 +36,2 @@ Write your CSS rules without vendor prefixes (in fact, forget about them | ||
} | ||
:-ms-input-placeholder { | ||
color: gray; | ||
} | ||
::placeholder { | ||
@@ -63,1056 +60,9 @@ color: gray; | ||
[@autoprefixer]: https://twitter.com/autoprefixer | ||
[recommended]: https://developers.google.com/web/tools/setup/setup-buildtools#dont_trip_up_with_vendor_prefixes | ||
[Can I Use]: https://caniuse.com/ | ||
[cult-img]: http://cultofmartians.com/assets/badges/badge.svg | ||
[cult-img]: https://cultofmartians.com/assets/badges/badge.svg | ||
[PostCSS]: https://github.com/postcss/postcss | ||
[cult]: http://cultofmartians.com/tasks/autoprefixer-grid.html | ||
[cult]: https://cultofmartians.com/tasks/autoprefixer-grid.html | ||
## Contents | ||
* [Contents](#contents) | ||
* [Browsers](#browsers) | ||
* [FAQ](#faq) | ||
* [Does Autoprefixer polyfill Grid Layout for IE?](#does-autoprefixer-polyfill-grid-layout-for-ie) | ||
* [Does it add polyfills?](#does-it-add-polyfills) | ||
* [Why doesn’t Autoprefixer add prefixes to `border-radius`?](#why-doesnt-autoprefixer-add-prefixes-to-border-radius) | ||
* [Why does Autoprefixer use unprefixed properties in `@-webkit-keyframes`?](#why-does-autoprefixer-use-unprefixed-properties-in--webkit-keyframes) | ||
* [How to work with legacy `-webkit-` only code?](#how-to-work-with-legacy--webkit--only-code) | ||
* [Does Autoprefixer add `-epub-` prefix?](#does-autoprefixer-add--epub--prefix) | ||
* [Why doesn’t Autoprefixer transform generic font-family `system-ui`?](#why-doesnt-autoprefixer-transform-generic-font-family-system-ui) | ||
* [Usage](#usage) | ||
* [Gulp](#gulp) | ||
* [Webpack](#webpack) | ||
* [CSS-in-JS](#css-in-js) | ||
* [CLI](#cli) | ||
* [Other Build Tools](#other-build-tools) | ||
* [Preprocessors](#preprocessors) | ||
* [GUI Tools](#gui-tools) | ||
* [JavaScript](#javascript) | ||
* [Text Editors and IDE](#text-editors-and-ide) | ||
* [Warnings](#warnings) | ||
* [Disabling](#disabling) | ||
* [Prefixes](#prefixes) | ||
* [Features](#features) | ||
* [Control Comments](#control-comments) | ||
* [Options](#options) | ||
* [Environment Variables](#environment-variables) | ||
* [Using environment variables to support CSS Grid prefixes in Create React App](#using-environment-variables-to-support-css-grid-prefixes-in-create-react-app) | ||
* [Grid Autoplacement support in IE](#grid-autoplacement-support-in-ie) | ||
* [Beware of enabling autoplacement in old projects](#beware-of-enabling-autoplacement-in-old-projects) | ||
* [Autoplacement limitations](#autoplacement-limitations) | ||
* [Both columns and rows must be defined](#both-columns-and-rows-must-be-defined) | ||
* [Repeat auto-fit and auto-fill are not supported](#repeat-auto-fit-and-auto-fill-are-not-supported) | ||
* [No manual cell placement or column/row spans allowed inside an autoplacement grid](#no-manual-cell-placement-or-columnrow-spans-allowed-inside-an-autoplacement-grid) | ||
* [Do not create `::before` and `::after` pseudo elements](#do-not-create-before-and-after-pseudo-elements) | ||
* [When changing the `grid gap` value, columns and rows must be re-declared](#when-changing-the-grid-gap-value-columns-and-rows-must-be-re-declared) | ||
* [Debug](#debug) | ||
* [Security Contact](#security-contact) | ||
* [For Enterprise](#for-enterprise) | ||
## Browsers | ||
Autoprefixer uses [Browserslist], so you can specify the browsers | ||
you want to target in your project with queries like `> 5%` | ||
(see [Best Practices]). | ||
The best way to provide browsers is a `.browserslistrc` file in your project | ||
root, or by adding a `browserslist` key to your `package.json`. | ||
We recommend the use of these options over passing options to Autoprefixer so | ||
that the config can be shared with other tools such as [babel-preset-env] and | ||
[Stylelint]. | ||
See [Browserslist docs] for queries, browser names, config format, and defaults. | ||
[Browserslist docs]: https://github.com/browserslist/browserslist#queries | ||
[babel-preset-env]: https://github.com/babel/babel/tree/master/packages/babel-preset-env | ||
[Best Practices]: https://github.com/browserslist/browserslist#best-practices | ||
[Browserslist]: https://github.com/browserslist/browserslist | ||
[Stylelint]: https://stylelint.io/ | ||
## FAQ | ||
### Does Autoprefixer polyfill Grid Layout for IE? | ||
Autoprefixer can be used to translate modern CSS Grid syntax into IE 10 | ||
and IE 11 syntax, but this polyfill will not work in 100% of cases. | ||
This is why it is disabled by default. | ||
First, you need to enable Grid prefixes by using either the `grid: "autoplace"` | ||
option or the `/* autoprefixer grid: autoplace */` control comment. | ||
Also you can use environment variable to enable Grid: | ||
`AUTOPREFIXER_GRID=autoplace npm build`. | ||
Second, you need to test every fix with Grid in IE. It is not an enable and | ||
forget feature, but it is still very useful. | ||
Financial Times and Yandex use it in production. | ||
Third, there is only very limited auto placement support. Read the | ||
[Grid Autoplacement support in IE](#grid-autoplacement-support-in-ie) section | ||
for more details. | ||
Fourth, if you are not using the autoplacement feature, the best way | ||
to use Autoprefixer is by using `grid-template` or `grid-template-areas`. | ||
```css | ||
.page { | ||
display: grid; | ||
grid-gap: 33px; | ||
grid-template: | ||
"head head head" 1fr | ||
"nav main main" minmax(100px, 1fr) | ||
"nav foot foot" 2fr / | ||
1fr 100px 1fr; | ||
} | ||
.page__head { | ||
grid-area: head; | ||
} | ||
.page__nav { | ||
grid-area: nav; | ||
} | ||
.page__main { | ||
grid-area: main; | ||
} | ||
.page__footer { | ||
grid-area: foot; | ||
} | ||
``` | ||
See also: | ||
* [The guide about Grids in IE and Autoprefixer]. | ||
* [`postcss-gap-properties`] to use new `gap` property | ||
instead of old `grid-gap`. | ||
* [`postcss-grid-kiss`] has alternate “everything in one property” syntax, | ||
which makes using Autoprefixer’s Grid translations safer. | ||
[The guide about Grids in IE and Autoprefixer]: https://css-tricks.com/css-grid-in-ie-css-grid-and-the-new-autoprefixer/ | ||
[`postcss-gap-properties`]: https://github.com/jonathantneal/postcss-gap-properties | ||
[`postcss-grid-kiss`]: https://github.com/sylvainpolletvillard/postcss-grid-kiss | ||
### Does it add polyfills? | ||
No. Autoprefixer only adds prefixes. | ||
Most new CSS features will require client side JavaScript to handle a new | ||
behavior correctly. | ||
Depending on what you consider to be a “polyfill”, you can take a look at some | ||
other tools and libraries. If you are just looking for syntax sugar, | ||
you might take a look at: | ||
- [postcss-preset-env] is a plugins preset with polyfills and Autoprefixer | ||
to write future CSS today. | ||
- [Oldie], a PostCSS plugin that handles some IE hacks (opacity, rgba, etc). | ||
- [postcss-flexbugs-fixes], a PostCSS plugin to fix flexbox issues. | ||
[postcss-flexbugs-fixes]: https://github.com/luisrudge/postcss-flexbugs-fixes | ||
[postcss-preset-env]: https://github.com/jonathantneal/postcss-preset-env | ||
[Oldie]: https://github.com/jonathantneal/oldie | ||
### Why doesn’t Autoprefixer add prefixes to `border-radius`? | ||
Developers are often surprised by how few prefixes are required today. | ||
If Autoprefixer doesn’t add prefixes to your CSS, check if they’re still | ||
required on [Can I Use]. | ||
[Can I Use]: https://caniuse.com/ | ||
### Why does Autoprefixer use unprefixed properties in `@-webkit-keyframes`? | ||
Browser teams can remove some prefixes before others, so we try to use all | ||
combinations of prefixed/unprefixed values. | ||
### How to work with legacy `-webkit-` only code? | ||
Autoprefixer needs unprefixed property to add prefixes. So if you only | ||
wrote `-webkit-gradient` without W3C’s `gradient`, | ||
Autoprefixer will not add other prefixes. | ||
But [PostCSS] has plugins to convert CSS to unprefixed state. | ||
Use [postcss-unprefix] before Autoprefixer. | ||
[postcss-unprefix]: https://github.com/gucong3000/postcss-unprefix | ||
### Does Autoprefixer add `-epub-` prefix? | ||
No, Autoprefixer works only with browsers prefixes from Can I Use. | ||
But you can use [postcss-epub] for prefixing ePub3 properties. | ||
[postcss-epub]: https://github.com/Rycochet/postcss-epub | ||
### Why doesn’t Autoprefixer transform generic font-family `system-ui`? | ||
`system-ui` is technically not a prefix and the transformation is not | ||
future-proof. You can use [postcss-font-family-system-ui] to transform | ||
`system-ui` to a practical font-family list. | ||
[postcss-font-family-system-ui]: https://github.com/JLHwung/postcss-font-family-system-ui | ||
## Usage | ||
### Gulp | ||
In Gulp you can use [gulp-postcss] with `autoprefixer` npm package. | ||
```js | ||
gulp.task('autoprefixer', () => { | ||
const autoprefixer = require('autoprefixer') | ||
const sourcemaps = require('gulp-sourcemaps') | ||
const postcss = require('gulp-postcss') | ||
return gulp.src('./src/*.css') | ||
.pipe(sourcemaps.init()) | ||
.pipe(postcss([ autoprefixer() ])) | ||
.pipe(sourcemaps.write('.')) | ||
.pipe(gulp.dest('./dest')) | ||
}) | ||
``` | ||
With `gulp-postcss` you also can combine Autoprefixer | ||
with [other PostCSS plugins]. | ||
[gulp-postcss]: https://github.com/postcss/gulp-postcss | ||
[other PostCSS plugins]: https://github.com/postcss/postcss#plugins | ||
### Webpack | ||
In [webpack] you can use [postcss-loader] with `autoprefixer` | ||
and [other PostCSS plugins]. | ||
```js | ||
module.exports = { | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.css$/, | ||
use: ["style-loader", "css-loader", "postcss-loader"] | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
And create a `postcss.config.js` with: | ||
```js | ||
module.exports = { | ||
plugins: [ | ||
require('autoprefixer') | ||
] | ||
} | ||
``` | ||
[other PostCSS plugins]: https://github.com/postcss/postcss#plugins | ||
[postcss-loader]: https://github.com/postcss/postcss-loader | ||
[webpack]: https://webpack.js.org/ | ||
### CSS-in-JS | ||
The best way to use PostCSS with CSS-in-JS is [`astroturf`]. | ||
Add its loader to your `webpack.config.js`: | ||
```js | ||
module.exports = { | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.css$/, | ||
use: ['style-loader', 'postcss-loader'], | ||
}, | ||
{ | ||
test: /\.jsx?$/, | ||
use: ['babel-loader', 'astroturf/loader'], | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
Then create `postcss.config.js`: | ||
```js | ||
module.exports = { | ||
plugins: [ | ||
require('autoprefixer') | ||
] | ||
} | ||
``` | ||
[`astroturf`]: https://github.com/4Catalyzer/astroturf | ||
### CLI | ||
You can use the [postcss-cli] to run Autoprefixer from CLI: | ||
```sh | ||
npm install postcss-cli autoprefixer | ||
npx postcss *.css --use autoprefixer -d build/ | ||
``` | ||
See `postcss -h` for help. | ||
[postcss-cli]: https://github.com/postcss/postcss-cli | ||
### Other Build Tools | ||
* **Grunt:** [grunt-postcss] | ||
* **Ruby on Rails**: [autoprefixer-rails] | ||
* **Neutrino**: [neutrino-middleware-postcss] | ||
* **Jekyll**: add `autoprefixer-rails` and `jekyll-assets` to `Gemfile` | ||
* **Brunch**: [postcss-brunch] | ||
* **Broccoli**: [broccoli-postcss] | ||
* **Middleman**: [middleman-autoprefixer] | ||
* **Mincer**: add `autoprefixer` npm package and enable it: | ||
`environment.enable('autoprefixer')` | ||
[neutrino-middleware-postcss]: https://www.npmjs.com/package/neutrino-middleware-postcss | ||
[middleman-autoprefixer]: https://github.com/middleman/middleman-autoprefixer | ||
[autoprefixer-rails]: https://github.com/ai/autoprefixer-rails | ||
[broccoli-postcss]: https://github.com/jeffjewiss/broccoli-postcss | ||
[postcss-brunch]: https://github.com/iamvdo/postcss-brunch | ||
[grunt-postcss]: https://github.com/C-Lodder/grunt-postcss | ||
#### Preprocessors | ||
* **Less**: [less-plugin-autoprefix] | ||
* **Stylus**: [autoprefixer-stylus] | ||
* **Compass**: [autoprefixer-rails#compass] | ||
[less-plugin-autoprefix]: https://github.com/less/less-plugin-autoprefix | ||
[autoprefixer-stylus]: https://github.com/jenius/autoprefixer-stylus | ||
[autoprefixer-rails#compass]: https://github.com/ai/autoprefixer-rails#compass | ||
#### GUI Tools | ||
* [CodeKit](https://codekitapp.com/help/autoprefixer/) | ||
* [Prepros](https://prepros.io) | ||
### JavaScript | ||
You can use Autoprefixer with [PostCSS] in your Node.js application | ||
or if you want to develop an Autoprefixer plugin for a new environment. | ||
```js | ||
const autoprefixer = require('autoprefixer') | ||
const postcss = require('postcss') | ||
postcss([ autoprefixer ]).process(css).then(result => { | ||
result.warnings().forEach(warn => { | ||
console.warn(warn.toString()) | ||
}) | ||
console.log(result.css) | ||
}) | ||
``` | ||
There is also a [standalone build] for the browser or for a non-Node.js runtime. | ||
You can use [html-autoprefixer] to process HTML with inlined CSS. | ||
[html-autoprefixer]: https://github.com/RebelMail/html-autoprefixer | ||
[standalone build]: https://raw.github.com/ai/autoprefixer-rails/master/vendor/autoprefixer.js | ||
[PostCSS]: https://github.com/postcss/postcss | ||
### Text Editors and IDE | ||
Autoprefixer should be used in assets build tools. Text editor plugins are not | ||
a good solution, because prefixes decrease code readability and you will need | ||
to change values in all prefixed properties. | ||
I recommend you to learn how to use build tools like [Parcel]. | ||
They work much better and will open you a whole new world of useful plugins | ||
and automation. | ||
If you can’t move to a build tool, you can use text editor plugins: | ||
* [Visual Studio Code](https://github.com/mrmlnc/vscode-autoprefixer) | ||
* [Atom Editor](https://github.com/sindresorhus/atom-autoprefixer) | ||
* [Sublime Text](https://github.com/sindresorhus/sublime-autoprefixer) | ||
* [Brackets](https://github.com/mikaeljorhult/brackets-autoprefixer) | ||
[Parcel]: https://parceljs.org/ | ||
## Warnings | ||
Autoprefixer uses the [PostCSS warning API] to warn about really important | ||
problems in your CSS: | ||
* Old direction syntax in gradients. | ||
* Old unprefixed `display: box` instead of `display: flex` | ||
by latest specification version. | ||
You can get warnings from `result.warnings()`: | ||
```js | ||
result.warnings().forEach(warn => { | ||
console.warn(warn.toString()) | ||
}) | ||
``` | ||
Every Autoprefixer runner should display these warnings. | ||
[PostCSS warning API]: http://api.postcss.org/Warning.html | ||
## Disabling | ||
### Prefixes | ||
Autoprefixer was designed to have no interface – it just works. | ||
If you need some browser specific hack just write a prefixed property | ||
after the unprefixed one. | ||
```css | ||
a { | ||
transform: scale(0.5); | ||
-moz-transform: scale(0.6); | ||
} | ||
``` | ||
If some prefixes were generated incorrectly, please create an [issue on GitHub]. | ||
[issue on GitHub]: https://github.com/postcss/autoprefixer/issues | ||
### Features | ||
You can use these plugin options to control some of Autoprefixer’s features. | ||
* `grid: "autoplace"` will enable `-ms-` prefixes for Grid Layout including some | ||
[limited autoplacement support](#grid-autoplacement-support-in-ie). | ||
* `supports: false` will disable `@supports` parameters prefixing. | ||
* `flexbox: false` will disable flexbox properties prefixing. | ||
Or `flexbox: "no-2009"` will add prefixes only for final and IE | ||
versions of specification. | ||
* `remove: false` will disable cleaning outdated prefixes. | ||
You should set them inside the plugin like so: | ||
```js | ||
autoprefixer({ grid: 'autoplace' }) | ||
``` | ||
### Control Comments | ||
If you do not need Autoprefixer in some part of your CSS, | ||
you can use control comments to disable Autoprefixer. | ||
```css | ||
.a { | ||
transition: 1s; /* will be prefixed */ | ||
} | ||
.b { | ||
/* autoprefixer: off */ | ||
transition: 1s; /* will not be prefixed */ | ||
} | ||
.c { | ||
/* autoprefixer: ignore next */ | ||
transition: 1s; /* will not be prefixed */ | ||
mask: url(image.png); /* will be prefixed */ | ||
} | ||
``` | ||
There are three types of control comments: | ||
* `/* autoprefixer: (on|off) */`: enable/disable all Autoprefixer translations for the | ||
whole block both *before* and *after* the comment. | ||
* `/* autoprefixer: ignore next */`: disable Autoprefixer only for the next property | ||
or next rule selector or at-rule parameters (but not rule/at‑rule body). | ||
* `/* autoprefixer grid: (autoplace|no-autoplace|off) */`: control how Autoprefixer handles | ||
grid translations for the whole block: | ||
* `autoplace`: enable grid translations with autoplacement support. | ||
* `no-autoplace`: enable grid translations with autoplacement | ||
support *disabled* (alias for deprecated value `on`). | ||
* `off`: disable all grid translations. | ||
You can also use comments recursively: | ||
```css | ||
/* autoprefixer: off */ | ||
@supports (transition: all) { | ||
/* autoprefixer: on */ | ||
a { | ||
/* autoprefixer: off */ | ||
} | ||
} | ||
``` | ||
Note that comments that disable the whole block should not be featured in the same | ||
block twice: | ||
```css | ||
/* How not to use block level control comments */ | ||
.do-not-do-this { | ||
/* autoprefixer: off */ | ||
transition: 1s; | ||
/* autoprefixer: on */ | ||
transform: rotate(20deg); | ||
} | ||
``` | ||
## Options | ||
Function `autoprefixer(options)` returns a new PostCSS plugin. | ||
See [PostCSS API] for plugin usage documentation. | ||
```js | ||
autoprefixer({ cascade: false }) | ||
``` | ||
Available options are: | ||
* `env` (string): environment for Browserslist. | ||
* `cascade` (boolean): should Autoprefixer use Visual Cascade, | ||
if CSS is uncompressed. Default: `true` | ||
* `add` (boolean): should Autoprefixer add prefixes. Default is `true`. | ||
* `remove` (boolean): should Autoprefixer [remove outdated] prefixes. | ||
Default is `true`. | ||
* `supports` (boolean): should Autoprefixer add prefixes for `@supports` | ||
parameters. Default is `true`. | ||
* `flexbox` (boolean|string): should Autoprefixer add prefixes for flexbox | ||
properties. With `"no-2009"` value Autoprefixer will add prefixes only | ||
for final and IE 10 versions of specification. Default is `true`. | ||
* `grid` (false|`"autoplace"`|`"no-autoplace"`): should Autoprefixer | ||
add IE 10-11 prefixes for Grid Layout properties? | ||
* `false` (default): prevent Autoprefixer from outputting | ||
CSS Grid translations. | ||
* `"autoplace"`: enable Autoprefixer grid translations | ||
and *include* autoplacement support. You can also use | ||
`/* autoprefixer grid: autoplace */` in your CSS. | ||
* `"no-autoplace"`: enable Autoprefixer grid translations | ||
but *exclude* autoplacement support. You can also use | ||
`/* autoprefixer grid: no-autoplace */` in your CSS. | ||
(alias for the deprecated `true` value) | ||
* `stats` (object): custom [usage statistics] for `> 10% in my stats` | ||
browsers query. | ||
* `overrideBrowserslist` (array): 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. See [Browserslist docs] | ||
for available queries and default value. | ||
* `ignoreUnknownVersions` (boolean): do not raise error on unknown browser | ||
version in Browserslist config. Default is `false`. | ||
Plugin object has `info()` method for debugging purpose. | ||
You can use PostCSS processor to process several CSS files | ||
to increase performance. | ||
[usage statistics]: https://github.com/browserslist/browserslist#custom-usage-data | ||
[PostCSS API]: http://api.postcss.org | ||
## Environment Variables | ||
* `AUTOPREFIXER_GRID`: (`autoplace`|`no-autoplace`) should Autoprefixer | ||
add IE 10-11 prefixes for Grid Layout properties? | ||
* `autoplace`: enable Autoprefixer grid translations | ||
and *include* autoplacement support. | ||
* `no-autoplace`: enable Autoprefixer grid translations | ||
but *exclude* autoplacement support. | ||
Environment variables are useful, when you want to change Autoprefixer options but don't have access to config files. | ||
[Create React App] is a good example of this. | ||
[Create React App]: (https://reactjs.org/docs/create-a-new-react-app.html#create-react-app) | ||
### Using environment variables to support CSS Grid prefixes in Create React App | ||
1. Install the latest version of Autoprefixer and [cross-env](https://www.npmjs.com/package/cross-env): | ||
``` | ||
npm install autoprefixer@latest cross-env --save-dev | ||
``` | ||
2. Under `"browserslist"` > `"development"` in the package.json file, add `"last 1 ie version"` | ||
``` | ||
"browserslist": { | ||
"production": [ | ||
">0.2%", | ||
"not dead", | ||
"not op_mini all" | ||
], | ||
"development": [ | ||
"last 1 chrome version", | ||
"last 1 firefox version", | ||
"last 1 safari version", | ||
"last 1 ie version" | ||
] | ||
} | ||
``` | ||
3. Update `"scripts"` in the package.json file to the following: | ||
``` | ||
"scripts": { | ||
"start": "cross-env AUTOPREFIXER_GRID=autoplace react-scripts start", | ||
"build": "cross-env AUTOPREFIXER_GRID=autoplace react-scripts build", | ||
"test": "cross-env AUTOPREFIXER_GRID=autoplace react-scripts test", | ||
"eject": "react-scripts eject" | ||
}, | ||
``` | ||
Replace `autoplace` with `no-autoplace` in the above example if you prefer to disable Autoprefixer Grid autoplacement support. | ||
Now when you run `npm start` you will see CSS Grid prefixes automatically being applied to your output CSS. | ||
See also [Browserslist environment variables] for more examples on how to use environment variables in your project. | ||
[Browserslist environment variables]: https://github.com/browserslist/browserslist#environment-variables | ||
## Grid Autoplacement support in IE | ||
If the `grid` option is set to `"autoplace"`, limited autoplacement support is added to Autoprefixers grid translations. You can also use | ||
the `/* autoprefixer grid: autoplace */` control comment or | ||
`AUTOPREFIXER_GRID=autoplace npm build` environment variable. | ||
Autoprefixer will only autoplace grid cells if both `grid-template-rows` | ||
and `grid-template-columns` has been set. If `grid-template` | ||
or `grid-template-areas` has been set, Autoprefixer will use area based | ||
cell placement instead. | ||
Autoprefixer supports autoplacement by using `nth-child` CSS selectors. | ||
It creates [number of columns] x [number of rows] `nth-child` selectors. | ||
For this reason Autoplacement is only supported within the explicit grid. | ||
```css | ||
/* Input CSS */ | ||
/* autoprefixer grid: autoplace */ | ||
.autoplacement-example { | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
grid-template-rows: auto auto; | ||
grid-gap: 20px; | ||
} | ||
``` | ||
```css | ||
/* Output CSS */ | ||
/* autoprefixer grid: autoplace */ | ||
.autoplacement-example { | ||
display: -ms-grid; | ||
display: grid; | ||
-ms-grid-columns: 1fr 20px 1fr; | ||
grid-template-columns: 1fr 1fr; | ||
-ms-grid-rows: auto 20px auto; | ||
grid-template-rows: auto auto; | ||
grid-gap: 20px; | ||
} | ||
.autoplacement-example > *:nth-child(1) { | ||
-ms-grid-row: 1; | ||
-ms-grid-column: 1; | ||
} | ||
.autoplacement-example > *:nth-child(2) { | ||
-ms-grid-row: 1; | ||
-ms-grid-column: 3; | ||
} | ||
.autoplacement-example > *:nth-child(3) { | ||
-ms-grid-row: 3; | ||
-ms-grid-column: 1; | ||
} | ||
.autoplacement-example > *:nth-child(4) { | ||
-ms-grid-row: 3; | ||
-ms-grid-column: 3; | ||
} | ||
``` | ||
### Beware of enabling autoplacement in old projects | ||
Be careful about enabling autoplacement in any already established projects that have | ||
previously not used Autoprefixer's grid autoplacement feature before. | ||
If this was your html: | ||
```html | ||
<div class="grid"> | ||
<div class="grid-cell"></div> | ||
</div> | ||
``` | ||
The following CSS will not work as expected with the autoplacement feature enabled: | ||
```css | ||
/* Unsafe CSS when Autoplacement is enabled */ | ||
.grid-cell { | ||
grid-column: 2; | ||
grid-row: 2; | ||
} | ||
.grid { | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-template-rows: repeat(3, 1fr); | ||
} | ||
``` | ||
Swapping the rules around will not fix the issue either: | ||
```css | ||
/* Also unsafe to use this CSS */ | ||
.grid { | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-template-rows: repeat(3, 1fr); | ||
} | ||
.grid-cell { | ||
grid-column: 2; | ||
grid-row: 2; | ||
} | ||
``` | ||
One way to deal with this issue is to disable autoplacement in the | ||
grid-declaration rule: | ||
```css | ||
/* Disable autoplacement to fix the issue */ | ||
.grid { | ||
/* autoprefixer grid: no-autoplace */ | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-template-rows: repeat(3, 1fr); | ||
} | ||
.grid-cell { | ||
grid-column: 2; | ||
grid-row: 2; | ||
} | ||
``` | ||
The absolute best way to integrate autoplacement into already existing projects | ||
though is to leave autoplacement turned off by default and then use a control | ||
comment to enable it when needed. This method is far less likely to cause | ||
something on the site to break. | ||
```css | ||
/* Disable autoplacement by default in old projects */ | ||
/* autoprefixer grid: no-autoplace */ | ||
/* Old code will function the same way it always has */ | ||
.old-grid { | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-template-rows: repeat(3, 1fr); | ||
} | ||
.old-grid-cell { | ||
grid-column: 2; | ||
grid-row: 2; | ||
} | ||
/* Enable autoplacement when you want to use it in new code */ | ||
.new-autoplace-friendly-grid { | ||
/* autoprefixer grid: autoplace */ | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-template-rows: repeat(3, auto); | ||
} | ||
``` | ||
Note that the `grid: "no-autoplace"` setting and the | ||
`/* autoprefixer grid: no-autoplace */` control comment share identical | ||
functionality to the `grid: true` setting and the `/* autoprefixer grid: on */` | ||
control comment. There is no need to refactor old code to use `no-autoplace` | ||
in place of the old `true` and `on` statements. | ||
### Autoplacement limitations | ||
#### Both columns and rows must be defined | ||
Autoplacement only works inside the explicit grid. The columns and rows need to be defined | ||
so that Autoprefixer knows how many `nth-child` selectors to generate. | ||
```css | ||
.not-allowed { | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
} | ||
.is-allowed { | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-template-rows: repeat(10, auto); | ||
} | ||
``` | ||
#### Repeat auto-fit and auto-fill are not supported | ||
The `repeat(auto-fit, ...)` and `repeat(auto-fill, ...)` grid functionality relies on | ||
knowledge from the browser about screen dimensions and the number of available grid | ||
items for it to work properly. Autoprefixer does not have access to this information | ||
so unfortunately this little snippet will _never_ be IE friendly. | ||
```css | ||
.grid { | ||
/* This will never be IE friendly */ | ||
grid-template-columns: repeat(auto-fit, min-max(200px, 1fr)) | ||
} | ||
``` | ||
#### No manual cell placement or column/row spans allowed inside an autoplacement grid | ||
Elements must not be manually placed or given column/row spans inside | ||
an autoplacement grid. Only the most basic of autoplacement grids are supported. | ||
Grid cells can still be placed manually outside the the explicit grid though. | ||
Support for manually placing individual grid cells inside an explicit | ||
autoplacement grid is planned for a future release. | ||
```css | ||
.autoplacement-grid { | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-template-rows: repeat(3, auto); | ||
} | ||
/* Grid cells placed inside the explicit grid | ||
will break the layout in IE */ | ||
.not-permitted-grid-cell { | ||
grid-column: 1; | ||
grid-row: 1; | ||
} | ||
/* Grid cells placed outside the | ||
explicit grid will work in IE */ | ||
.permitted-grid-cell { | ||
grid-column: 1 / span 2; | ||
grid-row: 4; | ||
} | ||
``` | ||
If manual cell placement is required, we recommend using `grid-template` or | ||
`grid-template-areas` instead: | ||
```css | ||
.page { | ||
display: grid; | ||
grid-gap: 30px; | ||
grid-template: | ||
"head head" | ||
"nav main" minmax(100px, 1fr) | ||
"foot foot" / | ||
200px 1fr; | ||
} | ||
.page__head { | ||
grid-area: head; | ||
} | ||
.page__nav { | ||
grid-area: nav; | ||
} | ||
.page__main { | ||
grid-area: main; | ||
} | ||
.page__footer { | ||
grid-area: foot; | ||
} | ||
``` | ||
#### Do not create `::before` and `::after` pseudo elements | ||
Let's say you have this HTML: | ||
```html | ||
<div class="grid"> | ||
<div class="grid-cell"></div> | ||
</div> | ||
``` | ||
And you write this CSS: | ||
```css | ||
.grid { | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
grid-template-rows: auto; | ||
} | ||
.grid::before { | ||
content: 'before'; | ||
} | ||
.grid::after { | ||
content: 'after'; | ||
} | ||
``` | ||
This will be the output: | ||
```css | ||
.grid { | ||
display: -ms-grid; | ||
display: grid; | ||
-ms-grid-columns: 1fr 1fr; | ||
grid-template-columns: 1fr 1fr; | ||
-ms-grid-rows: auto; | ||
grid-template-rows: auto; | ||
} | ||
.grid > *:nth-child(1) { | ||
-ms-grid-row: 1; | ||
-ms-grid-column: 1; | ||
} | ||
.grid > *:nth-child(2) { | ||
-ms-grid-row: 1; | ||
-ms-grid-column: 2; | ||
} | ||
.grid::before { | ||
content: 'before'; | ||
} | ||
.grid::after { | ||
content: 'after'; | ||
} | ||
``` | ||
IE will place `.grid-cell`, `::before` and `::after` in row 1 column 1. | ||
Modern browsers on the other hand will place `::before` in row 1 column 1, | ||
`.grid-cell` in row 1 column 2, and `::after` in row 2 column 1. | ||
See this [CodePen](https://codepen.io/daniel-tonon/pen/gBymVw) to see a visualization | ||
of the issue. View the CodePen in both a modern browser and IE to see the difference. | ||
Note that you can still create `::before` and `::after` elements as long as you manually | ||
place them outside the explicit grid. | ||
#### When changing the `grid gap` value, columns and rows must be re-declared | ||
If you wish to change the size of a `grid-gap`, you will need to redeclare the grid columns and rows. | ||
```css | ||
.grid { | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
grid-template-rows: auto; | ||
grid-gap: 50px; | ||
} | ||
/* This will *NOT* work in IE */ | ||
@media (max-width: 600px) { | ||
.grid { | ||
grid-gap: 20px; | ||
} | ||
} | ||
/* This will *NOT* work in IE */ | ||
.grid.small-gap { | ||
grid-gap: 20px; | ||
} | ||
``` | ||
```css | ||
.grid { | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
grid-template-rows: auto; | ||
grid-gap: 50px; | ||
} | ||
/* This *WILL* work in IE */ | ||
@media (max-width: 600px) { | ||
.grid { | ||
grid-template-columns: 1fr 1fr; | ||
grid-template-rows: auto; | ||
grid-gap: 20px; | ||
} | ||
} | ||
/* This *WILL* work in IE */ | ||
.grid.small-gap { | ||
grid-template-columns: 1fr 1fr; | ||
grid-template-rows: auto; | ||
grid-gap: 20px; | ||
} | ||
``` | ||
## Debug | ||
Run `npx autoprefixer --info` in your project directory to check | ||
which browsers are selected and which properties will be prefixed: | ||
``` | ||
$ npx autoprefixer --info | ||
Browsers: | ||
Edge: 16 | ||
These browsers account for 0.26% of all users globally | ||
At-Rules: | ||
@viewport: ms | ||
Selectors: | ||
::placeholder: ms | ||
Properties: | ||
appearance: webkit | ||
flow-from: ms | ||
flow-into: ms | ||
hyphens: ms | ||
overscroll-behavior: ms | ||
region-fragment: ms | ||
scroll-snap-coordinate: ms | ||
scroll-snap-destination: ms | ||
scroll-snap-points-x: ms | ||
scroll-snap-points-y: ms | ||
scroll-snap-type: ms | ||
text-size-adjust: ms | ||
text-spacing: ms | ||
user-select: ms | ||
``` | ||
JS API is also available: | ||
```js | ||
console.log(autoprefixer().info()) | ||
``` | ||
## Security Contact | ||
To report a security vulnerability, please use the [Tidelift security contact]. | ||
Tidelift will coordinate the fix and disclosure. | ||
[Tidelift security contact]: https://tidelift.com/security | ||
## For Enterprise | ||
Available as part of the Tidelift Subscription. | ||
The maintainers of `autoprefixer` and thousands of other packages are working | ||
with Tidelift to deliver commercial support and maintenance for the open source | ||
dependencies you use to build your applications. Save time, reduce risk, | ||
and improve code health, while paying the maintainers of the exact dependencies | ||
you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-autoprefixer?utm_source=npm-autoprefixer&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) | ||
## Docs | ||
Read full docs **[here](https://github.com/postcss/autoprefixer#readme)**. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
6999
0
199002
67
+ Addedpicocolors@^1.0.0
+ Addedcaniuse-lite@1.0.30001664(transitive)
+ Addedelectron-to-chromium@1.5.29(transitive)
- Removedcolorette@^1.2.2
- Removedcaniuse-lite@1.0.30001667(transitive)
- Removedcolorette@1.4.0(transitive)
- Removedelectron-to-chromium@1.5.32(transitive)
Updatedbrowserslist@^4.21.10
Updatedcaniuse-lite@^1.0.30001538
Updatedfraction.js@^4.3.6
Updatedpostcss-value-parser@^4.2.0