Implement transform for nullish-coalescing operator (#6483)

* Implement transform for nullish-coalescing operator

* Update example output

* Switch from BinaryExpression to LogicalExpression

* Address review comments

- Use generateUidIdentifierBasedOnNode
- Inline "??"
- Clone ref node
- Move "??" to LogicalExpression in babel-types

* Fix reference to @babel/helper-plugin-test-runner

* Fix reference to @babel/plugin-syntax-nullish-coalescing-operator

* Don't use parent scope

* Remove .vscode from .gitignore, change 'lib/index.js' to 'lib'

* Ensure `document.all ?? 0 === document.all`

* Fix note and copy to an inline comment
This commit is contained in:
Lucas Azzola 2017-10-18 18:10:05 +11:00 committed by Justin Ridgewell
parent 5c47929983
commit 99be60b53d
20 changed files with 219 additions and 1 deletions

View File

@ -0,0 +1,3 @@
src
test
*.log

View File

@ -0,0 +1,35 @@
# @babel/plugin-syntax-nullish-coalescing-operator
> Allow parsing of the nullish-coalescing operator.
## Installation
```sh
npm install --save-dev @babel/plugin-syntax-nullish-coalescing-operator
```
## Usage
### Via `.babelrc` (Recommended)
**.babelrc**
```json
{
"plugins": ["syntax-nullish-coalescing-operator"]
}
```
### Via CLI
```sh
babel --plugins syntax-nullish-coalescing-operator script.js
```
### Via Node API
```javascript
require("babel-core").transform("code", {
plugins: ["syntax-nullish-coalescing-operator"]
});
```

View File

@ -0,0 +1,13 @@
{
"name": "@babel/plugin-syntax-nullish-coalescing-operator",
"version": "7.0.0-beta.3",
"description": "Allow parsing of the nullish-coalescing operator",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-nullish-coalescing-operator",
"license": "MIT",
"main": "lib",
"keywords": [
"babel-plugin"
],
"dependencies": {},
"devDependencies": {}
}

View File

@ -0,0 +1,7 @@
export default function() {
return {
manipulateOptions(opts, parserOpts) {
parserOpts.plugins.push("nullishCoalescingOperator");
},
};
}

View File

@ -0,0 +1,3 @@
src
test
*.log

View File

@ -0,0 +1,54 @@
# @babel/plugin-transform-nullish-coalescing-operator
> Replace `??` with an inline helper.
## Example
**In**
```javascript
var foo = object.foo ?? "default";
```
**Out**
```javascript
var _object$foo;
var foo = (_object$foo = object.foo, _object$foo !== null && _object$foo !== void 0 ? _object$foo : "default");
```
> **NOTE:** We cannot use `!= null` here because `document.all == null` and
> `document.all` has been deemed not "nullish".
## Installation
```sh
npm install --save-dev @babel/plugin-transform-nullish-coalescing-operator
```
## Usage
### Via `.babelrc` (Recommended)
**.babelrc**
```json
{
"plugins": ["transform-nullish-coalescing-operator"]
}
```
### Via CLI
```sh
babel --plugins transform-nullish-coalescing-operator script.js
```
### Via Node API
```javascript
require("babel-core").transform("code", {
plugins: ["transform-nullish-coalescing-operator"]
});
```

View File

@ -0,0 +1,17 @@
{
"name": "@babel/plugin-transform-nullish-coalescing-operator",
"version": "7.0.0-beta.3",
"description": "Remove nullish coalescing operator",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-nullish-coalescing-opearator",
"license": "MIT",
"main": "lib",
"keywords": [
"babel-plugin"
],
"dependencies": {
"@babel/plugin-syntax-nullish-coalescing-operator": "7.0.0-beta.3"
},
"devDependencies": {
"@babel/helper-plugin-test-runner": "7.0.0-beta.3"
}
}

View File

@ -0,0 +1,40 @@
import syntaxNullishCoalescingOperator from "@babel/plugin-syntax-nullish-coalescing-operator";
export default function({ types: t }) {
return {
inherits: syntaxNullishCoalescingOperator,
visitor: {
LogicalExpression(path) {
const { node, scope } = path;
if (node.operator !== "??") {
return;
}
const ref = scope.generateUidIdentifierBasedOnNode(node.left);
scope.push({ id: ref });
path.replaceWith(
t.sequenceExpression([
t.assignmentExpression("=", ref, node.left),
t.conditionalExpression(
// We cannot use `!= null` here because `document.all == null`
// and `document.all` has been deemed not "nullish".
t.logicalExpression(
"&&",
t.binaryExpression("!==", t.clone(ref), t.nullLiteral()),
t.binaryExpression(
"!==",
t.clone(ref),
scope.buildUndefinedNode(),
),
),
t.clone(ref),
node.right,
),
]),
);
},
},
};
}

View File

@ -0,0 +1,19 @@
assert.equal(null ?? undefined, undefined);
assert.equal(undefined ?? null, null);
assert.equal(false ?? true, false);
assert.equal(0 ?? 1, 0);
assert.equal("" ?? "foo", "");
var obj = { exists: true };
assert.equal(obj.exists ?? false, true);
assert.equal(obj.doesNotExist ?? "foo", "foo");
var counter = 0;
function sideEffect() { return counter++; }
assert.equal(sideEffect() ?? -1, 0);
var counter2 = 0;
var obj2 = {
get foo() { return counter2++; }
};
assert.equal(obj2.foo ?? -1, 0);

View File

@ -0,0 +1,3 @@
{
"plugins": ["transform-nullish-coalescing-operator"]
}

View File

@ -0,0 +1 @@
function foo(foo, bar = foo ?? "bar") {}

View File

@ -0,0 +1,3 @@
function foo(foo, bar = (_foo = foo, _foo !== null && _foo !== void 0 ? _foo : "bar")) {
var _foo;
}

View File

@ -0,0 +1,3 @@
{
"plugins": ["transform-nullish-coalescing-operator"]
}

View File

@ -0,0 +1,3 @@
function foo(opts) {
var foo = opts.foo ?? "default";
}

View File

@ -0,0 +1,5 @@
function foo(opts) {
var _opts$foo;
var foo = (_opts$foo = opts.foo, _opts$foo !== null && _opts$foo !== void 0 ? _opts$foo : "default");
}

View File

@ -0,0 +1,3 @@
{
"plugins": ["transform-nullish-coalescing-operator"]
}

View File

@ -0,0 +1,3 @@
import runner from "@babel/helper-plugin-test-runner";
runner(__dirname);

View File

@ -12,6 +12,7 @@
"@babel/plugin-transform-export-default": "7.0.0-beta.3", "@babel/plugin-transform-export-default": "7.0.0-beta.3",
"@babel/plugin-transform-optional-chaining": "7.0.0-beta.3", "@babel/plugin-transform-optional-chaining": "7.0.0-beta.3",
"@babel/plugin-transform-pipeline-operator": "7.0.0-beta.3", "@babel/plugin-transform-pipeline-operator": "7.0.0-beta.3",
"@babel/plugin-transform-nullish-coalescing-operator": "7.0.0-beta.3",
"@babel/preset-stage-2": "7.0.0-beta.3" "@babel/preset-stage-2": "7.0.0-beta.3"
} }
} }

