diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts
index 62dd670fbc5..06615b3a97f 100644
--- a/src/compiler/builder.ts
+++ b/src/compiler/builder.ts
@@ -78,7 +78,8 @@ namespace ts {
if (useOldState) {
// Verify the sanity of old state
if (!oldState!.currentChangedFilePath) {
- Debug.assert(!oldState!.affectedFiles && (!oldState!.currentAffectedFilesSignatures || !oldState!.currentAffectedFilesSignatures!.size), "Cannot reuse if only few affected files of currentChangedFile were iterated");
+ const affectedSignatures = oldState!.currentAffectedFilesSignatures;
+ Debug.assert(!oldState!.affectedFiles && (!affectedSignatures || !affectedSignatures.size), "Cannot reuse if only few affected files of currentChangedFile were iterated");
}
if (canCopySemanticDiagnostics) {
Debug.assert(!forEachKey(oldState!.changedFilesSet, path => oldState!.semanticDiagnosticsPerFile!.has(path)), "Semantic diagnostics shouldnt be available for changed files");
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index eb0760c516c..f81aaa22854 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -4807,5 +4807,9 @@
"Add names to all parameters without names": {
"category": "Message",
"code": 95073
+ },
+ "Enable the 'experimentalDecorators' option in your configuration file": {
+ "category": "Message",
+ "code": 95074
}
}
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index f976f77ea98..53e45505014 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -7774,17 +7774,18 @@ namespace ts {
const libReferenceDirectives = context.libReferenceDirectives;
forEach(toArray(entryOrList), (arg: PragmaPseudoMap["reference"]) => {
// TODO: GH#18217
+ const { types, lib, path } = arg!.arguments;
if (arg!.arguments["no-default-lib"]) {
context.hasNoDefaultLib = true;
}
- else if (arg!.arguments.types) {
- typeReferenceDirectives.push({ pos: arg!.arguments.types!.pos, end: arg!.arguments.types!.end, fileName: arg!.arguments.types!.value });
+ else if (types) {
+ typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value });
}
- else if (arg!.arguments.lib) {
- libReferenceDirectives.push({ pos: arg!.arguments.lib!.pos, end: arg!.arguments.lib!.end, fileName: arg!.arguments.lib!.value });
+ else if (lib) {
+ libReferenceDirectives.push({ pos: lib.pos, end: lib.end, fileName: lib.value });
}
- else if (arg!.arguments.path) {
- referencedFiles.push({ pos: arg!.arguments.path!.pos, end: arg!.arguments.path!.end, fileName: arg!.arguments.path!.value });
+ else if (path) {
+ referencedFiles.push({ pos: path.pos, end: path.end, fileName: path.value });
}
else {
reportDiagnostic(arg!.range.pos, arg!.range.end - arg!.range.pos, Diagnostics.Invalid_reference_directive_syntax);
diff --git a/src/harness/compiler.ts b/src/harness/compiler.ts
index 532a2d65578..70bda90ef32 100644
--- a/src/harness/compiler.ts
+++ b/src/harness/compiler.ts
@@ -183,8 +183,9 @@ namespace compiler {
}
public getSourceMapRecord(): string | undefined {
- if (this.result!.sourceMaps && this.result!.sourceMaps!.length > 0) {
- return Harness.SourceMapRecorder.getSourceMapRecord(this.result!.sourceMaps!, this.program!, Array.from(this.js.values()).filter(d => !ts.fileExtensionIs(d.file, ts.Extension.Json)), Array.from(this.dts.values()));
+ const maps = this.result!.sourceMaps;
+ if (maps && maps.length > 0) {
+ return Harness.SourceMapRecorder.getSourceMapRecord(maps, this.program!, Array.from(this.js.values()).filter(d => !ts.fileExtensionIs(d.file, ts.Extension.Json)), Array.from(this.dts.values()));
}
}
diff --git a/src/services/codefixes/fixCannotFindModule.ts b/src/services/codefixes/fixCannotFindModule.ts
index 1c7070c74bf..1baaa9714f2 100644
--- a/src/services/codefixes/fixCannotFindModule.ts
+++ b/src/services/codefixes/fixCannotFindModule.ts
@@ -74,7 +74,7 @@ namespace ts.codefix {
const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile);
if (!tsconfigObjectLiteral) return undefined;
- const compilerOptionsProperty = findProperty(tsconfigObjectLiteral, "compilerOptions");
+ const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions");
if (!compilerOptionsProperty) {
const newCompilerOptions = createObjectLiteral([makeDefaultBaseUrl(), makeDefaultPaths()]);
changes.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createJsonPropertyAssignment("compilerOptions", newCompilerOptions));
@@ -94,7 +94,7 @@ namespace ts.codefix {
return createJsonPropertyAssignment("baseUrl", createStringLiteral(defaultBaseUrl));
}
function getOrAddBaseUrl(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression): string {
- const baseUrlProp = findProperty(compilerOptions, "baseUrl");
+ const baseUrlProp = findJsonProperty(compilerOptions, "baseUrl");
if (baseUrlProp) {
return isStringLiteral(baseUrlProp.initializer) ? baseUrlProp.initializer.text : defaultBaseUrl;
}
@@ -112,7 +112,7 @@ namespace ts.codefix {
return createJsonPropertyAssignment("paths", createObjectLiteral([makeDefaultPathMapping()]));
}
function getOrAddPathMapping(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression) {
- const paths = findProperty(compilerOptions, "paths");
+ const paths = findJsonProperty(compilerOptions, "paths");
if (!paths || !isObjectLiteralExpression(paths.initializer)) {
changes.insertNodeAtObjectStart(tsconfig, compilerOptions, makeDefaultPaths());
return defaultTypesDirectoryName;
@@ -129,14 +129,6 @@ namespace ts.codefix {
return defaultTypesDirectoryName;
}
- function createJsonPropertyAssignment(name: string, initializer: Expression) {
- return createPropertyAssignment(createStringLiteral(name), initializer);
- }
-
- function findProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined {
- return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name);
- }
-
function getInstallCommand(fileName: string, packageName: string): InstallPackageAction {
return { type: "install package", file: fileName, packageName };
}
diff --git a/src/services/codefixes/fixEnableExperimentalDecorators.ts b/src/services/codefixes/fixEnableExperimentalDecorators.ts
new file mode 100644
index 00000000000..8aeefe48a47
--- /dev/null
+++ b/src/services/codefixes/fixEnableExperimentalDecorators.ts
@@ -0,0 +1,24 @@
+/* @internal */
+namespace ts.codefix {
+ const fixId = "enableExperimentalDecorators";
+ const errorCodes = [
+ 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
+ ];
+ registerCodeFix({
+ errorCodes,
+ getCodeActions: (context) => {
+ const { configFile } = context.program.getCompilerOptions();
+ if (configFile === undefined) {
+ return undefined;
+ }
+
+ const changes = textChanges.ChangeTracker.with(context, changeTracker => makeChange(changeTracker, configFile));
+ return [createCodeFixActionNoFixId(fixId, changes, Diagnostics.Enable_the_experimentalDecorators_option_in_your_configuration_file)];
+ },
+ fixIds: [fixId],
+ });
+
+ function makeChange(changeTracker: textChanges.ChangeTracker, configFile: TsConfigSourceFile) {
+ setJsonCompilerOptionValue(changeTracker, configFile, "experimentalDecorators", createTrue());
+ }
+}
diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts
index 461771c0b5e..66488d5d697 100644
--- a/src/services/codefixes/helpers.ts
+++ b/src/services/codefixes/helpers.ts
@@ -249,4 +249,46 @@ namespace ts.codefix {
}
return undefined;
}
+
+ export function setJsonCompilerOptionValue(
+ changeTracker: textChanges.ChangeTracker,
+ configFile: TsConfigSourceFile,
+ optionName: string,
+ optionValue: Expression,
+ ) {
+ const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile);
+ if (!tsconfigObjectLiteral) return undefined;
+
+ const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions");
+ if (compilerOptionsProperty === undefined) {
+ changeTracker.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createJsonPropertyAssignment(
+ "compilerOptions",
+ createObjectLiteral([
+ createJsonPropertyAssignment(optionName, optionValue),
+ ])));
+ return;
+ }
+
+ const compilerOptions = compilerOptionsProperty.initializer;
+ if (!isObjectLiteralExpression(compilerOptions)) {
+ return;
+ }
+
+ const optionProperty = findJsonProperty(compilerOptions, optionName);
+
+ if (optionProperty === undefined) {
+ changeTracker.insertNodeAtObjectStart(configFile, compilerOptions, createJsonPropertyAssignment(optionName, optionValue));
+ }
+ else {
+ changeTracker.replaceNode(configFile, optionProperty.initializer, optionValue);
+ }
+ }
+
+ export function createJsonPropertyAssignment(name: string, initializer: Expression) {
+ return createPropertyAssignment(createStringLiteral(name), initializer);
+ }
+
+ export function findJsonProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined {
+ return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name);
+ }
}
diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json
index 15044416f84..21be663055a 100644
--- a/src/services/tsconfig.json
+++ b/src/services/tsconfig.json
@@ -61,6 +61,7 @@
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
"codefixes/fixClassSuperMustPrecedeThisAccess.ts",
"codefixes/fixConstructorForDerivedNeedSuperCall.ts",
+ "codefixes/fixEnableExperimentalDecorators.ts",
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
"codefixes/fixForgottenThisPropertyAccess.ts",
"codefixes/fixUnusedIdentifier.ts",
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_blankCompilerOptions.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_blankCompilerOptions.ts
new file mode 100644
index 00000000000..0e19b42d921
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_blankCompilerOptions.ts
@@ -0,0 +1,26 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+// @Filename: /dir/tsconfig.json
+////{
+//// "compilerOptions": {
+//// }
+////}
+
+goTo.file("/dir/a.ts");
+verify.codeFix({
+ description: "Enable the 'experimentalDecorators' option in your configuration file",
+ newFileContent: {
+ "/dir/tsconfig.json":
+`{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ }
+}`,
+ },
+});
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_disabledInCompilerOptions.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_disabledInCompilerOptions.ts
new file mode 100644
index 00000000000..056e7e15a48
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_disabledInCompilerOptions.ts
@@ -0,0 +1,27 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+// @Filename: /dir/tsconfig.json
+////{
+//// "compilerOptions": {
+//// "experimentalDecorators": false,
+//// }
+////}
+
+goTo.file("/dir/a.ts");
+verify.codeFix({
+ description: "Enable the 'experimentalDecorators' option in your configuration file",
+ newFileContent: {
+ "/dir/tsconfig.json":
+`{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ }
+}`,
+ },
+});
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_missingCompilerOptions.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_missingCompilerOptions.ts
new file mode 100644
index 00000000000..6d7006f3ce1
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_missingCompilerOptions.ts
@@ -0,0 +1,22 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+// @Filename: /dir/tsconfig.json
+////{
+////}
+
+goTo.file("/dir/a.ts");
+verify.codeFix({
+ description: "Enable the 'experimentalDecorators' option in your configuration file",
+ newFileContent: {
+ "/dir/tsconfig.json":
+`{
+ "compilerOptions": { "experimentalDecorators": true },
+}`,
+ },
+});
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_noTsconfig.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_noTsconfig.ts
new file mode 100644
index 00000000000..8430fd0734b
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_noTsconfig.ts
@@ -0,0 +1,10 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+goTo.file("/dir/a.ts");
+verify.not.codeFixAvailable();