Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transform ES2015 Unicode Escapes to ES5 #11377

Merged
merged 12 commits into from May 24, 2020
13 changes: 13 additions & 0 deletions packages/babel-compat-data/data/plugins.json
Expand Up @@ -87,6 +87,7 @@
"chrome": "62",
"opera": "49",
"edge": "79",
"firefox": "78",
"safari": "11.1",
"node": "8.10",
"ios": "11.3",
Expand All @@ -97,6 +98,7 @@
"chrome": "64",
"opera": "51",
"edge": "79",
"firefox": "78",
"safari": "11.1",
"node": "10",
"ios": "11.3",
Expand Down Expand Up @@ -268,6 +270,17 @@
"samsung": "5",
"electron": "0.37"
},
"transform-unicode-escapes": {
"chrome": "44",
"opera": "31",
"edge": "12",
"firefox": "53",
"safari": "9",
"node": "4",
"ios": "9",
"samsung": "4",
"electron": "0.30"
},
"transform-unicode-regex": {
"chrome": "50",
"opera": "37",
Expand Down
1 change: 1 addition & 0 deletions packages/babel-compat-data/scripts/data/plugin-features.js
Expand Up @@ -62,6 +62,7 @@ const es2015 = {
'RegExp "y" and "u" flags / "y" flag',
],
},
"transform-unicode-escapes": "Unicode code point escapes",
"transform-unicode-regex": {
features: [
'RegExp "y" and "u" flags / "u" flag, case folding',
Expand Down
@@ -1,7 +1,7 @@
#!/bin/bash
set -e

COMPAT_TABLE_COMMIT=9e07df3875d8416af85cf523716519e9dd1e5e44
COMPAT_TABLE_COMMIT=50e5424d113869b08911a5df956d0e931722e5b5
GIT_HEAD=build/compat-table/.git/HEAD

if [ -d "build/compat-table" ]; then
Expand Down
@@ -0,0 +1 @@
var foo = bar`\u0061\u{0061}\ud835\udc9c\u{1d49c}`;
@@ -0,0 +1,13 @@
function _templateObject() {
const data = _taggedTemplateLiteral(["aa\uD835\uDC9C\uD835\uDC9C"], ["\\u0061\\u{0061}\\ud835\\udc9c\\u{1d49c}"]);

_templateObject = function () {
return data;
};

return data;
}

function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }

var foo = bar(_templateObject());
3 changes: 3 additions & 0 deletions packages/babel-plugin-transform-unicode-escapes/.npmignore
@@ -0,0 +1,3 @@
src
test
*.log
19 changes: 19 additions & 0 deletions packages/babel-plugin-transform-unicode-escapes/README.md
@@ -0,0 +1,19 @@
# @babel/plugin-transform-unicode-escapes

> Compile ES2015 Unicode escapes to ES5

See our website [@babel/plugin-transform-unicode-escapes](https://babeljs.io/docs/en/next/babel-plugin-transform-unicode-escapes.html) for more information.

## Install

Using npm:

```sh
npm install --save-dev @babel/plugin-transform-unicode-escapes
```

or using yarn:

```sh
yarn add @babel/plugin-transform-unicode-escapes --dev
```
24 changes: 24 additions & 0 deletions packages/babel-plugin-transform-unicode-escapes/package.json
@@ -0,0 +1,24 @@
{
"name": "@babel/plugin-transform-unicode-escapes",
"version": "7.8.3",
"description": "Compile ES2015 Unicode escapes to ES5",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-unicode-escapes",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "lib/index.js",
"keywords": [
"babel-plugin"
],
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.3"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/helper-plugin-test-runner": "^7.8.3"
}
}
107 changes: 107 additions & 0 deletions packages/babel-plugin-transform-unicode-escapes/src/index.js
@@ -0,0 +1,107 @@
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";

export default declare(api => {
api.assertVersion(7);

const surrogate = /[\ud800-\udfff]/g;
const unicodeEscape = /(\\+)u\{([0-9a-fA-F]+)\}/g;

function escape(code) {
let str = code.toString(16);
// Sigh, node 6 doesn't have padStart
jridgewell marked this conversation as resolved.
Show resolved Hide resolved
// TODO: Remove in Babel 8, when we drop node 6.
while (str.length < 4) str = "0" + str;
return "\\u" + str;
}

function replacer(match, backslashes, code) {
if (backslashes.length % 2 === 0) {
return match;
}

const char = String.fromCodePoint(parseInt(code, 16));
const escaped = backslashes.slice(0, -1) + escape(char.charCodeAt(0));

return char.length === 1 ? escaped : escaped + escape(char.charCodeAt(1));
}

function replaceUnicodeEscapes(str) {
return str.replace(unicodeEscape, replacer);
}

function getUnicodeEscape(str) {
let match;
while ((match = unicodeEscape.exec(str))) {
if (match[1].length % 2 === 0) continue;
unicodeEscape.lastIndex = 0;
return match[0];
}
return null;
}

return {
name: "transform-unicode-escapes",
visitor: {
Identifier(path) {
const { node, key } = path;
const { name } = node;
const replaced = name.replace(surrogate, c => {
return `_u${c.charCodeAt(0).toString(16)}`;
});
if (name === replaced) return;

const str = t.inherits(t.stringLiteral(name), node);

if (key === "key") {
path.replaceWith(str);
return;
}

const { parentPath, scope } = path;
if (
parentPath.isMemberExpression({ property: node }) ||
parentPath.isOptionalMemberExpression({ property: node })
) {
parentPath.node.computed = true;
path.replaceWith(str);
return;
}

const binding = scope.getBinding(name);
if (binding) {
scope.rename(name, scope.generateUid(replaced));
return;
}

throw path.buildCodeFrameError(
`Can't reference '${name}' as a bare identifier`,
);
},

"StringLiteral|DirectiveLiteral"(path) {
const { node } = path;
const { extra } = node;

if (extra?.raw) extra.raw = replaceUnicodeEscapes(extra.raw);
},

TemplateElement(path) {
const { node, parentPath } = path;
const { value } = node;

const firstEscape = getUnicodeEscape(value.raw);
if (!firstEscape) return;

const grandParent = parentPath.parentPath;
if (grandParent.isTaggedTemplateExpression()) {
throw path.buildCodeFrameError(
`Can't replace Unicode escape '${firstEscape}' inside tagged template literals. You can enable '@babel/plugin-transform-template-literals' to compile them to classic strings.`,
);
}

value.raw = replaceUnicodeEscapes(value.raw);
},
},
};
});
@@ -0,0 +1 @@
"𝒜\ud835\udc9c\u{1d49c}"
@@ -0,0 +1 @@
"𝒜\ud835\udc9c\ud835\udc9c";
@@ -0,0 +1 @@
\u{1d49c};
@@ -0,0 +1,3 @@
{
"throws": "Can't reference '𝒜' as a bare identifier"
}
@@ -0,0 +1,2 @@
var \u{1d49c} = 1;
\u{1d49c};
@@ -0,0 +1,2 @@
var _ud835_udc9c = 1;
_ud835_udc9c;
@@ -0,0 +1 @@
var o = class { get \u{1d49c}() {} };
@@ -0,0 +1,4 @@
var o = class {
get "\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
var o = class { \u{1d49c} = 1 };
@@ -0,0 +1,3 @@
{
"plugins": ["transform-unicode-escapes", "syntax-class-properties"]
}
@@ -0,0 +1,3 @@
var o = class {
"\uD835\uDC9C" = 1;
};
@@ -0,0 +1 @@
var o = class { \u{1d49c}() {} };
@@ -0,0 +1,4 @@
var o = class {
"\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
obj.\u{1d49c};
@@ -0,0 +1 @@
obj["\uD835\uDC9C"];
@@ -0,0 +1 @@
var o = { get \u{1d49c}() {} };
@@ -0,0 +1,4 @@
var o = {
get "\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
var o = { \u{1d49c}() {} };
@@ -0,0 +1,4 @@
var o = {
"\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
var o = { \u{1d49c}: 1 };
@@ -0,0 +1,3 @@
var o = {
"\uD835\uDC9C": 1
};
@@ -0,0 +1 @@
var o = { \u{1d49c} };
@@ -0,0 +1,3 @@
{
"throws": "Can't reference '𝒜' as a bare identifier"
}
@@ -0,0 +1,2 @@
var \u{1d49c} = 1;
var o = { \u{1d49c} };
@@ -0,0 +1,4 @@
var _ud835_udc9c = 1;
var o = {
"\uD835\uDC9C": _ud835_udc9c
};
@@ -0,0 +1 @@
obj?.\u{1d49c};
@@ -0,0 +1 @@
obj?.["\uD835\uDC9C"];
@@ -0,0 +1,3 @@
{
"plugins": ["transform-unicode-escapes"]
}
@@ -0,0 +1 @@
𝒜;
@@ -0,0 +1,3 @@
{
"throws": "Can't reference '𝒜' as a bare identifier"
}
@@ -0,0 +1,2 @@
var 𝒜 = 1;
𝒜;
@@ -0,0 +1,2 @@
var _ud835_udc9c = 1;
_ud835_udc9c;
@@ -0,0 +1 @@
var o = class { get 𝒜 () {} };
@@ -0,0 +1,4 @@
var o = class {
get "\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
var o = class { 𝒜 = 1 };
@@ -0,0 +1,3 @@
{
"plugins": ["transform-unicode-escapes", "syntax-class-properties"]
}
@@ -0,0 +1,3 @@
var o = class {
"\uD835\uDC9C" = 1;
};
@@ -0,0 +1 @@
var o = class { 𝒜 () {} };
@@ -0,0 +1,4 @@
var o = class {
"\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
obj.𝒜 ;
@@ -0,0 +1 @@
obj["\uD835\uDC9C"];
@@ -0,0 +1 @@
var o = { get 𝒜 () {} };
@@ -0,0 +1,4 @@
var o = {
get "\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
var o = { 𝒜 () {} };
@@ -0,0 +1,4 @@
var o = {
"\uD835\uDC9C"() {}

};
@@ -0,0 +1 @@
var o = { 𝒜 : 1 };
@@ -0,0 +1,3 @@
var o = {
"\uD835\uDC9C": 1
};
@@ -0,0 +1 @@
var o = { 𝒜 };
@@ -0,0 +1,3 @@
{
"throws": "Can't reference '𝒜' as a bare identifier"
}
@@ -0,0 +1,2 @@
var 𝒜 = 1;
var o = { 𝒜 };
@@ -0,0 +1,4 @@
var _ud835_udc9c = 1;
var o = {
"\uD835\uDC9C": _ud835_udc9c
};
@@ -0,0 +1 @@
obj?.𝒜 ;
@@ -0,0 +1 @@
obj?.["\uD835\uDC9C"];
@@ -0,0 +1,7 @@
0;
"𝒜\ud835\udc9c\u{1d49c}";
"\u{1d49c}";
"\\u{1d49c}";
"\\\u{1d49c}";
"\\\\u{1d49c}";