Skip to content

Commit

Permalink
Add --metadataDecorator option
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Apr 6, 2021
1 parent 11097c6 commit 116c099
Show file tree
Hide file tree
Showing 112 changed files with 3,642 additions and 528 deletions.
551 changes: 409 additions & 142 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,21 @@ namespace ts {
description: Diagnostics.Include_modules_imported_with_json_extension
},

{
name: "metadataDecorator",
type: "string",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Specify_the_name_of_the_metadata_decorator_function_to_use_when_emitDecoratorMetadata_is_set
},
{
name: "metadataDecoratorImportSource",
type: "string",
affectsEmit: true,
affectsModuleResolution: true,
category: Diagnostics.Advanced_Options,
description: Diagnostics.Specify_the_module_specifier_to_be_used_to_import_the_metadata_decorator_provided_by_metadataDecorator
},

{
name: "out",
type: "string",
Expand Down
39 changes: 34 additions & 5 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,19 @@
"category": "Error",
"code": 1432
},
"Unable to resolve signature of implicit decorator '{0}' when called as an expression.": {
"category": "Error",
"code": 1433
},
"Unable to resolve signature of implicit decorator '{0}' from module '{1}' when called as an expression.": {
"category": "Error",
"code": 1434
},
"Unable to import implicit decorator '{0}' from module '{1}' as this file is not a module.": {
"category": "Error",
"code": 1435
},


"The types of '{0}' are incompatible between these types.": {
"category": "Error",
Expand Down Expand Up @@ -3308,6 +3321,18 @@
"category": "Error",
"code": 2808
},
"Namespace '{0}' from module '{1}' has no exported member '{2}'.": {
"category": "Error",
"code": 2809
},
"'{0}' from module '{1}' has no exported member named '{2}'. Did you mean '{3}'?": {
"category": "Error",
"code": 2810
},
"Cannot find namespace '{0}'. Did you mean '{1}?": {
"category": "Error",
"code": 2811
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down Expand Up @@ -3814,7 +3839,7 @@
"category": "Error",
"code": 5066
},
"Invalid value for 'jsxFactory'. '{0}' is not a valid identifier or qualified-name.": {
"Invalid value for '{0}'. '{1}' is not a valid identifier or qualified-name.": {
"category": "Error",
"code": 5067
},
Expand Down Expand Up @@ -4821,6 +4846,14 @@
"category": "Error",
"code": 6238
},
"Specify the name of the metadata decorator function to use when '--emitDecoratorMetadata' is set": {
"category": "Message",
"code": 6239
},
"Specify the module specifier to be used to import the metadata decorator provided by '--metadataDecorator'.": {
"category": "Message",
"code": 6240
},

