Merge branch 'master' into spelling-correction
This commit is contained in:
commit
9eaf40bded
39 changed files with 1405 additions and 571 deletions
|
@ -1436,10 +1436,10 @@ namespace ts {
|
||||||
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
|
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTargetOfExportSpecifier(node: ExportSpecifier, dontResolveAlias?: boolean): Symbol {
|
function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) {
|
||||||
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
|
return node.parent.parent.moduleSpecifier ?
|
||||||
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node, dontResolveAlias) :
|
getExternalModuleMember(node.parent.parent, node, dontResolveAlias) :
|
||||||
resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
|
resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol {
|
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol {
|
||||||
|
@ -1457,7 +1457,7 @@ namespace ts {
|
||||||
case SyntaxKind.ImportSpecifier:
|
case SyntaxKind.ImportSpecifier:
|
||||||
return getTargetOfImportSpecifier(<ImportSpecifier>node, dontRecursivelyResolve);
|
return getTargetOfImportSpecifier(<ImportSpecifier>node, dontRecursivelyResolve);
|
||||||
case SyntaxKind.ExportSpecifier:
|
case SyntaxKind.ExportSpecifier:
|
||||||
return getTargetOfExportSpecifier(<ExportSpecifier>node, dontRecursivelyResolve);
|
return getTargetOfExportSpecifier(<ExportSpecifier>node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve);
|
||||||
case SyntaxKind.ExportAssignment:
|
case SyntaxKind.ExportAssignment:
|
||||||
return getTargetOfExportAssignment(<ExportAssignment>node, dontRecursivelyResolve);
|
return getTargetOfExportAssignment(<ExportAssignment>node, dontRecursivelyResolve);
|
||||||
case SyntaxKind.NamespaceExportDeclaration:
|
case SyntaxKind.NamespaceExportDeclaration:
|
||||||
|
@ -3757,10 +3757,7 @@ namespace ts {
|
||||||
exportSymbol = resolveName(node.parent, node.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node);
|
exportSymbol = resolveName(node.parent, node.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node);
|
||||||
}
|
}
|
||||||
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
|
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
|
||||||
const exportSpecifier = <ExportSpecifier>node.parent;
|
exportSymbol = getTargetOfExportSpecifier(<ExportSpecifier>node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
|
||||||
exportSymbol = (<ExportDeclaration>exportSpecifier.parent.parent).moduleSpecifier ?
|
|
||||||
getExternalModuleMember(<ExportDeclaration>exportSpecifier.parent.parent, exportSpecifier) :
|
|
||||||
resolveEntityName(exportSpecifier.propertyName || exportSpecifier.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
|
|
||||||
}
|
}
|
||||||
const result: Node[] = [];
|
const result: Node[] = [];
|
||||||
if (exportSymbol) {
|
if (exportSymbol) {
|
||||||
|
@ -7293,7 +7290,7 @@ namespace ts {
|
||||||
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
|
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
|
||||||
getPropertyNameForKnownSymbolName((<Identifier>(<PropertyAccessExpression>accessExpression.argumentExpression).name).text) :
|
getPropertyNameForKnownSymbolName((<Identifier>(<PropertyAccessExpression>accessExpression.argumentExpression).name).text) :
|
||||||
undefined;
|
undefined;
|
||||||
if (propName) {
|
if (propName !== undefined) {
|
||||||
const prop = getPropertyOfType(objectType, propName);
|
const prop = getPropertyOfType(objectType, propName);
|
||||||
if (prop) {
|
if (prop) {
|
||||||
if (accessExpression) {
|
if (accessExpression) {
|
||||||
|
@ -9264,25 +9261,39 @@ namespace ts {
|
||||||
let result = Ternary.True;
|
let result = Ternary.True;
|
||||||
const saveErrorInfo = errorInfo;
|
const saveErrorInfo = errorInfo;
|
||||||
|
|
||||||
outer: for (const t of targetSignatures) {
|
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
|
||||||
// Only elaborate errors from the first failure
|
// We instantiations of the same anonymous type (which typically will be the type of a method).
|
||||||
let shouldElaborateErrors = reportErrors;
|
// Simply do a pairwise comparison of the signatures in the two signature lists instead of the
|
||||||
for (const s of sourceSignatures) {
|
// much more expensive N * M comparison matrix we explore below.
|
||||||
const related = signatureRelatedTo(s, t, shouldElaborateErrors);
|
for (let i = 0; i < targetSignatures.length; i++) {
|
||||||
if (related) {
|
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], reportErrors);
|
||||||
result &= related;
|
if (!related) {
|
||||||
errorInfo = saveErrorInfo;
|
return Ternary.False;
|
||||||
continue outer;
|
|
||||||
}
|
}
|
||||||
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) {
|
if (shouldElaborateErrors) {
|
||||||
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
|
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
|
||||||
typeToString(source),
|
typeToString(source),
|
||||||
signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
|
signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
|
||||||
|
}
|
||||||
|
return Ternary.False;
|
||||||
}
|
}
|
||||||
return Ternary.False;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -13273,6 +13284,8 @@ namespace ts {
|
||||||
let attributesTable = createMap<Symbol>();
|
let attributesTable = createMap<Symbol>();
|
||||||
let spread: Type = emptyObjectType;
|
let spread: Type = emptyObjectType;
|
||||||
let attributesArray: Symbol[] = [];
|
let attributesArray: Symbol[] = [];
|
||||||
|
let hasSpreadAnyType = false;
|
||||||
|
|
||||||
for (const attributeDecl of attributes.properties) {
|
for (const attributeDecl of attributes.properties) {
|
||||||
const member = attributeDecl.symbol;
|
const member = attributeDecl.symbol;
|
||||||
if (isJsxAttribute(attributeDecl)) {
|
if (isJsxAttribute(attributeDecl)) {
|
||||||
|
@ -13301,31 +13314,33 @@ namespace ts {
|
||||||
const exprType = checkExpression(attributeDecl.expression);
|
const exprType = checkExpression(attributeDecl.expression);
|
||||||
if (!isValidSpreadType(exprType)) {
|
if (!isValidSpreadType(exprType)) {
|
||||||
error(attributeDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
|
error(attributeDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
|
||||||
return anyType;
|
hasSpreadAnyType = true;
|
||||||
}
|
}
|
||||||
if (isTypeAny(exprType)) {
|
if (isTypeAny(exprType)) {
|
||||||
return anyType;
|
hasSpreadAnyType = true;
|
||||||
}
|
}
|
||||||
spread = getSpreadType(spread, exprType);
|
spread = getSpreadType(spread, exprType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spread !== emptyObjectType) {
|
if (!hasSpreadAnyType) {
|
||||||
if (attributesArray.length > 0) {
|
if (spread !== emptyObjectType) {
|
||||||
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
|
if (attributesArray.length > 0) {
|
||||||
attributesArray = [];
|
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
|
||||||
attributesTable = createMap<Symbol>();
|
attributesArray = [];
|
||||||
}
|
attributesTable = createMap<Symbol>();
|
||||||
attributesArray = getPropertiesOfType(spread);
|
|
||||||
}
|
|
||||||
|
|
||||||
attributesTable = createMap<Symbol>();
|
|
||||||
if (attributesArray) {
|
|
||||||
forEach(attributesArray, (attr) => {
|
|
||||||
if (!filter || filter(attr)) {
|
|
||||||
attributesTable.set(attr.name, attr);
|
|
||||||
}
|
}
|
||||||
});
|
attributesArray = getPropertiesOfType(spread);
|
||||||
|
}
|
||||||
|
|
||||||
|
attributesTable = createMap<Symbol>();
|
||||||
|
if (attributesArray) {
|
||||||
|
forEach(attributesArray, (attr) => {
|
||||||
|
if (!filter || filter(attr)) {
|
||||||
|
attributesTable.set(attr.name, attr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle children attribute
|
// Handle children attribute
|
||||||
|
@ -13349,7 +13364,7 @@ namespace ts {
|
||||||
// Error if there is a attribute named "children" and children element.
|
// Error if there is a attribute named "children" and children element.
|
||||||
// This is because children element will overwrite the value from attributes
|
// This is because children element will overwrite the value from attributes
|
||||||
const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
|
const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
|
||||||
if (jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
|
if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
|
||||||
if (attributesTable.has(jsxChildrenPropertyName)) {
|
if (attributesTable.has(jsxChildrenPropertyName)) {
|
||||||
error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName);
|
error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName);
|
||||||
}
|
}
|
||||||
|
@ -13363,7 +13378,7 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return createJsxAttributesType(attributes.symbol, attributesTable);
|
return hasSpreadAnyType ? anyType : createJsxAttributesType(attributes.symbol, attributesTable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create anonymous type from given attributes symbol table.
|
* Create anonymous type from given attributes symbol table.
|
||||||
|
|
|
@ -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
|
* Iterates through `array` by index and performs the callback on each element of array until the callback
|
||||||
* returns a falsey value, then returns false.
|
* returns a falsey value, then returns false.
|
||||||
|
|
|
@ -3211,9 +3211,16 @@
|
||||||
},
|
},
|
||||||
"Scoped package detected, looking in '{0}'": {
|
"Scoped package detected, looking in '{0}'": {
|
||||||
"category": "Message",
|
"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.": {
|
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||||
"category": "Error",
|
"category": "Error",
|
||||||
"code": 7005
|
"code": 7005
|
||||||
|
|
|
@ -1440,6 +1440,44 @@ namespace ts {
|
||||||
: node;
|
: 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[]) {
|
export function createEnumDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, members: EnumMember[]) {
|
||||||
const node = <EnumDeclaration>createSynthesizedNode(SyntaxKind.EnumDeclaration);
|
const node = <EnumDeclaration>createSynthesizedNode(SyntaxKind.EnumDeclaration);
|
||||||
node.decorators = asNodeArray(decorators);
|
node.decorators = asNodeArray(decorators);
|
||||||
|
|
|
@ -298,6 +298,7 @@ namespace ts {
|
||||||
let diagnosticsProducingTypeChecker: TypeChecker;
|
let diagnosticsProducingTypeChecker: TypeChecker;
|
||||||
let noDiagnosticsTypeChecker: TypeChecker;
|
let noDiagnosticsTypeChecker: TypeChecker;
|
||||||
let classifiableNames: Map<string>;
|
let classifiableNames: Map<string>;
|
||||||
|
let modifiedFilePaths: Path[] | undefined;
|
||||||
|
|
||||||
const cachedSemanticDiagnosticsForFile: DiagnosticCache = {};
|
const cachedSemanticDiagnosticsForFile: DiagnosticCache = {};
|
||||||
const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {};
|
const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {};
|
||||||
|
@ -367,7 +368,8 @@ namespace ts {
|
||||||
// used to track cases when two file names differ only in casing
|
// used to track cases when two file names differ only in casing
|
||||||
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
|
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));
|
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
|
||||||
|
|
||||||
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
|
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
|
||||||
|
@ -476,89 +478,114 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OldProgramState {
|
interface OldProgramState {
|
||||||
program: Program;
|
program: Program | undefined;
|
||||||
file: SourceFile;
|
file: SourceFile;
|
||||||
|
/** The collection of paths modified *since* the old program. */
|
||||||
modifiedFilePaths: Path[];
|
modifiedFilePaths: Path[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState?: OldProgramState) {
|
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState: OldProgramState) {
|
||||||
if (!oldProgramState && !file.ambientModuleNames.length) {
|
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
|
||||||
// if old program state is not supplied and file does not contain locally defined ambient modules
|
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
|
||||||
// then the best we can do is fallback to the default logic
|
// the best we can do is fallback to the default logic.
|
||||||
return resolveModuleNamesWorker(moduleNames, containingFile);
|
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
|
// - file has local declarations for ambient modules
|
||||||
// OR
|
|
||||||
// - old program state is available
|
// - old program state is available
|
||||||
// OR
|
// With this information, we can infer some module resolutions without performing resolution.
|
||||||
// - 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.
|
|
||||||
|
|
||||||
// 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[];
|
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
|
* The indexing of elements in this list matches that of `moduleNames`.
|
||||||
// so later we can split results to resolutions of modules and resolutions of module augmentations.
|
*
|
||||||
|
* 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[];
|
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>{};
|
const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
|
||||||
|
|
||||||
|
|
||||||
for (let i = 0; i < moduleNames.length; i++) {
|
for (let i = 0; i < moduleNames.length; i++) {
|
||||||
const moduleName = moduleNames[i];
|
const moduleName = moduleNames[i];
|
||||||
// module name is known to be resolved to ambient module if
|
// If we want to reuse resolutions more aggressively, we can refine this to check for whether the
|
||||||
// - module name is contained in the list of ambient modules that are locally declared in the file
|
// text of the corresponding modulenames has changed.
|
||||||
// - in the old program module name was resolved to ambient module whose declaration is in non-modified file
|
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)
|
// (so the same module declaration will land in the new program)
|
||||||
let isKnownToResolveToAmbientModule = false;
|
let resolvesToAmbientModuleInNonModifiedFile = false;
|
||||||
if (contains(file.ambientModuleNames, moduleName)) {
|
if (contains(file.ambientModuleNames, moduleName)) {
|
||||||
isKnownToResolveToAmbientModule = true;
|
resolvesToAmbientModuleInNonModifiedFile = true;
|
||||||
if (isTraceEnabled(options, host)) {
|
if (isTraceEnabled(options, host)) {
|
||||||
trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, containingFile);
|
trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, containingFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
isKnownToResolveToAmbientModule = checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
|
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isKnownToResolveToAmbientModule) {
|
if (resolvesToAmbientModuleInNonModifiedFile) {
|
||||||
if (!unknownModuleNames) {
|
(result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
else if (unknownModuleNames) {
|
else {
|
||||||
// found unknown module name and we are already using separate list for those - add it to the list
|
// Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
|
||||||
unknownModuleNames.push(moduleName);
|
(unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unknownModuleNames) {
|
const resolutions = unknownModuleNames && unknownModuleNames.length
|
||||||
// we've looked throught the list but have not seen any predicted resolution
|
|
||||||
// use default logic
|
|
||||||
return resolveModuleNamesWorker(moduleNames, containingFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolutions = unknownModuleNames.length
|
|
||||||
? resolveModuleNamesWorker(unknownModuleNames, containingFile)
|
? resolveModuleNamesWorker(unknownModuleNames, containingFile)
|
||||||
: emptyArray;
|
: 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;
|
let j = 0;
|
||||||
for (let i = 0; i < result.length; i++) {
|
for (let i = 0; i < result.length; i++) {
|
||||||
if (result[i] === predictedToResolveToAmbientModuleMarker) {
|
if (result[i]) {
|
||||||
result[i] = undefined;
|
// `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 {
|
else {
|
||||||
result[i] = resolutions[j];
|
result[i] = resolutions[j];
|
||||||
|
@ -566,18 +593,18 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Debug.assert(j === resolutions.length);
|
Debug.assert(j === resolutions.length);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
function checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState?: OldProgramState): boolean {
|
// If we change our policy of rechecking failed lookups on each program create,
|
||||||
if (!oldProgramState) {
|
// we should adjust the value returned here.
|
||||||
return false;
|
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean {
|
||||||
}
|
|
||||||
const resolutionToFile = getResolvedModule(oldProgramState.file, moduleName);
|
const resolutionToFile = getResolvedModule(oldProgramState.file, moduleName);
|
||||||
if (resolutionToFile) {
|
if (resolutionToFile) {
|
||||||
// module used to be resolved to file - ignore it
|
// module used to be resolved to file - ignore it
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ambientModule = oldProgram.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
|
const ambientModule = oldProgramState.program && oldProgramState.program.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
|
||||||
if (!(ambientModule && ambientModule.declarations)) {
|
if (!(ambientModule && ambientModule.declarations)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -599,99 +626,107 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryReuseStructureFromOldProgram(): boolean {
|
function tryReuseStructureFromOldProgram(): StructureIsReused {
|
||||||
if (!oldProgram) {
|
if (!oldProgram) {
|
||||||
return false;
|
return StructureIsReused.Not;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check properties that can affect structure of the program or module resolution strategy
|
// check properties that can affect structure of the program or module resolution strategy
|
||||||
// if any of these properties has changed - structure cannot be reused
|
// if any of these properties has changed - structure cannot be reused
|
||||||
const oldOptions = oldProgram.getCompilerOptions();
|
const oldOptions = oldProgram.getCompilerOptions();
|
||||||
if (changesAffectModuleResolution(oldOptions, options)) {
|
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
|
// there is an old program, check if we can reuse its structure
|
||||||
const oldRootNames = oldProgram.getRootFileNames();
|
const oldRootNames = oldProgram.getRootFileNames();
|
||||||
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
|
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
|
||||||
return false;
|
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arrayIsEqualTo(options.types, oldOptions.types)) {
|
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
|
// check if program source files has changed in the way that can affect structure of the program
|
||||||
const newSourceFiles: SourceFile[] = [];
|
const newSourceFiles: SourceFile[] = [];
|
||||||
const filePaths: Path[] = [];
|
const filePaths: Path[] = [];
|
||||||
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
|
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
|
||||||
|
oldProgram.structureIsReused = StructureIsReused.Completely;
|
||||||
|
|
||||||
for (const oldSourceFile of oldProgram.getSourceFiles()) {
|
for (const oldSourceFile of oldProgram.getSourceFiles()) {
|
||||||
let newSourceFile = host.getSourceFileByPath
|
const newSourceFile = host.getSourceFileByPath
|
||||||
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
|
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
|
||||||
: host.getSourceFile(oldSourceFile.fileName, options.target);
|
: host.getSourceFile(oldSourceFile.fileName, options.target);
|
||||||
|
|
||||||
if (!newSourceFile) {
|
if (!newSourceFile) {
|
||||||
return false;
|
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||||
}
|
}
|
||||||
|
|
||||||
newSourceFile.path = oldSourceFile.path;
|
newSourceFile.path = oldSourceFile.path;
|
||||||
filePaths.push(newSourceFile.path);
|
filePaths.push(newSourceFile.path);
|
||||||
|
|
||||||
if (oldSourceFile !== newSourceFile) {
|
if (oldSourceFile !== newSourceFile) {
|
||||||
|
// The `newSourceFile` object was created for the new program.
|
||||||
|
|
||||||
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
|
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
|
||||||
// value of no-default-lib has changed
|
// value of no-default-lib has changed
|
||||||
// this will affect if default library is injected into the list of files
|
// this will affect if default library is injected into the list of files
|
||||||
return false;
|
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check tripleslash references
|
// check tripleslash references
|
||||||
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
|
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
|
||||||
// tripleslash references has changed
|
// tripleslash references has changed
|
||||||
return false;
|
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check imports and module augmentations
|
// check imports and module augmentations
|
||||||
collectExternalModuleReferences(newSourceFile);
|
collectExternalModuleReferences(newSourceFile);
|
||||||
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
|
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
|
||||||
// imports has changed
|
// imports has changed
|
||||||
return false;
|
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||||
}
|
}
|
||||||
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
|
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
|
||||||
// moduleAugmentations has changed
|
// moduleAugmentations has changed
|
||||||
return false;
|
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
|
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
|
||||||
// 'types' references has changed
|
// 'types' references has changed
|
||||||
return false;
|
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tentatively approve the file
|
// tentatively approve the file
|
||||||
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
|
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
|
// if file has passed all checks it should be safe to reuse it
|
||||||
newSourceFiles.push(newSourceFile);
|
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
|
// try to verify results of module resolution
|
||||||
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
|
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
|
||||||
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
|
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
|
||||||
if (resolveModuleNamesWorker) {
|
if (resolveModuleNamesWorker) {
|
||||||
const moduleNames = map(concatenate(newSourceFile.imports, newSourceFile.moduleAugmentations), getTextOfLiteral);
|
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
|
// ensure that module resolution results are still correct
|
||||||
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
|
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
|
||||||
if (resolutionsChanged) {
|
if (resolutionsChanged) {
|
||||||
return false;
|
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||||
|
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (resolveTypeReferenceDirectiveNamesWorker) {
|
if (resolveTypeReferenceDirectiveNamesWorker) {
|
||||||
|
@ -700,12 +735,17 @@ namespace ts {
|
||||||
// ensure that types resolutions are still correct
|
// ensure that types resolutions are still correct
|
||||||
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
|
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
|
||||||
if (resolutionsChanged) {
|
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
|
// update fileName -> file mapping
|
||||||
|
@ -720,9 +760,8 @@ namespace ts {
|
||||||
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
|
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
|
||||||
}
|
}
|
||||||
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
|
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
|
||||||
oldProgram.structureIsReused = true;
|
|
||||||
|
|
||||||
return true;
|
return oldProgram.structureIsReused = StructureIsReused.Completely;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
|
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
|
||||||
|
@ -1528,11 +1567,11 @@ namespace ts {
|
||||||
function processImportedModules(file: SourceFile) {
|
function processImportedModules(file: SourceFile) {
|
||||||
collectExternalModuleReferences(file);
|
collectExternalModuleReferences(file);
|
||||||
if (file.imports.length || file.moduleAugmentations.length) {
|
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.
|
// 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 nonGlobalAugmentation = filter(file.moduleAugmentations, (moduleAugmentation) => moduleAugmentation.kind === SyntaxKind.StringLiteral);
|
||||||
const moduleNames = map(concatenate(file.imports, nonGlobalAugmentation), getTextOfLiteral);
|
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);
|
Debug.assert(resolutions.length === moduleNames.length);
|
||||||
for (let i = 0; i < moduleNames.length; i++) {
|
for (let i = 0; i < moduleNames.length; i++) {
|
||||||
const resolution = resolutions[i];
|
const resolution = resolutions[i];
|
||||||
|
|
|
@ -2408,7 +2408,14 @@ namespace ts {
|
||||||
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
|
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
|
||||||
/* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean;
|
/* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean;
|
||||||
// For testing purposes only.
|
// 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 {
|
export interface CustomTransformers {
|
||||||
|
|
|
@ -122,9 +122,8 @@ namespace ts {
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
export function hasChangesInResolutions<T>(names: string[], newResolutions: T[], oldResolutions: Map<T>, comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
|
export function hasChangesInResolutions<T>(names: string[], newResolutions: T[], oldResolutions: Map<T>, comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
|
||||||
if (names.length !== newResolutions.length) {
|
Debug.assert(names.length === newResolutions.length);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < names.length; i++) {
|
for (let i = 0; i < names.length; i++) {
|
||||||
const newResolution = newResolutions[i];
|
const newResolution = newResolutions[i];
|
||||||
const oldResolution = oldResolutions && oldResolutions.get(names[i]);
|
const oldResolution = oldResolutions && oldResolutions.get(names[i]);
|
||||||
|
|
|
@ -201,14 +201,13 @@ namespace ts {
|
||||||
assert.isTrue(diags.length === 1, "one diagnostic expected");
|
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.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);
|
fileMap.set(imported.name, imported);
|
||||||
fileExistsCalledForBar = false;
|
fileExistsCalledForBar = false;
|
||||||
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
|
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
|
||||||
|
|
||||||
diags = project.getLanguageService().getSemanticDiagnostics(root.name);
|
diags = project.getLanguageService().getSemanticDiagnostics(root.name);
|
||||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||||
assert.isTrue(diags.length === 0);
|
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");
|
assert.equal(diagnostics1.length, 1, "expected one diagnostic");
|
||||||
|
|
||||||
createProgram(names, {}, compilerHost, program1);
|
createProgram(names, {}, compilerHost, program1);
|
||||||
assert.isTrue(program1.structureIsReused);
|
assert.isTrue(program1.structureIsReused === StructureIsReused.Completely);
|
||||||
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
|
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
|
||||||
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
|
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
|
||||||
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
|
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
|
||||||
|
|
|
@ -159,12 +159,14 @@ namespace ts {
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void) {
|
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void, newTexts?: NamedSourceText[]) {
|
||||||
const texts: NamedSourceText[] = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
|
if (!newTexts) {
|
||||||
updater(texts);
|
newTexts = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
|
||||||
const host = createTestCompilerHost(texts, options.target, oldProgram);
|
}
|
||||||
|
updater(newTexts);
|
||||||
|
const host = createTestCompilerHost(newTexts, options.target, oldProgram);
|
||||||
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
|
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
|
||||||
program.sourceTexts = texts;
|
program.sourceTexts = newTexts;
|
||||||
program.host = host;
|
program.host = host;
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
@ -217,13 +219,15 @@ namespace ts {
|
||||||
|
|
||||||
describe("Reuse program structure", () => {
|
describe("Reuse program structure", () => {
|
||||||
const target = ScriptTarget.Latest;
|
const target = ScriptTarget.Latest;
|
||||||
const files = [
|
const files: NamedSourceText[] = [
|
||||||
{ name: "a.ts", text: SourceText.New(
|
{
|
||||||
`
|
name: "a.ts", text: SourceText.New(
|
||||||
|
`
|
||||||
/// <reference path='b.ts'/>
|
/// <reference path='b.ts'/>
|
||||||
/// <reference path='non-existing-file.ts'/>
|
/// <reference path='non-existing-file.ts'/>
|
||||||
/// <reference types="typerefs" />
|
/// <reference types="typerefs" />
|
||||||
`, "", `var x = 1`) },
|
`, "", `var x = 1`)
|
||||||
|
},
|
||||||
{ name: "b.ts", text: SourceText.New(`/// <reference path='c.ts'/>`, "", `var y = 2`) },
|
{ name: "b.ts", text: SourceText.New(`/// <reference path='c.ts'/>`, "", `var y = 2`) },
|
||||||
{ name: "c.ts", text: SourceText.New("", "", `var z = 1;`) },
|
{ name: "c.ts", text: SourceText.New("", "", `var z = 1;`) },
|
||||||
{ name: "types/typerefs/index.d.ts", text: SourceText.New("", "", `declare let z: number;`) },
|
{ 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 => {
|
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||||
files[0].text = files[0].text.updateProgram("var x = 100");
|
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 program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||||
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||||
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
|
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
|
||||||
|
@ -245,7 +249,7 @@ namespace ts {
|
||||||
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||||
files[0].text = files[0].text.updateProgram("var x = 100");
|
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 program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||||
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
|
||||||
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
|
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
|
||||||
|
@ -259,19 +263,19 @@ namespace ts {
|
||||||
`;
|
`;
|
||||||
files[0].text = files[0].text.updateReferences(newReferences);
|
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", () => {
|
it("fails if change affects type references", () => {
|
||||||
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
|
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
|
||||||
updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop);
|
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", () => {
|
it("succeeds if change doesn't affect type references", () => {
|
||||||
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
|
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
|
||||||
updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop);
|
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", () => {
|
it("fails if change affects imports", () => {
|
||||||
|
@ -279,7 +283,7 @@ namespace ts {
|
||||||
updateProgram(program_1, ["a.ts"], { target }, files => {
|
updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||||
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
|
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", () => {
|
it("fails if change affects type directives", () => {
|
||||||
|
@ -291,25 +295,25 @@ namespace ts {
|
||||||
/// <reference types="typerefs1" />`;
|
/// <reference types="typerefs1" />`;
|
||||||
files[0].text = files[0].text.updateReferences(newReferences);
|
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", () => {
|
it("fails if module kind changes", () => {
|
||||||
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
|
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
|
||||||
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
|
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", () => {
|
it("fails if rootdir changes", () => {
|
||||||
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" });
|
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);
|
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", () => {
|
it("fails if config path changes", () => {
|
||||||
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
|
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);
|
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", () => {
|
it("resolution cache follows imports", () => {
|
||||||
|
@ -328,7 +332,7 @@ namespace ts {
|
||||||
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
|
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
|
||||||
files[0].text = files[0].text.updateProgram("var x = 2");
|
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
|
// content of resolution cache should not change
|
||||||
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
|
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 => {
|
const program_3 = updateProgram(program_2, ["a.ts"], options, files => {
|
||||||
files[0].text = files[0].text.updateImportsAndExports("");
|
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);
|
checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
|
||||||
|
|
||||||
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
|
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
|
||||||
|
@ -347,7 +351,7 @@ namespace ts {
|
||||||
`;
|
`;
|
||||||
files[0].text = files[0].text.updateImportsAndExports(newImports);
|
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 }));
|
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 => {
|
const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
|
||||||
files[0].text = files[0].text.updateProgram("var x = 2");
|
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
|
// content of resolution cache should not change
|
||||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
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("");
|
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);
|
checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
|
||||||
|
|
||||||
updateProgram(program_3, ["/a.ts"], options, files => {
|
updateProgram(program_3, ["/a.ts"], options, files => {
|
||||||
|
@ -385,10 +389,74 @@ namespace ts {
|
||||||
`;
|
`;
|
||||||
files[0].text = files[0].text.updateReferences(newReferences);
|
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 } }));
|
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", () => {
|
it("can reuse ambient module declarations from non-modified files", () => {
|
||||||
const files = [
|
const files = [
|
||||||
{ name: "/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") },
|
{ 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.",
|
"File '/fs.jsx' does not exist.",
|
||||||
"======== Module name 'fs' was not resolved. ========",
|
"======== Module name 'fs' was not resolved. ========",
|
||||||
], "should look for 'fs' again since node.d.ts was changed");
|
], "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");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,9 @@ namespace ts {
|
||||||
transformers: {
|
transformers: {
|
||||||
before: [replaceUndefinedWithVoid0],
|
before: [replaceUndefinedWithVoid0],
|
||||||
after: [replaceIdentifiersNamedOldNameWithNewName]
|
after: [replaceIdentifiersNamedOldNameWithNewName]
|
||||||
|
},
|
||||||
|
compilerOptions: {
|
||||||
|
newLine: NewLineKind.CarriageReturnLineFeed
|
||||||
}
|
}
|
||||||
}).outputText;
|
}).outputText;
|
||||||
});
|
});
|
||||||
|
|
|
@ -552,7 +552,7 @@ namespace ts.server {
|
||||||
// bump up the version if
|
// bump up the version if
|
||||||
// - oldProgram is not set - this is a first time updateGraph is called
|
// - 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.
|
// - 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;
|
hasChanges = true;
|
||||||
if (oldProgram) {
|
if (oldProgram) {
|
||||||
for (const f of oldProgram.getSourceFiles()) {
|
for (const f of oldProgram.getSourceFiles()) {
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
/* @internal */
|
/* @internal */
|
||||||
namespace ts.codefix {
|
namespace ts.codefix {
|
||||||
|
registerCodeFix({
|
||||||
|
errorCodes: [
|
||||||
|
Diagnostics.Cannot_find_name_0.code,
|
||||||
|
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code,
|
||||||
|
Diagnostics.Cannot_find_namespace_0.code,
|
||||||
|
Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
|
||||||
|
],
|
||||||
|
getCodeActions: getImportCodeActions
|
||||||
|
});
|
||||||
|
|
||||||
type ImportCodeActionKind = "CodeChange" | "InsertingIntoExistingImport" | "NewImport";
|
type ImportCodeActionKind = "CodeChange" | "InsertingIntoExistingImport" | "NewImport";
|
||||||
interface ImportCodeAction extends CodeAction {
|
interface ImportCodeAction extends CodeAction {
|
||||||
|
@ -113,466 +122,458 @@ namespace ts.codefix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCodeFix({
|
function getImportCodeActions(context: CodeFixContext): ImportCodeAction[] {
|
||||||
errorCodes: [
|
const sourceFile = context.sourceFile;
|
||||||
Diagnostics.Cannot_find_name_0.code,
|
const checker = context.program.getTypeChecker();
|
||||||
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code,
|
const allSourceFiles = context.program.getSourceFiles();
|
||||||
Diagnostics.Cannot_find_namespace_0.code,
|
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
|
||||||
Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
|
|
||||||
],
|
|
||||||
getCodeActions: (context: CodeFixContext) => {
|
|
||||||
const sourceFile = context.sourceFile;
|
|
||||||
const checker = context.program.getTypeChecker();
|
|
||||||
const allSourceFiles = context.program.getSourceFiles();
|
|
||||||
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
|
|
||||||
|
|
||||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||||
const name = token.getText();
|
const name = token.getText();
|
||||||
const symbolIdActionMap = new ImportCodeActionMap();
|
const symbolIdActionMap = new ImportCodeActionMap();
|
||||||
|
|
||||||
// this is a module id -> module import declaration map
|
// this is a module id -> module import declaration map
|
||||||
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
|
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
|
||||||
let lastImportDeclaration: Node;
|
let lastImportDeclaration: Node;
|
||||||
|
|
||||||
const currentTokenMeaning = getMeaningFromLocation(token);
|
const currentTokenMeaning = getMeaningFromLocation(token);
|
||||||
if (context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) {
|
if (context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) {
|
||||||
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
|
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
|
||||||
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
|
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidateModules = checker.getAmbientModules();
|
||||||
|
for (const otherSourceFile of allSourceFiles) {
|
||||||
|
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
|
||||||
|
candidateModules.push(otherSourceFile.symbol);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const candidateModules = checker.getAmbientModules();
|
for (const moduleSymbol of candidateModules) {
|
||||||
for (const otherSourceFile of allSourceFiles) {
|
context.cancellationToken.throwIfCancellationRequested();
|
||||||
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
|
|
||||||
candidateModules.push(otherSourceFile.symbol);
|
// check the default export
|
||||||
|
const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol);
|
||||||
|
if (defaultExport) {
|
||||||
|
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
|
||||||
|
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
|
||||||
|
// check if this symbol is already used
|
||||||
|
const symbolId = getUniqueSymbolId(localSymbol);
|
||||||
|
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefault*/ true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const moduleSymbol of candidateModules) {
|
// check exports with the same name
|
||||||
context.cancellationToken.throwIfCancellationRequested();
|
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
|
||||||
|
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
|
||||||
|
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
|
||||||
|
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check the default export
|
return symbolIdActionMap.getAllActions();
|
||||||
const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol);
|
|
||||||
if (defaultExport) {
|
function getImportDeclarations(moduleSymbol: Symbol) {
|
||||||
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
|
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
|
||||||
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
|
|
||||||
// check if this symbol is already used
|
const cached = cachedImportDeclarations[moduleSymbolId];
|
||||||
const symbolId = getUniqueSymbolId(localSymbol);
|
if (cached) {
|
||||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefault*/ true));
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
|
||||||
|
for (const importModuleSpecifier of sourceFile.imports) {
|
||||||
|
const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier);
|
||||||
|
if (importSymbol === moduleSymbol) {
|
||||||
|
existingDeclarations.push(getImportDeclaration(importModuleSpecifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedImportDeclarations[moduleSymbolId] = existingDeclarations;
|
||||||
|
return existingDeclarations;
|
||||||
|
|
||||||
|
function getImportDeclaration(moduleSpecifier: LiteralExpression) {
|
||||||
|
let node: Node = moduleSpecifier;
|
||||||
|
while (node) {
|
||||||
|
if (node.kind === SyntaxKind.ImportDeclaration) {
|
||||||
|
return <ImportDeclaration>node;
|
||||||
}
|
}
|
||||||
|
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||||
|
return <ImportEqualsDeclaration>node;
|
||||||
|
}
|
||||||
|
node = node.parent;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check exports with the same name
|
function getUniqueSymbolId(symbol: Symbol) {
|
||||||
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
|
if (symbol.flags & SymbolFlags.Alias) {
|
||||||
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
|
return getSymbolId(checker.getAliasedSymbol(symbol));
|
||||||
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
|
}
|
||||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
|
return getSymbolId(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
|
||||||
|
const declarations = symbol.getDeclarations();
|
||||||
|
return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean, isNamespaceImport?: boolean): ImportCodeAction[] {
|
||||||
|
const existingDeclarations = getImportDeclarations(moduleSymbol);
|
||||||
|
if (existingDeclarations.length > 0) {
|
||||||
|
// With an existing import statement, there are more than one actions the user can do.
|
||||||
|
return getCodeActionsForExistingImport(existingDeclarations);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [getCodeActionForNewImport()];
|
||||||
}
|
}
|
||||||
|
|
||||||
return symbolIdActionMap.getAllActions();
|
function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] {
|
||||||
|
const actions: ImportCodeAction[] = [];
|
||||||
|
|
||||||
function getImportDeclarations(moduleSymbol: Symbol) {
|
// It is possible that multiple import statements with the same specifier exist in the file.
|
||||||
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
|
// e.g.
|
||||||
|
//
|
||||||
const cached = cachedImportDeclarations[moduleSymbolId];
|
// import * as ns from "foo";
|
||||||
if (cached) {
|
// import { member1, member2 } from "foo";
|
||||||
return cached;
|
//
|
||||||
}
|
// member3/**/ <-- cusor here
|
||||||
|
//
|
||||||
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
|
// in this case we should provie 2 actions:
|
||||||
for (const importModuleSpecifier of sourceFile.imports) {
|
// 1. change "member3" to "ns.member3"
|
||||||
const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier);
|
// 2. add "member3" to the second import statement's import list
|
||||||
if (importSymbol === moduleSymbol) {
|
// and it is up to the user to decide which one fits best.
|
||||||
existingDeclarations.push(getImportDeclaration(importModuleSpecifier));
|
let namespaceImportDeclaration: ImportDeclaration | ImportEqualsDeclaration;
|
||||||
}
|
let namedImportDeclaration: ImportDeclaration;
|
||||||
}
|
let existingModuleSpecifier: string;
|
||||||
cachedImportDeclarations[moduleSymbolId] = existingDeclarations;
|
for (const declaration of declarations) {
|
||||||
return existingDeclarations;
|
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
||||||
|
const namedBindings = declaration.importClause && declaration.importClause.namedBindings;
|
||||||
function getImportDeclaration(moduleSpecifier: LiteralExpression) {
|
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
|
||||||
let node: Node = moduleSpecifier;
|
// case:
|
||||||
while (node) {
|
// import * as ns from "foo"
|
||||||
if (node.kind === SyntaxKind.ImportDeclaration) {
|
namespaceImportDeclaration = declaration;
|
||||||
return <ImportDeclaration>node;
|
|
||||||
}
|
}
|
||||||
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
|
else {
|
||||||
return <ImportEqualsDeclaration>node;
|
// cases:
|
||||||
|
// import default from "foo"
|
||||||
|
// import { bar } from "foo" or combination with the first one
|
||||||
|
// import "foo"
|
||||||
|
namedImportDeclaration = declaration;
|
||||||
}
|
}
|
||||||
node = node.parent;
|
existingModuleSpecifier = declaration.moduleSpecifier.getText();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// case:
|
||||||
|
// import foo = require("foo")
|
||||||
|
namespaceImportDeclaration = declaration;
|
||||||
|
existingModuleSpecifier = getModuleSpecifierFromImportEqualsDeclaration(declaration);
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getUniqueSymbolId(symbol: Symbol) {
|
if (namespaceImportDeclaration) {
|
||||||
if (symbol.flags & SymbolFlags.Alias) {
|
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
|
||||||
return getSymbolId(checker.getAliasedSymbol(symbol));
|
|
||||||
}
|
}
|
||||||
return getSymbolId(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
|
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
|
||||||
const declarations = symbol.getDeclarations();
|
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
|
||||||
return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false;
|
/**
|
||||||
}
|
* If the existing import declaration already has a named import list, just
|
||||||
|
* insert the identifier into that list.
|
||||||
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean, isNamespaceImport?: boolean): ImportCodeAction[] {
|
*/
|
||||||
const existingDeclarations = getImportDeclarations(moduleSymbol);
|
const fileTextChanges = getTextChangeForImportClause(namedImportDeclaration.importClause);
|
||||||
if (existingDeclarations.length > 0) {
|
const moduleSpecifierWithoutQuotes = stripQuotes(namedImportDeclaration.moduleSpecifier.getText());
|
||||||
// With an existing import statement, there are more than one actions the user can do.
|
actions.push(createCodeAction(
|
||||||
return getCodeActionsForExistingImport(existingDeclarations);
|
Diagnostics.Add_0_to_existing_import_declaration_from_1,
|
||||||
|
[name, moduleSpecifierWithoutQuotes],
|
||||||
|
fileTextChanges,
|
||||||
|
"InsertingIntoExistingImport",
|
||||||
|
moduleSpecifierWithoutQuotes
|
||||||
|
));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return [getCodeActionForNewImport()];
|
// we need to create a new import statement, but the existing module specifier can be reused.
|
||||||
|
actions.push(getCodeActionForNewImport(existingModuleSpecifier));
|
||||||
|
}
|
||||||
|
return actions;
|
||||||
|
|
||||||
|
function getModuleSpecifierFromImportEqualsDeclaration(declaration: ImportEqualsDeclaration) {
|
||||||
|
if (declaration.moduleReference && declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
|
||||||
|
return declaration.moduleReference.expression.getText();
|
||||||
|
}
|
||||||
|
return declaration.moduleReference.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] {
|
function getTextChangeForImportClause(importClause: ImportClause): FileTextChanges[] {
|
||||||
const actions: ImportCodeAction[] = [];
|
const importList = <NamedImports>importClause.namedBindings;
|
||||||
|
const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name));
|
||||||
// It is possible that multiple import statements with the same specifier exist in the file.
|
// case 1:
|
||||||
// e.g.
|
// original text: import default from "module"
|
||||||
//
|
// change to: import default, { name } from "module"
|
||||||
// import * as ns from "foo";
|
// case 2:
|
||||||
// import { member1, member2 } from "foo";
|
// original text: import {} from "module"
|
||||||
//
|
// change to: import { name } from "module"
|
||||||
// member3/**/ <-- cusor here
|
if (!importList || importList.elements.length === 0) {
|
||||||
//
|
const newImportClause = createImportClause(importClause.name, createNamedImports([newImportSpecifier]));
|
||||||
// in this case we should provie 2 actions:
|
return createChangeTracker().replaceNode(sourceFile, importClause, newImportClause).getChanges();
|
||||||
// 1. change "member3" to "ns.member3"
|
|
||||||
// 2. add "member3" to the second import statement's import list
|
|
||||||
// and it is up to the user to decide which one fits best.
|
|
||||||
let namespaceImportDeclaration: ImportDeclaration | ImportEqualsDeclaration;
|
|
||||||
let namedImportDeclaration: ImportDeclaration;
|
|
||||||
let existingModuleSpecifier: string;
|
|
||||||
for (const declaration of declarations) {
|
|
||||||
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
|
||||||
const namedBindings = declaration.importClause && declaration.importClause.namedBindings;
|
|
||||||
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
|
|
||||||
// case:
|
|
||||||
// import * as ns from "foo"
|
|
||||||
namespaceImportDeclaration = declaration;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// cases:
|
|
||||||
// import default from "foo"
|
|
||||||
// import { bar } from "foo" or combination with the first one
|
|
||||||
// import "foo"
|
|
||||||
namedImportDeclaration = declaration;
|
|
||||||
}
|
|
||||||
existingModuleSpecifier = declaration.moduleSpecifier.getText();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// case:
|
|
||||||
// import foo = require("foo")
|
|
||||||
namespaceImportDeclaration = declaration;
|
|
||||||
existingModuleSpecifier = getModuleSpecifierFromImportEqualsDeclaration(declaration);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namespaceImportDeclaration) {
|
/**
|
||||||
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
|
* If the import list has one import per line, preserve that. Otherwise, insert on same line as last element
|
||||||
}
|
* import {
|
||||||
|
* foo
|
||||||
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
|
* } from "./module";
|
||||||
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
|
*/
|
||||||
/**
|
return createChangeTracker().insertNodeInListAfter(
|
||||||
* If the existing import declaration already has a named import list, just
|
sourceFile,
|
||||||
* insert the identifier into that list.
|
importList.elements[importList.elements.length - 1],
|
||||||
*/
|
newImportSpecifier).getChanges();
|
||||||
const fileTextChanges = getTextChangeForImportClause(namedImportDeclaration.importClause);
|
|
||||||
const moduleSpecifierWithoutQuotes = stripQuotes(namedImportDeclaration.moduleSpecifier.getText());
|
|
||||||
actions.push(createCodeAction(
|
|
||||||
Diagnostics.Add_0_to_existing_import_declaration_from_1,
|
|
||||||
[name, moduleSpecifierWithoutQuotes],
|
|
||||||
fileTextChanges,
|
|
||||||
"InsertingIntoExistingImport",
|
|
||||||
moduleSpecifierWithoutQuotes
|
|
||||||
));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// we need to create a new import statement, but the existing module specifier can be reused.
|
|
||||||
actions.push(getCodeActionForNewImport(existingModuleSpecifier));
|
|
||||||
}
|
|
||||||
return actions;
|
|
||||||
|
|
||||||
function getModuleSpecifierFromImportEqualsDeclaration(declaration: ImportEqualsDeclaration) {
|
|
||||||
if (declaration.moduleReference && declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
|
|
||||||
return declaration.moduleReference.expression.getText();
|
|
||||||
}
|
|
||||||
return declaration.moduleReference.getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTextChangeForImportClause(importClause: ImportClause): FileTextChanges[] {
|
|
||||||
const importList = <NamedImports>importClause.namedBindings;
|
|
||||||
const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name));
|
|
||||||
// case 1:
|
|
||||||
// original text: import default from "module"
|
|
||||||
// change to: import default, { name } from "module"
|
|
||||||
// case 2:
|
|
||||||
// original text: import {} from "module"
|
|
||||||
// change to: import { name } from "module"
|
|
||||||
if (!importList || importList.elements.length === 0) {
|
|
||||||
const newImportClause = createImportClause(importClause.name, createNamedImports([newImportSpecifier]));
|
|
||||||
return createChangeTracker().replaceNode(sourceFile, importClause, newImportClause).getChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the import list has one import per line, preserve that. Otherwise, insert on same line as last element
|
|
||||||
* import {
|
|
||||||
* foo
|
|
||||||
* } from "./module";
|
|
||||||
*/
|
|
||||||
return createChangeTracker().insertNodeInListAfter(
|
|
||||||
sourceFile,
|
|
||||||
importList.elements[importList.elements.length - 1],
|
|
||||||
newImportSpecifier).getChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction {
|
|
||||||
let namespacePrefix: string;
|
|
||||||
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
|
||||||
namespacePrefix = (<NamespaceImport>declaration.importClause.namedBindings).name.getText();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
namespacePrefix = declaration.name.getText();
|
|
||||||
}
|
|
||||||
namespacePrefix = stripQuotes(namespacePrefix);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cases:
|
|
||||||
* import * as ns from "mod"
|
|
||||||
* import default, * as ns from "mod"
|
|
||||||
* import ns = require("mod")
|
|
||||||
*
|
|
||||||
* Because there is no import list, we alter the reference to include the
|
|
||||||
* namespace instead of altering the import declaration. For example, "foo" would
|
|
||||||
* become "ns.foo"
|
|
||||||
*/
|
|
||||||
return createCodeAction(
|
|
||||||
Diagnostics.Change_0_to_1,
|
|
||||||
[name, `${namespacePrefix}.${name}`],
|
|
||||||
createChangeTracker().replaceNode(sourceFile, token, createPropertyAccess(createIdentifier(namespacePrefix), name)).getChanges(),
|
|
||||||
"CodeChange"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
|
function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction {
|
||||||
if (!lastImportDeclaration) {
|
let namespacePrefix: string;
|
||||||
// insert after any existing imports
|
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
||||||
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
|
namespacePrefix = (<NamespaceImport>declaration.importClause.namedBindings).name.getText();
|
||||||
const statement = sourceFile.statements[i];
|
|
||||||
if (statement.kind === SyntaxKind.ImportEqualsDeclaration || statement.kind === SyntaxKind.ImportDeclaration) {
|
|
||||||
lastImportDeclaration = statement;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
|
||||||
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport());
|
|
||||||
const changeTracker = createChangeTracker();
|
|
||||||
const importClause = isDefault
|
|
||||||
? createImportClause(createIdentifier(name), /*namedBindings*/ undefined)
|
|
||||||
: isNamespaceImport
|
|
||||||
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(name)))
|
|
||||||
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name))]));
|
|
||||||
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
|
|
||||||
if (!lastImportDeclaration) {
|
|
||||||
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
|
namespacePrefix = declaration.name.getText();
|
||||||
}
|
}
|
||||||
|
namespacePrefix = stripQuotes(namespacePrefix);
|
||||||
|
|
||||||
// if this file doesn't have any import statements, insert an import statement and then insert a new line
|
/**
|
||||||
// between the only import statement and user code. Otherwise just insert the statement because chances
|
* Cases:
|
||||||
// are there are already a new line seperating code and import statements.
|
* import * as ns from "mod"
|
||||||
|
* import default, * as ns from "mod"
|
||||||
|
* import ns = require("mod")
|
||||||
|
*
|
||||||
|
* Because there is no import list, we alter the reference to include the
|
||||||
|
* namespace instead of altering the import declaration. For example, "foo" would
|
||||||
|
* become "ns.foo"
|
||||||
|
*/
|
||||||
return createCodeAction(
|
return createCodeAction(
|
||||||
Diagnostics.Import_0_from_1,
|
Diagnostics.Change_0_to_1,
|
||||||
[name, `"${moduleSpecifierWithoutQuotes}"`],
|
[name, `${namespacePrefix}.${name}`],
|
||||||
changeTracker.getChanges(),
|
createChangeTracker().replaceNode(sourceFile, token, createPropertyAccess(createIdentifier(namespacePrefix), name)).getChanges(),
|
||||||
"NewImport",
|
"CodeChange"
|
||||||
moduleSpecifierWithoutQuotes
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getModuleSpecifierForNewImport() {
|
function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
|
||||||
const fileName = sourceFile.fileName;
|
if (!lastImportDeclaration) {
|
||||||
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
|
// insert after any existing imports
|
||||||
const sourceDirectory = getDirectoryPath(fileName);
|
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
|
||||||
const options = context.program.getCompilerOptions();
|
const statement = sourceFile.statements[i];
|
||||||
|
if (statement.kind === SyntaxKind.ImportEqualsDeclaration || statement.kind === SyntaxKind.ImportDeclaration) {
|
||||||
return tryGetModuleNameFromAmbientModule() ||
|
lastImportDeclaration = statement;
|
||||||
tryGetModuleNameFromTypeRoots() ||
|
break;
|
||||||
tryGetModuleNameAsNodeModule() ||
|
|
||||||
tryGetModuleNameFromBaseUrl() ||
|
|
||||||
tryGetModuleNameFromRootDirs() ||
|
|
||||||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
|
|
||||||
|
|
||||||
function tryGetModuleNameFromAmbientModule(): string {
|
|
||||||
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
|
|
||||||
return moduleSymbol.name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function tryGetModuleNameFromBaseUrl() {
|
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||||
if (!options.baseUrl) {
|
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport());
|
||||||
return undefined;
|
const changeTracker = createChangeTracker();
|
||||||
}
|
const importClause = isDefault
|
||||||
|
? createImportClause(createIdentifier(name), /*namedBindings*/ undefined)
|
||||||
|
: isNamespaceImport
|
||||||
|
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(name)))
|
||||||
|
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name))]));
|
||||||
|
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
|
||||||
|
if (!lastImportDeclaration) {
|
||||||
|
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
|
||||||
|
}
|
||||||
|
|
||||||
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
|
// if this file doesn't have any import statements, insert an import statement and then insert a new line
|
||||||
if (!relativeName) {
|
// between the only import statement and user code. Otherwise just insert the statement because chances
|
||||||
return undefined;
|
// are there are already a new line seperating code and import statements.
|
||||||
}
|
return createCodeAction(
|
||||||
|
Diagnostics.Import_0_from_1,
|
||||||
|
[name, `"${moduleSpecifierWithoutQuotes}"`],
|
||||||
|
changeTracker.getChanges(),
|
||||||
|
"NewImport",
|
||||||
|
moduleSpecifierWithoutQuotes
|
||||||
|
);
|
||||||
|
|
||||||
const relativeNameWithIndex = removeFileExtension(relativeName);
|
function getModuleSpecifierForNewImport() {
|
||||||
relativeName = removeExtensionAndIndexPostFix(relativeName);
|
const fileName = sourceFile.fileName;
|
||||||
|
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
|
||||||
|
const sourceDirectory = getDirectoryPath(fileName);
|
||||||
|
const options = context.program.getCompilerOptions();
|
||||||
|
|
||||||
if (options.paths) {
|
return tryGetModuleNameFromAmbientModule() ||
|
||||||
for (const key in options.paths) {
|
tryGetModuleNameFromTypeRoots() ||
|
||||||
for (const pattern of options.paths[key]) {
|
tryGetModuleNameAsNodeModule() ||
|
||||||
const indexOfStar = pattern.indexOf("*");
|
tryGetModuleNameFromBaseUrl() ||
|
||||||
if (indexOfStar === 0 && pattern.length === 1) {
|
tryGetModuleNameFromRootDirs() ||
|
||||||
continue;
|
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
|
||||||
}
|
|
||||||
else if (indexOfStar !== -1) {
|
|
||||||
const prefix = pattern.substr(0, indexOfStar);
|
|
||||||
const suffix = pattern.substr(indexOfStar + 1);
|
|
||||||
if (relativeName.length >= prefix.length + suffix.length &&
|
|
||||||
startsWith(relativeName, prefix) &&
|
|
||||||
endsWith(relativeName, suffix)) {
|
|
||||||
const matchedStar = relativeName.substr(prefix.length, relativeName.length - suffix.length);
|
|
||||||
return key.replace("\*", matchedStar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pattern === relativeName || pattern === relativeNameWithIndex) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return relativeName;
|
function tryGetModuleNameFromAmbientModule(): string {
|
||||||
|
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
|
||||||
|
return moduleSymbol.name;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function tryGetModuleNameFromRootDirs() {
|
function tryGetModuleNameFromBaseUrl() {
|
||||||
if (options.rootDirs) {
|
if (!options.baseUrl) {
|
||||||
const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, options.rootDirs);
|
|
||||||
const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, options.rootDirs);
|
|
||||||
if (normalizedTargetPath !== undefined) {
|
|
||||||
const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath;
|
|
||||||
return removeFileExtension(relativePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryGetModuleNameFromTypeRoots() {
|
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
|
||||||
const typeRoots = getEffectiveTypeRoots(options, context.host);
|
if (!relativeName) {
|
||||||
if (typeRoots) {
|
return undefined;
|
||||||
const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName));
|
|
||||||
for (const typeRoot of normalizedTypeRoots) {
|
|
||||||
if (startsWith(moduleFileName, typeRoot)) {
|
|
||||||
const relativeFileName = moduleFileName.substring(typeRoot.length + 1);
|
|
||||||
return removeExtensionAndIndexPostFix(relativeFileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryGetModuleNameAsNodeModule() {
|
const relativeNameWithIndex = removeFileExtension(relativeName);
|
||||||
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
|
relativeName = removeExtensionAndIndexPostFix(relativeName);
|
||||||
// nothing to do here
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
|
if (options.paths) {
|
||||||
if (indexOfNodeModules < 0) {
|
for (const key in options.paths) {
|
||||||
return undefined;
|
for (const pattern of options.paths[key]) {
|
||||||
}
|
const indexOfStar = pattern.indexOf("*");
|
||||||
|
if (indexOfStar === 0 && pattern.length === 1) {
|
||||||
let relativeFileName: string;
|
continue;
|
||||||
if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
|
}
|
||||||
// if node_modules folder is in this folder or any of its parent folder, no need to keep it.
|
else if (indexOfStar !== -1) {
|
||||||
relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
|
const prefix = pattern.substr(0, indexOfStar);
|
||||||
}
|
const suffix = pattern.substr(indexOfStar + 1);
|
||||||
else {
|
if (relativeName.length >= prefix.length + suffix.length &&
|
||||||
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
|
startsWith(relativeName, prefix) &&
|
||||||
}
|
endsWith(relativeName, suffix)) {
|
||||||
|
const matchedStar = relativeName.substr(prefix.length, relativeName.length - suffix.length);
|
||||||
relativeFileName = removeFileExtension(relativeFileName);
|
return key.replace("\*", matchedStar);
|
||||||
if (endsWith(relativeFileName, "/index")) {
|
|
||||||
relativeFileName = getDirectoryPath(relativeFileName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
const moduleDirectory = getDirectoryPath(moduleFileName);
|
|
||||||
const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
|
|
||||||
if (packageJsonContent) {
|
|
||||||
const mainFile = packageJsonContent.main || packageJsonContent.typings;
|
|
||||||
if (mainFile) {
|
|
||||||
const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
|
|
||||||
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
|
|
||||||
relativeFileName = getDirectoryPath(relativeFileName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (pattern === relativeName || pattern === relativeNameWithIndex) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return relativeFileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return relativeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
|
function tryGetModuleNameFromRootDirs() {
|
||||||
for (const rootDir of rootDirs) {
|
if (options.rootDirs) {
|
||||||
const relativeName = getRelativePathIfInDirectory(path, rootDir);
|
const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, options.rootDirs);
|
||||||
if (relativeName !== undefined) {
|
const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, options.rootDirs);
|
||||||
return relativeName;
|
if (normalizedTargetPath !== undefined) {
|
||||||
|
const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath;
|
||||||
|
return removeFileExtension(relativePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeExtensionAndIndexPostFix(fileName: string) {
|
function tryGetModuleNameFromTypeRoots() {
|
||||||
fileName = removeFileExtension(fileName);
|
const typeRoots = getEffectiveTypeRoots(options, context.host);
|
||||||
if (endsWith(fileName, "/index")) {
|
if (typeRoots) {
|
||||||
fileName = fileName.substr(0, fileName.length - 6/* "/index".length */);
|
const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName));
|
||||||
|
for (const typeRoot of normalizedTypeRoots) {
|
||||||
|
if (startsWith(moduleFileName, typeRoot)) {
|
||||||
|
const relativeFileName = moduleFileName.substring(typeRoot.length + 1);
|
||||||
|
return removeExtensionAndIndexPostFix(relativeFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRelativePathIfInDirectory(path: string, directoryPath: string) {
|
function tryGetModuleNameAsNodeModule() {
|
||||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
|
||||||
return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath;
|
// nothing to do here
|
||||||
}
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function getRelativePath(path: string, directoryPath: string) {
|
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
|
||||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
if (indexOfNodeModules < 0) {
|
||||||
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let relativeFileName: string;
|
||||||
|
if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
|
||||||
|
// if node_modules folder is in this folder or any of its parent folder, no need to keep it.
|
||||||
|
relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
relativeFileName = removeFileExtension(relativeFileName);
|
||||||
|
if (endsWith(relativeFileName, "/index")) {
|
||||||
|
relativeFileName = getDirectoryPath(relativeFileName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
const moduleDirectory = getDirectoryPath(moduleFileName);
|
||||||
|
const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
|
||||||
|
if (packageJsonContent) {
|
||||||
|
const mainFile = packageJsonContent.main || packageJsonContent.typings;
|
||||||
|
if (mainFile) {
|
||||||
|
const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
|
||||||
|
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
|
||||||
|
relativeFileName = getDirectoryPath(relativeFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return relativeFileName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
|
||||||
|
for (const rootDir of rootDirs) {
|
||||||
|
const relativeName = getRelativePathIfInDirectory(path, rootDir);
|
||||||
|
if (relativeName !== undefined) {
|
||||||
|
return relativeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeExtensionAndIndexPostFix(fileName: string) {
|
||||||
|
fileName = removeFileExtension(fileName);
|
||||||
|
if (endsWith(fileName, "/index")) {
|
||||||
|
fileName = fileName.substr(0, fileName.length - 6/* "/index".length */);
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRelativePathIfInDirectory(path: string, directoryPath: string) {
|
||||||
|
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||||
|
return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRelativePath(path: string, directoryPath: string) {
|
||||||
|
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||||
|
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createChangeTracker() {
|
|
||||||
return textChanges.ChangeTracker.fromCodeFixContext(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCodeAction(
|
|
||||||
description: DiagnosticMessage,
|
|
||||||
diagnosticArgs: string[],
|
|
||||||
changes: FileTextChanges[],
|
|
||||||
kind: ImportCodeActionKind,
|
|
||||||
moduleSpecifier?: string): ImportCodeAction {
|
|
||||||
return {
|
|
||||||
description: formatMessage.apply(undefined, [undefined, description].concat(<any[]>diagnosticArgs)),
|
|
||||||
changes,
|
|
||||||
kind,
|
|
||||||
moduleSpecifier
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
function createChangeTracker() {
|
||||||
|
return textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCodeAction(
|
||||||
|
description: DiagnosticMessage,
|
||||||
|
diagnosticArgs: string[],
|
||||||
|
changes: FileTextChanges[],
|
||||||
|
kind: ImportCodeActionKind,
|
||||||
|
moduleSpecifier?: string): ImportCodeAction {
|
||||||
|
return {
|
||||||
|
description: formatMessage.apply(undefined, [undefined, description].concat(<any[]>diagnosticArgs)),
|
||||||
|
changes,
|
||||||
|
kind,
|
||||||
|
moduleSpecifier
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,7 +283,9 @@ namespace ts.formatting {
|
||||||
this.NoSpaceAfterDot = new Rule(RuleDescriptor.create3(SyntaxKind.DotToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
this.NoSpaceAfterDot = new Rule(RuleDescriptor.create3(SyntaxKind.DotToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||||
|
|
||||||
// No space before and after indexer
|
// No space before and after indexer
|
||||||
this.NoSpaceBeforeOpenBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
this.NoSpaceBeforeOpenBracket = new Rule(
|
||||||
|
RuleDescriptor.create2(Shared.TokenRange.AnyExcept(SyntaxKind.AsyncKeyword), SyntaxKind.OpenBracketToken),
|
||||||
|
RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||||
this.NoSpaceAfterCloseBracket = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNotBeforeBlockInFunctionDeclarationContext), RuleAction.Delete));
|
this.NoSpaceAfterCloseBracket = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNotBeforeBlockInFunctionDeclarationContext), RuleAction.Delete));
|
||||||
|
|
||||||
// Place a space before open brace in a function declaration
|
// Place a space before open brace in a function declaration
|
||||||
|
|
|
@ -41,8 +41,7 @@ namespace ts.formatting {
|
||||||
}
|
}
|
||||||
|
|
||||||
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
|
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
|
||||||
const specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any &&
|
const specificRule = rule.Descriptor.LeftTokenRange.isSpecific() && rule.Descriptor.RightTokenRange.isSpecific();
|
||||||
rule.Descriptor.RightTokenRange !== Shared.TokenRange.Any;
|
|
||||||
|
|
||||||
rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => {
|
rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => {
|
||||||
rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => {
|
rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace ts.formatting {
|
||||||
export interface ITokenAccess {
|
export interface ITokenAccess {
|
||||||
GetTokens(): SyntaxKind[];
|
GetTokens(): SyntaxKind[];
|
||||||
Contains(token: SyntaxKind): boolean;
|
Contains(token: SyntaxKind): boolean;
|
||||||
|
isSpecific(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TokenRangeAccess implements ITokenAccess {
|
export class TokenRangeAccess implements ITokenAccess {
|
||||||
|
@ -27,6 +28,8 @@ namespace ts.formatting {
|
||||||
public Contains(token: SyntaxKind): boolean {
|
public Contains(token: SyntaxKind): boolean {
|
||||||
return this.tokens.indexOf(token) >= 0;
|
return this.tokens.indexOf(token) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isSpecific() { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TokenValuesAccess implements ITokenAccess {
|
export class TokenValuesAccess implements ITokenAccess {
|
||||||
|
@ -43,6 +46,8 @@ namespace ts.formatting {
|
||||||
public Contains(token: SyntaxKind): boolean {
|
public Contains(token: SyntaxKind): boolean {
|
||||||
return this.tokens.indexOf(token) >= 0;
|
return this.tokens.indexOf(token) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isSpecific() { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TokenSingleValueAccess implements ITokenAccess {
|
export class TokenSingleValueAccess implements ITokenAccess {
|
||||||
|
@ -56,15 +61,18 @@ namespace ts.formatting {
|
||||||
public Contains(tokenValue: SyntaxKind): boolean {
|
public Contains(tokenValue: SyntaxKind): boolean {
|
||||||
return tokenValue === this.token;
|
return tokenValue === this.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isSpecific() { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
const allTokens: SyntaxKind[] = [];
|
||||||
|
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
|
||||||
|
allTokens.push(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TokenAllAccess implements ITokenAccess {
|
export class TokenAllAccess implements ITokenAccess {
|
||||||
public GetTokens(): SyntaxKind[] {
|
public GetTokens(): SyntaxKind[] {
|
||||||
const result: SyntaxKind[] = [];
|
return allTokens;
|
||||||
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
|
|
||||||
result.push(token);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Contains(): boolean {
|
public Contains(): boolean {
|
||||||
|
@ -74,6 +82,22 @@ namespace ts.formatting {
|
||||||
public toString(): string {
|
public toString(): string {
|
||||||
return "[allTokens]";
|
return "[allTokens]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isSpecific() { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TokenAllExceptAccess implements ITokenAccess {
|
||||||
|
constructor(readonly except: SyntaxKind) {}
|
||||||
|
|
||||||
|
public GetTokens(): SyntaxKind[] {
|
||||||
|
return allTokens.filter(t => t !== this.except);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Contains(token: SyntaxKind): boolean {
|
||||||
|
return token !== this.except;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSpecific() { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TokenRange {
|
export class TokenRange {
|
||||||
|
@ -92,8 +116,8 @@ namespace ts.formatting {
|
||||||
return new TokenRange(new TokenRangeAccess(f, to, except));
|
return new TokenRange(new TokenRangeAccess(f, to, except));
|
||||||
}
|
}
|
||||||
|
|
||||||
static AllTokens(): TokenRange {
|
static AnyExcept(token: SyntaxKind): TokenRange {
|
||||||
return new TokenRange(new TokenAllAccess());
|
return new TokenRange(new TokenAllExceptAccess(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetTokens(): SyntaxKind[] {
|
public GetTokens(): SyntaxKind[] {
|
||||||
|
@ -108,8 +132,12 @@ namespace ts.formatting {
|
||||||
return this.tokenAccess.toString();
|
return this.tokenAccess.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Any: TokenRange = TokenRange.AllTokens();
|
public isSpecific() {
|
||||||
static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia]));
|
return this.tokenAccess.isSpecific();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Any: TokenRange = new TokenRange(new TokenAllAccess());
|
||||||
|
static AnyIncludingMultilineComments = TokenRange.FromTokens([...allTokens, SyntaxKind.MultiLineCommentTrivia]);
|
||||||
static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
|
static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
|
||||||
static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
|
static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
|
||||||
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);
|
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);
|
||||||
|
|
23
tests/baselines/reference/correctlyMarkAliasAsReferences1.js
Normal file
23
tests/baselines/reference/correctlyMarkAliasAsReferences1.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//// [tests/cases/conformance/jsx/correctlyMarkAliasAsReferences1.tsx] ////
|
||||||
|
|
||||||
|
//// [declaration.d.ts]
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
//// [0.tsx]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps; // any
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
</button>;
|
||||||
|
|
||||||
|
|
||||||
|
//// [0.js]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
let buttonProps; // any
|
||||||
|
let k = React.createElement("button", Object.assign({}, buttonProps),
|
||||||
|
React.createElement("span", { className: cx('class1', { class2: true }) }));
|
|
@ -0,0 +1,29 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : Symbol(React, Decl(0.tsx, 2, 6))
|
||||||
|
|
||||||
|
let buttonProps; // any
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
>k : Symbol(k, Decl(0.tsx, 5, 3))
|
||||||
|
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2385, 43))
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2460, 51))
|
||||||
|
>className : Symbol(className, Decl(0.tsx, 6, 17))
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
>class2 : Symbol(class2, Decl(0.tsx, 6, 43))
|
||||||
|
|
||||||
|
</button>;
|
||||||
|
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2385, 43))
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
|
@ -0,0 +1,35 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : any
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : typeof React
|
||||||
|
|
||||||
|
let buttonProps; // any
|
||||||
|
>buttonProps : any
|
||||||
|
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
>k : JSX.Element
|
||||||
|
><button {...buttonProps}> <span className={cx('class1', { class2: true })} /> </button> : JSX.Element
|
||||||
|
>button : any
|
||||||
|
>buttonProps : any
|
||||||
|
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
><span className={cx('class1', { class2: true })} /> : JSX.Element
|
||||||
|
>span : any
|
||||||
|
>className : any
|
||||||
|
>cx('class1', { class2: true }) : any
|
||||||
|
>cx : any
|
||||||
|
>'class1' : "class1"
|
||||||
|
>{ class2: true } : { class2: boolean; }
|
||||||
|
>class2 : boolean
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
</button>;
|
||||||
|
>button : any
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
23
tests/baselines/reference/correctlyMarkAliasAsReferences2.js
Normal file
23
tests/baselines/reference/correctlyMarkAliasAsReferences2.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//// [tests/cases/conformance/jsx/correctlyMarkAliasAsReferences2.tsx] ////
|
||||||
|
|
||||||
|
//// [declaration.d.ts]
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
//// [0.tsx]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
</button>;
|
||||||
|
|
||||||
|
|
||||||
|
//// [0.js]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
let buttonProps;
|
||||||
|
let k = React.createElement("button", Object.assign({}, buttonProps),
|
||||||
|
React.createElement("span", { className: cx('class1', { class2: true }) }));
|
|
@ -0,0 +1,30 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : Symbol(React, Decl(0.tsx, 2, 6))
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
>attributeName : Symbol(attributeName, Decl(0.tsx, 4, 20))
|
||||||
|
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
>k : Symbol(k, Decl(0.tsx, 5, 3))
|
||||||
|
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2385, 43))
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2460, 51))
|
||||||
|
>className : Symbol(className, Decl(0.tsx, 6, 17))
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
>class2 : Symbol(class2, Decl(0.tsx, 6, 43))
|
||||||
|
|
||||||
|
</button>;
|
||||||
|
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2385, 43))
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
|
@ -0,0 +1,36 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : any
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : typeof React
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
>buttonProps : { [attributeName: string]: ""; }
|
||||||
|
>attributeName : string
|
||||||
|
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
>k : JSX.Element
|
||||||
|
><button {...buttonProps}> <span className={cx('class1', { class2: true })} /> </button> : JSX.Element
|
||||||
|
>button : any
|
||||||
|
>buttonProps : { [attributeName: string]: ""; }
|
||||||
|
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
><span className={cx('class1', { class2: true })} /> : JSX.Element
|
||||||
|
>span : any
|
||||||
|
>className : any
|
||||||
|
>cx('class1', { class2: true }) : any
|
||||||
|
>cx : any
|
||||||
|
>'class1' : "class1"
|
||||||
|
>{ class2: true } : { class2: boolean; }
|
||||||
|
>class2 : boolean
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
</button>;
|
||||||
|
>button : any
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
23
tests/baselines/reference/correctlyMarkAliasAsReferences3.js
Normal file
23
tests/baselines/reference/correctlyMarkAliasAsReferences3.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//// [tests/cases/conformance/jsx/correctlyMarkAliasAsReferences3.tsx] ////
|
||||||
|
|
||||||
|
//// [declaration.d.ts]
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
//// [0.tsx]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps;
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
</button>;
|
||||||
|
|
||||||
|
|
||||||
|
//// [0.js]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
let buttonProps;
|
||||||
|
let k = React.createElement("button", Object.assign({}, buttonProps),
|
||||||
|
React.createElement("span", { className: cx('class1', { class2: true }) }));
|
|
@ -0,0 +1,29 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : Symbol(React, Decl(0.tsx, 2, 6))
|
||||||
|
|
||||||
|
let buttonProps;
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
>k : Symbol(k, Decl(0.tsx, 5, 3))
|
||||||
|
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2385, 43))
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2460, 51))
|
||||||
|
>className : Symbol(className, Decl(0.tsx, 6, 17))
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
>class2 : Symbol(class2, Decl(0.tsx, 6, 43))
|
||||||
|
|
||||||
|
</button>;
|
||||||
|
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2385, 43))
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
|
@ -0,0 +1,35 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : any
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : typeof React
|
||||||
|
|
||||||
|
let buttonProps;
|
||||||
|
>buttonProps : any
|
||||||
|
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
>k : JSX.Element
|
||||||
|
><button {...buttonProps}> <span className={cx('class1', { class2: true })} /> </button> : JSX.Element
|
||||||
|
>button : any
|
||||||
|
>buttonProps : undefined
|
||||||
|
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
><span className={cx('class1', { class2: true })} /> : JSX.Element
|
||||||
|
>span : any
|
||||||
|
>className : any
|
||||||
|
>cx('class1', { class2: true }) : any
|
||||||
|
>cx : any
|
||||||
|
>'class1' : "class1"
|
||||||
|
>{ class2: true } : { class2: boolean; }
|
||||||
|
>class2 : boolean
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
</button>;
|
||||||
|
>button : any
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
19
tests/baselines/reference/correctlyMarkAliasAsReferences4.js
Normal file
19
tests/baselines/reference/correctlyMarkAliasAsReferences4.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//// [tests/cases/conformance/jsx/correctlyMarkAliasAsReferences4.tsx] ////
|
||||||
|
|
||||||
|
//// [declaration.d.ts]
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
//// [0.tsx]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
let k = <button {...buttonProps} className={cx('class1', { class2: true })} />;
|
||||||
|
|
||||||
|
//// [0.js]
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
let buttonProps;
|
||||||
|
let k = React.createElement("button", Object.assign({}, buttonProps, { className: cx('class1', { class2: true }) }));
|
|
@ -0,0 +1,24 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : Symbol(React, Decl(0.tsx, 2, 6))
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
>attributeName : Symbol(attributeName, Decl(0.tsx, 4, 20))
|
||||||
|
|
||||||
|
let k = <button {...buttonProps} className={cx('class1', { class2: true })} />;
|
||||||
|
>k : Symbol(k, Decl(0.tsx, 5, 3))
|
||||||
|
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2385, 43))
|
||||||
|
>buttonProps : Symbol(buttonProps, Decl(0.tsx, 4, 3))
|
||||||
|
>className : Symbol(className, Decl(0.tsx, 5, 32))
|
||||||
|
>cx : Symbol(cx, Decl(0.tsx, 1, 6))
|
||||||
|
>class2 : Symbol(class2, Decl(0.tsx, 5, 58))
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
|
@ -0,0 +1,29 @@
|
||||||
|
=== tests/cases/conformance/jsx/0.tsx ===
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
>cx : any
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
>React : typeof React
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
>buttonProps : { [attributeName: string]: ""; }
|
||||||
|
>attributeName : string
|
||||||
|
|
||||||
|
let k = <button {...buttonProps} className={cx('class1', { class2: true })} />;
|
||||||
|
>k : JSX.Element
|
||||||
|
><button {...buttonProps} className={cx('class1', { class2: true })} /> : JSX.Element
|
||||||
|
>button : any
|
||||||
|
>buttonProps : { [attributeName: string]: ""; }
|
||||||
|
>className : any
|
||||||
|
>cx('class1', { class2: true }) : any
|
||||||
|
>cx : any
|
||||||
|
>'class1' : "class1"
|
||||||
|
>{ class2: true } : { class2: boolean; }
|
||||||
|
>class2 : boolean
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
=== tests/cases/conformance/jsx/declaration.d.ts ===
|
||||||
|
declare module "classnames";
|
||||||
|
No type information for this code.
|
||||||
|
No type information for this code.
|
|
@ -4,4 +4,5 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(1,9): error TS7017: E
|
||||||
==== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts (1 errors) ====
|
==== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts (1 errors) ====
|
||||||
var x = {}["hello"];
|
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]
|
//// [noImplicitAnyStringIndexerOnObject.ts]
|
||||||
var x = {}["hello"];
|
var x = {}["hello"];
|
||||||
|
var y: string = { '': 'foo' }[''];
|
||||||
|
|
||||||
//// [noImplicitAnyStringIndexerOnObject.js]
|
//// [noImplicitAnyStringIndexerOnObject.js]
|
||||||
var x = {}["hello"];
|
var x = {}["hello"];
|
||||||
|
var y = { '': 'foo' }[''];
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
var newName = void 0 /*undefined*/;
|
var newName = void 0 /*undefined*/;
|
||||||
|
|
|
@ -3,10 +3,12 @@ tests/cases/conformance/jsx/file.tsx(10,33): error TS2698: Spread types may only
|
||||||
tests/cases/conformance/jsx/file.tsx(11,33): error TS2698: Spread types may only be created from object types.
|
tests/cases/conformance/jsx/file.tsx(11,33): error TS2698: Spread types may only be created from object types.
|
||||||
tests/cases/conformance/jsx/file.tsx(12,33): error TS2698: Spread types may only be created from object types.
|
tests/cases/conformance/jsx/file.tsx(12,33): error TS2698: Spread types may only be created from object types.
|
||||||
tests/cases/conformance/jsx/file.tsx(14,33): error TS2698: Spread types may only be created from object types.
|
tests/cases/conformance/jsx/file.tsx(14,33): error TS2698: Spread types may only be created from object types.
|
||||||
|
tests/cases/conformance/jsx/file.tsx(14,63): error TS2698: Spread types may only be created from object types.
|
||||||
tests/cases/conformance/jsx/file.tsx(15,33): error TS2698: Spread types may only be created from object types.
|
tests/cases/conformance/jsx/file.tsx(15,33): error TS2698: Spread types may only be created from object types.
|
||||||
|
tests/cases/conformance/jsx/file.tsx(15,55): error TS2698: Spread types may only be created from object types.
|
||||||
|
|
||||||
|
|
||||||
==== tests/cases/conformance/jsx/file.tsx (6 errors) ====
|
==== tests/cases/conformance/jsx/file.tsx (8 errors) ====
|
||||||
import React = require('react')
|
import React = require('react')
|
||||||
|
|
||||||
declare function OverloadComponent<U>(): JSX.Element;
|
declare function OverloadComponent<U>(): JSX.Element;
|
||||||
|
@ -30,9 +32,13 @@ tests/cases/conformance/jsx/file.tsx(15,33): error TS2698: Spread types may only
|
||||||
let a4 = <OverloadComponent />;
|
let a4 = <OverloadComponent />;
|
||||||
let a5 = <OverloadComponent {...arg2} ignore-prop="hello" {...arg1} />;
|
let a5 = <OverloadComponent {...arg2} ignore-prop="hello" {...arg1} />;
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
!!! error TS2698: Spread types may only be created from object types.
|
||||||
|
~~~~~~~~~
|
||||||
!!! error TS2698: Spread types may only be created from object types.
|
!!! error TS2698: Spread types may only be created from object types.
|
||||||
let a6 = <OverloadComponent {...arg2} ignore-prop {...arg1} />;
|
let a6 = <OverloadComponent {...arg2} ignore-prop {...arg1} />;
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
!!! error TS2698: Spread types may only be created from object types.
|
||||||
|
~~~~~~~~~
|
||||||
!!! error TS2698: Spread types may only be created from object types.
|
!!! error TS2698: Spread types may only be created from object types.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
// @noimplicitany: true
|
// @noimplicitany: true
|
||||||
|
|
||||||
var x = {}["hello"];
|
var x = {}["hello"];
|
||||||
|
var y: string = { '': 'foo' }[''];
|
|
@ -0,0 +1,17 @@
|
||||||
|
// @target: es2017
|
||||||
|
// @jsx: react
|
||||||
|
// @moduleResolution: node
|
||||||
|
// @libFiles: react.d.ts,lib.d.ts
|
||||||
|
|
||||||
|
// @filename: declaration.d.ts
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
// @filename: 0.tsx
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps; // any
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
</button>;
|
|
@ -0,0 +1,17 @@
|
||||||
|
// @target: es2017
|
||||||
|
// @jsx: react
|
||||||
|
// @moduleResolution: node
|
||||||
|
// @libFiles: react.d.ts,lib.d.ts
|
||||||
|
|
||||||
|
// @filename: declaration.d.ts
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
// @filename: 0.tsx
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
</button>;
|
|
@ -0,0 +1,18 @@
|
||||||
|
// @target: es2017
|
||||||
|
// @jsx: react
|
||||||
|
// @moduleResolution: node
|
||||||
|
// @noImplicitAny: true
|
||||||
|
// @libFiles: react.d.ts,lib.d.ts
|
||||||
|
|
||||||
|
// @filename: declaration.d.ts
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
// @filename: 0.tsx
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps;
|
||||||
|
let k = <button {...buttonProps}>
|
||||||
|
<span className={cx('class1', { class2: true })} />
|
||||||
|
</button>;
|
|
@ -0,0 +1,15 @@
|
||||||
|
// @target: es2017
|
||||||
|
// @jsx: react
|
||||||
|
// @moduleResolution: node
|
||||||
|
// @libFiles: react.d.ts,lib.d.ts
|
||||||
|
|
||||||
|
// @filename: declaration.d.ts
|
||||||
|
declare module "classnames";
|
||||||
|
|
||||||
|
// @filename: 0.tsx
|
||||||
|
///<reference path="declaration.d.ts" />
|
||||||
|
import * as cx from 'classnames';
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
let buttonProps : {[attributeName: string]: ''}
|
||||||
|
let k = <button {...buttonProps} className={cx('class1', { class2: true })} />;
|
9
tests/cases/fourslash/formatAsyncComputedMethod.ts
Normal file
9
tests/cases/fourslash/formatAsyncComputedMethod.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/// <reference path="fourslash.ts"/>
|
||||||
|
|
||||||
|
////class C {
|
||||||
|
//// /*method*/async [0]() { }
|
||||||
|
////}
|
||||||
|
|
||||||
|
format.document();
|
||||||
|
goTo.marker("method");
|
||||||
|
verify.currentLineContentIs(" async [0]() { }");
|
4
tests/lib/react.d.ts
vendored
4
tests/lib/react.d.ts
vendored
|
@ -2359,9 +2359,9 @@ declare namespace JSX {
|
||||||
interface ElementClass extends React.Component<any, any> {
|
interface ElementClass extends React.Component<any, any> {
|
||||||
render(): JSX.Element | null;
|
render(): JSX.Element | null;
|
||||||
}
|
}
|
||||||
interface ElementAttributesProperty { props; }
|
interface ElementAttributesProperty { props: any; }
|
||||||
|
|
||||||
interface ElementChildrenAttribute { children; }
|
interface ElementChildrenAttribute { children: any; }
|
||||||
|
|
||||||
interface IntrinsicAttributes extends React.Attributes { }
|
interface IntrinsicAttributes extends React.Attributes { }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue