@@ -1916,8 +1916,9 @@ func (p *parser) parseStringLiteral() js_ast.Expr {
1916
1916
}
1917
1917
1918
1918
type propertyOpts struct {
1919
- decorators []js_ast.Expr
1920
- decoratorScope *js_ast.Scope
1919
+ decorators []js_ast.Expr
1920
+ decoratorScope *js_ast.Scope
1921
+ decoratorContext decoratorContextFlags
1921
1922
1922
1923
asyncRange logger.Range
1923
1924
generatorRange logger.Range
@@ -2317,7 +2318,7 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op
2317
2318
yield = allowExpr
2318
2319
}
2319
2320
2320
- fn, hadBody := p.parseFn(nil, opts.classKeyword, fnOrArrowDataParse{
2321
+ fn, hadBody := p.parseFn(nil, opts.classKeyword, opts.decoratorContext, fnOrArrowDataParse{
2321
2322
needsAsyncLoc: key.Loc,
2322
2323
asyncRange: opts.asyncRange,
2323
2324
await: await,
@@ -2810,7 +2811,7 @@ func (p *parser) parseFnExpr(loc logger.Loc, isAsync bool, asyncRange logger.Ran
2810
2811
yield = allowExpr
2811
2812
}
2812
2813
2813
- fn, _ := p.parseFn(name, logger.Range{}, fnOrArrowDataParse{
2814
+ fn, _ := p.parseFn(name, logger.Range{}, 0, fnOrArrowDataParse{
2814
2815
needsAsyncLoc: loc,
2815
2816
asyncRange: asyncRange,
2816
2817
await: await,
@@ -3454,33 +3455,20 @@ func (p *parser) parsePrefix(level js_ast.L, errors *deferredErrors, flags exprF
3454
3455
return p.parseFnExpr(loc, false /* isAsync */, logger.Range{})
3455
3456
3456
3457
case js_lexer.TClass:
3457
- classKeyword := p.lexer.Range()
3458
- p.markSyntaxFeature(compat.Class, classKeyword)
3459
- p.lexer.Next()
3460
- var name *js_ast.LocRef
3458
+ return p.parseClassExpr(loc, nil)
3461
3459
3462
- p.pushScopeForParsePass(js_ast.ScopeClassName, loc)
3460
+ case js_lexer.TAt:
3461
+ // Parse decorators before class statements, which are potentially exported
3462
+ scopeIndex := len(p.scopesInOrder)
3463
+ decorators := p.parseDecorators(p.currentScope, logger.Range{}, decoratorBeforeClassExpr)
3463
3464
3464
- // Parse an optional class name
3465
- if p.lexer.Token == js_lexer.TIdentifier {
3466
- if nameText := p.lexer.Identifier.String; !p.options.ts.Parse || nameText != "implements" {
3467
- if p.fnOrArrowDataParse.await != allowIdent && nameText == "await" {
3468
- p.log.AddError(&p.tracker, p.lexer.Range(), "Cannot use \"await\" as an identifier here:")
3469
- }
3470
- name = &js_ast.LocRef{Loc: p.lexer.Loc(), Ref: p.newSymbol(js_ast.SymbolOther, nameText)}
3471
- p.lexer.Next()
3472
- }
3465
+ // "@decorator class {}"
3466
+ // "@decorator class Foo {}"
3467
+ if p.lexer.Token != js_lexer.TClass && p.lexer.Token != js_lexer.TExport {
3468
+ p.logDecoratorWithoutFollowingClassError(loc, scopeIndex)
3473
3469
}
3474
3470
3475
- // Even anonymous classes can have TypeScript type parameters
3476
- if p.options.ts.Parse {
3477
- p.skipTypeScriptTypeParameters(allowInOutVarianceAnnotations | allowConstModifier)
3478
- }
3479
-
3480
- class := p.parseClass(classKeyword, name, parseClassOpts{})
3481
-
3482
- p.popScope()
3483
- return js_ast.Expr{Loc: loc, Data: &js_ast.EClass{Class: class}}
3471
+ return p.parseClassExpr(loc, decorators)
3484
3472
3485
3473
case js_lexer.TNew:
3486
3474
p.lexer.Next()
@@ -5607,7 +5595,12 @@ func (p *parser) parseBinding() js_ast.Binding {
5607
5595
return js_ast.Binding{}
5608
5596
}
5609
5597
5610
- func (p *parser) parseFn(name *js_ast.LocRef, classKeyword logger.Range, data fnOrArrowDataParse) (fn js_ast.Fn, hadBody bool) {
5598
+ func (p *parser) parseFn(
5599
+ name *js_ast.LocRef,
5600
+ classKeyword logger.Range,
5601
+ decoratorContext decoratorContextFlags,
5602
+ data fnOrArrowDataParse,
5603
+ ) (fn js_ast.Fn, hadBody bool) {
5611
5604
fn.Name = name
5612
5605
fn.HasRestArg = false
5613
5606
fn.IsAsync = data.await == allowExpr
@@ -5703,12 +5696,10 @@ func (p *parser) parseFn(name *js_ast.LocRef, classKeyword logger.Range, data fn
5703
5696
p.fnOrArrowDataParse.needsAsyncLoc = oldFnOrArrowData.needsAsyncLoc
5704
5697
}
5705
5698
5706
- decorators = p.parseDecorators(data.decoratorScope)
5699
+ decorators = p.parseDecorators(data.decoratorScope, classKeyword, decoratorContext|decoratorInFnArgs )
5707
5700
5708
5701
p.fnOrArrowDataParse.await = oldAwait
5709
5702
p.fnOrArrowDataParse.needsAsyncLoc = oldNeedsAsyncLoc
5710
- } else if classKeyword.Len > 0 {
5711
- p.logInvalidDecoratorError(classKeyword)
5712
5703
}
5713
5704
5714
5705
if !fn.HasRestArg && p.lexer.Token == js_lexer.TDotDotDot {
@@ -5899,9 +5890,45 @@ func (p *parser) parseClassStmt(loc logger.Loc, opts parseStmtOpts) js_ast.Stmt
5899
5890
return js_ast.Stmt{Loc: loc, Data: &js_ast.SClass{Class: class, IsExport: opts.isExport}}
5900
5891
}
5901
5892
5893
+ func (p *parser) parseClassExpr(loc logger.Loc, decorators []js_ast.Expr) js_ast.Expr {
5894
+ classKeyword := p.lexer.Range()
5895
+ p.markSyntaxFeature(compat.Class, classKeyword)
5896
+ p.lexer.Next()
5897
+ var name *js_ast.LocRef
5898
+
5899
+ opts := parseClassOpts{
5900
+ decorators: decorators,
5901
+ decoratorScope: p.currentScope,
5902
+ decoratorContext: decoratorInClassExpr,
5903
+ }
5904
+ p.pushScopeForParsePass(js_ast.ScopeClassName, loc)
5905
+
5906
+ // Parse an optional class name
5907
+ if p.lexer.Token == js_lexer.TIdentifier {
5908
+ if nameText := p.lexer.Identifier.String; !p.options.ts.Parse || nameText != "implements" {
5909
+ if p.fnOrArrowDataParse.await != allowIdent && nameText == "await" {
5910
+ p.log.AddError(&p.tracker, p.lexer.Range(), "Cannot use \"await\" as an identifier here:")
5911
+ }
5912
+ name = &js_ast.LocRef{Loc: p.lexer.Loc(), Ref: p.newSymbol(js_ast.SymbolOther, nameText)}
5913
+ p.lexer.Next()
5914
+ }
5915
+ }
5916
+
5917
+ // Even anonymous classes can have TypeScript type parameters
5918
+ if p.options.ts.Parse {
5919
+ p.skipTypeScriptTypeParameters(allowInOutVarianceAnnotations | allowConstModifier)
5920
+ }
5921
+
5922
+ class := p.parseClass(classKeyword, name, opts)
5923
+
5924
+ p.popScope()
5925
+ return js_ast.Expr{Loc: loc, Data: &js_ast.EClass{Class: class}}
5926
+ }
5927
+
5902
5928
type parseClassOpts struct {
5903
5929
decorators []js_ast.Expr
5904
5930
decoratorScope *js_ast.Scope
5931
+ decoratorContext decoratorContextFlags
5905
5932
isTypeScriptDeclare bool
5906
5933
}
5907
5934
@@ -5951,10 +5978,11 @@ func (p *parser) parseClass(classKeyword logger.Range, name *js_ast.LocRef, clas
5951
5978
scopeIndex := p.pushScopeForParsePass(js_ast.ScopeClassBody, bodyLoc)
5952
5979
5953
5980
opts := propertyOpts{
5954
- isClass: true,
5955
- decoratorScope: classOpts.decoratorScope,
5956
- classHasExtends: extendsOrNil.Data != nil,
5957
- classKeyword: classKeyword,
5981
+ isClass: true,
5982
+ decoratorScope: classOpts.decoratorScope,
5983
+ decoratorContext: classOpts.decoratorContext,
5984
+ classHasExtends: extendsOrNil.Data != nil,
5985
+ classKeyword: classKeyword,
5958
5986
}
5959
5987
hasConstructor := false
5960
5988
@@ -5966,12 +5994,7 @@ func (p *parser) parseClass(classKeyword logger.Range, name *js_ast.LocRef, clas
5966
5994
5967
5995
// Parse decorators for this property
5968
5996
firstDecoratorLoc := p.lexer.Loc()
5969
- if opts.decoratorScope != nil {
5970
- opts.decorators = p.parseDecorators(opts.decoratorScope)
5971
- } else {
5972
- opts.decorators = nil
5973
- p.logInvalidDecoratorError(classKeyword)
5974
- }
5997
+ opts.decorators = p.parseDecorators(opts.decoratorScope, classKeyword, opts.decoratorContext)
5975
5998
5976
5999
// This property may turn out to be a type in TypeScript, which should be ignored
5977
6000
if property, ok := p.parseProperty(p.saveExprCommentsHere(), js_ast.PropertyNormal, opts, nil); ok {
@@ -5981,7 +6004,7 @@ func (p *parser) parseClass(classKeyword logger.Range, name *js_ast.LocRef, clas
5981
6004
if key, ok := property.Key.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(key.Value, "constructor") {
5982
6005
if len(opts.decorators) > 0 {
5983
6006
p.log.AddError(&p.tracker, logger.Range{Loc: firstDecoratorLoc},
5984
- "TypeScript does not allow decorators on class constructors")
6007
+ "Decorators are not allowed on class constructors")
5985
6008
}
5986
6009
if property.Flags.Has(js_ast.PropertyIsMethod) && !property.Flags.Has(js_ast.PropertyIsStatic) && !property.Flags.Has(js_ast.PropertyIsComputed) {
5987
6010
if hasConstructor {
@@ -6171,7 +6194,7 @@ func (p *parser) parseFnStmt(loc logger.Loc, opts parseStmtOpts, isAsync bool, a
6171
6194
yield = allowExpr
6172
6195
}
6173
6196
6174
- fn, hadBody := p.parseFn(name, logger.Range{}, fnOrArrowDataParse{
6197
+ fn, hadBody := p.parseFn(name, logger.Range{}, 0, fnOrArrowDataParse{
6175
6198
needsAsyncLoc: loc,
6176
6199
asyncRange: asyncRange,
6177
6200
await: await,
@@ -6234,35 +6257,55 @@ type deferredDecorators struct {
6234
6257
firstAtLoc logger.Loc
6235
6258
}
6236
6259
6237
- func (p *parser) parseDecorators(decoratorScope *js_ast.Scope) []js_ast.Expr {
6238
- var decorators []js_ast.Expr
6239
-
6240
- if p.options.ts.Parse {
6241
- // TypeScript decorators cause us to temporarily revert to the scope that
6242
- // encloses the class declaration, since that's where the generated code
6243
- // for TypeScript decorators will be inserted.
6244
- oldScope := p.currentScope
6245
- p.currentScope = decoratorScope
6260
+ type decoratorContextFlags uint8
6246
6261
6247
- for p.lexer.Token == js_lexer.TAt {
6248
- p.lexer.Next()
6262
+ const (
6263
+ decoratorBeforeClassExpr = 1 << iota
6264
+ decoratorInClassExpr
6265
+ decoratorInFnArgs
6266
+ )
6249
6267
6250
- // Parse a new/call expression with "exprFlagDecorator" so we ignore
6251
- // EIndex expressions, since they may be part of a computed property:
6252
- //
6253
- // class Foo {
6254
- // @foo ['computed']() {}
6255
- // }
6256
- //
6257
- // This matches the behavior of the TypeScript compiler.
6258
- value := p.parseExprWithFlags(js_ast.LNew, exprFlagDecorator)
6259
- decorators = append(decorators, value)
6268
+ func (p *parser) parseDecorators(decoratorScope *js_ast.Scope, classKeyword logger.Range, context decoratorContextFlags) (decorators []js_ast.Expr) {
6269
+ if p.lexer.Token == js_lexer.TAt {
6270
+ if p.options.ts.Parse {
6271
+ if (context & decoratorInClassExpr) != 0 {
6272
+ p.lexer.AddRangeErrorWithNotes(p.lexer.Range(), "Experimental decorators can only be used with class declarations in TypeScript",
6273
+ []logger.MsgData{p.tracker.MsgData(classKeyword, "This is a class expression, not a class declaration:")})
6274
+ } else if (context & decoratorBeforeClassExpr) != 0 {
6275
+ p.log.AddError(&p.tracker, p.lexer.Range(), "Experimental decorators cannot be used in expression position in TypeScript")
6276
+ }
6277
+ } else {
6278
+ if (context & decoratorInFnArgs) != 0 {
6279
+ p.log.AddError(&p.tracker, p.lexer.Range(), "Parameter decorators are not allowed in JavaScript")
6280
+ } else {
6281
+ p.log.AddError(&p.tracker, p.lexer.Range(), "JavaScript decorators are not currently supported")
6282
+ }
6260
6283
}
6284
+ }
6261
6285
6262
- // Avoid "popScope" because this decorator scope is not hierarchical
6263
- p.currentScope = oldScope
6286
+ // TypeScript decorators cause us to temporarily revert to the scope that
6287
+ // encloses the class declaration, since that's where the generated code
6288
+ // for TypeScript decorators will be inserted.
6289
+ oldScope := p.currentScope
6290
+ p.currentScope = decoratorScope
6291
+
6292
+ for p.lexer.Token == js_lexer.TAt {
6293
+ p.lexer.Next()
6294
+
6295
+ // Parse a new/call expression with "exprFlagDecorator" so we ignore
6296
+ // EIndex expressions, since they may be part of a computed property:
6297
+ //
6298
+ // class Foo {
6299
+ // @foo ['computed']() {}
6300
+ // }
6301
+ //
6302
+ // This matches the behavior of the TypeScript compiler.
6303
+ value := p.parseExprWithFlags(js_ast.LNew, exprFlagDecorator)
6304
+ decorators = append(decorators, value)
6264
6305
}
6265
6306
6307
+ // Avoid "popScope" because this decorator scope is not hierarchical
6308
+ p.currentScope = oldScope
6266
6309
return decorators
6267
6310
}
6268
6311
@@ -6636,45 +6679,40 @@ func (p *parser) parseStmt(opts parseStmtOpts) js_ast.Stmt {
6636
6679
6637
6680
case js_lexer.TAt:
6638
6681
// Parse decorators before class statements, which are potentially exported
6639
- if p.options.ts.Parse {
6640
- scopeIndex := len(p.scopesInOrder)
6641
- decorators := p.parseDecorators(p.currentScope)
6682
+ scopeIndex := len(p.scopesInOrder)
6683
+ decorators := p.parseDecorators(p.currentScope, logger.Range{}, 0)
6642
6684
6643
- // If this turns out to be a "declare class" statement, we need to undo the
6644
- // scopes that were potentially pushed while parsing the decorator arguments.
6645
- // That can look like any one of the following:
6646
- //
6647
- // "@decorator declare class Foo {}"
6648
- // "@decorator declare abstract class Foo {}"
6649
- // "@decorator export declare class Foo {}"
6650
- // "@decorator export declare abstract class Foo {}"
6651
- //
6652
- opts.decorators = &deferredDecorators{
6653
- firstAtLoc: loc,
6654
- values: decorators,
6655
- scopeIndex: scopeIndex,
6656
- }
6657
-
6658
- // "@decorator class Foo {}"
6659
- // "@decorator abstract class Foo {}"
6660
- // "@decorator declare class Foo {}"
6661
- // "@decorator declare abstract class Foo {}"
6662
- // "@decorator export class Foo {}"
6663
- // "@decorator export abstract class Foo {}"
6664
- // "@decorator export declare class Foo {}"
6665
- // "@decorator export declare abstract class Foo {}"
6666
- // "@decorator export default class Foo {}"
6667
- // "@decorator export default abstract class Foo {}"
6668
- if p.lexer.Token != js_lexer.TClass && p.lexer.Token != js_lexer.TExport &&
6669
- !p.lexer.IsContextualKeyword("abstract") && !p.lexer.IsContextualKeyword("declare") {
6670
- p.logDecoratorWithoutFollowingClassError(opts.decorators.firstAtLoc, opts.decorators.scopeIndex)
6671
- }
6685
+ // If this turns out to be a "declare class" statement, we need to undo the
6686
+ // scopes that were potentially pushed while parsing the decorator arguments.
6687
+ // That can look like any one of the following:
6688
+ //
6689
+ // "@decorator declare class Foo {}"
6690
+ // "@decorator declare abstract class Foo {}"
6691
+ // "@decorator export declare class Foo {}"
6692
+ // "@decorator export declare abstract class Foo {}"
6693
+ //
6694
+ opts.decorators = &deferredDecorators{
6695
+ firstAtLoc: loc,
6696
+ values: decorators,
6697
+ scopeIndex: scopeIndex,
6698
+ }
6672
6699
6673
- return p.parseStmt(opts)
6700
+ // "@decorator class Foo {}"
6701
+ // "@decorator abstract class Foo {}"
6702
+ // "@decorator declare class Foo {}"
6703
+ // "@decorator declare abstract class Foo {}"
6704
+ // "@decorator export class Foo {}"
6705
+ // "@decorator export abstract class Foo {}"
6706
+ // "@decorator export declare class Foo {}"
6707
+ // "@decorator export declare abstract class Foo {}"
6708
+ // "@decorator export default class Foo {}"
6709
+ // "@decorator export default abstract class Foo {}"
6710
+ if p.lexer.Token != js_lexer.TClass && p.lexer.Token != js_lexer.TExport &&
6711
+ (!p.options.ts.Parse || (!p.lexer.IsContextualKeyword("abstract") && !p.lexer.IsContextualKeyword("declare"))) {
6712
+ p.logDecoratorWithoutFollowingClassError(opts.decorators.firstAtLoc, opts.decorators.scopeIndex)
6674
6713
}
6675
6714
6676
- p.lexer.Unexpected()
6677
- return js_ast.Stmt{}
6715
+ return p.parseStmt(opts)
6678
6716
6679
6717
case js_lexer.TClass:
6680
6718
if opts.lexicalDecl != lexicalDeclAllowAll {
0 commit comments