Skip to content

Commit 21c2745

Browse files
committed
Fix class name emit in ES5
1 parent c6bf6a2 commit 21c2745

File tree

6 files changed

+44
-20
lines changed

6 files changed

+44
-20
lines changed

src/compiler/factory.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2937,6 +2937,28 @@ namespace ts {
29372937
);
29382938
}
29392939

2940+
/**
2941+
* Gets the internal name of a declaration. This is primarily used for declarations that can be
2942+
* referred to by name in the body of an ES5 class function body. An internal name will *never*
2943+
* be prefixed with an module or namespace export modifier like "exports." when emitted as an
2944+
* expression. An internal name will also *never* be renamed due to a collision with a block
2945+
* scoped variable.
2946+
*
2947+
* @param node The declaration.
2948+
* @param allowComments A value indicating whether comments may be emitted for the name.
2949+
* @param allowSourceMaps A value indicating whether source maps may be emitted for the name.
2950+
*/
2951+
export function getInternalName(node: Declaration, allowComments?: boolean, allowSourceMaps?: boolean) {
2952+
return getName(node, allowComments, allowSourceMaps, EmitFlags.LocalName | EmitFlags.InternalName);
2953+
}
2954+
2955+
/**
2956+
* Gets whether an identifier should only be referred to by its internal name.
2957+
*/
2958+
export function isInternalName(node: Identifier) {
2959+
return (getEmitFlags(node) & EmitFlags.InternalName) !== 0;
2960+
}
2961+
29402962
/**
29412963
* Gets the local name of a declaration. This is primarily used for declarations that can be
29422964
* referred to by name in the declaration's immediate scope (classes, enums, namespaces). A

src/compiler/transformers/es2015.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ namespace ts {
813813

814814
// Create a synthetic text range for the return statement.
815815
const closingBraceLocation = createTokenRange(skipTrivia(currentText, node.members.end), SyntaxKind.CloseBraceToken);
816-
const localName = getLocalName(node);
816+
const localName = getInternalName(node);
817817

818818
// The following partially-emitted expression exists purely to align our sourcemap
819819
// emit with the original emitter.
@@ -870,7 +870,7 @@ namespace ts {
870870
/*decorators*/ undefined,
871871
/*modifiers*/ undefined,
872872
/*asteriskToken*/ undefined,
873-
getDeclarationName(node),
873+
getInternalName(node),
874874
/*typeParameters*/ undefined,
875875
transformConstructorParameters(constructor, hasSynthesizedSuper),
876876
/*type*/ undefined,
@@ -3725,7 +3725,7 @@ namespace ts {
37253725
function substituteIdentifier(node: Identifier) {
37263726
// Only substitute the identifier if we have enabled substitutions for block-scoped
37273727
// bindings.
3728-
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) {
3728+
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) {
37293729
const original = getParseTreeNode(node, isIdentifier);
37303730
if (original && isNameOfDeclarationWithCollidingName(original)) {
37313731
return setTextRange(getGeneratedNameForNode(original), node);
@@ -3778,7 +3778,7 @@ namespace ts {
37783778
* @param node An Identifier node.
37793779
*/
37803780
function substituteExpressionIdentifier(node: Identifier): Identifier {
3781-
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) {
3781+
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) {
37823782
const declaration = resolver.getReferencedDeclarationWithCollidingName(node);
37833783
if (declaration) {
37843784
return setTextRange(getGeneratedNameForNode(declaration.name), node);
@@ -3802,8 +3802,9 @@ namespace ts {
38023802
}
38033803

38043804
function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) {
3805-
const expression = getLocalName(node);
3806-
return hasModifier(member, ModifierFlags.Static) ? expression : createPropertyAccess(expression, "prototype");
3805+
return hasModifier(member, ModifierFlags.Static)
3806+
? getLocalName(node)
3807+
: createPropertyAccess(getInternalName(node), "prototype");
38073808
}
38083809

38093810
function hasSynthesizedDefaultSuperCall(constructor: ConstructorDeclaration, hasExtendsClause: boolean) {

src/compiler/types.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,14 +3940,15 @@ namespace ts {
39403940
HelperName = 1 << 12,
39413941
ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
39423942
LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
3943-
Indented = 1 << 15, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
3944-
NoIndentation = 1 << 16, // Do not indent the node.
3945-
AsyncFunctionBody = 1 << 17,
3946-
ReuseTempVariableScope = 1 << 18, // Reuse the existing temp variable scope during emit.
3947-
CustomPrologue = 1 << 19, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
3948-
NoHoisting = 1 << 20, // Do not hoist this declaration in --module system
3949-
HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
3950-
Iterator = 1 << 22, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
3943+
InternalName = 1 << 15, // The name is internal to an ES5 class body function.
3944+
Indented = 1 << 16, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
3945+
NoIndentation = 1 << 17, // Do not indent the node.
3946+
AsyncFunctionBody = 1 << 18,
3947+
ReuseTempVariableScope = 1 << 19, // Reuse the existing temp variable scope during emit.
3948+
CustomPrologue = 1 << 20, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
3949+
NoHoisting = 1 << 21, // Do not hoist this declaration in --module system
3950+
HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
3951+
Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
39513952
}
39523953

39533954
export interface EmitHelper {

tests/baselines/reference/classDeclarationBlockScoping1.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ var C = (function () {
1515
}());
1616
{
1717
var C_1 = (function () {
18-
function C_1() {
18+
function C() {
1919
}
20-
return C_1;
20+
return C;
2121
}());
2222
}

tests/baselines/reference/classDeclarationBlockScoping2.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ function f() {
1919
var c1 = C;
2020
{
2121
var C_1 = (function () {
22-
function C_1() {
22+
function C() {
2323
}
24-
return C_1;
24+
return C;
2525
}());
2626
var c2 = C_1;
2727
}

tests/baselines/reference/localTypes1.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,9 @@ function f3(b) {
206206
}
207207
else {
208208
var A_1 = (function () {
209-
function A_1() {
209+
function A() {
210210
}
211-
return A_1;
211+
return A;
212212
}());
213213
var c = [new A_1()];
214214
c[0].x = E.B;

0 commit comments

Comments
 (0)