From 00f58b9bfa0abc66cbc4d875ca65c715401935d6 Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Mon, 24 Apr 2017 21:15:15 +0200 Subject: [PATCH 01/33] feat: optional chaing --- .../.npmignore | 3 + .../README.md | 1 + .../package.json | 20 ++++++ .../src/index.js | 64 +++++++++++++++++++ .../fixtures/general/member-access/actual.js | 1 + .../general/member-access/expected.js | 1 + .../general/nested-member-access/actual.js | 1 + .../general/nested-member-access/expected.js | 1 + .../test/fixtures/general/options.json | 3 + .../test/index.js | 3 + 10 files changed, 98 insertions(+) create mode 100644 packages/babel-plugin-transform-optional-chaining/.npmignore create mode 100644 packages/babel-plugin-transform-optional-chaining/README.md create mode 100644 packages/babel-plugin-transform-optional-chaining/package.json create mode 100644 packages/babel-plugin-transform-optional-chaining/src/index.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/options.json create mode 100644 packages/babel-plugin-transform-optional-chaining/test/index.js diff --git a/packages/babel-plugin-transform-optional-chaining/.npmignore b/packages/babel-plugin-transform-optional-chaining/.npmignore new file mode 100644 index 0000000000..f980694583 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-plugin-transform-optional-chaining/README.md b/packages/babel-plugin-transform-optional-chaining/README.md new file mode 100644 index 0000000000..41fdea4a70 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/README.md @@ -0,0 +1 @@ +# babel-plugin-transform-optional-chaining diff --git a/packages/babel-plugin-transform-optional-chaining/package.json b/packages/babel-plugin-transform-optional-chaining/package.json new file mode 100644 index 0000000000..1f6fc11699 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/package.json @@ -0,0 +1,20 @@ +{ + "name": "babel-plugin-transform-optional-chaining", + "version": "7.0.0-alpha.7", + "description": "", + "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-optional-chaining", + "license": "MIT", + "main": "lib/index.js", + "dependencies": { + "babel-traverse": "7.0.0-alpha.7", + "babel-types": "7.0.0-alpha.7", + "babel-template": "7.0.0-alpha.7", + "lodash": "^4.2.0" + }, + "keywords": [ + "babel-plugin" + ], + "devDependencies": { + "babel-helper-plugin-test-runner": "7.0.0-alpha.7" + } +} diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js new file mode 100644 index 0000000000..6e29dde42a --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -0,0 +1,64 @@ +import createTemplate from "babel-template"; +import traverse from "babel-traverse"; + +const nullOrUndefinedCheck = createTemplate(` + typeof CHECK !== "undefined" && CHECK !== null + ? NEXT + : null +`); + +function isOptional(path) { + return path.node.optional === true; +} + +const nullOrUndefinedCheckVisitor = { + noScope: true, + + Identifier(path, replacements) { + if (path.node.name in replacements) { + path.replaceInline(replacements[path.node.name]); + } + }, +}; + +function createCheck(node, object) { + + const template = nullOrUndefinedCheck(); + + traverse(template, nullOrUndefinedCheckVisitor, null, { + CHECK: object, + NEXT: node, + }); + + return template; +} + +export default function ({ types: t }) { + + return { + visitor: { + MemberExpression(path) { + + if (isOptional(path)) { + let { object } = path.node; + + while ( + t.isMemberExpression(object) + && isOptional({ node: object }) + ) { + object = createCheck( + object, + object.object + ); + } + + path.replaceWith( + createCheck(path.node, object) + ); + + path.stop(); + } + }, + }, + }; +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js new file mode 100644 index 0000000000..6ed5c83b93 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js @@ -0,0 +1 @@ +foo?.bar diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js new file mode 100644 index 0000000000..473cdedb0f --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -0,0 +1 @@ +typeof foo !== "undefined" && foo !== null ? foo.bar : null; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js new file mode 100644 index 0000000000..fc0ce9aea6 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js @@ -0,0 +1 @@ +foo?.bar?.vroum diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js new file mode 100644 index 0000000000..3c37951fcf --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js @@ -0,0 +1 @@ +typeof (typeof foo !== "undefined" && foo !== null ? foo.bar : null) !== "undefined" && (typeof foo !== "undefined" && foo !== null ? foo.bar : null) !== null ? foo.bar.vroum : null; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/options.json b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/options.json new file mode 100644 index 0000000000..846cf98412 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-optional-chaining"] +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/index.js b/packages/babel-plugin-transform-optional-chaining/test/index.js new file mode 100644 index 0000000000..09cfbc31f5 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/index.js @@ -0,0 +1,3 @@ +import runner from "babel-helper-plugin-test-runner"; + +runner(__dirname); From 72259ca5d3208aa4df9282f50b1c4a9027494854 Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Tue, 25 Apr 2017 16:04:53 +0200 Subject: [PATCH 02/33] feat: cleanup --- .../src/index.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 6e29dde42a..5493230d88 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -7,8 +7,8 @@ const nullOrUndefinedCheck = createTemplate(` : null `); -function isOptional(path) { - return path.node.optional === true; +function isNodeOptional(node) { + return node.optional === true; } const nullOrUndefinedCheckVisitor = { @@ -39,18 +39,16 @@ export default function ({ types: t }) { visitor: { MemberExpression(path) { - if (isOptional(path)) { + if (isNodeOptional(path.node)) { let { object } = path.node; - while ( - t.isMemberExpression(object) - && isOptional({ node: object }) - ) { + do { object = createCheck( object, object.object ); - } + + } while (t.isMemberExpression(object) && isNodeOptional(object)); path.replaceWith( createCheck(path.node, object) From 64ff5a080dd577a773e37c17bf348fe1157b55fe Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Sat, 29 Apr 2017 18:35:15 +0200 Subject: [PATCH 03/33] refactor: improved transformation logic --- .../src/index.js | 89 ++++++++----------- .../general/member-access/expected.js | 4 +- .../general/nested-member-access/actual.js | 2 +- .../general/nested-member-access/expected.js | 4 +- 4 files changed, 43 insertions(+), 56 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 5493230d88..aca9d8e6c3 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,61 +1,44 @@ -import createTemplate from "babel-template"; -import traverse from "babel-traverse"; - -const nullOrUndefinedCheck = createTemplate(` - typeof CHECK !== "undefined" && CHECK !== null - ? NEXT - : null -`); - -function isNodeOptional(node) { - return node.optional === true; -} - -const nullOrUndefinedCheckVisitor = { - noScope: true, - - Identifier(path, replacements) { - if (path.node.name in replacements) { - path.replaceInline(replacements[path.node.name]); - } - }, -}; - -function createCheck(node, object) { - - const template = nullOrUndefinedCheck(); - - traverse(template, nullOrUndefinedCheckVisitor, null, { - CHECK: object, - NEXT: node, - }); - - return template; -} - export default function ({ types: t }) { + function createCondition(ref, access, nextProperty) { + + return t.conditionalExpression( + createCheckAgainstNull( + t.AssignmentExpression("=", ref, access) + ), + + t.memberExpression(ref, nextProperty), + t.NullLiteral() + ); + } + + function createCheckAgainstNull(left) { + return t.BinaryExpression("!=", left, t.NullLiteral()); + } + + function isNodeOptional(node) { + return node.optional === true; + } + return { visitor: { - MemberExpression(path) { - - if (isNodeOptional(path.node)) { - let { object } = path.node; - - do { - object = createCheck( - object, - object.object - ); - - } while (t.isMemberExpression(object) && isNodeOptional(object)); - - path.replaceWith( - createCheck(path.node, object) - ); - - path.stop(); + MemberExpression(path, state) { + if (!isNodeOptional(path.node)) { + return; } + + const { object, property } = path.node; + + if (!state.optionalTemp) { + const id = path.scope.generateUidIdentifier(); + + state.optionalTemp = id; + path.scope.push({ id }); + } + + path.replaceWith( + createCondition(state.optionalTemp, object, property) + ); }, }, }; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index 473cdedb0f..beb5eddc75 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1 +1,3 @@ -typeof foo !== "undefined" && foo !== null ? foo.bar : null; \ No newline at end of file +var _temp; + +(_temp = foo) != null ? _temp.bar : null; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js index fc0ce9aea6..f9e4957390 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js @@ -1 +1 @@ -foo?.bar?.vroum +a?.b.c?.d.e diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js index 3c37951fcf..59d23923f8 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js @@ -1 +1,3 @@ -typeof (typeof foo !== "undefined" && foo !== null ? foo.bar : null) !== "undefined" && (typeof foo !== "undefined" && foo !== null ? foo.bar : null) !== null ? foo.bar.vroum : null; \ No newline at end of file +var _temp; + +((_temp = ((_temp = a) != null ? _temp.b : null).c) != null ? _temp.d : null).e; From ed15443dba6e3f79ec8ce7db2b32391b4eba8c8b Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Sat, 29 Apr 2017 18:53:19 +0200 Subject: [PATCH 04/33] fix: use undefined instead of null --- .../src/index.js | 13 +++++++++---- .../test/fixtures/general/member-access/expected.js | 2 +- .../general/nested-member-access/expected.js | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index aca9d8e6c3..204ade516e 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,6 +1,6 @@ export default function ({ types: t }) { - function createCondition(ref, access, nextProperty) { + function createCondition(ref, access, nextProperty, bailout) { return t.conditionalExpression( createCheckAgainstNull( @@ -8,7 +8,7 @@ export default function ({ types: t }) { ), t.memberExpression(ref, nextProperty), - t.NullLiteral() + bailout, ); } @@ -36,9 +36,14 @@ export default function ({ types: t }) { path.scope.push({ id }); } - path.replaceWith( - createCondition(state.optionalTemp, object, property) + const remplacement = createCondition( + state.optionalTemp, + object, + property, + t.identifier("undefined") ); + + path.replaceWith(remplacement); }, }, }; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index beb5eddc75..c8e1bdce5e 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1,3 +1,3 @@ var _temp; -(_temp = foo) != null ? _temp.bar : null; +(_temp = foo) != null ? _temp.bar : undefined; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js index 59d23923f8..3e328660a1 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js @@ -1,3 +1,3 @@ var _temp; -((_temp = ((_temp = a) != null ? _temp.b : null).c) != null ? _temp.d : null).e; +((_temp = ((_temp = a) != null ? _temp.b : undefined).c) != null ? _temp.d : undefined).e; \ No newline at end of file From 6bd3bf4d2f11a22a430906efc6b57063e9713f55 Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Sat, 29 Apr 2017 20:15:11 +0200 Subject: [PATCH 05/33] feat: optional chaining with function call --- .../src/index.js | 26 ++++++++++++++----- .../fixtures/general/function-call/actual.js | 1 + .../general/function-call/expected.js | 3 +++ 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 204ade516e..73058c520d 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -22,6 +22,7 @@ export default function ({ types: t }) { return { visitor: { + MemberExpression(path, state) { if (!isNodeOptional(path.node)) { return; @@ -36,12 +37,25 @@ export default function ({ types: t }) { path.scope.push({ id }); } - const remplacement = createCondition( - state.optionalTemp, - object, - property, - t.identifier("undefined") - ); + let remplacement; + + if (t.isCallExpression(path.parent)) { + + remplacement = createCondition( + state.optionalTemp, + object, + property, + t.callExpression(t.identifier("Function"), []) + ); + } else { + + remplacement = createCondition( + state.optionalTemp, + object, + property, + t.identifier("undefined") + ); + } path.replaceWith(remplacement); }, diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js new file mode 100644 index 0000000000..62587bbd25 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js @@ -0,0 +1 @@ +foo?.bar() diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js new file mode 100644 index 0000000000..b20ea71dac --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -0,0 +1,3 @@ +var _temp; + +((_temp = foo) != null ? _temp.bar : Function())(); \ No newline at end of file From cc6959e1fa23d27d3df06f1476556f83f47a32ca Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Thu, 4 May 2017 18:44:03 +0200 Subject: [PATCH 06/33] feat: WIP assignements --- .../src/index.js | 26 +++++++++++++------ .../fixtures/general/assignement/actual.js | 1 + 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 73058c520d..b856e0d7a7 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -37,27 +37,37 @@ export default function ({ types: t }) { path.scope.push({ id }); } - let remplacement; + if (t.isAssignmentExpression(path.parent)) { - if (t.isCallExpression(path.parent)) { + const remplacement = createCondition( + t.identifier("temp_here_please"), + object, + property, + t.identifier("undefined"), + ); - remplacement = createCondition( + path.parentPath.replaceWith(remplacement); + } else if (t.isCallExpression(path.parent)) { + + const remplacement = createCondition( state.optionalTemp, object, property, - t.callExpression(t.identifier("Function"), []) + t.callExpression(t.identifier("Function"), []), ); + + path.replaceWith(remplacement); } else { - remplacement = createCondition( + const remplacement = createCondition( state.optionalTemp, object, property, - t.identifier("undefined") + t.identifier("undefined"), ); - } - path.replaceWith(remplacement); + path.replaceWith(remplacement); + } }, }, }; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js new file mode 100644 index 0000000000..bf7a81758e --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js @@ -0,0 +1 @@ +a?.b = 42 From 3faca62a77fcff9e5e487ab7e843fee9f7621959 Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Sun, 7 May 2017 11:21:27 +0200 Subject: [PATCH 07/33] wip --- .../src/index.js | 108 ++++++++++++++++-- .../fixtures/general/assignement/actual.js | 2 + .../fixtures/general/assignement/expected.js | 9 ++ .../test/fixtures/general/delete/actual.js | 3 + .../test/fixtures/general/delete/expected.js | 9 ++ .../fixtures/general/member-access/actual.js | 2 + .../general/member-access/expected.js | 4 +- .../general/nested-member-access/actual.js | 1 - .../general/nested-member-access/expected.js | 3 - 9 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js delete mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js delete mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index b856e0d7a7..c2ddf4965b 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,4 +1,15 @@ export default function ({ types: t }) { + const nilIdentifier = t.identifier("undefined"); + + function setOptionalTransformed(node) { + t.assertMemberExpression(node); // Dev + node._optionalTransformed = true; + } + + function isOptionalTransformed(node) { + t.assertMemberExpression(node); // Dev + return node._optionalTransformed === true; + } function createCondition(ref, access, nextProperty, bailout) { @@ -23,8 +34,87 @@ export default function ({ types: t }) { return { visitor: { + AssignmentExpression(path, state) { + const { left } = path.node; + + if (!isNodeOptional(left) || isOptionalTransformed(left)) { + return; + } + + if (!state.optionalTemp) { + const id = path.scope.generateUidIdentifier(); + + state.optionalTemp = id; + path.scope.push({ id }); + } + + const { object, property } = left; + + const isChainNil = t.BinaryExpression( + "!=", + createCondition( + state.optionalTemp, + object, + property, + nilIdentifier, + ), + nilIdentifier, + ); + + // FIXME(sven): if will be a ConditionalExpression for childs, only top level will be ifStatement + const remplacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); + + setOptionalTransformed(left); + + path.traverse({ + MemberExpression({ node }) { + setOptionalTransformed(node); + }, + }); + + path.parentPath.replaceWith(remplacement); + }, + + UnaryExpression(path, state) { + const { operator, argument } = path.node; + + if (operator !== "delete") { + return; + } + + if (!isNodeOptional(argument) || isOptionalTransformed(argument)) { + return; + } + + if (!state.optionalTemp) { + const id = path.scope.generateUidIdentifier(); + + state.optionalTemp = id; + path.scope.push({ id }); + } + + const { object, property } = argument; + + const isChainNil = t.BinaryExpression( + "!=", + createCondition( + state.optionalTemp, + object, + property, + nilIdentifier, + ), + nilIdentifier, + ); + + const remplacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); + + setOptionalTransformed(argument); + + path.parentPath.replaceWith(remplacement); + }, + MemberExpression(path, state) { - if (!isNodeOptional(path.node)) { + if (!isNodeOptional(path.node) || isOptionalTransformed(path.node)) { return; } @@ -38,15 +128,9 @@ export default function ({ types: t }) { } if (t.isAssignmentExpression(path.parent)) { - - const remplacement = createCondition( - t.identifier("temp_here_please"), - object, - property, - t.identifier("undefined"), - ); - - path.parentPath.replaceWith(remplacement); + return; + } else if (t.isUnaryExpression(path.parent)) { + return; } else if (t.isCallExpression(path.parent)) { const remplacement = createCondition( @@ -56,6 +140,7 @@ export default function ({ types: t }) { t.callExpression(t.identifier("Function"), []), ); + setOptionalTransformed(path.node); path.replaceWith(remplacement); } else { @@ -63,9 +148,10 @@ export default function ({ types: t }) { state.optionalTemp, object, property, - t.identifier("undefined"), + nilIdentifier, ); + setOptionalTransformed(path.node); path.replaceWith(remplacement); } }, diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js index bf7a81758e..b9405035c9 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js @@ -1 +1,3 @@ a?.b = 42 + +a?.b?.c?.d = 42 diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js new file mode 100644 index 0000000000..a4c87fdf0c --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js @@ -0,0 +1,9 @@ +var _temp; + +if (((_temp = a) != null ? _temp.b : undefined) != undefined) { + a.b = 42; +} + +if (((_temp = a.b.c) != null ? _temp.d : undefined) != undefined) { + a.b.c.d = 42; +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/actual.js new file mode 100644 index 0000000000..e75af35f7f --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/actual.js @@ -0,0 +1,3 @@ +delete a?.b + +delete a?.b?.c?.d diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js new file mode 100644 index 0000000000..ec0bfb91c3 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js @@ -0,0 +1,9 @@ +var _temp; + +if (((_temp = a) != null ? _temp.b : undefined) != undefined) { + delete a.b; +} + +if (((_temp = ((_temp = a) != null ? _temp.b : undefined).c) != null ? _temp.d : undefined) != undefined) { + delete ((_temp = a.b) != null ? _temp.c : undefined).d; +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js index 6ed5c83b93..83447e76d5 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js @@ -1 +1,3 @@ foo?.bar + +a?.b.c?.d.e diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index c8e1bdce5e..013c8d6f86 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1,3 +1,5 @@ var _temp; -(_temp = foo) != null ? _temp.bar : undefined; \ No newline at end of file +(_temp = foo) != null ? _temp.bar : undefined; + +((_temp = ((_temp = a) != null ? _temp.b : undefined).c) != null ? _temp.d : undefined).e; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js deleted file mode 100644 index f9e4957390..0000000000 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/actual.js +++ /dev/null @@ -1 +0,0 @@ -a?.b.c?.d.e diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js deleted file mode 100644 index 3e328660a1..0000000000 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/nested-member-access/expected.js +++ /dev/null @@ -1,3 +0,0 @@ -var _temp; - -((_temp = ((_temp = a) != null ? _temp.b : undefined).c) != null ? _temp.d : undefined).e; \ No newline at end of file From a9d8040c0feef22d915b487b33ead6ae787ca662 Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Mon, 29 May 2017 18:57:57 +0200 Subject: [PATCH 08/33] refactor: change undefined to void 0 --- .../babel-plugin-transform-optional-chaining/src/index.js | 2 +- .../test/fixtures/general/assignement/expected.js | 6 +++--- .../test/fixtures/general/delete/expected.js | 8 ++++---- .../test/fixtures/general/function-call/expected.js | 2 +- .../test/fixtures/general/member-access/expected.js | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index c2ddf4965b..75fff8aabc 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,5 +1,5 @@ export default function ({ types: t }) { - const nilIdentifier = t.identifier("undefined"); + const nilIdentifier = t.unaryExpression("void", t.numericLiteral(0)); function setOptionalTransformed(node) { t.assertMemberExpression(node); // Dev diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js index a4c87fdf0c..28f9f5b1e2 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js @@ -1,9 +1,9 @@ var _temp; -if (((_temp = a) != null ? _temp.b : undefined) != undefined) { +if (((_temp = a) != null ? _temp.b : void 0) != void 0) { a.b = 42; } -if (((_temp = a.b.c) != null ? _temp.d : undefined) != undefined) { +if (((_temp = a.b.c) != null ? _temp.d : void 0) != void 0) { a.b.c.d = 42; -} \ No newline at end of file +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js index ec0bfb91c3..a2fd158a7c 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js @@ -1,9 +1,9 @@ var _temp; -if (((_temp = a) != null ? _temp.b : undefined) != undefined) { +if (((_temp = a) != null ? _temp.b : void 0) != void 0) { delete a.b; } -if (((_temp = ((_temp = a) != null ? _temp.b : undefined).c) != null ? _temp.d : undefined) != undefined) { - delete ((_temp = a.b) != null ? _temp.c : undefined).d; -} \ No newline at end of file +if (((_temp = ((_temp = a) != null ? _temp.b : void 0).c) != null ? _temp.d : void 0) != void 0) { + delete ((_temp = a.b) != null ? _temp.c : void 0).d; +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js index b20ea71dac..141f7dd896 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -1,3 +1,3 @@ var _temp; -((_temp = foo) != null ? _temp.bar : Function())(); \ No newline at end of file +((_temp = foo) != null ? _temp.bar : Function())(); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index 013c8d6f86..fe9a91bd04 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1,5 +1,5 @@ var _temp; -(_temp = foo) != null ? _temp.bar : undefined; +(_temp = foo) != null ? _temp.bar : void 0; -((_temp = ((_temp = a) != null ? _temp.b : undefined).c) != null ? _temp.d : undefined).e; \ No newline at end of file +((_temp = ((_temp = a) != null ? _temp.b : void 0).c) != null ? _temp.d : void 0).e; From 98487b5a15c222dd7e7fd0bc4732bfcaaeac9670 Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Mon, 29 May 2017 18:58:27 +0200 Subject: [PATCH 09/33] fix: typo --- .../src/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 75fff8aabc..77a6a20ff2 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -62,7 +62,7 @@ export default function ({ types: t }) { ); // FIXME(sven): if will be a ConditionalExpression for childs, only top level will be ifStatement - const remplacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); + const replacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); setOptionalTransformed(left); @@ -72,7 +72,7 @@ export default function ({ types: t }) { }, }); - path.parentPath.replaceWith(remplacement); + path.parentPath.replaceWith(replacement); }, UnaryExpression(path, state) { @@ -106,11 +106,11 @@ export default function ({ types: t }) { nilIdentifier, ); - const remplacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); + const replacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); setOptionalTransformed(argument); - path.parentPath.replaceWith(remplacement); + path.parentPath.replaceWith(replacement); }, MemberExpression(path, state) { @@ -133,7 +133,7 @@ export default function ({ types: t }) { return; } else if (t.isCallExpression(path.parent)) { - const remplacement = createCondition( + const replacement = createCondition( state.optionalTemp, object, property, @@ -141,10 +141,10 @@ export default function ({ types: t }) { ); setOptionalTransformed(path.node); - path.replaceWith(remplacement); + path.replaceWith(replacement); } else { - const remplacement = createCondition( + const replacement = createCondition( state.optionalTemp, object, property, @@ -152,7 +152,7 @@ export default function ({ types: t }) { ); setOptionalTransformed(path.node); - path.replaceWith(remplacement); + path.replaceWith(replacement); } }, }, From 750b03a22fad2f5efd0473a5235b3c0e1be31015 Mon Sep 17 00:00:00 2001 From: Sven SAULEAU Date: Mon, 29 May 2017 22:40:01 +0200 Subject: [PATCH 10/33] refactor: use WeakSet to keep track of transformed nodes --- .../babel-plugin-transform-optional-chaining/src/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 77a6a20ff2..584294ac54 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,14 +1,15 @@ export default function ({ types: t }) { + const optionalNodesTransformed = new WeakSet(); const nilIdentifier = t.unaryExpression("void", t.numericLiteral(0)); function setOptionalTransformed(node) { t.assertMemberExpression(node); // Dev - node._optionalTransformed = true; + optionalNodesTransformed.add(node); } function isOptionalTransformed(node) { t.assertMemberExpression(node); // Dev - return node._optionalTransformed === true; + return optionalNodesTransformed.has(node); } function createCondition(ref, access, nextProperty, bailout) { From 3fae1214608ea49c3b41c269c733af72d9cd6af0 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Fri, 2 Jun 2017 03:51:04 -0400 Subject: [PATCH 11/33] Implement Null Propagation Operator --- .../src/index.js | 252 +++++++----------- .../fixtures/general/assignement/expected.js | 10 +- .../test/fixtures/general/delete/expected.js | 10 +- .../fixtures/general/function-call/actual.js | 6 + .../general/function-call/expected.js | 10 +- .../general/member-access/expected.js | 6 +- .../test/fixtures/general/new/actual.js | 8 + .../test/fixtures/general/new/expected.js | 10 + 8 files changed, 139 insertions(+), 173 deletions(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 584294ac54..287f14963d 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,161 +1,105 @@ export default function ({ types: t }) { - const optionalNodesTransformed = new WeakSet(); - const nilIdentifier = t.unaryExpression("void", t.numericLiteral(0)); - - function setOptionalTransformed(node) { - t.assertMemberExpression(node); // Dev - optionalNodesTransformed.add(node); - } - - function isOptionalTransformed(node) { - t.assertMemberExpression(node); // Dev - return optionalNodesTransformed.has(node); - } - - function createCondition(ref, access, nextProperty, bailout) { - - return t.conditionalExpression( - createCheckAgainstNull( - t.AssignmentExpression("=", ref, access) - ), - - t.memberExpression(ref, nextProperty), - bailout, - ); - } - - function createCheckAgainstNull(left) { - return t.BinaryExpression("!=", left, t.NullLiteral()); - } - - function isNodeOptional(node) { - return node.optional === true; - } - - return { - visitor: { - - AssignmentExpression(path, state) { - const { left } = path.node; - - if (!isNodeOptional(left) || isOptionalTransformed(left)) { - return; - } - - if (!state.optionalTemp) { - const id = path.scope.generateUidIdentifier(); - - state.optionalTemp = id; - path.scope.push({ id }); - } - - const { object, property } = left; - - const isChainNil = t.BinaryExpression( - "!=", - createCondition( - state.optionalTemp, - object, - property, - nilIdentifier, - ), - nilIdentifier, - ); - - // FIXME(sven): if will be a ConditionalExpression for childs, only top level will be ifStatement - const replacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); - - setOptionalTransformed(left); - - path.traverse({ - MemberExpression({ node }) { - setOptionalTransformed(node); - }, - }); - - path.parentPath.replaceWith(replacement); - }, - - UnaryExpression(path, state) { - const { operator, argument } = path.node; - - if (operator !== "delete") { - return; - } - - if (!isNodeOptional(argument) || isOptionalTransformed(argument)) { - return; - } - - if (!state.optionalTemp) { - const id = path.scope.generateUidIdentifier(); - - state.optionalTemp = id; - path.scope.push({ id }); - } - - const { object, property } = argument; - - const isChainNil = t.BinaryExpression( - "!=", - createCondition( - state.optionalTemp, - object, - property, - nilIdentifier, - ), - nilIdentifier, - ); - - const replacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)])); - - setOptionalTransformed(argument); - - path.parentPath.replaceWith(replacement); - }, - - MemberExpression(path, state) { - if (!isNodeOptional(path.node) || isOptionalTransformed(path.node)) { - return; - } - - const { object, property } = path.node; - - if (!state.optionalTemp) { - const id = path.scope.generateUidIdentifier(); - - state.optionalTemp = id; - path.scope.push({ id }); - } - - if (t.isAssignmentExpression(path.parent)) { - return; - } else if (t.isUnaryExpression(path.parent)) { - return; - } else if (t.isCallExpression(path.parent)) { - - const replacement = createCondition( - state.optionalTemp, - object, - property, - t.callExpression(t.identifier("Function"), []), - ); - - setOptionalTransformed(path.node); - path.replaceWith(replacement); - } else { - - const replacement = createCondition( - state.optionalTemp, - object, - property, - nilIdentifier, - ); - - setOptionalTransformed(path.node); + // DO NOT SUBMIT. This is until the parser is complete + const fixer = { + NewExpression: { + exit(path) { + const { callee } = path.node; + if (t.isCallExpression(callee)) { + const replacement = t.newExpression(callee.callee, callee.arguments); + replacement.optional = true; path.replaceWith(replacement); } }, }, + + CallExpression(path) { + const { node } = path; + if (!node.optional || node.callee) { + return; + } + + const callee = t.clone(node.arguments[0]); + if (t.isMemberExpression(callee)) { + callee.optional = node.arguments[1].value; + } + node.callee = callee; + }, + }; + // END DO NOT SUBMIT + + const visitor = { + Program(path) { + path.traverse(fixer); + }, + + MemberExpression(path) { + if (!path.node.optional) { + return; + } + + const { scope, node } = path; + const { object } = node; + + node.optional = false; + + const ref = scope.generateUidIdentifierBasedOnNode(object); + scope.push({ id: ref }); + node.object = ref; + + let parent = path; + let expression; + do { + expression = parent; + parent = parent.parentPath; + } while (!parent.container); + + const replace = parent.isExpression() ? parent : expression; + replace.replaceWith(t.conditionalExpression( + t.binaryExpression("==", t.assignmentExpression("=", ref, object), t.nullLiteral()), + scope.buildUndefinedNode(), + replace.node + )); + }, + + "NewExpression|CallExpression": { + exit(path) { + if (!path.node.optional) { + return; + } + + const { scope, node } = path; + const { callee } = node; + + node.optional = false; + + const ref = scope.generateUidIdentifierBasedOnNode(callee); + scope.push({ id: ref }); + node.callee = ref; + + if (t.isMemberExpression(callee) && !t.isNewExpression(node)) { + const context = scope.generateUidIdentifierBasedOnNode(callee.object); + scope.push({ id: context }); + callee.object = t.assignmentExpression("=", context, callee.object); + + node.arguments.unshift(context); + node.callee = t.memberExpression(node.callee, t.identifier("call")); + } + + path.replaceWith(t.conditionalExpression( + t.binaryExpression("==", t.assignmentExpression("=", ref, callee), t.nullLiteral()), + scope.buildUndefinedNode(), + node + )); + }, + }, + + }; + + return { + visitor, + + manipulateOptions(opts, parserOpts) { + parserOpts.plugins.push("optionalChaining"); + }, }; } diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js index 28f9f5b1e2..c8ee711d7d 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js @@ -1,9 +1,5 @@ -var _temp; +var _a, _a$b$c, _a$b, _a2; -if (((_temp = a) != null ? _temp.b : void 0) != void 0) { - a.b = 42; -} +(_a = a) == null ? void 0 : _a.b = 42; -if (((_temp = a.b.c) != null ? _temp.d : void 0) != void 0) { - a.b.c.d = 42; -} +(((_a2 = a) == null ? void 0 : _a$b = _a2.b) == null ? void 0 : _a$b$c = _a$b.c) == null ? void 0 : _a$b$c.d = 42; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js index a2fd158a7c..1e690c7d45 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js @@ -1,9 +1,5 @@ -var _temp; +var _a, _a$b$c, _a$b, _a2; -if (((_temp = a) != null ? _temp.b : void 0) != void 0) { - delete a.b; -} +(_a = a) == null ? void 0 : delete _a.b; -if (((_temp = ((_temp = a) != null ? _temp.b : void 0).c) != null ? _temp.d : void 0) != void 0) { - delete ((_temp = a.b) != null ? _temp.c : void 0).d; -} +(((_a2 = a) == null ? void 0 : _a$b = _a2.b) == null ? void 0 : _a$b$c = _a$b.c) == null ? void 0 : delete _a$b$c.d; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js index 62587bbd25..9499c5f20e 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js @@ -1 +1,7 @@ +foo?.(foo); + foo?.bar() + +foo.bar?.(foo.bar, false) + +foo?.bar?.(foo.bar, true) diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js index 141f7dd896..1df03edc8c 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -1,3 +1,9 @@ -var _temp; +var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5; -((_temp = foo) != null ? _temp.bar : Function())(); +(_foo = foo) == null ? void 0 : _foo(foo); + +(_foo2 = foo) == null ? void 0 : _foo2.bar(); + +(_foo$bar = (_foo3 = foo).bar) == null ? void 0 : _foo$bar.call(_foo3, foo.bar, false); + +(_foo4 = foo) == null ? void 0 : (_foo4$bar = (_foo5 = _foo4).bar) == null ? void 0 : _foo4$bar.call(_foo5, foo.bar, true); \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index fe9a91bd04..1c779cf856 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1,5 +1,5 @@ -var _temp; +var _foo, _a$b$c, _a; -(_temp = foo) != null ? _temp.bar : void 0; +(_foo = foo) == null ? void 0 : _foo.bar; -((_temp = ((_temp = a) != null ? _temp.b : void 0).c) != null ? _temp.d : void 0).e; +(_a$b$c = (_a = a) == null ? void 0 : _a.b.c) == null ? void 0 : _a$b$c.d.e; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js new file mode 100644 index 0000000000..b937f96491 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js @@ -0,0 +1,8 @@ +new a?.b +new a?.b?.c?.d + +new a?.b() +new a?.b?.c?.d() + +new b?.(b) +new a?.b?.(a.b, true) diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js new file mode 100644 index 0000000000..80b39699a2 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js @@ -0,0 +1,10 @@ +var _a, _a$b$c, _a$b, _a2, _a3, _a$b$c2, _a$b2, _a4, _b, _a5, _a5$b; + +(_a = a) == null ? void 0 : new _a.b(); +(((_a2 = a) == null ? void 0 : _a$b = _a2.b) == null ? void 0 : _a$b$c = _a$b.c) == null ? void 0 : new _a$b$c.d(); + +(_a3 = a) == null ? void 0 : new _a3.b(); +(((_a4 = a) == null ? void 0 : _a$b2 = _a4.b) == null ? void 0 : _a$b$c2 = _a$b2.c) == null ? void 0 : new _a$b$c2.d(); + +(_b = b) == null ? void 0 : new _b(b); +(_a5 = a) == null ? void 0 : (_a5$b = _a5.b) == null ? void 0 : new _a5$b(a.b, true); \ No newline at end of file From 2a496890ffcb5409eac42782ebb5ee4192d5367f Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sat, 3 Jun 2017 02:07:40 -0400 Subject: [PATCH 12/33] Use a better nested syntax This way, it quickly returns from the conditions. The first nil will now exit, instead of checking every nil. This also allows conditionalChaining inside a container to still operate the container. --- .../babel-plugin-transform-optional-chaining/src/index.js | 6 +++--- .../test/fixtures/general/assignement/expected.js | 2 +- .../test/fixtures/general/containers/actual.js | 6 ++++++ .../test/fixtures/general/containers/expected.js | 8 ++++++++ .../test/fixtures/general/delete/expected.js | 2 +- .../test/fixtures/general/member-access/expected.js | 2 +- .../test/fixtures/general/new/expected.js | 4 ++-- 7 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 287f14963d..9bde485b52 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -47,11 +47,11 @@ export default function ({ types: t }) { node.object = ref; let parent = path; - let expression; - do { + let expression = path; + while (parent.listKey === undefined) { expression = parent; parent = parent.parentPath; - } while (!parent.container); + } const replace = parent.isExpression() ? parent : expression; replace.replaceWith(t.conditionalExpression( diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js index c8ee711d7d..86ba0f57ef 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js @@ -2,4 +2,4 @@ var _a, _a$b$c, _a$b, _a2; (_a = a) == null ? void 0 : _a.b = 42; -(((_a2 = a) == null ? void 0 : _a$b = _a2.b) == null ? void 0 : _a$b$c = _a$b.c) == null ? void 0 : _a$b$c.d = 42; \ No newline at end of file +(_a2 = a) == null ? void 0 : (_a$b = _a2.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : _a$b$c.d = 42; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/actual.js new file mode 100644 index 0000000000..d699b16fa1 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/actual.js @@ -0,0 +1,6 @@ +var street = user.address?.street +street = user.address?.street + +test(a?.b, 1); + +(1, a?.b, 2); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js new file mode 100644 index 0000000000..811e1603a0 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js @@ -0,0 +1,8 @@ +var _user$address, _user$address2, _a, _a2; + +var street = (_user$address = user.address) == null ? void 0 : _user$address.street; +(_user$address2 = user.address) == null ? void 0 : street = _user$address2.street; + +test((_a = a) == null ? void 0 : _a.b, 1); + +1, (_a2 = a) == null ? void 0 : _a2.b, 2; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js index 1e690c7d45..49afe9cd6e 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js @@ -2,4 +2,4 @@ var _a, _a$b$c, _a$b, _a2; (_a = a) == null ? void 0 : delete _a.b; -(((_a2 = a) == null ? void 0 : _a$b = _a2.b) == null ? void 0 : _a$b$c = _a$b.c) == null ? void 0 : delete _a$b$c.d; \ No newline at end of file +(_a2 = a) == null ? void 0 : (_a$b = _a2.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : delete _a$b$c.d; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index 1c779cf856..a8a381af7a 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -2,4 +2,4 @@ var _foo, _a$b$c, _a; (_foo = foo) == null ? void 0 : _foo.bar; -(_a$b$c = (_a = a) == null ? void 0 : _a.b.c) == null ? void 0 : _a$b$c.d.e; \ No newline at end of file +(_a = a) == null ? void 0 : (_a$b$c = _a.b.c) == null ? void 0 : _a$b$c.d.e; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js index 80b39699a2..1d6d1ad2d5 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js @@ -1,10 +1,10 @@ var _a, _a$b$c, _a$b, _a2, _a3, _a$b$c2, _a$b2, _a4, _b, _a5, _a5$b; (_a = a) == null ? void 0 : new _a.b(); -(((_a2 = a) == null ? void 0 : _a$b = _a2.b) == null ? void 0 : _a$b$c = _a$b.c) == null ? void 0 : new _a$b$c.d(); +(_a2 = a) == null ? void 0 : (_a$b = _a2.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : new _a$b$c.d(); (_a3 = a) == null ? void 0 : new _a3.b(); -(((_a4 = a) == null ? void 0 : _a$b2 = _a4.b) == null ? void 0 : _a$b$c2 = _a$b2.c) == null ? void 0 : new _a$b$c2.d(); +(_a4 = a) == null ? void 0 : (_a$b2 = _a4.b) == null ? void 0 : (_a$b$c2 = _a$b2.c) == null ? void 0 : new _a$b$c2.d(); (_b = b) == null ? void 0 : new _b(b); (_a5 = a) == null ? void 0 : (_a5$b = _a5.b) == null ? void 0 : new _a5$b(a.b, true); \ No newline at end of file From 30ee87159dcc6b0b179ba5adb83cc7fba4c7b693 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sat, 3 Jun 2017 02:39:16 -0400 Subject: [PATCH 13/33] Split syntax and transform into two plugins --- .../.gitignore | 3 + .../.npmignore | 3 + .../README.md | 35 +++++ .../index.js | 7 + .../package.json | 13 ++ .../package.json | 5 +- .../src/index.js | 129 +++++++----------- 7 files changed, 109 insertions(+), 86 deletions(-) create mode 100644 packages/babel-plugin-syntax-optional-chaining/.gitignore create mode 100644 packages/babel-plugin-syntax-optional-chaining/.npmignore create mode 100644 packages/babel-plugin-syntax-optional-chaining/README.md create mode 100644 packages/babel-plugin-syntax-optional-chaining/index.js create mode 100644 packages/babel-plugin-syntax-optional-chaining/package.json diff --git a/packages/babel-plugin-syntax-optional-chaining/.gitignore b/packages/babel-plugin-syntax-optional-chaining/.gitignore new file mode 100644 index 0000000000..cace0d6ddc --- /dev/null +++ b/packages/babel-plugin-syntax-optional-chaining/.gitignore @@ -0,0 +1,3 @@ +node_modules +*.log +src diff --git a/packages/babel-plugin-syntax-optional-chaining/.npmignore b/packages/babel-plugin-syntax-optional-chaining/.npmignore new file mode 100644 index 0000000000..f980694583 --- /dev/null +++ b/packages/babel-plugin-syntax-optional-chaining/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-plugin-syntax-optional-chaining/README.md b/packages/babel-plugin-syntax-optional-chaining/README.md new file mode 100644 index 0000000000..b18c1c25b3 --- /dev/null +++ b/packages/babel-plugin-syntax-optional-chaining/README.md @@ -0,0 +1,35 @@ +# babel-plugin-syntax-optional-chaining + +Allow parsing of optional properties. + +## Installation + +```sh +npm install --save-dev babel-plugin-syntax-optional-chaining +``` + +## Usage + +### Via `.babelrc` (Recommended) + +**.babelrc** + +```json +{ + "plugins": ["syntax-optional-chaining"] +} +``` + +### Via CLI + +```sh +babel --plugins syntax-optional-chaining script.js +``` + +### Via Node API + +```javascript +require("babel-core").transform("code", { + plugins: ["syntax-optional-chaining"] +}); +``` diff --git a/packages/babel-plugin-syntax-optional-chaining/index.js b/packages/babel-plugin-syntax-optional-chaining/index.js new file mode 100644 index 0000000000..37e4c8f53d --- /dev/null +++ b/packages/babel-plugin-syntax-optional-chaining/index.js @@ -0,0 +1,7 @@ +export default function () { + return { + manipulateOptions(opts, parserOpts) { + parserOpts.plugins.push("optionalChaining"); + }, + }; +} diff --git a/packages/babel-plugin-syntax-optional-chaining/package.json b/packages/babel-plugin-syntax-optional-chaining/package.json new file mode 100644 index 0000000000..d14cd0549a --- /dev/null +++ b/packages/babel-plugin-syntax-optional-chaining/package.json @@ -0,0 +1,13 @@ +{ + "name": "babel-plugin-syntax-optional-chaining", + "version": "7.0.0-alpha.12", + "description": "Allow parsing of optional properties", + "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-chaining", + "license": "MIT", + "main": "lib/index.js", + "keywords": [ + "babel-plugin" + ], + "dependencies": {}, + "devDependencies": {} +} diff --git a/packages/babel-plugin-transform-optional-chaining/package.json b/packages/babel-plugin-transform-optional-chaining/package.json index 1f6fc11699..04292fc777 100644 --- a/packages/babel-plugin-transform-optional-chaining/package.json +++ b/packages/babel-plugin-transform-optional-chaining/package.json @@ -6,10 +6,7 @@ "license": "MIT", "main": "lib/index.js", "dependencies": { - "babel-traverse": "7.0.0-alpha.7", - "babel-types": "7.0.0-alpha.7", - "babel-template": "7.0.0-alpha.7", - "lodash": "^4.2.0" + "babel-plugin-syntax-optional-chaining": "7.0.0-alpha.12" }, "keywords": [ "babel-plugin" diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 9bde485b52..95a565de82 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,105 +1,70 @@ +import syntaxOptionalChaining from "babel-plugin-syntax-optional-chaining"; + export default function ({ types: t }) { - // DO NOT SUBMIT. This is until the parser is complete - const fixer = { - NewExpression: { - exit(path) { - const { callee } = path.node; - if (t.isCallExpression(callee)) { - const replacement = t.newExpression(callee.callee, callee.arguments); - replacement.optional = true; - path.replaceWith(replacement); - } - }, - }, + return { + inherits: syntaxOptionalChaining, - CallExpression(path) { - const { node } = path; - if (!node.optional || node.callee) { - return; - } - - const callee = t.clone(node.arguments[0]); - if (t.isMemberExpression(callee)) { - callee.optional = node.arguments[1].value; - } - node.callee = callee; - }, - }; - // END DO NOT SUBMIT - - const visitor = { - Program(path) { - path.traverse(fixer); - }, - - MemberExpression(path) { - if (!path.node.optional) { - return; - } - - const { scope, node } = path; - const { object } = node; - - node.optional = false; - - const ref = scope.generateUidIdentifierBasedOnNode(object); - scope.push({ id: ref }); - node.object = ref; - - let parent = path; - let expression = path; - while (parent.listKey === undefined) { - expression = parent; - parent = parent.parentPath; - } - - const replace = parent.isExpression() ? parent : expression; - replace.replaceWith(t.conditionalExpression( - t.binaryExpression("==", t.assignmentExpression("=", ref, object), t.nullLiteral()), - scope.buildUndefinedNode(), - replace.node - )); - }, - - "NewExpression|CallExpression": { - exit(path) { + visitor: { + MemberExpression(path) { if (!path.node.optional) { return; } const { scope, node } = path; - const { callee } = node; + const { object } = node; node.optional = false; - const ref = scope.generateUidIdentifierBasedOnNode(callee); + const ref = scope.generateUidIdentifierBasedOnNode(object); scope.push({ id: ref }); - node.callee = ref; + node.object = ref; - if (t.isMemberExpression(callee) && !t.isNewExpression(node)) { - const context = scope.generateUidIdentifierBasedOnNode(callee.object); - scope.push({ id: context }); - callee.object = t.assignmentExpression("=", context, callee.object); - - node.arguments.unshift(context); - node.callee = t.memberExpression(node.callee, t.identifier("call")); + let parent = path; + let expression = path; + while (parent.listKey === undefined) { + expression = parent; + parent = parent.parentPath; } - path.replaceWith(t.conditionalExpression( - t.binaryExpression("==", t.assignmentExpression("=", ref, callee), t.nullLiteral()), + const replace = parent.isExpression() ? parent : expression; + replace.replaceWith(t.conditionalExpression( + t.binaryExpression("==", t.assignmentExpression("=", ref, object), t.nullLiteral()), scope.buildUndefinedNode(), - node + replace.node )); }, - }, - }; + "NewExpression|CallExpression": { + exit(path) { + if (!path.node.optional) { + return; + } - return { - visitor, + const { scope, node } = path; + const { callee } = node; - manipulateOptions(opts, parserOpts) { - parserOpts.plugins.push("optionalChaining"); + node.optional = false; + + const ref = scope.generateUidIdentifierBasedOnNode(callee); + scope.push({ id: ref }); + node.callee = ref; + + if (t.isMemberExpression(callee) && !t.isNewExpression(node)) { + const context = scope.generateUidIdentifierBasedOnNode(callee.object); + scope.push({ id: context }); + callee.object = t.assignmentExpression("=", context, callee.object); + + node.arguments.unshift(context); + node.callee = t.memberExpression(node.callee, t.identifier("call")); + } + + path.replaceWith(t.conditionalExpression( + t.binaryExpression("==", t.assignmentExpression("=", ref, callee), t.nullLiteral()), + scope.buildUndefinedNode(), + node + )); + }, + }, }, }; } From 5fe48035620b74db4434a5c5953ea9952c33048c Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sat, 3 Jun 2017 02:39:38 -0400 Subject: [PATCH 14/33] Simplify NewExpression|CallExpression visitor --- .../src/index.js | 46 +++++++++---------- .../general/function-call/expected.js | 4 +- .../test/fixtures/general/new/expected.js | 4 +- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 95a565de82..49ff4c81e7 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -34,36 +34,34 @@ export default function ({ types: t }) { )); }, - "NewExpression|CallExpression": { - exit(path) { - if (!path.node.optional) { - return; - } + "NewExpression|CallExpression"(path) { + if (!path.node.optional) { + return; + } - const { scope, node } = path; - const { callee } = node; + const { scope, node } = path; + const { callee } = node; - node.optional = false; + node.optional = false; - const ref = scope.generateUidIdentifierBasedOnNode(callee); - scope.push({ id: ref }); - node.callee = ref; + const ref = scope.generateUidIdentifierBasedOnNode(callee); + scope.push({ id: ref }); + node.callee = ref; - if (t.isMemberExpression(callee) && !t.isNewExpression(node)) { - const context = scope.generateUidIdentifierBasedOnNode(callee.object); - scope.push({ id: context }); - callee.object = t.assignmentExpression("=", context, callee.object); + if (t.isMemberExpression(callee) && !t.isNewExpression(node)) { + const context = scope.generateUidIdentifierBasedOnNode(callee.object); + scope.push({ id: context }); + callee.object = t.assignmentExpression("=", context, callee.object); - node.arguments.unshift(context); - node.callee = t.memberExpression(node.callee, t.identifier("call")); - } + node.arguments.unshift(context); + node.callee = t.memberExpression(node.callee, t.identifier("call")); + } - path.replaceWith(t.conditionalExpression( - t.binaryExpression("==", t.assignmentExpression("=", ref, callee), t.nullLiteral()), - scope.buildUndefinedNode(), - node - )); - }, + path.replaceWith(t.conditionalExpression( + t.binaryExpression("==", t.assignmentExpression("=", ref, callee), t.nullLiteral()), + scope.buildUndefinedNode(), + node + )); }, }, }; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js index 1df03edc8c..0b6f985356 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -1,4 +1,4 @@ -var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5; +var _foo, _foo2, _foo$bar, _foo3, _foo$bar2, _foo4, _foo5; (_foo = foo) == null ? void 0 : _foo(foo); @@ -6,4 +6,4 @@ var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5; (_foo$bar = (_foo3 = foo).bar) == null ? void 0 : _foo$bar.call(_foo3, foo.bar, false); -(_foo4 = foo) == null ? void 0 : (_foo4$bar = (_foo5 = _foo4).bar) == null ? void 0 : _foo4$bar.call(_foo5, foo.bar, true); \ No newline at end of file +(_foo5 = _foo4 = foo) == null ? void 0 : (_foo$bar2 = _foo5.bar) == null ? void 0 : _foo$bar2.call(_foo4, foo.bar, true); \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js index 1d6d1ad2d5..cf631ecfd0 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js @@ -1,4 +1,4 @@ -var _a, _a$b$c, _a$b, _a2, _a3, _a$b$c2, _a$b2, _a4, _b, _a5, _a5$b; +var _a, _a$b$c, _a$b, _a2, _a3, _a$b$c2, _a$b2, _a4, _b, _a$b3, _a5; (_a = a) == null ? void 0 : new _a.b(); (_a2 = a) == null ? void 0 : (_a$b = _a2.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : new _a$b$c.d(); @@ -7,4 +7,4 @@ var _a, _a$b$c, _a$b, _a2, _a3, _a$b$c2, _a$b2, _a4, _b, _a5, _a5$b; (_a4 = a) == null ? void 0 : (_a$b2 = _a4.b) == null ? void 0 : (_a$b$c2 = _a$b2.c) == null ? void 0 : new _a$b$c2.d(); (_b = b) == null ? void 0 : new _b(b); -(_a5 = a) == null ? void 0 : (_a5$b = _a5.b) == null ? void 0 : new _a5$b(a.b, true); \ No newline at end of file +(_a5 = a) == null ? void 0 : (_a$b3 = _a5.b) == null ? void 0 : new _a$b3(a.b, true); \ No newline at end of file From 85b6b4b1b2627682e9cc38dbb82bb538cb295739 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sat, 3 Jun 2017 21:38:02 -0400 Subject: [PATCH 15/33] Print optional chain operator --- packages/babel-generator/src/generators/expressions.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/babel-generator/src/generators/expressions.js b/packages/babel-generator/src/generators/expressions.js index a3600f83b5..68afd50ea7 100644 --- a/packages/babel-generator/src/generators/expressions.js +++ b/packages/babel-generator/src/generators/expressions.js @@ -203,12 +203,17 @@ export function MemberExpression(node: Object) { computed = true; } + if (node.optional) { + this.token("?."); + } if (computed) { this.token("["); this.print(node.property, node); this.token("]"); } else { - this.token("."); + if (!node.optional) { + this.token("."); + } this.print(node.property, node); } } From acdd3637bccde01420dc9fe3f027299399fe05e9 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sat, 3 Jun 2017 21:38:24 -0400 Subject: [PATCH 16/33] Refactor --- .../src/index.js | 133 ++++++++++++------ .../fixtures/general/assignement/actual.js | 4 + .../fixtures/general/assignement/expected.js | 8 +- .../fixtures/general/containers/expected.js | 4 +- .../test/fixtures/general/delete/expected.js | 6 +- .../general/function-call-loose/actual.js | 7 + .../general/function-call-loose/expected.js | 9 ++ .../general/function-call-loose/options.json | 3 + .../general/function-call/expected.js | 4 +- .../fixtures/general/member-access/actual.js | 8 ++ .../general/member-access/expected.js | 12 +- .../fixtures/general/memoize-loose/actual.js | 21 +++ .../general/memoize-loose/expected.js | 23 +++ .../general/memoize-loose/options.json | 3 + .../test/fixtures/general/memoize/actual.js | 21 +++ .../test/fixtures/general/memoize/expected.js | 23 +++ .../test/fixtures/general/new/expected.js | 8 +- 17 files changed, 239 insertions(+), 58 deletions(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/expected.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/options.json create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/expected.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/options.json create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/actual.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 49ff4c81e7..9c4b7805a5 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,6 +1,78 @@ import syntaxOptionalChaining from "babel-plugin-syntax-optional-chaining"; export default function ({ types: t }) { + function optional(path, key, replacementPath, needsContext = false, loose = false) { + const { scope } = path; + const optionals = [path.node]; + + let objectPath = path.get(key); + while (objectPath.isMemberExpression()) { + const { node } = objectPath; + if (node.optional) { + optionals.push(node); + } + + objectPath = objectPath.get("object"); + } + + for (let i = optionals.length - 1; i >= 0; i--) { + const node = optionals[i]; + node.optional = false; + + const replaceKey = i == 0 ? key : "object"; + const atCall = needsContext && i == 0; + + const chain = node[replaceKey]; + + let ref; + let check; + if (loose && atCall) { + // If we are using a loose transform (avoiding a Function#call) and we are at the call, + // we can avoid a needless memoize. + ref = chain; + check = ref; + } else { + ref = scope.maybeGenerateMemoised(chain); + if (ref) { + check = t.assignmentExpression("=", ref, chain); + node[replaceKey] = ref; + } else { + ref = chain; + check = chain; + } + } + + // Ensure call expressions have the proper `this` + // `foo.bar()` has context `foo`. + if (atCall && t.isMemberExpression(chain)) { + if (loose) { + // To avoid a Function#call, we can instead re-grab the property from the context object. + // `a.?b.?()` translates roughly to `_a.b != null && _a.b()` + node.callee = chain + } else { + // Otherwise, we need to memoize the context object, and change the call into a Function#call. + // `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)` + const { object } = chain; + const context = scope.generateUidIdentifierBasedOnNode(object); + + scope.push({ id: context }); + chain.object = t.assignmentExpression("=", context, object); + + node.arguments.unshift(context); + node.callee = t.memberExpression(node.callee, t.identifier("call")); + } + } + + replacementPath.replaceWith(t.conditionalExpression( + t.binaryExpression("==", check, t.nullLiteral()), + scope.buildUndefinedNode(), + replacementPath.node + )); + + replacementPath = replacementPath.get("alternate"); + } + } + return { inherits: syntaxOptionalChaining, @@ -10,28 +82,25 @@ export default function ({ types: t }) { return; } - const { scope, node } = path; - const { object } = node; + const replace = path.find(path => { + const { parentPath } = path; + if (parentPath.isStatement()) { + return true; + } - node.optional = false; + if (path.key == "left" && parentPath.isAssignmentExpression()) { + return false; + } + if (path.key == "object" && parentPath.isMemberExpression()) { + return false; + } + if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) { + return false; + } + return true; + }); - const ref = scope.generateUidIdentifierBasedOnNode(object); - scope.push({ id: ref }); - node.object = ref; - - let parent = path; - let expression = path; - while (parent.listKey === undefined) { - expression = parent; - parent = parent.parentPath; - } - - const replace = parent.isExpression() ? parent : expression; - replace.replaceWith(t.conditionalExpression( - t.binaryExpression("==", t.assignmentExpression("=", ref, object), t.nullLiteral()), - scope.buildUndefinedNode(), - replace.node - )); + optional(path, "object", replace); }, "NewExpression|CallExpression"(path) { @@ -39,29 +108,7 @@ export default function ({ types: t }) { return; } - const { scope, node } = path; - const { callee } = node; - - node.optional = false; - - const ref = scope.generateUidIdentifierBasedOnNode(callee); - scope.push({ id: ref }); - node.callee = ref; - - if (t.isMemberExpression(callee) && !t.isNewExpression(node)) { - const context = scope.generateUidIdentifierBasedOnNode(callee.object); - scope.push({ id: context }); - callee.object = t.assignmentExpression("=", context, callee.object); - - node.arguments.unshift(context); - node.callee = t.memberExpression(node.callee, t.identifier("call")); - } - - path.replaceWith(t.conditionalExpression( - t.binaryExpression("==", t.assignmentExpression("=", ref, callee), t.nullLiteral()), - scope.buildUndefinedNode(), - node - )); + optional(path, "callee", path, path.isCallExpression(), this.opts.loose); }, }, }; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js index b9405035c9..b7d9cee953 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/actual.js @@ -1,3 +1,7 @@ a?.b = 42 a?.b?.c?.d = 42 + +a?.b?.c?.d++ + +a?.b?.c?.d += 1 diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js index 86ba0f57ef..5670c0f0e1 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/assignement/expected.js @@ -1,5 +1,9 @@ -var _a, _a$b$c, _a$b, _a2; +var _a, _a2, _a2$b, _a2$b$c, _a3, _a3$b, _a3$b$c, _a4, _a4$b, _a4$b$c; (_a = a) == null ? void 0 : _a.b = 42; -(_a2 = a) == null ? void 0 : (_a$b = _a2.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : _a$b$c.d = 42; \ No newline at end of file +(_a2 = a) == null ? void 0 : (_a2$b = _a2.b) == null ? void 0 : (_a2$b$c = _a2$b.c) == null ? void 0 : _a2$b$c.d = 42; + +(_a3 = a) == null ? void 0 : (_a3$b = _a3.b) == null ? void 0 : (_a3$b$c = _a3$b.c) == null ? void 0 : _a3$b$c.d++; + +(_a4 = a) == null ? void 0 : (_a4$b = _a4.b) == null ? void 0 : (_a4$b$c = _a4$b.c) == null ? void 0 : _a4$b$c.d += 1; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js index 811e1603a0..b0dc489b21 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/containers/expected.js @@ -1,8 +1,8 @@ var _user$address, _user$address2, _a, _a2; var street = (_user$address = user.address) == null ? void 0 : _user$address.street; -(_user$address2 = user.address) == null ? void 0 : street = _user$address2.street; +street = (_user$address2 = user.address) == null ? void 0 : _user$address2.street; test((_a = a) == null ? void 0 : _a.b, 1); -1, (_a2 = a) == null ? void 0 : _a2.b, 2; +1, (_a2 = a) == null ? void 0 : _a2.b, 2; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js index 49afe9cd6e..d1c9cb8617 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js @@ -1,5 +1,5 @@ -var _a, _a$b$c, _a$b, _a2; +var _a, _a2, _a2$b, _a2$b$c; -(_a = a) == null ? void 0 : delete _a.b; +delete ((_a = a) == null ? void 0 : _a.b); -(_a2 = a) == null ? void 0 : (_a$b = _a2.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : delete _a$b$c.d; \ No newline at end of file +delete ((_a2 = a) == null ? void 0 : (_a2$b = _a2.b) == null ? void 0 : (_a2$b$c = _a2$b.c) == null ? void 0 : _a2$b$c.d); \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/actual.js new file mode 100644 index 0000000000..9499c5f20e --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/actual.js @@ -0,0 +1,7 @@ +foo?.(foo); + +foo?.bar() + +foo.bar?.(foo.bar, false) + +foo?.bar?.(foo.bar, true) diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/expected.js new file mode 100644 index 0000000000..7c40785d92 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/expected.js @@ -0,0 +1,9 @@ +var _foo, _foo2; + +foo == null ? void 0 : foo(foo); + +(_foo = foo) == null ? void 0 : _foo.bar(); + +foo.bar == null ? void 0 : foo.bar(foo.bar, false); + +(_foo2 = foo) == null ? void 0 : _foo2.bar == null ? void 0 : _foo2.bar(foo.bar, true); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/options.json b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/options.json new file mode 100644 index 0000000000..acc83616f9 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call-loose/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-optional-chaining", {"loose": true}]] +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js index 0b6f985356..1df03edc8c 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -1,4 +1,4 @@ -var _foo, _foo2, _foo$bar, _foo3, _foo$bar2, _foo4, _foo5; +var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5; (_foo = foo) == null ? void 0 : _foo(foo); @@ -6,4 +6,4 @@ var _foo, _foo2, _foo$bar, _foo3, _foo$bar2, _foo4, _foo5; (_foo$bar = (_foo3 = foo).bar) == null ? void 0 : _foo$bar.call(_foo3, foo.bar, false); -(_foo5 = _foo4 = foo) == null ? void 0 : (_foo$bar2 = _foo5.bar) == null ? void 0 : _foo$bar2.call(_foo4, foo.bar, true); \ No newline at end of file +(_foo4 = foo) == null ? void 0 : (_foo4$bar = (_foo5 = _foo4).bar) == null ? void 0 : _foo4$bar.call(_foo5, foo.bar, true); \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js index 83447e76d5..d6a9752544 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js @@ -1,3 +1,11 @@ foo?.bar a?.b.c?.d.e + +orders?.[0].price + +orders?.[0]?.price + +orders[client?.key].price + +orders[client.key]?.price diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index a8a381af7a..ffc9f8bc4e 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1,5 +1,13 @@ -var _foo, _a$b$c, _a; +var _foo, _a, _a$b$c, _orders, _orders2, _orders2$, _client, _orders$client$key; (_foo = foo) == null ? void 0 : _foo.bar; -(_a = a) == null ? void 0 : (_a$b$c = _a.b.c) == null ? void 0 : _a$b$c.d.e; \ No newline at end of file +(_a = a) == null ? void 0 : (_a$b$c = _a.b.c) == null ? void 0 : _a$b$c.d.e; + +(_orders = orders) == null ? void 0 : _orders[0].price; + +(_orders2 = orders) == null ? void 0 : (_orders2$ = _orders2[0]) == null ? void 0 : _orders2$.price; + +orders[(_client = client) == null ? void 0 : _client.key].price; + +(_orders$client$key = orders[client.key]) == null ? void 0 : _orders$client$key.price; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/actual.js new file mode 100644 index 0000000000..ffe543adde --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/actual.js @@ -0,0 +1,21 @@ +function test(foo) { + foo?.bar; + + foo?.bar?.baz; + + foo?.(foo); + + foo?.bar() + + foo.bar?.(foo.bar, false) + + foo?.bar?.(foo.bar, true) + + foo.bar?.baz(foo.bar, false) + + foo?.bar?.baz(foo.bar, true) + + foo.bar?.baz?.(foo.bar, false) + + foo?.bar?.baz?.(foo.bar, true) +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/expected.js new file mode 100644 index 0000000000..66f4809d09 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/expected.js @@ -0,0 +1,23 @@ +function test(foo) { + var _foo$bar, _foo$bar2, _foo$bar3, _foo$bar4, _foo$bar5; + + foo == null ? void 0 : foo.bar; + + foo == null ? void 0 : (_foo$bar = foo.bar) == null ? void 0 : _foo$bar.baz; + + foo == null ? void 0 : foo(foo); + + foo == null ? void 0 : foo.bar(); + + foo.bar == null ? void 0 : foo.bar(foo.bar, false); + + foo == null ? void 0 : foo.bar == null ? void 0 : foo.bar(foo.bar, true); + + (_foo$bar2 = foo.bar) == null ? void 0 : _foo$bar2.baz(foo.bar, false); + + foo == null ? void 0 : (_foo$bar3 = foo.bar) == null ? void 0 : _foo$bar3.baz(foo.bar, true); + + (_foo$bar4 = foo.bar) == null ? void 0 : _foo$bar4.baz == null ? void 0 : _foo$bar4.baz(foo.bar, false); + + foo == null ? void 0 : (_foo$bar5 = foo.bar) == null ? void 0 : _foo$bar5.baz == null ? void 0 : _foo$bar5.baz(foo.bar, true); +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/options.json b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/options.json new file mode 100644 index 0000000000..acc83616f9 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize-loose/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-optional-chaining", {"loose": true}]] +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/actual.js new file mode 100644 index 0000000000..ffe543adde --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/actual.js @@ -0,0 +1,21 @@ +function test(foo) { + foo?.bar; + + foo?.bar?.baz; + + foo?.(foo); + + foo?.bar() + + foo.bar?.(foo.bar, false) + + foo?.bar?.(foo.bar, true) + + foo.bar?.baz(foo.bar, false) + + foo?.bar?.baz(foo.bar, true) + + foo.bar?.baz?.(foo.bar, false) + + foo?.bar?.baz?.(foo.bar, true) +} diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js new file mode 100644 index 0000000000..d02f52f934 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js @@ -0,0 +1,23 @@ +function test(foo) { + var _foo$bar, _foo$bar2, _foo, _foo$bar3, _foo2, _foo$bar4, _foo$bar5, _foo$bar6, _foo$bar6$baz, _foo$bar7, _foo$bar8, _foo$bar8$baz, _foo$bar9; + + foo == null ? void 0 : foo.bar; + + foo == null ? void 0 : (_foo$bar = foo.bar) == null ? void 0 : _foo$bar.baz; + + foo == null ? void 0 : foo(foo); + + foo == null ? void 0 : foo.bar(); + + (_foo$bar2 = (_foo = foo).bar) == null ? void 0 : _foo$bar2.call(_foo, foo.bar, false); + + foo == null ? void 0 : (_foo$bar3 = (_foo2 = foo).bar) == null ? void 0 : _foo$bar3.call(_foo2, foo.bar, true); + + (_foo$bar4 = foo.bar) == null ? void 0 : _foo$bar4.baz(foo.bar, false); + + foo == null ? void 0 : (_foo$bar5 = foo.bar) == null ? void 0 : _foo$bar5.baz(foo.bar, true); + + (_foo$bar6 = foo.bar) == null ? void 0 : (_foo$bar6$baz = (_foo$bar7 = _foo$bar6).baz) == null ? void 0 : _foo$bar6$baz.call(_foo$bar7, foo.bar, false); + + foo == null ? void 0 : (_foo$bar8 = foo.bar) == null ? void 0 : (_foo$bar8$baz = (_foo$bar9 = _foo$bar8).baz) == null ? void 0 : _foo$bar8$baz.call(_foo$bar9, foo.bar, true); +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js index cf631ecfd0..241badcfdd 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js @@ -1,10 +1,10 @@ -var _a, _a$b$c, _a$b, _a2, _a3, _a$b$c2, _a$b2, _a4, _b, _a$b3, _a5; +var _a, _a2, _a2$b, _a2$b$c, _a3, _a4, _a4$b, _a4$b$c, _b, _a5, _a5$b; (_a = a) == null ? void 0 : new _a.b(); -(_a2 = a) == null ? void 0 : (_a$b = _a2.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : new _a$b$c.d(); +(_a2 = a) == null ? void 0 : (_a2$b = _a2.b) == null ? void 0 : (_a2$b$c = _a2$b.c) == null ? void 0 : new _a2$b$c.d(); (_a3 = a) == null ? void 0 : new _a3.b(); -(_a4 = a) == null ? void 0 : (_a$b2 = _a4.b) == null ? void 0 : (_a$b$c2 = _a$b2.c) == null ? void 0 : new _a$b$c2.d(); +(_a4 = a) == null ? void 0 : (_a4$b = _a4.b) == null ? void 0 : (_a4$b$c = _a4$b.c) == null ? void 0 : new _a4$b$c.d(); (_b = b) == null ? void 0 : new _b(b); -(_a5 = a) == null ? void 0 : (_a$b3 = _a5.b) == null ? void 0 : new _a$b3(a.b, true); \ No newline at end of file +(_a5 = a) == null ? void 0 : (_a5$b = _a5.b) == null ? void 0 : new _a5$b(a.b, true); \ No newline at end of file From 9ce797dd34ac16bd5b29efbedae2751a177e0859 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sat, 3 Jun 2017 23:52:04 -0400 Subject: [PATCH 17/33] Reduce context memoization when possible --- .../src/index.js | 16 ++++++++-------- .../fixtures/general/function-call/expected.js | 4 ++-- .../test/fixtures/general/memoize/expected.js | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 9c4b7805a5..826a5b8a4c 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -29,16 +29,14 @@ export default function ({ types: t }) { if (loose && atCall) { // If we are using a loose transform (avoiding a Function#call) and we are at the call, // we can avoid a needless memoize. - ref = chain; - check = ref; + check = ref = chain; } else { ref = scope.maybeGenerateMemoised(chain); if (ref) { check = t.assignmentExpression("=", ref, chain); node[replaceKey] = ref; } else { - ref = chain; - check = chain; + check = ref = chain; } } @@ -53,10 +51,12 @@ export default function ({ types: t }) { // Otherwise, we need to memoize the context object, and change the call into a Function#call. // `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)` const { object } = chain; - const context = scope.generateUidIdentifierBasedOnNode(object); - - scope.push({ id: context }); - chain.object = t.assignmentExpression("=", context, object); + let context = scope.maybeGenerateMemoised(object); + if (context) { + chain.object = t.assignmentExpression("=", context, object); + } else { + context = object; + } node.arguments.unshift(context); node.callee = t.memberExpression(node.callee, t.identifier("call")); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js index 1df03edc8c..ca85c85dac 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -1,4 +1,4 @@ -var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5; +var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar; (_foo = foo) == null ? void 0 : _foo(foo); @@ -6,4 +6,4 @@ var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5; (_foo$bar = (_foo3 = foo).bar) == null ? void 0 : _foo$bar.call(_foo3, foo.bar, false); -(_foo4 = foo) == null ? void 0 : (_foo4$bar = (_foo5 = _foo4).bar) == null ? void 0 : _foo4$bar.call(_foo5, foo.bar, true); \ No newline at end of file +(_foo4 = foo) == null ? void 0 : (_foo4$bar = _foo4.bar) == null ? void 0 : _foo4$bar.call(_foo4, foo.bar, true); \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js index d02f52f934..2e24fb6f41 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/memoize/expected.js @@ -1,5 +1,5 @@ function test(foo) { - var _foo$bar, _foo$bar2, _foo, _foo$bar3, _foo2, _foo$bar4, _foo$bar5, _foo$bar6, _foo$bar6$baz, _foo$bar7, _foo$bar8, _foo$bar8$baz, _foo$bar9; + var _foo$bar, _foo$bar2, _foo$bar3, _foo$bar4, _foo$bar5, _foo$bar6, _foo$bar6$baz, _foo$bar7, _foo$bar7$baz; foo == null ? void 0 : foo.bar; @@ -9,15 +9,15 @@ function test(foo) { foo == null ? void 0 : foo.bar(); - (_foo$bar2 = (_foo = foo).bar) == null ? void 0 : _foo$bar2.call(_foo, foo.bar, false); + (_foo$bar2 = foo.bar) == null ? void 0 : _foo$bar2.call(foo, foo.bar, false); - foo == null ? void 0 : (_foo$bar3 = (_foo2 = foo).bar) == null ? void 0 : _foo$bar3.call(_foo2, foo.bar, true); + foo == null ? void 0 : (_foo$bar3 = foo.bar) == null ? void 0 : _foo$bar3.call(foo, foo.bar, true); (_foo$bar4 = foo.bar) == null ? void 0 : _foo$bar4.baz(foo.bar, false); foo == null ? void 0 : (_foo$bar5 = foo.bar) == null ? void 0 : _foo$bar5.baz(foo.bar, true); - (_foo$bar6 = foo.bar) == null ? void 0 : (_foo$bar6$baz = (_foo$bar7 = _foo$bar6).baz) == null ? void 0 : _foo$bar6$baz.call(_foo$bar7, foo.bar, false); + (_foo$bar6 = foo.bar) == null ? void 0 : (_foo$bar6$baz = _foo$bar6.baz) == null ? void 0 : _foo$bar6$baz.call(_foo$bar6, foo.bar, false); - foo == null ? void 0 : (_foo$bar8 = foo.bar) == null ? void 0 : (_foo$bar8$baz = (_foo$bar9 = _foo$bar8).baz) == null ? void 0 : _foo$bar8$baz.call(_foo$bar9, foo.bar, true); + foo == null ? void 0 : (_foo$bar7 = foo.bar) == null ? void 0 : (_foo$bar7$baz = _foo$bar7.baz) == null ? void 0 : _foo$bar7$baz.call(_foo$bar7, foo.bar, true); } \ No newline at end of file From b048bff77d8d2289f11e18af29a87874e3b199ff Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 4 Jun 2017 00:20:21 -0400 Subject: [PATCH 18/33] Add optional to MemberExpression --- packages/babel-types/src/definitions/core.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index 02572c87b5..8cfaa756b0 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -416,7 +416,7 @@ defineType("LogicalExpression", { }); defineType("MemberExpression", { - builder: ["object", "property", "computed"], + builder: ["object", "property", "computed", "optional"], visitor: ["object", "property"], aliases: ["Expression", "LVal"], fields: { @@ -437,6 +437,10 @@ defineType("MemberExpression", { computed: { default: false, }, + optional: { + validate: assertOneOf(true, false), + optional: true, + } }, }); From 899634d20b14ea39d3087baa39bd476bc071d117 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 4 Jun 2017 01:12:42 -0400 Subject: [PATCH 19/33] Add exec tests --- .../src/index.js | 3 + .../test/fixtures/execute/assignment-left.js | 14 ++++ .../test/fixtures/execute/assignment-right.js | 26 ++++++++ .../test/fixtures/execute/call.js | 61 +++++++++++++++++ .../test/fixtures/execute/delete.js | 20 ++++++ .../test/fixtures/execute/new.js | 65 +++++++++++++++++++ .../test/fixtures/general/delete/expected.js | 4 +- .../test/fixtures/{general => }/options.json | 0 8 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js rename packages/babel-plugin-transform-optional-chaining/test/fixtures/{general => }/options.json (100%) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 826a5b8a4c..ff14c27467 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -97,6 +97,9 @@ export default function ({ types: t }) { if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) { return false; } + if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete"})) { + return false; + } return true; }); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js new file mode 100644 index 0000000000..631cb72fcd --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js @@ -0,0 +1,14 @@ +const obj = { + a: { + b: 0, + }, +}; + +obj?.a.b = 1; +assert.equal(obj.a.b, 1); + +obj?.a?.b = 2; +assert.equal(obj.a.b, 2); + +obj?.b?.b = 3; +assert.equal(obj.b, undefined); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js new file mode 100644 index 0000000000..d2299449ff --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js @@ -0,0 +1,26 @@ +const obj = { + a: { + b: { + c: { + d: 2, + }, + }, + }, +}; + +const a = obj?.a; +assert.equal(a, obj.a); + +const b = obj?.a?.b; +assert.equal(b, obj.a.b); + +const bad = obj?.b?.b; +assert.equal(bad, undefined); + +let val; +val = obj?.a?.b; +assert.equal(val, obj.a.b); + +assert.throws(() => { + const bad = obj?.b.b; +}); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js new file mode 100644 index 0000000000..846b15d7dc --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js @@ -0,0 +1,61 @@ +let calls = 0; +const obj = { + a: { + b(val) { + assert.equal(val, 1); + assert.equal(this, obj.a); + return calls++; + }, + }, + c(val) { + assert.equal(val, 1); + assert.equal(this, obj); + return calls++; + }, +}; + +let ab = obj?.a?.b(1); +assert.equal(ab, 0); + +ab = obj?.a.b(1); +assert.equal(ab, 1); + +ab = obj?.a?.b?.(1); +assert.equal(ab, 2); + +ab = obj?.a.b?.(1); +assert.equal(ab, 3); + +ab = obj?.b?.b(1); +assert.equal(ab, undefined); + +ab = obj?.b?.b?.(1); +assert.equal(ab, undefined); + +let c = obj?.c(1); +assert.equal(c, 4); + +c = obj?.c?.(1); +assert.equal(c, 5); + +c = obj?.d?.(1); +assert.equal(c, undefined); + +obj?.a.b(1); +assert.equal(calls, 7); + +obj?.a?.b(1); +assert.equal(calls, 8); + +obj?.a?.b?.(1); +assert.equal(calls, 9); + +obj?.a.b?.(1); +assert.equal(calls, 10); + +obj?.c?.(1); +assert.equal(calls, 11); + +obj?.b?.b(1); +obj?.b?.b?.(1); +obj?.d?.(1); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js new file mode 100644 index 0000000000..cbb890e5fe --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js @@ -0,0 +1,20 @@ +const obj = { + a: { + b: 0, + }, +}; + +let test = delete obj?.a?.b; +assert.equal(obj.a.b, undefined); +assert.equal(test, true); + +test = delete obj?.a.b; +assert.equal(obj.a.b, undefined); +assert.equal(test, true); + +test = delete obj?.b?.b; +assert.equal(obj.b, undefined); +assert.equal(test, undefined); + +delete obj?.a; +assert.equal(obj.a, undefined); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js new file mode 100644 index 0000000000..bef413aa6e --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js @@ -0,0 +1,65 @@ +let calls = 0; +const obj = { + a: { + b: class { + constructor(val) { + assert.equal(val, 1); + assert(this instanceof obj.a.b); + calls++; + } + }, + }, + c: class { + constructor(val) { + assert.equal(val, 1); + assert(this instanceof obj.c); + calls++; + } + }, +}; + +let ab = new obj?.a?.b(1); +assert(ab instanceof obj.a.b); + +ab = new obj?.a.b(1); +assert(ab instanceof obj.a.b); + +ab = new obj?.a?.b?.(1); +assert(ab instanceof obj.a.b); + +ab = new obj?.a.b?.(1); +assert(ab instanceof obj.a.b); + +ab = new obj?.b?.b(1); +assert.equal(ab, undefined); + +ab = new obj?.b?.b?.(1); +assert.equal(ab, undefined); + +let c = new obj?.c(1); +assert(c instanceof obj.c); + +c = new obj?.c?.(1); +assert(c instanceof obj.c); + +c = new obj?.d?.(1); +assert.equal(c, undefined); + +new obj?.a.b(1); +assert.equal(calls, 7); + +new obj?.a?.b(1); +assert.equal(calls, 8); + +new obj?.a?.b?.(1); +assert.equal(calls, 9); + +new obj?.a.b?.(1); +assert.equal(calls, 10); + +new obj?.c?.(1); +assert.equal(calls, 11); + +new obj?.b?.b(1); +new obj?.b?.b?.(1); +new obj?.d?.(1); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js index d1c9cb8617..a005adc52f 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/delete/expected.js @@ -1,5 +1,5 @@ var _a, _a2, _a2$b, _a2$b$c; -delete ((_a = a) == null ? void 0 : _a.b); +(_a = a) == null ? void 0 : delete _a.b; -delete ((_a2 = a) == null ? void 0 : (_a2$b = _a2.b) == null ? void 0 : (_a2$b$c = _a2$b.c) == null ? void 0 : _a2$b$c.d); \ No newline at end of file +(_a2 = a) == null ? void 0 : (_a2$b = _a2.b) == null ? void 0 : (_a2$b$c = _a2$b.c) == null ? void 0 : delete _a2$b$c.d; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/options.json b/packages/babel-plugin-transform-optional-chaining/test/fixtures/options.json similarity index 100% rename from packages/babel-plugin-transform-optional-chaining/test/fixtures/general/options.json rename to packages/babel-plugin-transform-optional-chaining/test/fixtures/options.json From 0e5f597ee60292e9eef0c8817fb3954061032742 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 4 Jun 2017 01:28:01 -0400 Subject: [PATCH 20/33] Readme --- .../README.md | 113 ++++++++++++++++++ .../package.json | 2 +- 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-optional-chaining/README.md b/packages/babel-plugin-transform-optional-chaining/README.md index 41fdea4a70..5c03a95117 100644 --- a/packages/babel-plugin-transform-optional-chaining/README.md +++ b/packages/babel-plugin-transform-optional-chaining/README.md @@ -1 +1,114 @@ # babel-plugin-transform-optional-chaining + +The Optional Chaining Operator allows you to handle properties of deeply nested +objects without worrying about undefined intermediate objects. + +## Example + +### Accessing deeply nested properties + +```js +const obj = { + foo: { + bar: { + baz: 42, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz; // 42 + +const safe = obj?.qux?.baz; // undefined + +// Optional chaining and normal chaining can be intermixed +obj?.foo.bar?.baz; // Only access `foo` if `obj` exists, and `baz` if + // `bar` exists +``` + +### Calling deeply nested functions + +```js +const obj = { + foo: { + bar: { + baz() { + return 42; + }, + }, + }, +}; + +const baz = obj?.foo?.bar?.baz(); // 42 + +const safe = obj?.qux?.baz(); // undefined +const safe2 = obj?.foo.bar.qux?.(); // undefined + +// Top function can be called directly, too. +function test() { + return 42; +} +test?.(); // 42 + +exists?.(); // undefined +``` + +### Constructing deeply nested classes + +```js +const obj = { + foo: { + bar: { + baz: class { + }, + }, + }, +}; + +const baz = new obj?.foo?.bar?.baz(); // baz instance + +const safe = new obj?.qux?.baz(); // undefined +const safe2 = new obj?.foo.bar.qux?.(); // undefined + +// Top classes can be called directly, too. +class Test { +} +new Test?.(); // test instance + +new exists?.(); // undefined +``` + +## Installation + +```sh +npm install --save-dev babel-plugin-syntax-optional-chaining +``` + +## Usage + +### Via `.babelrc` (Recommended) + +**.babelrc** + +```json +{ + "plugins": ["syntax-optional-chaining"] +} +``` + +### Via CLI + +```sh +babel --plugins syntax-optional-chaining script.js +``` + +### Via Node API + +```javascript +require("babel-core").transform("code", { + plugins: ["syntax-optional-chaining"] +}); +``` + +## References + +* [Proposal: Optional Chaining](https://github.com/tc39/proposal-optional-chaining) diff --git a/packages/babel-plugin-transform-optional-chaining/package.json b/packages/babel-plugin-transform-optional-chaining/package.json index 04292fc777..ef10fd42ff 100644 --- a/packages/babel-plugin-transform-optional-chaining/package.json +++ b/packages/babel-plugin-transform-optional-chaining/package.json @@ -1,7 +1,7 @@ { "name": "babel-plugin-transform-optional-chaining", "version": "7.0.0-alpha.7", - "description": "", + "description": "Transform optional chaining operators into a series of nil checks", "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-optional-chaining", "license": "MIT", "main": "lib/index.js", From 1f22ac353aafb337b5480b6f4bc7b0564c8ea38f Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 4 Jun 2017 01:34:52 -0400 Subject: [PATCH 21/33] Lint --- .../babel-plugin-transform-optional-chaining/src/index.js | 6 +++--- packages/babel-types/src/definitions/core.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index ff14c27467..1687a945ca 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -46,7 +46,7 @@ export default function ({ types: t }) { if (loose) { // To avoid a Function#call, we can instead re-grab the property from the context object. // `a.?b.?()` translates roughly to `_a.b != null && _a.b()` - node.callee = chain + node.callee = chain; } else { // Otherwise, we need to memoize the context object, and change the call into a Function#call. // `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)` @@ -82,7 +82,7 @@ export default function ({ types: t }) { return; } - const replace = path.find(path => { + const replace = path.find((path) => { const { parentPath } = path; if (parentPath.isStatement()) { return true; @@ -97,7 +97,7 @@ export default function ({ types: t }) { if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) { return false; } - if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete"})) { + if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete" })) { return false; } return true; diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index 8cfaa756b0..8cbecc3d6e 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -440,7 +440,7 @@ defineType("MemberExpression", { optional: { validate: assertOneOf(true, false), optional: true, - } + }, }, }); From ef87acc389efbe8a7205032b05de11facd56f293 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 4 Jun 2017 01:46:14 -0400 Subject: [PATCH 22/33] Test not-top-level optional chaining --- .../test/fixtures/general/member-access/actual.js | 4 ++++ .../test/fixtures/general/member-access/expected.js | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js index d6a9752544..92234aa52b 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js @@ -2,6 +2,10 @@ foo?.bar a?.b.c?.d.e +a.b?.c.d?.e + +a.b.c?.d?.e + orders?.[0].price orders?.[0]?.price diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index ffc9f8bc4e..8dd41a0a23 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1,9 +1,13 @@ -var _foo, _a, _a$b$c, _orders, _orders2, _orders2$, _client, _orders$client$key; +var _foo, _a, _a$b$c, _a$b, _a$b$c$d, _a$b$c2, _a$b$c2$d, _orders, _orders2, _orders2$, _client, _orders$client$key; (_foo = foo) == null ? void 0 : _foo.bar; (_a = a) == null ? void 0 : (_a$b$c = _a.b.c) == null ? void 0 : _a$b$c.d.e; +(_a$b = a.b) == null ? void 0 : (_a$b$c$d = _a$b.c.d) == null ? void 0 : _a$b$c$d.e; + +(_a$b$c2 = a.b.c) == null ? void 0 : (_a$b$c2$d = _a$b$c2.d) == null ? void 0 : _a$b$c2$d.e; + (_orders = orders) == null ? void 0 : _orders[0].price; (_orders2 = orders) == null ? void 0 : (_orders2$ = _orders2[0]) == null ? void 0 : _orders2$.price; From a62cb9281e0cb1c04e98ffc1a0e2a95a8a03a7b4 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 4 Jun 2017 01:49:07 -0400 Subject: [PATCH 23/33] Delete unnecessary check --- .../babel-plugin-transform-optional-chaining/src/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 1687a945ca..99fb80b695 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -84,9 +84,6 @@ export default function ({ types: t }) { const replace = path.find((path) => { const { parentPath } = path; - if (parentPath.isStatement()) { - return true; - } if (path.key == "left" && parentPath.isAssignmentExpression()) { return false; @@ -100,6 +97,7 @@ export default function ({ types: t }) { if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete" })) { return false; } + return true; }); From 6cc2f5dc780e908eb27282c63436b9aafef0074f Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 4 Jun 2017 12:57:50 -0400 Subject: [PATCH 24/33] Consider any unary expression --- packages/babel-plugin-transform-optional-chaining/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 99fb80b695..4272827238 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -94,7 +94,7 @@ export default function ({ types: t }) { if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) { return false; } - if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete" })) { + if (path.key == "argument" && parentPath.isUnaryExpression()) { return false; } From faa6c9f7083a3f46cb7e82da9b339c0525e03317 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 5 Jun 2017 13:56:28 -0400 Subject: [PATCH 25/33] Use nil --- packages/babel-plugin-transform-optional-chaining/src/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 4272827238..f32eb713ec 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -4,6 +4,7 @@ export default function ({ types: t }) { function optional(path, key, replacementPath, needsContext = false, loose = false) { const { scope } = path; const optionals = [path.node]; + const nil = scope.buildUndefinedNode(); let objectPath = path.get(key); while (objectPath.isMemberExpression()) { @@ -65,7 +66,7 @@ export default function ({ types: t }) { replacementPath.replaceWith(t.conditionalExpression( t.binaryExpression("==", check, t.nullLiteral()), - scope.buildUndefinedNode(), + nil, replacementPath.node )); From d92309f0dbd16c6baa3d1178ab8f2f0f4839f224 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 5 Jun 2017 22:53:24 -0400 Subject: [PATCH 26/33] PR comments --- packages/babel-generator/src/generators/expressions.js | 10 +++++++++- .../babel-plugin-transform-optional-chaining/README.md | 4 ++++ .../src/index.js | 5 +---- packages/babel-types/src/definitions/core.js | 8 ++++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/babel-generator/src/generators/expressions.js b/packages/babel-generator/src/generators/expressions.js index 68afd50ea7..0f1a9d1651 100644 --- a/packages/babel-generator/src/generators/expressions.js +++ b/packages/babel-generator/src/generators/expressions.js @@ -51,11 +51,16 @@ export function NewExpression(node: Object, parent: Object) { this.word("new"); this.space(); this.print(node.callee, node); - if (node.arguments.length === 0 && this.format.minified && + if (this.format.minified && + node.arguments.length === 0 && + !node.optional && !t.isCallExpression(parent, { callee: node }) && !t.isMemberExpression(parent) && !t.isNewExpression(parent)) return; + if (node.optional) { + this.token("?."); + } this.token("("); this.printList(node.arguments, node); this.token(")"); @@ -89,6 +94,9 @@ function commaSeparatorNewline() { export function CallExpression(node: Object) { this.print(node.callee, node); + if (node.optional) { + this.token("?."); + } this.token("("); const isPrettyCall = node._prettyCall; diff --git a/packages/babel-plugin-transform-optional-chaining/README.md b/packages/babel-plugin-transform-optional-chaining/README.md index 5c03a95117..849b6d98a4 100644 --- a/packages/babel-plugin-transform-optional-chaining/README.md +++ b/packages/babel-plugin-transform-optional-chaining/README.md @@ -43,6 +43,8 @@ const baz = obj?.foo?.bar?.baz(); // 42 const safe = obj?.qux?.baz(); // undefined const safe2 = obj?.foo.bar.qux?.(); // undefined +const willThrow = obj?.foo.bar.qux(); // Error: not a function + // Top function can be called directly, too. function test() { return 42; @@ -69,6 +71,8 @@ const baz = new obj?.foo?.bar?.baz(); // baz instance const safe = new obj?.qux?.baz(); // undefined const safe2 = new obj?.foo.bar.qux?.(); // undefined +const willThrow = new obj?.foo.bar.qux(); // Error: not a constructor + // Top classes can be called directly, too. class Test { } diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index f32eb713ec..d48492ff9e 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -6,14 +6,11 @@ export default function ({ types: t }) { const optionals = [path.node]; const nil = scope.buildUndefinedNode(); - let objectPath = path.get(key); - while (objectPath.isMemberExpression()) { + for (let objectPath = path.get(key); objectPath.isMemberExpression(); objectPath = objectPath.get("object")) { const { node } = objectPath; if (node.optional) { optionals.push(node); } - - objectPath = objectPath.get("object"); } for (let i = optionals.length - 1; i >= 0; i--) { diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index 8cbecc3d6e..d72feba5e3 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -119,6 +119,10 @@ defineType("CallExpression", { arguments: { validate: chain(assertValueType("array"), assertEach(assertNodeType("Expression", "SpreadElement"))), }, + optional: { + validate: assertOneOf(true, false), + optional: true, + }, }, aliases: ["Expression"], }); @@ -454,6 +458,10 @@ defineType("NewExpression", { arguments: { validate: chain(assertValueType("array"), assertEach(assertNodeType("Expression", "SpreadElement"))), }, + optional: { + validate: assertOneOf(true, false), + optional: true, + }, }, }); From 54d9732d0b74bd242a0872f6d48bbc9025059ded Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 5 Jun 2017 23:48:20 -0400 Subject: [PATCH 27/33] Test Update and Unary expressions --- .../src/index.js | 5 ++++- .../test/fixtures/execute/unary.js | 17 +++++++++++++++++ .../test/fixtures/execute/update.js | 14 ++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js create mode 100644 packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index d48492ff9e..3e93ea52a2 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -92,7 +92,10 @@ export default function ({ types: t }) { if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) { return false; } - if (path.key == "argument" && parentPath.isUnaryExpression()) { + if (path.key == "argument" && parentPath.isUpdateExpression()) { + return false; + } + if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete" })) { return false; } diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js new file mode 100644 index 0000000000..8d71fe89c7 --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js @@ -0,0 +1,17 @@ +const obj = { + a: { + b: 0, + }, +}; + +let test = +obj?.a?.b; +assert.equal(test, 0); + +test = +obj?.a.b; +assert.equal(test, 0); + +test = +obj?.b?.b; +assert.isNaN(test); + +test = +obj?.b?.b; +assert.isNaN(test); diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js new file mode 100644 index 0000000000..06d1024e4d --- /dev/null +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js @@ -0,0 +1,14 @@ +const obj = { + a: { + b: 0, + }, +}; + +obj?.a.b++; +assert.equal(obj.a.b, 1); + +obj?.a?.b++; +assert.equal(obj.a.b, 2); + +obj?.b?.b++; +assert.equal(obj.b, undefined); From 9e91ac54d3718a1e70b469ac8be3ff5daf438ee5 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Wed, 7 Jun 2017 01:58:31 -0400 Subject: [PATCH 28/33] Optional call expressions short circuit later member expressions --- .../src/index.js | 50 ++++++++++--------- .../fixtures/general/function-call/actual.js | 12 +++++ .../general/function-call/expected.js | 16 +++++- .../fixtures/general/member-access/actual.js | 20 +++++--- .../general/member-access/expected.js | 8 ++- .../test/fixtures/general/new/actual.js | 8 +++ .../test/fixtures/general/new/expected.js | 12 ++++- 7 files changed, 88 insertions(+), 38 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 3e93ea52a2..86b6b0d7ca 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -71,6 +71,30 @@ export default function ({ types: t }) { } } + function findReplacementPath(path) { + return path.find((path) => { + const { parentPath } = path; + + if (path.key == "left" && parentPath.isAssignmentExpression()) { + return false; + } + if (path.key == "object" && parentPath.isMemberExpression()) { + return false; + } + if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) { + return false; + } + if (path.key == "argument" && parentPath.isUpdateExpression()) { + return false; + } + if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete" })) { + return false; + } + + return true; + }); + } + return { inherits: syntaxOptionalChaining, @@ -80,29 +104,7 @@ export default function ({ types: t }) { return; } - const replace = path.find((path) => { - const { parentPath } = path; - - if (path.key == "left" && parentPath.isAssignmentExpression()) { - return false; - } - if (path.key == "object" && parentPath.isMemberExpression()) { - return false; - } - if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) { - return false; - } - if (path.key == "argument" && parentPath.isUpdateExpression()) { - return false; - } - if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete" })) { - return false; - } - - return true; - }); - - optional(path, "object", replace); + optional(path, "object", findReplacementPath(path)); }, "NewExpression|CallExpression"(path) { @@ -110,7 +112,7 @@ export default function ({ types: t }) { return; } - optional(path, "callee", path, path.isCallExpression(), this.opts.loose); + optional(path, "callee", findReplacementPath(path), path.isCallExpression(), this.opts.loose); }, }, }; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js index 9499c5f20e..51882eacf6 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/actual.js @@ -5,3 +5,15 @@ foo?.bar() foo.bar?.(foo.bar, false) foo?.bar?.(foo.bar, true) + +foo?.().bar + +foo?.()?.bar + +foo.bar?.().baz + +foo.bar?.()?.baz + +foo?.bar?.().baz + +foo?.bar?.()?.baz diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js index ca85c85dac..dcf4091343 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -1,4 +1,4 @@ -var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar; +var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5, _foo6, _foo7, _foo$bar2, _foo8, _foo$bar3, _foo$bar4, _foo9, _foo10, _foo10$bar, _foo$bar5, _foo11, _foo11$bar; (_foo = foo) == null ? void 0 : _foo(foo); @@ -6,4 +6,16 @@ var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar; (_foo$bar = (_foo3 = foo).bar) == null ? void 0 : _foo$bar.call(_foo3, foo.bar, false); -(_foo4 = foo) == null ? void 0 : (_foo4$bar = _foo4.bar) == null ? void 0 : _foo4$bar.call(_foo4, foo.bar, true); \ No newline at end of file +(_foo4 = foo) == null ? void 0 : (_foo4$bar = _foo4.bar) == null ? void 0 : _foo4$bar.call(_foo4, foo.bar, true); + +(_foo5 = foo) == null ? void 0 : _foo5().bar; + +(_foo6 = (_foo7 = foo) == null ? void 0 : _foo7()) == null ? void 0 : _foo6.bar; + +(_foo$bar2 = (_foo8 = foo).bar) == null ? void 0 : _foo$bar2.call(_foo8).baz; + +(_foo$bar3 = (_foo$bar4 = (_foo9 = foo).bar) == null ? void 0 : _foo$bar4.call(_foo9)) == null ? void 0 : _foo$bar3.baz; + +(_foo10 = foo) == null ? void 0 : (_foo10$bar = _foo10.bar) == null ? void 0 : _foo10$bar.call(_foo10).baz; + +(_foo$bar5 = (_foo11 = foo) == null ? void 0 : (_foo11$bar = _foo11.bar) == null ? void 0 : _foo11$bar.call(_foo11)) == null ? void 0 : _foo$bar5.baz; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js index 92234aa52b..23c228afe6 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/actual.js @@ -1,15 +1,19 @@ -foo?.bar +foo?.bar; -a?.b.c?.d.e +a?.b.c?.d.e; -a.b?.c.d?.e +a.b?.c.d?.e; -a.b.c?.d?.e +a.b.c?.d?.e; -orders?.[0].price +orders?.[0].price; -orders?.[0]?.price +orders?.[0]?.price; -orders[client?.key].price +orders[client?.key].price; -orders[client.key]?.price +orders[client.key]?.price; + +(0, a?.b).c; + +(0, (0, a?.b).c?.d).e; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js index 8dd41a0a23..1e82625c57 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/member-access/expected.js @@ -1,4 +1,4 @@ -var _foo, _a, _a$b$c, _a$b, _a$b$c$d, _a$b$c2, _a$b$c2$d, _orders, _orders2, _orders2$, _client, _orders$client$key; +var _foo, _a, _a$b$c, _a$b, _a$b$c$d, _a$b$c2, _a$b$c2$d, _orders, _orders2, _orders2$, _client, _orders$client$key, _a2, _c, _a3; (_foo = foo) == null ? void 0 : _foo.bar; @@ -14,4 +14,8 @@ var _foo, _a, _a$b$c, _a$b, _a$b$c$d, _a$b$c2, _a$b$c2$d, _orders, _orders2, _or orders[(_client = client) == null ? void 0 : _client.key].price; -(_orders$client$key = orders[client.key]) == null ? void 0 : _orders$client$key.price; \ No newline at end of file +(_orders$client$key = orders[client.key]) == null ? void 0 : _orders$client$key.price; + +(0, (_a2 = a) == null ? void 0 : _a2.b).c; + +(0, (_c = (0, (_a3 = a) == null ? void 0 : _a3.b).c) == null ? void 0 : _c.d).e; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js index b937f96491..a42e650ea4 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/actual.js @@ -6,3 +6,11 @@ new a?.b?.c?.d() new b?.(b) new a?.b?.(a.b, true) + +new b?.().c +new b?.()?.c + +new a.b?.().c +new a.b?.()?.c +new a?.b?.().c +new a?.b?.()?.c diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js index 241badcfdd..865fc96d63 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js @@ -1,4 +1,4 @@ -var _a, _a2, _a2$b, _a2$b$c, _a3, _a4, _a4$b, _a4$b$c, _b, _a5, _a5$b; +var _a, _a2, _a2$b, _a2$b$c, _a3, _a4, _a4$b, _a4$b$c, _b, _a5, _a5$b, _b2, _ref, _b3, _a$b, _ref2, _a$b2, _a6, _a6$b, _ref3, _a7, _a7$b; (_a = a) == null ? void 0 : new _a.b(); (_a2 = a) == null ? void 0 : (_a2$b = _a2.b) == null ? void 0 : (_a2$b$c = _a2$b.c) == null ? void 0 : new _a2$b$c.d(); @@ -7,4 +7,12 @@ var _a, _a2, _a2$b, _a2$b$c, _a3, _a4, _a4$b, _a4$b$c, _b, _a5, _a5$b; (_a4 = a) == null ? void 0 : (_a4$b = _a4.b) == null ? void 0 : (_a4$b$c = _a4$b.c) == null ? void 0 : new _a4$b$c.d(); (_b = b) == null ? void 0 : new _b(b); -(_a5 = a) == null ? void 0 : (_a5$b = _a5.b) == null ? void 0 : new _a5$b(a.b, true); \ No newline at end of file +(_a5 = a) == null ? void 0 : (_a5$b = _a5.b) == null ? void 0 : new _a5$b(a.b, true); + +(_b2 = b) == null ? void 0 : new _b2().c; +(_ref = (_b3 = b) == null ? void 0 : new _b3()) == null ? void 0 : _ref.c; + +(_a$b = a.b) == null ? void 0 : new _a$b().c; +(_ref2 = (_a$b2 = a.b) == null ? void 0 : new _a$b2()) == null ? void 0 : _ref2.c; +(_a6 = a) == null ? void 0 : (_a6$b = _a6.b) == null ? void 0 : new _a6$b().c; +(_ref3 = (_a7 = a) == null ? void 0 : (_a7$b = _a7.b) == null ? void 0 : new _a7$b()) == null ? void 0 : _ref3.c; \ No newline at end of file From 462825b15af0ec844df28cc77b1598591e825661 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Wed, 7 Jun 2017 02:56:20 -0400 Subject: [PATCH 29/33] Simplify transform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also makes the output use less comparisons for `foo?.()?.bar` cases. 😁 --- .../src/index.js | 34 +++++++++---------- .../general/function-call/expected.js | 8 ++--- .../test/fixtures/general/new/expected.js | 8 ++--- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/packages/babel-plugin-transform-optional-chaining/src/index.js b/packages/babel-plugin-transform-optional-chaining/src/index.js index 86b6b0d7ca..a3db104e61 100644 --- a/packages/babel-plugin-transform-optional-chaining/src/index.js +++ b/packages/babel-plugin-transform-optional-chaining/src/index.js @@ -1,30 +1,36 @@ import syntaxOptionalChaining from "babel-plugin-syntax-optional-chaining"; export default function ({ types: t }) { - function optional(path, key, replacementPath, needsContext = false, loose = false) { + function optional(path, replacementPath, loose = false) { const { scope } = path; - const optionals = [path.node]; + const optionals = []; const nil = scope.buildUndefinedNode(); - for (let objectPath = path.get(key); objectPath.isMemberExpression(); objectPath = objectPath.get("object")) { + let objectPath = path; + while (objectPath.isMemberExpression() || objectPath.isCallExpression() || objectPath.isNewExpression()) { const { node } = objectPath; if (node.optional) { optionals.push(node); } + + if (objectPath.isMemberExpression()) { + objectPath = objectPath.get("object"); + } else { + objectPath = objectPath.get("callee"); + } } for (let i = optionals.length - 1; i >= 0; i--) { const node = optionals[i]; node.optional = false; - const replaceKey = i == 0 ? key : "object"; - const atCall = needsContext && i == 0; - + const isCall = t.isCallExpression(node); + const replaceKey = isCall || t.isNewExpression(node) ? "callee" : "object"; const chain = node[replaceKey]; let ref; let check; - if (loose && atCall) { + if (loose && isCall) { // If we are using a loose transform (avoiding a Function#call) and we are at the call, // we can avoid a needless memoize. check = ref = chain; @@ -40,7 +46,7 @@ export default function ({ types: t }) { // Ensure call expressions have the proper `this` // `foo.bar()` has context `foo`. - if (atCall && t.isMemberExpression(chain)) { + if (isCall && t.isMemberExpression(chain)) { if (loose) { // To avoid a Function#call, we can instead re-grab the property from the context object. // `a.?b.?()` translates roughly to `_a.b != null && _a.b()` @@ -99,20 +105,12 @@ export default function ({ types: t }) { inherits: syntaxOptionalChaining, visitor: { - MemberExpression(path) { + "MemberExpression|NewExpression|CallExpression"(path) { if (!path.node.optional) { return; } - optional(path, "object", findReplacementPath(path)); - }, - - "NewExpression|CallExpression"(path) { - if (!path.node.optional) { - return; - } - - optional(path, "callee", findReplacementPath(path), path.isCallExpression(), this.opts.loose); + optional(path, findReplacementPath(path), this.opts.loose); }, }, }; diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js index dcf4091343..297241cb9f 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/function-call/expected.js @@ -1,4 +1,4 @@ -var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5, _foo6, _foo7, _foo$bar2, _foo8, _foo$bar3, _foo$bar4, _foo9, _foo10, _foo10$bar, _foo$bar5, _foo11, _foo11$bar; +var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5, _foo6, _foo7, _foo$bar2, _foo8, _foo$bar3, _foo9, _foo$bar3$call, _foo10, _foo10$bar, _foo11, _foo11$bar, _foo11$bar$call; (_foo = foo) == null ? void 0 : _foo(foo); @@ -10,12 +10,12 @@ var _foo, _foo2, _foo$bar, _foo3, _foo4, _foo4$bar, _foo5, _foo6, _foo7, _foo$ba (_foo5 = foo) == null ? void 0 : _foo5().bar; -(_foo6 = (_foo7 = foo) == null ? void 0 : _foo7()) == null ? void 0 : _foo6.bar; +(_foo6 = foo) == null ? void 0 : (_foo7 = _foo6()) == null ? void 0 : _foo7.bar; (_foo$bar2 = (_foo8 = foo).bar) == null ? void 0 : _foo$bar2.call(_foo8).baz; -(_foo$bar3 = (_foo$bar4 = (_foo9 = foo).bar) == null ? void 0 : _foo$bar4.call(_foo9)) == null ? void 0 : _foo$bar3.baz; +(_foo$bar3 = (_foo9 = foo).bar) == null ? void 0 : (_foo$bar3$call = _foo$bar3.call(_foo9)) == null ? void 0 : _foo$bar3$call.baz; (_foo10 = foo) == null ? void 0 : (_foo10$bar = _foo10.bar) == null ? void 0 : _foo10$bar.call(_foo10).baz; -(_foo$bar5 = (_foo11 = foo) == null ? void 0 : (_foo11$bar = _foo11.bar) == null ? void 0 : _foo11$bar.call(_foo11)) == null ? void 0 : _foo$bar5.baz; \ No newline at end of file +(_foo11 = foo) == null ? void 0 : (_foo11$bar = _foo11.bar) == null ? void 0 : (_foo11$bar$call = _foo11$bar.call(_foo11)) == null ? void 0 : _foo11$bar$call.baz; \ No newline at end of file diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js index 865fc96d63..7c22a8bc0d 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/general/new/expected.js @@ -1,4 +1,4 @@ -var _a, _a2, _a2$b, _a2$b$c, _a3, _a4, _a4$b, _a4$b$c, _b, _a5, _a5$b, _b2, _ref, _b3, _a$b, _ref2, _a$b2, _a6, _a6$b, _ref3, _a7, _a7$b; +var _a, _a2, _a2$b, _a2$b$c, _a3, _a4, _a4$b, _a4$b$c, _b, _a5, _a5$b, _b2, _b3, _ref, _a$b, _a$b2, _ref2, _a6, _a6$b, _a7, _a7$b, _ref3; (_a = a) == null ? void 0 : new _a.b(); (_a2 = a) == null ? void 0 : (_a2$b = _a2.b) == null ? void 0 : (_a2$b$c = _a2$b.c) == null ? void 0 : new _a2$b$c.d(); @@ -10,9 +10,9 @@ var _a, _a2, _a2$b, _a2$b$c, _a3, _a4, _a4$b, _a4$b$c, _b, _a5, _a5$b, _b2, _ref (_a5 = a) == null ? void 0 : (_a5$b = _a5.b) == null ? void 0 : new _a5$b(a.b, true); (_b2 = b) == null ? void 0 : new _b2().c; -(_ref = (_b3 = b) == null ? void 0 : new _b3()) == null ? void 0 : _ref.c; +(_b3 = b) == null ? void 0 : (_ref = new _b3()) == null ? void 0 : _ref.c; (_a$b = a.b) == null ? void 0 : new _a$b().c; -(_ref2 = (_a$b2 = a.b) == null ? void 0 : new _a$b2()) == null ? void 0 : _ref2.c; +(_a$b2 = a.b) == null ? void 0 : (_ref2 = new _a$b2()) == null ? void 0 : _ref2.c; (_a6 = a) == null ? void 0 : (_a6$b = _a6.b) == null ? void 0 : new _a6$b().c; -(_ref3 = (_a7 = a) == null ? void 0 : (_a7$b = _a7.b) == null ? void 0 : new _a7$b()) == null ? void 0 : _ref3.c; \ No newline at end of file +(_a7 = a) == null ? void 0 : (_a7$b = _a7.b) == null ? void 0 : (_ref3 = new _a7$b()) == null ? void 0 : _ref3.c; \ No newline at end of file From f537fc7da7c92ef9afdce0b78e2b180874263e99 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Wed, 7 Jun 2017 03:10:53 -0400 Subject: [PATCH 30/33] Add babel-generator tests --- .../types/Optional-CallExpression/actual.js | 39 +++++++++++++++++++ .../types/Optional-CallExpression/expected.js | 39 +++++++++++++++++++ .../types/Optional-MemberExpression/actual.js | 16 ++++++++ .../Optional-MemberExpression/expected.js | 16 ++++++++ .../types/Optional-NewExpression/actual.js | 39 +++++++++++++++++++ .../types/Optional-NewExpression/expected.js | 39 +++++++++++++++++++ packages/babel-generator/test/index.js | 1 + 7 files changed, 189 insertions(+) create mode 100644 packages/babel-generator/test/fixtures/types/Optional-CallExpression/actual.js create mode 100644 packages/babel-generator/test/fixtures/types/Optional-CallExpression/expected.js create mode 100644 packages/babel-generator/test/fixtures/types/Optional-MemberExpression/actual.js create mode 100644 packages/babel-generator/test/fixtures/types/Optional-MemberExpression/expected.js create mode 100644 packages/babel-generator/test/fixtures/types/Optional-NewExpression/actual.js create mode 100644 packages/babel-generator/test/fixtures/types/Optional-NewExpression/expected.js diff --git a/packages/babel-generator/test/fixtures/types/Optional-CallExpression/actual.js b/packages/babel-generator/test/fixtures/types/Optional-CallExpression/actual.js new file mode 100644 index 0000000000..7bf538b249 --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/Optional-CallExpression/actual.js @@ -0,0 +1,39 @@ +foo?.(); +foo?.("foo"); +foo?.("foo", "bar"); +foo?.(bar()); +foo?.(bar("test")); +foo(bar?.()); +foo(bar?.("test")); + +a.foo?.(); +a.foo?.("foo"); +a.foo?.("foo", "bar"); +a.foo?.(bar()); +a.foo?.(bar("test")); +a.foo(bar?.()); +a.foo(bar?.("test")); + +a?.foo?.(); +a?.foo?.("foo"); +a?.foo?.("foo", "bar"); +a?.foo?.(bar()); +a?.foo?.(bar("test")); +a?.foo(bar?.()); +a?.foo(bar?.("test")); + +a.foo?.().baz; +a.foo?.("foo").baz; +a.foo?.("foo", "bar").baz; +a.foo?.(bar()).baz; +a.foo?.(bar("test")).baz; +a.foo(bar?.()).baz; +a.foo(bar?.("test")).baz; + +a.foo?.()?.baz; +a.foo?.("foo")?.baz; +a.foo?.("foo", "bar")?.baz; +a.foo?.(bar())?.baz; +a.foo?.(bar("test"))?.baz; +a.foo(bar?.())?.baz; +a.foo(bar?.("test"))?.baz; diff --git a/packages/babel-generator/test/fixtures/types/Optional-CallExpression/expected.js b/packages/babel-generator/test/fixtures/types/Optional-CallExpression/expected.js new file mode 100644 index 0000000000..7bf538b249 --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/Optional-CallExpression/expected.js @@ -0,0 +1,39 @@ +foo?.(); +foo?.("foo"); +foo?.("foo", "bar"); +foo?.(bar()); +foo?.(bar("test")); +foo(bar?.()); +foo(bar?.("test")); + +a.foo?.(); +a.foo?.("foo"); +a.foo?.("foo", "bar"); +a.foo?.(bar()); +a.foo?.(bar("test")); +a.foo(bar?.()); +a.foo(bar?.("test")); + +a?.foo?.(); +a?.foo?.("foo"); +a?.foo?.("foo", "bar"); +a?.foo?.(bar()); +a?.foo?.(bar("test")); +a?.foo(bar?.()); +a?.foo(bar?.("test")); + +a.foo?.().baz; +a.foo?.("foo").baz; +a.foo?.("foo", "bar").baz; +a.foo?.(bar()).baz; +a.foo?.(bar("test")).baz; +a.foo(bar?.()).baz; +a.foo(bar?.("test")).baz; + +a.foo?.()?.baz; +a.foo?.("foo")?.baz; +a.foo?.("foo", "bar")?.baz; +a.foo?.(bar())?.baz; +a.foo?.(bar("test"))?.baz; +a.foo(bar?.())?.baz; +a.foo(bar?.("test"))?.baz; diff --git a/packages/babel-generator/test/fixtures/types/Optional-MemberExpression/actual.js b/packages/babel-generator/test/fixtures/types/Optional-MemberExpression/actual.js new file mode 100644 index 0000000000..1c60f76870 --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/Optional-MemberExpression/actual.js @@ -0,0 +1,16 @@ +foo?.["bar"]; +foo?.bar; + +foo.bar?.foo; +foo?.bar.foo; +foo?.bar?.foo; +foo.bar?.["foo"]; +foo?.bar["foo"]; +foo?.bar?.["foo"]; +foo["bar"]?.foo; +foo?.["bar"].foo; +foo?.["bar"]?.foo; + +0.?.toString(); +0.5?.toString(); +1.000?.toString(); diff --git a/packages/babel-generator/test/fixtures/types/Optional-MemberExpression/expected.js b/packages/babel-generator/test/fixtures/types/Optional-MemberExpression/expected.js new file mode 100644 index 0000000000..1c60f76870 --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/Optional-MemberExpression/expected.js @@ -0,0 +1,16 @@ +foo?.["bar"]; +foo?.bar; + +foo.bar?.foo; +foo?.bar.foo; +foo?.bar?.foo; +foo.bar?.["foo"]; +foo?.bar["foo"]; +foo?.bar?.["foo"]; +foo["bar"]?.foo; +foo?.["bar"].foo; +foo?.["bar"]?.foo; + +0.?.toString(); +0.5?.toString(); +1.000?.toString(); diff --git a/packages/babel-generator/test/fixtures/types/Optional-NewExpression/actual.js b/packages/babel-generator/test/fixtures/types/Optional-NewExpression/actual.js new file mode 100644 index 0000000000..fe994af68f --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/Optional-NewExpression/actual.js @@ -0,0 +1,39 @@ +new foo?.(); +new foo?.("foo"); +new foo?.("foo", "bar"); +new foo?.(bar()); +new foo?.(bar("test")); +foo(new bar?.()); +foo(new bar?.("test")); + +new a.foo?.(); +new a.foo?.("foo"); +new a.foo?.("foo", "bar"); +new a.foo?.(bar()); +new a.foo?.(bar("test")); +a.foo(new bar?.()); +a.foo(new bar?.("test")); + +new a?.foo?.(); +new a?.foo?.("foo"); +new a?.foo?.("foo", "bar"); +new a?.foo?.(bar()); +new a?.foo?.(bar("test")); +a?.foo(new bar?.()); +a?.foo(new bar?.("test")); + +new a.foo?.().baz; +new a.foo?.("foo").baz; +new a.foo?.("foo", "bar").baz; +new a.foo?.(bar()).baz; +new a.foo?.(bar("test")).baz; +a.foo(new bar?.()).baz; +a.foo(new bar?.("test")).baz; + +new a.foo?.()?.baz; +new a.foo?.("foo")?.baz; +new a.foo?.("foo", "bar")?.baz; +new a.foo?.(bar())?.baz; +new a.foo?.(bar("test"))?.baz; +a.foo(new bar?.())?.baz; +a.foo(new bar?.("test"))?.baz; diff --git a/packages/babel-generator/test/fixtures/types/Optional-NewExpression/expected.js b/packages/babel-generator/test/fixtures/types/Optional-NewExpression/expected.js new file mode 100644 index 0000000000..fe994af68f --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/Optional-NewExpression/expected.js @@ -0,0 +1,39 @@ +new foo?.(); +new foo?.("foo"); +new foo?.("foo", "bar"); +new foo?.(bar()); +new foo?.(bar("test")); +foo(new bar?.()); +foo(new bar?.("test")); + +new a.foo?.(); +new a.foo?.("foo"); +new a.foo?.("foo", "bar"); +new a.foo?.(bar()); +new a.foo?.(bar("test")); +a.foo(new bar?.()); +a.foo(new bar?.("test")); + +new a?.foo?.(); +new a?.foo?.("foo"); +new a?.foo?.("foo", "bar"); +new a?.foo?.(bar()); +new a?.foo?.(bar("test")); +a?.foo(new bar?.()); +a?.foo(new bar?.("test")); + +new a.foo?.().baz; +new a.foo?.("foo").baz; +new a.foo?.("foo", "bar").baz; +new a.foo?.(bar()).baz; +new a.foo?.(bar("test")).baz; +a.foo(new bar?.()).baz; +a.foo(new bar?.("test")).baz; + +new a.foo?.()?.baz; +new a.foo?.("foo")?.baz; +new a.foo?.("foo", "bar")?.baz; +new a.foo?.(bar())?.baz; +new a.foo?.(bar("test"))?.baz; +a.foo(new bar?.())?.baz; +a.foo(new bar?.("test"))?.baz; diff --git a/packages/babel-generator/test/index.js b/packages/babel-generator/test/index.js index c0e0ec4584..54c107bcd5 100644 --- a/packages/babel-generator/test/index.js +++ b/packages/babel-generator/test/index.js @@ -318,6 +318,7 @@ suites.forEach(function (testSuite) { "functionSent", "jsx", "objectRestSpread", + "optionalChaining", ], strictMode: false, sourceType: "module", From 97d0ab78cb949ad5d9e26874f11f04d73b7ecd99 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Wed, 7 Jun 2017 03:52:42 -0400 Subject: [PATCH 31/33] Update babylon --- packages/babel-core/package.json | 2 +- packages/babel-generator/package.json | 2 +- packages/babel-plugin-syntax-optional-chaining/package.json | 2 +- .../babel-plugin-transform-optional-chaining/package.json | 6 +++--- packages/babel-template/package.json | 2 +- packages/babel-traverse/package.json | 2 +- packages/babel-types/package.json | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/babel-core/package.json b/packages/babel-core/package.json index 02a94c552f..785dd3a65b 100644 --- a/packages/babel-core/package.json +++ b/packages/babel-core/package.json @@ -32,7 +32,7 @@ "babel-template": "7.0.0-alpha.12", "babel-traverse": "7.0.0-alpha.12", "babel-types": "7.0.0-alpha.12", - "babylon": "7.0.0-beta.12", + "babylon": "7.0.0-beta.13", "convert-source-map": "^1.1.0", "debug": "^2.1.1", "json5": "^0.5.0", diff --git a/packages/babel-generator/package.json b/packages/babel-generator/package.json index 5b8d2f622e..ec5bf9846a 100644 --- a/packages/babel-generator/package.json +++ b/packages/babel-generator/package.json @@ -20,6 +20,6 @@ }, "devDependencies": { "babel-helper-fixtures": "7.0.0-alpha.12", - "babylon": "^7.0.0-beta.12" + "babylon": "^7.0.0-beta.13" } } diff --git a/packages/babel-plugin-syntax-optional-chaining/package.json b/packages/babel-plugin-syntax-optional-chaining/package.json index d14cd0549a..39ea71318d 100644 --- a/packages/babel-plugin-syntax-optional-chaining/package.json +++ b/packages/babel-plugin-syntax-optional-chaining/package.json @@ -1,6 +1,6 @@ { "name": "babel-plugin-syntax-optional-chaining", - "version": "7.0.0-alpha.12", + "version": "7.0.0-alpha.13", "description": "Allow parsing of optional properties", "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-chaining", "license": "MIT", diff --git a/packages/babel-plugin-transform-optional-chaining/package.json b/packages/babel-plugin-transform-optional-chaining/package.json index ef10fd42ff..f77af6f2c6 100644 --- a/packages/babel-plugin-transform-optional-chaining/package.json +++ b/packages/babel-plugin-transform-optional-chaining/package.json @@ -1,17 +1,17 @@ { "name": "babel-plugin-transform-optional-chaining", - "version": "7.0.0-alpha.7", + "version": "7.0.0-alpha.13", "description": "Transform optional chaining operators into a series of nil checks", "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-optional-chaining", "license": "MIT", "main": "lib/index.js", "dependencies": { - "babel-plugin-syntax-optional-chaining": "7.0.0-alpha.12" + "babel-plugin-syntax-optional-chaining": "7.0.0-alpha.13" }, "keywords": [ "babel-plugin" ], "devDependencies": { - "babel-helper-plugin-test-runner": "7.0.0-alpha.7" + "babel-helper-plugin-test-runner": "7.0.0-alpha.12" } } diff --git a/packages/babel-template/package.json b/packages/babel-template/package.json index 329b478c40..8825eb9b5d 100644 --- a/packages/babel-template/package.json +++ b/packages/babel-template/package.json @@ -10,7 +10,7 @@ "dependencies": { "babel-traverse": "7.0.0-alpha.12", "babel-types": "7.0.0-alpha.12", - "babylon": "7.0.0-beta.12", + "babylon": "7.0.0-beta.13", "lodash": "^4.2.0" } } diff --git a/packages/babel-traverse/package.json b/packages/babel-traverse/package.json index c5d71590b2..7e1629f339 100644 --- a/packages/babel-traverse/package.json +++ b/packages/babel-traverse/package.json @@ -12,7 +12,7 @@ "babel-helper-function-name": "7.0.0-alpha.7", "babel-messages": "7.0.0-alpha.12", "babel-types": "7.0.0-alpha.12", - "babylon": "7.0.0-beta.12", + "babylon": "7.0.0-beta.13", "debug": "^2.2.0", "globals": "^9.0.0", "invariant": "^2.2.0", diff --git a/packages/babel-types/package.json b/packages/babel-types/package.json index 0a70a3b843..a3208469fd 100644 --- a/packages/babel-types/package.json +++ b/packages/babel-types/package.json @@ -14,6 +14,6 @@ }, "devDependencies": { "babel-generator": "7.0.0-alpha.12", - "babylon": "^7.0.0-beta.12" + "babylon": "^7.0.0-beta.13" } } From 0740e6113172f7d08a86a45f0f25a30a4c7e39dc Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 8 Jun 2017 00:47:19 -0400 Subject: [PATCH 32/33] Use strict --- .../test/fixtures/execute/assignment-left.js | 2 ++ .../test/fixtures/execute/assignment-right.js | 2 ++ .../test/fixtures/execute/call.js | 2 ++ .../test/fixtures/execute/delete.js | 2 ++ .../test/fixtures/execute/new.js | 2 ++ .../test/fixtures/execute/unary.js | 2 ++ .../test/fixtures/execute/update.js | 2 ++ 7 files changed, 14 insertions(+) diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js index 631cb72fcd..e9ae376fef 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-left.js @@ -1,3 +1,5 @@ +"use strict"; + const obj = { a: { b: 0, diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js index d2299449ff..58d99d7927 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/assignment-right.js @@ -1,3 +1,5 @@ +"use strict"; + const obj = { a: { b: { diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js index 846b15d7dc..b3c044c519 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/call.js @@ -1,3 +1,5 @@ +"use strict"; + let calls = 0; const obj = { a: { diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js index cbb890e5fe..a397021233 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/delete.js @@ -1,3 +1,5 @@ +"use strict"; + const obj = { a: { b: 0, diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js index bef413aa6e..184524f49e 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/new.js @@ -1,3 +1,5 @@ +"use strict"; + let calls = 0; const obj = { a: { diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js index 8d71fe89c7..f8a55d8646 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/unary.js @@ -1,3 +1,5 @@ +"use strict"; + const obj = { a: { b: 0, diff --git a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js index 06d1024e4d..320a8c75e0 100644 --- a/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js +++ b/packages/babel-plugin-transform-optional-chaining/test/fixtures/execute/update.js @@ -1,3 +1,5 @@ +"use strict"; + const obj = { a: { b: 0, From f363ec6b99228fa9a434a7bb2317e6ae9b75a422 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Tue, 27 Jun 2017 01:27:41 -0400 Subject: [PATCH 33/33] Add to stage-1 preset --- packages/babel-preset-stage-1/package.json | 1 + packages/babel-preset-stage-1/src/index.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/babel-preset-stage-1/package.json b/packages/babel-preset-stage-1/package.json index fcbe9d4838..e173b22fcc 100644 --- a/packages/babel-preset-stage-1/package.json +++ b/packages/babel-preset-stage-1/package.json @@ -11,6 +11,7 @@ "babel-plugin-transform-decorators": "7.0.0-alpha.12", "babel-plugin-transform-export-extensions": "7.0.0-alpha.12", "babel-plugin-transform-numeric-separator": "7.0.0-alpha.12", + "babel-plugin-transform-optional-chaining": "7.0.0-alpha.13", "babel-preset-stage-2": "7.0.0-alpha.12" } } diff --git a/packages/babel-preset-stage-1/src/index.js b/packages/babel-preset-stage-1/src/index.js index fd1be2d539..5a6e69a320 100644 --- a/packages/babel-preset-stage-1/src/index.js +++ b/packages/babel-preset-stage-1/src/index.js @@ -3,6 +3,7 @@ import presetStage2 from "babel-preset-stage-2"; import transformDecorators from "babel-plugin-transform-decorators"; import transformExportExtensions from "babel-plugin-transform-export-extensions"; import transformNumericSeparator from "babel-plugin-transform-numeric-separator"; +import transformOptionalChaining from "babel-plugin-transform-optional-chaining"; export default function () { return { @@ -13,6 +14,7 @@ export default function () { transformDecorators, transformExportExtensions, transformNumericSeparator, + transformOptionalChaining, ], }; }