Skip to content

Commit 8d28f92

Browse files
author
Josh Goldberg
committed
Added codefix to enable experimentalDecorators in the user's config file
Starts on microsoft#29035 by creating a codefix to enable the `experimentalDecorators` setting in a user's config file, if one exists. The issue's discussion also mentions giving a more precise error message if the user has a jsconfig or tsconfig or creating one if not; I'd rather tackle those in separate PRs to keep this one small. Doesn't create the code action if no config file is present. Otherwise keeps to the precedent of returning without action when the config file contents aren't the expected JSON structure (looking at `fixCannotFindModule.ts`). Moves a couple JSON helpers from that file into the sibling `helpers.ts` so both codefixes can use them.
1 parent 52b8256 commit 8d28f92

9 files changed

+161
-11
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4811,5 +4811,9 @@
48114811
"Add names to all parameters without names": {
48124812
"category": "Message",
48134813
"code": 95073
4814+
},
4815+
"Enable the 'experimentalDecorators' option in your configuration file": {
4816+
"category": "Message",
4817+
"code": 95074
48144818
}
48154819
}

src/services/codefixes/fixCannotFindModule.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ namespace ts.codefix {
7474
const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile);
7575
if (!tsconfigObjectLiteral) return undefined;
7676

77-
const compilerOptionsProperty = findProperty(tsconfigObjectLiteral, "compilerOptions");
77+
const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions");
7878
if (!compilerOptionsProperty) {
7979
const newCompilerOptions = createObjectLiteral([makeDefaultBaseUrl(), makeDefaultPaths()]);
8080
changes.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createJsonPropertyAssignment("compilerOptions", newCompilerOptions));
@@ -94,7 +94,7 @@ namespace ts.codefix {
9494
return createJsonPropertyAssignment("baseUrl", createStringLiteral(defaultBaseUrl));
9595
}
9696
function getOrAddBaseUrl(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression): string {
97-
const baseUrlProp = findProperty(compilerOptions, "baseUrl");
97+
const baseUrlProp = findJsonProperty(compilerOptions, "baseUrl");
9898
if (baseUrlProp) {
9999
return isStringLiteral(baseUrlProp.initializer) ? baseUrlProp.initializer.text : defaultBaseUrl;
100100
}
@@ -112,7 +112,7 @@ namespace ts.codefix {
112112
return createJsonPropertyAssignment("paths", createObjectLiteral([makeDefaultPathMapping()]));
113113
}
114114
function getOrAddPathMapping(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression) {
115-
const paths = findProperty(compilerOptions, "paths");
115+
const paths = findJsonProperty(compilerOptions, "paths");
116116
if (!paths || !isObjectLiteralExpression(paths.initializer)) {
117117
changes.insertNodeAtObjectStart(tsconfig, compilerOptions, makeDefaultPaths());
118118
return defaultTypesDirectoryName;
@@ -129,14 +129,6 @@ namespace ts.codefix {
129129
return defaultTypesDirectoryName;
130130
}
131131

132-
function createJsonPropertyAssignment(name: string, initializer: Expression) {
133-
return createPropertyAssignment(createStringLiteral(name), initializer);
134-
}
135-
136-
function findProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined {
137-
return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name);
138-
}
139-
140132
function getInstallCommand(fileName: string, packageName: string): InstallPackageAction {
141133
return { type: "install package", file: fileName, packageName };
142134
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
const fixId = "enableExperimentalDecorators";
4+
const errorCodes = [
5+
Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning.code
6+
];
7+
registerCodeFix({
8+
errorCodes,
9+
getCodeActions: (context) => {
10+
const { configFile } = context.program.getCompilerOptions();
11+
if (configFile === undefined) {
12+
return undefined;
13+
}
14+
15+
const changes = textChanges.ChangeTracker.with(context, changeTracker => makeChange(changeTracker, configFile));
16+
return [createCodeFixActionNoFixId(fixId, changes, Diagnostics.Enable_the_experimentalDecorators_option_in_your_configuration_file)];
17+
},
18+
fixIds: [fixId],
19+
});
20+
21+
function makeChange(changeTracker: textChanges.ChangeTracker, configFile: TsConfigSourceFile) {
22+
const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile);
23+
if (tsconfigObjectLiteral === undefined) {
24+
return;
25+
}
26+
27+
const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions");
28+
if (compilerOptionsProperty === undefined) {
29+
changeTracker.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createCompilerOptionsAssignment());
30+
return;
31+
}
32+
33+
const compilerOptions = compilerOptionsProperty.initializer;
34+
if (!isObjectLiteralExpression(compilerOptions)) {
35+
return;
36+
}
37+
38+
const experimentalDecoratorsProperty = findJsonProperty(compilerOptions, "experimentalDecorators");
39+
40+
if (experimentalDecoratorsProperty === undefined) {
41+
changeTracker.insertNodeAtObjectStart(configFile, compilerOptions, createExperimentalDecoratorsAssignment());
42+
}
43+
else {
44+
changeTracker.replaceNodeWithText(configFile, experimentalDecoratorsProperty.initializer, "true");
45+
}
46+
}
47+
48+
function createCompilerOptionsAssignment() {
49+
return createJsonPropertyAssignment(
50+
"compilerOptions",
51+
createObjectLiteral([
52+
createExperimentalDecoratorsAssignment(),
53+
]),
54+
);
55+
}
56+
57+
function createExperimentalDecoratorsAssignment() {
58+
return createJsonPropertyAssignment("experimentalDecorators", createTrue());
59+
}
60+
}

