acorn-es7-plugin
Advanced tools
Comparing version 1.0.13 to 1.0.14
@@ -5,4 +5,16 @@ var NotAsync = {} ; | ||
var atomOrPropertyOrLabel = /^\s*[):;]/ ; | ||
var asyncAtEndOfLine = /^async[\t ]*\n/ ; | ||
var removeComments = /([^\n])\/\*(\*(?!\/)|[^\n*])*\*\/([^\n])/g ; | ||
function hasLineTerminatorBeforeNext(st, since) { | ||
return st.lineStart >= since; | ||
} | ||
function test(regex,st,noComment) { | ||
var src = st.input.slice(st.start) ; | ||
if (noComment) { | ||
src = src.replace(removeComments,"$1 $3") ; | ||
} | ||
return regex.test(src); | ||
} | ||
/* Return the object holding the parser's 'State'. This is different between acorn ('this') | ||
@@ -52,2 +64,6 @@ * and babylon ('this.state') */ | ||
this.reservedWordsStrictBind = new RegExp(this.reservedWordsStrictBind.toString().replace(/await|async/g,"").replace("|/","/").replace("/|","/").replace("||","|")) ; | ||
this.inAsyncFunction = options.inAsyncFunction ; | ||
if (options.awaitAnywhere && options.inAsyncFunction) | ||
parser.raise(node.start,"The options awaitAnywhere and inAsyncFunction are mutually exclusive") ; | ||
return base.apply(this,arguments); | ||
@@ -59,3 +75,3 @@ } | ||
return function(){ | ||
if (this.type.label==='name' && this.value==='async' && asyncFunction.test(this.input.substr(this.start))) { | ||
if (this.type.label==='name' && this.value==='async' && test(asyncFunction,state(this))) { | ||
return true ; | ||
@@ -66,3 +82,3 @@ } | ||
}) ; | ||
parser.extend("parseStatement",function(base){ | ||
@@ -74,3 +90,3 @@ return function (declaration, topLevel) { | ||
if (st.type.label==='name') { | ||
if (asyncFunction.test(st.input.slice(st.start))) { | ||
if (test(asyncFunction,st,true)) { | ||
var wasAsync = st.inAsyncFunction ; | ||
@@ -81,3 +97,3 @@ try { | ||
var r = this.parseStatement(declaration, topLevel) ; | ||
r.async = true ; | ||
r.async = true ; | ||
r.start = start; | ||
@@ -89,3 +105,3 @@ r.loc && (r.loc.start = startLoc); | ||
} | ||
} else if ((typeof options==="object" && options.asyncExits) && asyncExit.test(st.input.slice(st.start))) { | ||
} else if ((typeof options==="object" && options.asyncExits) && test(asyncExit,st)) { | ||
// NON-STANDARD EXTENSION iff. options.asyncExits is set, the | ||
@@ -127,3 +143,3 @@ // extensions 'async return <expr>?' and 'async throw <expr>?' | ||
if (r.type==='Identifier') { | ||
if (r.name==='async' && !asyncAtEndOfLine.test(st.input.slice(start))) { | ||
if (r.name==='async' && !hasLineTerminatorBeforeNext(st, r.end)) { | ||
// Is this really an async function? | ||
@@ -237,3 +253,3 @@ var isAsync = st.inAsyncFunction ; | ||
var key = base.apply(this,arguments) ; | ||
if (key.type === "Identifier" && key.name === "async") { | ||
if (key.type === "Identifier" && key.name === "async" && !hasLineTerminatorBeforeNext(st, key.end)) { | ||
// Look-ahead to see if this is really a property or label called async or await | ||
@@ -240,0 +256,0 @@ if (!st.input.slice(key.end).match(atomOrPropertyOrLabel)){ |
{ | ||
"name": "acorn-es7-plugin", | ||
"version": "1.0.13", | ||
"version": "1.0.14", | ||
"description": "A plugin for the Acorn parser that understands the ES7 keywords async and await", | ||
@@ -5,0 +5,0 @@ "main": "acorn-es7-plugin.js", |
@@ -101,3 +101,4 @@ [![NPM](https://nodei.co/npm/acorn-es7-plugin.png?downloads=true&downloadRank=true)](https://nodei.co/npm/acorn-es7-plugin/) | ||
|------|---------| | ||
| 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(....)'. | | ||
| 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(....)'. Should not be used with inAsyncFunction option | | ||
| inAsyncFunction | Parse the code as if it is the body of an `async function`. This means `await` cannot be an identifier and is always an AwaitExpression, even if the argument is parenthesized. Should not be used with the awaitAnywhere option | | ||
| asyncExits | Allow the additional sequences `async return <optional-expression>` and `async throw <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. | ||
@@ -107,4 +108,9 @@ | ||
========= | ||
03-May-16: v1.0.13 | ||
03-May-16: v1.0.14 | ||
- Correctly parse async statements containing comments. | ||
- Implement the option inAsyncFunction | ||
03-May-16: v1.0.13 | ||
- Correctly parse the statement `export async function name(){...}` as _async function name(){...}_ is a valid named declaration. | ||
@@ -111,0 +117,0 @@ |
@@ -7,3 +7,3 @@ { | ||
"scripts": { | ||
"test": "node test-es5.js ; mocha --opts ./mocha.opts" | ||
"test": "mocha --opts ./mocha.opts ; node test-es5.js " | ||
}, | ||
@@ -10,0 +10,0 @@ "repository": { |
'use strict'; | ||
/* Simple test script that doesn't need mocha or similar - it just parses stuff and checks the returned AST */ | ||
@@ -7,63 +6,194 @@ var acorn = require('acorn'); | ||
require('../')(acorn); | ||
function parse(code, pluginOptions) { | ||
if (Array.isArray(code)) { | ||
code = code.join('\n'); | ||
} | ||
return acorn.parse(code, { | ||
sourceType: 'module', | ||
ecmaVersion: 7, | ||
locations: true, | ||
ranges: true, | ||
plugins: {asyncawait: pluginOptions || pluginOptions !== false} | ||
if (Array.isArray(code)) { | ||
code = code.join('\n'); | ||
} | ||
return acorn.parse(code, { | ||
sourceType: 'module', | ||
ecmaVersion: 7, | ||
locations: true, | ||
ranges: true, | ||
plugins: { | ||
asyncawait: pluginOptions || {} | ||
} | ||
}); | ||
} | ||
var tests = [ | ||
{desc:"Simple async function",code:"async function x() { return undefined; }", | ||
pass:function(ast){ return ast.body[0].async===true} | ||
}, | ||
{desc:"Await in async is AwaitExpression",code:"async function x() { await(undefined); await undefined ; }", | ||
pass:function(ast){ return ast.body[0].body.body[0].expression.type==='AwaitExpression' && ast.body[0].body.body[1].expression.type==='AwaitExpression'} | ||
}, | ||
{desc:"Await in function is identifier",code:"function x() { await(undefined); }", | ||
pass:function(ast){ return ast.body[0].body.body[0].expression.callee.name==='await'} | ||
}, | ||
{desc:"Async method",code:"var a = {async x(){}}", | ||
pass:function(ast){ return ast.body[0].declarations[0].init.properties[0].value.async } | ||
}, | ||
{desc:"Async get method",code:"var a = {async get x(){}}", | ||
pass:function(ast){ return ast.body[0].declarations[0].init.properties[0].value.async } | ||
}, | ||
{desc:"Async arrow",code:"var a = async()=>0", | ||
pass:function(ast){ return ast.body[0].declarations[0].init.async } | ||
}, | ||
{desc:"Async set method fails",code:"var a = {async set x(){}}", | ||
pass:function(ex){ return ex === "'set <member>(value)' cannot be be async (1:15)" } | ||
}, | ||
{desc:"Async constructor fails",code:"var a = {async constructor(){}}", | ||
pass:function(ex){ return ex === "'constructor()' cannot be be async (1:15)" } | ||
}, | ||
{desc:"Await declaration fails in async function",code:"async function x() { var await; }", | ||
pass:function(ex){ return ex === "'await' is reserved within async functions (1:25)" } | ||
}, | ||
{desc:"Await function declaration fails in async function",code:"async function x() { function await() {} }", | ||
pass:function(ex){ return ex === "'await' is reserved within async functions (1:30)" } | ||
}, | ||
{desc:"Await reference fails in async function",code:"async function x() { return 1+await; }", | ||
pass:function(ex){ return ex === "Unexpected token (1:35)" } | ||
} | ||
] ; | ||
function isIdentThenFnDecl(ast) { | ||
return ast.body[0].type === 'ExpressionStatement' && ast.body[0].expression.type === 'Identifier' && ast.body[0].expression.name === 'async' && !ast.body[1].async === true && ast.body[1].type == "FunctionDeclaration"; | ||
} | ||
function isAsyncFnDecl(ast) { | ||
return ast.body[0].async === true && ast.body[0].type == "FunctionDeclaration"; | ||
} | ||
function isExprType(type) { | ||
return function (ast) { | ||
return ast.body[0].type === 'ExpressionStatement' && ast.body[0].expression.type === type; | ||
}; | ||
} | ||
var tests = [{ | ||
desc: "Simple async function", | ||
code: "async function x() { return undefined; }", | ||
pass: function (ast) { | ||
return ast.body[0].async === true; | ||
} | ||
},{ | ||
desc: "Await in async is AwaitExpression", | ||
code: "async function x() { await(undefined); await undefined ; }", | ||
pass: function (ast) { | ||
return ast.body[0].body.body[0].expression.type === 'AwaitExpression' && ast.body[0].body.body[1].expression.type === 'AwaitExpression'; | ||
} | ||
},{ | ||
desc: "Await in function is identifier", | ||
code: "function x() { await(undefined); }", | ||
pass: function (ast) { | ||
return ast.body[0].body.body[0].expression.callee.name === 'await'; | ||
} | ||
},{ | ||
desc: "Async method", | ||
code: "var a = {async x(){}}", | ||
pass: function (ast) { | ||
return ast.body[0].declarations[0].init.properties[0].value.async; | ||
} | ||
},{ | ||
desc: "Async get method", | ||
code: "var a = {async get x(){}}", | ||
pass: function (ast) { | ||
return ast.body[0].declarations[0].init.properties[0].value.async; | ||
} | ||
},{ | ||
desc: "Async arrow", | ||
code: "var a = async()=>0", | ||
pass: function (ast) { | ||
return ast.body[0].declarations[0].init.async; | ||
} | ||
},{ | ||
desc: "Async set method fails", | ||
code: "var a = {async set x(){}}", | ||
pass: function (ex) { | ||
return ex === "'set <member>(value)' cannot be be async (1:15)"; | ||
} | ||
},{ | ||
desc: "Async constructor fails", | ||
code: "var a = {async constructor(){}}", | ||
pass: function (ex) { | ||
return ex === "'constructor()' cannot be be async (1:15)"; | ||
} | ||
},{ | ||
desc: "Await declaration fails in async function", | ||
code: "async function x() { var await; }", | ||
pass: function (ex) { | ||
return ex === "'await' is reserved within async functions (1:25)"; | ||
} | ||
},{ | ||
desc: "Await function declaration fails in async function", | ||
code: "async function x() { function await() {} }", | ||
pass: function (ex) { | ||
return ex === "'await' is reserved within async functions (1:30)"; | ||
} | ||
},{ | ||
desc: "Await reference fails in async function", | ||
code: "async function x() { return 1+await; }", | ||
pass: function (ex) { | ||
return ex === "Unexpected token (1:35)"; | ||
} | ||
},{ | ||
desc: "{code} is an async FunctionDeclaration", | ||
code: "async /* a */ function x(){}", | ||
pass: isAsyncFnDecl | ||
},{ | ||
desc: "{code} is a reference to 'async' and a sync FunctionDeclaration", | ||
code: "async /*\n*/function x(){}", | ||
pass: isIdentThenFnDecl | ||
},{ | ||
desc: "{code} is a reference to 'async' and a sync FunctionDeclaration", | ||
code: "async /* a */\nfunction x(){}", | ||
pass: isIdentThenFnDecl | ||
},{ | ||
desc: "{code} is a reference to 'async' and a sync FunctionDeclaration", | ||
code: "async\nfunction x(){}", | ||
pass: isIdentThenFnDecl | ||
},{ | ||
desc: "{code} is a reference to 'async' and a sync FunctionDeclaration", | ||
code: "async //\nfunction x(){}", | ||
pass: isIdentThenFnDecl | ||
},{ | ||
desc: "{code} is a reference to 'async' and a sync FunctionDeclaration", | ||
code: "async /*\n*/\nfunction x(){}", | ||
pass: isIdentThenFnDecl | ||
},{ | ||
/* Valid combinations of await options; none, just inAsyncFunction, or just awaitAnywhere */ | ||
desc: "{code} is an AwaitExpression when inAsyncFunction option is true", | ||
code: "await(x)", | ||
options: { | ||
inAsyncFunction: true | ||
}, | ||
pass: isExprType('AwaitExpression') | ||
},{ | ||
desc: "{code} is an AwaitExpression when inAsyncFunction option is true", | ||
code: "await x", | ||
options: { | ||
inAsyncFunction: true | ||
}, | ||
pass: isExprType('AwaitExpression') | ||
},{ | ||
desc: "{code} is a CallExpression when awaitAnywhere option is true", | ||
code: "await(x)", | ||
options: { | ||
awaitAnywhere: true | ||
}, | ||
pass: isExprType('CallExpression') | ||
},{ | ||
desc: "{code} is an AwaitExpression when awaitAnywhere option is true", | ||
code: "await x", | ||
options: { | ||
awaitAnywhere: true | ||
}, | ||
pass: isExprType('AwaitExpression') | ||
},{ | ||
desc: "{code} is a CallExpression when inAsyncFunction and awaitAnywhere option are false", | ||
code: "await(x)", | ||
pass: isExprType('CallExpression') | ||
},{ | ||
desc: "{code} is a SyntaxError when inAsyncFunction and awaitAnywhere option are false", | ||
code: "await x", | ||
pass: function (ex) { | ||
return ex === "Unexpected token (1:6)"; | ||
} | ||
}]; | ||
var out = { | ||
true:"pass".green, | ||
false:"fail".red | ||
true: "pass".green, | ||
false: "fail".red | ||
}; | ||
tests.forEach(function(test,idx){ | ||
var testNumber = +process.argv[2] || 0; | ||
if (testNumber) { | ||
tests = [tests[testNumber - 1]]; | ||
} else { | ||
testNumber += 1; | ||
} | ||
var results = { | ||
true: 0, | ||
false: 0 | ||
}; | ||
tests.forEach(function (test, idx) { | ||
var code = test.code.replace(/\n/g, ' <linefeed> '); | ||
var desc = test.desc.replace('{code}', code.yellow); | ||
var pass = function () { | ||
var p = test.pass.apply(this, arguments); | ||
results[p] += 1; | ||
return p; | ||
}; | ||
try { | ||
console.log((idx+1)+")\t",test.desc,test.code.yellow,out[test.pass(parse(test.code))]); | ||
} catch(ex) { | ||
console.log((idx+1)+")\t",test.desc,test.code.yellow,ex.message.cyan,out[test.pass(ex.message)]); | ||
console.log(idx + testNumber + ")\t", desc, out[pass(parse(test.code, test.options))]); | ||
} catch (ex) { | ||
console.log(idx + testNumber + ")\t", desc, ex.message.cyan, out[pass(ex.message)]); | ||
} | ||
}) ; | ||
}); | ||
console.log(''); | ||
if (results.true) | ||
console.log((results.true + " of " + tests.length + " tests passed").green); | ||
if (results.false) | ||
console.log((results.false + " of " + tests.length + " tests failed").red); | ||
532
test/test.js
@@ -59,38 +59,116 @@ 'use strict'; | ||
beforeEach(() => { | ||
node = find( | ||
'FunctionDeclaration', | ||
parse([ | ||
'async function foo() {', | ||
' x = await bar()', | ||
'}' | ||
]) | ||
describe('-', () => { | ||
beforeEach(() => { | ||
node = find( | ||
'FunctionDeclaration', | ||
parse([ | ||
'async function foo() {', | ||
' x = await bar()', | ||
'}' | ||
]) | ||
); | ||
}); | ||
it('marks the node as async', () => | ||
assert(node.async) | ||
); | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 0) | ||
); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, 42) | ||
); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 1, | ||
column: 0 | ||
}) | ||
); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 3, | ||
column: 1 | ||
}) | ||
); | ||
}); | ||
it('marks the node as async', () => | ||
assert(node.async) | ||
); | ||
var assertFindsIdentifierExpressionStatement = (ast) => { | ||
node = find('ExpressionStatement', ast); | ||
assert.strictEqual(node.expression.type, 'Identifier'); | ||
assert.strictEqual(node.expression.name, 'async'); | ||
assert.deepEqual(node.expression.loc, { | ||
start: { | ||
line: 1, | ||
column: 0 | ||
}, | ||
end: { | ||
line: 1, | ||
column: 5 | ||
} | ||
}); | ||
}; | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 0) | ||
); | ||
describe('linefeed after async (simple)', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'async \t\t ', | ||
'function foo() {', | ||
'}' | ||
]); | ||
}); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, 42) | ||
); | ||
it('finds Identifier ExpressionStatement', () => { | ||
assertFindsIdentifierExpressionStatement(ast); | ||
}); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 1, | ||
column: 0 | ||
}) | ||
); | ||
it('does not mark FunctionDeclaration as async', () => { | ||
node = find('FunctionDeclaration', ast); | ||
assert(!node.async, 'Expected node.async to be false'); | ||
}); | ||
}); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 3, | ||
column: 1 | ||
}) | ||
); | ||
describe('linefeed after async (single line comment)', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'async // flag enables async completion', | ||
'function foo() {', | ||
'}' | ||
]); | ||
}); | ||
it('finds Identifier ExpressionStatement', () => { | ||
assertFindsIdentifierExpressionStatement(ast); | ||
}); | ||
it('does not mark FunctionDeclaration as async', () => { | ||
node = find('FunctionDeclaration', ast); | ||
assert(!node.async, 'Expected node.async to be false'); | ||
}); | ||
}); | ||
describe('linefeed after async (multiline comment) function', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'async /* flag enables async completion', | ||
' of the callback */function foo() {', | ||
'}' | ||
]); | ||
}); | ||
it('finds Identifier ExpressionStatement', () => { | ||
assertFindsIdentifierExpressionStatement(ast); | ||
}); | ||
it('does not mark FunctionDeclaration as async', () => { | ||
node = find('FunctionDeclaration', ast); | ||
assert(!node.async, 'Expected node.async to be false'); | ||
}); | ||
}); | ||
}); | ||
@@ -101,39 +179,117 @@ | ||
beforeEach(() => { | ||
code = [ | ||
'foo = async function () {', | ||
' x = await bar()', | ||
'}' | ||
]; | ||
node = find( | ||
'FunctionExpression', | ||
parse(code) | ||
describe('-', () => { | ||
beforeEach(() => { | ||
code = [ | ||
'foo = async function () {', | ||
' x = await bar()', | ||
'}' | ||
]; | ||
node = find( | ||
'FunctionExpression', | ||
parse(code) | ||
); | ||
}); | ||
it('marks the node as async', () => | ||
assert(node.async) | ||
); | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 6) | ||
); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, code.join('\n').length) | ||
); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 1, | ||
column: 6 | ||
}) | ||
); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 3, | ||
column: 1 | ||
}) | ||
); | ||
}); | ||
it('marks the node as async', () => | ||
assert(node.async) | ||
); | ||
var assertFindsIdentifierAssignmentExpressionRHS = (ast) => { | ||
node = find('AssignmentExpression', ast); | ||
assert.strictEqual(node.right.type, 'Identifier'); | ||
assert.strictEqual(node.right.name, 'async'); | ||
assert.deepEqual(node.right.loc, { | ||
start: { | ||
line: 1, | ||
column: 6 | ||
}, | ||
end: { | ||
line: 1, | ||
column: 11 | ||
} | ||
}); | ||
}; | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 6) | ||
); | ||
describe('linefeed after async (simple)', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'foo = async \t\t ', | ||
', function() {', | ||
'}' | ||
]); | ||
}); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, code.join('\n').length) | ||
); | ||
it('finds Identifier ExpressionStatement', () => { | ||
assertFindsIdentifierAssignmentExpressionRHS(ast); | ||
}); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 1, | ||
column: 6 | ||
}) | ||
); | ||
it('does not mark FunctionExpression as async', () => { | ||
node = find('FunctionExpression', ast); | ||
assert(!node.async, 'Expected node.async to be false'); | ||
}); | ||
}); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 3, | ||
column: 1 | ||
}) | ||
); | ||
describe('linefeed after async (single line comment)', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'foo = async // flag enables async completion', | ||
', function() {', | ||
'}' | ||
]); | ||
}); | ||
it('finds Identifier ExpressionStatement', () => { | ||
assertFindsIdentifierAssignmentExpressionRHS(ast); | ||
}); | ||
it('does not mark FunctionExpression as async', () => { | ||
node = find('FunctionExpression', ast); | ||
assert(!node.async, 'Expected node.async to be false'); | ||
}); | ||
}); | ||
describe('linefeed after async (multiline comment), function', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'foo = async /* flag enables async completion', | ||
' of the callback */, function() {', | ||
'}' | ||
]); | ||
}); | ||
it('finds Identifier ExpressionStatement', () => { | ||
assertFindsIdentifierAssignmentExpressionRHS(ast); | ||
}); | ||
it('does not mark FunctionExpression as async', () => { | ||
node = find('FunctionExpression', ast); | ||
assert(!node.async, 'Expected node.async to be false'); | ||
}); | ||
}); | ||
}); | ||
@@ -144,44 +300,79 @@ | ||
beforeEach(() => { | ||
code = [ | ||
'var x = {', | ||
' async foo() {}', | ||
'};' | ||
]; | ||
node = find( | ||
// TODO: Is it really supposed to mark the Property async? Why not the FunctionExpression? | ||
'Property', | ||
parse(code) | ||
describe('-', () => { | ||
beforeEach(() => { | ||
code = [ | ||
'var x = {', | ||
' async foo() {}', | ||
'};' | ||
]; | ||
node = find( | ||
// TODO: Is it really supposed to mark the Property async? Why not the FunctionExpression? | ||
'Property', | ||
parse(code) | ||
); | ||
}); | ||
it('marks the node value as async', () => | ||
assert(node.value.async) | ||
); | ||
}); | ||
it('marks the node value as async', () => | ||
assert(node.value.async) | ||
); | ||
it('does not mark the node as async', () => | ||
assert(!node.async) | ||
); | ||
it('does not mark the node as async', () => | ||
assert(!node.async) | ||
); | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 12) | ||
); | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 12) | ||
); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, code[0].length + code[1].length + 1) // + 1 is due to newline char | ||
); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, code[0].length + code[1].length + 1) // + 1 is due to newline char | ||
); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 2, | ||
column: 2 | ||
}) | ||
); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 2, | ||
column: 2 | ||
}) | ||
); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 2, | ||
column: 16 | ||
}) | ||
); | ||
}); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 2, | ||
column: 16 | ||
}) | ||
); | ||
describe('linefeed after async (simple)', () => { | ||
it('fails to parse', () => { | ||
assert.throws(() => parse([ | ||
'var x = {', | ||
' async \t\t ', | ||
' foo() {}', | ||
'};' | ||
])); | ||
}); | ||
}); | ||
describe('linefeed after async (single line comment)', () => { | ||
it('fails to parse', () => { | ||
assert.throws(() => parse([ | ||
'var x = {', | ||
' async // flag enables async completion', | ||
' foo() {}', | ||
'};' | ||
])); | ||
}); | ||
}); | ||
describe('linefeed after async (multiline comment) illegal decl', () => { | ||
it('finds Identifier ExpressionStatement', () => { | ||
assert.throws(() => parse([ | ||
'var x = {', | ||
' async /* flag enables async completion', | ||
' of the callback */ foo() {}', | ||
'};' | ||
])); | ||
}); | ||
}); | ||
}); | ||
@@ -192,35 +383,136 @@ | ||
beforeEach(() => { | ||
code = 'var x = async () => {}'; | ||
node = find( | ||
'ArrowFunctionExpression', | ||
parse(code) | ||
describe('-', () => { | ||
beforeEach(() => { | ||
code = 'var x = async () => {}'; | ||
node = find( | ||
'ArrowFunctionExpression', | ||
parse(code) | ||
); | ||
}); | ||
it('marks the node as async', () => | ||
assert(node.async) | ||
); | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 8) | ||
); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, code.length) | ||
); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 1, | ||
column: 8 | ||
}) | ||
); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 1, | ||
column: code.length | ||
}) | ||
); | ||
}); | ||
it('marks the node as async', () => | ||
assert(node.async) | ||
); | ||
describe('linefeed after async (simple)', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'var x = async \t\t ', | ||
'()' | ||
]); | ||
}); | ||
it('finds correct start position', () => | ||
assert.strictEqual(node.start, 8) | ||
); | ||
it('fails to parse if linefeed preceeds arrow arguments', () => { | ||
assert.throws(() => parse([ | ||
'var x = async \t\t ', | ||
'() => {}' | ||
])); | ||
}); | ||
it('finds correct end position', () => | ||
assert.strictEqual(node.end, code.length) | ||
); | ||
it('finds CallExpression with "async" Identifier callee', () => { | ||
node = find('CallExpression', ast); | ||
assert.strictEqual(node.callee.type, 'Identifier'); | ||
assert.strictEqual(node.callee.name, 'async'); | ||
assert.deepEqual(node.callee.loc, { | ||
start: { | ||
line: 1, | ||
column: 8 | ||
}, | ||
end: { | ||
line: 1, | ||
column: 13 | ||
} | ||
}); | ||
}); | ||
}); | ||
it('finds correct start line/column', () => | ||
assert.deepEqual(node.loc.start, { | ||
line: 1, | ||
column: 8 | ||
}) | ||
); | ||
describe('linefeed after async (single line comment)', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'var x = async // flag enables async completion', | ||
'()' | ||
]); | ||
}); | ||
it('finds correct end line/column', () => | ||
assert.deepEqual(node.loc.end, { | ||
line: 1, | ||
column: code.length | ||
}) | ||
); | ||
it('fails to parse if linefeed preceeds arrow arguments', () => { | ||
assert.throws(() => parse([ | ||
'var x = async \t\t ', | ||
'() => {}' | ||
])); | ||
}); | ||
it('finds CallExpression with "async" Identifier callee', () => { | ||
node = find('CallExpression', ast); | ||
assert.strictEqual(node.callee.type, 'Identifier'); | ||
assert.strictEqual(node.callee.name, 'async'); | ||
assert.deepEqual(node.callee.loc, { | ||
start: { | ||
line: 1, | ||
column: 8 | ||
}, | ||
end: { | ||
line: 1, | ||
column: 13 | ||
} | ||
}); | ||
}); | ||
}); | ||
describe('linefeed after async (multiline comment) arrow decl', () => { | ||
var ast; | ||
beforeEach(() => { | ||
ast = parse([ | ||
'var x = async /* flag enables async completion', | ||
' of the callback */()' | ||
]); | ||
}); | ||
it('fails to parse if linefeed preceeds arrow arguments', () => { | ||
assert.throws(() => parse([ | ||
'var x = async /* flag enables async completion', | ||
' of the callback */() => {}' | ||
])); | ||
}); | ||
it('finds CallExpression with "async" Identifier callee', () => { | ||
node = find('CallExpression', ast); | ||
assert.strictEqual(node.callee.type, 'Identifier'); | ||
assert.strictEqual(node.callee.name, 'async'); | ||
assert.deepEqual(node.callee.loc, { | ||
start: { | ||
line: 1, | ||
column: 8 | ||
}, | ||
end: { | ||
line: 1, | ||
column: 13 | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -227,0 +519,0 @@ }); |
36556
10
960
127