Skip to content

Commit 116c099

Browse files
committed
Add --metadataDecorator option
1 parent 11097c6 commit 116c099

File tree

112 files changed

+3642
-528
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+3642
-528
lines changed

src/compiler/checker.ts

Lines changed: 409 additions & 142 deletions
Large diffs are not rendered by default.

src/compiler/commandLineParser.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,21 @@ namespace ts {
872872
description: Diagnostics.Include_modules_imported_with_json_extension
873873
},
874874

875+
{
876+
name: "metadataDecorator",
877+
type: "string",
878+
category: Diagnostics.Advanced_Options,
879+
description: Diagnostics.Specify_the_name_of_the_metadata_decorator_function_to_use_when_emitDecoratorMetadata_is_set
880+
},
881+
{
882+
name: "metadataDecoratorImportSource",
883+
type: "string",
884+
affectsEmit: true,
885+
affectsModuleResolution: true,
886+
category: Diagnostics.Advanced_Options,
887+
description: Diagnostics.Specify_the_module_specifier_to_be_used_to_import_the_metadata_decorator_provided_by_metadataDecorator
888+
},
889+
875890
{
876891
name: "out",
877892
type: "string",

src/compiler/diagnosticMessages.json

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,19 @@
13601360
"category": "Error",
13611361
"code": 1432
13621362
},
1363+
"Unable to resolve signature of implicit decorator '{0}' when called as an expression.": {
1364+
"category": "Error",
1365+
"code": 1433
1366+
},
1367+
"Unable to resolve signature of implicit decorator '{0}' from module '{1}' when called as an expression.": {
1368+
"category": "Error",
1369+
"code": 1434
1370+
},
1371+
"Unable to import implicit decorator '{0}' from module '{1}' as this file is not a module.": {
1372+
"category": "Error",
1373+
"code": 1435
1374+
},
1375+
13631376

13641377
"The types of '{0}' are incompatible between these types.": {
13651378
"category": "Error",
@@ -3308,6 +3321,18 @@
33083321
"category": "Error",
33093322
"code": 2808
33103323
},
3324+
"Namespace '{0}' from module '{1}' has no exported member '{2}'.": {
3325+
"category": "Error",
3326+
"code": 2809
3327+
},
3328+
"'{0}' from module '{1}' has no exported member named '{2}'. Did you mean '{3}'?": {
3329+
"category": "Error",
3330+
"code": 2810
3331+
},
3332+
"Cannot find namespace '{0}'. Did you mean '{1}?": {
3333+
"category": "Error",
3334+
"code": 2811
3335+
},
33113336

33123337
"Import declaration '{0}' is using private name '{1}'.": {
33133338
"category": "Error",
@@ -3814,7 +3839,7 @@
38143839
"category": "Error",
38153840
"code": 5066
38163841
},
3817-
"Invalid value for 'jsxFactory'. '{0}' is not a valid identifier or qualified-name.": {
3842+
"Invalid value for '{0}'. '{1}' is not a valid identifier or qualified-name.": {
38183843
"category": "Error",
38193844
"code": 5067
38203845
},
@@ -4821,6 +4846,14 @@
48214846
"category": "Error",
48224847
"code": 6238
48234848
},
4849+
"Specify the name of the metadata decorator function to use when '--emitDecoratorMetadata' is set": {
4850+
"category": "Message",
4851+
"code": 6239
4852+
},
4853+
"Specify the module specifier to be used to import the metadata decorator provided by '--metadataDecorator'.": {
4854+
"category": "Message",
4855+
"code": 6240
4856+
},
48244857

