From 9d16225bc2c96a06589f8e9b5f8a60d79a82d31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Fri, 11 Jan 2019 13:34:18 +0800 Subject: [PATCH 1/7] emit jsx type arguments --- src/compiler/emitter.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9c09ed79a77..62aba51afec 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2561,6 +2561,7 @@ namespace ts { function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { writePunctuation("<"); emitJsxTagName(node.tagName); + emitTypeArguments(node, node.typeArguments); writeSpace(); emit(node.attributes); writePunctuation("/>"); @@ -2577,6 +2578,7 @@ namespace ts { if (isJsxOpeningElement(node)) { emitJsxTagName(node.tagName); + emitTypeArguments(node, node.typeArguments); if (node.attributes.properties && node.attributes.properties.length > 0) { writeSpace(); } From 11b150129a988fe43bd74f6d349f19edb799f19b Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Fri, 14 Dec 2018 14:26:32 +0100 Subject: [PATCH 2/7] Do not process library reference directives with noLib set. When a user sets `noLib`, this indicates that they will supply their own list of `lib*.d.ts` files as part of input sources. In this situation, TypeScript should not try to resolve library reference directives. This avoids a problem where TypeScript loads a file that e.g. contains `/// `. Previously, TypeScript would use its builtin ts.libMap and attempt to load builtin libraries from the TypeScript installation, instead of respecting the user-supplied set of libraries. --- src/compiler/program.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 080b17b8cbe..271bdab976e 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2227,8 +2227,9 @@ namespace ts { processReferencedFiles(file, isDefaultLib); processTypeReferenceDirectives(file); } - - processLibReferenceDirectives(file); + if (!options.noLib) { + processLibReferenceDirectives(file); + } modulesWithElidedImports.set(file.path, false); processImportedModules(file); @@ -2315,8 +2316,10 @@ namespace ts { processReferencedFiles(file, isDefaultLib); processTypeReferenceDirectives(file); } + if (!options.noLib) { + processLibReferenceDirectives(file); + } - processLibReferenceDirectives(file); // always process imported modules to record module name resolutions processImportedModules(file); From cc7ddaed281f9ab72586fd4028d6f11f3459fd5f Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Sat, 15 Dec 2018 16:43:37 +0100 Subject: [PATCH 3/7] Add tests for noLib with . --- .../baselines/reference/libReferenceNoLib.js | 51 +++++++++++++++++++ .../reference/libReferenceNoLib.symbols | 40 +++++++++++++++ .../reference/libReferenceNoLib.types | 24 +++++++++ .../declarationEmit/libReferenceNoLib.ts | 21 ++++++++ 4 files changed, 136 insertions(+) create mode 100644 tests/baselines/reference/libReferenceNoLib.js create mode 100644 tests/baselines/reference/libReferenceNoLib.symbols create mode 100644 tests/baselines/reference/libReferenceNoLib.types create mode 100644 tests/cases/conformance/declarationEmit/libReferenceNoLib.ts diff --git a/tests/baselines/reference/libReferenceNoLib.js b/tests/baselines/reference/libReferenceNoLib.js new file mode 100644 index 00000000000..21d1cbbe177 --- /dev/null +++ b/tests/baselines/reference/libReferenceNoLib.js @@ -0,0 +1,51 @@ +//// [tests/cases/conformance/declarationEmit/libReferenceNoLib.ts] //// + +//// [fakelib.ts] +// Test that passing noLib disables resolution. + +interface Object { } +interface Array { } +interface String { } +interface Boolean { } +interface Number { } +interface Function { } +interface RegExp { } +interface IArguments { } + + +//// [file1.ts] +/// +export declare interface HTMLElement { field: string; } +export const elem: HTMLElement = { field: 'a' }; + + +//// [fakelib.js] +// Test that passing noLib disables resolution. +//// [file1.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.elem = { field: 'a' }; + + +//// [fakelib.d.ts] +interface Object { +} +interface Array { +} +interface String { +} +interface Boolean { +} +interface Number { +} +interface Function { +} +interface RegExp { +} +interface IArguments { +} +//// [file1.d.ts] +export declare interface HTMLElement { + field: string; +} +export declare const elem: HTMLElement; diff --git a/tests/baselines/reference/libReferenceNoLib.symbols b/tests/baselines/reference/libReferenceNoLib.symbols new file mode 100644 index 00000000000..5f180fd1eed --- /dev/null +++ b/tests/baselines/reference/libReferenceNoLib.symbols @@ -0,0 +1,40 @@ +=== tests/cases/conformance/declarationEmit/fakelib.ts === +// Test that passing noLib disables resolution. + +interface Object { } +>Object : Symbol(Object, Decl(fakelib.ts, 0, 0)) + +interface Array { } +>Array : Symbol(Array, Decl(fakelib.ts, 2, 20)) +>T : Symbol(T, Decl(fakelib.ts, 3, 16)) + +interface String { } +>String : Symbol(String, Decl(fakelib.ts, 3, 22)) + +interface Boolean { } +>Boolean : Symbol(Boolean, Decl(fakelib.ts, 4, 20)) + +interface Number { } +>Number : Symbol(Number, Decl(fakelib.ts, 5, 21)) + +interface Function { } +>Function : Symbol(Function, Decl(fakelib.ts, 6, 20)) + +interface RegExp { } +>RegExp : Symbol(RegExp, Decl(fakelib.ts, 7, 22)) + +interface IArguments { } +>IArguments : Symbol(IArguments, Decl(fakelib.ts, 8, 20)) + + +=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare interface HTMLElement { field: string; } +>HTMLElement : Symbol(HTMLElement, Decl(file1.ts, 0, 0)) +>field : Symbol(HTMLElement.field, Decl(file1.ts, 1, 38)) + +export const elem: HTMLElement = { field: 'a' }; +>elem : Symbol(elem, Decl(file1.ts, 2, 12)) +>HTMLElement : Symbol(HTMLElement, Decl(file1.ts, 0, 0)) +>field : Symbol(field, Decl(file1.ts, 2, 34)) + diff --git a/tests/baselines/reference/libReferenceNoLib.types b/tests/baselines/reference/libReferenceNoLib.types new file mode 100644 index 00000000000..0b2733c2265 --- /dev/null +++ b/tests/baselines/reference/libReferenceNoLib.types @@ -0,0 +1,24 @@ +=== tests/cases/conformance/declarationEmit/fakelib.ts === +// Test that passing noLib disables resolution. +No type information for this code. +No type information for this code.interface Object { } +No type information for this code.interface Array { } +No type information for this code.interface String { } +No type information for this code.interface Boolean { } +No type information for this code.interface Number { } +No type information for this code.interface Function { } +No type information for this code.interface RegExp { } +No type information for this code.interface IArguments { } +No type information for this code. +No type information for this code. +No type information for this code.=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare interface HTMLElement { field: string; } +>field : string + +export const elem: HTMLElement = { field: 'a' }; +>elem : HTMLElement +>{ field: 'a' } : { field: string; } +>field : string +>'a' : "a" + diff --git a/tests/cases/conformance/declarationEmit/libReferenceNoLib.ts b/tests/cases/conformance/declarationEmit/libReferenceNoLib.ts new file mode 100644 index 00000000000..d29da9a5bc5 --- /dev/null +++ b/tests/cases/conformance/declarationEmit/libReferenceNoLib.ts @@ -0,0 +1,21 @@ +// @target: esnext +// @module: commonjs +// @noLib: true +// @declaration: true +// Test that passing noLib disables resolution. + +// @filename: fakelib.ts +interface Object { } +interface Array { } +interface String { } +interface Boolean { } +interface Number { } +interface Function { } +interface RegExp { } +interface IArguments { } + + +// @filename: file1.ts +/// +export declare interface HTMLElement { field: string; } +export const elem: HTMLElement = { field: 'a' }; From f3f5877c5f7659851fc7494b6c15bc5cb12834af Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Fri, 11 Jan 2019 08:36:16 +0100 Subject: [PATCH 4/7] Add tests for noLib with and bundling. --- .../reference/libReferenceNoLibBundle.js | 53 +++++++++++++++++++ .../reference/libReferenceNoLibBundle.symbols | 40 ++++++++++++++ .../reference/libReferenceNoLibBundle.types | 24 +++++++++ .../libReferenceNoLibBundle.ts | 23 ++++++++ 4 files changed, 140 insertions(+) create mode 100644 tests/baselines/reference/libReferenceNoLibBundle.js create mode 100644 tests/baselines/reference/libReferenceNoLibBundle.symbols create mode 100644 tests/baselines/reference/libReferenceNoLibBundle.types create mode 100644 tests/cases/conformance/declarationEmit/libReferenceNoLibBundle.ts diff --git a/tests/baselines/reference/libReferenceNoLibBundle.js b/tests/baselines/reference/libReferenceNoLibBundle.js new file mode 100644 index 00000000000..a881f11a8a1 --- /dev/null +++ b/tests/baselines/reference/libReferenceNoLibBundle.js @@ -0,0 +1,53 @@ +//// [tests/cases/conformance/declarationEmit/libReferenceNoLibBundle.ts] //// + +//// [fakelib.ts] +// Test that passing noLib disables resolution. + +interface Object { } +interface Array { } +interface String { } +interface Boolean { } +interface Number { } +interface Function { } +interface RegExp { } +interface IArguments { } + + +//// [file1.ts] +/// +export declare interface HTMLElement { field: string; } +export const elem: HTMLElement = { field: 'a' }; + + +//// [bundle.js] +// Test that passing noLib disables resolution. +define("file1", ["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.elem = { field: 'a' }; +}); + + +//// [bundle.d.ts] +interface Object { +} +interface Array { +} +interface String { +} +interface Boolean { +} +interface Number { +} +interface Function { +} +interface RegExp { +} +interface IArguments { +} +declare module "file1" { + export interface HTMLElement { + field: string; + } + export const elem: HTMLElement; +} diff --git a/tests/baselines/reference/libReferenceNoLibBundle.symbols b/tests/baselines/reference/libReferenceNoLibBundle.symbols new file mode 100644 index 00000000000..5f180fd1eed --- /dev/null +++ b/tests/baselines/reference/libReferenceNoLibBundle.symbols @@ -0,0 +1,40 @@ +=== tests/cases/conformance/declarationEmit/fakelib.ts === +// Test that passing noLib disables resolution. + +interface Object { } +>Object : Symbol(Object, Decl(fakelib.ts, 0, 0)) + +interface Array { } +>Array : Symbol(Array, Decl(fakelib.ts, 2, 20)) +>T : Symbol(T, Decl(fakelib.ts, 3, 16)) + +interface String { } +>String : Symbol(String, Decl(fakelib.ts, 3, 22)) + +interface Boolean { } +>Boolean : Symbol(Boolean, Decl(fakelib.ts, 4, 20)) + +interface Number { } +>Number : Symbol(Number, Decl(fakelib.ts, 5, 21)) + +interface Function { } +>Function : Symbol(Function, Decl(fakelib.ts, 6, 20)) + +interface RegExp { } +>RegExp : Symbol(RegExp, Decl(fakelib.ts, 7, 22)) + +interface IArguments { } +>IArguments : Symbol(IArguments, Decl(fakelib.ts, 8, 20)) + + +=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare interface HTMLElement { field: string; } +>HTMLElement : Symbol(HTMLElement, Decl(file1.ts, 0, 0)) +>field : Symbol(HTMLElement.field, Decl(file1.ts, 1, 38)) + +export const elem: HTMLElement = { field: 'a' }; +>elem : Symbol(elem, Decl(file1.ts, 2, 12)) +>HTMLElement : Symbol(HTMLElement, Decl(file1.ts, 0, 0)) +>field : Symbol(field, Decl(file1.ts, 2, 34)) + diff --git a/tests/baselines/reference/libReferenceNoLibBundle.types b/tests/baselines/reference/libReferenceNoLibBundle.types new file mode 100644 index 00000000000..0b2733c2265 --- /dev/null +++ b/tests/baselines/reference/libReferenceNoLibBundle.types @@ -0,0 +1,24 @@ +=== tests/cases/conformance/declarationEmit/fakelib.ts === +// Test that passing noLib disables resolution. +No type information for this code. +No type information for this code.interface Object { } +No type information for this code.interface Array { } +No type information for this code.interface String { } +No type information for this code.interface Boolean { } +No type information for this code.interface Number { } +No type information for this code.interface Function { } +No type information for this code.interface RegExp { } +No type information for this code.interface IArguments { } +No type information for this code. +No type information for this code. +No type information for this code.=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare interface HTMLElement { field: string; } +>field : string + +export const elem: HTMLElement = { field: 'a' }; +>elem : HTMLElement +>{ field: 'a' } : { field: string; } +>field : string +>'a' : "a" + diff --git a/tests/cases/conformance/declarationEmit/libReferenceNoLibBundle.ts b/tests/cases/conformance/declarationEmit/libReferenceNoLibBundle.ts new file mode 100644 index 00000000000..18f0b5387c5 --- /dev/null +++ b/tests/cases/conformance/declarationEmit/libReferenceNoLibBundle.ts @@ -0,0 +1,23 @@ +// @target: esnext +// @module: amd +// @noLib: true +// @declaration: true +// @outFile: bundle.js + +// Test that passing noLib disables resolution. + +// @filename: fakelib.ts +interface Object { } +interface Array { } +interface String { } +interface Boolean { } +interface Number { } +interface Function { } +interface RegExp { } +interface IArguments { } + + +// @filename: file1.ts +/// +export declare interface HTMLElement { field: string; } +export const elem: HTMLElement = { field: 'a' }; From 8d28f9230cb3b4e032218b8019da22e61dd9abdd Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 11 Jan 2019 09:20:12 -0500 Subject: [PATCH 5/7] Added codefix to enable experimentalDecorators in the user's config file Starts on #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. --- src/compiler/diagnosticMessages.json | 4 ++ src/services/codefixes/fixCannotFindModule.ts | 14 +---- .../fixEnableExperimentalDecorators.ts | 60 +++++++++++++++++++ src/services/codefixes/helpers.ts | 8 +++ src/services/tsconfig.json | 1 + ...rimentalDecorators_blankCompilerOptions.ts | 26 ++++++++ ...talDecorators_disabledInCompilerOptions.ts | 27 +++++++++ ...mentalDecorators_missingCompilerOptions.ts | 22 +++++++ ...EnableExperimentalDecorators_noTsconfig.ts | 10 ++++ 9 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 src/services/codefixes/fixEnableExperimentalDecorators.ts create mode 100644 tests/cases/fourslash/codefixEnableExperimentalDecorators_blankCompilerOptions.ts create mode 100644 tests/cases/fourslash/codefixEnableExperimentalDecorators_disabledInCompilerOptions.ts create mode 100644 tests/cases/fourslash/codefixEnableExperimentalDecorators_missingCompilerOptions.ts create mode 100644 tests/cases/fourslash/codefixEnableExperimentalDecorators_noTsconfig.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 9a918507dc4..5bcaf448599 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4811,5 +4811,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/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..8179abdfd1d --- /dev/null +++ b/src/services/codefixes/fixEnableExperimentalDecorators.ts @@ -0,0 +1,60 @@ +/* @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) { + const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile); + if (tsconfigObjectLiteral === undefined) { + return; + } + + const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions"); + if (compilerOptionsProperty === undefined) { + changeTracker.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createCompilerOptionsAssignment()); + return; + } + + const compilerOptions = compilerOptionsProperty.initializer; + if (!isObjectLiteralExpression(compilerOptions)) { + return; + } + + const experimentalDecoratorsProperty = findJsonProperty(compilerOptions, "experimentalDecorators"); + + if (experimentalDecoratorsProperty === undefined) { + changeTracker.insertNodeAtObjectStart(configFile, compilerOptions, createExperimentalDecoratorsAssignment()); + } + else { + changeTracker.replaceNodeWithText(configFile, experimentalDecoratorsProperty.initializer, "true"); + } + } + + function createCompilerOptionsAssignment() { + return createJsonPropertyAssignment( + "compilerOptions", + createObjectLiteral([ + createExperimentalDecoratorsAssignment(), + ]), + ); + } + + function createExperimentalDecoratorsAssignment() { + return createJsonPropertyAssignment("experimentalDecorators", createTrue()); + } +} diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 461771c0b5e..0344f51a854 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -1,5 +1,13 @@ /* @internal */ namespace ts.codefix { + 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); + } + /** * Finds members of the resolved type that are missing in the class pointed to by class decl * and generates source code for the missing members. 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(); From 7b6adae6dd438e2393d55f8b8a932c82ad67a10a Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 11 Jan 2019 15:05:24 -0500 Subject: [PATCH 6/7] Extracted compilerOptions setting to helper function --- .../fixEnableExperimentalDecorators.ts | 38 +------------- src/services/codefixes/helpers.ts | 50 ++++++++++++++++--- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/services/codefixes/fixEnableExperimentalDecorators.ts b/src/services/codefixes/fixEnableExperimentalDecorators.ts index 8179abdfd1d..8aeefe48a47 100644 --- a/src/services/codefixes/fixEnableExperimentalDecorators.ts +++ b/src/services/codefixes/fixEnableExperimentalDecorators.ts @@ -19,42 +19,6 @@ namespace ts.codefix { }); function makeChange(changeTracker: textChanges.ChangeTracker, configFile: TsConfigSourceFile) { - const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile); - if (tsconfigObjectLiteral === undefined) { - return; - } - - const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions"); - if (compilerOptionsProperty === undefined) { - changeTracker.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createCompilerOptionsAssignment()); - return; - } - - const compilerOptions = compilerOptionsProperty.initializer; - if (!isObjectLiteralExpression(compilerOptions)) { - return; - } - - const experimentalDecoratorsProperty = findJsonProperty(compilerOptions, "experimentalDecorators"); - - if (experimentalDecoratorsProperty === undefined) { - changeTracker.insertNodeAtObjectStart(configFile, compilerOptions, createExperimentalDecoratorsAssignment()); - } - else { - changeTracker.replaceNodeWithText(configFile, experimentalDecoratorsProperty.initializer, "true"); - } - } - - function createCompilerOptionsAssignment() { - return createJsonPropertyAssignment( - "compilerOptions", - createObjectLiteral([ - createExperimentalDecoratorsAssignment(), - ]), - ); - } - - function createExperimentalDecoratorsAssignment() { - return createJsonPropertyAssignment("experimentalDecorators", createTrue()); + setJsonCompilerOptionValue(changeTracker, configFile, "experimentalDecorators", createTrue()); } } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 0344f51a854..66488d5d697 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -1,13 +1,5 @@ /* @internal */ namespace ts.codefix { - 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); - } - /** * Finds members of the resolved type that are missing in the class pointed to by class decl * and generates source code for the missing members. @@ -257,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); + } } From fadd95f72b5ad7f7f1cffa2b6ac82f612694462c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 11 Jan 2019 14:24:49 -0800 Subject: [PATCH 7/7] Fix unneeded cast lints (#29383) --- src/compiler/builder.ts | 3 ++- src/compiler/parser.ts | 13 +++++++------ src/harness/compiler.ts | 5 +++-- 3 files changed, 12 insertions(+), 9 deletions(-) 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/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())); } }