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 + )); + }, + }, }, }; }