48254858
"Projects to reference": {
48264859
"category": "Message",
@@ -6438,10 +6471,6 @@
64386471
"category": "Message",
64396472
"code": 18034
64406473
},
6441-
"Invalid value for 'jsxFragmentFactory'. '{0}' is not a valid identifier or qualified-name.": {
6442-
"category": "Error",
6443-
"code": 18035
6444-
},
64456474
"Class decorators can't be used with static private identifier. Consider removing the experimental decorator.": {
64466475
"category": "Error",
64476476
"code": 18036

src/compiler/factory/emitHelpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ namespace ts {
234234

235235
// ES2017 Helpers
236236

237-
function createAwaiterHelper(hasLexicalThis: boolean, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression | undefined, body: Block) {
237+
function createAwaiterHelper(hasLexicalThis: boolean, hasLexicalArguments: boolean, promiseConstructor: EntityNameOrEntityNameExpression | undefined, body: Block) {
238238
context.requestEmitHelper(awaiterHelper);
239239

240240
const generatorFunc = factory.createFunctionExpression(
@@ -256,7 +256,7 @@ namespace ts {
256256
[
257257
hasLexicalThis ? factory.createThis() : factory.createVoidZero(),
258258
hasLexicalArguments ? factory.createIdentifier("arguments") : factory.createVoidZero(),
259-
promiseConstructor ? createExpressionFromEntityName(factory, promiseConstructor) : factory.createVoidZero(),
259+
promiseConstructor ? isEntityName(promiseConstructor) ? createExpressionFromEntityName(factory, promiseConstructor) : promiseConstructor : factory.createVoidZero(),
260260
generatorFunc
261261
]
262262
);

src/compiler/factory/emitNode.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,24 @@ namespace ts {
259259
getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
260260
return node;
261261
}
262+
263+
/**
264+
* For a synthetic import, specifies the synthesized import reference so that the import can be resolved during subsequent transformations.
265+
*
266+
* @param node Imported identifier
267+
* @param importReference The `ImportSpecifier` to use for the reference, or `undefined`.
268+
*/
269+
/* @internal */
270+
export function setGeneratedImportReference<T extends Identifier>(node: T, importReference: ImportSpecifier | undefined) {
271+
getOrCreateEmitNode(node).generatedImportReference = importReference;
272+
return node;
273+
}
274+
275+
/**
276+
* For a synthetic import, gets any associated synthesized import reference.
277+
*/
278+
/* @internal */
279+
export function getGeneratedImportReference(node: Identifier) {
280+
return node.emitNode?.generatedImportReference;
281+
}
262282
}

src/compiler/factory/nodeFactory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ namespace ts {
427427
createUnparsedSyntheticReference,
428428
createInputFiles,
429429
createSyntheticExpression,
430+
createSyntheticCallExpression,
430431
createSyntaxList,
431432
createNotEmittedStatement,
432433
createPartiallyEmittedExpression,
@@ -5104,6 +5105,17 @@ namespace ts {
51045105
return node;
51055106
}
51065107

5108+
// @api
5109+
function createSyntheticCallExpression(thisArg: LeftHandSideExpression | SyntheticExpression | undefined, expression: Expression | SyntheticExpression, typeArguments: readonly TypeNode[] | undefined, argumentList: readonly Expression[], containingMessageChain?: () => DiagnosticMessageChain | undefined): SyntheticCallExpression {
5110+
const node = createBaseNode<SyntheticCallExpression>(SyntaxKind.SyntheticCallExpression);
5111+
node.thisArg = thisArg;
5112+
node.expression = expression;
5113+
node.typeArguments = typeArguments;
5114+
node.arguments = argumentList;
5115+
node.containingMessageChain = containingMessageChain;
5116+
return node;
5117+
}
5118+
51075119
// @api
51085120
function createSyntaxList(children: Node[]) {
51095121
const node = createBaseNode<SyntaxList>(SyntaxKind.SyntaxList);

src/compiler/factory/nodeTests.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,10 @@ namespace ts {
444444
return node.kind === SyntaxKind.CommaListExpression;
445445
}
446446

447+
export function isSyntheticCallExpression(node: Node): node is SyntheticCallExpression {
448+
return node.kind === SyntaxKind.SyntheticCallExpression;
449+
}
450+
447451
// Misc
448452

449453
export function isTemplateSpan(node: Node): node is TemplateSpan {
@@ -629,7 +633,7 @@ namespace ts {
629633
}
630634

631635
/* @internal */
632-
export function isSyntheticReference(node: Node): node is SyntheticReferenceExpression {
636+
export function isSyntheticReferenceExpression(node: Node): node is SyntheticReferenceExpression {
633637
return node.kind === SyntaxKind.SyntheticReferenceExpression;
634638
}
635639

src/compiler/factory/utilities.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,27 @@ namespace ts {
157157
}
158158
}
159159

160-
export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName | Expression): Expression {
160+
export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName, emulateParseTree = true): EntityNameExpression {
161161
if (isQualifiedName(node)) {
162-
const left = createExpressionFromEntityName(factory, node.left);
163-
// TODO(rbuckton): Does this need to be parented?
164-
const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent);
165-
return setTextRange(factory.createPropertyAccessExpression(left, right), node);
162+
const left = createExpressionFromEntityName(factory, node.left, emulateParseTree);
163+
const right = factory.cloneNode(node.right);
164+
if (emulateParseTree) {
165+
setTextRange(right, node.right);
166+
setParent(right, node.right.parent);
167+
}
168+
const expression = factory.createPropertyAccessExpression(left, right);
169+
if (emulateParseTree) {
170+
setTextRange(expression, node);
171+
}
172+
return expression as PropertyAccessEntityNameExpression;
166173
}
167174
else {
168-
// TODO(rbuckton): Does this need to be parented?
169-
return setParent(setTextRange(factory.cloneNode(node), node), node.parent);
175+
const name = factory.cloneNode(node);
176+
if (emulateParseTree) {
177+
setTextRange(name, node);
178+
setParent(name, node.parent);
179+
}
180+
return name;
170181
}
171182
}
172183

src/compiler/program.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,6 +2290,14 @@ namespace ts {
22902290
// synthesize `import "base/jsx-runtime"` declaration
22912291
(imports ||= []).push(createSyntheticImport(jsxImport, file));
22922292
}
2293+
if (file.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax &&
2294+
options.emitDecoratorMetadata &&
2295+
options.metadataDecorator) {
2296+
const metadataDecoratorImport = options.metadataDecoratorImportSource;
2297+
if (metadataDecoratorImport) {
2298+
(imports ||= []).push(createSyntheticImport(metadataDecoratorImport, file));
2299+
}
2300+
}
22932301
}
22942302

22952303
for (const node of file.statements) {
@@ -3244,6 +3252,30 @@ namespace ts {
32443252
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators");
32453253
}
32463254

3255+
if (options.metadataDecorator) {
3256+
if (!options.experimentalDecorators) {
3257+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "experimentalDecorators");
3258+
}
3259+
if (!options.emitDecoratorMetadata) {
3260+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "emitDecoratorMetadata");
3261+
}
3262+
if (!parseIsolatedEntityName(options.metadataDecorator, languageVersion)) {
3263+
createOptionValueDiagnostic("metadataDecorator", Diagnostics.Invalid_value_for_0_1_is_not_a_valid_identifier_or_qualified_name, "metadataDecorator", options.metadataDecorator);
3264+
}
3265+
}
3266+
3267+
if (options.metadataDecoratorImportSource) {
3268+
if (!options.experimentalDecorators) {
3269+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "experimentalDecorators");
3270+
}
3271+
if (!options.emitDecoratorMetadata) {
3272+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "emitDecoratorMetadata");
3273+
}
3274+
if (!options.metadataDecorator) {
3275+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecoratorImportSource", "metadataDecorator");
3276+
}
3277+
}
3278+
32473279
if (options.jsxFactory) {
32483280
if (options.reactNamespace) {
32493281
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory");
@@ -3252,7 +3284,7 @@ namespace ts {
32523284
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx));
32533285
}
32543286
if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
3255-
createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory);
3287+
createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_0_1_is_not_a_valid_identifier_or_qualified_name, "jsxFactory", options.jsxFactory);
32563288
}
32573289
}
32583290
else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
@@ -3267,7 +3299,7 @@ namespace ts {
32673299
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx));
32683300
}
32693301
if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
3270-
createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
3302+
createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_0_1_is_not_a_valid_identifier_or_qualified_name, "jsxFragmentFactory", options.jsxFragmentFactory);
32713303
}
32723304
}
32733305

