Print \x and \u escapes in strings and regexes lowercase (#522)

* Print \x and \u escapes in strings and regexes lowercase

Theoretically, we would want to do this for escapes int identifiers as
well. However, neither flow nor babylon preserves escapes in
identifiers. For example, `\u0061.\u{0061}` cannot be distinguished from
`a.a`. Nobody uses such escapes in real code anyway. It could also be
considered a feature that such escapes are converted to real unicode
characters.

* Update snapshots

* Normalize escapes in template literals

* Update snapshots
master
Simon Lydell 2017-01-31 15:56:26 +01:00 committed by Christopher Chedeau
parent ea597182a9
commit 7148184d65
5 changed files with 160 additions and 4 deletions

View File

@ -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) {

View File

@ -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/;
"
`;

20
tests/literal/regex.js Normal file
View File

@ -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/;

View File

@ -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}\`;
"
`;

View File

@ -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}`;