src/services/codefixes/helpers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
/* @internal */
22
namespace ts.codefix {
3+
export function createJsonPropertyAssignment(name: string, initializer: Expression) {
4+
return createPropertyAssignment(createStringLiteral(name), initializer);
5+
}
6+
7+
export function findJsonProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined {
8+
return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name);
9+
}
10+
311
/**
412
* Finds members of the resolved type that are missing in the class pointed to by class decl
513
* and generates source code for the missing members.

src/services/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
6262
"codefixes/fixClassSuperMustPrecedeThisAccess.ts",
6363
"codefixes/fixConstructorForDerivedNeedSuperCall.ts",
64+
"codefixes/fixEnableExperimentalDecorators.ts",
6465
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
6566
"codefixes/fixForgottenThisPropertyAccess.ts",
6667
"codefixes/fixUnusedIdentifier.ts",
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: /dir/a.ts
4+
////declare const decorator: any;
5+
////class A {
6+
//// @decorator method() {};
7+
////};
8+
9+
// @Filename: /dir/tsconfig.json
10+
////{
11+
//// "compilerOptions": {
12+
//// }
13+
////}
14+
15+
goTo.file("/dir/a.ts");
16+
verify.codeFix({
17+
description: "Enable the 'experimentalDecorators' option in your configuration file",
18+
newFileContent: {
19+
"/dir/tsconfig.json":
20+
`{
21+
"compilerOptions": {
22+
"experimentalDecorators": true,
23+
}
24+
}`,
25+
},
26+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: /dir/a.ts
4+
////declare const decorator: any;
5+
////class A {
6+
//// @decorator method() {};
7+
////};
8+
9+
// @Filename: /dir/tsconfig.json
10+
////{
11+
//// "compilerOptions": {
12+
//// "experimentalDecorators": false,
13+
//// }
14+
////}
15+
16+
goTo.file("/dir/a.ts");
17+
verify.codeFix({
18+
description: "Enable the 'experimentalDecorators' option in your configuration file",
19+
newFileContent: {
20+
"/dir/tsconfig.json":
21+
`{
22+
"compilerOptions": {
23+
"experimentalDecorators": true,
24+
}
25+
}`,
26+
},
27+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: /dir/a.ts
4+
////declare const decorator: any;
5+
////class A {
6+
//// @decorator method() {};
7+
////};
8+
9+
// @Filename: /dir/tsconfig.json
10+
////{
11+
////}
12+
13+
goTo.file("/dir/a.ts");
14+
verify.codeFix({
15+
description: "Enable the 'experimentalDecorators' option in your configuration file",
16+
newFileContent: {
17+
"/dir/tsconfig.json":
18+
`{
19+
"compilerOptions": { "experimentalDecorators": true },
20+
}`,
21+
},
22+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: /dir/a.ts
4+
////declare const decorator: any;
5+
////class A {
6+
//// @decorator method() {};
7+
////};
8+
9+
goTo.file("/dir/a.ts");
10+
verify.not.codeFixAvailable();

0 commit comments

Comments
 (0)