@@ -3553,8 +3585,8 @@ namespace ts {
35533585
createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3);
35543586
}
35553587

3556-
function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string) {
3557-
createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
3588+
function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string, arg1?: string) {
3589+
createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0, arg1);
35583590
}
35593591

35603592
function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {

src/compiler/transformers/es2020.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ namespace ts {
2323
switch (node.kind) {
2424
case SyntaxKind.CallExpression: {
2525
const updated = visitNonOptionalCallExpression(node as CallExpression, /*captureThisArg*/ false);
26-
Debug.assertNotNode(updated, isSyntheticReference);
26+
Debug.assertNotNode(updated, isSyntheticReferenceExpression);
2727
return updated;
2828
}
2929
case SyntaxKind.PropertyAccessExpression:
3030
case SyntaxKind.ElementAccessExpression:
3131
if (isOptionalChain(node)) {
3232
const updated = visitOptionalExpression(node, /*captureThisArg*/ false, /*isDelete*/ false);
33-
Debug.assertNotNode(updated, isSyntheticReference);
33+
Debug.assertNotNode(updated, isSyntheticReferenceExpression);
3434
return updated;
3535
}
3636
return visitEachChild(node, visitor, context);
@@ -59,7 +59,7 @@ namespace ts {
5959

6060
function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean, isDelete: boolean): Expression {
6161
const expression = visitNonOptionalExpression(node.expression, captureThisArg, isDelete);
62-
if (isSyntheticReference(expression)) {
62+
if (isSyntheticReferenceExpression(expression)) {
6363
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
6464
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
6565
return factory.createSyntheticReferenceExpression(factory.updateParenthesizedExpression(node, expression.expression), expression.thisArg);
@@ -74,7 +74,7 @@ namespace ts {
7474
}
7575

7676
let expression: Expression = visitNode(node.expression, visitor, isExpression);
77-
Debug.assertNotNode(expression, isSyntheticReference);
77+
Debug.assertNotNode(expression, isSyntheticReferenceExpression);
7878

7979
let thisArg: Expression | undefined;
8080
if (captureThisArg) {
@@ -102,7 +102,7 @@ namespace ts {
102102
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
103103
const expression = visitNonOptionalParenthesizedExpression(node.expression, /*captureThisArg*/ true, /*isDelete*/ false);
104104
const args = visitNodes(node.arguments, visitor, isExpression);
105-
if (isSyntheticReference(expression)) {
105+
if (isSyntheticReferenceExpression(expression)) {
106106
return setTextRange(factory.createFunctionCallCall(expression.expression, expression.thisArg, args), node);
107107
}
108108
return factory.updateCallExpression(node, expression, /*typeArguments*/ undefined, args);
@@ -123,8 +123,8 @@ namespace ts {
123123
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean, isDelete: boolean): Expression {
124124
const { expression, chain } = flattenChain(node);
125125
const left = visitNonOptionalExpression(expression, isCallChain(chain[0]), /*isDelete*/ false);
126-
const leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined;
127-
let leftExpression = isSyntheticReference(left) ? left.expression : left;
126+
const leftThisArg = isSyntheticReferenceExpression(left) ? left.thisArg : undefined;
127+
let leftExpression = isSyntheticReferenceExpression(left) ? left.expression : left;
128128
let capturedLeft: Expression = leftExpression;
129129
if (!isSimpleCopiableExpression(leftExpression)) {
130130
capturedLeft = factory.createTempVariable(hoistVariableDeclaration);

src/compiler/transformers/jsx.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ namespace ts {
5757
}
5858
const generatedName = factory.createUniqueName(`_${name}`, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel | GeneratedIdentifierFlags.AllowNameSubstitution);
5959
const specifier = factory.createImportSpecifier(factory.createIdentifier(name), generatedName);
60-
generatedName.generatedImportReference = specifier;
60+
setGeneratedImportReference(generatedName, specifier);
6161
specifierSourceImports.set(name, specifier);
6262
return generatedName;
6363
}
@@ -525,7 +525,7 @@ namespace ts {
525525
return factory.createStringLiteral(idText(name));
526526
}
527527
else {
528-
return createExpressionFromEntityName(factory, name);
528+
return name;
529529
}
530530
}
531531
}

0 commit comments

Comments
 (0)