diff --git a/src/printer.js b/src/printer.js index 0eca71ae..465b4729 100644 --- a/src/printer.js +++ b/src/printer.js @@ -729,7 +729,7 @@ function genericPrintNoParens(path, options, print) { case "NullLiteral": return "null"; // Babel 6 Literal split case "RegExpLiteral": - return n.extra.raw; + return printRegex(n); // Babel 6 Literal split case "NumericLiteral": return printNumber(n.extra.raw); @@ -739,6 +739,7 @@ function genericPrintNoParens(path, options, print) { case "StringLiteral": case "Literal": if (typeof n.value === "number") return printNumber(n.raw); + if (n.regex) return printRegex(n.regex); if (typeof n.value !== "string") return "" + n.value; return nodeStr(n, options); // Babel 6 @@ -1233,7 +1234,10 @@ function genericPrintNoParens(path, options, print) { case "ClassExpression": return concat(printClass(path, options, print)); case "TemplateElement": - return join(literalline, n.value.raw.split("\n")); + return join( + literalline, + n.value.raw.split("\n").map(line => normalizeEscapes(line)) + ); case "TemplateLiteral": var expressions = path.map(print, "expressions"); @@ -2524,7 +2528,37 @@ function makeString(rawContent, enclosingQuote) { return match; }); - return enclosingQuote + newContent + enclosingQuote; + return enclosingQuote + normalizeEscapes(newContent) + enclosingQuote; +} + +function printRegex(regexData) { + const pattern = regexData.pattern; + const flags = regexData.flags; + const skipES2015 = !flags.includes("u"); + + return "/" + normalizeEscapes(pattern, skipES2015) + "/" + flags; +} + +function normalizeEscapes(rawContent, skipES2015) { + // Matches \x escapes and \u escapes (including the ES2015 variant) and other + // escapes. + const regex = /(\\x[\da-f]{2}|\\u[\da-f]{4}|\\u\{[\da-f]+\})|(\\[\s\S])/gi; + + return rawContent.replace(regex, (match, escape, otherEscape) => { + // Return other escapes as-is. + if (otherEscape) { + return otherEscape; + } + + // Regexes without the /u flag do not support ES2015 unicode escapes, so + // return the match as-is. + if (skipES2015 && escape[2] === "{") { + return escape; + } + + // Print all \x and \u escapes lowercase. + return escape.toLowerCase(); + }); } function printNumber(rawNumber) { diff --git a/tests/literal/__snapshots__/jsfmt.spec.js.snap b/tests/literal/__snapshots__/jsfmt.spec.js.snap index 9e26c22a..458f8170 100644 --- a/tests/literal/__snapshots__/jsfmt.spec.js.snap +++ b/tests/literal/__snapshots__/jsfmt.spec.js.snap @@ -116,3 +116,48 @@ function test5(): string { 0.1e-10; " `; + +exports[`test regex.js 1`] = ` +"// Normalization of \\x and \\u escapes: + +// Basic case. +/a\\xAaAb\\uB1cDE/gim; + +// ES2015 unicode escapes. +/\\u{1Fa3}/u; +/\\u{00000000A0}/u; + +// Leaves what looks like a ES2015 unicode escape alone if not using the /u flag. +/\\u{1Fa3}/; + +// Leaves what looks like escapes but aren\'t alone. +/\\xA\\u00BG/; + +// Leaves other escapes alone. +/\\B\\S/; + +// Handles escaped backslashes. +/\\\\xAB\\\\\\xAB\\\\\\\\xAB\\B\\\\\\B\\uAbCd/; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Normalization of \\x and \\u escapes: + +// Basic case. +/a\\xaaAb\\ub1cdE/gim; + +// ES2015 unicode escapes. +/\\u{1fa3}/u; +/\\u{00000000a0}/u; + +// Leaves what looks like a ES2015 unicode escape alone if not using the /u flag. +/\\u{1Fa3}/; + +// Leaves what looks like escapes but aren\'t alone. +/\\xA\\u00BG/; + +// Leaves other escapes alone. +/\\B\\S/; + +// Handles escaped backslashes. +/\\\\xAB\\\\\\xab\\\\\\\\xAB\\B\\\\\\B\\uabcd/; +" +`; diff --git a/tests/literal/regex.js b/tests/literal/regex.js new file mode 100644 index 00000000..30a7b047 --- /dev/null +++ b/tests/literal/regex.js @@ -0,0 +1,20 @@ +// Normalization of \x and \u escapes: + +// Basic case. +/a\xAaAb\uB1cDE/gim; + +// ES2015 unicode escapes. +/\u{1Fa3}/u; +/\u{00000000A0}/u; + +// Leaves what looks like a ES2015 unicode escape alone if not using the /u flag. +/\u{1Fa3}/; + +// Leaves what looks like escapes but aren't alone. +/\xA\u00BG/; + +// Leaves other escapes alone. +/\B\S/; + +// Handles escaped backslashes. +/\\xAB\\\xAB\\\\xAB\B\\\B\uAbCd/; diff --git a/tests/strings/__snapshots__/jsfmt.spec.js.snap b/tests/strings/__snapshots__/jsfmt.spec.js.snap index b75363d4..7707b472 100644 --- a/tests/strings/__snapshots__/jsfmt.spec.js.snap +++ b/tests/strings/__snapshots__/jsfmt.spec.js.snap @@ -24,6 +24,29 @@ exports[`test strings.js 1`] = ` \'\\uD801\\uDC28\', ]; + +// Normalization of \\x and \\u escapes: + +// Basic case. +\"a\\xAaAb\\uB1cDE\"; +\`a\\xAaAb\\uB1cDE\`; + +// ES2015 unicode escapes. +\"\\u{1Fa3}\"; +\`\\u{1Fa3}\`; +\"\\u{00000000A0}\"; +\`\\u{00000000A0}\`; + +// Leaves other escapes alone. +\"\\B\\S\"; +\`\\B\\S\`; + +// Handles escaped backslashes. +\"\\\\xAB\\\\\\xAB\\\\\\\\xAB\\B\\\\\\B\\u1234\"; +\`\\\\xAB\\\\\\xAB\\\\\\\\xAB\\B\\\\\\B\\u1234\`; + +// Mix of everything. +\"\\xF0\"\`a\\uaBcD\${\'\\xFdE\'}\\\\\\u{00AbCdE}\`; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [ \"abc\", @@ -42,6 +65,17 @@ exports[`test strings.js 1`] = ` \"\\0\", \"🐶\", \'\\uD801\\uDC28\' -]; +]; // Normalization of \\x and \\u escapes: // Basic case. +\"a\\xaaAb\\ub1cdE\"; +\`a\\xaaAb\\ub1cdE\`; // ES2015 unicode escapes. +\"\\u{1fa3}\"; +\`\\u{1fa3}\`; +\"\\u{00000000a0}\"; +\`\\u{00000000a0}\`; // Leaves other escapes alone. +\"\\B\\S\"; +\`\\B\\S\`; // Handles escaped backslashes. +\"\\\\xAB\\\\\\xab\\\\\\\\xAB\\B\\\\\\B\\u1234\"; +\`\\\\xAB\\\\\\xab\\\\\\\\xAB\\B\\\\\\B\\u1234\`; // Mix of everything. +\"\\xf0\"\`a\\uabcd\${\"\\xfdE\"}\\\\\\u{00abcde}\`; " `; diff --git a/tests/strings/strings.js b/tests/strings/strings.js index d31dceb2..c2722e2b 100644 --- a/tests/strings/strings.js +++ b/tests/strings/strings.js @@ -23,3 +23,26 @@ '\uD801\uDC28', ]; + +// Normalization of \x and \u escapes: + +// Basic case. +"a\xAaAb\uB1cDE"; +`a\xAaAb\uB1cDE`; + +// ES2015 unicode escapes. +"\u{1Fa3}"; +`\u{1Fa3}`; +"\u{00000000A0}"; +`\u{00000000A0}`; + +// Leaves other escapes alone. +"\B\S"; +`\B\S`; + +// Handles escaped backslashes. +"\\xAB\\\xAB\\\\xAB\B\\\B\u1234"; +`\\xAB\\\xAB\\\\xAB\B\\\B\u1234`; + +// Mix of everything. +"\xF0"`a\uaBcD${'\xFdE'}\\\u{00AbCdE}`;