1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Support top-level await (#5371)

* Support top-level await

* Remove code duplication

* Avoid use of trimEnd so tests pass in old Node

* Proposed rewrite of tests

* startsWith tests; revert eqJS

* build:browser
This commit is contained in:
Erik Demaine 2021-09-15 13:40:18 -04:00 committed by GitHub
parent b0946c3c60
commit c4f1fe7132
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 45 additions and 19 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -715,13 +715,14 @@
constructor(body1) {
super();
this.body = body1;
this.isAsync = (new Code([], this.body)).isAsync;
}
// Wrap everything in a safety closure, unless requested not to. It would be
// better not to generate them in the first place, but for now, clean up
// obvious double-parentheses.
compileNode(o) {
var fragments;
var fragments, parts;
o.indent = o.bare ? '' : TAB;
o.level = LEVEL_TOP;
o.compiling = true;
@ -730,7 +731,15 @@
if (o.bare) {
return fragments;
}
return [].concat(this.makeCode("(function() {\n"), fragments, this.makeCode("\n}).call(this);\n"));
parts = [];
parts.push(this.makeCode('('));
if (this.isAsync) {
parts.push(this.makeCode('async '));
}
parts.push(this.makeCode('function() {\n'));
parts.push(...fragments);
parts.push(this.makeCode('\n}).call(this);\n'));
return [].concat(...parts);
}
initializeScope(o) {
@ -7120,7 +7129,9 @@
var op, parts, ref1;
parts = [];
op = this.operator;
this.checkContinuation(o);
if (!this.isAwait()) {
this.checkContinuation(o);
}
if (indexOf.call(Object.keys(this.first), 'expression') >= 0 && !(this.first instanceof Throw)) {
if (this.first.expression != null) {
parts.push(this.first.expression.compileToFragments(o, LEVEL_OP));
@ -7176,7 +7187,7 @@
}
astNode(o) {
if (this.isYield() || this.isAwait()) {
if (this.isYield()) {
this.checkContinuation(o);
}
this.checkDeleteOperand(o);

View file

@ -513,6 +513,8 @@ exports.Root = class Root extends Base
constructor: (@body) ->
super()
@isAsync = (new Code [], @body).isAsync
children: ['body']
# Wrap everything in a safety closure, unless requested not to. It would be
@ -525,7 +527,13 @@ exports.Root = class Root extends Base
@initializeScope o
fragments = @body.compileRoot o
return fragments if o.bare
[].concat @makeCode("(function() {\n"), fragments, @makeCode("\n}).call(this);\n")
parts = []
parts.push @makeCode '('
parts.push @makeCode 'async ' if @isAsync
parts.push @makeCode 'function() {\n'
parts.push ...fragments
parts.push @makeCode '\n}).call(this);\n'
[].concat ...parts
initializeScope: (o) ->
o.scope = new Scope null, @body, null, o.referencedVars ? []
@ -4782,7 +4790,7 @@ exports.Op = class Op extends Base
compileContinuation: (o) ->
parts = []
op = @operator
@checkContinuation o
@checkContinuation o unless @isAwait()
if 'expression' in Object.keys(@first) and not (@first instanceof Throw)
parts.push @first.expression.compileToFragments o, LEVEL_OP if @first.expression?
else
@ -4817,7 +4825,7 @@ exports.Op = class Op extends Base
@error 'delete operand may not be argument or var'
astNode: (o) ->
@checkContinuation o if @isYield() or @isAwait()
@checkContinuation o if @isYield()
@checkDeleteOperand o
super o

View file

@ -204,3 +204,17 @@ test "#3199: await multiline implicit object", ->
type: 'a'
msg: 'b'
eq undefined, y
test "top-level await", ->
eqJS 'await null', 'await null;'
test "top-level wrapper has correct async attribute", ->
starts = (code, prefix) ->
compiled = CoffeeScript.compile code
unless compiled.startsWith prefix
fail """Expected generated JavaScript to start with:
#{reset}#{prefix}#{red}
but instead it was:
#{reset}#{compiled}#{red}"""
starts 'await null', '(async function'
starts 'do -> await null', '(function'

View file

@ -1257,14 +1257,7 @@ test "CoffeeScript keywords cannot be used as local names in import list aliases
^^^^^^
'''
test "cannot have `await` outside a function", ->
assertErrorFormat '''
await 1
''', '''
[stdin]:1:1: error: await can only occur inside functions
await 1
^^^^^^^
'''
test "cannot have `await return` outside a function", ->
assertErrorFormat '''
await return
''', '''