"Projects to reference": {
"category": "Message",
Expand Down Expand Up @@ -6438,10 +6471,6 @@
"category": "Message",
"code": 18034
},
"Invalid value for 'jsxFragmentFactory'. '{0}' is not a valid identifier or qualified-name.": {
"category": "Error",
"code": 18035
},
"Class decorators can't be used with static private identifier. Consider removing the experimental decorator.": {
"category": "Error",
"code": 18036
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/factory/emitHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ namespace ts {

// ES2017 Helpers

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

const generatorFunc = factory.createFunctionExpression(
Expand All @@ -256,7 +256,7 @@ namespace ts {
[
hasLexicalThis ? factory.createThis() : factory.createVoidZero(),
hasLexicalArguments ? factory.createIdentifier("arguments") : factory.createVoidZero(),
promiseConstructor ? createExpressionFromEntityName(factory, promiseConstructor) : factory.createVoidZero(),
promiseConstructor ? isEntityName(promiseConstructor) ? createExpressionFromEntityName(factory, promiseConstructor) : promiseConstructor : factory.createVoidZero(),
generatorFunc
]
);
Expand Down
20 changes: 20 additions & 0 deletions src/compiler/factory/emitNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,24 @@ namespace ts {
getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
return node;
}

/**
* For a synthetic import, specifies the synthesized import reference so that the import can be resolved during subsequent transformations.
*
* @param node Imported identifier
* @param importReference The `ImportSpecifier` to use for the reference, or `undefined`.
*/
/* @internal */
export function setGeneratedImportReference<T extends Identifier>(node: T, importReference: ImportSpecifier | undefined) {
getOrCreateEmitNode(node).generatedImportReference = importReference;
return node;
}

/**
* For a synthetic import, gets any associated synthesized import reference.
*/
/* @internal */
export function getGeneratedImportReference(node: Identifier) {
return node.emitNode?.generatedImportReference;
}
}
12 changes: 12 additions & 0 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ namespace ts {
createUnparsedSyntheticReference,
createInputFiles,
createSyntheticExpression,
createSyntheticCallExpression,
createSyntaxList,
createNotEmittedStatement,
createPartiallyEmittedExpression,
Expand Down Expand Up @@ -5104,6 +5105,17 @@ namespace ts {
return node;
}

// @api
function createSyntheticCallExpression(thisArg: LeftHandSideExpression | SyntheticExpression | undefined, expression: Expression | SyntheticExpression, typeArguments: readonly TypeNode[] | undefined, argumentList: readonly Expression[], containingMessageChain?: () => DiagnosticMessageChain | undefined): SyntheticCallExpression {
const node = createBaseNode<SyntheticCallExpression>(SyntaxKind.SyntheticCallExpression);
node.thisArg = thisArg;
node.expression = expression;
node.typeArguments = typeArguments;
node.arguments = argumentList;
node.containingMessageChain = containingMessageChain;
return node;
}

// @api
function createSyntaxList(children: Node[]) {
const node = createBaseNode<SyntaxList>(SyntaxKind.SyntaxList);
Expand Down
6 changes: 5 additions & 1 deletion src/compiler/factory/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,10 @@ namespace ts {
return node.kind === SyntaxKind.CommaListExpression;
}

export function isSyntheticCallExpression(node: Node): node is SyntheticCallExpression {
return node.kind === SyntaxKind.SyntheticCallExpression;
}

// Misc

export function isTemplateSpan(node: Node): node is TemplateSpan {
Expand Down Expand Up @@ -629,7 +633,7 @@ namespace ts {
}

/* @internal */
export function isSyntheticReference(node: Node): node is SyntheticReferenceExpression {
export function isSyntheticReferenceExpression(node: Node): node is SyntheticReferenceExpression {
return node.kind === SyntaxKind.SyntheticReferenceExpression;
}

Expand Down
25 changes: 18 additions & 7 deletions src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,27 @@ namespace ts {
}
}

export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName | Expression): Expression {
export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName, emulateParseTree = true): EntityNameExpression {
if (isQualifiedName(node)) {
const left = createExpressionFromEntityName(factory, node.left);
// TODO(rbuckton): Does this need to be parented?
const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent);
return setTextRange(factory.createPropertyAccessExpression(left, right), node);
const left = createExpressionFromEntityName(factory, node.left, emulateParseTree);
const right = factory.cloneNode(node.right);
if (emulateParseTree) {
setTextRange(right, node.right);
setParent(right, node.right.parent);
}
const expression = factory.createPropertyAccessExpression(left, right);
if (emulateParseTree) {
setTextRange(expression, node);
}
return expression as PropertyAccessEntityNameExpression;
}
else {
// TODO(rbuckton): Does this need to be parented?
return setParent(setTextRange(factory.cloneNode(node), node), node.parent);
const name = factory.cloneNode(node);
if (emulateParseTree) {
setTextRange(name, node);
setParent(name, node.parent);
}
return name;
}
}

Expand Down
40 changes: 36 additions & 4 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2290,6 +2290,14 @@ namespace ts {
// synthesize `import "base/jsx-runtime"` declaration
(imports ||= []).push(createSyntheticImport(jsxImport, file));
}
if (file.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax &&
options.emitDecoratorMetadata &&
options.metadataDecorator) {
const metadataDecoratorImport = options.metadataDecoratorImportSource;
if (metadataDecoratorImport) {
(imports ||= []).push(createSyntheticImport(metadataDecoratorImport, file));
}
}
}

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

if (options.metadataDecorator) {
if (!options.experimentalDecorators) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "experimentalDecorators");
}
if (!options.emitDecoratorMetadata) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "emitDecoratorMetadata");
}
if (!parseIsolatedEntityName(options.metadataDecorator, languageVersion)) {
createOptionValueDiagnostic("metadataDecorator", Diagnostics.Invalid_value_for_0_1_is_not_a_valid_identifier_or_qualified_name, "metadataDecorator", options.metadataDecorator);
}
}

