acorn-es7-plugin
Advanced tools
Comparing version 1.0.0 to 1.0.3
@@ -1,100 +0,159 @@ | ||
module.exports = function(acorn) { | ||
var tokens = { | ||
async:new acorn.TokenType("async",{beforeExpr: true, prefix: true, startsExpr: true, keyword: "async"}), | ||
await:new acorn.TokenType("await",{beforeExpr: true, prefix: true, startsExpr: true, keyword: "await"}) | ||
} ; | ||
var NotAsync = {} ; | ||
var asyncExit = /^async[\t ]+(return|throw)/ ; | ||
var asyncFunction = /^async[\t ]+function/ ; | ||
acorn.plugins.asyncawait = function(parser,options){ | ||
var es7check = function(){} ; | ||
/* Create a new parser derived from the specified parser, so that in the | ||
* event of an error we can back out and try again */ | ||
function subParse(parser, how, pos, extensions) { | ||
var p = new parser.constructor(parser.options, parser.input, pos); | ||
if (extensions) | ||
for (var k in extensions) | ||
p[k] = extensions[k] ; | ||
parser.extend("initialContext",function(base){ | ||
return function(){ | ||
if (this.options.ecmaVersion < 7) { | ||
es7check = function(node) { | ||
parser.raise(node.start,"async/await keywords only available when ecmaVersion>=7") ; | ||
} ; | ||
} | ||
return base.apply(this,arguments); | ||
p.inFunction = parser.inFunction ; | ||
p.inAsyncFunction = parser.inAsyncFunction ; | ||
p.inGenerator = parser.inGenerator ; | ||
p.inModule = parser.inModule ; | ||
p.nextToken(); | ||
return p[how](); | ||
} | ||
function asyncAwaitPlugin (parser,options){ | ||
var es7check = function(){} ; | ||
parser.extend("initialContext",function(base){ | ||
return function(){ | ||
if (this.options.ecmaVersion < 7) { | ||
es7check = function(node) { | ||
parser.raise(node.start,"async/await keywords only available when ecmaVersion>=7") ; | ||
} ; | ||
} | ||
}) ; | ||
return base.apply(this,arguments); | ||
} | ||
}) ; | ||
parser.extend("finishNode",function(base){ | ||
return function(node,type){ | ||
ret = base.call(this,node,type); | ||
if (typeof options==="object" && options.asyncExits | ||
&& (type==='ReturnStatement' || type==='ThrowStatement')) { | ||
if (node.argument && node.argument.operator==='async') { | ||
node.argument = node.argument.argument ; | ||
node.async = true ; | ||
parser.extend("parseStatement",function(base){ | ||
return function (declaration, topLevel) { | ||
if (this.type.label==='name') { | ||
if (asyncFunction.test(this.input.slice(this.start))) { | ||
var wasAsync = this.inAsyncFunction ; | ||
try { | ||
this.inAsyncFunction = true ; | ||
this.next() ; | ||
var r = this.parseStatement(declaration, topLevel) ; | ||
r.async = true ; | ||
return r ; | ||
} finally { | ||
this.inAsyncFunction = wasAsync ; | ||
} | ||
} | ||
if (type==='UnaryExpression' && node.operator==='await') { | ||
es7check(node) ; | ||
node.type = 'AwaitExpression' ; | ||
} else if ((typeof options==="object" && options.asyncExits) && asyncExit.test(this.input.slice(this.start))) { | ||
// NON-STANDARD EXTENSION iff. options.asyncExits is set, the | ||
// extensions 'async return <expr>?' and 'async throw <expr>?' | ||
// are enabled. In each case they are the standard ESTree nodes | ||
// with the flag 'async:true' | ||
this.next() ; | ||
var r = this.parseStatement(declaration, topLevel) ; | ||
r.async = true ; | ||
return r ; | ||
} | ||
if (type==='UnaryExpression' && node.operator==='async') { | ||
es7check(node) ; | ||
if (!node.argument.async && (node.argument.type==='FunctionDeclaration' || | ||
node.argument.type==='FunctionExpression' || | ||
node.argument.type==='ArrowFunctionExpression')) { | ||
var fn = node.argument ; | ||
delete node.argument ; | ||
delete node.operator ; | ||
delete node.prefix ; | ||
node.async = true ; | ||
Object.keys(fn).forEach(function(k){ | ||
if (k!=='start') | ||
node[k] = fn[k] ; | ||
} | ||
return base.apply(this,arguments); | ||
} | ||
}) ; | ||
parser.extend("parseExprAtom",function(base){ | ||
return function(refShorthandDefaultPos){ | ||
var start = this.start ; | ||
var rhs,r = base.apply(this,arguments); | ||
if (r.type==='Identifier') { | ||
if (r.name==='async' && !/^async[\t ]*\n/.test(this.input.slice(start))) { | ||
// Is this really an async function? | ||
var isAsync = this.inAsyncFunction ; | ||
try { | ||
this.inAsyncFunction = true ; | ||
var pp = this ; | ||
var inBody = false ; | ||
rhs = subParse(this,'parseExpression',this.start,{ | ||
parseFunctionBody:function(){ | ||
try { | ||
var wasInBody = inBody ; | ||
inBody = true ; | ||
return pp.parseFunctionBody.apply(this,arguments) ; | ||
} finally { | ||
inBody = wasInBody ; | ||
} | ||
}, | ||
raise:function(){ | ||
try { | ||
return pp.raise.apply(this,arguments) ; | ||
} catch(ex) { | ||
throw inBody?ex:NotAsync ; | ||
} | ||
} | ||
}) ; | ||
if (rhs.type==='SequenceExpression') | ||
rhs = rhs.expressions[0] ; | ||
if (rhs.type==='FunctionExpression' || rhs.type==='FunctionDeclaration' || rhs.type==='ArrowFunctionExpression') { | ||
rhs.async = true ; | ||
this.pos = rhs.end ; | ||
this.next(); | ||
es7check(rhs) ; | ||
return rhs ; | ||
} | ||
} catch (ex) { | ||
if (ex!==NotAsync) | ||
throw ex ; | ||
} | ||
finally { | ||
this.inAsyncFunction = isAsync ; | ||
} | ||
} | ||
if (type==='ExpressionStatement' && node.expression.type==='FunctionExpression' && node.expression.async) { | ||
es7check(node) ; | ||
var fn = node.expression ; | ||
fn.type = 'FunctionDeclaration' ; | ||
delete node.expression ; | ||
Object.keys(fn).forEach(function(k){ | ||
if (k!=='start') | ||
node[k] = fn[k] ; | ||
}) ; | ||
} | ||
return ret ; | ||
} | ||
}) ; | ||
parser.extend("finishToken",function(base){ | ||
return function(type,val){ | ||
type = type || (tokens.hasOwnProperty(val) && tokens[val]) ; | ||
return base.call(this,type,val); | ||
} | ||
}) ; | ||
parser.extend("isKeyword",function(base){ | ||
return function(str){ | ||
if (str==="async") { | ||
this.potentialArrowAt = this.start+str.length+1 ; | ||
return true ; | ||
else if (r.name==='await') { | ||
var n = this.startNode() ; | ||
if (this.inAsyncFunction) { | ||
rhs = this.parseExprSubscripts() ; | ||
n.operator = 'await' ; | ||
n.argument = rhs ; | ||
n = this.finishNodeAt(n,'AwaitExpression', rhs.end, rhs.loc) ; | ||
es7check(n) ; | ||
return n ; | ||
} else | ||
// NON-STANDARD EXTENSION iff. options.awaitAnywhere is true, | ||
// an 'AwaitExpression' is allowed anywhere the token 'await' | ||
// could not be an identifier with the name 'await'. | ||
if (typeof options==="object" && options.awaitAnywhere) { | ||
var start = this.start ; | ||
rhs = subParse(this,'parseExprSubscripts',this.start-4) ; | ||
if (rhs.end<=start) { | ||
rhs = subParse(this,'parseExprSubscripts',this.start) ; | ||
n.operator = 'await' ; | ||
n.argument = rhs ; | ||
n = this.finishNodeAt(n,'AwaitExpression', rhs.end, rhs.loc) ; | ||
this.pos = rhs.end ; | ||
this.next(); | ||
es7check(n) ; | ||
return n ; | ||
} | ||
} | ||
} | ||
return tokens.hasOwnProperty(str) || base.apply(this,arguments); | ||
} | ||
}) ; | ||
return r ; | ||
} | ||
}) ; | ||
parser.extend("isReservedWord",function(base){ | ||
return function(str){ | ||
return tokens.hasOwnProperty(str) || base.apply(this,arguments); | ||
parser.extend("parsePropertyName",function(base){ | ||
return function (prop) { | ||
var key = base.apply(this,arguments) ; | ||
if (key.type === "Identifier" && key.name === "async") { | ||
es7check(prop) ; | ||
prop.async = true ; | ||
key = base.apply(this,arguments) ; | ||
} | ||
}) ; | ||
return key; | ||
}; | ||
}) ; | ||
} | ||
parser.extend("parsePropertyName",function(base){ | ||
return function (prop) { | ||
var key = base.apply(this,arguments) ; | ||
if (key.type === "Identifier" && key.name === "async") { | ||
es7check(prop) ; | ||
prop.async = true ; | ||
key = base.apply(this,arguments) ; | ||
} | ||
return key; | ||
}; | ||
}) ; | ||
} | ||
module.exports = function(acorn) { | ||
acorn.plugins.asyncawait = asyncAwaitPlugin ; | ||
} |
{ | ||
"name": "acorn-es7-plugin", | ||
"version": "1.0.0", | ||
"version": "1.0.3", | ||
"description": "A plugin for the Acorn parser that understands the ES7 keywords async and await", | ||
@@ -5,0 +5,0 @@ "main": "acorn-es7-plugin.js", |
@@ -33,1 +33,12 @@ [![NPM](https://nodei.co/npm/acorn-es7-plugin.png?downloads=true&downloadRank=true)](https://nodei.co/npm/acorn-es7-plugin/) | ||
console.log(JSON.stringify(ast,null,2)) ; | ||
Options & Compliance | ||
==================== | ||
The parser attempts to enforce strict contextualisation of `async` and `await`. Specifically, `async` is only a keyword if it precedes a function declaration, function expression or arrow function. `await` is only a keyword inside an `async` function. Outside of these contexts, both tokens are treated as identifiers (as they were in ES6 and earlier). | ||
When using the plugin, you can supply an object in place of the 'true' flag with the following options. | ||
| flag | meaning | | ||
|------|---------| | ||
| awaitAnywhere | If `await` is used outside of an async function and could not be an identifier, generate an AwaitExpression node. This typically means you can use `await` anywhere _except_ when its argument would require parentheses, as this parses to a call to 'await(....)'. | | ||
| asyncExits | Allow the additional sequences `async return <optional-expression>` and `async throw <optional-expression>`. These sequences are used with [nodent](https://github.com/MatAtBread/nodent). In each case, as with async functions, a standard ReturnStatement (or ThrowStatement) node is generated, with an additional member 'async' set to true. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
7527
151
44