Skip to content

Commit

Permalink
Transform ES2015 Unicode Escapes to ES5 (#11377)
Browse files Browse the repository at this point in the history
  • Loading branch information
jridgewell committed May 24, 2020
1 parent 66b86e0 commit 97f0b7c
Show file tree
Hide file tree
Showing 138 changed files with 468 additions and 2 deletions.
13 changes: 13 additions & 0 deletions packages/babel-compat-data/data/plugins.json
Expand Up @@ -98,6 +98,7 @@
"chrome": "62",
"opera": "49",
"edge": "79",
"firefox": "78",
"safari": "11.1",
"node": "8.10",
"ios": "11.3",
Expand All @@ -108,6 +109,7 @@
"chrome": "64",
"opera": "51",
"edge": "79",
"firefox": "78",
"safari": "11.1",
"node": "10",
"ios": "11.3",
Expand Down Expand Up @@ -279,6 +281,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
// 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}";

0 comments on commit 97f0b7c

Please sign in to comment.