mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into add-codefix-cannot-find-name-in-for-loop
This commit is contained in:
@@ -16,7 +16,7 @@ Please fill in the *entire* template below.
|
||||
-->
|
||||
|
||||
<!-- Please try to reproduce the issue with `typescript@next`. It may have already been fixed. -->
|
||||
**TypeScript Version:** 3.2.0-dev.201xxxxx
|
||||
**TypeScript Version:** 3.3.0-dev.201xxxxx
|
||||
|
||||
<!-- Search terms you tried before logging this (so others can find this issue more easily) -->
|
||||
**Search Terms:**
|
||||
|
||||
+1
-5
@@ -16,11 +16,7 @@ matrix:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- release-2.7
|
||||
- release-2.8
|
||||
- release-2.9
|
||||
- release-3.0
|
||||
- release-3.1
|
||||
- /^release-.*/
|
||||
|
||||
install:
|
||||
- npm uninstall typescript --no-save
|
||||
|
||||
+14
-7
@@ -82,19 +82,26 @@ Your pull request should:
|
||||
* To avoid line ending issues, set `autocrlf = input` and `whitespace = cr-at-eol` in your git configuration
|
||||
|
||||
## Contributing `lib.d.ts` fixes
|
||||
|
||||
The library sources are in: [src/lib](https://github.com/Microsoft/TypeScript/tree/master/src/lib)
|
||||
|
||||
Library files in `built/local/` are updated by running
|
||||
```Shell
|
||||
There are three relevant locations to be aware of when it comes to TypeScript's library declaration files:
|
||||
|
||||
* `src/lib`: the location of the sources themselves.
|
||||
* `lib`: the location of the last-known-good (LKG) versions of the files which are updated periodically.
|
||||
* `built/local`: the build output location, including where `src/lib` files will be copied to.
|
||||
|
||||
Any changes should be made to [src/lib](https://github.com/Microsoft/TypeScript/tree/master/src/lib). **Most** of these files can be updated by hand, with the exception of any generated files (see below).
|
||||
|
||||
Library files in `built/local/` are updated automatically by running the standard build task:
|
||||
|
||||
```sh
|
||||
jake
|
||||
```
|
||||
|
||||
The files in `lib/` are used to bootstrap compilation and usually do not need to be updated.
|
||||
The files in `lib/` are used to bootstrap compilation and usually **should not** be updated unless publishing a new version or updating the LKG.
|
||||
|
||||
#### `src/lib/dom.generated.d.ts` and `src/lib/webworker.generated.d.ts`
|
||||
### Modifying generated library files
|
||||
|
||||
These two files represent the DOM typings and are auto-generated. To make any modifications to them, please submit a PR to https://github.com/Microsoft/TSJS-lib-generator
|
||||
The files `src/lib/dom.generated.d.ts` and `src/lib/webworker.generated.d.ts` both represent type declarations for the DOM and are auto-generated. To make any modifications to them, you will have to direct changes to https://github.com/Microsoft/TSJS-lib-generator
|
||||
|
||||
## Running the Tests
|
||||
|
||||
|
||||
+2
-1
@@ -117,7 +117,8 @@ const generatedLCGFile = "built/local/enu/diagnosticMessages.generated.json.lcg"
|
||||
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
|
||||
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
|
||||
*/
|
||||
const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"]
|
||||
const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"]
|
||||
.map(f => f.toLowerCase())
|
||||
.map(f => `built/local/${f}/diagnosticMessages.generated.json`)
|
||||
.concat(generatedLCGFile);
|
||||
|
||||
|
||||
-15
@@ -24,8 +24,6 @@ else if (process.env.PATH !== undefined) {
|
||||
|
||||
const host = process.env.TYPESCRIPT_HOST || process.env.host || "node";
|
||||
|
||||
const locales = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"];
|
||||
|
||||
const defaultTestTimeout = 40000;
|
||||
|
||||
let useDebugMode = true;
|
||||
@@ -709,19 +707,6 @@ const Travis = {
|
||||
}
|
||||
};
|
||||
|
||||
function buildLocalizedTargets() {
|
||||
/**
|
||||
* The localization target produces the two following transformations:
|
||||
* 1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
|
||||
* convert localized resources into a .json file the compiler can understand
|
||||
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
|
||||
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
|
||||
*/
|
||||
const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"]
|
||||
.map(f => path.join(Paths.builtLocal,f))
|
||||
.concat(path.dirname(Paths.generatedLCGFile));
|
||||
}
|
||||
|
||||
function toNs(diff) {
|
||||
return diff[0] * 1e9 + diff[1];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////namespace wwer./**/w
|
||||
|
||||
verify.completions({ marker: "", exact: [], isNewIdentifierLocation: true });
|
||||
@@ -51,6 +51,12 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";A_class_can_only_implement_an_object_type_or_intersection_of_object_types_with_statically_known_memb_2422" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A class can only implement an object type or intersection of object types with statically known members.]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";A_class_declaration_without_the_default_modifier_must_have_a_name_1211" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A class declaration without the 'default' modifier must have a name.]]></Val>
|
||||
@@ -63,12 +69,6 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";A_class_may_only_implement_another_class_or_interface_2422" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A class may only implement another class or interface.]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";A_class_member_cannot_have_the_0_keyword_1248" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[A class member cannot have the '{0}' keyword.]]></Val>
|
||||
@@ -693,6 +693,18 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Add_missing_new_operator_to_all_calls_95072" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add missing 'new' operator to all calls]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Add_missing_new_operator_to_call_95071" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add missing 'new' operator to call]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Add_missing_super_call_90001" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add missing 'super()' call]]></Val>
|
||||
@@ -705,12 +717,24 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Add_names_to_all_parameters_without_names_95073" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add names to all parameters without names]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Add_or_remove_braces_in_an_arrow_function_95058" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add or remove braces in an arrow function]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Add_parameter_name_90034" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add parameter name]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Add_qualifier_to_all_unresolved_variables_matching_a_member_name_95037" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Add qualifier to all unresolved variables matching a member name]]></Val>
|
||||
@@ -1077,9 +1101,9 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";An_interface_may_only_extend_a_class_or_another_interface_2312" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Item ItemId=";An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_me_2312" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[An interface may only extend a class or another interface.]]></Val>
|
||||
<Val><![CDATA[An interface can only extend an object type or intersection of object types with statically known members.]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
@@ -1209,9 +1233,9 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Base_constructor_return_type_0_is_not_a_class_or_interface_type_2509" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Item ItemId=";Base_constructor_return_type_0_is_not_an_object_type_or_intersection_of_object_types_with_statically_2509" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Base constructor return type '{0}' is not a class or interface type.]]></Val>
|
||||
<Val><![CDATA[Base constructor return type '{0}' is not an object type or intersection of object types with statically known members.]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
@@ -1317,9 +1341,15 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property_2540" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Item ItemId=";Cannot_assign_to_0_because_it_is_a_constant_2588" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Cannot assign to '{0}' because it is a constant or a read-only property.]]></Val>
|
||||
<Val><![CDATA[Cannot assign to '{0}' because it is a constant.]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Cannot_assign_to_0_because_it_is_a_read_only_property_2540" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Cannot assign to '{0}' because it is a read-only property.]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
@@ -4299,6 +4329,12 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1_7051" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Parameter has a name but no type. Did you mean '{0}: {1}'?]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2_4036" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Parameter type of public setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.]]></Val>
|
||||
@@ -5541,6 +5577,12 @@
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";The_inferred_type_of_0_cannot_be_named_without_a_reference_to_1_This_is_likely_not_portable_A_type_a_2742" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[The inferred type of '{0}' cannot be named without a reference to '{1}'. This is likely not portable. A type annotation is necessary.]]></Val>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
<Item ItemId=";The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary_2527" ItemType="0" PsrId="306" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[The inferred type of '{0}' references an inaccessible '{1}' type. A type annotation is necessary.]]></Val>
|
||||
|
||||
+404
-245
File diff suppressed because it is too large
Load Diff
+701
-396
File diff suppressed because it is too large
Load Diff
Vendored
+12
-5
@@ -14,7 +14,7 @@ and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
declare namespace ts {
|
||||
const versionMajorMinor = "3.2";
|
||||
const versionMajorMinor = "3.3";
|
||||
/** The version of the TypeScript compiler release */
|
||||
const version: string;
|
||||
}
|
||||
@@ -1976,7 +1976,8 @@ declare namespace ts {
|
||||
AllowEmptyTuple = 524288,
|
||||
AllowUniqueESSymbolType = 1048576,
|
||||
AllowEmptyIndexInfoType = 2097152,
|
||||
IgnoreErrors = 3112960,
|
||||
AllowNodeModulesRelativePaths = 67108864,
|
||||
IgnoreErrors = 70221824,
|
||||
InObjectTypeLiteral = 4194304,
|
||||
InTypeAlias = 8388608,
|
||||
InInitialEntityName = 16777216,
|
||||
@@ -3131,7 +3132,6 @@ declare namespace ts {
|
||||
function isIdentifierPart(ch: number, languageVersion: ScriptTarget | undefined): boolean;
|
||||
function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean, languageVariant?: LanguageVariant, textInitial?: string, onError?: ErrorCallback, start?: number, length?: number): Scanner;
|
||||
}
|
||||
/** Non-internal stuff goes here */
|
||||
declare namespace ts {
|
||||
function isExternalModuleNameRelative(moduleName: string): boolean;
|
||||
function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics: ReadonlyArray<T>): SortedReadonlyArray<T>;
|
||||
@@ -3478,6 +3478,7 @@ declare namespace ts {
|
||||
type TemplateLiteralToken = NoSubstitutionTemplateLiteral | TemplateHead | TemplateMiddle | TemplateTail;
|
||||
function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken;
|
||||
function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail;
|
||||
function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier;
|
||||
function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken;
|
||||
function isModifier(node: Node): node is Modifier;
|
||||
function isEntityName(node: Node): node is EntityName;
|
||||
@@ -4867,7 +4868,7 @@ declare namespace ts {
|
||||
message: string;
|
||||
position: number;
|
||||
}
|
||||
class TextChange {
|
||||
interface TextChange {
|
||||
span: TextSpan;
|
||||
newText: string;
|
||||
}
|
||||
@@ -8435,11 +8436,17 @@ declare namespace ts.server {
|
||||
}
|
||||
interface FileStats {
|
||||
readonly js: number;
|
||||
readonly jsSize?: number;
|
||||
readonly jsx: number;
|
||||
readonly jsxSize?: number;
|
||||
readonly ts: number;
|
||||
readonly tsSize?: number;
|
||||
readonly tsx: number;
|
||||
readonly tsxSize?: number;
|
||||
readonly dts: number;
|
||||
readonly dtsSize?: number;
|
||||
readonly deferred: number;
|
||||
readonly deferredSize?: number;
|
||||
}
|
||||
interface OpenFileInfo {
|
||||
readonly checkJs: boolean;
|
||||
@@ -8893,7 +8900,7 @@ declare namespace ts.server {
|
||||
private getFullNavigateToItems;
|
||||
private getSupportedCodeFixes;
|
||||
private isLocation;
|
||||
private extractPositionAndRange;
|
||||
private extractPositionOrRange;
|
||||
private getApplicableRefactors;
|
||||
private getEditsForRefactor;
|
||||
private organizeImports;
|
||||
|
||||
+652
-397
File diff suppressed because it is too large
Load Diff
Vendored
+5
-4
@@ -14,7 +14,7 @@ and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
declare namespace ts {
|
||||
const versionMajorMinor = "3.2";
|
||||
const versionMajorMinor = "3.3";
|
||||
/** The version of the TypeScript compiler release */
|
||||
const version: string;
|
||||
}
|
||||
@@ -1976,7 +1976,8 @@ declare namespace ts {
|
||||
AllowEmptyTuple = 524288,
|
||||
AllowUniqueESSymbolType = 1048576,
|
||||
AllowEmptyIndexInfoType = 2097152,
|
||||
IgnoreErrors = 3112960,
|
||||
AllowNodeModulesRelativePaths = 67108864,
|
||||
IgnoreErrors = 70221824,
|
||||
InObjectTypeLiteral = 4194304,
|
||||
InTypeAlias = 8388608,
|
||||
InInitialEntityName = 16777216,
|
||||
@@ -3131,7 +3132,6 @@ declare namespace ts {
|
||||
function isIdentifierPart(ch: number, languageVersion: ScriptTarget | undefined): boolean;
|
||||
function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean, languageVariant?: LanguageVariant, textInitial?: string, onError?: ErrorCallback, start?: number, length?: number): Scanner;
|
||||
}
|
||||
/** Non-internal stuff goes here */
|
||||
declare namespace ts {
|
||||
function isExternalModuleNameRelative(moduleName: string): boolean;
|
||||
function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics: ReadonlyArray<T>): SortedReadonlyArray<T>;
|
||||
@@ -3478,6 +3478,7 @@ declare namespace ts {
|
||||
type TemplateLiteralToken = NoSubstitutionTemplateLiteral | TemplateHead | TemplateMiddle | TemplateTail;
|
||||
function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken;
|
||||
function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail;
|
||||
function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier;
|
||||
function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken;
|
||||
function isModifier(node: Node): node is Modifier;
|
||||
function isEntityName(node: Node): node is EntityName;
|
||||
@@ -4867,7 +4868,7 @@ declare namespace ts {
|
||||
message: string;
|
||||
position: number;
|
||||
}
|
||||
class TextChange {
|
||||
interface TextChange {
|
||||
span: TextSpan;
|
||||
newText: string;
|
||||
}
|
||||
|
||||
+575
-378
File diff suppressed because it is too large
Load Diff
Vendored
+5
-4
@@ -14,7 +14,7 @@ and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
declare namespace ts {
|
||||
const versionMajorMinor = "3.2";
|
||||
const versionMajorMinor = "3.3";
|
||||
/** The version of the TypeScript compiler release */
|
||||
const version: string;
|
||||
}
|
||||
@@ -1976,7 +1976,8 @@ declare namespace ts {
|
||||
AllowEmptyTuple = 524288,
|
||||
AllowUniqueESSymbolType = 1048576,
|
||||
AllowEmptyIndexInfoType = 2097152,
|
||||
IgnoreErrors = 3112960,
|
||||
AllowNodeModulesRelativePaths = 67108864,
|
||||
IgnoreErrors = 70221824,
|
||||
InObjectTypeLiteral = 4194304,
|
||||
InTypeAlias = 8388608,
|
||||
InInitialEntityName = 16777216,
|
||||
@@ -3131,7 +3132,6 @@ declare namespace ts {
|
||||
function isIdentifierPart(ch: number, languageVersion: ScriptTarget | undefined): boolean;
|
||||
function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean, languageVariant?: LanguageVariant, textInitial?: string, onError?: ErrorCallback, start?: number, length?: number): Scanner;
|
||||
}
|
||||
/** Non-internal stuff goes here */
|
||||
declare namespace ts {
|
||||
function isExternalModuleNameRelative(moduleName: string): boolean;
|
||||
function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics: ReadonlyArray<T>): SortedReadonlyArray<T>;
|
||||
@@ -3478,6 +3478,7 @@ declare namespace ts {
|
||||
type TemplateLiteralToken = NoSubstitutionTemplateLiteral | TemplateHead | TemplateMiddle | TemplateTail;
|
||||
function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken;
|
||||
function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail;
|
||||
function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier;
|
||||
function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken;
|
||||
function isModifier(node: Node): node is Modifier;
|
||||
function isEntityName(node: Node): node is EntityName;
|
||||
@@ -4867,7 +4868,7 @@ declare namespace ts {
|
||||
message: string;
|
||||
position: number;
|
||||
}
|
||||
class TextChange {
|
||||
interface TextChange {
|
||||
span: TextSpan;
|
||||
newText: string;
|
||||
}
|
||||
|
||||
+575
-378
File diff suppressed because it is too large
Load Diff
+418
-250
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -2,7 +2,7 @@
|
||||
"name": "typescript",
|
||||
"author": "Microsoft Corp.",
|
||||
"homepage": "https://www.typescriptlang.org/",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "TypeScript is a language for application scale JavaScript development",
|
||||
"keywords": [
|
||||
@@ -82,6 +82,7 @@
|
||||
"mocha": "latest",
|
||||
"mocha-fivemat-progress-reporter": "latest",
|
||||
"plugin-error": "latest",
|
||||
"pretty-hrtime": "^1.0.3",
|
||||
"prex": "^0.4.3",
|
||||
"q": "latest",
|
||||
"remove-internal": "^2.9.2",
|
||||
|
||||
@@ -12,7 +12,7 @@ function baselineAccept(subfolder = "") {
|
||||
}
|
||||
|
||||
function baselineCopy(subfolder = "") {
|
||||
return gulp.src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**`, `!${localBaseline}${subfolder}/**/*.delete`], { base: localBaseline, read: false })
|
||||
return gulp.src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**`, `!${localBaseline}${subfolder}/**/*.delete`], { base: localBaseline })
|
||||
.pipe(gulp.dest(refBaseline));
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ function baselineDelete(subfolder = "") {
|
||||
.pipe(rm())
|
||||
.pipe(rename({ extname: "" }))
|
||||
.pipe(rm(refBaseline));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,11 +683,17 @@ function ensureCompileTask(projectGraph, options) {
|
||||
}
|
||||
});
|
||||
}
|
||||
const js = (projectGraphConfig.resolvedOptions.js ? projectGraphConfig.resolvedOptions.js(stream.js) : stream.js)
|
||||
|
||||
const additionalJsOutputs = projectGraphConfig.resolvedOptions.js ? projectGraphConfig.resolvedOptions.js(stream.js)
|
||||
.pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.write(sourceMapPath, sourceMapOptions))) : undefined;
|
||||
const additionalDtsOutputs = projectGraphConfig.resolvedOptions.dts ? projectGraphConfig.resolvedOptions.dts(stream.dts)
|
||||
.pipe(gulpif(declarationMap, sourcemaps.write(sourceMapPath, sourceMapOptions))) : undefined;
|
||||
|
||||
const js = stream.js
|
||||
.pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.write(sourceMapPath, sourceMapOptions)));
|
||||
const dts = (projectGraphConfig.resolvedOptions.dts ? projectGraphConfig.resolvedOptions.dts(stream.dts) : stream.dts)
|
||||
const dts = stream.dts
|
||||
.pipe(gulpif(declarationMap, sourcemaps.write(sourceMapPath, sourceMapOptions)));
|
||||
return merge2([js, dts])
|
||||
return merge2([js, dts, additionalJsOutputs, additionalDtsOutputs].filter(x => !!x))
|
||||
.pipe(gulp.dest(destPath));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function main(): void {
|
||||
console.error("Unexpected XML file structure. Expected to find result.LCX.$.TgtCul.");
|
||||
process.exit(1);
|
||||
}
|
||||
const outputDirectoryName = getPreferedLocaleName(result.LCX.$.TgtCul);
|
||||
const outputDirectoryName = getPreferedLocaleName(result.LCX.$.TgtCul).toLowerCase();
|
||||
if (!outputDirectoryName) {
|
||||
console.error(`Invalid output locale name for '${result.LCX.$.TgtCul}'.`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"module": "commonjs",
|
||||
"outDir": "../../built/local/tslint",
|
||||
"baseUrl": "../..",
|
||||
"types": ["node"],
|
||||
"paths": {
|
||||
"typescript": ["lib/typescript.d.ts"]
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace ts {
|
||||
|
||||
let symbolCount = 0;
|
||||
|
||||
let Symbol: { new (flags: SymbolFlags, name: __String): Symbol }; // tslint:disable-line variable-name
|
||||
let Symbol: new (flags: SymbolFlags, name: __String) => Symbol; // tslint:disable-line variable-name
|
||||
let classifiableNames: UnderscoreEscapedMap<true>;
|
||||
|
||||
const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
|
||||
@@ -233,6 +233,11 @@ namespace ts {
|
||||
symbol.members = createSymbolTable();
|
||||
}
|
||||
|
||||
// On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate)
|
||||
if (symbol.constEnumOnlyModule && (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) {
|
||||
symbol.constEnumOnlyModule = false;
|
||||
}
|
||||
|
||||
if (symbolFlags & SymbolFlags.Value) {
|
||||
setValueDeclaration(symbol, node);
|
||||
}
|
||||
@@ -748,7 +753,7 @@ namespace ts {
|
||||
|
||||
function isNarrowableReference(expr: Expression): boolean {
|
||||
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
|
||||
isPropertyAccessExpression(expr) && isNarrowableReference(expr.expression) ||
|
||||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
|
||||
isElementAccessExpression(expr) && expr.argumentExpression &&
|
||||
(isStringLiteral(expr.argumentExpression) || isNumericLiteral(expr.argumentExpression)) &&
|
||||
isNarrowableReference(expr.expression);
|
||||
@@ -2608,7 +2613,7 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
const node = symbol.valueDeclaration;
|
||||
if (isCallExpression(node)) {
|
||||
if (node && isCallExpression(node)) {
|
||||
return !!getAssignedExpandoInitializer(node);
|
||||
}
|
||||
let init = !node ? undefined :
|
||||
|
||||
+24
-8
@@ -64,7 +64,9 @@ namespace ts {
|
||||
const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderProgramState;
|
||||
state.program = newProgram;
|
||||
const compilerOptions = newProgram.getCompilerOptions();
|
||||
if (!compilerOptions.outFile && !compilerOptions.out) {
|
||||
// With --out or --outFile, any change affects all semantic diagnostics so no need to cache them
|
||||
// With --isolatedModules, emitting changed file doesnt emit dependent files so we cant know of dependent files to retrieve errors so dont cache the errors
|
||||
if (!compilerOptions.outFile && !compilerOptions.out && !compilerOptions.isolatedModules) {
|
||||
state.semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>();
|
||||
}
|
||||
state.changedFilesSet = createMap<true>();
|
||||
@@ -76,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");
|
||||
@@ -281,10 +284,19 @@ namespace ts {
|
||||
}
|
||||
|
||||
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
|
||||
return !!forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) =>
|
||||
if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) =>
|
||||
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
|
||||
exportedModules.has(filePath) &&
|
||||
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile)
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove diagnostics of files that import this file (without going to exports of referencing files)
|
||||
return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) =>
|
||||
referencesInFile.has(filePath) &&
|
||||
!seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file
|
||||
removeSemanticDiagnosticsOf(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal
|
||||
);
|
||||
}
|
||||
|
||||
@@ -329,15 +341,19 @@ namespace ts {
|
||||
*/
|
||||
function getSemanticDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
|
||||
const path = sourceFile.path;
|
||||
const cachedDiagnostics = state.semanticDiagnosticsPerFile!.get(path);
|
||||
// Report the semantic diagnostics from the cache if we already have those diagnostics present
|
||||
if (cachedDiagnostics) {
|
||||
return cachedDiagnostics;
|
||||
if (state.semanticDiagnosticsPerFile) {
|
||||
const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path);
|
||||
// Report the semantic diagnostics from the cache if we already have those diagnostics present
|
||||
if (cachedDiagnostics) {
|
||||
return cachedDiagnostics;
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostics werent cached, get them from program, and cache the result
|
||||
const diagnostics = state.program.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
state.semanticDiagnosticsPerFile!.set(path, diagnostics);
|
||||
if (state.semanticDiagnosticsPerFile) {
|
||||
state.semanticDiagnosticsPerFile.set(path, diagnostics);
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
|
||||
+860
-478
File diff suppressed because it is too large
Load Diff
@@ -119,6 +119,18 @@ namespace ts {
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Enable_tracing_of_the_name_resolution_process
|
||||
},
|
||||
{
|
||||
name: "diagnostics",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Show_diagnostic_information
|
||||
},
|
||||
{
|
||||
name: "extendedDiagnostics",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Show_verbose_diagnostic_information
|
||||
},
|
||||
];
|
||||
|
||||
/* @internal */
|
||||
@@ -592,18 +604,6 @@ namespace ts {
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_e_g_React_createElement_or_h
|
||||
},
|
||||
{
|
||||
name: "diagnostics",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Show_diagnostic_information
|
||||
},
|
||||
{
|
||||
name: "extendedDiagnostics",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Show_verbose_diagnostic_information
|
||||
},
|
||||
{
|
||||
name: "resolveJsonModule",
|
||||
type: "boolean",
|
||||
@@ -1673,13 +1673,13 @@ namespace ts {
|
||||
const files = map(
|
||||
filter(
|
||||
configParseResult.fileNames,
|
||||
!configParseResult.configFileSpecs ? _ => false : matchesSpecs(
|
||||
(!configParseResult.configFileSpecs || !configParseResult.configFileSpecs.validatedIncludeSpecs) ? _ => true : matchesSpecs(
|
||||
configFileName,
|
||||
configParseResult.configFileSpecs.validatedIncludeSpecs,
|
||||
configParseResult.configFileSpecs.validatedExcludeSpecs
|
||||
)
|
||||
),
|
||||
f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), f, getCanonicalFileName)
|
||||
f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), getNormalizedAbsolutePath(f, host.getCurrentDirectory()), getCanonicalFileName)
|
||||
);
|
||||
const optionMap = serializeCompilerOptions(configParseResult.options, { configFilePath: getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), useCaseSensitiveFileNames: host.useCaseSensitiveFileNames });
|
||||
const config = {
|
||||
@@ -1693,6 +1693,8 @@ namespace ts {
|
||||
listFiles: undefined,
|
||||
listEmittedFiles: undefined,
|
||||
project: undefined,
|
||||
build: undefined,
|
||||
version: undefined,
|
||||
},
|
||||
references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath, originalPath: undefined })),
|
||||
files: length(files) ? files : undefined,
|
||||
@@ -1713,24 +1715,24 @@ namespace ts {
|
||||
}
|
||||
|
||||
function matchesSpecs(path: string, includeSpecs: ReadonlyArray<string> | undefined, excludeSpecs: ReadonlyArray<string> | undefined): (path: string) => boolean {
|
||||
if (!includeSpecs) return _ => false;
|
||||
if (!includeSpecs) return _ => true;
|
||||
const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, sys.useCaseSensitiveFileNames, sys.getCurrentDirectory());
|
||||
const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, sys.useCaseSensitiveFileNames);
|
||||
const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, sys.useCaseSensitiveFileNames);
|
||||
if (includeRe) {
|
||||
if (excludeRe) {
|
||||
return path => includeRe.test(path) && !excludeRe.test(path);
|
||||
return path => !(includeRe.test(path) && !excludeRe.test(path));
|
||||
}
|
||||
return path => includeRe.test(path);
|
||||
return path => !includeRe.test(path);
|
||||
}
|
||||
if (excludeRe) {
|
||||
return path => !excludeRe.test(path);
|
||||
return path => excludeRe.test(path);
|
||||
}
|
||||
return _ => false;
|
||||
return _ => true;
|
||||
}
|
||||
|
||||
function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map<string | number> | undefined {
|
||||
if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") {
|
||||
if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean" || optionDefinition.type === "object") {
|
||||
// this is of a type CommandLineOptionOfPrimitiveType
|
||||
return undefined;
|
||||
}
|
||||
@@ -1893,7 +1895,7 @@ namespace ts {
|
||||
}
|
||||
result.push(`}`);
|
||||
|
||||
return result.join(newLine);
|
||||
return result.join(newLine) + newLine;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ts {
|
||||
// WARNING: The script `configureNightly.ts` uses a regexp to parse out these values.
|
||||
// If changing the text in this section, be sure to test `configureNightly` too.
|
||||
export const versionMajorMinor = "3.2";
|
||||
export const versionMajorMinor = "3.3";
|
||||
/** The version of the TypeScript compiler release */
|
||||
export const version = `${versionMajorMinor}.0-dev`;
|
||||
}
|
||||
@@ -112,13 +112,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
// The global Map object. This may not be available, so we must test for it.
|
||||
declare const Map: { new <T>(): Map<T> } | undefined;
|
||||
declare const Map: (new <T>() => Map<T>) | undefined;
|
||||
// Internet Explorer's Map doesn't support iteration, so don't use it.
|
||||
// tslint:disable-next-line no-in-operator variable-name
|
||||
export const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap();
|
||||
|
||||
// Keep the class inside a function so it doesn't get compiled if it's not used.
|
||||
function shimMap(): { new <T>(): Map<T> } {
|
||||
function shimMap(): new <T>() => Map<T> {
|
||||
|
||||
class MapIterator<T, U extends (string | T | [string, T])> {
|
||||
private data: MapLike<T>;
|
||||
@@ -2165,6 +2165,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function fill<T>(length: number, cb: (index: number) => T): T[] {
|
||||
return new Array(length).fill(0).map((_, i) => cb(i));
|
||||
const result = Array<T>(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
result[i] = cb(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1011,6 +1011,18 @@
|
||||
"category": "Message",
|
||||
"code": 1350
|
||||
},
|
||||
"An identifier or keyword cannot immediately follow a numeric literal.": {
|
||||
"category": "Error",
|
||||
"code": 1351
|
||||
},
|
||||
"A bigint literal cannot use exponential notation.": {
|
||||
"category": "Error",
|
||||
"code": 1352
|
||||
},
|
||||
"A bigint literal must be an integer.": {
|
||||
"category": "Error",
|
||||
"code": 1353
|
||||
},
|
||||
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
@@ -1632,14 +1644,6 @@
|
||||
"category": "Error",
|
||||
"code": 2458
|
||||
},
|
||||
"Type '{0}' has no property '{1}' and no string index signature.": {
|
||||
"category": "Error",
|
||||
"code": 2459
|
||||
},
|
||||
"Type '{0}' has no property '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2460
|
||||
},
|
||||
"Type '{0}' is not an array type.": {
|
||||
"category": "Error",
|
||||
"code": 2461
|
||||
@@ -1692,7 +1696,7 @@
|
||||
"category": "Error",
|
||||
"code": 2473
|
||||
},
|
||||
"In 'const' enum declarations member initializer must be constant expression.": {
|
||||
"const enum member initializers can only contain literal values and other computed enum values.": {
|
||||
"category": "Error",
|
||||
"code": 2474
|
||||
},
|
||||
@@ -1756,7 +1760,7 @@
|
||||
"category": "Error",
|
||||
"code": 2492
|
||||
},
|
||||
"Tuple type '{0}' with length '{1}' cannot be assigned to tuple with length '{2}'.": {
|
||||
"Tuple type '{0}' of length '{1}' has no element at index '{2}'.": {
|
||||
"category": "Error",
|
||||
"code": 2493
|
||||
},
|
||||
@@ -1772,7 +1776,7 @@
|
||||
"category": "Error",
|
||||
"code": 2496
|
||||
},
|
||||
"Module '{0}' resolves to a non-module entity and cannot be imported using this construct.": {
|
||||
"This module can only be referenced with ECMAScript imports/exports by turning on the '{0}' flag and referencing its default export.": {
|
||||
"category": "Error",
|
||||
"code": 2497
|
||||
},
|
||||
@@ -2052,10 +2056,6 @@
|
||||
"category": "Error",
|
||||
"code": 2567
|
||||
},
|
||||
"Type '{0}' is not an array type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": {
|
||||
"category": "Error",
|
||||
"code": 2568
|
||||
},
|
||||
"Type '{0}' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": {
|
||||
"category": "Error",
|
||||
"code": 2569
|
||||
@@ -2529,6 +2529,26 @@
|
||||
"category": "Error",
|
||||
"code": 2742
|
||||
},
|
||||
"No overload expects {0} type arguments, but overloads do exist that expect either {1} or {2} type arguments.": {
|
||||
"category": "Error",
|
||||
"code": 2743
|
||||
},
|
||||
"Type parameter defaults can only reference previously declared type parameters.": {
|
||||
"category": "Error",
|
||||
"code": 2744
|
||||
},
|
||||
"This JSX tag's '{0}' prop expects type '{1}' which requires multiple children, but only a single child was provided.": {
|
||||
"category": "Error",
|
||||
"code": 2745
|
||||
},
|
||||
"This JSX tag's '{0}' prop expects a single child of type '{1}', but multiple children were provided.": {
|
||||
"category": "Error",
|
||||
"code": 2746
|
||||
},
|
||||
"'{0}' components don't accept text as child elements. Text in JSX has the type 'string', but the expected type of '{1}' is '{2}'.": {
|
||||
"category": "Error",
|
||||
"code": 2747
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
@@ -4254,6 +4274,10 @@
|
||||
"category": "Error",
|
||||
"code": 8031
|
||||
},
|
||||
"Qualified name '{0}' is not allowed without a leading '@param {object} {1}'.": {
|
||||
"category": "Error",
|
||||
"code": 8032
|
||||
},
|
||||
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
|
||||
"category": "Error",
|
||||
"code": 9002
|
||||
@@ -4800,12 +4824,16 @@
|
||||
"category": "Message",
|
||||
"code": 95073
|
||||
},
|
||||
"Add 'const' to unresolved variable": {
|
||||
"Enable the 'experimentalDecorators' option in your configuration file": {
|
||||
"category": "Message",
|
||||
"code": 95074
|
||||
},
|
||||
"Add 'const' to all unresolved variables": {
|
||||
"Add 'const' to unresolved variable": {
|
||||
"category": "Message",
|
||||
"code": 95075
|
||||
},
|
||||
"Add 'const' to all unresolved variables": {
|
||||
"category": "Message",
|
||||
"code": 95076
|
||||
}
|
||||
}
|
||||
|
||||
+19
-8
@@ -1651,11 +1651,17 @@ namespace ts {
|
||||
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
|
||||
let indentBeforeDot = false;
|
||||
let indentAfterDot = false;
|
||||
const dotRangeFirstCommentStart = skipTrivia(
|
||||
currentSourceFile!.text,
|
||||
node.expression.end,
|
||||
/*stopAfterLineBreak*/ false,
|
||||
/*stopAtComments*/ true
|
||||
);
|
||||
const dotRangeStart = skipTrivia(currentSourceFile!.text, dotRangeFirstCommentStart);
|
||||
const dotRangeEnd = dotRangeStart + 1;
|
||||
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
|
||||
const dotRangeStart = node.expression.end;
|
||||
const dotRangeEnd = skipTrivia(currentSourceFile!.text, node.expression.end) + 1;
|
||||
const dotToken = createToken(SyntaxKind.DotToken);
|
||||
dotToken.pos = dotRangeStart;
|
||||
dotToken.pos = node.expression.end;
|
||||
dotToken.end = dotRangeEnd;
|
||||
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
|
||||
indentAfterDot = needsIndentation(node, dotToken, node.name);
|
||||
@@ -1664,7 +1670,8 @@ namespace ts {
|
||||
emitExpression(node.expression);
|
||||
increaseIndentIf(indentBeforeDot, /*writeSpaceIfNotIndenting*/ false);
|
||||
|
||||
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
|
||||
const dotHasCommentTrivia = dotRangeFirstCommentStart !== dotRangeStart;
|
||||
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression, dotHasCommentTrivia);
|
||||
if (shouldEmitDotDot) {
|
||||
writePunctuation(".");
|
||||
}
|
||||
@@ -1677,13 +1684,15 @@ namespace ts {
|
||||
|
||||
// 1..toString is a valid property access, emit a dot after the literal
|
||||
// Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
|
||||
function needsDotDotForPropertyAccess(expression: Expression) {
|
||||
function needsDotDotForPropertyAccess(expression: Expression, dotHasTrivia: boolean) {
|
||||
expression = skipPartiallyEmittedExpressions(expression);
|
||||
if (isNumericLiteral(expression)) {
|
||||
// check if numeric literal is a decimal literal that was originally written with a dot
|
||||
const text = getLiteralTextOfNode(<LiteralExpression>expression, /*neverAsciiEscape*/ true);
|
||||
return !expression.numericLiteralFlags
|
||||
&& !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
|
||||
// If he number will be printed verbatim and it doesn't already contain a dot, add one
|
||||
// if the expression doesn't have any comments that will be emitted.
|
||||
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!) &&
|
||||
(!dotHasTrivia || printerOptions.removeComments);
|
||||
}
|
||||
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
|
||||
// check if constant enum value is integer
|
||||
@@ -2552,6 +2561,7 @@ namespace ts {
|
||||
function emitJsxSelfClosingElement(node: JsxSelfClosingElement) {
|
||||
writePunctuation("<");
|
||||
emitJsxTagName(node.tagName);
|
||||
emitTypeArguments(node, node.typeArguments);
|
||||
writeSpace();
|
||||
emit(node.attributes);
|
||||
writePunctuation("/>");
|
||||
@@ -2568,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();
|
||||
}
|
||||
@@ -4380,7 +4391,7 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(currentSourceFile!, pos);
|
||||
const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(sourceMapSource, pos);
|
||||
sourceMapGenerator!.addMapping(
|
||||
writer.getLine(),
|
||||
writer.getColumn(),
|
||||
|
||||
@@ -40,18 +40,18 @@ namespace ts {
|
||||
|
||||
type Recurser = <T>(obj: unknown, name: string, cbOk: () => T, cbFail: (isCircularReference: boolean, keyStack: ReadonlyArray<string>) => T) => T;
|
||||
function getRecurser(): Recurser {
|
||||
const seen = new Set<unknown>();
|
||||
const seen: unknown[] = [];
|
||||
const nameStack: string[] = [];
|
||||
return (obj, name, cbOk, cbFail) => {
|
||||
if (seen.has(obj) || nameStack.length > 4) {
|
||||
return cbFail(seen.has(obj), nameStack);
|
||||
if (seen.indexOf(obj) !== -1 || nameStack.length > 4) {
|
||||
return cbFail(seen.indexOf(obj) !== -1, nameStack);
|
||||
}
|
||||
|
||||
seen.add(obj);
|
||||
seen.push(obj);
|
||||
nameStack.push(name);
|
||||
const res = cbOk();
|
||||
nameStack.pop();
|
||||
seen.delete(obj);
|
||||
seen.pop();
|
||||
return res;
|
||||
};
|
||||
}
|
||||
@@ -104,8 +104,8 @@ namespace ts {
|
||||
key === "constructor" ? undefined : getValueInfo(key, value, recurser));
|
||||
}
|
||||
|
||||
const ignoredProperties: ReadonlySet<string> = new Set(["arguments", "caller", "constructor", "eval", "super_"]);
|
||||
const reservedFunctionProperties: ReadonlySet<string> = new Set(Object.getOwnPropertyNames(noop));
|
||||
const ignoredProperties: ReadonlyArray<string> = ["arguments", "caller", "constructor", "eval", "super_"];
|
||||
const reservedFunctionProperties: ReadonlyArray<string> = Object.getOwnPropertyNames(noop);
|
||||
interface ObjectEntry { readonly key: string; readonly value: unknown; }
|
||||
function getEntriesOfObject(obj: object): ReadonlyArray<ObjectEntry> {
|
||||
const seen = createMap<true>();
|
||||
@@ -114,8 +114,8 @@ namespace ts {
|
||||
while (!isNullOrUndefined(chain) && chain !== Object.prototype && chain !== Function.prototype) {
|
||||
for (const key of Object.getOwnPropertyNames(chain)) {
|
||||
if (!isJsPrivate(key) &&
|
||||
!ignoredProperties.has(key) &&
|
||||
(typeof obj !== "function" || !reservedFunctionProperties.has(key)) &&
|
||||
ignoredProperties.indexOf(key) === -1 &&
|
||||
(typeof obj !== "function" || reservedFunctionProperties.indexOf(key) === -1) &&
|
||||
// Don't add property from a higher prototype if it already exists in a lower one
|
||||
addToSeen(seen, key)) {
|
||||
const value = safeGetPropertyOfObject(chain, key);
|
||||
@@ -148,7 +148,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isJsPrivate(name: string): boolean {
|
||||
return name.startsWith("_");
|
||||
return startsWith(name, "_");
|
||||
}
|
||||
|
||||
function tryRequire(fileNameToRequire: string): unknown {
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace ts.moduleSpecifiers {
|
||||
return; // Don't want to a package to globally import from itself
|
||||
}
|
||||
|
||||
const target = targets.find(t => compareStrings(t.slice(0, resolved.length + 1), resolved + "/") === Comparison.EqualTo);
|
||||
const target = find(targets, t => compareStrings(t.slice(0, resolved.length + 1), resolved + "/") === Comparison.EqualTo);
|
||||
if (target === undefined) return;
|
||||
|
||||
const relative = getRelativePathFromDirectory(resolved, target, getCanonicalFileName);
|
||||
|
||||
+38
-33
@@ -1717,6 +1717,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function currentNode(parsingContext: ParsingContext): Node | undefined {
|
||||
// If we don't have a cursor or the parsing context isn't reusable, there's nothing to reuse.
|
||||
//
|
||||
// If there is an outstanding parse error that we've encountered, but not attached to
|
||||
// some node, then we cannot get a node from the old source tree. This is because we
|
||||
// want to mark the next node we encounter as being unusable.
|
||||
@@ -1724,30 +1726,17 @@ namespace ts {
|
||||
// Note: This may be too conservative. Perhaps we could reuse the node and set the bit
|
||||
// on it (or its leftmost child) as having the error. For now though, being conservative
|
||||
// is nice and likely won't ever affect perf.
|
||||
if (parseErrorBeforeNextFinishedNode) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!syntaxCursor) {
|
||||
// if we don't have a cursor, we could never return a node from the old tree.
|
||||
if (!syntaxCursor || !isReusableParsingContext(parsingContext) || parseErrorBeforeNextFinishedNode) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const node = syntaxCursor.currentNode(scanner.getStartPos());
|
||||
|
||||
// Can't reuse a missing node.
|
||||
if (nodeIsMissing(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Can't reuse a node that intersected the change range.
|
||||
if (node.intersectsChange) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Can't reuse a node that contains a parse error. This is necessary so that we
|
||||
// produce the same set of errors again.
|
||||
if (containsParseError(node)) {
|
||||
if (nodeIsMissing(node) || node.intersectsChange || containsParseError(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1788,6 +1777,23 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
function isReusableParsingContext(parsingContext: ParsingContext): boolean {
|
||||
switch (parsingContext) {
|
||||
case ParsingContext.ClassMembers:
|
||||
case ParsingContext.SwitchClauses:
|
||||
case ParsingContext.SourceElements:
|
||||
case ParsingContext.BlockStatements:
|
||||
case ParsingContext.SwitchClauseStatements:
|
||||
case ParsingContext.EnumMembers:
|
||||
case ParsingContext.TypeMembers:
|
||||
case ParsingContext.VariableDeclarations:
|
||||
case ParsingContext.JSDocParameters:
|
||||
case ParsingContext.Parameters:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function canReuseNode(node: Node, parsingContext: ParsingContext): boolean {
|
||||
switch (parsingContext) {
|
||||
case ParsingContext.ClassMembers:
|
||||
@@ -1814,25 +1820,23 @@ namespace ts {
|
||||
case ParsingContext.Parameters:
|
||||
return isReusableParameter(node);
|
||||
|
||||
case ParsingContext.RestProperties:
|
||||
return false;
|
||||
|
||||
// Any other lists we do not care about reusing nodes in. But feel free to add if
|
||||
// you can do so safely. Danger areas involve nodes that may involve speculative
|
||||
// parsing. If speculative parsing is involved with the node, then the range the
|
||||
// parser reached while looking ahead might be in the edited range (see the example
|
||||
// in canReuseVariableDeclaratorNode for a good case of this).
|
||||
case ParsingContext.HeritageClauses:
|
||||
|
||||
// case ParsingContext.HeritageClauses:
|
||||
// This would probably be safe to reuse. There is no speculative parsing with
|
||||
// heritage clauses.
|
||||
|
||||
case ParsingContext.TypeParameters:
|
||||
// case ParsingContext.TypeParameters:
|
||||
// This would probably be safe to reuse. There is no speculative parsing with
|
||||
// type parameters. Note that that's because type *parameters* only occur in
|
||||
// unambiguous *type* contexts. While type *arguments* occur in very ambiguous
|
||||
// *expression* contexts.
|
||||
|
||||
case ParsingContext.TupleElementTypes:
|
||||
// case ParsingContext.TupleElementTypes:
|
||||
// This would probably be safe to reuse. There is no speculative parsing with
|
||||
// tuple types.
|
||||
|
||||
@@ -1841,28 +1845,28 @@ namespace ts {
|
||||
// produced from speculative parsing a < as a type argument list), we only have
|
||||
// the types because speculative parsing succeeded. Thus, the lookahead never
|
||||
// went past the end of the list and rewound.
|
||||
case ParsingContext.TypeArguments:
|
||||
// case ParsingContext.TypeArguments:
|
||||
|
||||
// Note: these are almost certainly not safe to ever reuse. Expressions commonly
|
||||
// need a large amount of lookahead, and we should not reuse them as they may
|
||||
// have actually intersected the edit.
|
||||
case ParsingContext.ArgumentExpressions:
|
||||
// case ParsingContext.ArgumentExpressions:
|
||||
|
||||
// This is not safe to reuse for the same reason as the 'AssignmentExpression'
|
||||
// cases. i.e. a property assignment may end with an expression, and thus might
|
||||
// have lookahead far beyond it's old node.
|
||||
case ParsingContext.ObjectLiteralMembers:
|
||||
// case ParsingContext.ObjectLiteralMembers:
|
||||
|
||||
// This is probably not safe to reuse. There can be speculative parsing with
|
||||
// type names in a heritage clause. There can be generic names in the type
|
||||
// name list, and there can be left hand side expressions (which can have type
|
||||
// arguments.)
|
||||
case ParsingContext.HeritageClauseElement:
|
||||
// case ParsingContext.HeritageClauseElement:
|
||||
|
||||
// Perhaps safe to reuse, but it's unlikely we'd see more than a dozen attributes
|
||||
// on any given element. Same for children.
|
||||
case ParsingContext.JsxAttributes:
|
||||
case ParsingContext.JsxChildren:
|
||||
// case ParsingContext.JsxAttributes:
|
||||
// case ParsingContext.JsxChildren:
|
||||
|
||||
}
|
||||
|
||||
@@ -7770,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);
|
||||
|
||||
+154
-25
@@ -73,7 +73,6 @@ namespace ts {
|
||||
// TODO(shkamat): update this after reworking ts build API
|
||||
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
|
||||
const existingDirectories = createMap<boolean>();
|
||||
|
||||
function getCanonicalFileName(fileName: string): string {
|
||||
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
|
||||
// otherwise use toLowerCase as a canonical form.
|
||||
@@ -84,7 +83,7 @@ namespace ts {
|
||||
let text: string | undefined;
|
||||
try {
|
||||
performance.mark("beforeIORead");
|
||||
text = system.readFile(fileName, options.charset);
|
||||
text = compilerHost.readFile(fileName);
|
||||
performance.mark("afterIORead");
|
||||
performance.measure("I/O Read", "beforeIORead", "afterIORead");
|
||||
}
|
||||
@@ -113,7 +112,12 @@ namespace ts {
|
||||
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
|
||||
const parentDirectory = getDirectoryPath(directoryPath);
|
||||
ensureDirectoriesExist(parentDirectory);
|
||||
system.createDirectory(directoryPath);
|
||||
if (compilerHost.createDirectory) {
|
||||
compilerHost.createDirectory(directoryPath);
|
||||
}
|
||||
else {
|
||||
system.createDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +181,7 @@ namespace ts {
|
||||
|
||||
const newLine = getNewLineCharacter(options, () => system.newLine);
|
||||
const realpath = system.realpath && ((path: string) => system.realpath!(path));
|
||||
|
||||
return {
|
||||
const compilerHost: CompilerHost = {
|
||||
getSourceFile,
|
||||
getDefaultLibLocation,
|
||||
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
|
||||
@@ -194,7 +197,117 @@ namespace ts {
|
||||
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
|
||||
getDirectories: (path: string) => system.getDirectories(path),
|
||||
realpath,
|
||||
readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth)
|
||||
readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth),
|
||||
createDirectory: d => system.createDirectory(d)
|
||||
};
|
||||
return compilerHost;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function changeCompilerHostToUseCache(
|
||||
host: CompilerHost,
|
||||
toPath: (fileName: string) => Path,
|
||||
useCacheForSourceFile: boolean
|
||||
) {
|
||||
const originalReadFile = host.readFile;
|
||||
const originalFileExists = host.fileExists;
|
||||
const originalDirectoryExists = host.directoryExists;
|
||||
const originalCreateDirectory = host.createDirectory;
|
||||
const originalWriteFile = host.writeFile;
|
||||
const originalGetSourceFile = host.getSourceFile;
|
||||
const readFileCache = createMap<string | false>();
|
||||
const fileExistsCache = createMap<boolean>();
|
||||
const directoryExistsCache = createMap<boolean>();
|
||||
const sourceFileCache = createMap<SourceFile>();
|
||||
|
||||
const readFileWithCache = (fileName: string): string | undefined => {
|
||||
const key = toPath(fileName);
|
||||
const value = readFileCache.get(key);
|
||||
if (value !== undefined) return value || undefined;
|
||||
return setReadFileCache(key, fileName);
|
||||
};
|
||||
const setReadFileCache = (key: Path, fileName: string) => {
|
||||
const newValue = originalReadFile.call(host, fileName);
|
||||
readFileCache.set(key, newValue || false);
|
||||
return newValue;
|
||||
};
|
||||
host.readFile = fileName => {
|
||||
const key = toPath(fileName);
|
||||
const value = readFileCache.get(key);
|
||||
if (value !== undefined) return value; // could be .d.ts from output
|
||||
if (!fileExtensionIs(fileName, Extension.Json)) {
|
||||
return originalReadFile.call(host, fileName);
|
||||
}
|
||||
|
||||
return setReadFileCache(key, fileName);
|
||||
};
|
||||
|
||||
if (useCacheForSourceFile) {
|
||||
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
||||
const key = toPath(fileName);
|
||||
const value = sourceFileCache.get(key);
|
||||
if (value) return value;
|
||||
|
||||
const sourceFile = originalGetSourceFile.call(host, fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
||||
if (sourceFile && (isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json))) {
|
||||
sourceFileCache.set(key, sourceFile);
|
||||
}
|
||||
return sourceFile;
|
||||
};
|
||||
}
|
||||
|
||||
// fileExists for any kind of extension
|
||||
host.fileExists = fileName => {
|
||||
const key = toPath(fileName);
|
||||
const value = fileExistsCache.get(key);
|
||||
if (value !== undefined) return value;
|
||||
const newValue = originalFileExists.call(host, fileName);
|
||||
fileExistsCache.set(key, !!newValue);
|
||||
return newValue;
|
||||
};
|
||||
host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
|
||||
const key = toPath(fileName);
|
||||
fileExistsCache.delete(key);
|
||||
|
||||
const value = readFileCache.get(key);
|
||||
if (value && value !== data) {
|
||||
readFileCache.delete(key);
|
||||
sourceFileCache.delete(key);
|
||||
}
|
||||
else if (useCacheForSourceFile) {
|
||||
const sourceFile = sourceFileCache.get(key);
|
||||
if (sourceFile && sourceFile.text !== data) {
|
||||
sourceFileCache.delete(key);
|
||||
}
|
||||
}
|
||||
originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
};
|
||||
|
||||
// directoryExists
|
||||
if (originalDirectoryExists && originalCreateDirectory) {
|
||||
host.directoryExists = directory => {
|
||||
const key = toPath(directory);
|
||||
const value = directoryExistsCache.get(key);
|
||||
if (value !== undefined) return value;
|
||||
const newValue = originalDirectoryExists.call(host, directory);
|
||||
directoryExistsCache.set(key, !!newValue);
|
||||
return newValue;
|
||||
};
|
||||
host.createDirectory = directory => {
|
||||
const key = toPath(directory);
|
||||
directoryExistsCache.delete(key);
|
||||
originalCreateDirectory.call(host, directory);
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
originalReadFile,
|
||||
originalFileExists,
|
||||
originalDirectoryExists,
|
||||
originalCreateDirectory,
|
||||
originalWriteFile,
|
||||
originalGetSourceFile,
|
||||
readFileWithCache
|
||||
};
|
||||
}
|
||||
|
||||
@@ -674,7 +787,13 @@ namespace ts {
|
||||
// Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it.
|
||||
let redirectTargetsMap = createMultiMap<string>();
|
||||
|
||||
const filesByName = createMap<SourceFile | undefined>();
|
||||
/**
|
||||
* map with
|
||||
* - SourceFile if present
|
||||
* - false if sourceFile missing for source of project reference redirect
|
||||
* - undefined otherwise
|
||||
*/
|
||||
const filesByName = createMap<SourceFile | false | undefined>();
|
||||
let missingFilePaths: ReadonlyArray<Path> | undefined;
|
||||
// stores 'filename -> file association' ignoring case
|
||||
// used to track cases when two file names differ only in casing
|
||||
@@ -683,6 +802,7 @@ namespace ts {
|
||||
// A parallel array to projectReferences storing the results of reading in the referenced tsconfig files
|
||||
let resolvedProjectReferences: ReadonlyArray<ResolvedProjectReference | undefined> | undefined;
|
||||
let projectReferenceRedirects: Map<ResolvedProjectReference | false> | undefined;
|
||||
let mapFromFileToProjectReferenceRedirects: Map<Path> | undefined;
|
||||
|
||||
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
|
||||
const structuralIsReused = tryReuseStructureFromOldProgram();
|
||||
@@ -740,7 +860,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
missingFilePaths = arrayFrom(filesByName.keys(), p => <Path>p).filter(p => !filesByName.get(p));
|
||||
missingFilePaths = arrayFrom(mapDefinedIterator(filesByName.entries(), ([path, file]) => file === undefined ? path as Path : undefined));
|
||||
files = stableSort(processingDefaultLibFiles, compareDefaultLibFiles).concat(processingOtherFiles);
|
||||
processingDefaultLibFiles = undefined;
|
||||
processingOtherFiles = undefined;
|
||||
@@ -1453,7 +1573,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getSourceFileByPath(path: Path): SourceFile | undefined {
|
||||
return filesByName.get(path);
|
||||
return filesByName.get(path) || undefined;
|
||||
}
|
||||
|
||||
function getDiagnosticsHelper<T extends Diagnostic>(
|
||||
@@ -1990,7 +2110,7 @@ namespace ts {
|
||||
|
||||
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
|
||||
function getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined {
|
||||
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)));
|
||||
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)) || undefined);
|
||||
}
|
||||
|
||||
function getSourceFileFromReferenceWorker(
|
||||
@@ -2107,8 +2227,9 @@ namespace ts {
|
||||
processReferencedFiles(file, isDefaultLib);
|
||||
processTypeReferenceDirectives(file);
|
||||
}
|
||||
|
||||
processLibReferenceDirectives(file);
|
||||
if (!options.noLib) {
|
||||
processLibReferenceDirectives(file);
|
||||
}
|
||||
|
||||
modulesWithElidedImports.set(file.path, false);
|
||||
processImportedModules(file);
|
||||
@@ -2121,7 +2242,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
return file || undefined;
|
||||
}
|
||||
|
||||
let redirectedPath: Path | undefined;
|
||||
@@ -2195,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);
|
||||
@@ -2213,9 +2336,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
|
||||
filesByName.set(path, file);
|
||||
if (redirectedPath) {
|
||||
filesByName.set(redirectedPath, file);
|
||||
filesByName.set(path, file || false);
|
||||
}
|
||||
else {
|
||||
filesByName.set(path, file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2231,7 +2357,6 @@ namespace ts {
|
||||
if (!referencedProject) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const out = referencedProject.commandLine.options.outFile || referencedProject.commandLine.options.out;
|
||||
return out ?
|
||||
changeExtension(out, Extension.Dts) :
|
||||
@@ -2242,16 +2367,20 @@ namespace ts {
|
||||
* Get the referenced project if the file is input file from that reference project
|
||||
*/
|
||||
function getResolvedProjectReferenceToRedirect(fileName: string) {
|
||||
return forEachResolvedProjectReference((referencedProject, referenceProjectPath) => {
|
||||
// not input file from the referenced project, ignore
|
||||
if (!referencedProject ||
|
||||
toPath(options.configFilePath!) === referenceProjectPath ||
|
||||
!contains(referencedProject.commandLine.fileNames, fileName, isSameFile)) {
|
||||
return undefined;
|
||||
}
|
||||
if (mapFromFileToProjectReferenceRedirects === undefined) {
|
||||
mapFromFileToProjectReferenceRedirects = createMap();
|
||||
forEachResolvedProjectReference((referencedProject, referenceProjectPath) => {
|
||||
// not input file from the referenced project, ignore
|
||||
if (referencedProject &&
|
||||
toPath(options.configFilePath!) !== referenceProjectPath) {
|
||||
referencedProject.commandLine.fileNames.forEach(f =>
|
||||
mapFromFileToProjectReferenceRedirects!.set(toPath(f), referenceProjectPath));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return referencedProject;
|
||||
});
|
||||
const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName));
|
||||
return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath);
|
||||
}
|
||||
|
||||
function forEachResolvedProjectReference<T>(
|
||||
|
||||
+47
-4
@@ -337,17 +337,35 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number): number {
|
||||
return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character, sourceFile.text);
|
||||
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number): number;
|
||||
/* @internal */
|
||||
// tslint:disable-next-line:unified-signatures
|
||||
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number, allowEdits?: true): number;
|
||||
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number, allowEdits?: true): number {
|
||||
return sourceFile.getPositionOfLineAndCharacter ?
|
||||
sourceFile.getPositionOfLineAndCharacter(line, character, allowEdits) :
|
||||
computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character, sourceFile.text, allowEdits);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray<number>, line: number, character: number, debugText?: string): number {
|
||||
export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray<number>, line: number, character: number, debugText?: string, allowEdits?: true): number {
|
||||
if (line < 0 || line >= lineStarts.length) {
|
||||
Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`);
|
||||
if (allowEdits) {
|
||||
// Clamp line to nearest allowable value
|
||||
line = line < 0 ? 0 : line >= lineStarts.length ? lineStarts.length - 1 : line;
|
||||
}
|
||||
else {
|
||||
Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`);
|
||||
}
|
||||
}
|
||||
|
||||
const res = lineStarts[line] + character;
|
||||
if (allowEdits) {
|
||||
// Clamp to nearest allowable values to allow the underlying to be edited without crashing (accuracy is lost, instead)
|
||||
// TODO: Somehow track edits between file as it was during the creation of sourcemap we have and the current file and
|
||||
// apply them to the computed position to improve accuracy
|
||||
return res > lineStarts[line + 1] ? lineStarts[line + 1] : typeof debugText === "string" && res > debugText.length ? debugText.length : res;
|
||||
}
|
||||
if (line < lineStarts.length - 1) {
|
||||
Debug.assert(res < lineStarts[line + 1]);
|
||||
}
|
||||
@@ -957,7 +975,9 @@ namespace ts {
|
||||
else {
|
||||
result = text.substring(start, end); // No need to use all the fragments; no _ removal needed
|
||||
}
|
||||
|
||||
if (decimalFragment !== undefined || tokenFlags & TokenFlags.Scientific) {
|
||||
checkForIdentifierStartAfterNumericLiteral(start, decimalFragment === undefined && !!(tokenFlags & TokenFlags.Scientific));
|
||||
return {
|
||||
type: SyntaxKind.NumericLiteral,
|
||||
value: "" + +result // if value is not an integer, it can be safely coerced to a number
|
||||
@@ -966,10 +986,33 @@ namespace ts {
|
||||
else {
|
||||
tokenValue = result;
|
||||
const type = checkBigIntSuffix(); // if value is an integer, check whether it is a bigint
|
||||
checkForIdentifierStartAfterNumericLiteral(start);
|
||||
return { type, value: tokenValue };
|
||||
}
|
||||
}
|
||||
|
||||
function checkForIdentifierStartAfterNumericLiteral(numericStart: number, isScientific?: boolean) {
|
||||
if (!isIdentifierStart(text.charCodeAt(pos), languageVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const identifierStart = pos;
|
||||
const { length } = scanIdentifierParts();
|
||||
|
||||
if (length === 1 && text[identifierStart] === "n") {
|
||||
if (isScientific) {
|
||||
error(Diagnostics.A_bigint_literal_cannot_use_exponential_notation, numericStart, identifierStart - numericStart + 1);
|
||||
}
|
||||
else {
|
||||
error(Diagnostics.A_bigint_literal_must_be_an_integer, numericStart, identifierStart - numericStart + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
error(Diagnostics.An_identifier_or_keyword_cannot_immediately_follow_a_numeric_literal, identifierStart, length);
|
||||
pos = identifierStart;
|
||||
}
|
||||
}
|
||||
|
||||
function scanOctalDigits(): number {
|
||||
const start = pos;
|
||||
while (isOctalDigit(text.charCodeAt(pos))) {
|
||||
|
||||
+24
-14
@@ -266,14 +266,24 @@ namespace ts {
|
||||
const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)\s*$/;
|
||||
const whitespaceOrMapCommentRegExp = /^\s*(\/\/[@#] .*)?$/;
|
||||
|
||||
export interface LineInfo {
|
||||
getLineCount(): number;
|
||||
getLineText(line: number): string;
|
||||
}
|
||||
|
||||
export function getLineInfo(text: string, lineStarts: ReadonlyArray<number>): LineInfo {
|
||||
return {
|
||||
getLineCount: () => lineStarts.length,
|
||||
getLineText: line => text.substring(lineStarts[line], lineStarts[line + 1])
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find the sourceMappingURL comment at the end of a file.
|
||||
* @param text The source text of the file.
|
||||
* @param lineStarts The line starts of the file.
|
||||
*/
|
||||
export function tryGetSourceMappingURL(text: string, lineStarts: ReadonlyArray<number> = computeLineStarts(text)) {
|
||||
for (let index = lineStarts.length - 1; index >= 0; index--) {
|
||||
const line = text.substring(lineStarts[index], lineStarts[index + 1]);
|
||||
export function tryGetSourceMappingURL(lineInfo: LineInfo) {
|
||||
for (let index = lineInfo.getLineCount() - 1; index >= 0; index--) {
|
||||
const line = lineInfo.getLineText(index);
|
||||
const comment = sourceMapCommentRegExp.exec(line);
|
||||
if (comment) {
|
||||
return comment[1];
|
||||
@@ -573,7 +583,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
function compareSourcePositions(left: SourceMappedPosition, right: SourceMappedPosition) {
|
||||
return compareValues(left.sourceIndex, right.sourceIndex);
|
||||
// Compares sourcePosition without comparing sourceIndex
|
||||
// since the mappings are grouped by sourceIndex
|
||||
Debug.assert(left.sourceIndex === right.sourceIndex);
|
||||
return compareValues(left.sourcePosition, right.sourcePosition);
|
||||
}
|
||||
|
||||
function compareGeneratedPositions(left: MappedPosition, right: MappedPosition) {
|
||||
@@ -592,11 +605,9 @@ namespace ts {
|
||||
const mapDirectory = getDirectoryPath(mapPath);
|
||||
const sourceRoot = map.sourceRoot ? getNormalizedAbsolutePath(map.sourceRoot, mapDirectory) : mapDirectory;
|
||||
const generatedAbsoluteFilePath = getNormalizedAbsolutePath(map.file, mapDirectory);
|
||||
const generatedCanonicalFilePath = host.getCanonicalFileName(generatedAbsoluteFilePath) as Path;
|
||||
const generatedFile = host.getSourceFileLike(generatedCanonicalFilePath);
|
||||
const generatedFile = host.getSourceFileLike(generatedAbsoluteFilePath);
|
||||
const sourceFileAbsolutePaths = map.sources.map(source => getNormalizedAbsolutePath(source, sourceRoot));
|
||||
const sourceFileCanonicalPaths = sourceFileAbsolutePaths.map(source => host.getCanonicalFileName(source) as Path);
|
||||
const sourceToSourceIndexMap = createMapFromEntries(sourceFileCanonicalPaths.map((source, i) => [source, i] as [string, number]));
|
||||
const sourceToSourceIndexMap = createMapFromEntries(sourceFileAbsolutePaths.map((source, i) => [host.getCanonicalFileName(source), i] as [string, number]));
|
||||
let decodedMappings: ReadonlyArray<MappedPosition> | undefined;
|
||||
let generatedMappings: SortedReadonlyArray<MappedPosition> | undefined;
|
||||
let sourceMappings: ReadonlyArray<SortedReadonlyArray<SourceMappedPosition>> | undefined;
|
||||
@@ -608,16 +619,15 @@ namespace ts {
|
||||
|
||||
function processMapping(mapping: Mapping): MappedPosition {
|
||||
const generatedPosition = generatedFile !== undefined
|
||||
? getPositionOfLineAndCharacter(generatedFile, mapping.generatedLine, mapping.generatedCharacter)
|
||||
? getPositionOfLineAndCharacter(generatedFile, mapping.generatedLine, mapping.generatedCharacter, /*allowEdits*/ true)
|
||||
: -1;
|
||||
let source: string | undefined;
|
||||
let sourcePosition: number | undefined;
|
||||
if (isSourceMapping(mapping)) {
|
||||
const sourceFilePath = sourceFileCanonicalPaths[mapping.sourceIndex];
|
||||
const sourceFile = host.getSourceFileLike(sourceFilePath);
|
||||
const sourceFile = host.getSourceFileLike(sourceFileAbsolutePaths[mapping.sourceIndex]);
|
||||
source = map.sources[mapping.sourceIndex];
|
||||
sourcePosition = sourceFile !== undefined
|
||||
? getPositionOfLineAndCharacter(sourceFile, mapping.sourceLine, mapping.sourceCharacter)
|
||||
? getPositionOfLineAndCharacter(sourceFile, mapping.sourceLine, mapping.sourceCharacter, /*allowEdits*/ true)
|
||||
: -1;
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -634,7 +634,10 @@ namespace ts {
|
||||
if (!isLateVisibilityPaintedStatement(i)) {
|
||||
return Debug.fail(`Late replaced statement was found which is not handled by the declaration transformer!: ${(ts as any).SyntaxKind ? (ts as any).SyntaxKind[(i as any).kind] : (i as any).kind}`);
|
||||
}
|
||||
const priorNeedsDeclare = needsDeclare;
|
||||
needsDeclare = i.parent && isSourceFile(i.parent) && !(isExternalModule(i.parent) && isBundledEmit);
|
||||
const result = transformTopLevelDeclaration(i, /*privateDeclaration*/ true);
|
||||
needsDeclare = priorNeedsDeclare;
|
||||
lateStatementReplacementMap.set("" + getOriginalNodeId(i), result);
|
||||
}
|
||||
|
||||
@@ -1100,7 +1103,8 @@ namespace ts {
|
||||
if (extendsClause && !isEntityNameExpression(extendsClause.expression) && extendsClause.expression.kind !== SyntaxKind.NullKeyword) {
|
||||
// We must add a temporary declaration for the extends clause expression
|
||||
|
||||
const newId = createOptimisticUniqueName(`${unescapeLeadingUnderscores(input.name!.escapedText)}_base`); // TODO: GH#18217
|
||||
const oldId = input.name ? unescapeLeadingUnderscores(input.name.escapedText) : "default";
|
||||
const newId = createOptimisticUniqueName(`${oldId}_base`);
|
||||
getSymbolAccessibilityDiagnostic = () => ({
|
||||
diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1,
|
||||
errorNode: extendsClause,
|
||||
|
||||
@@ -389,6 +389,7 @@ namespace ts {
|
||||
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1;
|
||||
break;
|
||||
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
|
||||
break;
|
||||
@@ -410,6 +411,7 @@ namespace ts {
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1;
|
||||
break;
|
||||
|
||||
@@ -780,7 +780,7 @@ namespace ts {
|
||||
enableSubstitutionsForBlockScopedBindings();
|
||||
}
|
||||
|
||||
const extendsClauseElement = getEffectiveBaseTypeNode(node);
|
||||
const extendsClauseElement = getClassExtendsHeritageElement(node);
|
||||
const classFunction = createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
@@ -1350,8 +1350,8 @@ namespace ts {
|
||||
* part of a constructor declaration with a
|
||||
* synthesized call to `super`
|
||||
*/
|
||||
function shouldAddRestParameter(node: ParameterDeclaration | undefined, inConstructorWithSynthesizedSuper: boolean) {
|
||||
return node && node.dotDotDotToken && node.name.kind === SyntaxKind.Identifier && !inConstructorWithSynthesizedSuper;
|
||||
function shouldAddRestParameter(node: ParameterDeclaration | undefined, inConstructorWithSynthesizedSuper: boolean): node is ParameterDeclaration {
|
||||
return !!(node && node.dotDotDotToken && !inConstructorWithSynthesizedSuper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1370,11 +1370,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
// `declarationName` is the name of the local declaration for the parameter.
|
||||
const declarationName = getMutableClone(<Identifier>parameter!.name);
|
||||
const declarationName = parameter.name.kind === SyntaxKind.Identifier ? getMutableClone(parameter.name) : createTempVariable(/*recordTempVariable*/ undefined);
|
||||
setEmitFlags(declarationName, EmitFlags.NoSourceMap);
|
||||
|
||||
// `expressionName` is the name of the parameter used in expressions.
|
||||
const expressionName = getSynthesizedClone(<Identifier>parameter!.name);
|
||||
const expressionName = parameter.name.kind === SyntaxKind.Identifier ? getSynthesizedClone(parameter.name) : declarationName;
|
||||
const restIndex = node.parameters.length - 1;
|
||||
const temp = createLoopVariable();
|
||||
|
||||
@@ -1439,6 +1439,24 @@ namespace ts {
|
||||
setEmitFlags(forStatement, EmitFlags.CustomPrologue);
|
||||
startOnNewLine(forStatement);
|
||||
statements.push(forStatement);
|
||||
|
||||
if (parameter.name.kind !== SyntaxKind.Identifier) {
|
||||
// do the actual destructuring of the rest parameter if necessary
|
||||
statements.push(
|
||||
setEmitFlags(
|
||||
setTextRange(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList(
|
||||
flattenDestructuringBinding(parameter, visitor, context, FlattenLevel.All, expressionName),
|
||||
)
|
||||
),
|
||||
parameter
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1898,6 +1898,7 @@ namespace ts {
|
||||
case SyntaxKind.StringLiteral:
|
||||
return createIdentifier("String");
|
||||
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return createIdentifier("Number");
|
||||
|
||||
|
||||
+52
-18
@@ -32,6 +32,8 @@ namespace ts {
|
||||
pretty?: boolean;
|
||||
|
||||
traceResolution?: boolean;
|
||||
/* @internal */ diagnostics?: boolean;
|
||||
/* @internal */ extendedDiagnostics?: boolean;
|
||||
}
|
||||
|
||||
enum BuildResultFlags {
|
||||
@@ -326,6 +328,11 @@ namespace ts {
|
||||
|
||||
reportDiagnostic: DiagnosticReporter; // Technically we want to move it out and allow steps of actions on Solution, but for now just merge stuff in build host here
|
||||
reportSolutionBuilderStatus: DiagnosticReporter;
|
||||
|
||||
// TODO: To do better with watch mode and normal build mode api that creates program and emits files
|
||||
// This currently helps enable --diagnostics and --extendedDiagnostics
|
||||
beforeCreateProgram?(options: CompilerOptions): void;
|
||||
afterProgramEmitAndDiagnostics?(program: Program): void;
|
||||
}
|
||||
|
||||
export interface SolutionBuilderHost extends SolutionBuilderHostBase {
|
||||
@@ -426,6 +433,7 @@ namespace ts {
|
||||
const missingRoots = createMap<true>();
|
||||
let globalDependencyGraph: DependencyGraph | undefined;
|
||||
const writeFileName = (s: string) => host.trace && host.trace(s);
|
||||
let readFileWithCache = (f: string) => host.readFile(f);
|
||||
|
||||
// Watch state
|
||||
const diagnostics = createFileMap<ReadonlyArray<Diagnostic>>(toPath);
|
||||
@@ -997,7 +1005,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function buildSingleProject(proj: ResolvedConfigFileName): BuildResultFlags {
|
||||
if (options.dry) {
|
||||
reportStatus(Diagnostics.A_non_dry_build_would_build_project_0, proj);
|
||||
@@ -1030,6 +1037,9 @@ namespace ts {
|
||||
options: configFile.options,
|
||||
configFileParsingDiagnostics: configFile.errors
|
||||
};
|
||||
if (host.beforeCreateProgram) {
|
||||
host.beforeCreateProgram(options);
|
||||
}
|
||||
const program = createProgram(programOptions);
|
||||
|
||||
// Don't emit anything in the presence of syntactic errors or options diagnostics
|
||||
@@ -1041,14 +1051,6 @@ namespace ts {
|
||||
return buildErrors(syntaxDiagnostics, BuildResultFlags.SyntaxErrors, "Syntactic");
|
||||
}
|
||||
|
||||
// Don't emit .d.ts if there are decl file errors
|
||||
if (getEmitDeclarations(program.getCompilerOptions())) {
|
||||
const declDiagnostics = program.getDeclarationDiagnostics();
|
||||
if (declDiagnostics.length) {
|
||||
return buildErrors(declDiagnostics, BuildResultFlags.DeclarationEmitErrors, "Declaration file");
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above but now for semantic diagnostics
|
||||
const semanticDiagnostics = program.getSemanticDiagnostics();
|
||||
if (semanticDiagnostics.length) {
|
||||
@@ -1057,14 +1059,23 @@ namespace ts {
|
||||
|
||||
let newestDeclarationFileContentChangedTime = minimumDate;
|
||||
let anyDtsChanged = false;
|
||||
let emitDiagnostics: Diagnostic[] | undefined;
|
||||
const reportEmitDiagnostic = (d: Diagnostic) => (emitDiagnostics || (emitDiagnostics = [])).push(d);
|
||||
emitFilesAndReportErrors(program, reportEmitDiagnostic, writeFileName, /*reportSummary*/ undefined, (fileName, content, writeBom, onError) => {
|
||||
let declDiagnostics: Diagnostic[] | undefined;
|
||||
const reportDeclarationDiagnostics = (d: Diagnostic) => (declDiagnostics || (declDiagnostics = [])).push(d);
|
||||
const outputFiles: OutputFile[] = [];
|
||||
emitFilesAndReportErrors(program, reportDeclarationDiagnostics, writeFileName, /*reportSummary*/ undefined, (name, text, writeByteOrderMark) => outputFiles.push({ name, text, writeByteOrderMark }));
|
||||
// Don't emit .d.ts if there are decl file errors
|
||||
if (declDiagnostics) {
|
||||
return buildErrors(declDiagnostics, BuildResultFlags.DeclarationEmitErrors, "Declaration file");
|
||||
}
|
||||
|
||||
// Actual Emit
|
||||
const emitterDiagnostics = createDiagnosticCollection();
|
||||
outputFiles.forEach(({ name, text, writeByteOrderMark }) => {
|
||||
let priorChangeTime: Date | undefined;
|
||||
if (!anyDtsChanged && isDeclarationFile(fileName)) {
|
||||
if (!anyDtsChanged && isDeclarationFile(name)) {
|
||||
// Check for unchanged .d.ts files
|
||||
if (host.fileExists(fileName) && host.readFile(fileName) === content) {
|
||||
priorChangeTime = host.getModifiedTime(fileName);
|
||||
if (host.fileExists(name) && readFileWithCache(name) === text) {
|
||||
priorChangeTime = host.getModifiedTime(name);
|
||||
}
|
||||
else {
|
||||
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
|
||||
@@ -1072,14 +1083,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
host.writeFile(fileName, content, writeBom, onError, emptyArray);
|
||||
writeFile(host, emitterDiagnostics, name, text, writeByteOrderMark);
|
||||
if (priorChangeTime !== undefined) {
|
||||
newestDeclarationFileContentChangedTime = newer(priorChangeTime, newestDeclarationFileContentChangedTime);
|
||||
unchangedOutputs.setValue(fileName, priorChangeTime);
|
||||
unchangedOutputs.setValue(name, priorChangeTime);
|
||||
}
|
||||
});
|
||||
|
||||
if (emitDiagnostics) {
|
||||
const emitDiagnostics = emitterDiagnostics.getDiagnostics();
|
||||
if (emitDiagnostics.length) {
|
||||
return buildErrors(emitDiagnostics, BuildResultFlags.EmitErrors, "Emit");
|
||||
}
|
||||
|
||||
@@ -1089,12 +1101,18 @@ namespace ts {
|
||||
};
|
||||
diagnostics.removeKey(proj);
|
||||
projectStatus.setValue(proj, status);
|
||||
if (host.afterProgramEmitAndDiagnostics) {
|
||||
host.afterProgramEmitAndDiagnostics(program);
|
||||
}
|
||||
return resultFlags;
|
||||
|
||||
function buildErrors(diagnostics: ReadonlyArray<Diagnostic>, errorFlags: BuildResultFlags, errorType: string) {
|
||||
resultFlags |= errorFlags;
|
||||
reportAndStoreErrors(proj, diagnostics);
|
||||
projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: `${errorType} errors` });
|
||||
if (host.afterProgramEmitAndDiagnostics) {
|
||||
host.afterProgramEmitAndDiagnostics(program);
|
||||
}
|
||||
return resultFlags;
|
||||
}
|
||||
}
|
||||
@@ -1167,6 +1185,15 @@ namespace ts {
|
||||
|
||||
function buildAllProjects(): ExitStatus {
|
||||
if (options.watch) { reportWatchStatus(Diagnostics.Starting_compilation_in_watch_mode); }
|
||||
// TODO:: In watch mode as well to use caches for incremental build once we can invalidate caches correctly and have right api
|
||||
// Override readFile for json files and output .d.ts to cache the text
|
||||
const { originalReadFile, originalFileExists, originalDirectoryExists,
|
||||
originalCreateDirectory, originalWriteFile, originalGetSourceFile,
|
||||
readFileWithCache: newReadFileWithCache
|
||||
} = changeCompilerHostToUseCache(host, toPath, /*useCacheForSourceFile*/ true);
|
||||
const savedReadFileWithCache = readFileWithCache;
|
||||
readFileWithCache = newReadFileWithCache;
|
||||
|
||||
const graph = getGlobalDependencyGraph();
|
||||
reportBuildQueue(graph);
|
||||
let anyFailed = false;
|
||||
@@ -1217,6 +1244,13 @@ namespace ts {
|
||||
anyFailed = anyFailed || !!(buildResult & BuildResultFlags.AnyErrors);
|
||||
}
|
||||
reportErrorSummary();
|
||||
host.readFile = originalReadFile;
|
||||
host.fileExists = originalFileExists;
|
||||
host.directoryExists = originalDirectoryExists;
|
||||
host.createDirectory = originalCreateDirectory;
|
||||
host.writeFile = originalWriteFile;
|
||||
readFileWithCache = savedReadFileWithCache;
|
||||
host.getSourceFile = originalGetSourceFile;
|
||||
return anyFailed ? ExitStatus.DiagnosticsPresent_OutputsSkipped : ExitStatus.Success;
|
||||
}
|
||||
|
||||
|
||||
+29
-13
@@ -1745,6 +1745,9 @@ namespace ts {
|
||||
export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression;
|
||||
export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression;
|
||||
|
||||
/* @internal */
|
||||
export type AccessExpression = PropertyAccessExpression | ElementAccessExpression;
|
||||
|
||||
export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration {
|
||||
kind: SyntaxKind.PropertyAccessExpression;
|
||||
expression: LeftHandSideExpression;
|
||||
@@ -2614,6 +2617,8 @@ namespace ts {
|
||||
export interface SourceFileLike {
|
||||
readonly text: string;
|
||||
lineMap?: ReadonlyArray<number>;
|
||||
/* @internal */
|
||||
getPositionOfLineAndCharacter?(line: number, character: number, allowEdits?: true): number;
|
||||
}
|
||||
|
||||
|
||||
@@ -3032,8 +3037,10 @@ namespace ts {
|
||||
/* @internal */ typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined; // tslint:disable-line unified-signatures
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration & {typeArguments?: NodeArray<TypeNode>} | undefined;
|
||||
/* @internal */ signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): SignatureDeclaration & {typeArguments?: NodeArray<TypeNode>} | undefined; // tslint:disable-line unified-signatures
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration | undefined;
|
||||
/* @internal */ indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): IndexSignatureDeclaration | undefined; // tslint:disable-line unified-signatures
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
symbolToEntityName(symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): EntityName | undefined;
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
@@ -3103,7 +3110,7 @@ namespace ts {
|
||||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined;
|
||||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: string): boolean;
|
||||
/** Exclude accesses to private properties or methods with a `this` parameter that `type` doesn't satisfy. */
|
||||
/* @internal */ isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode, type: Type, property: Symbol): boolean;
|
||||
/* @internal */ isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean;
|
||||
/** Follow all aliases to get the original symbol. */
|
||||
getAliasedSymbol(symbol: Symbol): Symbol;
|
||||
/** Follow a *single* alias to get the immediately aliased symbol. */
|
||||
@@ -3141,6 +3148,7 @@ namespace ts {
|
||||
/* @internal */ getNeverType(): Type;
|
||||
/* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type;
|
||||
/* @internal */ createArrayType(elementType: Type): Type;
|
||||
/* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined;
|
||||
/* @internal */ createPromiseType(type: Type): Type;
|
||||
|
||||
/* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type;
|
||||
@@ -3671,15 +3679,17 @@ namespace ts {
|
||||
Readonly = 1 << 3, // Readonly transient symbol
|
||||
Partial = 1 << 4, // Synthetic property present in some but not all constituents
|
||||
HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents
|
||||
ContainsPublic = 1 << 6, // Synthetic property with public constituent(s)
|
||||
ContainsProtected = 1 << 7, // Synthetic property with protected constituent(s)
|
||||
ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s)
|
||||
ContainsStatic = 1 << 9, // Synthetic property with static constituent(s)
|
||||
Late = 1 << 10, // Late-bound symbol for a computed property with a dynamic name
|
||||
ReverseMapped = 1 << 11, // Property of reverse-inferred homomorphic mapped type
|
||||
OptionalParameter = 1 << 12, // Optional parameter
|
||||
RestParameter = 1 << 13, // Rest parameter
|
||||
Synthetic = SyntheticProperty | SyntheticMethod
|
||||
HasLiteralType = 1 << 6, // Synthetic property with at least one literal type in constituents
|
||||
ContainsPublic = 1 << 7, // Synthetic property with public constituent(s)
|
||||
ContainsProtected = 1 << 8, // Synthetic property with protected constituent(s)
|
||||
ContainsPrivate = 1 << 9, // Synthetic property with private constituent(s)
|
||||
ContainsStatic = 1 << 10, // Synthetic property with static constituent(s)
|
||||
Late = 1 << 11, // Late-bound symbol for a computed property with a dynamic name
|
||||
ReverseMapped = 1 << 12, // Property of reverse-inferred homomorphic mapped type
|
||||
OptionalParameter = 1 << 13, // Optional parameter
|
||||
RestParameter = 1 << 14, // Rest parameter
|
||||
Synthetic = SyntheticProperty | SyntheticMethod,
|
||||
Discriminant = HasNonUniformType | HasLiteralType
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -3908,7 +3918,9 @@ namespace ts {
|
||||
aliasTypeArguments?: ReadonlyArray<Type>; // Alias type arguments (if any)
|
||||
/* @internal */ aliasTypeArgumentsContainsMarker?: boolean; // Alias type arguments (if any)
|
||||
/* @internal */
|
||||
wildcardInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
|
||||
permissiveInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
|
||||
/* @internal */
|
||||
restrictiveInstantiation?: Type; // Instantiation with type parameters mapped to unconstrained form
|
||||
/* @internal */
|
||||
immediateBaseConstraint?: Type; // Immediate base constraint cache
|
||||
}
|
||||
@@ -5013,6 +5025,9 @@ namespace ts {
|
||||
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
|
||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||
createHash?(data: string): string;
|
||||
|
||||
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesnt use compilerHost as base
|
||||
/*@internal*/createDirectory?(directory: string): void;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -5524,9 +5539,9 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
export interface DocumentPositionMapperHost {
|
||||
getSourceFileLike(path: Path): SourceFileLike | undefined;
|
||||
getSourceFileLike(fileName: string): SourceFileLike | undefined;
|
||||
getCanonicalFileName(path: string): string;
|
||||
log?(text: string): void;
|
||||
log(text: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5849,6 +5864,7 @@ namespace ts {
|
||||
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
|
||||
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
readonly providePrefixAndSuffixTextForRename?: boolean;
|
||||
}
|
||||
|
||||
/** Represents a bigint literal value without requiring bigint support */
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/** Non-internal stuff goes here */
|
||||
namespace ts {
|
||||
export function isExternalModuleNameRelative(moduleName: string): boolean {
|
||||
// TypeScript 1.0 spec (April 2014): 11.2.1
|
||||
@@ -889,7 +888,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const isMissing = nodeIsMissing(errorNode);
|
||||
const pos = isMissing
|
||||
const pos = isMissing || isJsxText(node)
|
||||
? errorNode.pos
|
||||
: skipTrivia(sourceFile.text, errorNode.pos);
|
||||
|
||||
@@ -2122,7 +2121,7 @@ namespace ts {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function getSingleInitializerOfVariableStatementOrPropertyDeclaration(node: Node): Expression | undefined {
|
||||
export function getSingleInitializerOfVariableStatementOrPropertyDeclaration(node: Node): Expression | undefined {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VariableStatement:
|
||||
const v = getSingleVariableOfVariableStatement(node);
|
||||
@@ -3403,7 +3402,7 @@ namespace ts {
|
||||
return combinePaths(newDirPath, sourceFilePath);
|
||||
}
|
||||
|
||||
export function writeFile(host: EmitHost, diagnostics: DiagnosticCollection, fileName: string, data: string, writeByteOrderMark: boolean, sourceFiles?: ReadonlyArray<SourceFile>) {
|
||||
export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, data: string, writeByteOrderMark: boolean, sourceFiles?: ReadonlyArray<SourceFile>) {
|
||||
host.writeFile(fileName, data, writeByteOrderMark, hostErrorMessage => {
|
||||
diagnostics.add(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, hostErrorMessage));
|
||||
}, sourceFiles);
|
||||
@@ -4591,6 +4590,10 @@ namespace ts {
|
||||
|| kind === SyntaxKind.JSDocFunctionType
|
||||
|| kind === SyntaxKind.JSDocVariadicType;
|
||||
}
|
||||
|
||||
export function isAccessExpression(node: Node): node is AccessExpression {
|
||||
return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
@@ -7021,6 +7024,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function formatMessage(_dummy: any, message: DiagnosticMessage, ...args: (string | number | undefined)[]): string;
|
||||
export function formatMessage(_dummy: any, message: DiagnosticMessage): string {
|
||||
let text = getLocaleSpecificMessage(message);
|
||||
|
||||
@@ -7688,16 +7692,38 @@ namespace ts {
|
||||
return path;
|
||||
}
|
||||
|
||||
// check path for these segments: '', '.'. '..'
|
||||
const relativePathSegmentRegExp = /(^|\/)\.{0,2}($|\/)/;
|
||||
|
||||
function comparePathsWorker(a: string, b: string, componentComparer: (a: string, b: string) => Comparison) {
|
||||
if (a === b) return Comparison.EqualTo;
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
|
||||
// NOTE: Performance optimization - shortcut if the root segments differ as there would be no
|
||||
// need to perform path reduction.
|
||||
const aRoot = a.substring(0, getRootLength(a));
|
||||
const bRoot = b.substring(0, getRootLength(b));
|
||||
const result = compareStringsCaseInsensitive(aRoot, bRoot);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Performance optimization - shortcut if there are no relative path segments in
|
||||
// the non-root portion of the path
|
||||
const aRest = a.substring(aRoot.length);
|
||||
const bRest = b.substring(bRoot.length);
|
||||
if (!relativePathSegmentRegExp.test(aRest) && !relativePathSegmentRegExp.test(bRest)) {
|
||||
return componentComparer(aRest, bRest);
|
||||
}
|
||||
|
||||
// The path contains a relative path segment. Normalize the paths and perform a slower component
|
||||
// by component comparison.
|
||||
const aComponents = reducePathComponents(getPathComponents(a));
|
||||
const bComponents = reducePathComponents(getPathComponents(b));
|
||||
const sharedLength = Math.min(aComponents.length, bComponents.length);
|
||||
for (let i = 0; i < sharedLength; i++) {
|
||||
const stringComparer = i === 0 ? compareStringsCaseInsensitive : componentComparer;
|
||||
const result = stringComparer(aComponents[i], bComponents[i]);
|
||||
for (let i = 1; i < sharedLength; i++) {
|
||||
const result = componentComparer(aComponents[i], bComponents[i]);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace ts.server {
|
||||
private sequence = 0;
|
||||
private lineMaps: Map<number[]> = createMap<number[]>();
|
||||
private messages: string[] = [];
|
||||
private lastRenameEntry: RenameEntry;
|
||||
private lastRenameEntry: RenameEntry | undefined;
|
||||
|
||||
constructor(private host: SessionClientHost) {
|
||||
}
|
||||
@@ -384,7 +384,8 @@ namespace ts.server {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo {
|
||||
getRenameInfo(fileName: string, position: number, _options?: RenameInfoOptions, findInStrings?: boolean, findInComments?: boolean): RenameInfo {
|
||||
// Not passing along 'options' because server should already have those from the 'configure' command
|
||||
const args: protocol.RenameRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), findInStrings, findInComments };
|
||||
|
||||
const request = this.processRequest<protocol.RenameRequest>(CommandNames.Rename, args);
|
||||
@@ -428,10 +429,10 @@ namespace ts.server {
|
||||
this.lastRenameEntry.inputs.position !== position ||
|
||||
this.lastRenameEntry.inputs.findInStrings !== findInStrings ||
|
||||
this.lastRenameEntry.inputs.findInComments !== findInComments) {
|
||||
this.getRenameInfo(fileName, position, findInStrings, findInComments);
|
||||
this.getRenameInfo(fileName, position, { allowRenameOfImportPath: true }, findInStrings, findInComments);
|
||||
}
|
||||
|
||||
return this.lastRenameEntry.locations;
|
||||
return this.lastRenameEntry!.locations;
|
||||
}
|
||||
|
||||
private decodeNavigationBarItems(items: protocol.NavigationBarItem[] | undefined, fileName: string, lineMap: number[]): NavigationBarItem[] {
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace fakes {
|
||||
|
||||
private _setParentNodes: boolean;
|
||||
private _sourceFiles: collections.SortedMap<string, ts.SourceFile>;
|
||||
private _parseConfigHost: ParseConfigHost;
|
||||
private _parseConfigHost: ParseConfigHost | undefined;
|
||||
private _newLine: string;
|
||||
|
||||
constructor(sys: System | vfs.FileSystem, options = ts.getDefaultCompilerOptions(), setParentNodes = false) {
|
||||
|
||||
+23
-16
@@ -158,7 +158,7 @@ namespace FourSlash {
|
||||
public lastKnownMarker = "";
|
||||
|
||||
// The file that's currently 'opened'
|
||||
public activeFile: FourSlashFile;
|
||||
public activeFile!: FourSlashFile;
|
||||
|
||||
// Whether or not we should format on keystrokes
|
||||
public enableFormatting = true;
|
||||
@@ -571,7 +571,8 @@ namespace FourSlash {
|
||||
public verifyNoErrors() {
|
||||
ts.forEachKey(this.inputFiles, fileName => {
|
||||
if (!ts.isAnySupportedFileExtension(fileName)
|
||||
|| !this.getProgram().getCompilerOptions().allowJs && !ts.extensionIsTS(ts.extensionFromPath(fileName))) return;
|
||||
|| Harness.getConfigNameFromFileName(fileName)
|
||||
|| !this.getProgram().getCompilerOptions().allowJs && !ts.resolutionExtensionIsTSOrJson(ts.extensionFromPath(fileName))) return;
|
||||
const errors = this.getDiagnostics(fileName).filter(e => e.category !== ts.DiagnosticCategory.Suggestion);
|
||||
if (errors.length) {
|
||||
this.printErrorLog(/*expectErrors*/ false, errors);
|
||||
@@ -599,7 +600,7 @@ namespace FourSlash {
|
||||
throw new Error("Expected exactly one output from emit of " + this.activeFile.fileName);
|
||||
}
|
||||
|
||||
const evaluation = new Function(`${emit.outputFiles[0].text};\r\nreturn (${expr});`)();
|
||||
const evaluation = new Function(`${emit.outputFiles[0].text};\r\nreturn (${expr});`)(); // tslint:disable-line:function-constructor
|
||||
if (evaluation !== value) {
|
||||
this.raiseError(`Expected evaluation of expression "${expr}" to equal "${value}", but got "${evaluation}"`);
|
||||
}
|
||||
@@ -854,9 +855,9 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
/** Use `getProgram` instead of accessing this directly. */
|
||||
private _program: ts.Program;
|
||||
private _program: ts.Program | undefined;
|
||||
/** Use `getChecker` instead of accessing this directly. */
|
||||
private _checker: ts.TypeChecker;
|
||||
private _checker: ts.TypeChecker | undefined;
|
||||
|
||||
private getProgram(): ts.Program {
|
||||
return this._program || (this._program = this.languageService.getProgram()!); // TODO: GH#18217
|
||||
@@ -1169,7 +1170,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
|
||||
public verifyRenameLocations(startRanges: ArrayOrSingle<Range>, options: FourSlashInterface.RenameLocationsOptions) {
|
||||
const { findInStrings = false, findInComments = false, ranges = this.getRanges() } = ts.isArray(options) ? { findInStrings: false, findInComments: false, ranges: options } : options;
|
||||
const { findInStrings = false, findInComments = false, ranges = this.getRanges(), providePrefixAndSuffixTextForRename = true } = ts.isArray(options) ? { findInStrings: false, findInComments: false, ranges: options, providePrefixAndSuffixTextForRename: true } : options;
|
||||
|
||||
for (const startRange of toArray(startRanges)) {
|
||||
this.goToRangeStart(startRange);
|
||||
@@ -1181,7 +1182,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
|
||||
const references = this.languageService.findRenameLocations(
|
||||
this.activeFile.fileName, this.currentCaretPosition, findInStrings, findInComments);
|
||||
this.activeFile.fileName, this.currentCaretPosition, findInStrings, findInComments, providePrefixAndSuffixTextForRename);
|
||||
|
||||
const sort = (locations: ReadonlyArray<ts.RenameLocation> | undefined) =>
|
||||
locations && ts.sort(locations, (r1, r2) => ts.compareStringsCaseSensitive(r1.fileName, r2.fileName) || r1.textSpan.start - r2.textSpan.start);
|
||||
@@ -1307,8 +1308,8 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyRenameInfoSucceeded(displayName: string | undefined, fullDisplayName: string | undefined, kind: string | undefined, kindModifiers: string | undefined, fileToRename: string | undefined, expectedRange: Range | undefined): void {
|
||||
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
|
||||
public verifyRenameInfoSucceeded(displayName: string | undefined, fullDisplayName: string | undefined, kind: string | undefined, kindModifiers: string | undefined, fileToRename: string | undefined, expectedRange: Range | undefined, renameInfoOptions: ts.RenameInfoOptions | undefined): void {
|
||||
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition, renameInfoOptions || { allowRenameOfImportPath: true });
|
||||
if (!renameInfo.canRename) {
|
||||
throw this.raiseError("Rename did not succeed");
|
||||
}
|
||||
@@ -1333,8 +1334,9 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyRenameInfoFailed(message?: string) {
|
||||
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
|
||||
public verifyRenameInfoFailed(message?: string, allowRenameOfImportPath?: boolean) {
|
||||
allowRenameOfImportPath = allowRenameOfImportPath === undefined ? true : allowRenameOfImportPath;
|
||||
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition, { allowRenameOfImportPath });
|
||||
if (renameInfo.canRename) {
|
||||
throw this.raiseError("Rename was expected to fail");
|
||||
}
|
||||
@@ -3733,7 +3735,7 @@ namespace FourSlashInterface {
|
||||
}
|
||||
|
||||
export class VerifyNegatable {
|
||||
public not: VerifyNegatable;
|
||||
public not: VerifyNegatable | undefined;
|
||||
|
||||
constructor(protected state: FourSlash.TestState, private negative = false) {
|
||||
if (!negative) {
|
||||
@@ -4090,12 +4092,12 @@ namespace FourSlashInterface {
|
||||
this.state.verifySemanticClassifications(classifications);
|
||||
}
|
||||
|
||||
public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range) {
|
||||
this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange);
|
||||
public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range, options?: ts.RenameInfoOptions) {
|
||||
this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange, options);
|
||||
}
|
||||
|
||||
public renameInfoFailed(message?: string) {
|
||||
this.state.verifyRenameInfoFailed(message);
|
||||
public renameInfoFailed(message?: string, allowRenameOfImportPath?: boolean) {
|
||||
this.state.verifyRenameInfoFailed(message, allowRenameOfImportPath);
|
||||
}
|
||||
|
||||
public renameLocations(startRanges: ArrayOrSingle<FourSlash.Range>, options: RenameLocationsOptions) {
|
||||
@@ -4449,6 +4451,8 @@ namespace FourSlashInterface {
|
||||
interfaceEntry("ObjectConstructor"),
|
||||
constEntry("Function"),
|
||||
interfaceEntry("FunctionConstructor"),
|
||||
typeEntry("ThisParameterType"),
|
||||
typeEntry("OmitThisParameter"),
|
||||
interfaceEntry("CallableFunction"),
|
||||
interfaceEntry("NewableFunction"),
|
||||
interfaceEntry("IArguments"),
|
||||
@@ -4774,6 +4778,7 @@ namespace FourSlashInterface {
|
||||
"package",
|
||||
"yield",
|
||||
"async",
|
||||
"await",
|
||||
].map(keywordEntry);
|
||||
|
||||
// TODO: many of these are inappropriate to always provide
|
||||
@@ -4906,6 +4911,7 @@ namespace FourSlashInterface {
|
||||
"package",
|
||||
"yield",
|
||||
"async",
|
||||
"await",
|
||||
].map(keywordEntry);
|
||||
|
||||
export const globalKeywordsPlusUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = (() => {
|
||||
@@ -5081,6 +5087,7 @@ namespace FourSlashInterface {
|
||||
readonly findInStrings?: boolean;
|
||||
readonly findInComments?: boolean;
|
||||
readonly ranges: ReadonlyArray<RenameLocationOptions>;
|
||||
readonly providePrefixAndSuffixTextForRename?: boolean;
|
||||
};
|
||||
export type RenameLocationOptions = FourSlash.Range | { readonly range: FourSlash.Range, readonly prefixText?: string, readonly suffixText?: string };
|
||||
}
|
||||
|
||||
@@ -28,12 +28,10 @@ var assert: typeof _chai.assert = _chai.assert;
|
||||
};
|
||||
}
|
||||
|
||||
var global: NodeJS.Global = Function("return this").call(undefined);
|
||||
var global: NodeJS.Global = Function("return this").call(undefined); // tslint:disable-line:function-constructor
|
||||
|
||||
declare var window: {};
|
||||
declare var XMLHttpRequest: {
|
||||
new(): XMLHttpRequest;
|
||||
};
|
||||
declare var XMLHttpRequest: new() => XMLHttpRequest;
|
||||
interface XMLHttpRequest {
|
||||
readonly readyState: number;
|
||||
readonly responseText: string;
|
||||
|
||||
@@ -289,8 +289,8 @@ namespace Harness.LanguageService {
|
||||
class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost, ts.CoreServicesShimHost {
|
||||
private nativeHost: NativeLanguageServiceHost;
|
||||
|
||||
public getModuleResolutionsForFile: (fileName: string) => string;
|
||||
public getTypeReferenceDirectiveResolutionsForFile: (fileName: string) => string;
|
||||
public getModuleResolutionsForFile: ((fileName: string) => string) | undefined;
|
||||
public getTypeReferenceDirectiveResolutionsForFile: ((fileName: string) => string) | undefined;
|
||||
|
||||
constructor(preprocessToResolve: boolean, cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
super(cancellationToken, options);
|
||||
@@ -469,11 +469,11 @@ namespace Harness.LanguageService {
|
||||
getSignatureHelpItems(fileName: string, position: number, options: ts.SignatureHelpItemsOptions | undefined): ts.SignatureHelpItems {
|
||||
return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position, options));
|
||||
}
|
||||
getRenameInfo(fileName: string, position: number): ts.RenameInfo {
|
||||
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position));
|
||||
getRenameInfo(fileName: string, position: number, options?: ts.RenameInfoOptions): ts.RenameInfo {
|
||||
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position, options));
|
||||
}
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ts.RenameLocation[] {
|
||||
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments));
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ts.RenameLocation[] {
|
||||
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
|
||||
}
|
||||
getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
|
||||
return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position));
|
||||
@@ -639,7 +639,7 @@ namespace Harness.LanguageService {
|
||||
|
||||
// Server adapter
|
||||
class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost {
|
||||
private client: ts.server.SessionClient;
|
||||
private client!: ts.server.SessionClient;
|
||||
|
||||
constructor(cancellationToken: ts.HostCancellationToken | undefined, settings: ts.CompilerOptions | undefined) {
|
||||
super(cancellationToken, settings);
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Harness.SourceMapRecorder {
|
||||
SourceMapDecoder.initializeSourceMapDecoding(sourceMapData);
|
||||
sourceMapRecorder.WriteLine("===================================================================");
|
||||
sourceMapRecorder.WriteLine("JsFile: " + sourceMapData.sourceMap.file);
|
||||
sourceMapRecorder.WriteLine("mapUrl: " + ts.tryGetSourceMappingURL(jsFile.text, jsLineMap));
|
||||
sourceMapRecorder.WriteLine("mapUrl: " + ts.tryGetSourceMappingURL(ts.getLineInfo(jsFile.text, jsLineMap)));
|
||||
sourceMapRecorder.WriteLine("sourceRoot: " + sourceMapData.sourceMap.sourceRoot);
|
||||
sourceMapRecorder.WriteLine("sources: " + sourceMapData.sourceMap.sources);
|
||||
if (sourceMapData.sourceMap.sourcesContent) {
|
||||
|
||||
@@ -21,7 +21,7 @@ interface TypeWriterResult {
|
||||
}
|
||||
|
||||
class TypeWriterWalker {
|
||||
currentSourceFile: ts.SourceFile;
|
||||
currentSourceFile!: ts.SourceFile;
|
||||
|
||||
private checker: ts.TypeChecker;
|
||||
|
||||
|
||||
@@ -341,7 +341,7 @@ interface Array<T> {}`
|
||||
private readonly currentDirectory: string;
|
||||
private readonly dynamicPriorityWatchFile: HostWatchFile | undefined;
|
||||
private readonly customRecursiveWatchDirectory: HostWatchDirectory | undefined;
|
||||
public require: (initialPath: string, moduleName: string) => server.RequireResult;
|
||||
public require: ((initialPath: string, moduleName: string) => server.RequireResult) | undefined;
|
||||
|
||||
constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderorSymLinkList: ReadonlyArray<FileOrFolderOrSymLink>, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean, private readonly environmentVariables?: Map<string>) {
|
||||
this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
|
||||
Vendored
+12
-2
@@ -295,6 +295,16 @@ interface FunctionConstructor {
|
||||
|
||||
declare const Function: FunctionConstructor;
|
||||
|
||||
/**
|
||||
* Extracts the type of the 'this' parameter of a function type, or 'unknown' if the function type has no 'this' parameter.
|
||||
*/
|
||||
type ThisParameterType<T> = T extends (this: unknown, ...args: any[]) => any ? unknown : T extends (this: infer U, ...args: any[]) => any ? U : unknown;
|
||||
|
||||
/**
|
||||
* Removes the 'this' parameter from a function type.
|
||||
*/
|
||||
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;
|
||||
|
||||
interface CallableFunction extends Function {
|
||||
/**
|
||||
* Calls the function with the specified object as the this value and the elements of specified array as the arguments.
|
||||
@@ -317,7 +327,7 @@ interface CallableFunction extends Function {
|
||||
* @param thisArg The object to be used as the this object.
|
||||
* @param args Arguments to bind to the parameters of the function.
|
||||
*/
|
||||
bind<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R;
|
||||
bind<T>(this: T, thisArg: ThisParameterType<T>): OmitThisParameter<T>;
|
||||
bind<T, A0, A extends any[], R>(this: (this: T, arg0: A0, ...args: A) => R, thisArg: T, arg0: A0): (...args: A) => R;
|
||||
bind<T, A0, A1, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1): (...args: A) => R;
|
||||
bind<T, A0, A1, A2, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2): (...args: A) => R;
|
||||
@@ -347,7 +357,7 @@ interface NewableFunction extends Function {
|
||||
* @param thisArg The object to be used as the this object.
|
||||
* @param args Arguments to bind to the parameters of the function.
|
||||
*/
|
||||
bind<A extends any[], R>(this: new (...args: A) => R, thisArg: any): new (...args: A) => R;
|
||||
bind<T>(this: T, thisArg: any): T;
|
||||
bind<A0, A extends any[], R>(this: new (arg0: A0, ...args: A) => R, thisArg: any, arg0: A0): new (...args: A) => R;
|
||||
bind<A0, A1, A extends any[], R>(this: new (arg0: A0, arg1: A1, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1): new (...args: A) => R;
|
||||
bind<A0, A1, A2, A extends any[], R>(this: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1, arg2: A2): new (...args: A) => R;
|
||||
|
||||
@@ -2083,7 +2083,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Cannot find the common subdirectory path for the input files.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[找不到輸入檔案的一般子目錄路徑。]]></Val>
|
||||
<Val><![CDATA[找不到輸入檔的一般子目錄路徑。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -7567,7 +7567,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Specify the root directory of input files. Use to control the output directory structure with --outDir.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[指定輸入檔案的根目錄。用以控制具有 --outDir 的輸出目錄結構。]]></Val>
|
||||
<Val><![CDATA[指定輸入檔的根目錄。用以控制具有 --outDir 的輸出目錄結構。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -9172,7 +9172,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Watch input files.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[監看輸入檔案。]]></Val>
|
||||
<Val><![CDATA[監看輸入檔。]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+265
-16
@@ -123,11 +123,22 @@ namespace ts.server {
|
||||
|
||||
export interface FileStats {
|
||||
readonly js: number;
|
||||
readonly jsSize?: number;
|
||||
|
||||
readonly jsx: number;
|
||||
readonly jsxSize?: number;
|
||||
|
||||
readonly ts: number;
|
||||
readonly tsSize?: number;
|
||||
|
||||
readonly tsx: number;
|
||||
readonly tsxSize?: number;
|
||||
|
||||
readonly dts: number;
|
||||
readonly dtsSize?: number;
|
||||
|
||||
readonly deferred: number;
|
||||
readonly deferredSize?: number;
|
||||
}
|
||||
|
||||
export interface OpenFileInfo {
|
||||
@@ -331,6 +342,7 @@ namespace ts.server {
|
||||
FailedLookupLocation = "Directory of Failed lookup locations in module resolution",
|
||||
TypeRoots = "Type root directory",
|
||||
NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them",
|
||||
MissingSourceMapFile = "Missing source map file"
|
||||
}
|
||||
|
||||
const enum ConfigFileWatcherStatus {
|
||||
@@ -426,7 +438,8 @@ namespace ts.server {
|
||||
/**
|
||||
* Container of all known scripts
|
||||
*/
|
||||
private readonly filenameToScriptInfo = createMap<ScriptInfo>();
|
||||
/*@internal*/
|
||||
readonly filenameToScriptInfo = createMap<ScriptInfo>();
|
||||
private readonly scriptInfoInNodeModulesWatchers = createMap <ScriptInfoInNodeModulesWatcher>();
|
||||
/**
|
||||
* Contains all the deleted script info's version information so that
|
||||
@@ -468,7 +481,7 @@ namespace ts.server {
|
||||
*/
|
||||
private readonly openFilesWithNonRootedDiskPath = createMap<ScriptInfo>();
|
||||
|
||||
private compilerOptionsForInferredProjects: CompilerOptions;
|
||||
private compilerOptionsForInferredProjects: CompilerOptions | undefined;
|
||||
private compilerOptionsForInferredProjectsPerProjectRoot = createMap<CompilerOptions>();
|
||||
/**
|
||||
* Project size for configured or external projects
|
||||
@@ -490,7 +503,7 @@ namespace ts.server {
|
||||
|
||||
private pendingProjectUpdates = createMap<Project>();
|
||||
/* @internal */
|
||||
pendingEnsureProjectForOpenFiles: boolean;
|
||||
pendingEnsureProjectForOpenFiles = false;
|
||||
|
||||
readonly currentDirectory: NormalizedPath;
|
||||
readonly toCanonicalFileName: (f: string) => string;
|
||||
@@ -933,10 +946,42 @@ namespace ts.server {
|
||||
// this file and set of inferred projects
|
||||
info.delayReloadNonMixedContentFile();
|
||||
this.delayUpdateProjectGraphs(info.containingProjects);
|
||||
this.handleSourceMapProjects(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleSourceMapProjects(info: ScriptInfo) {
|
||||
// Change in d.ts, update source projects as well
|
||||
if (info.sourceMapFilePath) {
|
||||
if (isString(info.sourceMapFilePath)) {
|
||||
const sourceMapFileInfo = this.getScriptInfoForPath(info.sourceMapFilePath);
|
||||
this.delayUpdateSourceInfoProjects(sourceMapFileInfo && sourceMapFileInfo.sourceInfos);
|
||||
}
|
||||
else {
|
||||
this.delayUpdateSourceInfoProjects(info.sourceMapFilePath.sourceInfos);
|
||||
}
|
||||
}
|
||||
// Change in mapInfo, update declarationProjects and source projects
|
||||
this.delayUpdateSourceInfoProjects(info.sourceInfos);
|
||||
if (info.declarationInfoPath) {
|
||||
this.delayUpdateProjectsOfScriptInfoPath(info.declarationInfoPath);
|
||||
}
|
||||
}
|
||||
|
||||
private delayUpdateSourceInfoProjects(sourceInfos: Map<true> | undefined) {
|
||||
if (sourceInfos) {
|
||||
sourceInfos.forEach((_value, path) => this.delayUpdateProjectsOfScriptInfoPath(path as Path));
|
||||
}
|
||||
}
|
||||
|
||||
private delayUpdateProjectsOfScriptInfoPath(path: Path) {
|
||||
const info = this.getScriptInfoForPath(path);
|
||||
if (info) {
|
||||
this.delayUpdateProjectGraphs(info.containingProjects);
|
||||
}
|
||||
}
|
||||
|
||||
private handleDeletedFile(info: ScriptInfo) {
|
||||
this.stopWatchingScriptInfo(info);
|
||||
|
||||
@@ -950,6 +995,15 @@ namespace ts.server {
|
||||
|
||||
// update projects to make sure that set of referenced files is correct
|
||||
this.delayUpdateProjectGraphs(containingProjects);
|
||||
this.handleSourceMapProjects(info);
|
||||
info.closeSourceMapFileWatcher();
|
||||
// need to recalculate source map from declaration file
|
||||
if (info.declarationInfoPath) {
|
||||
const declarationInfo = this.getScriptInfoForPath(info.declarationInfoPath);
|
||||
if (declarationInfo) {
|
||||
declarationInfo.sourceMapFilePath = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1223,7 +1277,8 @@ namespace ts.server {
|
||||
private setConfigFileExistenceByNewConfiguredProject(project: ConfiguredProject) {
|
||||
const configFileExistenceInfo = this.getConfigFileExistenceInfo(project);
|
||||
if (configFileExistenceInfo) {
|
||||
Debug.assert(configFileExistenceInfo.exists);
|
||||
// The existance might not be set if the file watcher is not invoked by the time config project is created by external project
|
||||
configFileExistenceInfo.exists = true;
|
||||
// close existing watcher
|
||||
if (configFileExistenceInfo.configFileWatcherForRootOfInferredProject) {
|
||||
const configFileName = project.getConfigFilePath();
|
||||
@@ -1600,7 +1655,7 @@ namespace ts.server {
|
||||
setProjectOptionsUsed(project);
|
||||
const data: ProjectInfoTelemetryEventData = {
|
||||
projectId: this.host.createSHA256Hash(project.projectName),
|
||||
fileStats: countEachFileTypes(project.getScriptInfos()),
|
||||
fileStats: countEachFileTypes(project.getScriptInfos(), /*includeSizes*/ true),
|
||||
compilerOptions: convertCompilerOptionsForTelemetry(project.getCompilationSettings()),
|
||||
typeAcquisition: convertTypeAcquisition(project.getTypeAcquisition()),
|
||||
extends: projectOptions && projectOptions.configHasExtendsProperty,
|
||||
@@ -1925,7 +1980,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: NormalizedPath): InferredProject {
|
||||
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
|
||||
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects!; // TODO: GH#18217
|
||||
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory, this.currentPluginConfigOverrides);
|
||||
if (isSingleInferredProject) {
|
||||
this.inferredProjects.unshift(project);
|
||||
@@ -1953,7 +2008,7 @@ namespace ts.server {
|
||||
const path = toNormalizedPath(uncheckedFileName);
|
||||
const info = this.getScriptInfoForNormalizedPath(path);
|
||||
if (info) return info;
|
||||
const configProject = this.configuredProjects.get(uncheckedFileName);
|
||||
const configProject = this.configuredProjects.get(this.toPath(uncheckedFileName));
|
||||
return configProject && configProject.getCompilerOptions().configFile;
|
||||
}
|
||||
|
||||
@@ -2184,6 +2239,150 @@ namespace ts.server {
|
||||
return this.filenameToScriptInfo.get(fileName);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getDocumentPositionMapper(project: Project, generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined {
|
||||
// Since declaration info and map file watches arent updating project's directory structure host (which can cache file structure) use host
|
||||
const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(generatedFileName, project.currentDirectory, this.host);
|
||||
if (!declarationInfo) return undefined;
|
||||
|
||||
// Try to get from cache
|
||||
declarationInfo.getSnapshot(); // Ensure synchronized
|
||||
if (isString(declarationInfo.sourceMapFilePath)) {
|
||||
// Ensure mapper is synchronized
|
||||
const sourceMapFileInfo = this.getScriptInfoForPath(declarationInfo.sourceMapFilePath);
|
||||
if (sourceMapFileInfo) {
|
||||
sourceMapFileInfo.getSnapshot();
|
||||
if (sourceMapFileInfo.documentPositionMapper !== undefined) {
|
||||
sourceMapFileInfo.sourceInfos = this.addSourceInfoToSourceMap(sourceFileName, project, sourceMapFileInfo.sourceInfos);
|
||||
return sourceMapFileInfo.documentPositionMapper ? sourceMapFileInfo.documentPositionMapper : undefined;
|
||||
}
|
||||
}
|
||||
declarationInfo.sourceMapFilePath = undefined;
|
||||
}
|
||||
else if (declarationInfo.sourceMapFilePath) {
|
||||
declarationInfo.sourceMapFilePath.sourceInfos = this.addSourceInfoToSourceMap(sourceFileName, project, declarationInfo.sourceMapFilePath.sourceInfos);
|
||||
return undefined;
|
||||
}
|
||||
else if (declarationInfo.sourceMapFilePath !== undefined) {
|
||||
// Doesnt have sourceMap
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Create the mapper
|
||||
let sourceMapFileInfo: ScriptInfo | undefined;
|
||||
let mapFileNameFromDeclarationInfo: string | undefined;
|
||||
|
||||
let readMapFile: ReadMapFile | undefined = (mapFileName, mapFileNameFromDts) => {
|
||||
const mapInfo = this.getOrCreateScriptInfoNotOpenedByClient(mapFileName, project.currentDirectory, this.host);
|
||||
if (!mapInfo) {
|
||||
mapFileNameFromDeclarationInfo = mapFileNameFromDts;
|
||||
return undefined;
|
||||
}
|
||||
sourceMapFileInfo = mapInfo;
|
||||
const snap = mapInfo.getSnapshot();
|
||||
if (mapInfo.documentPositionMapper !== undefined) return mapInfo.documentPositionMapper;
|
||||
return snap.getText(0, snap.getLength());
|
||||
};
|
||||
const projectName = project.projectName;
|
||||
const documentPositionMapper = getDocumentPositionMapper(
|
||||
{ getCanonicalFileName: this.toCanonicalFileName, log: s => this.logger.info(s), getSourceFileLike: f => this.getSourceFileLike(f, projectName, declarationInfo) },
|
||||
declarationInfo.fileName,
|
||||
declarationInfo.getLineInfo(),
|
||||
readMapFile
|
||||
);
|
||||
readMapFile = undefined; // Remove ref to project
|
||||
if (sourceMapFileInfo) {
|
||||
declarationInfo.sourceMapFilePath = sourceMapFileInfo.path;
|
||||
sourceMapFileInfo.declarationInfoPath = declarationInfo.path;
|
||||
sourceMapFileInfo.documentPositionMapper = documentPositionMapper || false;
|
||||
sourceMapFileInfo.sourceInfos = this.addSourceInfoToSourceMap(sourceFileName, project, sourceMapFileInfo.sourceInfos);
|
||||
}
|
||||
else if (mapFileNameFromDeclarationInfo) {
|
||||
declarationInfo.sourceMapFilePath = {
|
||||
watcher: this.addMissingSourceMapFile(
|
||||
project.currentDirectory === this.currentDirectory ?
|
||||
mapFileNameFromDeclarationInfo :
|
||||
getNormalizedAbsolutePath(mapFileNameFromDeclarationInfo, project.currentDirectory),
|
||||
declarationInfo.path
|
||||
),
|
||||
sourceInfos: this.addSourceInfoToSourceMap(sourceFileName, project)
|
||||
};
|
||||
}
|
||||
else {
|
||||
declarationInfo.sourceMapFilePath = false;
|
||||
}
|
||||
return documentPositionMapper;
|
||||
}
|
||||
|
||||
private addSourceInfoToSourceMap(sourceFileName: string | undefined, project: Project, sourceInfos?: Map<true>) {
|
||||
if (sourceFileName) {
|
||||
// Attach as source
|
||||
const sourceInfo = this.getOrCreateScriptInfoNotOpenedByClient(sourceFileName, project.currentDirectory, project.directoryStructureHost)!;
|
||||
(sourceInfos || (sourceInfos = createMap())).set(sourceInfo.path, true);
|
||||
}
|
||||
return sourceInfos;
|
||||
}
|
||||
|
||||
private addMissingSourceMapFile(mapFileName: string, declarationInfoPath: Path) {
|
||||
const fileWatcher = this.watchFactory.watchFile(
|
||||
this.host,
|
||||
mapFileName,
|
||||
() => {
|
||||
const declarationInfo = this.getScriptInfoForPath(declarationInfoPath);
|
||||
if (declarationInfo && declarationInfo.sourceMapFilePath && !isString(declarationInfo.sourceMapFilePath)) {
|
||||
// Update declaration and source projects
|
||||
this.delayUpdateProjectGraphs(declarationInfo.containingProjects);
|
||||
this.delayUpdateSourceInfoProjects(declarationInfo.sourceMapFilePath.sourceInfos);
|
||||
declarationInfo.closeSourceMapFileWatcher();
|
||||
}
|
||||
},
|
||||
PollingInterval.High,
|
||||
WatchType.MissingSourceMapFile,
|
||||
);
|
||||
return fileWatcher;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getSourceFileLike(fileName: string, projectNameOrProject: string | Project, declarationInfo?: ScriptInfo) {
|
||||
const project = (projectNameOrProject as Project).projectName ? projectNameOrProject as Project : this.findProject(projectNameOrProject as string);
|
||||
if (project) {
|
||||
const path = project.toPath(fileName);
|
||||
const sourceFile = project.getSourceFile(path);
|
||||
if (sourceFile && sourceFile.resolvedPath === path) return sourceFile;
|
||||
}
|
||||
|
||||
// Need to look for other files.
|
||||
const info = this.getOrCreateScriptInfoNotOpenedByClient(fileName, (project || this).currentDirectory, project ? project.directoryStructureHost : this.host);
|
||||
if (!info) return undefined;
|
||||
|
||||
// Attach as source
|
||||
if (declarationInfo && isString(declarationInfo.sourceMapFilePath) && info !== declarationInfo) {
|
||||
const sourceMapInfo = this.getScriptInfoForPath(declarationInfo.sourceMapFilePath);
|
||||
if (sourceMapInfo) {
|
||||
(sourceMapInfo.sourceInfos || (sourceMapInfo.sourceInfos = createMap())).set(info.path, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Key doesnt matter since its only for text and lines
|
||||
if (info.cacheSourceFile) return info.cacheSourceFile.sourceFile;
|
||||
|
||||
// Create sourceFileLike
|
||||
if (!info.sourceFileLike) {
|
||||
info.sourceFileLike = {
|
||||
get text() {
|
||||
Debug.fail("shouldnt need text");
|
||||
return "";
|
||||
},
|
||||
getLineAndCharacterOfPosition: pos => {
|
||||
const lineOffset = info.positionToLineOffset(pos);
|
||||
return { line: lineOffset.line - 1, character: lineOffset.offset - 1 };
|
||||
},
|
||||
getPositionOfLineAndCharacter: (line, character, allowEdits) => info.lineOffsetToPosition(line + 1, character + 1, allowEdits)
|
||||
};
|
||||
}
|
||||
return info.sourceFileLike;
|
||||
}
|
||||
|
||||
setHostConfiguration(args: protocol.ConfigureRequestArguments) {
|
||||
if (args.file) {
|
||||
const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file));
|
||||
@@ -2405,7 +2604,7 @@ namespace ts.server {
|
||||
|
||||
/** @internal */
|
||||
fileExists(fileName: NormalizedPath): boolean {
|
||||
return this.filenameToScriptInfo.has(fileName) || this.host.fileExists(fileName);
|
||||
return !!this.getScriptInfoForNormalizedPath(fileName) || this.host.fileExists(fileName);
|
||||
}
|
||||
|
||||
private findExternalProjectContainingOpenScriptInfo(info: ScriptInfo): ExternalProject | undefined {
|
||||
@@ -2479,13 +2678,7 @@ namespace ts.server {
|
||||
// when some file/s were closed which resulted in project removal.
|
||||
// It was then postponed to cleanup these script infos so that they can be reused if
|
||||
// the file from that old project is reopened because of opening file from here.
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
if (!info.isScriptOpen() && info.isOrphan()) {
|
||||
// if there are not projects that include this script info - delete it
|
||||
this.stopWatchingScriptInfo(info);
|
||||
this.deleteScriptInfo(info);
|
||||
}
|
||||
});
|
||||
this.removeOrphanScriptInfos();
|
||||
|
||||
this.printProjects();
|
||||
|
||||
@@ -2528,12 +2721,68 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private removeOrphanScriptInfos() {
|
||||
const toRemoveScriptInfos = cloneMap(this.filenameToScriptInfo);
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
// If script info is open or orphan, retain it and its dependencies
|
||||
if (!info.isScriptOpen() && info.isOrphan()) {
|
||||
// Otherwise if there is any source info that is alive, this alive too
|
||||
if (!info.sourceMapFilePath) return;
|
||||
let sourceInfos: Map<true> | undefined;
|
||||
if (isString(info.sourceMapFilePath)) {
|
||||
const sourceMapInfo = this.getScriptInfoForPath(info.sourceMapFilePath);
|
||||
sourceInfos = sourceMapInfo && sourceMapInfo.sourceInfos;
|
||||
}
|
||||
else {
|
||||
sourceInfos = info.sourceMapFilePath.sourceInfos;
|
||||
}
|
||||
if (!sourceInfos) return;
|
||||
if (!forEachKey(sourceInfos, path => {
|
||||
const info = this.getScriptInfoForPath(path as Path);
|
||||
return !!info && (info.isScriptOpen() || !info.isOrphan());
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Retain this script info
|
||||
toRemoveScriptInfos.delete(info.path);
|
||||
if (info.sourceMapFilePath) {
|
||||
let sourceInfos: Map<true> | undefined;
|
||||
if (isString(info.sourceMapFilePath)) {
|
||||
// And map file info and source infos
|
||||
toRemoveScriptInfos.delete(info.sourceMapFilePath);
|
||||
const sourceMapInfo = this.getScriptInfoForPath(info.sourceMapFilePath);
|
||||
sourceInfos = sourceMapInfo && sourceMapInfo.sourceInfos;
|
||||
}
|
||||
else {
|
||||
sourceInfos = info.sourceMapFilePath.sourceInfos;
|
||||
}
|
||||
if (sourceInfos) {
|
||||
sourceInfos.forEach((_value, path) => toRemoveScriptInfos.delete(path));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
toRemoveScriptInfos.forEach(info => {
|
||||
// if there are not projects that include this script info - delete it
|
||||
this.stopWatchingScriptInfo(info);
|
||||
this.deleteScriptInfo(info);
|
||||
info.closeSourceMapFileWatcher();
|
||||
});
|
||||
}
|
||||
|
||||
private telemetryOnOpenFile(scriptInfo: ScriptInfo): void {
|
||||
if (this.syntaxOnly || !this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const info: OpenFileInfo = { checkJs: !!scriptInfo.getDefaultProject().getSourceFile(scriptInfo.path)!.checkJsDirective };
|
||||
const project = scriptInfo.getDefaultProject();
|
||||
if (!project.languageServiceEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const info: OpenFileInfo = { checkJs: !!project.getSourceFile(scriptInfo.path)!.checkJsDirective };
|
||||
this.eventHandler({ eventName: OpenFileInfoTelemetryEvent, data: { info } });
|
||||
}
|
||||
|
||||
|
||||
+55
-25
@@ -10,26 +10,43 @@ namespace ts.server {
|
||||
export type Mutable<T> = { -readonly [K in keyof T]: T[K]; };
|
||||
|
||||
/* @internal */
|
||||
export function countEachFileTypes(infos: ScriptInfo[]): FileStats {
|
||||
const result: Mutable<FileStats> = { js: 0, jsx: 0, ts: 0, tsx: 0, dts: 0, deferred: 0 };
|
||||
export function countEachFileTypes(infos: ScriptInfo[], includeSizes = false): FileStats {
|
||||
const result: Mutable<FileStats> = {
|
||||
js: 0, jsSize: 0,
|
||||
jsx: 0, jsxSize: 0,
|
||||
ts: 0, tsSize: 0,
|
||||
tsx: 0, tsxSize: 0,
|
||||
dts: 0, dtsSize: 0,
|
||||
deferred: 0, deferredSize: 0,
|
||||
};
|
||||
for (const info of infos) {
|
||||
const fileSize = includeSizes ? info.getTelemetryFileSize() : 0;
|
||||
switch (info.scriptKind) {
|
||||
case ScriptKind.JS:
|
||||
result.js += 1;
|
||||
result.jsSize! += fileSize;
|
||||
break;
|
||||
case ScriptKind.JSX:
|
||||
result.jsx += 1;
|
||||
result.jsxSize! += fileSize;
|
||||
break;
|
||||
case ScriptKind.TS:
|
||||
fileExtensionIs(info.fileName, Extension.Dts)
|
||||
? result.dts += 1
|
||||
: result.ts += 1;
|
||||
if (fileExtensionIs(info.fileName, Extension.Dts)) {
|
||||
result.dts += 1;
|
||||
result.dtsSize! += fileSize;
|
||||
}
|
||||
else {
|
||||
result.ts += 1;
|
||||
result.tsSize! += fileSize;
|
||||
}
|
||||
break;
|
||||
case ScriptKind.TSX:
|
||||
result.tsx += 1;
|
||||
result.tsxSize! += fileSize;
|
||||
break;
|
||||
case ScriptKind.Deferred:
|
||||
result.deferred += 1;
|
||||
result.deferredSize! += fileSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -95,9 +112,9 @@ namespace ts.server {
|
||||
export abstract class Project implements LanguageServiceHost, ModuleResolutionHost {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap: Map<ProjectRoot> = createMap<ProjectRoot>();
|
||||
private program: Program;
|
||||
private externalFiles: SortedReadonlyArray<string>;
|
||||
private missingFilesMap: Map<FileWatcher>;
|
||||
private program: Program | undefined;
|
||||
private externalFiles: SortedReadonlyArray<string> | undefined;
|
||||
private missingFilesMap: Map<FileWatcher> | undefined;
|
||||
private plugins: PluginModuleWithName[] = [];
|
||||
|
||||
/*@internal*/
|
||||
@@ -124,7 +141,7 @@ namespace ts.server {
|
||||
readonly realpath?: (path: string) => string;
|
||||
|
||||
/*@internal*/
|
||||
hasInvalidatedResolution: HasInvalidatedResolution;
|
||||
hasInvalidatedResolution: HasInvalidatedResolution | undefined;
|
||||
|
||||
/*@internal*/
|
||||
resolutionCache: ResolutionCache;
|
||||
@@ -137,7 +154,7 @@ namespace ts.server {
|
||||
/**
|
||||
* Set of files that was returned from the last call to getChangesSinceVersion.
|
||||
*/
|
||||
private lastReportedFileNames: Map<true>;
|
||||
private lastReportedFileNames: Map<true> | undefined;
|
||||
/**
|
||||
* Last version that was reported.
|
||||
*/
|
||||
@@ -486,6 +503,16 @@ namespace ts.server {
|
||||
return this.getLanguageService().getSourceMapper();
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getDocumentPositionMapper(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined {
|
||||
return this.projectService.getDocumentPositionMapper(this, generatedFileName, sourceFileName);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getSourceFileLike(fileName: string) {
|
||||
return this.projectService.getSourceFileLike(fileName, this);
|
||||
}
|
||||
|
||||
private shouldEmitFile(scriptInfo: ScriptInfo) {
|
||||
return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent();
|
||||
}
|
||||
@@ -495,8 +522,8 @@ namespace ts.server {
|
||||
return [];
|
||||
}
|
||||
updateProjectIfDirty(this);
|
||||
this.builderState = BuilderState.create(this.program, this.projectService.toCanonicalFileName, this.builderState);
|
||||
return mapDefined(BuilderState.getFilesAffectedBy(this.builderState, this.program, scriptInfo.path, this.cancellationToken, data => this.projectService.host.createHash!(data)), // TODO: GH#18217
|
||||
this.builderState = BuilderState.create(this.program!, this.projectService.toCanonicalFileName, this.builderState);
|
||||
return mapDefined(BuilderState.getFilesAffectedBy(this.builderState, this.program!, scriptInfo.path, this.cancellationToken, data => this.projectService.host.createHash!(data)), // TODO: GH#18217
|
||||
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)!) ? sourceFile.fileName : undefined);
|
||||
}
|
||||
|
||||
@@ -577,7 +604,7 @@ namespace ts.server {
|
||||
|
||||
/* @internal */
|
||||
getSourceFileOrConfigFile(path: Path): SourceFile | undefined {
|
||||
const options = this.program.getCompilerOptions();
|
||||
const options = this.program!.getCompilerOptions();
|
||||
return path === options.configFilePath ? options.configFile : this.getSourceFile(path);
|
||||
}
|
||||
|
||||
@@ -664,7 +691,7 @@ namespace ts.server {
|
||||
// if language service is not enabled - return just root files
|
||||
return this.rootFiles;
|
||||
}
|
||||
return map(this.program.getSourceFiles(), sourceFile => {
|
||||
return map(this.program!.getSourceFiles(), sourceFile => {
|
||||
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.resolvedPath);
|
||||
Debug.assert(!!scriptInfo, "getScriptInfo", () => `scriptInfo for a file '${sourceFile.fileName}' Path: '${sourceFile.path}' / '${sourceFile.resolvedPath}' is missing.`);
|
||||
return scriptInfo!;
|
||||
@@ -732,7 +759,10 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
containsScriptInfo(info: ScriptInfo): boolean {
|
||||
return this.isRoot(info) || (this.program && this.program.getSourceFileByPath(info.path) !== undefined);
|
||||
if (this.isRoot(info)) return true;
|
||||
if (!this.program) return false;
|
||||
const file = this.program.getSourceFileByPath(info.path);
|
||||
return !!file && file.resolvedPath === info.path;
|
||||
}
|
||||
|
||||
containsFile(filename: NormalizedPath, requireOpen?: boolean): boolean {
|
||||
@@ -828,7 +858,7 @@ namespace ts.server {
|
||||
// (can reuse cached imports for files that were not changed)
|
||||
// 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch
|
||||
if (hasNewProgram || changedFiles.length) {
|
||||
this.lastCachedUnresolvedImportsList = getUnresolvedImports(this.program, this.cachedUnresolvedImportsPerFile);
|
||||
this.lastCachedUnresolvedImportsList = getUnresolvedImports(this.program!, this.cachedUnresolvedImportsPerFile);
|
||||
}
|
||||
|
||||
this.projectService.typingsCache.enqueueInstallTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasAddedorRemovedFiles);
|
||||
@@ -855,7 +885,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
getCurrentProgram() {
|
||||
getCurrentProgram(): Program | undefined {
|
||||
return this.program;
|
||||
}
|
||||
|
||||
@@ -894,7 +924,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
oldProgram.forEachResolvedProjectReference((resolvedProjectReference, resolvedProjectReferencePath) => {
|
||||
if (resolvedProjectReference && !this.program.getResolvedProjectReferenceByPath(resolvedProjectReferencePath)) {
|
||||
if (resolvedProjectReference && !this.program!.getResolvedProjectReferenceByPath(resolvedProjectReferencePath)) {
|
||||
this.detachScriptInfoFromProject(resolvedProjectReference.sourceFile.fileName);
|
||||
}
|
||||
});
|
||||
@@ -950,8 +980,8 @@ namespace ts.server {
|
||||
this.getCachedDirectoryStructureHost().addOrDeleteFile(fileName, missingFilePath, eventKind);
|
||||
}
|
||||
|
||||
if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) {
|
||||
this.missingFilesMap.delete(missingFilePath);
|
||||
if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap!.has(missingFilePath)) {
|
||||
this.missingFilesMap!.delete(missingFilePath);
|
||||
fileWatcher.close();
|
||||
|
||||
// When a missing file is created, we should update the graph.
|
||||
@@ -966,7 +996,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private isWatchedMissingFile(path: Path) {
|
||||
return this.missingFilesMap && this.missingFilesMap.has(path);
|
||||
return !!this.missingFilesMap && this.missingFilesMap.has(path);
|
||||
}
|
||||
|
||||
getScriptInfoForNormalizedPath(fileName: NormalizedPath): ScriptInfo | undefined {
|
||||
@@ -1327,14 +1357,14 @@ namespace ts.server {
|
||||
* Otherwise it will create an InferredProject.
|
||||
*/
|
||||
export class ConfiguredProject extends Project {
|
||||
private typeAcquisition: TypeAcquisition;
|
||||
private typeAcquisition!: TypeAcquisition; // TODO: GH#18217
|
||||
/* @internal */
|
||||
configFileWatcher: FileWatcher | undefined;
|
||||
private directoriesWatchedForWildcards: Map<WildcardDirectoryWatcher> | undefined;
|
||||
readonly canonicalConfigFilePath: NormalizedPath;
|
||||
|
||||
/* @internal */
|
||||
pendingReload: ConfigFileProgramReloadLevel;
|
||||
pendingReload: ConfigFileProgramReloadLevel | undefined;
|
||||
/* @internal */
|
||||
pendingReloadReason: string | undefined;
|
||||
|
||||
@@ -1342,7 +1372,7 @@ namespace ts.server {
|
||||
configFileSpecs: ConfigFileSpecs | undefined;
|
||||
|
||||
/*@internal*/
|
||||
canConfigFileJsonReportNoInputFiles: boolean;
|
||||
canConfigFileJsonReportNoInputFiles = false;
|
||||
|
||||
/** Ref count to the project when opened from external project */
|
||||
private externalProjectRefCount = 0;
|
||||
@@ -1573,7 +1603,7 @@ namespace ts.server {
|
||||
*/
|
||||
export class ExternalProject extends Project {
|
||||
excludedFiles: ReadonlyArray<NormalizedPath> = [];
|
||||
private typeAcquisition: TypeAcquisition;
|
||||
private typeAcquisition!: TypeAcquisition; // TODO: GH#18217
|
||||
/*@internal*/
|
||||
constructor(public externalProjectName: string,
|
||||
projectService: ProjectService,
|
||||
|
||||
@@ -2905,6 +2905,8 @@ namespace ts.server.protocol {
|
||||
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
|
||||
readonly providePrefixAndSuffixTextForRename?: boolean;
|
||||
readonly allowRenameOfImportPath?: boolean;
|
||||
}
|
||||
|
||||
export interface CompilerOptions {
|
||||
|
||||
+113
-17
@@ -25,18 +25,26 @@ namespace ts.server {
|
||||
*/
|
||||
private lineMap: number[] | undefined;
|
||||
|
||||
/**
|
||||
* When a large file is loaded, text will artificially be set to "".
|
||||
* In order to be able to report correct telemetry, we store the actual
|
||||
* file size in this case. (In other cases where text === "", e.g.
|
||||
* for mixed content or dynamic files, fileSize will be undefined.)
|
||||
*/
|
||||
private fileSize: number | undefined;
|
||||
|
||||
/**
|
||||
* True if the text is for the file thats open in the editor
|
||||
*/
|
||||
public isOpen: boolean;
|
||||
public isOpen = false;
|
||||
/**
|
||||
* True if the text present is the text from the file on the disk
|
||||
*/
|
||||
private ownFileText: boolean;
|
||||
private ownFileText = false;
|
||||
/**
|
||||
* True when reloading contents of file from the disk is pending
|
||||
*/
|
||||
private pendingReloadFromDisk: boolean;
|
||||
private pendingReloadFromDisk = false;
|
||||
|
||||
constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath, initialVersion: ScriptInfoVersion | undefined, private readonly info: ScriptInfo) {
|
||||
this.version = initialVersion || { svc: 0, text: 0 };
|
||||
@@ -56,10 +64,22 @@ namespace ts.server {
|
||||
this.switchToScriptVersionCache();
|
||||
}
|
||||
|
||||
private resetSourceMapInfo() {
|
||||
this.info.sourceFileLike = undefined;
|
||||
this.info.closeSourceMapFileWatcher();
|
||||
this.info.sourceMapFilePath = undefined;
|
||||
this.info.declarationInfoPath = undefined;
|
||||
this.info.sourceInfos = undefined;
|
||||
this.info.documentPositionMapper = undefined;
|
||||
}
|
||||
|
||||
/** Public for testing */
|
||||
public useText(newText?: string) {
|
||||
this.svc = undefined;
|
||||
this.text = newText;
|
||||
this.lineMap = undefined;
|
||||
this.fileSize = undefined;
|
||||
this.resetSourceMapInfo();
|
||||
this.version.text++;
|
||||
}
|
||||
|
||||
@@ -68,13 +88,15 @@ namespace ts.server {
|
||||
this.ownFileText = false;
|
||||
this.text = undefined;
|
||||
this.lineMap = undefined;
|
||||
this.fileSize = undefined;
|
||||
this.resetSourceMapInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents as newText
|
||||
* returns true if text changed
|
||||
*/
|
||||
public reload(newText: string) {
|
||||
public reload(newText: string): boolean {
|
||||
Debug.assert(newText !== undefined);
|
||||
|
||||
// Reload always has fresh content
|
||||
@@ -91,6 +113,8 @@ namespace ts.server {
|
||||
this.ownFileText = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +122,9 @@ namespace ts.server {
|
||||
* returns true if text changed
|
||||
*/
|
||||
public reloadWithFileText(tempFileName?: string) {
|
||||
const reloaded = this.reload(this.getFileText(tempFileName));
|
||||
const { text: newText, fileSize } = this.getFileTextAndSize(tempFileName);
|
||||
const reloaded = this.reload(newText);
|
||||
this.fileSize = fileSize; // NB: after reload since reload clears it
|
||||
this.ownFileText = !tempFileName || tempFileName === this.fileName;
|
||||
return reloaded;
|
||||
}
|
||||
@@ -118,14 +144,31 @@ namespace ts.server {
|
||||
this.pendingReloadFromDisk = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For telemetry purposes, we would like to be able to report the size of the file.
|
||||
* However, we do not want telemetry to require extra file I/O so we report a size
|
||||
* that may be stale (e.g. may not reflect change made on disk since the last reload).
|
||||
* NB: Will read from disk if the file contents have never been loaded because
|
||||
* telemetry falsely indicating size 0 would be counter-productive.
|
||||
*/
|
||||
public getTelemetryFileSize(): number {
|
||||
return !!this.fileSize
|
||||
? this.fileSize
|
||||
: !!this.text // Check text before svc because its length is cheaper
|
||||
? this.text.length // Could be wrong if this.pendingReloadFromDisk
|
||||
: !!this.svc
|
||||
? this.svc.getSnapshot().getLength() // Could be wrong if this.pendingReloadFromDisk
|
||||
: this.getSnapshot().getLength(); // Should be strictly correct
|
||||
}
|
||||
|
||||
public getSnapshot(): IScriptSnapshot {
|
||||
return this.useScriptVersionCacheIfValidOrOpen()
|
||||
? this.svc!.getSnapshot()
|
||||
: ScriptSnapshot.fromString(this.getOrLoadText());
|
||||
}
|
||||
|
||||
public getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this.switchToScriptVersionCache().getLineInfo(line);
|
||||
public getAbsolutePositionAndLineText(line: number): AbsolutePositionAndLineText {
|
||||
return this.switchToScriptVersionCache().getAbsolutePositionAndLineText(line);
|
||||
}
|
||||
/**
|
||||
* @param line 0 based index
|
||||
@@ -144,9 +187,9 @@ namespace ts.server {
|
||||
* @param line 1 based index
|
||||
* @param offset 1 based index
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
lineOffsetToPosition(line: number, offset: number, allowEdits?: true): number {
|
||||
if (!this.useScriptVersionCacheIfValidOrOpen()) {
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text);
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text, allowEdits);
|
||||
}
|
||||
|
||||
// TODO: assert this offset is actually on the line
|
||||
@@ -161,7 +204,7 @@ namespace ts.server {
|
||||
return this.svc!.positionToLineOffset(position);
|
||||
}
|
||||
|
||||
private getFileText(tempFileName?: string) {
|
||||
private getFileTextAndSize(tempFileName?: string): { text: string, fileSize?: number } {
|
||||
let text: string;
|
||||
const fileName = tempFileName || this.fileName;
|
||||
const getText = () => text === undefined ? (text = this.host.readFile(fileName) || "") : text;
|
||||
@@ -173,10 +216,10 @@ namespace ts.server {
|
||||
const service = this.info.containingProjects[0].projectService;
|
||||
service.logger.info(`Skipped loading contents of large file ${fileName} for info ${this.info.fileName}: fileSize: ${fileSize}`);
|
||||
this.info.containingProjects[0].projectService.sendLargeFileReferencedEvent(fileName, fileSize);
|
||||
return "";
|
||||
return { text: "", fileSize };
|
||||
}
|
||||
}
|
||||
return getText();
|
||||
return { text: getText() };
|
||||
}
|
||||
|
||||
private switchToScriptVersionCache(): ScriptVersionCache {
|
||||
@@ -214,6 +257,17 @@ namespace ts.server {
|
||||
Debug.assert(!this.svc, "ScriptVersionCache should not be set");
|
||||
return this.lineMap || (this.lineMap = computeLineStarts(this.getOrLoadText()));
|
||||
}
|
||||
|
||||
getLineInfo(): LineInfo {
|
||||
if (this.svc) {
|
||||
return {
|
||||
getLineCount: () => this.svc!.getLineCount(),
|
||||
getLineText: line => this.svc!.getAbsolutePositionAndLineText(line + 1).lineText!
|
||||
};
|
||||
}
|
||||
const lineMap = this.getLineMap();
|
||||
return getLineInfo(this.text!, lineMap);
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
@@ -227,6 +281,12 @@ namespace ts.server {
|
||||
sourceFile: SourceFile;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export interface SourceMapFileWatcher {
|
||||
watcher: FileWatcher;
|
||||
sourceInfos?: Map<true>;
|
||||
}
|
||||
|
||||
export class ScriptInfo {
|
||||
/**
|
||||
* All projects that include this file
|
||||
@@ -247,11 +307,25 @@ namespace ts.server {
|
||||
private realpath: Path | undefined;
|
||||
|
||||
/*@internal*/
|
||||
cacheSourceFile: DocumentRegistrySourceFileCache;
|
||||
cacheSourceFile: DocumentRegistrySourceFileCache | undefined;
|
||||
|
||||
/*@internal*/
|
||||
mTime?: number;
|
||||
|
||||
/*@internal*/
|
||||
sourceFileLike?: SourceFileLike;
|
||||
|
||||
/*@internal*/
|
||||
sourceMapFilePath?: Path | SourceMapFileWatcher | false;
|
||||
|
||||
// Present on sourceMapFile info
|
||||
/*@internal*/
|
||||
declarationInfoPath?: Path;
|
||||
/*@internal*/
|
||||
sourceInfos?: Map<true>;
|
||||
/*@internal*/
|
||||
documentPositionMapper?: DocumentPositionMapper | false;
|
||||
|
||||
constructor(
|
||||
private readonly host: ServerHost,
|
||||
readonly fileName: NormalizedPath,
|
||||
@@ -276,6 +350,11 @@ namespace ts.server {
|
||||
return this.textStorage.version;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getTelemetryFileSize() {
|
||||
return this.textStorage.getTelemetryFileSize();
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
public isDynamicOrHasMixedContent() {
|
||||
return this.hasMixedContent || this.isDynamic;
|
||||
@@ -484,8 +563,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this.textStorage.getLineInfo(line);
|
||||
getAbsolutePositionAndLineText(line: number): AbsolutePositionAndLineText {
|
||||
return this.textStorage.getAbsolutePositionAndLineText(line);
|
||||
}
|
||||
|
||||
editContent(start: number, end: number, newText: string): void {
|
||||
@@ -514,8 +593,12 @@ namespace ts.server {
|
||||
* @param line 1 based index
|
||||
* @param offset 1 based index
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
return this.textStorage.lineOffsetToPosition(line, offset);
|
||||
lineOffsetToPosition(line: number, offset: number): number;
|
||||
/*@internal*/
|
||||
// tslint:disable-next-line:unified-signatures
|
||||
lineOffsetToPosition(line: number, offset: number, allowEdits?: true): number;
|
||||
lineOffsetToPosition(line: number, offset: number, allowEdits?: true): number {
|
||||
return this.textStorage.lineOffsetToPosition(line, offset, allowEdits);
|
||||
}
|
||||
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
@@ -525,5 +608,18 @@ namespace ts.server {
|
||||
public isJavaScript() {
|
||||
return this.scriptKind === ScriptKind.JS || this.scriptKind === ScriptKind.JSX;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getLineInfo(): LineInfo {
|
||||
return this.textStorage.getLineInfo();
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
closeSourceMapFileWatcher() {
|
||||
if (this.sourceMapFilePath && !isString(this.sourceMapFilePath)) {
|
||||
closeFileWatcherOf(this.sourceMapFilePath);
|
||||
this.sourceMapFilePath = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ namespace ts.server {
|
||||
// path to start of range
|
||||
private readonly startPath: LineCollection[];
|
||||
private readonly endBranch: LineCollection[] = [];
|
||||
private branchNode: LineNode;
|
||||
private branchNode: LineNode | undefined;
|
||||
// path to current node
|
||||
private readonly stack: LineNode[];
|
||||
private state = CharRangeSection.Entire;
|
||||
private lineCollectionAtBranch: LineCollection;
|
||||
private lineCollectionAtBranch: LineCollection | undefined;
|
||||
private initialText = "";
|
||||
private trailingText = "";
|
||||
|
||||
@@ -308,8 +308,8 @@ namespace ts.server {
|
||||
return this._getSnapshot().version;
|
||||
}
|
||||
|
||||
getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this._getSnapshot().index.lineNumberToInfo(line);
|
||||
getAbsolutePositionAndLineText(oneBasedLine: number): AbsolutePositionAndLineText {
|
||||
return this._getSnapshot().index.lineNumberToInfo(oneBasedLine);
|
||||
}
|
||||
|
||||
lineOffsetToPosition(line: number, column: number): number {
|
||||
@@ -348,6 +348,10 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getLineCount() {
|
||||
return this._getSnapshot().index.getLineCount();
|
||||
}
|
||||
|
||||
static fromString(script: string) {
|
||||
const svc = new ScriptVersionCache();
|
||||
const snap = new LineIndexSnapshot(0, svc, new LineIndex());
|
||||
@@ -383,7 +387,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export class LineIndex {
|
||||
root: LineNode;
|
||||
root!: LineNode;
|
||||
// set this to true to check each edit for accuracy
|
||||
checkEdits = false;
|
||||
|
||||
@@ -400,8 +404,12 @@ namespace ts.server {
|
||||
return this.root.charOffsetToLineInfo(1, position);
|
||||
}
|
||||
|
||||
getLineCount() {
|
||||
return this.root.lineCount();
|
||||
}
|
||||
|
||||
lineNumberToInfo(oneBasedLine: number): AbsolutePositionAndLineText {
|
||||
const lineCount = this.root.lineCount();
|
||||
const lineCount = this.getLineCount();
|
||||
if (oneBasedLine <= lineCount) {
|
||||
const { position, leaf } = this.root.lineNumberToInfo(oneBasedLine, 0);
|
||||
return { absolutePosition: position, lineText: leaf && leaf.text };
|
||||
|
||||
+14
-14
@@ -314,7 +314,8 @@ namespace ts.server {
|
||||
defaultProject: Project,
|
||||
initialLocation: DocumentPosition,
|
||||
findInStrings: boolean,
|
||||
findInComments: boolean
|
||||
findInComments: boolean,
|
||||
hostPreferences: UserPreferences
|
||||
): ReadonlyArray<RenameLocation> {
|
||||
const outputs: RenameLocation[] = [];
|
||||
|
||||
@@ -323,7 +324,7 @@ namespace ts.server {
|
||||
defaultProject,
|
||||
initialLocation,
|
||||
({ project, location }, tryAddToTodo) => {
|
||||
for (const output of project.getLanguageService().findRenameLocations(location.fileName, location.pos, findInStrings, findInComments) || emptyArray) {
|
||||
for (const output of project.getLanguageService().findRenameLocations(location.fileName, location.pos, findInStrings, findInComments, hostPreferences.providePrefixAndSuffixTextForRename) || emptyArray) {
|
||||
if (!contains(outputs, output, documentSpansEqual) && !tryAddToTodo(project, documentSpanLocation(output))) {
|
||||
outputs.push(output);
|
||||
}
|
||||
@@ -516,7 +517,7 @@ namespace ts.server {
|
||||
protected projectService: ProjectService;
|
||||
private changeSeq = 0;
|
||||
|
||||
private currentRequestId: number;
|
||||
private currentRequestId!: number;
|
||||
private errorCheck: MultistepOperation;
|
||||
|
||||
protected host: ServerHost;
|
||||
@@ -1177,7 +1178,8 @@ namespace ts.server {
|
||||
private getRenameInfo(args: protocol.FileLocationRequestArgs): RenameInfo {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const position = this.getPositionInFile(args, file);
|
||||
return project.getLanguageService().getRenameInfo(file, position);
|
||||
const preferences = this.getHostPreferences();
|
||||
return project.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: preferences.allowRenameOfImportPath });
|
||||
}
|
||||
|
||||
private getProjects(args: protocol.FileRequestArgs, getScriptInfoEnsuringProjectsUptoDate?: boolean, ignoreNoProjectError?: boolean): Projects {
|
||||
@@ -1231,12 +1233,13 @@ namespace ts.server {
|
||||
this.getDefaultProject(args),
|
||||
{ fileName: args.file, pos: position },
|
||||
!!args.findInStrings,
|
||||
!!args.findInComments
|
||||
!!args.findInComments,
|
||||
this.getHostPreferences()
|
||||
);
|
||||
if (!simplifiedResult) return locations;
|
||||
|
||||
const defaultProject = this.getDefaultProject(args);
|
||||
const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position), Debug.assertDefined(this.projectService.getScriptInfo(file)));
|
||||
const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: this.getHostPreferences().allowRenameOfImportPath }), Debug.assertDefined(this.projectService.getScriptInfo(file)));
|
||||
return { info: renameInfo, locs: this.toSpanGroups(locations) };
|
||||
}
|
||||
|
||||
@@ -1474,7 +1477,7 @@ namespace ts.server {
|
||||
// only to the previous line. If all this is true, then
|
||||
// add edits necessary to properly indent the current line.
|
||||
if ((args.key === "\n") && ((!edits) || (edits.length === 0) || allEditsBeforePos(edits, position))) {
|
||||
const { lineText, absolutePosition } = scriptInfo.getLineInfo(args.line);
|
||||
const { lineText, absolutePosition } = scriptInfo.getAbsolutePositionAndLineText(args.line);
|
||||
if (lineText && lineText.search("\\S") < 0) {
|
||||
const preferredIndent = languageService.getIndentationAtPosition(file, position, formatOptions);
|
||||
let hasIndent = 0;
|
||||
@@ -1811,7 +1814,7 @@ namespace ts.server {
|
||||
return (<protocol.FileLocationRequestArgs>locationOrSpan).line !== undefined;
|
||||
}
|
||||
|
||||
private extractPositionAndRange(args: protocol.FileLocationOrRangeRequestArgs, scriptInfo: ScriptInfo): { position: number, textRange: TextRange } {
|
||||
private extractPositionOrRange(args: protocol.FileLocationOrRangeRequestArgs, scriptInfo: ScriptInfo): number | TextRange {
|
||||
let position: number | undefined;
|
||||
let textRange: TextRange | undefined;
|
||||
if (this.isLocation(args)) {
|
||||
@@ -1821,7 +1824,7 @@ namespace ts.server {
|
||||
const { startPosition, endPosition } = this.getStartAndEndPosition(args, scriptInfo);
|
||||
textRange = { pos: startPosition, end: endPosition };
|
||||
}
|
||||
return { position: position!, textRange: textRange! }; // TODO: GH#18217
|
||||
return Debug.assertDefined(position === undefined ? textRange : position);
|
||||
|
||||
function getPosition(loc: protocol.FileLocationRequestArgs) {
|
||||
return loc.position !== undefined ? loc.position : scriptInfo.lineOffsetToPosition(loc.line, loc.offset);
|
||||
@@ -1831,19 +1834,16 @@ namespace ts.server {
|
||||
private getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): protocol.ApplicableRefactorInfo[] {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file)!;
|
||||
const { position, textRange } = this.extractPositionAndRange(args, scriptInfo);
|
||||
return project.getLanguageService().getApplicableRefactors(file, position || textRange, this.getPreferences(file));
|
||||
return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file));
|
||||
}
|
||||
|
||||
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file)!;
|
||||
const { position, textRange } = this.extractPositionAndRange(args, scriptInfo);
|
||||
|
||||
const result = project.getLanguageService().getEditsForRefactor(
|
||||
file,
|
||||
this.getFormatOptions(file),
|
||||
position || textRange,
|
||||
this.extractPositionOrRange(args, scriptInfo),
|
||||
args.refactor,
|
||||
args.action,
|
||||
this.getPreferences(file),
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace ts.codefix {
|
||||
inJs: boolean,
|
||||
preferences: UserPreferences,
|
||||
): void {
|
||||
const methodDeclaration = createMethodFromCallExpression(context, callExpression, token.text, inJs, makeStatic, preferences, !isInterfaceDeclaration(typeDecl));
|
||||
const methodDeclaration = createMethodFromCallExpression(context, callExpression, token.text, inJs, makeStatic, preferences, typeDecl);
|
||||
const containingMethodDeclaration = getAncestor(callExpression, SyntaxKind.MethodDeclaration);
|
||||
|
||||
if (containingMethodDeclaration && containingMethodDeclaration.parent === typeDecl) {
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { program, sourceFile, span } = context;
|
||||
const { sourceFile, span } = context;
|
||||
const changes = textChanges.ChangeTracker.with(context, t =>
|
||||
addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t, context.preferences));
|
||||
addMissingMembers(getClass(sourceFile, span.start), sourceFile, context, t, context.preferences));
|
||||
return changes.length === 0 ? undefined : [createCodeFixAction(fixId, changes, Diagnostics.Implement_inherited_abstract_class, fixId, Diagnostics.Implement_all_inherited_abstract_classes)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
@@ -19,7 +19,7 @@ namespace ts.codefix {
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const classDeclaration = getClass(diag.file, diag.start);
|
||||
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {
|
||||
addMissingMembers(classDeclaration, context.sourceFile, context.program.getTypeChecker(), changes, context.preferences);
|
||||
addMissingMembers(classDeclaration, context.sourceFile, context, changes, context.preferences);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -32,15 +32,16 @@ namespace ts.codefix {
|
||||
return cast(token.parent, isClassLike);
|
||||
}
|
||||
|
||||
function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences): void {
|
||||
function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, context: TypeConstructionContext, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences): void {
|
||||
const extendsNode = getEffectiveBaseTypeNode(classDeclaration)!;
|
||||
const checker = context.program.getTypeChecker();
|
||||
const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode);
|
||||
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
// so duplicates cannot occur.
|
||||
const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember);
|
||||
|
||||
createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member));
|
||||
createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, context, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member));
|
||||
}
|
||||
|
||||
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
|
||||
|
||||
@@ -6,11 +6,10 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { program, sourceFile, span } = context;
|
||||
const { sourceFile, span } = context;
|
||||
const classDeclaration = getClass(sourceFile, span.start);
|
||||
const checker = program.getTypeChecker();
|
||||
return mapDefined<ExpressionWithTypeArguments, CodeFixAction>(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences));
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(context, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences));
|
||||
return changes.length === 0 ? undefined : createCodeFixAction(fixId, changes, [Diagnostics.Implement_interface_0, implementedTypeNode.getText(sourceFile)], fixId, Diagnostics.Implement_all_unimplemented_interfaces);
|
||||
});
|
||||
},
|
||||
@@ -21,7 +20,7 @@ namespace ts.codefix {
|
||||
const classDeclaration = getClass(diag.file, diag.start);
|
||||
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {
|
||||
for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)!) {
|
||||
addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file, classDeclaration, changes, context.preferences);
|
||||
addMissingDeclarations(context, implementedTypeNode, diag.file, classDeclaration, changes, context.preferences);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -37,13 +36,14 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function addMissingDeclarations(
|
||||
checker: TypeChecker,
|
||||
context: TypeConstructionContext,
|
||||
implementedTypeNode: ExpressionWithTypeArguments,
|
||||
sourceFile: SourceFile,
|
||||
classDeclaration: ClassLikeDeclaration,
|
||||
changeTracker: textChanges.ChangeTracker,
|
||||
preferences: UserPreferences,
|
||||
): void {
|
||||
const checker = context.program.getTypeChecker();
|
||||
const maybeHeritageClauseSymbol = getHeritageClauseSymbolTable(classDeclaration, checker);
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
// so duplicates cannot occur.
|
||||
@@ -60,12 +60,12 @@ namespace ts.codefix {
|
||||
createMissingIndexSignatureDeclaration(implementedType, IndexKind.String);
|
||||
}
|
||||
|
||||
createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member));
|
||||
createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, context, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member));
|
||||
|
||||
function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void {
|
||||
const indexInfoOfKind = checker.getIndexInfoOfType(type, kind);
|
||||
if (indexInfoOfKind) {
|
||||
changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration)!);
|
||||
changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration, /*flags*/ undefined, getNoopSymbolTrackerWithResolver(context))!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -6,23 +6,48 @@ namespace ts.codefix {
|
||||
* @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for.
|
||||
* @returns Empty string iff there are no member insertions.
|
||||
*/
|
||||
export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray<Symbol>, checker: TypeChecker, preferences: UserPreferences, out: (node: ClassElement) => void): void {
|
||||
export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray<Symbol>, context: TypeConstructionContext, preferences: UserPreferences, out: (node: ClassElement) => void): void {
|
||||
const classMembers = classDeclaration.symbol.members!;
|
||||
for (const symbol of possiblyMissingSymbols) {
|
||||
if (!classMembers.has(symbol.escapedName)) {
|
||||
addNewNodeForMemberSymbol(symbol, classDeclaration, checker, preferences, out);
|
||||
addNewNodeForMemberSymbol(symbol, classDeclaration, context, preferences, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getModuleSpecifierResolverHost(context: TypeConstructionContext): SymbolTracker["moduleResolverHost"] {
|
||||
return {
|
||||
directoryExists: context.host.directoryExists ? d => context.host.directoryExists!(d) : undefined,
|
||||
fileExists: context.host.fileExists ? f => context.host.fileExists!(f) : undefined,
|
||||
getCurrentDirectory: context.host.getCurrentDirectory ? () => context.host.getCurrentDirectory!() : undefined,
|
||||
readFile: context.host.readFile ? f => context.host.readFile!(f) : undefined,
|
||||
useCaseSensitiveFileNames: context.host.useCaseSensitiveFileNames ? () => context.host.useCaseSensitiveFileNames!() : undefined,
|
||||
getSourceFiles: () => context.program.getSourceFiles(),
|
||||
getCommonSourceDirectory: () => context.program.getCommonSourceDirectory(),
|
||||
};
|
||||
}
|
||||
|
||||
export function getNoopSymbolTrackerWithResolver(context: TypeConstructionContext): SymbolTracker {
|
||||
return {
|
||||
trackSymbol: noop,
|
||||
moduleResolverHost: getModuleSpecifierResolverHost(context),
|
||||
};
|
||||
}
|
||||
|
||||
export interface TypeConstructionContext {
|
||||
program: Program;
|
||||
host: ModuleSpecifierResolutionHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`.
|
||||
*/
|
||||
function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, preferences: UserPreferences, out: (node: Node) => void): void {
|
||||
function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, context: TypeConstructionContext, preferences: UserPreferences, out: (node: Node) => void): void {
|
||||
const declarations = symbol.getDeclarations();
|
||||
if (!(declarations && declarations.length)) {
|
||||
return undefined;
|
||||
}
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
const declaration = declarations[0];
|
||||
const name = getSynthesizedDeepClone(getNameOfDeclaration(declaration), /*includeTrivia*/ false) as PropertyName;
|
||||
@@ -36,7 +61,7 @@ namespace ts.codefix {
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
const typeNode = checker.typeToTypeNode(type, enclosingDeclaration);
|
||||
const typeNode = checker.typeToTypeNode(type, enclosingDeclaration, /*flags*/ undefined, getNoopSymbolTrackerWithResolver(context));
|
||||
out(createProperty(
|
||||
/*decorators*/undefined,
|
||||
modifiers,
|
||||
@@ -83,13 +108,13 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function outputMethod(signature: Signature, modifiers: NodeArray<Modifier> | undefined, name: PropertyName, body?: Block): void {
|
||||
const method = signatureToMethodDeclaration(checker, signature, enclosingDeclaration, modifiers, name, optional, body);
|
||||
const method = signatureToMethodDeclaration(context, signature, enclosingDeclaration, modifiers, name, optional, body);
|
||||
if (method) out(method);
|
||||
}
|
||||
}
|
||||
|
||||
function signatureToMethodDeclaration(
|
||||
checker: TypeChecker,
|
||||
context: TypeConstructionContext,
|
||||
signature: Signature,
|
||||
enclosingDeclaration: ClassLikeDeclaration,
|
||||
modifiers: NodeArray<Modifier> | undefined,
|
||||
@@ -97,7 +122,8 @@ namespace ts.codefix {
|
||||
optional: boolean,
|
||||
body: Block | undefined,
|
||||
): MethodDeclaration | undefined {
|
||||
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.SuppressAnyReturnType);
|
||||
const program = context.program;
|
||||
const signatureDeclaration = <MethodDeclaration>program.getTypeChecker().signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.NoTruncation | NodeBuilderFlags.SuppressAnyReturnType, getNoopSymbolTrackerWithResolver(context));
|
||||
if (!signatureDeclaration) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -117,18 +143,20 @@ namespace ts.codefix {
|
||||
inJs: boolean,
|
||||
makeStatic: boolean,
|
||||
preferences: UserPreferences,
|
||||
body: boolean,
|
||||
contextNode: Node,
|
||||
): MethodDeclaration {
|
||||
const body = !isInterfaceDeclaration(contextNode);
|
||||
const { typeArguments, arguments: args, parent } = call;
|
||||
const checker = context.program.getTypeChecker();
|
||||
const tracker = getNoopSymbolTrackerWithResolver(context);
|
||||
const types = map(args, arg =>
|
||||
// Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {"
|
||||
checker.typeToTypeNode(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(arg))));
|
||||
checker.typeToTypeNode(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(arg)), contextNode, /*flags*/ undefined, tracker));
|
||||
const names = map(args, arg =>
|
||||
isIdentifier(arg) ? arg.text :
|
||||
isPropertyAccessExpression(arg) ? arg.name.text : undefined);
|
||||
const contextualType = checker.getContextualType(call);
|
||||
const returnType = inJs ? undefined : contextualType && checker.typeToTypeNode(contextualType, call) || createKeywordTypeNode(SyntaxKind.AnyKeyword);
|
||||
const returnType = inJs ? undefined : contextualType && checker.typeToTypeNode(contextualType, contextNode, /*flags*/ undefined, tracker) || createKeywordTypeNode(SyntaxKind.AnyKeyword);
|
||||
return createMethod(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined,
|
||||
@@ -249,4 +277,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ namespace ts.codefix {
|
||||
const symbolName = isJsxOpeningLikeElement(symbolToken.parent)
|
||||
&& symbolToken.parent.tagName === symbolToken
|
||||
&& (isIntrinsicJsxName(symbolToken.text) || checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.All, /*excludeGlobals*/ false))
|
||||
? checker.getJsxNamespace()
|
||||
? checker.getJsxNamespace(sourceFile)
|
||||
: symbolToken.text;
|
||||
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
|
||||
Debug.assert(symbolName !== InternalSymbolName.Default);
|
||||
|
||||
@@ -187,24 +187,10 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function isApplicableFunctionForInference(declaration: FunctionLike): declaration is MethodDeclaration | FunctionDeclaration | ConstructorDeclaration {
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
return true;
|
||||
case SyntaxKind.FunctionExpression:
|
||||
const parent = declaration.parent;
|
||||
return isVariableDeclaration(parent) && isIdentifier(parent.name) || !!declaration.name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function annotateParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterDeclaration: ParameterDeclaration, containingFunction: FunctionLike, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void {
|
||||
if (!isIdentifier(parameterDeclaration.name) || !isApplicableFunctionForInference(containingFunction)) {
|
||||
if (!isIdentifier(parameterDeclaration.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parameterInferences = inferTypeForParametersFromUsage(containingFunction, sourceFile, program, cancellationToken) ||
|
||||
containingFunction.parameters.map<ParameterInference>(p => ({
|
||||
declaration: p,
|
||||
@@ -216,11 +202,14 @@ namespace ts.codefix {
|
||||
annotateJSDocParameters(changes, sourceFile, parameterInferences, program, host);
|
||||
}
|
||||
else {
|
||||
const needParens = isArrowFunction(containingFunction) && !findChildOfKind(containingFunction, SyntaxKind.OpenParenToken, sourceFile);
|
||||
if (needParens) changes.insertNodeBefore(sourceFile, first(containingFunction.parameters), createToken(SyntaxKind.OpenParenToken));
|
||||
for (const { declaration, type } of parameterInferences) {
|
||||
if (declaration && !declaration.type && !declaration.initializer) {
|
||||
annotate(changes, sourceFile, declaration, type, program, host);
|
||||
}
|
||||
}
|
||||
if (needParens) changes.insertNodeAfter(sourceFile, last(containingFunction.parameters), createToken(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,12 +331,13 @@ namespace ts.codefix {
|
||||
return InferFromReference.unifyFromContext(types, checker);
|
||||
}
|
||||
|
||||
function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
|
||||
function inferTypeForParametersFromUsage(containingFunction: FunctionLike, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
|
||||
let searchToken;
|
||||
switch (containingFunction.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
searchToken = findChildOfKind<Token<SyntaxKind.ConstructorKeyword>>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile);
|
||||
break;
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
const parent = containingFunction.parent;
|
||||
searchToken = isVariableDeclaration(parent) && isIdentifier(parent.name) ?
|
||||
@@ -399,7 +389,7 @@ namespace ts.codefix {
|
||||
return inferFromContext(usageContext, checker);
|
||||
}
|
||||
|
||||
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier>, declaration: FunctionLikeDeclaration, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
|
||||
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier>, declaration: FunctionLike, program: Program, cancellationToken: CancellationToken): ParameterInference[] | undefined {
|
||||
const checker = program.getTypeChecker();
|
||||
if (references.length === 0) {
|
||||
return undefined;
|
||||
@@ -413,10 +403,9 @@ namespace ts.codefix {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
inferTypeFromContext(reference, checker, usageContext);
|
||||
}
|
||||
const isConstructor = declaration.kind === SyntaxKind.Constructor;
|
||||
const callContexts = isConstructor ? usageContext.constructContexts : usageContext.callContexts;
|
||||
return callContexts && declaration.parameters.map((parameter, parameterIndex): ParameterInference => {
|
||||
const types: Type[] = [];
|
||||
const callContexts = [...usageContext.constructContexts || [], ...usageContext.callContexts || []];
|
||||
return declaration.parameters.map((parameter, parameterIndex): ParameterInference => {
|
||||
const types = [];
|
||||
const isRest = isRestParameter(parameter);
|
||||
let isOptional = false;
|
||||
for (const callContext of callContexts) {
|
||||
@@ -434,7 +423,8 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
if (isIdentifier(parameter.name)) {
|
||||
types.push(...inferTypesFromReferences(getReferences(parameter.name, program, cancellationToken), checker, cancellationToken));
|
||||
const inferred = inferTypesFromReferences(getReferences(parameter.name, program, cancellationToken), checker, cancellationToken);
|
||||
types.push(...(isRest ? mapDefined(inferred, checker.getElementTypeOfArrayType) : inferred));
|
||||
}
|
||||
const type = unifyFromContext(types, checker);
|
||||
return {
|
||||
|
||||
+19
-14
@@ -104,7 +104,7 @@ namespace ts.Completions {
|
||||
getJSCompletionEntries(sourceFile, location!.pos, uniqueNames, compilerOptions.target!, entries); // TODO: GH#18217
|
||||
}
|
||||
else {
|
||||
if ((!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
|
||||
if (!isNewIdentifierLocation && (!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -367,7 +367,9 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function getSymbolName(symbol: Symbol, origin: SymbolOriginInfo | undefined, target: ScriptTarget): string {
|
||||
return origin && originIsExport(origin) && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default
|
||||
return origin && originIsExport(origin) && (
|
||||
(origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default) ||
|
||||
(symbol.escapedName === InternalSymbolName.ExportEquals))
|
||||
// Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
|
||||
? firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined)
|
||||
|| codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target)
|
||||
@@ -913,7 +915,7 @@ namespace ts.Completions {
|
||||
}
|
||||
else {
|
||||
for (const symbol of type.getApparentProperties()) {
|
||||
if (typeChecker.isValidPropertyAccessForCompletions(node.kind === SyntaxKind.ImportType ? <ImportTypeNode>node : <PropertyAccessExpression>node.parent, type, symbol)) {
|
||||
if (typeChecker.isValidPropertyAccessForCompletions(node.kind === SyntaxKind.ImportType ? <ImportTypeNode>node : <PropertyAccessExpression | QualifiedName>node.parent, type, symbol)) {
|
||||
addPropertySymbol(symbol);
|
||||
}
|
||||
}
|
||||
@@ -1021,7 +1023,8 @@ namespace ts.Completions {
|
||||
const scopeNode = getScopeNode(contextToken, adjustedPosition, sourceFile) || sourceFile;
|
||||
isInSnippetScope = isSnippetScope(scopeNode);
|
||||
|
||||
const symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias;
|
||||
const isTypeOnly = isTypeOnlyCompletion();
|
||||
const symbolMeanings = (isTypeOnly ? SymbolFlags.None : SymbolFlags.Value) | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias;
|
||||
|
||||
symbols = Debug.assertEachDefined(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings), "getSymbolsInScope() should all be defined");
|
||||
|
||||
@@ -1049,12 +1052,10 @@ namespace ts.Completions {
|
||||
if (sourceFile.externalModuleIndicator) return true;
|
||||
// If already using commonjs, don't introduce ES6.
|
||||
if (sourceFile.commonJsModuleIndicator) return false;
|
||||
// For JS, stay on the safe side.
|
||||
if (isUncheckedFile) return false;
|
||||
// If some file is using ES6 modules, assume that it's OK to add more.
|
||||
if (programContainsEs6Modules(program)) return true;
|
||||
// If module transpilation is enabled or we're targeting es6 or above, or not emitting, OK.
|
||||
return compilerOptionsIndicateEs6Modules(program.getCompilerOptions());
|
||||
if (compilerOptionsIndicateEs6Modules(program.getCompilerOptions())) return true;
|
||||
// If some file is using ES6 modules, assume that it's OK to add more.
|
||||
return programContainsEs6Modules(program);
|
||||
}
|
||||
|
||||
function isSnippetScope(scopeNode: Node): boolean {
|
||||
@@ -1070,9 +1071,9 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function filterGlobalCompletion(symbols: Symbol[]): void {
|
||||
const isTypeOnlyCompletion = insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken));
|
||||
const allowTypes = isTypeOnlyCompletion || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
|
||||
if (isTypeOnlyCompletion) keywordFilters = KeywordCompletionFilters.TypeKeywords;
|
||||
const isTypeOnly = isTypeOnlyCompletion();
|
||||
const allowTypes = isTypeOnly || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
|
||||
if (isTypeOnly) keywordFilters = KeywordCompletionFilters.TypeKeywords;
|
||||
|
||||
filterMutate(symbols, symbol => {
|
||||
if (!isSourceFile(location)) {
|
||||
@@ -1091,7 +1092,7 @@ namespace ts.Completions {
|
||||
if (allowTypes) {
|
||||
// Its a type, but you can reach it by namespace.type as well
|
||||
const symbolAllowedAsType = symbolCanBeReferencedAtTypeLocation(symbol);
|
||||
if (symbolAllowedAsType || isTypeOnlyCompletion) {
|
||||
if (symbolAllowedAsType || isTypeOnly) {
|
||||
return symbolAllowedAsType;
|
||||
}
|
||||
}
|
||||
@@ -1102,6 +1103,10 @@ namespace ts.Completions {
|
||||
});
|
||||
}
|
||||
|
||||
function isTypeOnlyCompletion(): boolean {
|
||||
return insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken));
|
||||
}
|
||||
|
||||
function isContextTokenValueLocation(contextToken: Node) {
|
||||
return contextToken &&
|
||||
contextToken.kind === SyntaxKind.TypeOfKeyword &&
|
||||
@@ -1954,7 +1959,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function isFunctionLikeBodyKeyword(kind: SyntaxKind) {
|
||||
return kind === SyntaxKind.AsyncKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind);
|
||||
return kind === SyntaxKind.AsyncKeyword || kind === SyntaxKind.AwaitKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind);
|
||||
}
|
||||
|
||||
function keywordForNode(node: Node): SyntaxKind {
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace ts.FindAllReferences {
|
||||
readonly isForRename?: boolean;
|
||||
/** True if we are searching for implementations. We will have a different method of adding references if so. */
|
||||
readonly implementations?: boolean;
|
||||
/**
|
||||
* True to opt in for enhanced renaming of shorthand properties and import/export specifiers.
|
||||
* Default is false for backwards compatibility.
|
||||
*/
|
||||
readonly providePrefixAndSuffixTextForRename?: boolean;
|
||||
}
|
||||
|
||||
export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
|
||||
@@ -157,8 +162,8 @@ namespace ts.FindAllReferences {
|
||||
return { displayParts, kind: symbolKind };
|
||||
}
|
||||
|
||||
export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker): RenameLocation {
|
||||
return { ...entryToDocumentSpan(entry), ...getPrefixAndSuffixText(entry, originalNode, checker) };
|
||||
export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker, providePrefixAndSuffixText: boolean): RenameLocation {
|
||||
return { ...entryToDocumentSpan(entry), ...(providePrefixAndSuffixText && getPrefixAndSuffixText(entry, originalNode, checker)) };
|
||||
}
|
||||
|
||||
export function toReferenceEntry(entry: Entry): ReferenceEntry {
|
||||
@@ -484,7 +489,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
/** Core find-all-references algorithm for a normal symbol. */
|
||||
function getReferencedSymbolsForSymbol(originalSymbol: Symbol, node: Node | undefined, sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
|
||||
const symbol = node && skipPastExportOrImportSpecifierOrUnion(originalSymbol, node, checker, !!options.isForRename) || originalSymbol;
|
||||
const symbol = node && skipPastExportOrImportSpecifierOrUnion(originalSymbol, node, checker, /*useLocalSymbolForExportSpecifier*/ !isForRenameWithPrefixAndSuffixText(options)) || originalSymbol;
|
||||
|
||||
// Compute the meaning from the location and the symbol it references
|
||||
const searchMeaning = node ? getIntersectingMeaningFromDeclarations(node, symbol) : SemanticMeaning.All;
|
||||
@@ -492,7 +497,7 @@ namespace ts.FindAllReferences.Core {
|
||||
const result: SymbolAndEntries[] = [];
|
||||
const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result);
|
||||
|
||||
const exportSpecifier = !options.isForRename ? undefined : find(symbol.declarations, isExportSpecifier);
|
||||
const exportSpecifier = !isForRenameWithPrefixAndSuffixText(options) ? undefined : find(symbol.declarations, isExportSpecifier);
|
||||
if (exportSpecifier) {
|
||||
// When renaming at an export specifier, rename the export and not the thing being exported.
|
||||
getReferencesAtExportSpecifier(exportSpecifier.name, symbol, exportSpecifier, state.createSearch(node, originalSymbol, /*comingFrom*/ undefined), state, /*addReferencesHere*/ true, /*alwaysGetReferences*/ true);
|
||||
@@ -502,7 +507,7 @@ namespace ts.FindAllReferences.Core {
|
||||
searchForImportsOfExport(node, symbol, { exportingModuleSymbol: Debug.assertDefined(symbol.parent, "Expected export symbol to have a parent"), exportKind: ExportKind.Default }, state);
|
||||
}
|
||||
else {
|
||||
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, !!options.isForRename, !!options.implementations) : [symbol] });
|
||||
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, !!options.isForRename, !!options.providePrefixAndSuffixTextForRename, !!options.implementations) : [symbol] });
|
||||
|
||||
// Try to get the smallest valid scope that we can limit our search to;
|
||||
// otherwise we'll need to search globally (i.e. include each file).
|
||||
@@ -538,9 +543,9 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
/** Handle a few special cases relating to export/import specifiers. */
|
||||
function skipPastExportOrImportSpecifierOrUnion(symbol: Symbol, node: Node, checker: TypeChecker, isForRename: boolean): Symbol | undefined {
|
||||
function skipPastExportOrImportSpecifierOrUnion(symbol: Symbol, node: Node, checker: TypeChecker, useLocalSymbolForExportSpecifier: boolean): Symbol | undefined {
|
||||
const { parent } = node;
|
||||
if (isExportSpecifier(parent) && !isForRename) {
|
||||
if (isExportSpecifier(parent) && useLocalSymbolForExportSpecifier) {
|
||||
return getLocalSymbolForExportSpecifier(node as Identifier, symbol, parent, checker);
|
||||
}
|
||||
// If the symbol is declared as part of a declaration like `{ type: "a" } | { type: "b" }`, use the property on the union type to get more references.
|
||||
@@ -1071,6 +1076,8 @@ namespace ts.FindAllReferences.Core {
|
||||
addReferencesHere: boolean,
|
||||
alwaysGetReferences?: boolean,
|
||||
): void {
|
||||
Debug.assert(!alwaysGetReferences || !!state.options.providePrefixAndSuffixTextForRename, "If alwaysGetReferences is true, then prefix/suffix text must be enabled");
|
||||
|
||||
const { parent, propertyName, name } = exportSpecifier;
|
||||
const exportDeclaration = parent.parent;
|
||||
const localSymbol = getLocalSymbolForExportSpecifier(referenceLocation, referenceSymbol, exportSpecifier, state.checker);
|
||||
@@ -1102,7 +1109,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
// For `export { foo as bar }`, rename `foo`, but not `bar`.
|
||||
if (!state.options.isForRename || alwaysGetReferences) {
|
||||
if (!isForRenameWithPrefixAndSuffixText(state.options) || alwaysGetReferences) {
|
||||
const exportKind = referenceLocation.originalKeywordKind === SyntaxKind.DefaultKeyword ? ExportKind.Default : ExportKind.Named;
|
||||
const exportSymbol = Debug.assertDefined(exportSpecifier.symbol);
|
||||
const exportInfo = Debug.assertDefined(getExportInfo(exportSymbol, exportKind, state.checker));
|
||||
@@ -1110,7 +1117,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
// At `export { x } from "foo"`, also search for the imported symbol `"foo".x`.
|
||||
if (search.comingFrom !== ImportExport.Export && exportDeclaration.moduleSpecifier && !propertyName && !state.options.isForRename) {
|
||||
if (search.comingFrom !== ImportExport.Export && exportDeclaration.moduleSpecifier && !propertyName && !isForRenameWithPrefixAndSuffixText(state.options)) {
|
||||
const imported = state.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier);
|
||||
if (imported) searchForImportedSymbol(imported, state);
|
||||
}
|
||||
@@ -1145,7 +1152,7 @@ namespace ts.FindAllReferences.Core {
|
||||
const { symbol } = importOrExport;
|
||||
|
||||
if (importOrExport.kind === ImportExport.Import) {
|
||||
if (!state.options.isForRename) {
|
||||
if (!(isForRenameWithPrefixAndSuffixText(state.options))) {
|
||||
searchForImportedSymbol(symbol, state);
|
||||
}
|
||||
}
|
||||
@@ -1428,6 +1435,10 @@ namespace ts.FindAllReferences.Core {
|
||||
return [{ definition: { type: DefinitionKind.Symbol, symbol: searchSpaceNode.symbol }, references }];
|
||||
}
|
||||
|
||||
function isParameterName(node: Node) {
|
||||
return node.kind === SyntaxKind.Identifier && node.parent.kind === SyntaxKind.Parameter && (<ParameterDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken): SymbolAndEntries[] | undefined {
|
||||
let searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false);
|
||||
|
||||
@@ -1450,7 +1461,7 @@ namespace ts.FindAllReferences.Core {
|
||||
searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
if (isExternalModule(<SourceFile>searchSpaceNode)) {
|
||||
if (isExternalModule(<SourceFile>searchSpaceNode) || isParameterName(thisOrSuperKeyword)) {
|
||||
return undefined;
|
||||
}
|
||||
// falls through
|
||||
@@ -1483,7 +1494,7 @@ namespace ts.FindAllReferences.Core {
|
||||
// and has the appropriate static modifier from the original container.
|
||||
return container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag;
|
||||
case SyntaxKind.SourceFile:
|
||||
return container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container);
|
||||
return container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container) && !isParameterName(node);
|
||||
}
|
||||
});
|
||||
}).map(n => nodeEntry(n));
|
||||
@@ -1510,16 +1521,16 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
// For certain symbol kinds, we need to include other symbols in the search set.
|
||||
// This is not needed when searching for re-exports.
|
||||
function populateSearchSymbolSet(symbol: Symbol, location: Node, checker: TypeChecker, isForRename: boolean, implementations: boolean): Symbol[] {
|
||||
function populateSearchSymbolSet(symbol: Symbol, location: Node, checker: TypeChecker, isForRename: boolean, providePrefixAndSuffixText: boolean, implementations: boolean): Symbol[] {
|
||||
const result: Symbol[] = [];
|
||||
forEachRelatedSymbol<void>(symbol, location, checker, isForRename,
|
||||
forEachRelatedSymbol<void>(symbol, location, checker, isForRename, !(isForRename && providePrefixAndSuffixText),
|
||||
(sym, root, base) => { result.push(base || root || sym); },
|
||||
/*allowBaseTypes*/ () => !implementations);
|
||||
return result;
|
||||
}
|
||||
|
||||
function forEachRelatedSymbol<T>(
|
||||
symbol: Symbol, location: Node, checker: TypeChecker, isForRenamePopulateSearchSymbolSet: boolean,
|
||||
symbol: Symbol, location: Node, checker: TypeChecker, isForRenamePopulateSearchSymbolSet: boolean, onlyIncludeBindingElementAtReferenceLocation: boolean,
|
||||
cbSymbol: (symbol: Symbol, rootSymbol?: Symbol, baseSymbol?: Symbol, kind?: NodeEntryKind) => T | undefined,
|
||||
allowBaseTypes: (rootSymbol: Symbol) => boolean,
|
||||
): T | undefined {
|
||||
@@ -1573,9 +1584,25 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
// symbolAtLocation for a binding element is the local symbol. See if the search symbol is the property.
|
||||
// Don't do this when populating search set for a rename -- just rename the local.
|
||||
// Don't do this when populating search set for a rename when prefix and suffix text will be provided -- just rename the local.
|
||||
if (!isForRenamePopulateSearchSymbolSet) {
|
||||
const bindingElementPropertySymbol = isObjectBindingElementWithoutPropertyName(location.parent) ? getPropertySymbolFromBindingElement(checker, location.parent) : undefined;
|
||||
let bindingElementPropertySymbol: Symbol | undefined;
|
||||
if (onlyIncludeBindingElementAtReferenceLocation) {
|
||||
bindingElementPropertySymbol = isObjectBindingElementWithoutPropertyName(location.parent) ? getPropertySymbolFromBindingElement(checker, location.parent) : undefined;
|
||||
}
|
||||
else {
|
||||
bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, checker);
|
||||
}
|
||||
return bindingElementPropertySymbol && fromRoot(bindingElementPropertySymbol, EntryKind.SearchedPropertyFoundLocal);
|
||||
}
|
||||
|
||||
Debug.assert(isForRenamePopulateSearchSymbolSet);
|
||||
// due to the above assert and the arguments at the uses of this function,
|
||||
// (onlyIncludeBindingElementAtReferenceLocation <=> !providePrefixAndSuffixTextForRename) holds
|
||||
const includeOriginalSymbolOfBindingElement = onlyIncludeBindingElementAtReferenceLocation;
|
||||
|
||||
if (includeOriginalSymbolOfBindingElement) {
|
||||
const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, checker);
|
||||
return bindingElementPropertySymbol && fromRoot(bindingElementPropertySymbol, EntryKind.SearchedPropertyFoundLocal);
|
||||
}
|
||||
|
||||
@@ -1593,6 +1620,13 @@ namespace ts.FindAllReferences.Core {
|
||||
? getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker, base => cbSymbol(sym, rootSymbol, base, kind))
|
||||
: undefined));
|
||||
}
|
||||
|
||||
function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
|
||||
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
|
||||
if (bindingElement && isObjectBindingElementWithoutPropertyName(bindingElement)) {
|
||||
return getPropertySymbolFromBindingElement(checker, bindingElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface RelatedSymbol {
|
||||
@@ -1602,6 +1636,7 @@ namespace ts.FindAllReferences.Core {
|
||||
function getRelatedSymbol(search: Search, referenceSymbol: Symbol, referenceLocation: Node, state: State): RelatedSymbol | undefined {
|
||||
const { checker } = state;
|
||||
return forEachRelatedSymbol(referenceSymbol, referenceLocation, checker, /*isForRenamePopulateSearchSymbolSet*/ false,
|
||||
/*onlyIncludeBindingElementAtReferenceLocation*/ !state.options.isForRename || !!state.options.providePrefixAndSuffixTextForRename,
|
||||
(sym, rootSymbol, baseSymbol, kind): RelatedSymbol | undefined => search.includes(baseSymbol || rootSymbol || sym)
|
||||
// For a base type, use the symbol for the derived type. For a synthetic (e.g. union) property, use the union symbol.
|
||||
? { symbol: rootSymbol && !(getCheckFlags(sym) & CheckFlags.Synthetic) ? rootSymbol : sym, kind }
|
||||
@@ -1692,4 +1727,8 @@ namespace ts.FindAllReferences.Core {
|
||||
t.symbol && t.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? t.symbol : undefined);
|
||||
return res.length === 0 ? undefined : res;
|
||||
}
|
||||
|
||||
function isForRenameWithPrefixAndSuffixText(options: Options) {
|
||||
return options.isForRename && options.providePrefixAndSuffixTextForRename;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1106,10 +1106,7 @@ namespace ts.formatting {
|
||||
* Trimming will be done for lines after the previous range
|
||||
*/
|
||||
function trimTrailingWhitespacesForRemainingRange() {
|
||||
if (!previousRange) {
|
||||
return;
|
||||
}
|
||||
const startPosition = previousRange.end;
|
||||
const startPosition = previousRange ? previousRange.end : originalRange.pos;
|
||||
|
||||
const startLine = sourceFile.getLineAndCharacterOfPosition(startPosition).line;
|
||||
const endLine = sourceFile.getLineAndCharacterOfPosition(originalRange.end).line;
|
||||
|
||||
@@ -10,11 +10,11 @@ namespace ts.formatting {
|
||||
}
|
||||
|
||||
export class FormattingContext {
|
||||
public currentTokenSpan: TextRangeWithKind;
|
||||
public nextTokenSpan: TextRangeWithKind;
|
||||
public contextNode: Node;
|
||||
public currentTokenParent: Node;
|
||||
public nextTokenParent: Node;
|
||||
public currentTokenSpan!: TextRangeWithKind;
|
||||
public nextTokenSpan!: TextRangeWithKind;
|
||||
public contextNode!: Node;
|
||||
public currentTokenParent!: Node;
|
||||
public nextTokenParent!: Node;
|
||||
|
||||
private contextNodeAllOnSameLine: boolean | undefined;
|
||||
private nextNodeAllOnSameLine: boolean | undefined;
|
||||
@@ -26,17 +26,11 @@ namespace ts.formatting {
|
||||
}
|
||||
|
||||
public updateContext(currentRange: TextRangeWithKind, currentTokenParent: Node, nextRange: TextRangeWithKind, nextTokenParent: Node, commonParent: Node) {
|
||||
Debug.assert(currentRange !== undefined, "currentTokenSpan is null");
|
||||
Debug.assert(currentTokenParent !== undefined, "currentTokenParent is null");
|
||||
Debug.assert(nextRange !== undefined, "nextTokenSpan is null");
|
||||
Debug.assert(nextTokenParent !== undefined, "nextTokenParent is null");
|
||||
Debug.assert(commonParent !== undefined, "commonParent is null");
|
||||
|
||||
this.currentTokenSpan = currentRange;
|
||||
this.currentTokenParent = currentTokenParent;
|
||||
this.nextTokenSpan = nextRange;
|
||||
this.nextTokenParent = nextTokenParent;
|
||||
this.contextNode = commonParent;
|
||||
this.currentTokenSpan = Debug.assertDefined(currentRange);
|
||||
this.currentTokenParent = Debug.assertDefined(currentTokenParent);
|
||||
this.nextTokenSpan = Debug.assertDefined(nextRange);
|
||||
this.nextTokenParent = Debug.assertDefined(nextTokenParent);
|
||||
this.contextNode = Debug.assertDefined(commonParent);
|
||||
|
||||
// drop cached results
|
||||
this.contextNodeAllOnSameLine = undefined;
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace ts {
|
||||
const toImport = oldFromNew !== undefined
|
||||
// If we're at the new location (file was already renamed), need to redo module resolution starting from the old location.
|
||||
// TODO:GH#18217
|
||||
? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew, host)
|
||||
? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew)
|
||||
: getSourceFileToImport(importedModuleSymbol, importLiteral, sourceFile, program, host, oldToNew);
|
||||
|
||||
// Need an update if the imported file moved, or the importing file moved and was using a relative path.
|
||||
@@ -192,28 +192,35 @@ namespace ts {
|
||||
const resolved = host.resolveModuleNames
|
||||
? host.getResolvedModuleWithFailedLookupLocationsFromCache && host.getResolvedModuleWithFailedLookupLocationsFromCache(importLiteral.text, importingSourceFile.fileName)
|
||||
: program.getResolvedModuleWithFailedLookupLocationsFromCache(importLiteral.text, importingSourceFile.fileName);
|
||||
return getSourceFileToImportFromResolved(resolved, oldToNew, host);
|
||||
return getSourceFileToImportFromResolved(resolved, oldToNew);
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceFileToImportFromResolved(resolved: ResolvedModuleWithFailedLookupLocations | undefined, oldToNew: PathUpdater, host: LanguageServiceHost): ToImport | undefined {
|
||||
function getSourceFileToImportFromResolved(resolved: ResolvedModuleWithFailedLookupLocations | undefined, oldToNew: PathUpdater): ToImport | undefined {
|
||||
// Search through all locations looking for a moved file, and only then test already existing files.
|
||||
// This is because if `a.ts` is compiled to `a.js` and `a.ts` is moved, we don't want to resolve anything to `a.js`, but to `a.ts`'s new location.
|
||||
return tryEach(tryGetNewFile) || tryEach(tryGetOldFile);
|
||||
if (!resolved) return undefined;
|
||||
|
||||
function tryEach(cb: (oldFileName: string) => ToImport | undefined): ToImport | undefined {
|
||||
return resolved && (
|
||||
(resolved.resolvedModule && cb(resolved.resolvedModule.resolvedFileName)) || firstDefined(resolved.failedLookupLocations, cb));
|
||||
// First try resolved module
|
||||
if (resolved.resolvedModule) {
|
||||
const result = tryChange(resolved.resolvedModule.resolvedFileName);
|
||||
if (result) return result;
|
||||
}
|
||||
|
||||
function tryGetNewFile(oldFileName: string): ToImport | undefined {
|
||||
const newFileName = oldToNew(oldFileName);
|
||||
return newFileName !== undefined && host.fileExists!(newFileName) ? { newFileName, updated: true } : undefined; // TODO: GH#18217
|
||||
// Then failed lookups except package.json since we dont want to touch them (only included ts/js files)
|
||||
const result = forEach(resolved.failedLookupLocations, tryChangeWithIgnoringPackageJson);
|
||||
if (result) return result;
|
||||
|
||||
// If nothing changed, then result is resolved module file thats not updated
|
||||
return resolved.resolvedModule && { newFileName: resolved.resolvedModule.resolvedFileName, updated: false };
|
||||
|
||||
function tryChangeWithIgnoringPackageJson(oldFileName: string) {
|
||||
return !endsWith(oldFileName, "/package.json") ? tryChange(oldFileName) : undefined;
|
||||
}
|
||||
|
||||
function tryGetOldFile(oldFileName: string): ToImport | undefined {
|
||||
function tryChange(oldFileName: string) {
|
||||
const newFileName = oldToNew(oldFileName);
|
||||
return host.fileExists!(oldFileName) ? newFileName !== undefined ? { newFileName, updated: true } : { newFileName: oldFileName, updated: false } : undefined; // TODO: GH#18217
|
||||
return newFileName && { newFileName, updated: true };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -660,7 +660,7 @@ namespace ts.NavigationBar {
|
||||
else if (isCallExpression(parent)) {
|
||||
const name = getCalledExpressionName(parent.expression);
|
||||
if (name !== undefined) {
|
||||
const args = mapDefined(parent.arguments, a => isStringLiteral(a) ? a.getText(curSourceFile) : undefined).join(", ");
|
||||
const args = mapDefined(parent.arguments, a => isStringLiteralLike(a) ? a.getText(curSourceFile) : undefined).join(", ");
|
||||
return `${name}(${args}) callback`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ namespace ts.OrganizeImports {
|
||||
organizeImportsWorker(topLevelExportDecls, coalesceExports);
|
||||
|
||||
for (const ambientModule of sourceFile.statements.filter(isAmbientModule)) {
|
||||
const ambientModuleBody = getModuleBlock(ambientModule as ModuleDeclaration)!; // TODO: GH#18217
|
||||
if (!ambientModule.body) { continue; }
|
||||
|
||||
const ambientModuleImportDecls = ambientModuleBody.statements.filter(isImportDeclaration);
|
||||
const ambientModuleImportDecls = ambientModule.body.statements.filter(isImportDeclaration);
|
||||
organizeImportsWorker(ambientModuleImportDecls, coalesceAndOrganizeImports);
|
||||
|
||||
const ambientModuleExportDecls = ambientModuleBody.statements.filter(isExportDeclaration);
|
||||
const ambientModuleExportDecls = ambientModule.body.statements.filter(isExportDeclaration);
|
||||
organizeImportsWorker(ambientModuleExportDecls, coalesceExports);
|
||||
}
|
||||
|
||||
@@ -81,14 +81,9 @@ namespace ts.OrganizeImports {
|
||||
}
|
||||
}
|
||||
|
||||
function getModuleBlock(moduleDecl: ModuleDeclaration): ModuleBlock | undefined {
|
||||
const body = moduleDecl.body;
|
||||
return body && !isIdentifier(body) ? (isModuleBlock(body) ? body : getModuleBlock(body)) : undefined;
|
||||
}
|
||||
|
||||
function removeUnusedImports(oldImports: ReadonlyArray<ImportDeclaration>, sourceFile: SourceFile, program: Program) {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const jsxNamespace = typeChecker.getJsxNamespace();
|
||||
const jsxNamespace = typeChecker.getJsxNamespace(sourceFile);
|
||||
const jsxElementsPresent = !!(sourceFile.transformFlags & TransformFlags.ContainsJsx);
|
||||
|
||||
const usedImports: ImportDeclaration[] = [];
|
||||
|
||||
@@ -37,6 +37,10 @@ namespace ts.OutliningElementsCollector {
|
||||
addOutliningForLeadingCommentsForNode(n, sourceFile, cancellationToken, out);
|
||||
}
|
||||
|
||||
if (isFunctionExpressionAssignedToVariable(n)) {
|
||||
addOutliningForLeadingCommentsForNode(n.parent.parent.parent, sourceFile, cancellationToken, out);
|
||||
}
|
||||
|
||||
const span = getOutliningSpanForNode(n, sourceFile);
|
||||
if (span) out.push(span);
|
||||
|
||||
@@ -54,6 +58,14 @@ namespace ts.OutliningElementsCollector {
|
||||
}
|
||||
depthRemaining++;
|
||||
}
|
||||
|
||||
function isFunctionExpressionAssignedToVariable(n: Node) {
|
||||
if (!isFunctionExpression(n) && !isArrowFunction(n)) {
|
||||
return false;
|
||||
}
|
||||
const ancestor = findAncestor(n, isVariableStatement);
|
||||
return !!ancestor && getSingleInitializerOfVariableStatementOrPropertyDeclaration(ancestor) === n;
|
||||
}
|
||||
}
|
||||
|
||||
function addRegionOutliningSpans(sourceFile: SourceFile, out: Push<OutliningSpan>): void {
|
||||
@@ -175,6 +187,7 @@ namespace ts.OutliningElementsCollector {
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return spanForNode(n.parent);
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.CaseBlock:
|
||||
@@ -206,10 +219,10 @@ namespace ts.OutliningElementsCollector {
|
||||
}
|
||||
|
||||
function spanForObjectOrArrayLiteral(node: Node, open: SyntaxKind.OpenBraceToken | SyntaxKind.OpenBracketToken = SyntaxKind.OpenBraceToken): OutliningSpan | undefined {
|
||||
// If the block has no leading keywords and is inside an array literal,
|
||||
// If the block has no leading keywords and is inside an array literal or call expression,
|
||||
// we only want to collapse the span of the block.
|
||||
// Otherwise, the collapsed section will include the end of the previous line.
|
||||
return spanForNode(node, /*autoCollapse*/ false, /*useFullStart*/ !isArrayLiteralExpression(node.parent), open);
|
||||
return spanForNode(node, /*autoCollapse*/ false, /*useFullStart*/ !isArrayLiteralExpression(node.parent) && !isCallExpression(node.parent), open);
|
||||
}
|
||||
|
||||
function spanForNode(hintSpanNode: Node, autoCollapse = false, useFullStart = true, open: SyntaxKind.OpenBraceToken | SyntaxKind.OpenBracketToken = SyntaxKind.OpenBraceToken): OutliningSpan | undefined {
|
||||
|
||||
@@ -449,8 +449,7 @@ namespace ts.refactor.extractSymbol {
|
||||
case SyntaxKind.ThisKeyword:
|
||||
rangeFacts |= RangeFacts.UsesThis;
|
||||
break;
|
||||
case SyntaxKind.LabeledStatement:
|
||||
{
|
||||
case SyntaxKind.LabeledStatement: {
|
||||
const label = (<LabeledStatement>node).label;
|
||||
(seenLabels || (seenLabels = [])).push(label.escapedText);
|
||||
forEachChild(node, visit);
|
||||
@@ -458,8 +457,7 @@ namespace ts.refactor.extractSymbol {
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.BreakStatement:
|
||||
case SyntaxKind.ContinueStatement:
|
||||
{
|
||||
case SyntaxKind.ContinueStatement: {
|
||||
const label = (<BreakStatement | ContinueStatement>node).label;
|
||||
if (label) {
|
||||
if (!contains(seenLabels, label.escapedText)) {
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
readonly declaration: AcceptedDeclaration;
|
||||
readonly fieldName: AcceptedNameType;
|
||||
readonly accessorName: AcceptedNameType;
|
||||
readonly originalName: AcceptedNameType;
|
||||
readonly originalName: string;
|
||||
readonly renameAccessor: boolean;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
// readonly modifier only existed in classLikeDeclaration
|
||||
const constructor = getFirstConstructorWithBody(<ClassLikeDeclaration>container);
|
||||
if (constructor) {
|
||||
updateReadonlyPropertyInitializerStatementConstructor(changeTracker, context, constructor, fieldName, originalName);
|
||||
updateReadonlyPropertyInitializerStatementConstructor(changeTracker, file, constructor, fieldName.text, originalName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -135,7 +135,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
isReadonly: hasReadonlyModifier(declaration),
|
||||
type: getTypeAnnotationNode(declaration),
|
||||
container: declaration.kind === SyntaxKind.Parameter ? declaration.parent.parent : declaration.parent,
|
||||
originalName: <AcceptedNameType>declaration.name,
|
||||
originalName: (<AcceptedNameType>declaration.name).text,
|
||||
declaration,
|
||||
fieldName,
|
||||
accessorName,
|
||||
@@ -221,22 +221,22 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
: changeTracker.insertNodeAfter(file, declaration, accessor);
|
||||
}
|
||||
|
||||
function updateReadonlyPropertyInitializerStatementConstructor(changeTracker: textChanges.ChangeTracker, context: RefactorContext, constructor: ConstructorDeclaration, fieldName: AcceptedNameType, originalName: AcceptedNameType) {
|
||||
function updateReadonlyPropertyInitializerStatementConstructor(changeTracker: textChanges.ChangeTracker, file: SourceFile, constructor: ConstructorDeclaration, fieldName: string, originalName: string) {
|
||||
if (!constructor.body) return;
|
||||
const { file, program, cancellationToken } = context;
|
||||
|
||||
const referenceEntries = mapDefined(FindAllReferences.getReferenceEntriesForNode(originalName.parent.pos, originalName, program, [file], cancellationToken!), entry => // TODO: GH#18217
|
||||
(entry.kind !== FindAllReferences.EntryKind.Span && rangeContainsRange(constructor, entry.node) && isIdentifier(entry.node) && isWriteAccess(entry.node)) ? entry.node : undefined);
|
||||
|
||||
forEach(referenceEntries, entry => {
|
||||
const parent = entry.parent;
|
||||
const accessorName = createIdentifier(fieldName.text);
|
||||
const node = isBinaryExpression(parent)
|
||||
? updateBinary(parent, accessorName, parent.right, parent.operatorToken)
|
||||
: isPropertyAccessExpression(parent)
|
||||
? updatePropertyAccess(parent, parent.expression, accessorName)
|
||||
: Debug.fail("Unexpected write access token");
|
||||
changeTracker.replaceNode(file, parent, node);
|
||||
constructor.body.forEachChild(function recur(node) {
|
||||
if (isElementAccessExpression(node) &&
|
||||
node.expression.kind === SyntaxKind.ThisKeyword &&
|
||||
isStringLiteral(node.argumentExpression) &&
|
||||
node.argumentExpression.text === originalName &&
|
||||
isWriteAccess(node)) {
|
||||
changeTracker.replaceNode(file, node.argumentExpression, createStringLiteral(fieldName));
|
||||
}
|
||||
if (isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword && node.name.text === originalName && isWriteAccess(node)) {
|
||||
changeTracker.replaceNode(file, node.name, createIdentifier(fieldName));
|
||||
}
|
||||
if (!isFunctionLike(node) && !isClassLike(node)) {
|
||||
node.forEachChild(recur);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* @internal */
|
||||
namespace ts.Rename {
|
||||
export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number): RenameInfo {
|
||||
export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number, options?: RenameInfoOptions): RenameInfo {
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
const renameInfo = node && nodeIsEligibleForRename(node)
|
||||
? getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile()))
|
||||
? getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile()), options)
|
||||
: undefined;
|
||||
return renameInfo || getRenameInfoError(Diagnostics.You_cannot_rename_this_element);
|
||||
}
|
||||
|
||||
function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean): RenameInfo | undefined {
|
||||
function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean, options?: RenameInfoOptions): RenameInfo | undefined {
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
if (!symbol) return;
|
||||
// Only allow a symbol to be renamed if it actually has at least one declaration.
|
||||
@@ -26,7 +26,7 @@ namespace ts.Rename {
|
||||
}
|
||||
|
||||
if (isStringLiteralLike(node) && tryGetImportFromModuleSpecifier(node)) {
|
||||
return getRenameInfoForModule(node, sourceFile, symbol);
|
||||
return options && options.allowRenameOfImportPath ? getRenameInfoForModule(node, sourceFile, symbol) : undefined;
|
||||
}
|
||||
|
||||
const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node);
|
||||
@@ -45,7 +45,7 @@ namespace ts.Rename {
|
||||
|
||||
const moduleSourceFile = find(moduleSymbol.declarations, isSourceFile);
|
||||
if (!moduleSourceFile) return undefined;
|
||||
const withoutIndex = node.text.endsWith("/index") || node.text.endsWith("/index.js") ? undefined : tryRemoveSuffix(removeFileExtension(moduleSourceFile.fileName), "/index");
|
||||
const withoutIndex = endsWith(node.text, "/index") || endsWith(node.text, "/index.js") ? undefined : tryRemoveSuffix(removeFileExtension(moduleSourceFile.fileName), "/index");
|
||||
const name = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex;
|
||||
const kind = withoutIndex === undefined ? ScriptElementKind.moduleElement : ScriptElementKind.directory;
|
||||
const indexAfterLastSlash = node.text.lastIndexOf("/") + 1;
|
||||
|
||||
+103
-91
@@ -17,9 +17,9 @@ namespace ts {
|
||||
public end: number;
|
||||
public flags: NodeFlags;
|
||||
public parent: Node;
|
||||
public symbol: Symbol;
|
||||
public jsDoc: JSDoc[];
|
||||
public original: Node;
|
||||
public symbol!: Symbol; // Actually optional, but it was too annoying to access `node.symbol!` everywhere since in many cases we know it must be defined
|
||||
public jsDoc?: JSDoc[];
|
||||
public original?: Node;
|
||||
public transformFlags: TransformFlags;
|
||||
private _children: Node[] | undefined;
|
||||
|
||||
@@ -196,14 +196,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
class TokenOrIdentifierObject implements Node {
|
||||
public kind: SyntaxKind;
|
||||
public kind!: SyntaxKind;
|
||||
public pos: number;
|
||||
public end: number;
|
||||
public flags: NodeFlags;
|
||||
public parent: Node;
|
||||
public symbol: Symbol;
|
||||
public jsDocComments: JSDoc[];
|
||||
public transformFlags: TransformFlags;
|
||||
public symbol!: Symbol;
|
||||
public jsDocComments?: JSDoc[];
|
||||
public transformFlags!: TransformFlags;
|
||||
|
||||
constructor(pos: number, end: number) {
|
||||
// Set properties in same order as NodeObject
|
||||
@@ -280,8 +280,8 @@ namespace ts {
|
||||
class SymbolObject implements Symbol {
|
||||
flags: SymbolFlags;
|
||||
escapedName: __String;
|
||||
declarations: Declaration[];
|
||||
valueDeclaration: Declaration;
|
||||
declarations!: Declaration[];
|
||||
valueDeclaration!: Declaration;
|
||||
|
||||
// Undefined is used to indicate the value has not been computed. If, after computing, the
|
||||
// symbol has no doc comment, then the empty array will be returned.
|
||||
@@ -334,7 +334,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
class TokenObject<TKind extends SyntaxKind> extends TokenOrIdentifierObject implements Token<TKind> {
|
||||
public symbol: Symbol;
|
||||
public symbol!: Symbol;
|
||||
public kind: TKind;
|
||||
|
||||
constructor(kind: TKind, pos: number, end: number) {
|
||||
@@ -344,10 +344,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
class IdentifierObject extends TokenOrIdentifierObject implements Identifier {
|
||||
public kind: SyntaxKind.Identifier;
|
||||
public escapedText: __String;
|
||||
public symbol: Symbol;
|
||||
public autoGenerateFlags: GeneratedIdentifierFlags;
|
||||
public kind!: SyntaxKind.Identifier;
|
||||
public escapedText!: __String;
|
||||
public symbol!: Symbol;
|
||||
public autoGenerateFlags!: GeneratedIdentifierFlags;
|
||||
_primaryExpressionBrand: any;
|
||||
_memberExpressionBrand: any;
|
||||
_leftHandSideExpressionBrand: any;
|
||||
@@ -355,7 +355,7 @@ namespace ts {
|
||||
_unaryExpressionBrand: any;
|
||||
_expressionBrand: any;
|
||||
_declarationBrand: any;
|
||||
/*@internal*/typeArguments: NodeArray<TypeNode>;
|
||||
/*@internal*/typeArguments!: NodeArray<TypeNode>;
|
||||
constructor(_kind: SyntaxKind.Identifier, pos: number, end: number) {
|
||||
super(pos, end);
|
||||
}
|
||||
@@ -370,8 +370,8 @@ namespace ts {
|
||||
checker: TypeChecker;
|
||||
flags: TypeFlags;
|
||||
objectFlags?: ObjectFlags;
|
||||
id: number;
|
||||
symbol: Symbol;
|
||||
id!: number;
|
||||
symbol!: Symbol;
|
||||
constructor(checker: TypeChecker, flags: TypeFlags) {
|
||||
this.checker = checker;
|
||||
this.flags = flags;
|
||||
@@ -447,16 +447,16 @@ namespace ts {
|
||||
|
||||
class SignatureObject implements Signature {
|
||||
checker: TypeChecker;
|
||||
declaration: SignatureDeclaration;
|
||||
declaration!: SignatureDeclaration;
|
||||
typeParameters?: TypeParameter[];
|
||||
parameters: Symbol[];
|
||||
thisParameter: Symbol;
|
||||
resolvedReturnType: Type;
|
||||
parameters!: Symbol[];
|
||||
thisParameter!: Symbol;
|
||||
resolvedReturnType!: Type;
|
||||
resolvedTypePredicate: TypePredicate | undefined;
|
||||
minTypeArgumentCount: number;
|
||||
minArgumentCount: number;
|
||||
hasRestParameter: boolean;
|
||||
hasLiteralTypes: boolean;
|
||||
minTypeArgumentCount!: number;
|
||||
minArgumentCount!: number;
|
||||
hasRestParameter!: boolean;
|
||||
hasLiteralTypes!: boolean;
|
||||
|
||||
// Undefined is used to indicate the value has not been computed. If, after computing, the
|
||||
// symbol has no doc comment, then the empty array will be returned.
|
||||
@@ -536,55 +536,55 @@ namespace ts {
|
||||
}
|
||||
|
||||
class SourceFileObject extends NodeObject implements SourceFile {
|
||||
public kind: SyntaxKind.SourceFile;
|
||||
public kind!: SyntaxKind.SourceFile;
|
||||
public _declarationBrand: any;
|
||||
public fileName: string;
|
||||
public path: Path;
|
||||
public resolvedPath: Path;
|
||||
public originalFileName: string;
|
||||
public text: string;
|
||||
public scriptSnapshot: IScriptSnapshot;
|
||||
public lineMap: ReadonlyArray<number>;
|
||||
public fileName!: string;
|
||||
public path!: Path;
|
||||
public resolvedPath!: Path;
|
||||
public originalFileName!: string;
|
||||
public text!: string;
|
||||
public scriptSnapshot!: IScriptSnapshot;
|
||||
public lineMap!: ReadonlyArray<number>;
|
||||
|
||||
public statements: NodeArray<Statement>;
|
||||
public endOfFileToken: Token<SyntaxKind.EndOfFileToken>;
|
||||
public statements!: NodeArray<Statement>;
|
||||
public endOfFileToken!: Token<SyntaxKind.EndOfFileToken>;
|
||||
|
||||
public amdDependencies: { name: string; path: string }[];
|
||||
public moduleName: string;
|
||||
public referencedFiles: FileReference[];
|
||||
public typeReferenceDirectives: FileReference[];
|
||||
public libReferenceDirectives: FileReference[];
|
||||
public amdDependencies!: { name: string; path: string }[];
|
||||
public moduleName!: string;
|
||||
public referencedFiles!: FileReference[];
|
||||
public typeReferenceDirectives!: FileReference[];
|
||||
public libReferenceDirectives!: FileReference[];
|
||||
|
||||
public syntacticDiagnostics: DiagnosticWithLocation[];
|
||||
public parseDiagnostics: DiagnosticWithLocation[];
|
||||
public bindDiagnostics: DiagnosticWithLocation[];
|
||||
public syntacticDiagnostics!: DiagnosticWithLocation[];
|
||||
public parseDiagnostics!: DiagnosticWithLocation[];
|
||||
public bindDiagnostics!: DiagnosticWithLocation[];
|
||||
public bindSuggestionDiagnostics?: DiagnosticWithLocation[];
|
||||
|
||||
public isDeclarationFile: boolean;
|
||||
public isDefaultLib: boolean;
|
||||
public hasNoDefaultLib: boolean;
|
||||
public externalModuleIndicator: Node; // The first node that causes this file to be an external module
|
||||
public commonJsModuleIndicator: Node; // The first node that causes this file to be a CommonJS module
|
||||
public nodeCount: number;
|
||||
public identifierCount: number;
|
||||
public symbolCount: number;
|
||||
public version: string;
|
||||
public scriptKind: ScriptKind;
|
||||
public languageVersion: ScriptTarget;
|
||||
public languageVariant: LanguageVariant;
|
||||
public identifiers: Map<string>;
|
||||
public nameTable: UnderscoreEscapedMap<number>;
|
||||
public resolvedModules: Map<ResolvedModuleFull>;
|
||||
public resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
|
||||
public imports: ReadonlyArray<StringLiteralLike>;
|
||||
public moduleAugmentations: StringLiteral[];
|
||||
private namedDeclarations: Map<Declaration[]>;
|
||||
public ambientModuleNames: string[];
|
||||
public isDeclarationFile!: boolean;
|
||||
public isDefaultLib!: boolean;
|
||||
public hasNoDefaultLib!: boolean;
|
||||
public externalModuleIndicator!: Node; // The first node that causes this file to be an external module
|
||||
public commonJsModuleIndicator!: Node; // The first node that causes this file to be a CommonJS module
|
||||
public nodeCount!: number;
|
||||
public identifierCount!: number;
|
||||
public symbolCount!: number;
|
||||
public version!: string;
|
||||
public scriptKind!: ScriptKind;
|
||||
public languageVersion!: ScriptTarget;
|
||||
public languageVariant!: LanguageVariant;
|
||||
public identifiers!: Map<string>;
|
||||
public nameTable: UnderscoreEscapedMap<number> | undefined;
|
||||
public resolvedModules: Map<ResolvedModuleFull> | undefined;
|
||||
public resolvedTypeReferenceDirectiveNames!: Map<ResolvedTypeReferenceDirective>;
|
||||
public imports!: ReadonlyArray<StringLiteralLike>;
|
||||
public moduleAugmentations!: StringLiteral[];
|
||||
private namedDeclarations: Map<Declaration[]> | undefined;
|
||||
public ambientModuleNames!: string[];
|
||||
public checkJsDirective: CheckJsDirective | undefined;
|
||||
public possiblyContainDynamicImport: boolean;
|
||||
public pragmas: PragmaMap;
|
||||
public localJsxFactory: EntityName;
|
||||
public localJsxNamespace: __String;
|
||||
public possiblyContainDynamicImport?: boolean;
|
||||
public pragmas!: PragmaMap;
|
||||
public localJsxFactory: EntityName | undefined;
|
||||
public localJsxNamespace: __String | undefined;
|
||||
|
||||
constructor(kind: SyntaxKind, pos: number, end: number) {
|
||||
super(kind, pos, end);
|
||||
@@ -602,8 +602,8 @@ namespace ts {
|
||||
return getLineStarts(this);
|
||||
}
|
||||
|
||||
public getPositionOfLineAndCharacter(line: number, character: number): number {
|
||||
return getPositionOfLineAndCharacter(this, line, character);
|
||||
public getPositionOfLineAndCharacter(line: number, character: number, allowEdits?: true): number {
|
||||
return computePositionOfLineAndCharacter(getLineStarts(this), line, character, this.text, allowEdits);
|
||||
}
|
||||
|
||||
public getLineEndOfPosition(pos: number): number {
|
||||
@@ -774,7 +774,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
class SourceMapSourceObject implements SourceMapSource {
|
||||
lineMap: number[];
|
||||
lineMap!: number[];
|
||||
constructor(public fileName: string, public text: string, public skipTrivia?: (pos: number) => number) { }
|
||||
|
||||
public getLineAndCharacterOfPosition(pos: number): LineAndCharacter {
|
||||
@@ -955,10 +955,10 @@ namespace ts {
|
||||
class SyntaxTreeCache {
|
||||
// For our syntactic only features, we also keep a cache of the syntax tree for the
|
||||
// currently edited file.
|
||||
private currentFileName: string;
|
||||
private currentFileVersion: string;
|
||||
private currentFileScriptSnapshot: IScriptSnapshot;
|
||||
private currentSourceFile: SourceFile;
|
||||
private currentFileName: string | undefined;
|
||||
private currentFileVersion: string | undefined;
|
||||
private currentFileScriptSnapshot: IScriptSnapshot | undefined;
|
||||
private currentSourceFile: SourceFile | undefined;
|
||||
|
||||
constructor(private host: LanguageServiceHost) {
|
||||
}
|
||||
@@ -980,8 +980,8 @@ namespace ts {
|
||||
}
|
||||
else if (this.currentFileVersion !== version) {
|
||||
// This is the same file, just a newer version. Incrementally parse the file.
|
||||
const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot);
|
||||
sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange);
|
||||
const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot!);
|
||||
sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile!, scriptSnapshot, version, editRange);
|
||||
}
|
||||
|
||||
if (sourceFile) {
|
||||
@@ -992,7 +992,7 @@ namespace ts {
|
||||
this.currentSourceFile = sourceFile;
|
||||
}
|
||||
|
||||
return this.currentSourceFile;
|
||||
return this.currentSourceFile!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1139,7 +1139,16 @@ namespace ts {
|
||||
const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host);
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
|
||||
const sourceMapper = getSourceMapper(useCaseSensitiveFileNames, currentDirectory, log, host, () => program);
|
||||
const sourceMapper = getSourceMapper({
|
||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
getProgram,
|
||||
fileExists: host.fileExists && (f => host.fileExists!(f)),
|
||||
readFile: host.readFile && ((f, encoding) => host.readFile!(f, encoding)),
|
||||
getDocumentPositionMapper: host.getDocumentPositionMapper && ((generatedFileName, sourceFileName) => host.getDocumentPositionMapper!(generatedFileName, sourceFileName)),
|
||||
getSourceFileLike: host.getSourceFileLike && (f => host.getSourceFileLike!(f)),
|
||||
log
|
||||
});
|
||||
|
||||
function getValidSourceFile(fileName: string): SourceFile {
|
||||
const sourceFile = program.getSourceFile(fileName);
|
||||
@@ -1203,15 +1212,7 @@ namespace ts {
|
||||
writeFile: noop,
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
fileExists,
|
||||
readFile(fileName) {
|
||||
// stub missing host functionality
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const entry = hostCache && hostCache.getEntryByPath(path);
|
||||
if (entry) {
|
||||
return isString(entry) ? undefined : getSnapshotText(entry.scriptSnapshot);
|
||||
}
|
||||
return host.readFile && host.readFile(fileName);
|
||||
},
|
||||
readFile,
|
||||
realpath: host.realpath && (path => host.realpath!(path)),
|
||||
directoryExists: directoryName => {
|
||||
return directoryProbablyExists(directoryName, host);
|
||||
@@ -1272,6 +1273,16 @@ namespace ts {
|
||||
(!!host.fileExists && host.fileExists(fileName));
|
||||
}
|
||||
|
||||
function readFile(fileName: string) {
|
||||
// stub missing host functionality
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const entry = hostCache && hostCache.getEntryByPath(path);
|
||||
if (entry) {
|
||||
return isString(entry) ? undefined : getSnapshotText(entry.scriptSnapshot);
|
||||
}
|
||||
return host.readFile && host.readFile(fileName);
|
||||
}
|
||||
|
||||
// Release any files we have acquired in the old program but are
|
||||
// not part of the new program.
|
||||
function onReleaseOldSourceFile(oldSourceFile: SourceFile, oldOptions: CompilerOptions) {
|
||||
@@ -1538,7 +1549,7 @@ namespace ts {
|
||||
return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch);
|
||||
}
|
||||
|
||||
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] | undefined {
|
||||
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] | undefined {
|
||||
synchronizeHostData();
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
@@ -1548,7 +1559,8 @@ namespace ts {
|
||||
({ fileName: sourceFile.fileName, textSpan: createTextSpanFromNode(node.tagName, sourceFile) }));
|
||||
}
|
||||
else {
|
||||
return getReferencesWorker(node, position, { findInStrings, findInComments, isForRename: true }, FindAllReferences.toRenameLocation);
|
||||
return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, isForRename: true },
|
||||
(entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2051,9 +2063,9 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getRenameInfo(fileName: string, position: number): RenameInfo {
|
||||
function getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo {
|
||||
synchronizeHostData();
|
||||
return Rename.getRenameInfo(program, getValidSourceFile(fileName), position);
|
||||
return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, options);
|
||||
}
|
||||
|
||||
function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings): RefactorContext {
|
||||
|
||||
+18
-12
@@ -164,13 +164,13 @@ namespace ts {
|
||||
* Returns a JSON-encoded value of the type:
|
||||
* { canRename: boolean, localizedErrorMessage: string, displayName: string, fullDisplayName: string, kind: string, kindModifiers: string, triggerSpan: { start; length } }
|
||||
*/
|
||||
getRenameInfo(fileName: string, position: number): string;
|
||||
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
* { fileName: string, textSpan: { start: number, length: number } }[]
|
||||
*/
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): string;
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
@@ -331,9 +331,9 @@ namespace ts {
|
||||
private loggingEnabled = false;
|
||||
private tracingEnabled = false;
|
||||
|
||||
public resolveModuleNames: (moduleName: string[], containingFile: string) => (ResolvedModuleFull | undefined)[];
|
||||
public resolveTypeReferenceDirectives: (typeDirectiveNames: string[], containingFile: string) => (ResolvedTypeReferenceDirective | undefined)[];
|
||||
public directoryExists: (directoryName: string) => boolean;
|
||||
public resolveModuleNames: ((moduleName: string[], containingFile: string) => (ResolvedModuleFull | undefined)[]) | undefined;
|
||||
public resolveTypeReferenceDirectives: ((typeDirectiveNames: string[], containingFile: string) => (ResolvedTypeReferenceDirective | undefined)[]) | undefined;
|
||||
public directoryExists: ((directoryName: string) => boolean) | undefined;
|
||||
|
||||
constructor(private shimHost: LanguageServiceShimHost) {
|
||||
// if shimHost is a COM object then property check will become method call with no arguments.
|
||||
@@ -490,13 +490,19 @@ namespace ts {
|
||||
public useCaseSensitiveFileNames: boolean;
|
||||
|
||||
constructor(private shimHost: CoreServicesShimHost) {
|
||||
this.useCaseSensitiveFileNames = this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false;
|
||||
this.useCaseSensitiveFileNames = this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false;
|
||||
if ("directoryExists" in this.shimHost) {
|
||||
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
|
||||
}
|
||||
else {
|
||||
this.directoryExists = undefined!; // TODO: GH#18217
|
||||
}
|
||||
if ("realpath" in this.shimHost) {
|
||||
this.realpath = path => this.shimHost.realpath!(path); // TODO: GH#18217
|
||||
}
|
||||
else {
|
||||
this.realpath = undefined!; // TODO: GH#18217
|
||||
}
|
||||
}
|
||||
|
||||
public readDirectory(rootDir: string, extensions: ReadonlyArray<string>, exclude: ReadonlyArray<string>, include: ReadonlyArray<string>, depth?: number): string[] {
|
||||
@@ -825,17 +831,17 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
public getRenameInfo(fileName: string, position: number): string {
|
||||
public getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string {
|
||||
return this.forwardJSONCall(
|
||||
`getRenameInfo('${fileName}', ${position})`,
|
||||
() => this.languageService.getRenameInfo(fileName, position)
|
||||
() => this.languageService.getRenameInfo(fileName, position, options)
|
||||
);
|
||||
}
|
||||
|
||||
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): string {
|
||||
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string {
|
||||
return this.forwardJSONCall(
|
||||
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments})`,
|
||||
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments)
|
||||
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments}, ${providePrefixAndSuffixTextForRename})`,
|
||||
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1182,7 +1188,7 @@ namespace ts {
|
||||
|
||||
export class TypeScriptServicesFactory implements ShimFactory {
|
||||
private _shims: Shim[] = [];
|
||||
private documentRegistry: DocumentRegistry;
|
||||
private documentRegistry: DocumentRegistry | undefined;
|
||||
|
||||
/*
|
||||
* Returns script API version.
|
||||
|
||||
+134
-103
@@ -9,151 +9,182 @@ namespace ts {
|
||||
clearCache(): void;
|
||||
}
|
||||
|
||||
export function getSourceMapper(
|
||||
useCaseSensitiveFileNames: boolean,
|
||||
currentDirectory: string,
|
||||
log: (message: string) => void,
|
||||
host: LanguageServiceHost,
|
||||
getProgram: () => Program,
|
||||
): SourceMapper {
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
let sourcemappedFileCache: SourceFileLikeCache;
|
||||
export interface SourceMapperHost {
|
||||
useCaseSensitiveFileNames(): boolean;
|
||||
getCurrentDirectory(): string;
|
||||
getProgram(): Program | undefined;
|
||||
fileExists?(path: string): boolean;
|
||||
readFile?(path: string, encoding?: string): string | undefined;
|
||||
getSourceFileLike?(fileName: string): SourceFileLike | undefined;
|
||||
getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined;
|
||||
log(s: string): void;
|
||||
}
|
||||
|
||||
export function getSourceMapper(host: SourceMapperHost): SourceMapper {
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const sourceFileLike = createMap<SourceFileLike | false>();
|
||||
const documentPositionMappers = createMap<DocumentPositionMapper>();
|
||||
return { tryGetSourcePosition, tryGetGeneratedPosition, toLineColumnOffset, clearCache };
|
||||
|
||||
function toPath(fileName: string) {
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function scanForSourcemapURL(fileName: string) {
|
||||
const mappedFile = sourcemappedFileCache.get(toPath(fileName));
|
||||
if (!mappedFile) {
|
||||
return;
|
||||
}
|
||||
function getDocumentPositionMapper(generatedFileName: string, sourceFileName?: string) {
|
||||
const path = toPath(generatedFileName);
|
||||
const value = documentPositionMappers.get(path);
|
||||
if (value) return value;
|
||||
|
||||
return tryGetSourceMappingURL(mappedFile.text, getLineStarts(mappedFile));
|
||||
}
|
||||
|
||||
function convertDocumentToSourceMapper(file: { sourceMapper?: DocumentPositionMapper }, contents: string, mapFileName: string) {
|
||||
const map = tryParseRawSourceMap(contents);
|
||||
if (!map || !map.sources || !map.file || !map.mappings) {
|
||||
// obviously invalid map
|
||||
return file.sourceMapper = identitySourceMapConsumer;
|
||||
let mapper: DocumentPositionMapper | undefined;
|
||||
if (host.getDocumentPositionMapper) {
|
||||
mapper = host.getDocumentPositionMapper(generatedFileName, sourceFileName);
|
||||
}
|
||||
const program = getProgram();
|
||||
return file.sourceMapper = createDocumentPositionMapper({
|
||||
getSourceFileLike: s => {
|
||||
// Lookup file in program, if provided
|
||||
const file = program && program.getSourceFileByPath(s);
|
||||
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
|
||||
if (file === undefined || file.resolvedPath !== s) {
|
||||
// Otherwise check the cache (which may hit disk)
|
||||
return sourcemappedFileCache.get(s);
|
||||
}
|
||||
return file;
|
||||
},
|
||||
getCanonicalFileName,
|
||||
log,
|
||||
}, map, mapFileName);
|
||||
}
|
||||
|
||||
function getSourceMapper(fileName: string, file: SourceFileLike): DocumentPositionMapper {
|
||||
if (!host.readFile || !host.fileExists) {
|
||||
return file.sourceMapper = identitySourceMapConsumer;
|
||||
else if (host.readFile) {
|
||||
const file = getSourceFileLike(generatedFileName);
|
||||
mapper = file && ts.getDocumentPositionMapper(
|
||||
{ getSourceFileLike, getCanonicalFileName, log: s => host.log(s) },
|
||||
generatedFileName,
|
||||
getLineInfo(file.text, getLineStarts(file)),
|
||||
f => !host.fileExists || host.fileExists(f) ? host.readFile!(f) : undefined
|
||||
);
|
||||
}
|
||||
if (file.sourceMapper) {
|
||||
return file.sourceMapper;
|
||||
}
|
||||
let mapFileName = scanForSourcemapURL(fileName);
|
||||
if (mapFileName) {
|
||||
const match = base64UrlRegExp.exec(mapFileName);
|
||||
if (match) {
|
||||
if (match[1]) {
|
||||
const base64Object = match[1];
|
||||
return convertDocumentToSourceMapper(file, base64decode(sys, base64Object), fileName);
|
||||
}
|
||||
// Not a data URL we can parse, skip it
|
||||
mapFileName = undefined;
|
||||
}
|
||||
}
|
||||
const possibleMapLocations: string[] = [];
|
||||
if (mapFileName) {
|
||||
possibleMapLocations.push(mapFileName);
|
||||
}
|
||||
possibleMapLocations.push(fileName + ".map");
|
||||
for (const location of possibleMapLocations) {
|
||||
const mapPath = ts.toPath(location, getDirectoryPath(fileName), getCanonicalFileName);
|
||||
if (host.fileExists(mapPath)) {
|
||||
return convertDocumentToSourceMapper(file, host.readFile(mapPath)!, mapPath); // TODO: GH#18217
|
||||
}
|
||||
}
|
||||
return file.sourceMapper = identitySourceMapConsumer;
|
||||
documentPositionMappers.set(path, mapper || identitySourceMapConsumer);
|
||||
return mapper || identitySourceMapConsumer;
|
||||
}
|
||||
|
||||
function tryGetSourcePosition(info: DocumentPosition): DocumentPosition | undefined {
|
||||
if (!isDeclarationFileName(info.fileName)) return undefined;
|
||||
|
||||
const file = getFile(info.fileName);
|
||||
const file = getSourceFile(info.fileName);
|
||||
if (!file) return undefined;
|
||||
const newLoc = getSourceMapper(info.fileName, file).getSourcePosition(info);
|
||||
return newLoc === info ? undefined : tryGetSourcePosition(newLoc) || newLoc;
|
||||
|
||||
const newLoc = getDocumentPositionMapper(info.fileName).getSourcePosition(info);
|
||||
return !newLoc || newLoc === info ? undefined : tryGetSourcePosition(newLoc) || newLoc;
|
||||
}
|
||||
|
||||
function tryGetGeneratedPosition(info: DocumentPosition): DocumentPosition | undefined {
|
||||
const program = getProgram();
|
||||
if (isDeclarationFileName(info.fileName)) return undefined;
|
||||
|
||||
const sourceFile = getSourceFile(info.fileName);
|
||||
if (!sourceFile) return undefined;
|
||||
|
||||
const program = host.getProgram()!;
|
||||
const options = program.getCompilerOptions();
|
||||
const outPath = options.outFile || options.out;
|
||||
|
||||
const declarationPath = outPath ?
|
||||
removeFileExtension(outPath) + Extension.Dts :
|
||||
getDeclarationEmitOutputFilePathWorker(info.fileName, program.getCompilerOptions(), currentDirectory, program.getCommonSourceDirectory(), getCanonicalFileName);
|
||||
if (declarationPath === undefined) return undefined;
|
||||
const declarationFile = getFile(declarationPath);
|
||||
if (!declarationFile) return undefined;
|
||||
const newLoc = getSourceMapper(declarationPath, declarationFile).getGeneratedPosition(info);
|
||||
|
||||
const newLoc = getDocumentPositionMapper(declarationPath, info.fileName).getGeneratedPosition(info);
|
||||
return newLoc === info ? undefined : newLoc;
|
||||
}
|
||||
|
||||
function getFile(fileName: string): SourceFileLike | undefined {
|
||||
function getSourceFile(fileName: string) {
|
||||
const program = host.getProgram();
|
||||
if (!program) return undefined;
|
||||
|
||||
const path = toPath(fileName);
|
||||
const file = getProgram().getSourceFileByPath(path);
|
||||
if (file && file.resolvedPath === path) {
|
||||
return file;
|
||||
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
|
||||
const file = program.getSourceFileByPath(path);
|
||||
return file && file.resolvedPath === path ? file : undefined;
|
||||
}
|
||||
|
||||
function getOrCreateSourceFileLike(fileName: string): SourceFileLike | undefined {
|
||||
const path = toPath(fileName);
|
||||
const fileFromCache = sourceFileLike.get(path);
|
||||
if (fileFromCache !== undefined) return fileFromCache ? fileFromCache : undefined;
|
||||
|
||||
if (!host.readFile || host.fileExists && !host.fileExists(path)) {
|
||||
sourceFileLike.set(path, false);
|
||||
return undefined;
|
||||
}
|
||||
return sourcemappedFileCache.get(path);
|
||||
|
||||
// And failing that, check the disk
|
||||
const text = host.readFile(path);
|
||||
const file = text ? createSourceFileLike(text) : false;
|
||||
sourceFileLike.set(path, file);
|
||||
return file ? file : undefined;
|
||||
}
|
||||
|
||||
// This can be called from source mapper in either source program or program that includes generated file
|
||||
function getSourceFileLike(fileName: string) {
|
||||
return !host.getSourceFileLike ?
|
||||
getSourceFile(fileName) || getOrCreateSourceFileLike(fileName) :
|
||||
host.getSourceFileLike(fileName);
|
||||
}
|
||||
|
||||
function toLineColumnOffset(fileName: string, position: number): LineAndCharacter {
|
||||
const file = getFile(fileName)!; // TODO: GH#18217
|
||||
const file = getSourceFileLike(fileName)!; // TODO: GH#18217
|
||||
return file.getLineAndCharacterOfPosition(position);
|
||||
}
|
||||
|
||||
function clearCache(): void {
|
||||
sourcemappedFileCache = createSourceFileLikeCache(host);
|
||||
sourceFileLike.clear();
|
||||
documentPositionMappers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
interface SourceFileLikeCache {
|
||||
get(path: Path): SourceFileLike | undefined;
|
||||
/**
|
||||
* string | undefined to contents of map file to create DocumentPositionMapper from it
|
||||
* DocumentPositionMapper | false to give back cached DocumentPositionMapper
|
||||
*/
|
||||
export type ReadMapFile = (mapFileName: string, mapFileNameFromDts: string | undefined) => string | undefined | DocumentPositionMapper | false;
|
||||
|
||||
export function getDocumentPositionMapper(
|
||||
host: DocumentPositionMapperHost,
|
||||
generatedFileName: string,
|
||||
generatedFileLineInfo: LineInfo,
|
||||
readMapFile: ReadMapFile) {
|
||||
let mapFileName = tryGetSourceMappingURL(generatedFileLineInfo);
|
||||
if (mapFileName) {
|
||||
const match = base64UrlRegExp.exec(mapFileName);
|
||||
if (match) {
|
||||
if (match[1]) {
|
||||
const base64Object = match[1];
|
||||
return convertDocumentToSourceMapper(host, base64decode(sys, base64Object), generatedFileName);
|
||||
}
|
||||
// Not a data URL we can parse, skip it
|
||||
mapFileName = undefined;
|
||||
}
|
||||
}
|
||||
const possibleMapLocations: string[] = [];
|
||||
if (mapFileName) {
|
||||
possibleMapLocations.push(mapFileName);
|
||||
}
|
||||
possibleMapLocations.push(generatedFileName + ".map");
|
||||
const originalMapFileName = mapFileName && getNormalizedAbsolutePath(mapFileName, getDirectoryPath(generatedFileName));
|
||||
for (const location of possibleMapLocations) {
|
||||
const mapFileName = getNormalizedAbsolutePath(location, getDirectoryPath(generatedFileName));
|
||||
const mapFileContents = readMapFile(mapFileName, originalMapFileName);
|
||||
if (isString(mapFileContents)) {
|
||||
return convertDocumentToSourceMapper(host, mapFileContents, mapFileName);
|
||||
}
|
||||
if (mapFileContents !== undefined) {
|
||||
return mapFileContents || undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function createSourceFileLikeCache(host: { readFile?: (path: string) => string | undefined, fileExists?: (path: string) => boolean }): SourceFileLikeCache {
|
||||
const cached = createMap<SourceFileLike>();
|
||||
function convertDocumentToSourceMapper(host: DocumentPositionMapperHost, contents: string, mapFileName: string) {
|
||||
const map = tryParseRawSourceMap(contents);
|
||||
if (!map || !map.sources || !map.file || !map.mappings) {
|
||||
// obviously invalid map
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return createDocumentPositionMapper(host, map, mapFileName);
|
||||
}
|
||||
|
||||
function createSourceFileLike(text: string, lineMap?: SourceFileLike["lineMap"]): SourceFileLike {
|
||||
return {
|
||||
get(path: Path) {
|
||||
if (cached.has(path)) {
|
||||
return cached.get(path);
|
||||
}
|
||||
if (!host.fileExists || !host.readFile || !host.fileExists(path)) return;
|
||||
// And failing that, check the disk
|
||||
const text = host.readFile(path)!; // TODO: GH#18217
|
||||
const file = {
|
||||
text,
|
||||
lineMap: undefined,
|
||||
getLineAndCharacterOfPosition(pos: number) {
|
||||
return computeLineAndCharacterOfPosition(getLineStarts(this), pos);
|
||||
}
|
||||
} as SourceFileLike;
|
||||
cached.set(path, file);
|
||||
return file;
|
||||
text,
|
||||
lineMap,
|
||||
getLineAndCharacterOfPosition(pos: number) {
|
||||
return computeLineAndCharacterOfPosition(getLineStarts(this), pos);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -281,33 +281,26 @@ namespace ts.Completions.StringCompletions {
|
||||
* Takes a script path and returns paths for all potential folders that could be merged with its
|
||||
* containing folder via the "rootDirs" compiler option
|
||||
*/
|
||||
function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptPath: string, ignoreCase: boolean): string[] {
|
||||
function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptDirectory: string, ignoreCase: boolean): ReadonlyArray<string> {
|
||||
// Make all paths absolute/normalized if they are not already
|
||||
rootDirs = rootDirs.map(rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory)));
|
||||
|
||||
// Determine the path to the directory containing the script relative to the root directory it is contained within
|
||||
const relativeDirectory = firstDefined(rootDirs, rootDirectory =>
|
||||
containsPath(rootDirectory, scriptPath, basePath, ignoreCase) ? scriptPath.substr(rootDirectory.length) : undefined)!; // TODO: GH#18217
|
||||
containsPath(rootDirectory, scriptDirectory, basePath, ignoreCase) ? scriptDirectory.substr(rootDirectory.length) : undefined)!; // TODO: GH#18217
|
||||
|
||||
// Now find a path for each potential directory that is to be merged with the one containing the script
|
||||
return deduplicate<string>(
|
||||
rootDirs.map(rootDirectory => combinePaths(rootDirectory, relativeDirectory)),
|
||||
[...rootDirs.map(rootDirectory => combinePaths(rootDirectory, relativeDirectory)), scriptDirectory],
|
||||
equateStringsCaseSensitive,
|
||||
compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensionOptions: ExtensionOptions, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): NameAndKind[] {
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptDirectory: string, extensionOptions: ExtensionOptions, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude: string): ReadonlyArray<NameAndKind> {
|
||||
const basePath = compilerOptions.project || host.getCurrentDirectory();
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
|
||||
|
||||
const result: NameAndKind[] = [];
|
||||
|
||||
for (const baseDirectory of baseDirectories) {
|
||||
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensionOptions, host, exclude, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptDirectory, ignoreCase);
|
||||
return flatMap(baseDirectories, baseDirectory => getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensionOptions, host, exclude));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
const visitedNestedConvertibleFunctions = createMap<true>();
|
||||
|
||||
export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): DiagnosticWithLocation[] {
|
||||
program.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
const diags: DiagnosticWithLocation[] = [];
|
||||
@@ -13,6 +15,7 @@ namespace ts {
|
||||
|
||||
const isJsFile = isSourceFileJS(sourceFile);
|
||||
|
||||
visitedNestedConvertibleFunctions.clear();
|
||||
check(sourceFile);
|
||||
|
||||
if (getAllowSyntheticDefaultImports(program.getCompilerOptions())) {
|
||||
@@ -114,17 +117,22 @@ namespace ts {
|
||||
}
|
||||
|
||||
function addConvertToAsyncFunctionDiagnostics(node: FunctionLikeDeclaration, checker: TypeChecker, diags: Push<DiagnosticWithLocation>): void {
|
||||
if (!isAsyncFunction(node) &&
|
||||
node.body &&
|
||||
isBlock(node.body) &&
|
||||
hasReturnStatementWithPromiseHandler(node.body) &&
|
||||
returnsPromise(node, checker)) {
|
||||
// need to check function before checking map so that deeper levels of nested callbacks are checked
|
||||
if (isConvertibleFunction(node, checker) && !visitedNestedConvertibleFunctions.has(getKeyFromNode(node))) {
|
||||
diags.push(createDiagnosticForNode(
|
||||
!node.name && isVariableDeclaration(node.parent) && isIdentifier(node.parent.name) ? node.parent.name : node,
|
||||
Diagnostics.This_may_be_converted_to_an_async_function));
|
||||
}
|
||||
}
|
||||
|
||||
function isConvertibleFunction(node: FunctionLikeDeclaration, checker: TypeChecker) {
|
||||
return !isAsyncFunction(node) &&
|
||||
node.body &&
|
||||
isBlock(node.body) &&
|
||||
hasReturnStatementWithPromiseHandler(node.body) &&
|
||||
returnsPromise(node, checker);
|
||||
}
|
||||
|
||||
function returnsPromise(node: FunctionLikeDeclaration, checker: TypeChecker): boolean {
|
||||
const functionType = checker.getTypeAtLocation(node);
|
||||
const callSignatures = checker.getSignaturesOfType(functionType, SignatureKind.Call);
|
||||
@@ -169,14 +177,20 @@ namespace ts {
|
||||
// should be kept up to date with getTransformationBody in convertToAsyncFunction.ts
|
||||
function isFixablePromiseArgument(arg: Expression): boolean {
|
||||
switch (arg.kind) {
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.Identifier: // identifier includes undefined
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
visitedNestedConvertibleFunctions.set(getKeyFromNode(arg as FunctionLikeDeclaration), true);
|
||||
/* falls through */
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.Identifier: // identifier includes undefined
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyFromNode(exp: FunctionLikeDeclaration) {
|
||||
return `${exp.pos.toString()}:${exp.end.toString()}`;
|
||||
}
|
||||
}
|
||||
|
||||
+22
-27
@@ -228,9 +228,8 @@ namespace ts.textChanges {
|
||||
/** Public for tests only. Other callers should use `ChangeTracker.with`. */
|
||||
constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext) {}
|
||||
|
||||
public deleteRange(sourceFile: SourceFile, range: TextRange) {
|
||||
public deleteRange(sourceFile: SourceFile, range: TextRange): void {
|
||||
this.changes.push({ kind: ChangeKind.Remove, sourceFile, range });
|
||||
return this;
|
||||
}
|
||||
|
||||
delete(sourceFile: SourceFile, node: Node | NodeArray<TypeParameterDeclaration>): void {
|
||||
@@ -241,11 +240,10 @@ namespace ts.textChanges {
|
||||
this.deleteRange(sourceFile, { pos: modifier.getStart(sourceFile), end: skipTrivia(sourceFile.text, modifier.end, /*stopAfterLineBreak*/ true) });
|
||||
}
|
||||
|
||||
public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = {}) {
|
||||
public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = {}): void {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.FullStart);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
|
||||
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
||||
return this;
|
||||
}
|
||||
|
||||
public deleteNodeRangeExcludingEnd(sourceFile: SourceFile, startNode: Node, afterEndNode: Node | undefined, options: ConfigurableStartEnd = {}): void {
|
||||
@@ -254,34 +252,32 @@ namespace ts.textChanges {
|
||||
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
||||
}
|
||||
|
||||
public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: InsertNodeOptions = {}): void {
|
||||
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range, options, node: newNode });
|
||||
return this;
|
||||
}
|
||||
|
||||
public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = useNonAdjustedPositions) {
|
||||
return this.replaceRange(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNode, options);
|
||||
public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = useNonAdjustedPositions): void {
|
||||
this.replaceRange(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNode, options);
|
||||
}
|
||||
|
||||
public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = useNonAdjustedPositions) {
|
||||
public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = useNonAdjustedPositions): void {
|
||||
this.replaceRange(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNode, options);
|
||||
}
|
||||
|
||||
private replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray<Node>, options: ReplaceWithMultipleNodesOptions & ConfigurableStartEnd = {}) {
|
||||
private replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray<Node>, options: ReplaceWithMultipleNodesOptions & ConfigurableStartEnd = {}): void {
|
||||
this.changes.push({ kind: ChangeKind.ReplaceWithMultipleNodes, sourceFile, range, options, nodes: newNodes });
|
||||
return this;
|
||||
}
|
||||
|
||||
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeNodeOptions = useNonAdjustedPositions) {
|
||||
return this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNodes, options);
|
||||
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeNodeOptions = useNonAdjustedPositions): void {
|
||||
this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNodes, options);
|
||||
}
|
||||
|
||||
public replaceNodeWithText(sourceFile: SourceFile, oldNode: Node, text: string): void {
|
||||
this.replaceRangeWithText(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, useNonAdjustedPositions), text);
|
||||
}
|
||||
|
||||
public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray<Node>, options: ReplaceWithMultipleNodesOptions & ConfigurableStartEnd = useNonAdjustedPositions) {
|
||||
return this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNodes, options);
|
||||
public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray<Node>, options: ReplaceWithMultipleNodesOptions & ConfigurableStartEnd = useNonAdjustedPositions): void {
|
||||
this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNodes, options);
|
||||
}
|
||||
|
||||
private nextCommaToken(sourceFile: SourceFile, node: Node): Node | undefined {
|
||||
@@ -289,12 +285,12 @@ namespace ts.textChanges {
|
||||
return next && next.kind === SyntaxKind.CommaToken ? next : undefined;
|
||||
}
|
||||
|
||||
public replacePropertyAssignment(sourceFile: SourceFile, oldNode: PropertyAssignment, newNode: PropertyAssignment) {
|
||||
public replacePropertyAssignment(sourceFile: SourceFile, oldNode: PropertyAssignment, newNode: PropertyAssignment): void {
|
||||
const suffix = this.nextCommaToken(sourceFile, oldNode) ? "" : ("," + this.newLineCharacter);
|
||||
return this.replaceNode(sourceFile, oldNode, newNode, { suffix });
|
||||
this.replaceNode(sourceFile, oldNode, newNode, { suffix });
|
||||
}
|
||||
|
||||
public insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
public insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}): void {
|
||||
this.replaceRange(sourceFile, createRange(pos), newNode, options);
|
||||
}
|
||||
|
||||
@@ -310,7 +306,7 @@ namespace ts.textChanges {
|
||||
});
|
||||
}
|
||||
|
||||
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false) {
|
||||
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false): void {
|
||||
this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}, Position.Start), newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween));
|
||||
}
|
||||
|
||||
@@ -343,7 +339,7 @@ namespace ts.textChanges {
|
||||
this.insertText(sourceFile, token.getStart(sourceFile), text);
|
||||
}
|
||||
|
||||
public insertJsdocCommentBefore(sourceFile: SourceFile, node: HasJSDoc, tag: JSDoc) {
|
||||
public insertJsdocCommentBefore(sourceFile: SourceFile, node: HasJSDoc, tag: JSDoc): void {
|
||||
const fnStart = node.getStart(sourceFile);
|
||||
if (node.jsDoc) {
|
||||
for (const jsdoc of node.jsDoc) {
|
||||
@@ -358,7 +354,7 @@ namespace ts.textChanges {
|
||||
this.insertNodeAt(sourceFile, fnStart, tag, { preserveLeadingWhitespace: false, suffix: this.newLineCharacter + indent });
|
||||
}
|
||||
|
||||
public replaceRangeWithText(sourceFile: SourceFile, range: TextRange, text: string) {
|
||||
public replaceRangeWithText(sourceFile: SourceFile, range: TextRange, text: string): void {
|
||||
this.changes.push({ kind: ChangeKind.Text, sourceFile, range, text });
|
||||
}
|
||||
|
||||
@@ -503,7 +499,7 @@ namespace ts.textChanges {
|
||||
return endPosition;
|
||||
}
|
||||
|
||||
private getInsertNodeAfterOptions(sourceFile: SourceFile, after: Node) {
|
||||
private getInsertNodeAfterOptions(sourceFile: SourceFile, after: Node): InsertNodeOptions {
|
||||
const options = this.getInsertNodeAfterOptionsWorker(after);
|
||||
return {
|
||||
...options,
|
||||
@@ -574,14 +570,14 @@ namespace ts.textChanges {
|
||||
* i.e. arguments in arguments lists, parameters in parameter lists etc.
|
||||
* Note that separators are part of the node in statements and class elements.
|
||||
*/
|
||||
public insertNodeInListAfter(sourceFile: SourceFile, after: Node, newNode: Node, containingList = formatting.SmartIndenter.getContainingList(after, sourceFile)) {
|
||||
public insertNodeInListAfter(sourceFile: SourceFile, after: Node, newNode: Node, containingList = formatting.SmartIndenter.getContainingList(after, sourceFile)): void {
|
||||
if (!containingList) {
|
||||
Debug.fail("node is not a list element");
|
||||
return this;
|
||||
return;
|
||||
}
|
||||
const index = indexOfNode(containingList, after);
|
||||
if (index < 0) {
|
||||
return this;
|
||||
return;
|
||||
}
|
||||
const end = after.getEnd();
|
||||
if (index !== containingList.length - 1) {
|
||||
@@ -683,7 +679,6 @@ namespace ts.textChanges {
|
||||
this.replaceRange(sourceFile, createRange(end), newNode, { prefix: `${tokenToString(separator)} ` });
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private finishClassesWithNodesInsertedAtStart(): void {
|
||||
@@ -737,7 +732,7 @@ namespace ts.textChanges {
|
||||
return changes;
|
||||
}
|
||||
|
||||
public createNewFile(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray<Statement>) {
|
||||
public createNewFile(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray<Statement>): void {
|
||||
this.newFiles.push({ oldFile, fileName, statements });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
|
||||
"codefixes/fixClassSuperMustPrecedeThisAccess.ts",
|
||||
"codefixes/fixConstructorForDerivedNeedSuperCall.ts",
|
||||
"codefixes/fixEnableExperimentalDecorators.ts",
|
||||
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
|
||||
"codefixes/fixForgottenThisPropertyAccess.ts",
|
||||
"codefixes/fixUnusedIdentifier.ts",
|
||||
|
||||
+12
-4
@@ -91,7 +91,6 @@ namespace ts {
|
||||
|
||||
export interface SourceFileLike {
|
||||
getLineAndCharacterOfPosition(pos: number): LineAndCharacter;
|
||||
/*@internal*/ sourceMapper?: DocumentPositionMapper;
|
||||
}
|
||||
|
||||
export interface SourceMapSource {
|
||||
@@ -233,6 +232,11 @@ namespace ts {
|
||||
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
|
||||
/* @internal */ inspectValue?(options: InspectValueOptions): Promise<ValueInfo>;
|
||||
writeFile?(fileName: string, content: string): void;
|
||||
|
||||
/* @internal */
|
||||
getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined;
|
||||
/* @internal */
|
||||
getSourceFileLike?(fileName: string): SourceFileLike | undefined;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -290,8 +294,8 @@ namespace ts {
|
||||
|
||||
getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined;
|
||||
|
||||
getRenameInfo(fileName: string, position: number): RenameInfo;
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReadonlyArray<RenameLocation> | undefined;
|
||||
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ReadonlyArray<RenameLocation> | undefined;
|
||||
|
||||
getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
|
||||
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined;
|
||||
@@ -489,7 +493,7 @@ namespace ts {
|
||||
position: number;
|
||||
}
|
||||
|
||||
export class TextChange {
|
||||
export interface TextChange {
|
||||
span: TextSpan;
|
||||
newText: string;
|
||||
}
|
||||
@@ -844,6 +848,10 @@ namespace ts {
|
||||
localizedErrorMessage: string;
|
||||
}
|
||||
|
||||
export interface RenameInfoOptions {
|
||||
readonly allowRenameOfImportPath?: boolean;
|
||||
}
|
||||
|
||||
export interface SignatureHelpParameter {
|
||||
name: string;
|
||||
documentation: SymbolDisplayPart[];
|
||||
|
||||
@@ -1282,11 +1282,11 @@ namespace ts {
|
||||
return !!compilerOptions.module || compilerOptions.target! >= ScriptTarget.ES2015 || !!compilerOptions.noEmit;
|
||||
}
|
||||
|
||||
export function hostUsesCaseSensitiveFileNames(host: LanguageServiceHost): boolean {
|
||||
export function hostUsesCaseSensitiveFileNames(host: { useCaseSensitiveFileNames?(): boolean; }): boolean {
|
||||
return host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : false;
|
||||
}
|
||||
|
||||
export function hostGetCanonicalFileName(host: LanguageServiceHost): GetCanonicalFileName {
|
||||
export function hostGetCanonicalFileName(host: { useCaseSensitiveFileNames?(): boolean; }): GetCanonicalFileName {
|
||||
return createGetCanonicalFileName(hostUsesCaseSensitiveFileNames(host));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
private testSuiteName: TestRunnerKind;
|
||||
private emit: boolean;
|
||||
|
||||
public options: string;
|
||||
public options: string | undefined;
|
||||
|
||||
constructor(public testType: CompilerTestType) {
|
||||
super();
|
||||
|
||||
@@ -21,6 +21,8 @@ class FourSlashRunner extends RunnerBase {
|
||||
this.basePath = "tests/cases/fourslash/server";
|
||||
this.testSuiteName = "fourslash-server";
|
||||
break;
|
||||
default:
|
||||
throw ts.Debug.assertNever(testType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ namespace Harness.Parallel.Host {
|
||||
let totalCost = 0;
|
||||
|
||||
class RemoteSuite extends Mocha.Suite {
|
||||
suites: RemoteSuite[];
|
||||
suites!: RemoteSuite[];
|
||||
suiteMap = ts.createMap<RemoteSuite>();
|
||||
tests: RemoteTest[];
|
||||
tests!: RemoteTest[];
|
||||
constructor(title: string) {
|
||||
super(title);
|
||||
this.pending = false;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace project {
|
||||
|
||||
class ProjectCompilerHost extends fakes.CompilerHost {
|
||||
private _testCase: ProjectRunnerTestCase & ts.CompilerOptions;
|
||||
private _projectParseConfigHost: ProjectParseConfigHost;
|
||||
private _projectParseConfigHost: ProjectParseConfigHost | undefined;
|
||||
|
||||
constructor(sys: fakes.System | vfs.FileSystem, compilerOptions: ts.CompilerOptions, _testCaseJustName: string, testCase: ProjectRunnerTestCase & ts.CompilerOptions, _moduleKind: ts.ModuleKind) {
|
||||
super(sys, compilerOptions);
|
||||
|
||||
@@ -36,64 +36,109 @@
|
||||
|
||||
"runner.ts",
|
||||
|
||||
"unittests/extractTestHelpers.ts",
|
||||
"unittests/tsserverProjectSystem.ts",
|
||||
"unittests/typingsInstaller.ts",
|
||||
"unittests/services/extract/helpers.ts",
|
||||
"unittests/tscWatch/helpers.ts",
|
||||
"unittests/tsserver/helpers.ts",
|
||||
|
||||
"unittests/asserts.ts",
|
||||
"unittests/base64.ts",
|
||||
"unittests/builder.ts",
|
||||
"unittests/cancellableLanguageServiceOperations.ts",
|
||||
"unittests/commandLineParsing.ts",
|
||||
"unittests/compileOnSave.ts",
|
||||
"unittests/compilerCore.ts",
|
||||
"unittests/configurationExtension.ts",
|
||||
"unittests/convertCompilerOptionsFromJson.ts",
|
||||
"unittests/convertToAsyncFunction.ts",
|
||||
"unittests/convertToBase64.ts",
|
||||
"unittests/convertTypeAcquisitionFromJson.ts",
|
||||
"unittests/customTransforms.ts",
|
||||
"unittests/extractConstants.ts",
|
||||
"unittests/extractFunctions.ts",
|
||||
"unittests/extractRanges.ts",
|
||||
"unittests/factory.ts",
|
||||
"unittests/hostNewLineSupport.ts",
|
||||
"unittests/incrementalParser.ts",
|
||||
"unittests/initializeTSConfig.ts",
|
||||
"unittests/jsDocParsing.ts",
|
||||
"unittests/languageService.ts",
|
||||
"unittests/matchFiles.ts",
|
||||
"unittests/moduleResolution.ts",
|
||||
"unittests/organizeImports.ts",
|
||||
"unittests/parsePseudoBigInt.ts",
|
||||
"unittests/paths.ts",
|
||||
"unittests/printer.ts",
|
||||
"unittests/programMissingFiles.ts",
|
||||
"unittests/programNoParseFalsyFileNames.ts",
|
||||
"unittests/projectErrors.ts",
|
||||
"unittests/projectReferences.ts",
|
||||
"unittests/programApi.ts",
|
||||
"unittests/publicApi.ts",
|
||||
"unittests/reuseProgramStructure.ts",
|
||||
"unittests/session.ts",
|
||||
"unittests/semver.ts",
|
||||
"unittests/showConfig.ts",
|
||||
"unittests/symbolWalker.ts",
|
||||
"unittests/telemetry.ts",
|
||||
"unittests/textChanges.ts",
|
||||
"unittests/textStorage.ts",
|
||||
"unittests/transform.ts",
|
||||
"unittests/transpile.ts",
|
||||
"unittests/tsbuild.ts",
|
||||
"unittests/tsbuildWatchMode.ts",
|
||||
"unittests/tsconfigParsing.ts",
|
||||
"unittests/tscWatchMode.ts",
|
||||
"unittests/versionCache.ts",
|
||||
"unittests/config/commandLineParsing.ts",
|
||||
"unittests/config/configurationExtension.ts",
|
||||
"unittests/config/convertCompilerOptionsFromJson.ts",
|
||||
"unittests/config/convertTypeAcquisitionFromJson.ts",
|
||||
"unittests/config/initializeTSConfig.ts",
|
||||
"unittests/config/matchFiles.ts",
|
||||
"unittests/config/projectReferences.ts",
|
||||
"unittests/config/showConfig.ts",
|
||||
"unittests/config/tsconfigParsing.ts",
|
||||
"unittests/evaluation/asyncArrow.ts",
|
||||
"unittests/evaluation/asyncGenerator.ts",
|
||||
"unittests/evaluation/forAwaitOf.ts",
|
||||
"unittests/services/cancellableLanguageServiceOperations.ts",
|
||||
"unittests/services/colorization.ts",
|
||||
"unittests/services/convertToAsyncFunction.ts",
|
||||
"unittests/services/documentRegistry.ts",
|
||||
"unittests/services/extract/constants.ts",
|
||||
"unittests/services/extract/functions.ts",
|
||||
"unittests/services/extract/symbolWalker.ts",
|
||||
"unittests/services/extract/ranges.ts",
|
||||
"unittests/services/hostNewLineSupport.ts",
|
||||
"unittests/services/languageService.ts",
|
||||
"unittests/services/organizeImports.ts",
|
||||
"unittests/services/patternMatcher.ts",
|
||||
"unittests/services/preProcessFile.ts"
|
||||
"unittests/services/preProcessFile.ts",
|
||||
"unittests/services/textChanges.ts",
|
||||
"unittests/services/transpile.ts",
|
||||
"unittests/tscWatch/consoleClearing.ts",
|
||||
"unittests/tscWatch/emit.ts",
|
||||
"unittests/tscWatch/emitAndErrorUpdates.ts",
|
||||
"unittests/tscWatch/programUpdates.ts",
|
||||
"unittests/tscWatch/resolutionCache.ts",
|
||||
"unittests/tscWatch/watchEnvironment.ts",
|
||||
"unittests/tscWatch/watchApi.ts",
|
||||
"unittests/tsserver/cachingFileSystemInformation.ts",
|
||||
"unittests/tsserver/cancellationToken.ts",
|
||||
"unittests/tsserver/compileOnSave.ts",
|
||||
"unittests/tsserver/completions.ts",
|
||||
"unittests/tsserver/configFileSearch.ts",
|
||||
"unittests/tsserver/configuredProjects.ts",
|
||||
"unittests/tsserver/declarationFileMaps.ts",
|
||||
"unittests/tsserver/documentRegistry.ts",
|
||||
"unittests/tsserver/duplicatePackages.ts",
|
||||
"unittests/tsserver/events/largeFileReferenced.ts",
|
||||
"unittests/tsserver/events/projectLanguageServiceState.ts",
|
||||
"unittests/tsserver/events/projectLoading.ts",
|
||||
"unittests/tsserver/events/projectUpdatedInBackground.ts",
|
||||
"unittests/tsserver/events/surveyReady.ts",
|
||||
"unittests/tsserver/externalProjects.ts",
|
||||
"unittests/tsserver/forceConsistentCasingInFileNames.ts",
|
||||
"unittests/tsserver/formatSettings.ts",
|
||||
"unittests/tsserver/getApplicableRefactors.ts",
|
||||
"unittests/tsserver/getEditsForFileRename.ts",
|
||||
"unittests/tsserver/importHelpers.ts",
|
||||
"unittests/tsserver/inferredProjects.ts",
|
||||
"unittests/tsserver/languageService.ts",
|
||||
"unittests/tsserver/maxNodeModuleJsDepth.ts",
|
||||
"unittests/tsserver/metadataInResponse.ts",
|
||||
"unittests/tsserver/navTo.ts",
|
||||
"unittests/tsserver/occurences.ts",
|
||||
"unittests/tsserver/openFile.ts",
|
||||
"unittests/tsserver/projectErrors.ts",
|
||||
"unittests/tsserver/projectReferences.ts",
|
||||
"unittests/tsserver/projects.ts",
|
||||
"unittests/tsserver/refactors.ts",
|
||||
"unittests/tsserver/reload.ts",
|
||||
"unittests/tsserver/rename.ts",
|
||||
"unittests/tsserver/resolutionCache.ts",
|
||||
"unittests/tsserver/session.ts",
|
||||
"unittests/tsserver/skipLibCheck.ts",
|
||||
"unittests/tsserver/symLinks.ts",
|
||||
"unittests/tsserver/syntaxOperations.ts",
|
||||
"unittests/tsserver/textStorage.ts",
|
||||
"unittests/tsserver/telemetry.ts",
|
||||
"unittests/tsserver/typeAquisition.ts",
|
||||
"unittests/tsserver/typeReferenceDirectives.ts",
|
||||
"unittests/tsserver/typingsInstaller.ts",
|
||||
"unittests/tsserver/untitledFiles.ts",
|
||||
"unittests/tsserver/versionCache.ts",
|
||||
"unittests/tsserver/watchEnvironment.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
describe("assert", () => {
|
||||
describe("unittests:: assert", () => {
|
||||
it("deepEqual", () => {
|
||||
assert.throws(() => assert.deepEqual(createNodeArray([createIdentifier("A")]), createNodeArray([createIdentifier("B")])));
|
||||
assert.throws(() => assert.deepEqual(createNodeArray([], /*hasTrailingComma*/ true), createNodeArray([], /*hasTrailingComma*/ false)));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
describe("base64", () => {
|
||||
describe("unittests:: base64", () => {
|
||||
describe("base64decode", () => {
|
||||
it("can decode input strings correctly without needing a host implementation", () => {
|
||||
const tests = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
describe("builder", () => {
|
||||
describe("unittests:: builder", () => {
|
||||
it("emits dependent files", () => {
|
||||
const files: NamedSourceText[] = [
|
||||
{ name: "/a.ts", text: SourceText.New("", 'import { b } from "./b";', "") },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
describe("compilerCore", () => {
|
||||
describe("unittests:: compilerCore", () => {
|
||||
describe("equalOwnProperties", () => {
|
||||
it("correctly equates objects", () => {
|
||||
assert.isTrue(equalOwnProperties({}, {}));
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
describe("parseCommandLine", () => {
|
||||
describe("unittests:: config:: commandLineParsing:: parseCommandLine", () => {
|
||||
|
||||
function assertParseResult(commandLine: string[], expectedParsedCommandLine: ParsedCommandLine) {
|
||||
const parsed = parseCommandLine(commandLine);
|
||||
@@ -367,7 +367,7 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseBuildOptions", () => {
|
||||
describe("unittests:: config:: commandLineParsing:: parseBuildOptions", () => {
|
||||
function assertParseResult(commandLine: string[], expectedParsedBuildCommand: ParsedBuildCommand) {
|
||||
const parsed = parseBuildCommand(commandLine);
|
||||
const parsedBuildOptions = JSON.stringify(parsed.buildOptions);
|
||||
+1
-1
@@ -208,7 +208,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
describe("configurationExtension", () => {
|
||||
describe("unittests:: config:: configurationExtension", () => {
|
||||
forEach<[string, string, fakes.ParseConfigHost], void>([
|
||||
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
|
||||
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
describe("convertCompilerOptionsFromJson", () => {
|
||||
describe("unittests:: config:: convertCompilerOptionsFromJson", () => {
|
||||
const formatDiagnosticHost: FormatDiagnosticsHost = {
|
||||
getCurrentDirectory: () => "/apath/",
|
||||
getCanonicalFileName: createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ true),
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
namespace ts {
|
||||
interface ExpectedResult { typeAcquisition: TypeAcquisition; errors: Diagnostic[]; }
|
||||
describe("convertTypeAcquisitionFromJson", () => {
|
||||
describe("unittests:: config:: convertTypeAcquisitionFromJson", () => {
|
||||
function assertTypeAcquisition(json: any, configFileName: string, expectedResult: ExpectedResult) {
|
||||
assertTypeAcquisitionWithJson(json, configFileName, expectedResult);
|
||||
assertTypeAcquisitionWithJsonNode(json, configFileName, expectedResult);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user