From e5f84467b7e2d0075f59ae44fd2597895cc2dec2 Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Tue, 15 Aug 2017 17:25:57 +0200 Subject: [PATCH 1/2] Normalize numbers in CSS like JS Unlike JS, `1.0` is also normalized to `1`. --- src/clean-ast.js | 10 ++ src/printer-postcss.js | 25 +++- src/printer.js | 25 +--- src/util.js | 20 ++- .../__snapshots__/jsfmt.spec.js.snap | 6 +- .../__snapshots__/jsfmt.spec.js.snap | 132 ++++++++++++++++++ tests/css_numbers/jsfmt.spec.js | 1 + tests/css_numbers/numbers.css | 63 +++++++++ .../__snapshots__/jsfmt.spec.js.snap | 2 +- .../values/__snapshots__/jsfmt.spec.js.snap | 4 +- 10 files changed, 258 insertions(+), 30 deletions(-) create mode 100644 tests/css_numbers/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/css_numbers/jsfmt.spec.js create mode 100644 tests/css_numbers/numbers.css diff --git a/src/clean-ast.js b/src/clean-ast.js index 8184afc5..deee65c9 100644 --- a/src/clean-ast.js +++ b/src/clean-ast.js @@ -104,6 +104,16 @@ function massageAST(ast) { delete newObj.quoted; } + if ( + (ast.type === "media-value" || ast.type === "value-number") && + newObj.value + ) { + newObj.value = newObj.value.replace(/[\d.eE+-]+/g, match => { + const num = Number(match); + return isNaN(num) ? match : num; + }); + } + // (TypeScript) Ignore `static` in `constructor(static p) {}` // and `export` in `constructor(export p) {}` if ( diff --git a/src/printer-postcss.js b/src/printer-postcss.js index 78cf5d17..c0346243 100644 --- a/src/printer-postcss.js +++ b/src/printer-postcss.js @@ -179,7 +179,7 @@ function genericPrint(path, options, print) { return concat([n.value, " "]); } case "media-value": { - return adjustStrings(n.value, options); + return adjustNumbers(adjustStrings(n.value, options)); } case "media-keyword": { return n.value; @@ -337,7 +337,7 @@ function genericPrint(path, options, print) { return n.value; } case "value-number": { - return concat([n.value, n.unit]); + return concat([printNumber(n.value), n.unit]); } case "value-operator": { return n.value; @@ -419,6 +419,11 @@ function printValue(value) { } const STRING_REGEX = /(['"])(?:(?!\1)[^\\]|\\[\s\S])*\1/g; +const NUMBER_REGEX = /(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g; +const STRING_OR_NUMBER_REGEX = RegExp( + `${STRING_REGEX.source}|(${NUMBER_REGEX.source})`, + "g" +); function adjustStrings(value, options) { return value.replace(STRING_REGEX, match => util.printString(match, options)); @@ -431,4 +436,20 @@ function quoteAttributeValue(value, options) { : quote + value + quote; } +function adjustNumbers(value) { + return value.replace( + STRING_OR_NUMBER_REGEX, + (match, quote, number) => (number ? printNumber(number) : match) + ); +} + +function printNumber(rawNumber) { + return ( + util + .printNumber(rawNumber) + // Remove trailing `.0`. + .replace(/\.0(?=$|e)/, "") + ); +} + module.exports = genericPrint; diff --git a/src/printer.js b/src/printer.js index d7cbcd1f..2113025d 100644 --- a/src/printer.js +++ b/src/printer.js @@ -1159,7 +1159,7 @@ function genericPrintNoParens(path, options, print, args) { case "RegExpLiteral": // Babel 6 Literal split return printRegex(n); case "NumericLiteral": // Babel 6 Literal split - return printNumber(n.extra.raw); + return util.printNumber(n.extra.raw); case "BooleanLiteral": // Babel 6 Literal split case "StringLiteral": // Babel 6 Literal split case "Literal": { @@ -1167,7 +1167,7 @@ function genericPrintNoParens(path, options, print, args) { return printRegex(n.regex); } if (typeof n.value === "number") { - return printNumber(n.raw); + return util.printNumber(n.raw); } if (typeof n.value !== "string") { return "" + n.value; @@ -2239,9 +2239,9 @@ function genericPrintNoParens(path, options, print, args) { assert.strictEqual(typeof n.value, "number"); if (n.extra != null) { - return printNumber(n.extra.raw); + return util.printNumber(n.extra.raw); } - return printNumber(n.raw); + return util.printNumber(n.raw); case "StringTypeAnnotation": return "string"; @@ -4336,23 +4336,6 @@ function printRegex(node) { return `/${node.pattern}/${flags}`; } -function printNumber(rawNumber) { - return ( - rawNumber - .toLowerCase() - // Remove unnecessary plus and zeroes from scientific notation. - .replace(/^([\d.]+e)(?:\+|(-))?0*(\d)/, "$1$2$3") - // Remove unnecessary scientific notation (1e0). - .replace(/^([\d.]+)e[+-]?0+$/, "$1") - // Make sure numbers always start with a digit. - .replace(/^\./, "0.") - // Remove extraneous trailing decimal zeroes. - .replace(/(\.\d+?)0+(?=e|$)/, "$1") - // Remove trailing dot. - .replace(/\.(?=e|$)/, "") - ); -} - function isLastStatement(path) { const parent = path.getParentNode(); if (!parent) { diff --git a/src/util.js b/src/util.js index 114171e8..69352aa4 100644 --- a/src/util.js +++ b/src/util.js @@ -567,6 +567,23 @@ function makeString(rawContent, enclosingQuote, unescapeUnnecessaryEscapes) { return enclosingQuote + newContent + enclosingQuote; } +function printNumber(rawNumber) { + return ( + rawNumber + .toLowerCase() + // Remove unnecessary plus and zeroes from scientific notation. + .replace(/^([\d.]+e)(?:\+|(-))?0*(\d)/, "$1$2$3") + // Remove unnecessary scientific notation (1e0). + .replace(/^([\d.]+)e[+-]?0+$/, "$1") + // Make sure numbers always start with a digit. + .replace(/^\./, "0.") + // Remove extraneous trailing decimal zeroes. + .replace(/(\.\d+?)0+(?=e|$)/, "$1") + // Remove trailing dot. + .replace(/\.(?=e|$)/, "") + ); +} + module.exports = { getPrecedence, shouldFlatten, @@ -593,5 +610,6 @@ module.exports = { isBlockComment, hasClosureCompilerTypeCastComment, getAlignmentSize, - printString + printString, + printNumber }; diff --git a/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap b/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap index 7fd6f3a2..8230ead9 100644 --- a/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap @@ -10,11 +10,11 @@ exports[`if-else.css 1`] = ` } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @if $media == phonePortrait { - $k: .15625; + $k: 0.15625; } @else if $media == phoneLandscape { - $k: .08803; + $k: 0.08803; } @else if $media == tabletPortrait { - $k: .065106; + $k: 0.065106; } `; diff --git a/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap b/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..cea61fec --- /dev/null +++ b/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,132 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`numbers.css 1`] = ` +@supports (margin: .5px ".30px" 1E+2px) { + a { + a: 0; + a: 1; + + a: 0.1; + a: 1.1; + + a: .1; + a: 1.; + + a: 1e1; + a: 1e+1; + a: 1e-1; + a: 1.e1; + a: .1e1; + a: 1.1e1; + a: 1.1e0010; + a: .1e+0010; + a: .1e-0010; + + a: 1E1; + a: 1E+1; + a: 1E-1; + a: 1.E1; + a: .1E1; + a: 1.1E1; + a: 1.1E0010; + a: .1E+0010; + a: .1E-0010; + + a: 0.5e0; + a: 0.5e00; + a: 0.5e+0; + a: 0.5e+00; + a: 0.5e-0; + a: 0.5e-00; + + a: 1; + a: 1.00500; + a: 1.0; + a: 1.5; + a: 1.50; + + a: 0.00500; + a: 0.0; + a: 0.0000; + + a: 500600.001230045000; + a: 1.00500e60; + a: 1.0e60; + a: 0.00500e60; + a: 0.0e60; + a: 0.0000e60; + a: .0e60; + a: 0.e60; + a: 0e60; + a: 500600.001230045000e60; + a: 10; + a: 9700; + a: 10e100; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@supports (margin: 0.5px ".30px" 1e2px) { + a { + a: 0; + a: 1; + + a: 0.1; + a: 1.1; + + a: 0.1; + a: 1; + + a: 1e1; + a: 1e+1; + a: 1e-1; + a: 1e1; + a: 0.1e1; + a: 1.1e1; + a: 1.1e0010; + a: 0.1e+0010; + a: 0.1e-0010; + + a: 1E1; + a: 1E+1; + a: 1E-1; + a: 1E1; + a: 0.1E1; + a: 1.1E1; + a: 1.1E0010; + a: 0.1E+0010; + a: 0.1E-0010; + + a: 0.5e0; + a: 0.5e00; + a: 0.5e+0; + a: 0.5e+00; + a: 0.5e-0; + a: 0.5e-00; + + a: 1; + a: 1.005; + a: 1; + a: 1.5; + a: 1.5; + + a: 0.005; + a: 0; + a: 0; + + a: 500600.001230045; + a: 1.005e60; + a: 1e60; + a: 0.005e60; + a: 0e60; + a: 0e60; + a: 0e60; + a: 0e60; + a: 0e60; + a: 500600.001230045e60; + a: 10; + a: 9700; + a: 10e100; + } +} + +`; diff --git a/tests/css_numbers/jsfmt.spec.js b/tests/css_numbers/jsfmt.spec.js new file mode 100644 index 00000000..a7d303b6 --- /dev/null +++ b/tests/css_numbers/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "postcss" }); diff --git a/tests/css_numbers/numbers.css b/tests/css_numbers/numbers.css new file mode 100644 index 00000000..93e84972 --- /dev/null +++ b/tests/css_numbers/numbers.css @@ -0,0 +1,63 @@ +@supports (margin: .5px ".30px" 1E+2px) { + a { + a: 0; + a: 1; + + a: 0.1; + a: 1.1; + + a: .1; + a: 1.; + + a: 1e1; + a: 1e+1; + a: 1e-1; + a: 1.e1; + a: .1e1; + a: 1.1e1; + a: 1.1e0010; + a: .1e+0010; + a: .1e-0010; + + a: 1E1; + a: 1E+1; + a: 1E-1; + a: 1.E1; + a: .1E1; + a: 1.1E1; + a: 1.1E0010; + a: .1E+0010; + a: .1E-0010; + + a: 0.5e0; + a: 0.5e00; + a: 0.5e+0; + a: 0.5e+00; + a: 0.5e-0; + a: 0.5e-00; + + a: 1; + a: 1.00500; + a: 1.0; + a: 1.5; + a: 1.50; + + a: 0.00500; + a: 0.0; + a: 0.0000; + + a: 500600.001230045000; + a: 1.00500e60; + a: 1.0e60; + a: 0.00500e60; + a: 0.0e60; + a: 0.0000e60; + a: .0e60; + a: 0.e60; + a: 0e60; + a: 500600.001230045000e60; + a: 10; + a: 9700; + a: 10e100; + } +} diff --git a/tests/stylefmt/cssnext-example/__snapshots__/jsfmt.spec.js.snap b/tests/stylefmt/cssnext-example/__snapshots__/jsfmt.spec.js.snap index 0d2ff7b5..00277206 100644 --- a/tests/stylefmt/cssnext-example/__snapshots__/jsfmt.spec.js.snap +++ b/tests/stylefmt/cssnext-example/__snapshots__/jsfmt.spec.js.snap @@ -100,7 +100,7 @@ table { filter: blur(4px); } .sepia { - filter: sepia(.8); + filter: sepia(0.8); } `; diff --git a/tests/stylefmt/values/__snapshots__/jsfmt.spec.js.snap b/tests/stylefmt/values/__snapshots__/jsfmt.spec.js.snap index 33d3b435..7f804190 100644 --- a/tests/stylefmt/values/__snapshots__/jsfmt.spec.js.snap +++ b/tests/stylefmt/values/__snapshots__/jsfmt.spec.js.snap @@ -12,8 +12,8 @@ exports[`values.css 1`] = ` } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .class { - background: linear-gradient(to bottom right, white, hsla(0, 0%, 100%, .8)); - border: 1px solid rgba(0, 0, 0, .3); + background: linear-gradient(to bottom right, white, hsla(0, 0%, 100%, 0.8)); + border: 1px solid rgba(0, 0, 0, 0.3); font-family: Arial, sans-serif; color: rgba(0, 0, 0, 1); margin: 0 20px 0 -24px; From 302d1d89cb874729722333222e3522402d3fec9c Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Wed, 16 Aug 2017 08:55:48 +0200 Subject: [PATCH 2/2] Switch to postcss-values-parser with support for exponential notation --- package.json | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 38 +++++++++---------- yarn.lock | 6 +-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 3c8a63b5..e472c439 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "postcss-media-query-parser": "0.2.3", "postcss-scss": "1.0.0", "postcss-selector-parser": "2.2.3", - "postcss-values-parser": "git://github.com/shellscape/postcss-values-parser.git#5e351360479116f3fe309602cdd15b0a233bc29f", + "postcss-values-parser": "git://github.com/lydell/postcss-values-parser.git#af2c80b2bb558a6e7d61540d97f068f9fa162b38", "strip-bom": "3.0.0", "typescript": "2.5.0-dev.20170617", "typescript-eslint-parser": "git://github.com/eslint/typescript-eslint-parser.git#01c34f44e7bb3b8a1ec3d28433a6e0c9b2901d3c" diff --git a/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap b/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap index cea61fec..9274b90c 100644 --- a/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap @@ -77,31 +77,31 @@ exports[`numbers.css 1`] = ` a: 1; a: 1e1; - a: 1e+1; + a: 1e1; a: 1e-1; a: 1e1; a: 0.1e1; a: 1.1e1; - a: 1.1e0010; - a: 0.1e+0010; - a: 0.1e-0010; + a: 1.1e10; + a: 0.1e10; + a: 0.1e-10; - a: 1E1; - a: 1E+1; - a: 1E-1; - a: 1E1; - a: 0.1E1; - a: 1.1E1; - a: 1.1E0010; - a: 0.1E+0010; - a: 0.1E-0010; + a: 1e1; + a: 1e1; + a: 1e-1; + a: 1e1; + a: 0.1e1; + a: 1.1e1; + a: 1.1e10; + a: 0.1e10; + a: 0.1e-10; - a: 0.5e0; - a: 0.5e00; - a: 0.5e+0; - a: 0.5e+00; - a: 0.5e-0; - a: 0.5e-00; + a: 0.5; + a: 0.5; + a: 0.5; + a: 0.5; + a: 0.5; + a: 0.5; a: 1; a: 1.005; diff --git a/yarn.lock b/yarn.lock index ec7dad87..412b67e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2998,9 +2998,9 @@ postcss-selector-parser@2.2.3: indexes-of "^1.0.1" uniq "^1.0.1" -"postcss-values-parser@git://github.com/shellscape/postcss-values-parser.git#5e351360479116f3fe309602cdd15b0a233bc29f": - version "1.2.2" - resolved "git://github.com/shellscape/postcss-values-parser.git#5e351360479116f3fe309602cdd15b0a233bc29f" +"postcss-values-parser@git://github.com/lydell/postcss-values-parser.git#af2c80b2bb558a6e7d61540d97f068f9fa162b38": + version "1.3.0" + resolved "git://github.com/lydell/postcss-values-parser.git#af2c80b2bb558a6e7d61540d97f068f9fa162b38" dependencies: flatten "^1.0.2" indexes-of "^1.0.1"