View File

@ -4,6 +4,7 @@ import transformDecorators from "@babel/plugin-transform-decorators";
import transformExportDefault from "@babel/plugin-transform-export-default"; import transformExportDefault from "@babel/plugin-transform-export-default";
import transformOptionalChaining from "@babel/plugin-transform-optional-chaining"; import transformOptionalChaining from "@babel/plugin-transform-optional-chaining";
import transformPipelineOperator from "@babel/plugin-transform-pipeline-operator"; import transformPipelineOperator from "@babel/plugin-transform-pipeline-operator";
import transformNullishCoalescingOperator from "@babel/plugin-transform-nullish-coalescing-operator";
export default function() { export default function() {
return { return {
@ -13,6 +14,7 @@ export default function() {
transformExportDefault, transformExportDefault,
transformOptionalChaining, transformOptionalChaining,
transformPipelineOperator, transformPipelineOperator,
transformNullishCoalescingOperator,
], ],
}; };
} }

View File

@ -9,7 +9,7 @@ export const COMMENT_KEYS = [
"innerComments", "innerComments",
]; ];
export const LOGICAL_OPERATORS = ["||", "&&"]; export const LOGICAL_OPERATORS = ["||", "&&", "??"];
export const UPDATE_OPERATORS = ["++", "--"]; export const UPDATE_OPERATORS = ["++", "--"];
export const BOOLEAN_NUMBER_BINARY_OPERATORS = [">", "<", ">=", "<="]; export const BOOLEAN_NUMBER_BINARY_OPERATORS = [">", "<", ">=", "<="];