if (options.metadataDecoratorImportSource) {
if (!options.experimentalDecorators) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "experimentalDecorators");
}
if (!options.emitDecoratorMetadata) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecorator", "emitDecoratorMetadata");
}
if (!options.metadataDecorator) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "metadataDecoratorImportSource", "metadataDecorator");
}
}

if (options.jsxFactory) {
if (options.reactNamespace) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory");
Expand All @@ -3252,7 +3284,7 @@ namespace ts {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx));
}
if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory);
createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_0_1_is_not_a_valid_identifier_or_qualified_name, "jsxFactory", options.jsxFactory);
}
}
else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
Expand All @@ -3267,7 +3299,7 @@ namespace ts {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx));
}
if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_0_1_is_not_a_valid_identifier_or_qualified_name, "jsxFragmentFactory", options.jsxFragmentFactory);
}
}

Expand Down Expand Up @@ -3553,8 +3585,8 @@ namespace ts {
createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3);
}

function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string) {
createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string, arg1?: string) {
createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0, arg1);
}

function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
Expand Down
14 changes: 7 additions & 7 deletions src/compiler/transformers/es2020.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ namespace ts {
switch (node.kind) {
case SyntaxKind.CallExpression: {
const updated = visitNonOptionalCallExpression(node as CallExpression, /*captureThisArg*/ false);
Debug.assertNotNode(updated, isSyntheticReference);
Debug.assertNotNode(updated, isSyntheticReferenceExpression);
return updated;
}
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
if (isOptionalChain(node)) {
const updated = visitOptionalExpression(node, /*captureThisArg*/ false, /*isDelete*/ false);
Debug.assertNotNode(updated, isSyntheticReference);
Debug.assertNotNode(updated, isSyntheticReferenceExpression);
return updated;
}
return visitEachChild(node, visitor, context);
Expand Down Expand Up @@ -59,7 +59,7 @@ namespace ts {

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

let expression: Expression = visitNode(node.expression, visitor, isExpression);
Debug.assertNotNode(expression, isSyntheticReference);
Debug.assertNotNode(expression, isSyntheticReferenceExpression);

let thisArg: Expression | undefined;
if (captureThisArg) {
Expand Down Expand Up @@ -102,7 +102,7 @@ namespace ts {
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
const expression = visitNonOptionalParenthesizedExpression(node.expression, /*captureThisArg*/ true, /*isDelete*/ false);
const args = visitNodes(node.arguments, visitor, isExpression);
if (isSyntheticReference(expression)) {
if (isSyntheticReferenceExpression(expression)) {
return setTextRange(factory.createFunctionCallCall(expression.expression, expression.thisArg, args), node);
}
return factory.updateCallExpression(node, expression, /*typeArguments*/ undefined, args);
Expand All @@ -123,8 +123,8 @@ namespace ts {
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean, isDelete: boolean): Expression {
const { expression, chain } = flattenChain(node);
const left = visitNonOptionalExpression(expression, isCallChain(chain[0]), /*isDelete*/ false);
const leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined;
let leftExpression = isSyntheticReference(left) ? left.expression : left;
const leftThisArg = isSyntheticReferenceExpression(left) ? left.thisArg : undefined;
let leftExpression = isSyntheticReferenceExpression(left) ? left.expression : left;
let capturedLeft: Expression = leftExpression;
if (!isSimpleCopiableExpression(leftExpression)) {
capturedLeft = factory.createTempVariable(hoistVariableDeclaration);
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/transformers/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ namespace ts {
}
const generatedName = factory.createUniqueName(`_${name}`, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel | GeneratedIdentifierFlags.AllowNameSubstitution);
const specifier = factory.createImportSpecifier(factory.createIdentifier(name), generatedName);
generatedName.generatedImportReference = specifier;
setGeneratedImportReference(generatedName, specifier);
specifierSourceImports.set(name, specifier);
return generatedName;
}
Expand Down Expand Up @@ -525,7 +525,7 @@ namespace ts {
return factory.createStringLiteral(idText(name));
}
else {
return createExpressionFromEntityName(factory, name);
return name;
}
}
}
Expand Down
Loading

0 comments on commit 116c099

Please sign in to comment.