Split syntax and transform into two plugins

This commit is contained in:
Justin Ridgewell 2017-06-03 02:39:16 -04:00
parent 2a496890ff
commit 30ee87159d
7 changed files with 109 additions and 86 deletions

View File

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

View File

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

View File

@ -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"]
});
```

View File

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

View File

@ -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": {}
}

View File

@ -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"

View File

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