Skip to content

Commit 3e76fb5

Browse files
authored
Fix CJS local binding emit for ES decorators (#53725)
1 parent 926c6f1 commit 3e76fb5

11 files changed

+268
-5
lines changed

src/compiler/transformers/esDecorators.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
getAllDecoratorsOfClassElement,
4747
getCommentRange,
4848
getEffectiveBaseTypeNode,
49+
getEmitScriptTarget,
4950
getFirstConstructorWithBody,
5051
getHeritageClause,
5152
getNonAssignmentOperatorForCompoundAssignment,
@@ -279,6 +280,9 @@ export function transformESDecorators(context: TransformationContext): (x: Sourc
279280
hoistVariableDeclaration,
280281
} = context;
281282

283+
const compilerOptions = context.getCompilerOptions();
284+
const languageVersion = getEmitScriptTarget(compilerOptions);
285+
282286
let top: LexicalEnvironmentStackEntry | undefined;
283287
let classInfo: ClassInfo | undefined;
284288
let classThis: Identifier | undefined;
@@ -1003,7 +1007,12 @@ export function transformESDecorators(context: TransformationContext): (x: Sourc
10031007
Debug.assertIsDefined(node.name, "A class declaration that is not a default export must have a name.");
10041008
const iife = transformClassLike(node, factory.createStringLiteralFromNode(node.name));
10051009
const modifiers = visitNodes(node.modifiers, modifierVisitor, isModifier);
1006-
const varDecl = factory.createVariableDeclaration(node.name, /*exclamationToken*/ undefined, /*type*/ undefined, iife);
1010+
// When we transform to ES5/3 this will be moved inside an IIFE and should reference the name
1011+
// without any block-scoped variable collision handling
1012+
const declName = languageVersion <= ScriptTarget.ES2015 ?
1013+
factory.getInternalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true) :
1014+
factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
1015+
const varDecl = factory.createVariableDeclaration(declName, /*exclamationToken*/ undefined, /*type*/ undefined, iife);
10071016
const varDecls = factory.createVariableDeclarationList([varDecl], NodeFlags.Let);
10081017
const statement = factory.createVariableStatement(modifiers, varDecls);
10091018
setOriginalNode(statement, node);

tests/baselines/reference/esDecorators-classDeclaration-commentPreservation(module=commonjs,target=es2015).js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ let C = (() => {
208208
Object.defineProperty(exports, "__esModule", { value: true });
209209
exports.D = void 0;
210210
/*34*/
211-
exports.D = (() => {
211+
let D = exports.D = (() => {
212212
let _classDecorators = [dec, dec];
213213
let _classDescriptor;
214214
let _classExtraInitializers = [];
@@ -243,7 +243,7 @@ exports.default = (() => {
243243
Object.defineProperty(exports, "__esModule", { value: true });
244244
exports.F = void 0;
245245
/*40*/
246-
exports.F = (() => {
246+
let F = exports.F = (() => {
247247
let _classDecorators = [dec, dec];
248248
let _classDescriptor;
249249
let _classExtraInitializers = [];

tests/baselines/reference/esDecorators-classDeclaration-commentPreservation(module=commonjs,target=es2022).js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ let C = (() => {
205205
Object.defineProperty(exports, "__esModule", { value: true });
206206
exports.D = void 0;
207207
/*34*/
208-
exports.D = (() => {
208+
let D = exports.D = (() => {
209209
let _classDecorators = [dec, dec];
210210
let _classDescriptor;
211211
let _classExtraInitializers = [];
@@ -238,7 +238,7 @@ exports.default = (() => {
238238
Object.defineProperty(exports, "__esModule", { value: true });
239239
exports.F = void 0;
240240
/*40*/
241-
exports.F = (() => {
241+
let F = exports.F = (() => {
242242
let _classDecorators = [dec, dec];
243243
let _classDescriptor;
244244
let _classExtraInitializers = [];
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//// [esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts]
2+
declare var deco: any;
3+
4+
@deco
5+
export class Example {
6+
static foo() {}
7+
}
8+
9+
export namespace Example {
10+
export const x = 1;
11+
}
12+
13+
Example.foo();
14+
15+
//// [esDecorators-classDeclaration-commonjs-classNamespaceMerge.js]
16+
"use strict";
17+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
18+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
19+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
20+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
21+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
22+
var _, done = false;
23+
for (var i = decorators.length - 1; i >= 0; i--) {
24+
var context = {};
25+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
26+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
27+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
28+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
29+
if (kind === "accessor") {
30+
if (result === void 0) continue;
31+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
32+
if (_ = accept(result.get)) descriptor.get = _;
33+
if (_ = accept(result.set)) descriptor.set = _;
34+
if (_ = accept(result.init)) initializers.push(_);
35+
}
36+
else if (_ = accept(result)) {
37+
if (kind === "field") initializers.push(_);
38+
else descriptor[key] = _;
39+
}
40+
}
41+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
42+
done = true;
43+
};
44+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
45+
var useValue = arguments.length > 2;
46+
for (var i = 0; i < initializers.length; i++) {
47+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
48+
}
49+
return useValue ? value : void 0;
50+
};
51+
Object.defineProperty(exports, "__esModule", { value: true });
52+
exports.Example = void 0;
53+
let Example = exports.Example = (() => {
54+
let _classDecorators = [deco];
55+
let _classDescriptor;
56+
let _classExtraInitializers = [];
57+
let _classThis;
58+
var Example = class {
59+
static {
60+
__esDecorate(null, _classDescriptor = { value: this }, _classDecorators, { kind: "class", name: this.name }, null, _classExtraInitializers);
61+
Example = _classThis = _classDescriptor.value;
62+
__runInitializers(_classThis, _classExtraInitializers);
63+
}
64+
static foo() { }
65+
};
66+
return Example = _classThis;
67+
})();
68+
(function (Example) {
69+
Example.x = 1;
70+
})(Example = exports.Example || (exports.Example = {}));
71+
Example.foo();
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/esDecorators/classDeclaration/esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts ===
2+
declare var deco: any;
3+
>deco : Symbol(deco, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 0, 11))
4+
5+
@deco
6+
>deco : Symbol(deco, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 0, 11))
7+
8+
export class Example {
9+
>Example : Symbol(Example, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 0, 22), Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 5, 1))
10+
11+
static foo() {}
12+
>foo : Symbol(Example.foo, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 3, 22))
13+
}
14+
15+
export namespace Example {
16+
>Example : Symbol(Example, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 0, 22), Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 5, 1))
17+
18+
export const x = 1;
19+
>x : Symbol(x, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 8, 16))
20+
}
21+
22+
Example.foo();
23+
>Example.foo : Symbol(Example.foo, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 3, 22))
24+
>Example : Symbol(Example, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 0, 22), Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 5, 1))
25+
>foo : Symbol(Example.foo, Decl(esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts, 3, 22))
26+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/conformance/esDecorators/classDeclaration/esDecorators-classDeclaration-commonjs-classNamespaceMerge.ts ===
2+
declare var deco: any;
3+
>deco : any
4+
5+
@deco
6+
>deco : any
7+
8+
export class Example {
9+
>Example : Example
10+
11+
static foo() {}
12+
>foo : () => void
13+
}
14+
15+
export namespace Example {
16+
>Example : typeof Example
17+
18+
export const x = 1;
19+
>x : 1
20+
>1 : 1
21+
}
22+
23+
Example.foo();
24+
>Example.foo() : void
25+
>Example.foo : () => void
26+
>Example : typeof Example
27+
>foo : () => void
28+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [esDecorators-classDeclaration-commonjs.ts]
2+
declare var deco: any;
3+
4+
@deco
5+
export class Example {
6+
static foo() {}
7+
}
8+
9+
Example.foo();
10+
11+
//// [esDecorators-classDeclaration-commonjs.js]
12+
"use strict";
13+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
14+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
15+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
16+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
17+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
18+
var _, done = false;
19+
for (var i = decorators.length - 1; i >= 0; i--) {
20+
var context = {};
21+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
22+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
23+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
24+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
25+
if (kind === "accessor") {
26+
if (result === void 0) continue;
27+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
28+
if (_ = accept(result.get)) descriptor.get = _;
29+
if (_ = accept(result.set)) descriptor.set = _;
30+
if (_ = accept(result.init)) initializers.push(_);
31+
}
32+
else if (_ = accept(result)) {
33+
if (kind === "field") initializers.push(_);
34+
else descriptor[key] = _;
35+
}
36+
}
37+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
38+
done = true;
39+
};
40+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
41+
var useValue = arguments.length > 2;
42+
for (var i = 0; i < initializers.length; i++) {
43+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
44+
}
45+
return useValue ? value : void 0;
46+
};
47+
Object.defineProperty(exports, "__esModule", { value: true });
48+
exports.Example = void 0;
49+
let Example = exports.Example = (() => {
50+
let _classDecorators = [deco];
51+
let _classDescriptor;
52+
let _classExtraInitializers = [];
53+
let _classThis;
54+
var Example = class {
55+
static {
56+
__esDecorate(null, _classDescriptor = { value: this }, _classDecorators, { kind: "class", name: this.name }, null, _classExtraInitializers);
57+
Example = _classThis = _classDescriptor.value;
58+
__runInitializers(_classThis, _classExtraInitializers);
59+
}
60+
static foo() { }
61+
};
62+
return Example = _classThis;
63+
})();
64+
Example.foo();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/esDecorators/classDeclaration/esDecorators-classDeclaration-commonjs.ts ===
2+
declare var deco: any;
3+
>deco : Symbol(deco, Decl(esDecorators-classDeclaration-commonjs.ts, 0, 11))
4+
5+
@deco
6+
>deco : Symbol(deco, Decl(esDecorators-classDeclaration-commonjs.ts, 0, 11))
7+
8+
export class Example {
9+
>Example : Symbol(Example, Decl(esDecorators-classDeclaration-commonjs.ts, 0, 22))
10+
11+
static foo() {}
12+
>foo : Symbol(Example.foo, Decl(esDecorators-classDeclaration-commonjs.ts, 3, 22))
13+
}
14+
15+
Example.foo();
16+
>Example.foo : Symbol(Example.foo, Decl(esDecorators-classDeclaration-commonjs.ts, 3, 22))
17+
>Example : Symbol(Example, Decl(esDecorators-classDeclaration-commonjs.ts, 0, 22))
18+
>foo : Symbol(Example.foo, Decl(esDecorators-classDeclaration-commonjs.ts, 3, 22))
19+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/conformance/esDecorators/classDeclaration/esDecorators-classDeclaration-commonjs.ts ===
2+
declare var deco: any;
3+
>deco : any
4+
5+
@deco
6+
>deco : any
7+
8+
export class Example {
9+
>Example : Example
10+
11+
static foo() {}
12+
>foo : () => void
13+
}
14+
15+
Example.foo();
16+
>Example.foo() : void
17+
>Example.foo : () => void
18+
>Example : typeof Example
19+
>foo : () => void
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @target: es2022
2+
// @module: commonjs
3+
4+
declare var deco: any;
5+
6+
@deco
7+
export class Example {
8+
static foo() {}
9+
}
10+
11+
export namespace Example {
12+
export const x = 1;
13+
}
14+
15+
Example.foo();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @target: es2022
2+
// @module: commonjs
3+
4+
declare var deco: any;
5+
6+
@deco
7+
export class Example {
8+
static foo() {}
9+
}
10+
11+
Example.foo();

0 commit comments

Comments
 (0)