Avoid printing comments on static fields twice. (#47363)

* Avoid printing comments on static fields twice.

In TS4.4, when a transformer adds a static field with a synthetic
comment to a decorated class, TS prints the synthetic comment twice:

  @Decorator
  class MyClass {
    /* comment */ staticField = 'x';  // field and comment added by transformer
  }

Becomes:

  var MyClass = class MyClass {}
  /*comment*/
  /*comment*/
  MyClass.newField = "x";
  __decorate(MyClass, Decorator, ...)

This is because the classFields transformer calls `setOriginalNode(n,
propertyDeclaration)` on both the expression statement , and the
assignment expression contained in it, leading to the synthetic comment
appearing twice.

This change avoids the problem by explicitly deleting any synthetic
comments from the assignment expression created for static fields when
creating the expression statement containing the assignment. This allows
us to retain the information of the original node without printing the
synthetic comment twice.

* Update src/testRunner/unittests/transform.ts

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
This commit is contained in:
Martin Probst 2022-01-11 21:19:36 +01:00 committed by GitHub
parent 0f1496f354
commit faee7b3621
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 0 deletions

View File

@ -1335,6 +1335,11 @@ namespace ts {
setSourceMapRange(statement, moveRangePastModifiers(property));
setCommentRange(statement, property);
setOriginalNode(statement, property);
// `setOriginalNode` *copies* the `emitNode` from `property`, so now both
// `statement` and `expression` have a copy of the synthesized comments.
// Drop the comments from expression to avoid printing them twice.
setSyntheticLeadingComments(expression, undefined);
setSyntheticTrailingComments(expression, undefined);
statements.push(statement);
}
}

View File

@ -599,6 +599,63 @@ module MyModule {
}, exports => {
assert.equal(exports.stringLength, 5);
});
function addStaticFieldWithComment(context: TransformationContext) {
return (sourceFile: SourceFile): SourceFile => {
return visitNode(sourceFile, rootTransform, isSourceFile);
};
function rootTransform<T extends Node>(node: T): Node {
if (isClassLike(node)) {
const newMembers = [factory.createPropertyDeclaration(/* decorators */ undefined, [factory.createModifier(SyntaxKind.StaticKeyword)], "newField", /* questionOrExclamationToken */ undefined, /* type */ undefined, factory.createStringLiteral("x"))];
setSyntheticLeadingComments(newMembers[0], [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "comment", pos: -1, end: -1, hasTrailingNewLine: true }]);
return isClassDeclaration(node) ?
factory.updateClassDeclaration(
node, node.decorators,
/* modifierFlags */ undefined, node.name,
node.typeParameters, node.heritageClauses,
newMembers) :
factory.updateClassExpression(
node, node.decorators,
/* modifierFlags */ undefined, node.name,
node.typeParameters, node.heritageClauses,
newMembers);
}
return visitEachChild(node, rootTransform, context);
}
}
testBaseline("transformSyntheticCommentOnStaticFieldInClassDeclaration", () => {
return transpileModule(`
declare const Decorator: any;
@Decorator
class MyClass {
}
`, {
transformers: {
before: [addStaticFieldWithComment],
},
compilerOptions: {
target: ScriptTarget.ES2015,
newLine: NewLineKind.CarriageReturnLineFeed,
}
}).outputText;
});
testBaseline("transformSyntheticCommentOnStaticFieldInClassExpression", () => {
return transpileModule(`
const MyClass = class {
};
`, {
transformers: {
before: [addStaticFieldWithComment],
},
compilerOptions: {
target: ScriptTarget.ES2015,
newLine: NewLineKind.CarriageReturnLineFeed,
}
}).outputText;
});
});
}

View File

@ -0,0 +1,13 @@
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
let MyClass = class MyClass {
};
/*comment*/
MyClass.newField = "x";
MyClass = __decorate([
Decorator
], MyClass);

View File

@ -0,0 +1,6 @@
var _a;
const MyClass = (_a = class {
},
/*comment*/
_a.newField = "x",
_a);