mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into dynamicNames
This commit is contained in:
+30
-16
@@ -7393,7 +7393,7 @@ namespace ts {
|
||||
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
|
||||
getPropertyNameForKnownSymbolName((<Identifier>(<PropertyAccessExpression>accessExpression.argumentExpression).name).text) :
|
||||
undefined;
|
||||
if (propName) {
|
||||
if (propName !== undefined) {
|
||||
const prop = getPropertyOfType(objectType, propName);
|
||||
if (prop) {
|
||||
if (accessExpression) {
|
||||
@@ -9364,25 +9364,39 @@ namespace ts {
|
||||
let result = Ternary.True;
|
||||
const saveErrorInfo = errorInfo;
|
||||
|
||||
outer: for (const t of targetSignatures) {
|
||||
// Only elaborate errors from the first failure
|
||||
let shouldElaborateErrors = reportErrors;
|
||||
for (const s of sourceSignatures) {
|
||||
const related = signatureRelatedTo(s, t, shouldElaborateErrors);
|
||||
if (related) {
|
||||
result &= related;
|
||||
errorInfo = saveErrorInfo;
|
||||
continue outer;
|
||||
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
|
||||
// We instantiations of the same anonymous type (which typically will be the type of a method).
|
||||
// Simply do a pairwise comparison of the signatures in the two signature lists instead of the
|
||||
// much more expensive N * M comparison matrix we explore below.
|
||||
for (let i = 0; i < targetSignatures.length; i++) {
|
||||
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], reportErrors);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
shouldElaborateErrors = false;
|
||||
result &= related;
|
||||
}
|
||||
}
|
||||
else {
|
||||
outer: for (const t of targetSignatures) {
|
||||
// Only elaborate errors from the first failure
|
||||
let shouldElaborateErrors = reportErrors;
|
||||
for (const s of sourceSignatures) {
|
||||
const related = signatureRelatedTo(s, t, shouldElaborateErrors);
|
||||
if (related) {
|
||||
result &= related;
|
||||
errorInfo = saveErrorInfo;
|
||||
continue outer;
|
||||
}
|
||||
shouldElaborateErrors = false;
|
||||
}
|
||||
|
||||
if (shouldElaborateErrors) {
|
||||
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
|
||||
typeToString(source),
|
||||
signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
|
||||
if (shouldElaborateErrors) {
|
||||
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
|
||||
typeToString(source),
|
||||
signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -251,6 +251,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function zipToMap<T>(keys: string[], values: T[]): Map<T> {
|
||||
Debug.assert(keys.length === values.length);
|
||||
const map = createMap<T>();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
map.set(keys[i], values[i]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through `array` by index and performs the callback on each element of array until the callback
|
||||
* returns a falsey value, then returns false.
|
||||
|
||||
@@ -3207,9 +3207,16 @@
|
||||
},
|
||||
"Scoped package detected, looking in '{0}'": {
|
||||
"category": "Message",
|
||||
"code": "6182"
|
||||
"code": 6182
|
||||
},
|
||||
"Reusing resolution of module '{0}' to file '{1}' from old program.": {
|
||||
"category": "Message",
|
||||
"code": 6183
|
||||
},
|
||||
"Reusing module resolutions originating in '{0}' since resolutions are unchanged from old program.": {
|
||||
"category": "Message",
|
||||
"code": 6184
|
||||
},
|
||||
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
"code": 7005
|
||||
|
||||
@@ -1440,6 +1440,44 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createInterfaceDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[] | undefined, members: TypeElement[]) {
|
||||
const node = <InterfaceDeclaration>createSynthesizedNode(SyntaxKind.InterfaceDeclaration);
|
||||
node.decorators = asNodeArray(decorators);
|
||||
node.modifiers = asNodeArray(modifiers);
|
||||
node.name = asName(name);
|
||||
node.typeParameters = asNodeArray(typeParameters);
|
||||
node.heritageClauses = asNodeArray(heritageClauses);
|
||||
node.members = createNodeArray(members);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateInterfaceDeclaration(node: InterfaceDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: Identifier, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[] | undefined, members: TypeElement[]) {
|
||||
return node.decorators !== decorators
|
||||
|| node.modifiers !== modifiers
|
||||
|| node.name !== name
|
||||
|| node.typeParameters !== typeParameters
|
||||
|| node.heritageClauses !== heritageClauses
|
||||
|| node.members !== members
|
||||
? updateNode(createInterfaceDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createTypeAliasDeclaration(name: string | Identifier, typeParameters: TypeParameterDeclaration[] | undefined, type: TypeNode) {
|
||||
const node = <TypeAliasDeclaration>createSynthesizedNode(SyntaxKind.TypeAliasDeclaration);
|
||||
node.name = asName(name);
|
||||
node.typeParameters = asNodeArray(typeParameters);
|
||||
node.type = type;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateTypeAliasDeclaration(node: TypeAliasDeclaration, name: Identifier, typeParameters: TypeParameterDeclaration[] | undefined, type: TypeNode) {
|
||||
return node.name !== name
|
||||
|| node.typeParameters !== typeParameters
|
||||
|| node.type !== type
|
||||
? updateNode(createTypeAliasDeclaration(name, typeParameters, type), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createEnumDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, members: EnumMember[]) {
|
||||
const node = <EnumDeclaration>createSynthesizedNode(SyntaxKind.EnumDeclaration);
|
||||
node.decorators = asNodeArray(decorators);
|
||||
|
||||
+122
-83
@@ -298,6 +298,7 @@ namespace ts {
|
||||
let diagnosticsProducingTypeChecker: TypeChecker;
|
||||
let noDiagnosticsTypeChecker: TypeChecker;
|
||||
let classifiableNames: Map<string>;
|
||||
let modifiedFilePaths: Path[] | undefined;
|
||||
|
||||
const cachedSemanticDiagnosticsForFile: DiagnosticCache = {};
|
||||
const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {};
|
||||
@@ -367,7 +368,8 @@ namespace ts {
|
||||
// used to track cases when two file names differ only in casing
|
||||
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
|
||||
|
||||
if (!tryReuseStructureFromOldProgram()) {
|
||||
const structuralIsReused = tryReuseStructureFromOldProgram();
|
||||
if (structuralIsReused !== StructureIsReused.Completely) {
|
||||
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
|
||||
|
||||
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
|
||||
@@ -476,89 +478,114 @@ namespace ts {
|
||||
}
|
||||
|
||||
interface OldProgramState {
|
||||
program: Program;
|
||||
program: Program | undefined;
|
||||
file: SourceFile;
|
||||
/** The collection of paths modified *since* the old program. */
|
||||
modifiedFilePaths: Path[];
|
||||
}
|
||||
|
||||
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState?: OldProgramState) {
|
||||
if (!oldProgramState && !file.ambientModuleNames.length) {
|
||||
// if old program state is not supplied and file does not contain locally defined ambient modules
|
||||
// then the best we can do is fallback to the default logic
|
||||
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState: OldProgramState) {
|
||||
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
|
||||
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
|
||||
// the best we can do is fallback to the default logic.
|
||||
return resolveModuleNamesWorker(moduleNames, containingFile);
|
||||
}
|
||||
|
||||
// at this point we know that either
|
||||
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
|
||||
if (oldSourceFile !== file && file.resolvedModules) {
|
||||
// `file` was created for the new program.
|
||||
//
|
||||
// We only set `file.resolvedModules` via work from the current function,
|
||||
// so it is defined iff we already called the current function on `file`.
|
||||
// That call happened no later than the creation of the `file` object,
|
||||
// which per above occured during the current program creation.
|
||||
// Since we assume the filesystem does not change during program creation,
|
||||
// it is safe to reuse resolutions from the earlier call.
|
||||
const result: ResolvedModuleFull[] = [];
|
||||
for (const moduleName of moduleNames) {
|
||||
const resolvedModule = file.resolvedModules.get(moduleName);
|
||||
result.push(resolvedModule);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// At this point, we know at least one of the following hold:
|
||||
// - file has local declarations for ambient modules
|
||||
// OR
|
||||
// - old program state is available
|
||||
// OR
|
||||
// - both of items above
|
||||
// With this it is possible that we can tell how some module names from the initial list will be resolved
|
||||
// without doing actual resolution (in particular if some name was resolved to ambient module).
|
||||
// Such names should be excluded from the list of module names that will be provided to `resolveModuleNamesWorker`
|
||||
// since we don't want to resolve them again.
|
||||
// With this information, we can infer some module resolutions without performing resolution.
|
||||
|
||||
// this is a list of modules for which we cannot predict resolution so they should be actually resolved
|
||||
/** An ordered list of module names for which we cannot recover the resolution. */
|
||||
let unknownModuleNames: string[];
|
||||
// this is a list of combined results assembles from predicted and resolved results.
|
||||
// Order in this list matches the order in the original list of module names `moduleNames` which is important
|
||||
// so later we can split results to resolutions of modules and resolutions of module augmentations.
|
||||
/**
|
||||
* The indexing of elements in this list matches that of `moduleNames`.
|
||||
*
|
||||
* Before combining results, result[i] is in one of the following states:
|
||||
* * undefined: needs to be recomputed,
|
||||
* * predictedToResolveToAmbientModuleMarker: known to be an ambient module.
|
||||
* Needs to be reset to undefined before returning,
|
||||
* * ResolvedModuleFull instance: can be reused.
|
||||
*/
|
||||
let result: ResolvedModuleFull[];
|
||||
// a transient placeholder that is used to mark predicted resolution in the result list
|
||||
/** A transient placeholder used to mark predicted resolution in the result list. */
|
||||
const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
|
||||
|
||||
|
||||
for (let i = 0; i < moduleNames.length; i++) {
|
||||
const moduleName = moduleNames[i];
|
||||
// module name is known to be resolved to ambient module if
|
||||
// - module name is contained in the list of ambient modules that are locally declared in the file
|
||||
// - in the old program module name was resolved to ambient module whose declaration is in non-modified file
|
||||
// If we want to reuse resolutions more aggressively, we can refine this to check for whether the
|
||||
// text of the corresponding modulenames has changed.
|
||||
if (file === oldSourceFile) {
|
||||
const oldResolvedModule = oldSourceFile && oldSourceFile.resolvedModules.get(moduleName);
|
||||
if (oldResolvedModule) {
|
||||
if (isTraceEnabled(options, host)) {
|
||||
trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, containingFile);
|
||||
}
|
||||
(result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// We know moduleName resolves to an ambient module provided that moduleName:
|
||||
// - is in the list of ambient modules locally declared in the current source file.
|
||||
// - resolved to an ambient module in the old program whose declaration is in an unmodified file
|
||||
// (so the same module declaration will land in the new program)
|
||||
let isKnownToResolveToAmbientModule = false;
|
||||
let resolvesToAmbientModuleInNonModifiedFile = false;
|
||||
if (contains(file.ambientModuleNames, moduleName)) {
|
||||
isKnownToResolveToAmbientModule = true;
|
||||
resolvesToAmbientModuleInNonModifiedFile = true;
|
||||
if (isTraceEnabled(options, host)) {
|
||||
trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, containingFile);
|
||||
}
|
||||
}
|
||||
else {
|
||||
isKnownToResolveToAmbientModule = checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
|
||||
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
|
||||
}
|
||||
|
||||
if (isKnownToResolveToAmbientModule) {
|
||||
if (!unknownModuleNames) {
|
||||
// found a first module name for which result can be prediced
|
||||
// this means that this module name should not be passed to `resolveModuleNamesWorker`.
|
||||
// We'll use a separate list for module names that are definitely unknown.
|
||||
result = new Array(moduleNames.length);
|
||||
// copy all module names that appear before the current one in the list
|
||||
// since they are known to be unknown
|
||||
unknownModuleNames = moduleNames.slice(0, i);
|
||||
}
|
||||
// mark prediced resolution in the result list
|
||||
result[i] = predictedToResolveToAmbientModuleMarker;
|
||||
if (resolvesToAmbientModuleInNonModifiedFile) {
|
||||
(result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
|
||||
}
|
||||
else if (unknownModuleNames) {
|
||||
// found unknown module name and we are already using separate list for those - add it to the list
|
||||
unknownModuleNames.push(moduleName);
|
||||
else {
|
||||
// Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
|
||||
(unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!unknownModuleNames) {
|
||||
// we've looked throught the list but have not seen any predicted resolution
|
||||
// use default logic
|
||||
return resolveModuleNamesWorker(moduleNames, containingFile);
|
||||
}
|
||||
|
||||
const resolutions = unknownModuleNames.length
|
||||
const resolutions = unknownModuleNames && unknownModuleNames.length
|
||||
? resolveModuleNamesWorker(unknownModuleNames, containingFile)
|
||||
: emptyArray;
|
||||
|
||||
// combine results of resolutions and predicted results
|
||||
// Combine results of resolutions and predicted results
|
||||
if (!result) {
|
||||
// There were no unresolved/ambient resolutions.
|
||||
Debug.assert(resolutions.length === moduleNames.length);
|
||||
return <ResolvedModuleFull[]>resolutions;
|
||||
}
|
||||
|
||||
let j = 0;
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
if (result[i] === predictedToResolveToAmbientModuleMarker) {
|
||||
result[i] = undefined;
|
||||
if (result[i]) {
|
||||
// `result[i]` is either a `ResolvedModuleFull` or a marker.
|
||||
// If it is the former, we can leave it as is.
|
||||
if (result[i] === predictedToResolveToAmbientModuleMarker) {
|
||||
result[i] = undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result[i] = resolutions[j];
|
||||
@@ -566,18 +593,18 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
Debug.assert(j === resolutions.length);
|
||||
|
||||
return result;
|
||||
|
||||
function checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState?: OldProgramState): boolean {
|
||||
if (!oldProgramState) {
|
||||
return false;
|
||||
}
|
||||
// If we change our policy of rechecking failed lookups on each program create,
|
||||
// we should adjust the value returned here.
|
||||
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean {
|
||||
const resolutionToFile = getResolvedModule(oldProgramState.file, moduleName);
|
||||
if (resolutionToFile) {
|
||||
// module used to be resolved to file - ignore it
|
||||
return false;
|
||||
}
|
||||
const ambientModule = oldProgram.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
|
||||
const ambientModule = oldProgramState.program && oldProgramState.program.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
|
||||
if (!(ambientModule && ambientModule.declarations)) {
|
||||
return false;
|
||||
}
|
||||
@@ -599,99 +626,107 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function tryReuseStructureFromOldProgram(): boolean {
|
||||
function tryReuseStructureFromOldProgram(): StructureIsReused {
|
||||
if (!oldProgram) {
|
||||
return false;
|
||||
return StructureIsReused.Not;
|
||||
}
|
||||
|
||||
// check properties that can affect structure of the program or module resolution strategy
|
||||
// if any of these properties has changed - structure cannot be reused
|
||||
const oldOptions = oldProgram.getCompilerOptions();
|
||||
if (changesAffectModuleResolution(oldOptions, options)) {
|
||||
return false;
|
||||
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||
}
|
||||
|
||||
Debug.assert(!oldProgram.structureIsReused);
|
||||
Debug.assert(!(oldProgram.structureIsReused & (StructureIsReused.Completely | StructureIsReused.SafeModules)));
|
||||
|
||||
// there is an old program, check if we can reuse its structure
|
||||
const oldRootNames = oldProgram.getRootFileNames();
|
||||
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
|
||||
return false;
|
||||
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||
}
|
||||
|
||||
if (!arrayIsEqualTo(options.types, oldOptions.types)) {
|
||||
return false;
|
||||
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||
}
|
||||
|
||||
// check if program source files has changed in the way that can affect structure of the program
|
||||
const newSourceFiles: SourceFile[] = [];
|
||||
const filePaths: Path[] = [];
|
||||
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
|
||||
oldProgram.structureIsReused = StructureIsReused.Completely;
|
||||
|
||||
for (const oldSourceFile of oldProgram.getSourceFiles()) {
|
||||
let newSourceFile = host.getSourceFileByPath
|
||||
const newSourceFile = host.getSourceFileByPath
|
||||
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
|
||||
: host.getSourceFile(oldSourceFile.fileName, options.target);
|
||||
|
||||
if (!newSourceFile) {
|
||||
return false;
|
||||
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||
}
|
||||
|
||||
newSourceFile.path = oldSourceFile.path;
|
||||
filePaths.push(newSourceFile.path);
|
||||
|
||||
if (oldSourceFile !== newSourceFile) {
|
||||
// The `newSourceFile` object was created for the new program.
|
||||
|
||||
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
|
||||
// value of no-default-lib has changed
|
||||
// this will affect if default library is injected into the list of files
|
||||
return false;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
// check tripleslash references
|
||||
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
|
||||
// tripleslash references has changed
|
||||
return false;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
// check imports and module augmentations
|
||||
collectExternalModuleReferences(newSourceFile);
|
||||
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
|
||||
// imports has changed
|
||||
return false;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
|
||||
// moduleAugmentations has changed
|
||||
return false;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
|
||||
// 'types' references has changed
|
||||
return false;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
// tentatively approve the file
|
||||
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
|
||||
}
|
||||
else {
|
||||
// file has no changes - use it as is
|
||||
newSourceFile = oldSourceFile;
|
||||
}
|
||||
|
||||
// if file has passed all checks it should be safe to reuse it
|
||||
newSourceFiles.push(newSourceFile);
|
||||
}
|
||||
|
||||
const modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
|
||||
if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
|
||||
return oldProgram.structureIsReused;
|
||||
}
|
||||
|
||||
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
|
||||
// try to verify results of module resolution
|
||||
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
|
||||
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
|
||||
if (resolveModuleNamesWorker) {
|
||||
const moduleNames = map(concatenate(newSourceFile.imports, newSourceFile.moduleAugmentations), getTextOfLiteral);
|
||||
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, { file: oldSourceFile, program: oldProgram, modifiedFilePaths });
|
||||
const oldProgramState = { program: oldProgram, file: oldSourceFile, modifiedFilePaths };
|
||||
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, oldProgramState);
|
||||
// ensure that module resolution results are still correct
|
||||
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
return false;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
|
||||
}
|
||||
else {
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
}
|
||||
}
|
||||
if (resolveTypeReferenceDirectiveNamesWorker) {
|
||||
@@ -700,12 +735,17 @@ namespace ts {
|
||||
// ensure that types resolutions are still correct
|
||||
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
return false;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, resolutions);
|
||||
}
|
||||
else {
|
||||
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
|
||||
}
|
||||
}
|
||||
// pass the cache of module/types resolutions from the old source file
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
|
||||
}
|
||||
|
||||
if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
|
||||
return oldProgram.structureIsReused;
|
||||
}
|
||||
|
||||
// update fileName -> file mapping
|
||||
@@ -720,9 +760,8 @@ namespace ts {
|
||||
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
|
||||
}
|
||||
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
|
||||
oldProgram.structureIsReused = true;
|
||||
|
||||
return true;
|
||||
return oldProgram.structureIsReused = StructureIsReused.Completely;
|
||||
}
|
||||
|
||||
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
|
||||
@@ -1528,11 +1567,11 @@ namespace ts {
|
||||
function processImportedModules(file: SourceFile) {
|
||||
collectExternalModuleReferences(file);
|
||||
if (file.imports.length || file.moduleAugmentations.length) {
|
||||
file.resolvedModules = createMap<ResolvedModuleFull>();
|
||||
// Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
|
||||
const nonGlobalAugmentation = filter(file.moduleAugmentations, (moduleAugmentation) => moduleAugmentation.kind === SyntaxKind.StringLiteral);
|
||||
const moduleNames = map(concatenate(file.imports, nonGlobalAugmentation), getTextOfLiteral);
|
||||
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory), file);
|
||||
const oldProgramState = { program: oldProgram, file, modifiedFilePaths };
|
||||
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory), file, oldProgramState);
|
||||
Debug.assert(resolutions.length === moduleNames.length);
|
||||
for (let i = 0; i < moduleNames.length; i++) {
|
||||
const resolution = resolutions[i];
|
||||
|
||||
@@ -2408,7 +2408,14 @@ namespace ts {
|
||||
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
|
||||
/* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean;
|
||||
// For testing purposes only.
|
||||
/* @internal */ structureIsReused?: boolean;
|
||||
/* @internal */ structureIsReused?: StructureIsReused;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum StructureIsReused {
|
||||
Not = 0,
|
||||
SafeModules = 1 << 0,
|
||||
Completely = 1 << 1,
|
||||
}
|
||||
|
||||
export interface CustomTransformers {
|
||||
|
||||
@@ -122,9 +122,8 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
export function hasChangesInResolutions<T>(names: string[], newResolutions: T[], oldResolutions: Map<T>, comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
|
||||
if (names.length !== newResolutions.length) {
|
||||
return false;
|
||||
}
|
||||
Debug.assert(names.length === newResolutions.length);
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const newResolution = newResolutions[i];
|
||||
const oldResolution = oldResolutions && oldResolutions.get(names[i]);
|
||||
|
||||
@@ -201,14 +201,13 @@ namespace ts {
|
||||
assert.isTrue(diags.length === 1, "one diagnostic expected");
|
||||
assert.isTrue(typeof diags[0].messageText === "string" && ((<string>diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message");
|
||||
|
||||
// assert that import will success once file appear on disk
|
||||
fileMap.set(imported.name, imported);
|
||||
fileExistsCalledForBar = false;
|
||||
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
|
||||
|
||||
diags = project.getLanguageService().getSemanticDiagnostics(root.name);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
||||
assert.isTrue(diags.length === 0);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||
assert.isTrue(diags.length === 0, "The import should succeed once the imported file appears on disk.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1042,7 +1042,7 @@ import b = require("./moduleB");
|
||||
assert.equal(diagnostics1.length, 1, "expected one diagnostic");
|
||||
|
||||
createProgram(names, {}, compilerHost, program1);
|
||||
assert.isTrue(program1.structureIsReused);
|
||||
assert.isTrue(program1.structureIsReused === StructureIsReused.Completely);
|
||||
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
|
||||
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
|
||||
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
|
||||
|
||||
@@ -159,12 +159,14 @@ namespace ts {
|
||||
return program;
|
||||
}
|
||||
|
||||
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void) {
|
||||
const texts: NamedSourceText[] = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
|
||||
updater(texts);
|
||||
const host = createTestCompilerHost(texts, options.target, oldProgram);
|
||||
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void, newTexts?: NamedSourceText[]) {
|
||||
if (!newTexts) {
|
||||
newTexts = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
|
||||
}
|
||||
updater(newTexts);
|
||||
const host = createTestCompilerHost(newTexts, options.target, oldProgram);
|
||||
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
|
||||
program.sourceTexts = texts;
|
||||
program.sourceTexts = newTexts;
|
||||
program.host = host;
|
||||
return program;
|
||||
}
|
||||
@@ -217,13 +219,15 @@ namespace ts {
|
||||
|
||||
describe("Reuse program structure", () => {
|
||||
const target = ScriptTarget.Latest;
|
||||
const files = [
|
||||
{ name: "a.ts", text: SourceText.New(
|
||||
`
|
||||
const files: NamedSourceText[] = [
|
||||
{
|
||||
name: "a.ts", text: SourceText.New(
|
||||
`
|
||||
/// <reference path='b.ts'/>
|
||||
/// <reference path='non-existing-file.ts'/>
|
||||
/// <reference types="typerefs" />
|
||||
`, "", `var x = 1`) },
|
||||
`, "", `var x = 1`)
|
||||
},
|
||||
{ name: "b.ts", text: SourceText.New(`/// <reference path='c.ts'/>`, "", `var y = 2`) },
|
||||
{ name: "c.ts", text: SourceText.New("", "", `var z = 1;`) },
|
||||
{ name: "types/typerefs/index.d.ts", text: SourceText.New("", "", `declare let z: number;`) },
|
||||
@@ -234,7 +238,7 @@ namespace ts {
|
||||
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||
files[0].text = files[0].text.updateProgram("var x = 100");
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
|
||||
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
|
||||
@@ -245,7 +249,7 @@ namespace ts {
|
||||
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||
files[0].text = files[0].text.updateProgram("var x = 100");
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
|
||||
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
|
||||
@@ -259,19 +263,19 @@ namespace ts {
|
||||
`;
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
|
||||
});
|
||||
|
||||
it("fails if change affects type references", () => {
|
||||
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
|
||||
updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
|
||||
});
|
||||
|
||||
it("succeeds if change doesn't affect type references", () => {
|
||||
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
|
||||
updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop);
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
|
||||
});
|
||||
|
||||
it("fails if change affects imports", () => {
|
||||
@@ -279,7 +283,7 @@ namespace ts {
|
||||
updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
|
||||
});
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
|
||||
});
|
||||
|
||||
it("fails if change affects type directives", () => {
|
||||
@@ -291,25 +295,25 @@ namespace ts {
|
||||
/// <reference types="typerefs1" />`;
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
|
||||
});
|
||||
|
||||
it("fails if module kind changes", () => {
|
||||
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
|
||||
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
|
||||
});
|
||||
|
||||
it("fails if rootdir changes", () => {
|
||||
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" });
|
||||
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
|
||||
});
|
||||
|
||||
it("fails if config path changes", () => {
|
||||
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
|
||||
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
|
||||
});
|
||||
|
||||
it("resolution cache follows imports", () => {
|
||||
@@ -328,7 +332,7 @@ namespace ts {
|
||||
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
|
||||
files[0].text = files[0].text.updateProgram("var x = 2");
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
|
||||
|
||||
// content of resolution cache should not change
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
|
||||
@@ -338,7 +342,7 @@ namespace ts {
|
||||
const program_3 = updateProgram(program_2, ["a.ts"], options, files => {
|
||||
files[0].text = files[0].text.updateImportsAndExports("");
|
||||
});
|
||||
assert.isTrue(!program_2.structureIsReused);
|
||||
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
|
||||
|
||||
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
|
||||
@@ -347,7 +351,7 @@ namespace ts {
|
||||
`;
|
||||
files[0].text = files[0].text.updateImportsAndExports(newImports);
|
||||
});
|
||||
assert.isTrue(!program_3.structureIsReused);
|
||||
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
|
||||
});
|
||||
|
||||
@@ -365,7 +369,7 @@ namespace ts {
|
||||
const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
|
||||
files[0].text = files[0].text.updateProgram("var x = 2");
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
|
||||
|
||||
// content of resolution cache should not change
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
@@ -376,7 +380,7 @@ namespace ts {
|
||||
files[0].text = files[0].text.updateReferences("");
|
||||
});
|
||||
|
||||
assert.isTrue(!program_2.structureIsReused);
|
||||
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
|
||||
|
||||
updateProgram(program_3, ["/a.ts"], options, files => {
|
||||
@@ -385,10 +389,74 @@ namespace ts {
|
||||
`;
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(!program_3.structureIsReused);
|
||||
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
});
|
||||
|
||||
it("fetches imports after npm install", () => {
|
||||
const file1Ts = { name: "file1.ts", text: SourceText.New("", `import * as a from "a";`, "const myX: number = a.x;") };
|
||||
const file2Ts = { name: "file2.ts", text: SourceText.New("", "", "") };
|
||||
const indexDTS = { name: "node_modules/a/index.d.ts", text: SourceText.New("", "export declare let x: number;", "") };
|
||||
const options: CompilerOptions = { target: ScriptTarget.ES2015, traceResolution: true, moduleResolution: ModuleResolutionKind.NodeJs };
|
||||
const rootFiles = [file1Ts, file2Ts];
|
||||
const filesAfterNpmInstall = [file1Ts, file2Ts, indexDTS];
|
||||
|
||||
const initialProgram = newProgram(rootFiles, rootFiles.map(f => f.name), options);
|
||||
{
|
||||
assert.deepEqual(initialProgram.host.getTrace(),
|
||||
[
|
||||
"======== Resolving module 'a' from 'file1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'NodeJs'.",
|
||||
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
|
||||
"File 'node_modules/a.ts' does not exist.",
|
||||
"File 'node_modules/a.tsx' does not exist.",
|
||||
"File 'node_modules/a.d.ts' does not exist.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a/index.ts' does not exist.",
|
||||
"File 'node_modules/a/index.tsx' does not exist.",
|
||||
"File 'node_modules/a/index.d.ts' does not exist.",
|
||||
"File 'node_modules/@types/a.d.ts' does not exist.",
|
||||
"File 'node_modules/@types/a/package.json' does not exist.",
|
||||
"File 'node_modules/@types/a/index.d.ts' does not exist.",
|
||||
"Loading module 'a' from 'node_modules' folder, target file type 'JavaScript'.",
|
||||
"File 'node_modules/a.js' does not exist.",
|
||||
"File 'node_modules/a.jsx' does not exist.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a/index.js' does not exist.",
|
||||
"File 'node_modules/a/index.jsx' does not exist.",
|
||||
"======== Module name 'a' was not resolved. ========"
|
||||
],
|
||||
"initialProgram: execute module resolution normally.");
|
||||
|
||||
const initialProgramDiagnostics = initialProgram.getSemanticDiagnostics(initialProgram.getSourceFile("file1.ts"));
|
||||
assert(initialProgramDiagnostics.length === 1, `initialProgram: import should fail.`);
|
||||
}
|
||||
|
||||
const afterNpmInstallProgram = updateProgram(initialProgram, rootFiles.map(f => f.name), options, f => {
|
||||
f[1].text = f[1].text.updateReferences(`/// <reference no-default-lib="true"/>`);
|
||||
}, filesAfterNpmInstall);
|
||||
{
|
||||
assert.deepEqual(afterNpmInstallProgram.host.getTrace(),
|
||||
[
|
||||
"======== Resolving module 'a' from 'file1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'NodeJs'.",
|
||||
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
|
||||
"File 'node_modules/a.ts' does not exist.",
|
||||
"File 'node_modules/a.tsx' does not exist.",
|
||||
"File 'node_modules/a.d.ts' does not exist.",
|
||||
"File 'node_modules/a/package.json' does not exist.",
|
||||
"File 'node_modules/a/index.ts' does not exist.",
|
||||
"File 'node_modules/a/index.tsx' does not exist.",
|
||||
"File 'node_modules/a/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name 'a' was successfully resolved to 'node_modules/a/index.d.ts'. ========"
|
||||
],
|
||||
"afterNpmInstallProgram: execute module resolution normally.");
|
||||
|
||||
const afterNpmInstallProgramDiagnostics = afterNpmInstallProgram.getSemanticDiagnostics(afterNpmInstallProgram.getSourceFile("file1.ts"));
|
||||
assert(afterNpmInstallProgramDiagnostics.length === 0, `afterNpmInstallProgram: program is well-formed with import.`);
|
||||
}
|
||||
});
|
||||
|
||||
it("can reuse ambient module declarations from non-modified files", () => {
|
||||
const files = [
|
||||
{ name: "/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") },
|
||||
@@ -468,7 +536,206 @@ namespace ts {
|
||||
"File '/fs.jsx' does not exist.",
|
||||
"======== Module name 'fs' was not resolved. ========",
|
||||
], "should look for 'fs' again since node.d.ts was changed");
|
||||
});
|
||||
|
||||
it("can reuse module resolutions from non-modified files", () => {
|
||||
const files = [
|
||||
{ name: "a1.ts", text: SourceText.New("", "", "let x = 1;") },
|
||||
{ name: "a2.ts", text: SourceText.New("", "", "let x = 1;") },
|
||||
{ name: "b1.ts", text: SourceText.New("", "export class B { x: number; }", "") },
|
||||
{ name: "b2.ts", text: SourceText.New("", "export class B { x: number; }", "") },
|
||||
{ name: "node_modules/@types/typerefs1/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
|
||||
{ name: "node_modules/@types/typerefs2/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
|
||||
{
|
||||
name: "f1.ts",
|
||||
text:
|
||||
SourceText.New(
|
||||
`/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>${newLine}/// <reference no-default-lib="true"/>`,
|
||||
`import { B } from './b1';${newLine}export let BB = B;`,
|
||||
"declare module './b1' { interface B { y: string; } }")
|
||||
},
|
||||
{
|
||||
name: "f2.ts",
|
||||
text: SourceText.New(
|
||||
`/// <reference path="a2.ts"/>${newLine}/// <reference types="typerefs2"/>`,
|
||||
`import { B } from './b2';${newLine}import { BB } from './f1';`,
|
||||
"(new BB).x; (new BB).y;")
|
||||
},
|
||||
];
|
||||
|
||||
const options: CompilerOptions = { target: ScriptTarget.ES2015, traceResolution: true, moduleResolution: ModuleResolutionKind.Classic };
|
||||
const program_1 = newProgram(files, files.map(f => f.name), options);
|
||||
let expectedErrors = 0;
|
||||
{
|
||||
assert.deepEqual(program_1.host.getTrace(),
|
||||
[
|
||||
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs1/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========",
|
||||
"======== Resolving module './b1' from 'f1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'b1.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
|
||||
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
|
||||
"======== Resolving module './b2' from 'f2.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'b2.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './b2' was successfully resolved to 'b2.ts'. ========",
|
||||
"======== Resolving module './f1' from 'f2.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'f1.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './f1' was successfully resolved to 'f1.ts'. ========"
|
||||
],
|
||||
"program_1: execute module reoslution normally.");
|
||||
|
||||
const program_1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("f2.ts"));
|
||||
assert(program_1Diagnostics.length === expectedErrors, `initial program should be well-formed`);
|
||||
}
|
||||
const indexOfF1 = 6;
|
||||
const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, f => {
|
||||
const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>`);
|
||||
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
|
||||
});
|
||||
|
||||
{
|
||||
const program_2Diagnostics = program_2.getSemanticDiagnostics(program_2.getSourceFile("f2.ts"));
|
||||
assert(program_2Diagnostics.length === expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
|
||||
|
||||
assert.deepEqual(program_2.host.getTrace(), [
|
||||
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs1/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========",
|
||||
"======== Resolving module './b1' from 'f1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'b1.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
|
||||
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
|
||||
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
|
||||
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
|
||||
], "program_2: reuse module resolutions in f2 since it is unchanged");
|
||||
}
|
||||
|
||||
const program_3 = updateProgram(program_2, program_2.getRootFileNames(), options, f => {
|
||||
const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>`);
|
||||
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
|
||||
});
|
||||
|
||||
{
|
||||
const program_3Diagnostics = program_3.getSemanticDiagnostics(program_3.getSourceFile("f2.ts"));
|
||||
assert(program_3Diagnostics.length === expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
|
||||
|
||||
assert.deepEqual(program_3.host.getTrace(), [
|
||||
"======== Resolving module './b1' from 'f1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'b1.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
|
||||
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
|
||||
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
|
||||
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
|
||||
], "program_3: reuse module resolutions in f2 since it is unchanged");
|
||||
}
|
||||
|
||||
|
||||
const program_4 = updateProgram(program_3, program_3.getRootFileNames(), options, f => {
|
||||
const newSourceText = f[indexOfF1].text.updateReferences("");
|
||||
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
|
||||
});
|
||||
|
||||
{
|
||||
const program_4Diagnostics = program_4.getSemanticDiagnostics(program_4.getSourceFile("f2.ts"));
|
||||
assert(program_4Diagnostics.length === expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
|
||||
|
||||
assert.deepEqual(program_4.host.getTrace(), [
|
||||
"======== Resolving module './b1' from 'f1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'b1.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
|
||||
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
|
||||
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
|
||||
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
|
||||
], "program_4: reuse module resolutions in f2 since it is unchanged");
|
||||
}
|
||||
|
||||
const program_5 = updateProgram(program_4, program_4.getRootFileNames(), options, f => {
|
||||
const newSourceText = f[indexOfF1].text.updateImportsAndExports(`import { B } from './b1';`);
|
||||
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
|
||||
});
|
||||
|
||||
{
|
||||
const program_5Diagnostics = program_5.getSemanticDiagnostics(program_5.getSourceFile("f2.ts"));
|
||||
assert(program_5Diagnostics.length === ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
|
||||
|
||||
assert.deepEqual(program_5.host.getTrace(), [
|
||||
"======== Resolving module './b1' from 'f1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'b1.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========"
|
||||
], "program_5: exports do not affect program structure, so f2's resolutions are silently reused.");
|
||||
}
|
||||
|
||||
const program_6 = updateProgram(program_5, program_5.getRootFileNames(), options, f => {
|
||||
const newSourceText = f[indexOfF1].text.updateProgram("");
|
||||
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
|
||||
});
|
||||
|
||||
{
|
||||
const program_6Diagnostics = program_6.getSemanticDiagnostics(program_6.getSourceFile("f2.ts"));
|
||||
assert(program_6Diagnostics.length === expectedErrors, `import of BB in f1 fails.`);
|
||||
|
||||
assert.deepEqual(program_6.host.getTrace(), [
|
||||
"======== Resolving module './b1' from 'f1.ts'. ========",
|
||||
"Explicitly specified module resolution kind: 'Classic'.",
|
||||
"File 'b1.ts' exist - use it as a name resolution result.",
|
||||
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
|
||||
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
|
||||
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
|
||||
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
|
||||
], "program_6: reuse module resolutions in f2 since it is unchanged");
|
||||
}
|
||||
|
||||
const program_7 = updateProgram(program_6, program_6.getRootFileNames(), options, f => {
|
||||
const newSourceText = f[indexOfF1].text.updateImportsAndExports("");
|
||||
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
|
||||
});
|
||||
|
||||
{
|
||||
const program_7Diagnostics = program_7.getSemanticDiagnostics(program_7.getSourceFile("f2.ts"));
|
||||
assert(program_7Diagnostics.length === expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
|
||||
|
||||
assert.deepEqual(program_7.host.getTrace(), [
|
||||
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
|
||||
"Resolving with primary search path 'node_modules/@types'.",
|
||||
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
|
||||
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
|
||||
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
|
||||
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
|
||||
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
|
||||
], "program_7 should reuse module resolutions in f2 since it is unchanged");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace ts {
|
||||
after: [replaceIdentifiersNamedOldNameWithNewName]
|
||||
},
|
||||
compilerOptions: {
|
||||
newLine: ts.NewLineKind.CarriageReturnLineFeed
|
||||
newLine: NewLineKind.CarriageReturnLineFeed
|
||||
}
|
||||
}).outputText;
|
||||
});
|
||||
|
||||
@@ -552,7 +552,7 @@ namespace ts.server {
|
||||
// bump up the version if
|
||||
// - oldProgram is not set - this is a first time updateGraph is called
|
||||
// - newProgram is different from the old program and structure of the old program was not reused.
|
||||
if (!oldProgram || (this.program !== oldProgram && !oldProgram.structureIsReused)) {
|
||||
if (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely))) {
|
||||
hasChanges = true;
|
||||
if (oldProgram) {
|
||||
for (const f of oldProgram.getSourceFiles()) {
|
||||
|
||||
@@ -18,142 +18,150 @@ namespace ts.codefix {
|
||||
|
||||
switch (token.kind) {
|
||||
case ts.SyntaxKind.Identifier:
|
||||
switch (token.parent.kind) {
|
||||
case ts.SyntaxKind.VariableDeclaration:
|
||||
switch (token.parent.parent.parent.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
const forStatement = <ForStatement>token.parent.parent.parent;
|
||||
const forInitializer = <VariableDeclarationList>forStatement.initializer;
|
||||
if (forInitializer.declarations.length === 1) {
|
||||
return deleteNode(forInitializer);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forOfStatement = <ForOfStatement>token.parent.parent.parent;
|
||||
if (forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
|
||||
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
|
||||
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
// There is no valid fix in the case of:
|
||||
// for .. in
|
||||
return undefined;
|
||||
|
||||
case SyntaxKind.CatchClause:
|
||||
const catchClause = <CatchClause>token.parent.parent;
|
||||
const parameter = catchClause.variableDeclaration.getChildren()[0];
|
||||
return deleteNode(parameter);
|
||||
|
||||
default:
|
||||
const variableStatement = <VariableStatement>token.parent.parent.parent;
|
||||
if (variableStatement.declarationList.declarations.length === 1) {
|
||||
return deleteNode(variableStatement);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
}
|
||||
// TODO: #14885
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
|
||||
if (typeParameters.length === 1) {
|
||||
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
|
||||
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
|
||||
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
return deleteNodeRange(previousToken, nextToken);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case ts.SyntaxKind.Parameter:
|
||||
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
|
||||
if (functionDeclaration.parameters.length === 1) {
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where 'import a = A;'
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
|
||||
return deleteNode(importEquals);
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
const namedImports = <NamedImports>token.parent.parent;
|
||||
if (namedImports.elements.length === 1) {
|
||||
// Only 1 import and it is unused. So the entire declaration should be removed.
|
||||
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importSpec);
|
||||
}
|
||||
else {
|
||||
// delete import specifier
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where "import d, * as ns from './file'"
|
||||
// or "'import {a, b as ns} from './file'"
|
||||
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
|
||||
const importClause = <ImportClause>token.parent;
|
||||
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
|
||||
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
// import |d,| * as ns from './file'
|
||||
const start = importClause.name.getStart(sourceFile);
|
||||
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
|
||||
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
|
||||
// shift first non-whitespace position after comma to the start position of the node
|
||||
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
|
||||
}
|
||||
else {
|
||||
return deleteNode(importClause.name);
|
||||
}
|
||||
}
|
||||
|
||||
case SyntaxKind.NamespaceImport:
|
||||
const namespaceImport = <NamespaceImport>token.parent;
|
||||
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
|
||||
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
|
||||
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
|
||||
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
|
||||
return deleteRange({ pos: startPosition, end: namespaceImport.end });
|
||||
}
|
||||
return deleteRange(namespaceImport);
|
||||
}
|
||||
}
|
||||
break;
|
||||
return deleteIdentifier();
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
return deleteNode(token.parent);
|
||||
|
||||
default:
|
||||
return deleteDefault();
|
||||
}
|
||||
if (isDeclarationName(token)) {
|
||||
return deleteNode(token.parent);
|
||||
|
||||
function deleteDefault() {
|
||||
if (isDeclarationName(token)) {
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else if (isLiteralComputedPropertyDeclarationName(token)) {
|
||||
return deleteNode(token.parent.parent);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else if (isLiteralComputedPropertyDeclarationName(token)) {
|
||||
return deleteNode(token.parent.parent);
|
||||
|
||||
function deleteIdentifier(): CodeAction[] | undefined {
|
||||
switch (token.parent.kind) {
|
||||
case ts.SyntaxKind.VariableDeclaration:
|
||||
return deleteVariableDeclaration(<ts.VariableDeclaration>token.parent);
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
|
||||
if (typeParameters.length === 1) {
|
||||
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
|
||||
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
|
||||
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
return deleteNodeRange(previousToken, nextToken);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case ts.SyntaxKind.Parameter:
|
||||
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
|
||||
if (functionDeclaration.parameters.length === 1) {
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where 'import a = A;'
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
|
||||
return deleteNode(importEquals);
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
const namedImports = <NamedImports>token.parent.parent;
|
||||
if (namedImports.elements.length === 1) {
|
||||
// Only 1 import and it is unused. So the entire declaration should be removed.
|
||||
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importSpec);
|
||||
}
|
||||
else {
|
||||
// delete import specifier
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where "import d, * as ns from './file'"
|
||||
// or "'import {a, b as ns} from './file'"
|
||||
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
|
||||
const importClause = <ImportClause>token.parent;
|
||||
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
|
||||
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
// import |d,| * as ns from './file'
|
||||
const start = importClause.name.getStart(sourceFile);
|
||||
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
|
||||
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
|
||||
// shift first non-whitespace position after comma to the start position of the node
|
||||
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
|
||||
}
|
||||
else {
|
||||
return deleteNode(importClause.name);
|
||||
}
|
||||
}
|
||||
|
||||
case SyntaxKind.NamespaceImport:
|
||||
const namespaceImport = <NamespaceImport>token.parent;
|
||||
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
|
||||
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
|
||||
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
|
||||
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
|
||||
return deleteRange({ pos: startPosition, end: namespaceImport.end });
|
||||
}
|
||||
return deleteRange(namespaceImport);
|
||||
}
|
||||
|
||||
default:
|
||||
return deleteDefault();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
|
||||
// token.parent is a variableDeclaration
|
||||
function deleteVariableDeclaration(varDecl: ts.VariableDeclaration): CodeAction[] | undefined {
|
||||
switch (varDecl.parent.parent.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
const forStatement = <ForStatement>varDecl.parent.parent;
|
||||
const forInitializer = <VariableDeclarationList>forStatement.initializer;
|
||||
if (forInitializer.declarations.length === 1) {
|
||||
return deleteNode(forInitializer);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(varDecl);
|
||||
}
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forOfStatement = <ForOfStatement>varDecl.parent.parent;
|
||||
Debug.assert(forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList);
|
||||
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
|
||||
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
// There is no valid fix in the case of:
|
||||
// for .. in
|
||||
return undefined;
|
||||
|
||||
default:
|
||||
const variableStatement = <VariableStatement>varDecl.parent.parent;
|
||||
if (variableStatement.declarationList.declarations.length === 1) {
|
||||
return deleteNode(variableStatement);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(varDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteNode(n: Node) {
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace ts.formatting {
|
||||
public NoSpaceBeforeComma: Rule;
|
||||
|
||||
public SpaceAfterCertainKeywords: Rule;
|
||||
public NoSpaceAfterNewKeywordOnConstructorSignature: Rule;
|
||||
public SpaceAfterLetConstInVariableDeclaration: Rule;
|
||||
public NoSpaceBeforeOpenParenInFuncCall: Rule;
|
||||
public SpaceAfterFunctionInFuncDecl: Rule;
|
||||
@@ -334,6 +335,7 @@ namespace ts.formatting {
|
||||
this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceAfterNewKeywordOnConstructorSignature = new Rule(RuleDescriptor.create1(SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsConstructorSignatureContext), RuleAction.Delete));
|
||||
this.SpaceAfterLetConstInVariableDeclaration = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsStartOfVariableDeclarationList), RuleAction.Space));
|
||||
this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionCallOrNewContext, Rules.IsPreviousTokenNotComma), RuleAction.Delete));
|
||||
this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
@@ -530,7 +532,8 @@ namespace ts.formatting {
|
||||
this.SpaceBeforeAt,
|
||||
this.NoSpaceAfterAt,
|
||||
this.SpaceAfterDecorator,
|
||||
this.NoSpaceBeforeNonNullAssertionOperator
|
||||
this.NoSpaceBeforeNonNullAssertionOperator,
|
||||
this.NoSpaceAfterNewKeywordOnConstructorSignature
|
||||
];
|
||||
|
||||
// These rules are applied after high priority rules.
|
||||
@@ -881,6 +884,10 @@ namespace ts.formatting {
|
||||
return context.contextNode.kind === SyntaxKind.TypeLiteral; // && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
|
||||
}
|
||||
|
||||
static IsConstructorSignatureContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind === SyntaxKind.ConstructSignature;
|
||||
}
|
||||
|
||||
static IsTypeArgumentOrParameterOrAssertion(token: TextRangeWithKind, parent: Node): boolean {
|
||||
if (token.kind !== SyntaxKind.LessThanToken && token.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return false;
|
||||
|
||||
@@ -212,6 +212,10 @@ namespace ts.FindAllReferences {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decl.importClause) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { importClause } = decl;
|
||||
|
||||
const { namedBindings } = importClause;
|
||||
|
||||
@@ -4,4 +4,5 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(1,9): error TS7017: E
|
||||
==== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts (1 errors) ====
|
||||
var x = {}["hello"];
|
||||
~~~~~~~~~~~
|
||||
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
|
||||
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
|
||||
var y: string = { '': 'foo' }[''];
|
||||
@@ -1,5 +1,7 @@
|
||||
//// [noImplicitAnyStringIndexerOnObject.ts]
|
||||
var x = {}["hello"];
|
||||
var x = {}["hello"];
|
||||
var y: string = { '': 'foo' }[''];
|
||||
|
||||
//// [noImplicitAnyStringIndexerOnObject.js]
|
||||
var x = {}["hello"];
|
||||
var y = { '': 'foo' }[''];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @noimplicitany: true
|
||||
|
||||
var x = {}["hello"];
|
||||
var x = {}["hello"];
|
||||
var y: string = { '': 'foo' }[''];
|
||||
@@ -0,0 +1,11 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/15452
|
||||
|
||||
// @Filename: /a.ts
|
||||
////export const [|{| "isWriteAccess": true, "isDefinition": true |}x|] = 0;
|
||||
|
||||
// @Filename: /b.ts
|
||||
////import "./a";
|
||||
|
||||
verify.singleReferenceGroup("const x: 0");
|
||||
@@ -0,0 +1,9 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
/////*1*/interface Gourai { new () {} }
|
||||
/////*2*/type Stylet = { new () {} }
|
||||
format.document();
|
||||
goTo.marker("1");
|
||||
verify.currentLineContentIs("interface Gourai { new() {} }");
|
||||
goTo.marker("2");
|
||||
verify.currentLineContentIs("type Stylet = { new() {} }");
|
||||
Reference in New Issue
Block a user