Skip to content

Commit

Permalink
decorators: printing preserves newline-tail status
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Nov 24, 2023
1 parent 7edc83d commit 7383d0d
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 201 deletions.
18 changes: 6 additions & 12 deletions internal/bundler_tests/snapshots/snapshots_dce.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,30 +412,24 @@ var fn = () => {
var Class = @fn class {
};
var Field = class {
@fn
field;
@fn field;
};
var Method = class {
@fn
method() {
@fn method() {
}
};
var Accessor = class {
@fn
accessor accessor;
@fn accessor accessor;
};
var StaticField = class {
@fn
static field;
@fn static field;
};
var StaticMethod = class {
@fn
static method() {
@fn static method() {
}
};
var StaticAccessor = class {
@fn
static accessor accessor;
@fn static accessor accessor;
};

================================================================================
Expand Down
56 changes: 16 additions & 40 deletions internal/bundler_tests/snapshots/snapshots_default.txt
Original file line number Diff line number Diff line change
Expand Up @@ -928,41 +928,29 @@ _ = class {
#bar;
classes = [
class {
@import_somewhere.imported
@((0, import_somewhere.imported)())
imported;
@import_somewhere.imported @((0, import_somewhere.imported)()) imported;
},
class {
@unbound
@unbound()
unbound;
@unbound @unbound() unbound;
},
class {
@(123)
@(123())
constant;
@(123) @(123()) constant;
},
class {
@(void 0)
@((void 0)())
undef;
@(void 0) @((void 0)()) undef;
},
class {
@(element[access])
indexed;
@(element[access]) indexed;
},
class {
@foo.#bar
private;
@foo.#bar private;
},
class {
@(foo["ヿ"])
unicode;
@(foo["ヿ"]) unicode;
},
class {
@(() => {
})
arrow;
}) arrow;
}
];
};
Expand All @@ -976,41 +964,29 @@ _ = class {
#bar;
classes = [
class {
@imported
@imported()
imported;
@imported @imported() imported;
},
class {
@unbound
@unbound()
unbound;
@unbound @unbound() unbound;
},
class {
@(123)
@(123())
constant;
@(123) @(123()) constant;
},
class {
@(void 0)
@((void 0)())
undef;
@(void 0) @((void 0)()) undef;
},
class {
@(element[access])
indexed;
@(element[access]) indexed;
},
class {
@foo.#bar
private;
@foo.#bar private;
},
class {
@(foo["ヿ"])
unicode;
@(foo["ヿ"]) unicode;
},
class {
@(() => {
})
arrow;
}) arrow;
}
];
};
Expand Down
24 changes: 6 additions & 18 deletions internal/bundler_tests/snapshots/snapshots_lower.txt
Original file line number Diff line number Diff line change
Expand Up @@ -624,26 +624,14 @@ TestJavaScriptDecoratorsESNext
@x.y()
@(new y.x())
export default class Foo {
@x
@y
mUndef;
@x
@y
mDef = 1;
@x
@y
method() {
@x @y mUndef;
@x @y mDef = 1;
@x @y method() {
return new Foo();
}
@x
@y
static sUndef;
@x
@y
static sDef = new Foo();
@x
@y
static sMethod() {
@x @y static sUndef;
@x @y static sDef = new Foo();
@x @y static sMethod() {
return new Foo();
}
}
Expand Down
80 changes: 20 additions & 60 deletions internal/bundler_tests/snapshots/snapshots_ts.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1178,74 +1178,34 @@ TestTSExperimentalDecoratorsNoConfig
---------- /out.js ----------
// entry.ts
var Foo = @x.y() @(new y.x()) class _Foo {
@x
@y
mUndef;
@x
@y
mDef = 1;
@x
@y
method() {
@x @y mUndef;
@x @y mDef = 1;
@x @y method() {
return new _Foo();
}
@x
@y
accessor aUndef;
@x
@y
accessor aDef = 1;
@x
@y
static sUndef;
@x
@y
static sDef = new _Foo();
@x
@y
static sMethod() {
@x @y accessor aUndef;
@x @y accessor aDef = 1;
@x @y static sUndef;
@x @y static sDef = new _Foo();
@x @y static sMethod() {
return new _Foo();
}
@x
@y
static accessor asUndef;
@x
@y
static accessor asDef = 1;
@x
@y
#mUndef;
@x
@y
#mDef = 1;
@x
@y
#method() {
@x @y static accessor asUndef;
@x @y static accessor asDef = 1;
@x @y #mUndef;
@x @y #mDef = 1;
@x @y #method() {
return new _Foo();
}
@x
@y
accessor #aUndef;
@x
@y
accessor #aDef = 1;
@x
@y
static #sUndef;
@x
@y
static #sDef = 1;
@x
@y
static #sMethod() {
@x @y accessor #aUndef;
@x @y accessor #aDef = 1;
@x @y static #sUndef;
@x @y static #sDef = 1;
@x @y static #sMethod() {
return new _Foo();
}
@x
@y
static accessor #asUndef;
@x
@y
static accessor #asDef = 1;
@x @y static accessor #asUndef;
@x @y static accessor #asDef = 1;
};
export {
Foo as default
Expand Down
5 changes: 3 additions & 2 deletions internal/js_ast/js_ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,9 @@ var OpTable = []OpTableEntry{
}

type Decorator struct {
Value Expr
AtLoc logger.Loc
Value Expr
AtLoc logger.Loc
OmitNewlineAfter bool
}

type PropertyKind uint8
Expand Down
6 changes: 5 additions & 1 deletion internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -6620,7 +6620,11 @@ func (p *parser) parseDecorators(decoratorScope *js_ast.Scope, classKeyword logg
// special parser that doesn't allow normal expressions (e.g. "?.").
value = p.parseDecorator()
}
decorators = append(decorators, js_ast.Decorator{Value: value, AtLoc: atLoc})
decorators = append(decorators, js_ast.Decorator{
Value: value,
AtLoc: atLoc,
OmitNewlineAfter: !p.lexer.HasNewlineBefore,
})
}

// Avoid "popScope" because this decorator scope is not hierarchical
Expand Down
46 changes: 23 additions & 23 deletions internal/js_parser/js_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2000,31 +2000,31 @@ func TestAutoAccessors(t *testing.T) {
}

func TestDecorators(t *testing.T) {
expectPrinted(t, "@x @y class Foo {}", "@x\n@y\nclass Foo {\n}\n")
expectPrinted(t, "@x @y export class Foo {}", "@x\n@y\nexport class Foo {\n}\n")
expectPrinted(t, "@x @y export default class Foo {}", "@x\n@y\nexport default class Foo {\n}\n")
expectPrinted(t, "@x @y class Foo {}", "@x @y class Foo {\n}\n")
expectPrinted(t, "@x @y export class Foo {}", "@x @y export class Foo {\n}\n")
expectPrinted(t, "@x @y export default class Foo {}", "@x @y export default class Foo {\n}\n")
expectPrinted(t, "_ = @x @y class {}", "_ = @x @y class {\n};\n")

expectPrinted(t, "class Foo { @x y }", "class Foo {\n @x\n y;\n}\n")
expectPrinted(t, "class Foo { @x y() {} }", "class Foo {\n @x\n y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x static y }", "class Foo {\n @x\n static y;\n}\n")
expectPrinted(t, "class Foo { @x static y() {} }", "class Foo {\n @x\n static y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x accessor y }", "class Foo {\n @x\n accessor y;\n}\n")
expectPrinted(t, "class Foo { @x y }", "class Foo {\n @x y;\n}\n")
expectPrinted(t, "class Foo { @x y() {} }", "class Foo {\n @x y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x static y }", "class Foo {\n @x static y;\n}\n")
expectPrinted(t, "class Foo { @x static y() {} }", "class Foo {\n @x static y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x accessor y }", "class Foo {\n @x accessor y;\n}\n")

expectPrinted(t, "class Foo { @x #y }", "class Foo {\n @x\n #y;\n}\n")
expectPrinted(t, "class Foo { @x #y() {} }", "class Foo {\n @x\n #y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x static #y }", "class Foo {\n @x\n static #y;\n}\n")
expectPrinted(t, "class Foo { @x static #y() {} }", "class Foo {\n @x\n static #y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x accessor #y }", "class Foo {\n @x\n accessor #y;\n}\n")
expectPrinted(t, "class Foo { @x #y }", "class Foo {\n @x #y;\n}\n")
expectPrinted(t, "class Foo { @x #y() {} }", "class Foo {\n @x #y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x static #y }", "class Foo {\n @x static #y;\n}\n")
expectPrinted(t, "class Foo { @x static #y() {} }", "class Foo {\n @x static #y() {\n }\n}\n")
expectPrinted(t, "class Foo { @x accessor #y }", "class Foo {\n @x accessor #y;\n}\n")

expectParseError(t, "class Foo { x(@y z) {} }", "<stdin>: ERROR: Parameter decorators are not allowed in JavaScript\n")
expectParseError(t, "class Foo { @x static {} }", "<stdin>: ERROR: Expected \";\" but found \"{\"\n")

expectPrinted(t, "@\na\n(\n)\n@\n(\nb\n)\nclass\nFoo\n{\n}\n", "@a()\n@b\nclass Foo {\n}\n")
expectPrinted(t, "@(a, b) class Foo {}\n", "@(a, b)\nclass Foo {\n}\n")
expectPrinted(t, "@x() class Foo {}", "@x()\nclass Foo {\n}\n")
expectPrinted(t, "@x.y() class Foo {}", "@x.y()\nclass Foo {\n}\n")
expectPrinted(t, "@(() => {}) class Foo {}", "@(() => {\n})\nclass Foo {\n}\n")
expectPrinted(t, "@(a, b) class Foo {}", "@(a, b) class Foo {\n}\n")
expectPrinted(t, "@x() class Foo {}", "@x() class Foo {\n}\n")
expectPrinted(t, "@x.y() class Foo {}", "@x.y() class Foo {\n}\n")
expectPrinted(t, "@(() => {}) class Foo {}", "@(() => {\n}) class Foo {\n}\n")
expectPrinted(t, "class Foo { #x = @y.#x.y.#x class {} }", "class Foo {\n #x = @y.#x.y.#x class {\n };\n}\n")
expectParseError(t, "@123 class Foo {}", "<stdin>: ERROR: Expected identifier but found \"123\"\n")
expectParseError(t, "@x[y] class Foo {}", "<stdin>: ERROR: Expected \";\" but found \"class\"\n")
Expand Down Expand Up @@ -2052,12 +2052,12 @@ func TestDecorators(t *testing.T) {
expectParseError(t, "@x abstract\nclass Foo {}", "<stdin>: ERROR: Decorators are not valid here\n")

// Check decorator locations in relation to the "export" keyword
expectPrinted(t, "@x export class Foo {}", "@x\nexport class Foo {\n}\n")
expectPrinted(t, "export @x class Foo {}", "@x\nexport class Foo {\n}\n")
expectPrinted(t, "@x export default class {}", "@x\nexport default class {\n}\n")
expectPrinted(t, "export default @x class {}", "@x\nexport default class {\n}\n")
expectPrinted(t, "@x export default class Foo {}", "@x\nexport default class Foo {\n}\n")
expectPrinted(t, "export default @x class Foo {}", "@x\nexport default class Foo {\n}\n")
expectPrinted(t, "@x export class Foo {}", "@x export class Foo {\n}\n")
expectPrinted(t, "export @x class Foo {}", "@x export class Foo {\n}\n")
expectPrinted(t, "@x export default class {}", "@x export default class {\n}\n")
expectPrinted(t, "export default @x class {}", "@x export default class {\n}\n")
expectPrinted(t, "@x export default class Foo {}", "@x export default class Foo {\n}\n")
expectPrinted(t, "export default @x class Foo {}", "@x export default class Foo {\n}\n")
expectPrinted(t, "export default (@x class {})", "export default (@x class {\n});\n")
expectPrinted(t, "export default (@x class Foo {})", "export default (@x class Foo {\n});\n")
expectParseError(t, "export @x default class {}", "<stdin>: ERROR: Unexpected \"default\"\n")
Expand Down
Loading

0 comments on commit 7383d0d

Please sign in to comment.