Merge branch 'master' into master-dynamicImport

This commit is contained in:
Kanchalai Tanglertsampan 2017-05-04 10:14:32 -07:00
commit 1b7d3bfa07
78 changed files with 2318 additions and 991 deletions

View File

@ -39,7 +39,6 @@
"@types/gulp-help": "latest",
"@types/gulp-newer": "latest",
"@types/gulp-sourcemaps": "latest",
"@types/gulp-typescript": "latest",
"@types/merge2": "latest",
"@types/minimatch": "latest",
"@types/minimist": "latest",

View File

@ -1400,10 +1400,10 @@ namespace ts {
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
}
function getTargetOfExportSpecifier(node: ExportSpecifier, dontResolveAlias?: boolean): Symbol {
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node, dontResolveAlias) :
resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) {
return node.parent.parent.moduleSpecifier ?
getExternalModuleMember(node.parent.parent, node, dontResolveAlias) :
resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol {
@ -1421,7 +1421,7 @@ namespace ts {
case SyntaxKind.ImportSpecifier:
return getTargetOfImportSpecifier(<ImportSpecifier>node, dontRecursivelyResolve);
case SyntaxKind.ExportSpecifier:
return getTargetOfExportSpecifier(<ExportSpecifier>node, dontRecursivelyResolve);
return getTargetOfExportSpecifier(<ExportSpecifier>node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(<ExportAssignment>node, dontRecursivelyResolve);
case SyntaxKind.NamespaceExportDeclaration:
@ -3721,10 +3721,7 @@ namespace ts {
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) {
const exportSpecifier = <ExportSpecifier>node.parent;
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);
exportSymbol = getTargetOfExportSpecifier(<ExportSpecifier>node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
}
const result: Node[] = [];
if (exportSymbol) {
@ -7257,7 +7254,7 @@ namespace ts {
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
getPropertyNameForKnownSymbolName((<Identifier>(<PropertyAccessExpression>accessExpression.argumentExpression).name).text) :
undefined;
if (propName) {
if (propName !== undefined) {
const prop = getPropertyOfType(objectType, propName);
if (prop) {
if (accessExpression) {
@ -9234,25 +9231,39 @@ namespace ts {
let result = Ternary.True;
const saveErrorInfo = errorInfo;
outer: for (const t of targetSignatures) {
// Only elaborate errors from the first failure
let shouldElaborateErrors = reportErrors;
for (const s of sourceSignatures) {
const related = signatureRelatedTo(s, t, shouldElaborateErrors);
if (related) {
result &= related;
errorInfo = saveErrorInfo;
continue outer;
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
// We instantiations of the same anonymous type (which typically will be the type of a method).
// Simply do a pairwise comparison of the signatures in the two signature lists instead of the
// much more expensive N * M comparison matrix we explore below.
for (let i = 0; i < targetSignatures.length; i++) {
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], reportErrors);
if (!related) {
return Ternary.False;
}
shouldElaborateErrors = false;
result &= related;
}
}
else {
outer: for (const t of targetSignatures) {
// Only elaborate errors from the first failure
let shouldElaborateErrors = reportErrors;
for (const s of sourceSignatures) {
const related = signatureRelatedTo(s, t, shouldElaborateErrors);
if (related) {
result &= related;
errorInfo = saveErrorInfo;
continue outer;
}
shouldElaborateErrors = false;
}
if (shouldElaborateErrors) {
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
typeToString(source),
signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
if (shouldElaborateErrors) {
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
typeToString(source),
signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
}
return Ternary.False;
}
return Ternary.False;
}
return result;
}
@ -13243,6 +13254,8 @@ namespace ts {
let attributesTable = createMap<Symbol>();
let spread: Type = emptyObjectType;
let attributesArray: Symbol[] = [];
let hasSpreadAnyType = false;
for (const attributeDecl of attributes.properties) {
const member = attributeDecl.symbol;
if (isJsxAttribute(attributeDecl)) {
@ -13271,31 +13284,33 @@ namespace ts {
const exprType = checkExpression(attributeDecl.expression);
if (!isValidSpreadType(exprType)) {
error(attributeDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
return anyType;
hasSpreadAnyType = true;
}
if (isTypeAny(exprType)) {
return anyType;
hasSpreadAnyType = true;
}
spread = getSpreadType(spread, exprType);
}
}
if (spread !== emptyObjectType) {
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
attributesArray = [];
attributesTable = createMap<Symbol>();
}
attributesArray = getPropertiesOfType(spread);
}
attributesTable = createMap<Symbol>();
if (attributesArray) {
forEach(attributesArray, (attr) => {
if (!filter || filter(attr)) {
attributesTable.set(attr.name, attr);
if (!hasSpreadAnyType) {
if (spread !== emptyObjectType) {
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
attributesArray = [];
attributesTable = createMap<Symbol>();
}
});
attributesArray = getPropertiesOfType(spread);
}
attributesTable = createMap<Symbol>();
if (attributesArray) {
forEach(attributesArray, (attr) => {
if (!filter || filter(attr)) {
attributesTable.set(attr.name, attr);
}
});
}
}
// Handle children attribute
@ -13319,7 +13334,7 @@ namespace ts {
// Error if there is a attribute named "children" and children element.
// This is because children element will overwrite the value from attributes
const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
if (jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
if (attributesTable.has(jsxChildrenPropertyName)) {
error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName);
}
@ -13333,7 +13348,7 @@ namespace ts {
}
}
return createJsxAttributesType(attributes.symbol, attributesTable);
return hasSpreadAnyType ? anyType : createJsxAttributesType(attributes.symbol, attributesTable);
/**
* Create anonymous type from given attributes symbol table.
@ -13429,7 +13444,18 @@ namespace ts {
}
}
return getUnionType(map(signatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
const instantiatedSignatures = [];
for (const signature of signatures) {
if (signature.typeParameters) {
const typeArguments = fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0);
instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments));
}
else {
instantiatedSignatures.push(signature);
}
}
return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
}
/**

View File

@ -251,6 +251,15 @@ namespace ts {
}
}
export function zipToMap<T>(keys: string[], values: T[]): Map<T> {
Debug.assert(keys.length === values.length);
const map = createMap<T>();
for (let i = 0; i < keys.length; ++i) {
map.set(keys[i], values[i]);
}
return map;
}
/**
* Iterates through `array` by index and performs the callback on each element of array until the callback
* returns a falsey value, then returns false.

View File

@ -3224,9 +3224,16 @@
},
"Scoped package detected, looking in '{0}'": {
"category": "Message",
"code": "6182"
"code": 6182
},
"Reusing resolution of module '{0}' to file '{1}' from old program.": {
"category": "Message",
"code": 6183
},
"Reusing module resolutions originating in '{0}' since resolutions are unchanged from old program.": {
"category": "Message",
"code": 6184
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005

View File

@ -1440,6 +1440,44 @@ namespace ts {
: node;
}
export function createInterfaceDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[] | undefined, members: TypeElement[]) {
const node = <InterfaceDeclaration>createSynthesizedNode(SyntaxKind.InterfaceDeclaration);
node.decorators = asNodeArray(decorators);
node.modifiers = asNodeArray(modifiers);
node.name = asName(name);
node.typeParameters = asNodeArray(typeParameters);
node.heritageClauses = asNodeArray(heritageClauses);
node.members = createNodeArray(members);
return node;
}
export function updateInterfaceDeclaration(node: InterfaceDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: Identifier, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[] | undefined, members: TypeElement[]) {
return node.decorators !== decorators
|| node.modifiers !== modifiers
|| node.name !== name
|| node.typeParameters !== typeParameters
|| node.heritageClauses !== heritageClauses
|| node.members !== members
? updateNode(createInterfaceDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members), node)
: node;
}
export function createTypeAliasDeclaration(name: string | Identifier, typeParameters: TypeParameterDeclaration[] | undefined, type: TypeNode) {
const node = <TypeAliasDeclaration>createSynthesizedNode(SyntaxKind.TypeAliasDeclaration);
node.name = asName(name);
node.typeParameters = asNodeArray(typeParameters);
node.type = type;
return node;
}
export function updateTypeAliasDeclaration(node: TypeAliasDeclaration, name: Identifier, typeParameters: TypeParameterDeclaration[] | undefined, type: TypeNode) {
return node.name !== name
|| node.typeParameters !== typeParameters
|| node.type !== type
? updateNode(createTypeAliasDeclaration(name, typeParameters, type), node)
: node;
}
export function createEnumDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, members: EnumMember[]) {
const node = <EnumDeclaration>createSynthesizedNode(SyntaxKind.EnumDeclaration);
node.decorators = asNodeArray(decorators);

View File

@ -298,6 +298,7 @@ namespace ts {
let diagnosticsProducingTypeChecker: TypeChecker;
let noDiagnosticsTypeChecker: TypeChecker;
let classifiableNames: Map<string>;
let modifiedFilePaths: Path[] | undefined;
const cachedSemanticDiagnosticsForFile: DiagnosticCache = {};
const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {};
@ -367,7 +368,8 @@ namespace ts {
// used to track cases when two file names differ only in casing
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
if (!tryReuseStructureFromOldProgram()) {
const structuralIsReused = tryReuseStructureFromOldProgram();
if (structuralIsReused !== StructureIsReused.Completely) {
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
@ -476,89 +478,114 @@ namespace ts {
}
interface OldProgramState {
program: Program;
program: Program | undefined;
file: SourceFile;
/** The collection of paths modified *since* the old program. */
modifiedFilePaths: Path[];
}
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState?: OldProgramState) {
if (!oldProgramState && !file.ambientModuleNames.length) {
// if old program state is not supplied and file does not contain locally defined ambient modules
// then the best we can do is fallback to the default logic
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState: OldProgramState) {
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveModuleNamesWorker(moduleNames, containingFile);
}
// at this point we know that either
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
if (oldSourceFile !== file && file.resolvedModules) {
// `file` was created for the new program.
//
// We only set `file.resolvedModules` via work from the current function,
// so it is defined iff we already called the current function on `file`.
// That call happened no later than the creation of the `file` object,
// which per above occured during the current program creation.
// Since we assume the filesystem does not change during program creation,
// it is safe to reuse resolutions from the earlier call.
const result: ResolvedModuleFull[] = [];
for (const moduleName of moduleNames) {
const resolvedModule = file.resolvedModules.get(moduleName);
result.push(resolvedModule);
}
return result;
}
// At this point, we know at least one of the following hold:
// - file has local declarations for ambient modules
// OR
// - old program state is available
// OR
// - both of items above
// With this it is possible that we can tell how some module names from the initial list will be resolved
// without doing actual resolution (in particular if some name was resolved to ambient module).
// Such names should be excluded from the list of module names that will be provided to `resolveModuleNamesWorker`
// since we don't want to resolve them again.
// With this information, we can infer some module resolutions without performing resolution.
// this is a list of modules for which we cannot predict resolution so they should be actually resolved
/** An ordered list of module names for which we cannot recover the resolution. */
let unknownModuleNames: string[];
// this is a list of combined results assembles from predicted and resolved results.
// Order in this list matches the order in the original list of module names `moduleNames` which is important
// so later we can split results to resolutions of modules and resolutions of module augmentations.
/**
* The indexing of elements in this list matches that of `moduleNames`.
*
* Before combining results, result[i] is in one of the following states:
* * undefined: needs to be recomputed,
* * predictedToResolveToAmbientModuleMarker: known to be an ambient module.
* Needs to be reset to undefined before returning,
* * ResolvedModuleFull instance: can be reused.
*/
let result: ResolvedModuleFull[];
// a transient placeholder that is used to mark predicted resolution in the result list
/** A transient placeholder used to mark predicted resolution in the result list. */
const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
for (let i = 0; i < moduleNames.length; i++) {
const moduleName = moduleNames[i];
// module name is known to be resolved to ambient module if
// - module name is contained in the list of ambient modules that are locally declared in the file
// - in the old program module name was resolved to ambient module whose declaration is in non-modified file
// If we want to reuse resolutions more aggressively, we can refine this to check for whether the
// text of the corresponding modulenames has changed.
if (file === oldSourceFile) {
const oldResolvedModule = oldSourceFile && oldSourceFile.resolvedModules.get(moduleName);
if (oldResolvedModule) {
if (isTraceEnabled(options, host)) {
trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, containingFile);
}
(result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
continue;
}
}
// We know moduleName resolves to an ambient module provided that moduleName:
// - is in the list of ambient modules locally declared in the current source file.
// - resolved to an ambient module in the old program whose declaration is in an unmodified file
// (so the same module declaration will land in the new program)
let isKnownToResolveToAmbientModule = false;
let resolvesToAmbientModuleInNonModifiedFile = false;
if (contains(file.ambientModuleNames, moduleName)) {
isKnownToResolveToAmbientModule = true;
resolvesToAmbientModuleInNonModifiedFile = true;
if (isTraceEnabled(options, host)) {
trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, containingFile);
}
}
else {
isKnownToResolveToAmbientModule = checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
}
if (isKnownToResolveToAmbientModule) {
if (!unknownModuleNames) {
// found a first module name for which result can be prediced
// this means that this module name should not be passed to `resolveModuleNamesWorker`.
// We'll use a separate list for module names that are definitely unknown.
result = new Array(moduleNames.length);
// copy all module names that appear before the current one in the list
// since they are known to be unknown
unknownModuleNames = moduleNames.slice(0, i);
}
// mark prediced resolution in the result list
result[i] = predictedToResolveToAmbientModuleMarker;
if (resolvesToAmbientModuleInNonModifiedFile) {
(result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
}
else if (unknownModuleNames) {
// found unknown module name and we are already using separate list for those - add it to the list
unknownModuleNames.push(moduleName);
else {
// Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
(unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
}
}
if (!unknownModuleNames) {
// we've looked throught the list but have not seen any predicted resolution
// use default logic
return resolveModuleNamesWorker(moduleNames, containingFile);
}
const resolutions = unknownModuleNames.length
const resolutions = unknownModuleNames && unknownModuleNames.length
? resolveModuleNamesWorker(unknownModuleNames, containingFile)
: emptyArray;
// combine results of resolutions and predicted results
// Combine results of resolutions and predicted results
if (!result) {
// There were no unresolved/ambient resolutions.
Debug.assert(resolutions.length === moduleNames.length);
return <ResolvedModuleFull[]>resolutions;
}
let j = 0;
for (let i = 0; i < result.length; i++) {
if (result[i] === predictedToResolveToAmbientModuleMarker) {
result[i] = undefined;
if (result[i]) {
// `result[i]` is either a `ResolvedModuleFull` or a marker.
// If it is the former, we can leave it as is.
if (result[i] === predictedToResolveToAmbientModuleMarker) {
result[i] = undefined;
}
}
else {
result[i] = resolutions[j];
@ -566,18 +593,18 @@ namespace ts {
}
}
Debug.assert(j === resolutions.length);
return result;
function checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState?: OldProgramState): boolean {
if (!oldProgramState) {
return false;
}
// If we change our policy of rechecking failed lookups on each program create,
// we should adjust the value returned here.
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean {
const resolutionToFile = getResolvedModule(oldProgramState.file, moduleName);
if (resolutionToFile) {
// module used to be resolved to file - ignore it
return false;
}
const ambientModule = oldProgram.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
const ambientModule = oldProgramState.program && oldProgramState.program.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
if (!(ambientModule && ambientModule.declarations)) {
return false;
}
@ -599,99 +626,107 @@ namespace ts {
}
}
function tryReuseStructureFromOldProgram(): boolean {
function tryReuseStructureFromOldProgram(): StructureIsReused {
if (!oldProgram) {
return false;
return StructureIsReused.Not;
}
// check properties that can affect structure of the program or module resolution strategy
// if any of these properties has changed - structure cannot be reused
const oldOptions = oldProgram.getCompilerOptions();
if (changesAffectModuleResolution(oldOptions, options)) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
Debug.assert(!oldProgram.structureIsReused);
Debug.assert(!(oldProgram.structureIsReused & (StructureIsReused.Completely | StructureIsReused.SafeModules)));
// there is an old program, check if we can reuse its structure
const oldRootNames = oldProgram.getRootFileNames();
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
if (!arrayIsEqualTo(options.types, oldOptions.types)) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
// check if program source files has changed in the way that can affect structure of the program
const newSourceFiles: SourceFile[] = [];
const filePaths: Path[] = [];
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
oldProgram.structureIsReused = StructureIsReused.Completely;
for (const oldSourceFile of oldProgram.getSourceFiles()) {
let newSourceFile = host.getSourceFileByPath
const newSourceFile = host.getSourceFileByPath
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
: host.getSourceFile(oldSourceFile.fileName, options.target);
if (!newSourceFile) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
newSourceFile.path = oldSourceFile.path;
filePaths.push(newSourceFile.path);
if (oldSourceFile !== newSourceFile) {
// The `newSourceFile` object was created for the new program.
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
// value of no-default-lib has changed
// this will affect if default library is injected into the list of files
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
// check tripleslash references
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
// tripleslash references has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
// check imports and module augmentations
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
// imports has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
// moduleAugmentations has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
// 'types' references has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
// tentatively approve the file
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
}
else {
// file has no changes - use it as is
newSourceFile = oldSourceFile;
}
// if file has passed all checks it should be safe to reuse it
newSourceFiles.push(newSourceFile);
}
const modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
}
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
// try to verify results of module resolution
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
if (resolveModuleNamesWorker) {
const moduleNames = map(concatenate(newSourceFile.imports, newSourceFile.moduleAugmentations), getTextOfLiteral);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, { file: oldSourceFile, program: oldProgram, modifiedFilePaths });
const oldProgramState = { program: oldProgram, file: oldSourceFile, modifiedFilePaths };
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, oldProgramState);
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
}
else {
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
}
}
if (resolveTypeReferenceDirectiveNamesWorker) {
@ -700,12 +735,17 @@ namespace ts {
// ensure that types resolutions are still correct
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
if (resolutionsChanged) {
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, resolutions);
}
else {
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
}
}
// pass the cache of module/types resolutions from the old source file
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
}
if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
}
// update fileName -> file mapping
@ -720,9 +760,8 @@ namespace ts {
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
}
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
oldProgram.structureIsReused = true;
return true;
return oldProgram.structureIsReused = StructureIsReused.Completely;
}
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
@ -1533,11 +1572,11 @@ namespace ts {
function processImportedModules(file: SourceFile) {
collectExternalModuleReferences(file);
if (file.imports.length || file.moduleAugmentations.length) {
file.resolvedModules = createMap<ResolvedModuleFull>();
// Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
const nonGlobalAugmentation = filter(file.moduleAugmentations, (moduleAugmentation) => moduleAugmentation.kind === SyntaxKind.StringLiteral);
const moduleNames = map(concatenate(file.imports, nonGlobalAugmentation), getTextOfLiteral);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory), file);
const oldProgramState = { program: oldProgram, file, modifiedFilePaths };
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory), file, oldProgramState);
Debug.assert(resolutions.length === moduleNames.length);
for (let i = 0; i < moduleNames.length; i++) {
const resolution = resolutions[i];

View File

@ -2601,14 +2601,16 @@ namespace ts {
* Adds a leading VariableStatement for a enum or module declaration.
*/
function addVarForEnumOrModuleDeclaration(statements: Statement[], node: ModuleDeclaration | EnumDeclaration) {
// Emit a variable statement for the module.
// Emit a variable statement for the module. We emit top-level enums as a `var`
// declaration to avoid static errors in global scripts scripts due to redeclaration.
// enums in any other scope are emitted as a `let` declaration.
const statement = createVariableStatement(
visitNodes(node.modifiers, modifierVisitor, isModifier),
[
createVariableDeclarationList([
createVariableDeclaration(
getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)
)
]
], currentScope.kind === SyntaxKind.SourceFile ? NodeFlags.None : NodeFlags.Let)
);
setOriginalNode(statement, node);

View File

@ -2423,7 +2423,14 @@ namespace ts {
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
/* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean;
// For testing purposes only.
/* @internal */ structureIsReused?: boolean;
/* @internal */ structureIsReused?: StructureIsReused;
}
/* @internal */
export const enum StructureIsReused {
Not = 0,
SafeModules = 1 << 0,
Completely = 1 << 1,
}
export interface CustomTransformers {

View File

@ -122,9 +122,8 @@ namespace ts {
/* @internal */
export function hasChangesInResolutions<T>(names: string[], newResolutions: T[], oldResolutions: Map<T>, comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
if (names.length !== newResolutions.length) {
return false;
}
Debug.assert(names.length === newResolutions.length);
for (let i = 0; i < names.length; i++) {
const newResolution = newResolutions[i];
const oldResolution = oldResolutions && oldResolutions.get(names[i]);

View File

@ -201,14 +201,13 @@ namespace ts {
assert.isTrue(diags.length === 1, "one diagnostic expected");
assert.isTrue(typeof diags[0].messageText === "string" && ((<string>diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message");
// assert that import will success once file appear on disk
fileMap.set(imported.name, imported);
fileExistsCalledForBar = false;
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
diags = project.getLanguageService().getSemanticDiagnostics(root.name);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
assert.isTrue(diags.length === 0);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
assert.isTrue(diags.length === 0, "The import should succeed once the imported file appears on disk.");
});
});
}

View File

@ -1042,7 +1042,7 @@ import b = require("./moduleB");
assert.equal(diagnostics1.length, 1, "expected one diagnostic");
createProgram(names, {}, compilerHost, program1);
assert.isTrue(program1.structureIsReused);
assert.isTrue(program1.structureIsReused === StructureIsReused.Completely);
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");

View File

@ -159,12 +159,14 @@ namespace ts {
return program;
}
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void) {
const texts: NamedSourceText[] = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
updater(texts);
const host = createTestCompilerHost(texts, options.target, oldProgram);
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void, newTexts?: NamedSourceText[]) {
if (!newTexts) {
newTexts = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
}
updater(newTexts);
const host = createTestCompilerHost(newTexts, options.target, oldProgram);
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
program.sourceTexts = texts;
program.sourceTexts = newTexts;
program.host = host;
return program;
}
@ -217,13 +219,15 @@ namespace ts {
describe("Reuse program structure", () => {
const target = ScriptTarget.Latest;
const files = [
{ name: "a.ts", text: SourceText.New(
`
const files: NamedSourceText[] = [
{
name: "a.ts", text: SourceText.New(
`
/// <reference path='b.ts'/>
/// <reference path='non-existing-file.ts'/>
/// <reference types="typerefs" />
`, "", `var x = 1`) },
`, "", `var x = 1`)
},
{ name: "b.ts", text: SourceText.New(`/// <reference path='c.ts'/>`, "", `var y = 2`) },
{ name: "c.ts", text: SourceText.New("", "", `var z = 1;`) },
{ name: "types/typerefs/index.d.ts", text: SourceText.New("", "", `declare let z: number;`) },
@ -234,7 +238,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
assert.isTrue(program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
@ -245,7 +249,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
assert.isTrue(program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
@ -259,19 +263,19 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
});
it("fails if change affects type references", () => {
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop);
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
});
it("succeeds if change doesn't affect type references", () => {
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop);
assert.isTrue(program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
});
it("fails if change affects imports", () => {
@ -279,7 +283,7 @@ namespace ts {
updateProgram(program_1, ["a.ts"], { target }, files => {
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
});
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
});
it("fails if change affects type directives", () => {
@ -291,25 +295,25 @@ namespace ts {
/// <reference types="typerefs1" />`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
});
it("fails if module kind changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
});
it("fails if rootdir changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop);
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
});
it("fails if config path changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop);
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
});
it("resolution cache follows imports", () => {
@ -328,7 +332,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
assert.isTrue(program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
// content of resolution cache should not change
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
@ -338,7 +342,7 @@ namespace ts {
const program_3 = updateProgram(program_2, ["a.ts"], options, files => {
files[0].text = files[0].text.updateImportsAndExports("");
});
assert.isTrue(!program_2.structureIsReused);
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
@ -347,7 +351,7 @@ namespace ts {
`;
files[0].text = files[0].text.updateImportsAndExports(newImports);
});
assert.isTrue(!program_3.structureIsReused);
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
});
@ -365,7 +369,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
assert.isTrue(program_1.structureIsReused);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
// content of resolution cache should not change
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
@ -376,7 +380,7 @@ namespace ts {
files[0].text = files[0].text.updateReferences("");
});
assert.isTrue(!program_2.structureIsReused);
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
updateProgram(program_3, ["/a.ts"], options, files => {
@ -385,10 +389,74 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(!program_3.structureIsReused);
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
});
it("fetches imports after npm install", () => {
const file1Ts = { name: "file1.ts", text: SourceText.New("", `import * as a from "a";`, "const myX: number = a.x;") };
const file2Ts = { name: "file2.ts", text: SourceText.New("", "", "") };
const indexDTS = { name: "node_modules/a/index.d.ts", text: SourceText.New("", "export declare let x: number;", "") };
const options: CompilerOptions = { target: ScriptTarget.ES2015, traceResolution: true, moduleResolution: ModuleResolutionKind.NodeJs };
const rootFiles = [file1Ts, file2Ts];
const filesAfterNpmInstall = [file1Ts, file2Ts, indexDTS];
const initialProgram = newProgram(rootFiles, rootFiles.map(f => f.name), options);
{
assert.deepEqual(initialProgram.host.getTrace(),
[
"======== Resolving module 'a' from 'file1.ts'. ========",
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
"File 'node_modules/a.ts' does not exist.",
"File 'node_modules/a.tsx' does not exist.",
"File 'node_modules/a.d.ts' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.ts' does not exist.",
"File 'node_modules/a/index.tsx' does not exist.",
"File 'node_modules/a/index.d.ts' does not exist.",
"File 'node_modules/@types/a.d.ts' does not exist.",
"File 'node_modules/@types/a/package.json' does not exist.",
"File 'node_modules/@types/a/index.d.ts' does not exist.",
"Loading module 'a' from 'node_modules' folder, target file type 'JavaScript'.",
"File 'node_modules/a.js' does not exist.",
"File 'node_modules/a.jsx' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.js' does not exist.",
"File 'node_modules/a/index.jsx' does not exist.",
"======== Module name 'a' was not resolved. ========"
],
"initialProgram: execute module resolution normally.");
const initialProgramDiagnostics = initialProgram.getSemanticDiagnostics(initialProgram.getSourceFile("file1.ts"));
assert(initialProgramDiagnostics.length === 1, `initialProgram: import should fail.`);
}
const afterNpmInstallProgram = updateProgram(initialProgram, rootFiles.map(f => f.name), options, f => {
f[1].text = f[1].text.updateReferences(`/// <reference no-default-lib="true"/>`);
}, filesAfterNpmInstall);
{
assert.deepEqual(afterNpmInstallProgram.host.getTrace(),
[
"======== Resolving module 'a' from 'file1.ts'. ========",
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
"File 'node_modules/a.ts' does not exist.",
"File 'node_modules/a.tsx' does not exist.",
"File 'node_modules/a.d.ts' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.ts' does not exist.",
"File 'node_modules/a/index.tsx' does not exist.",
"File 'node_modules/a/index.d.ts' exist - use it as a name resolution result.",
"======== Module name 'a' was successfully resolved to 'node_modules/a/index.d.ts'. ========"
],
"afterNpmInstallProgram: execute module resolution normally.");
const afterNpmInstallProgramDiagnostics = afterNpmInstallProgram.getSemanticDiagnostics(afterNpmInstallProgram.getSourceFile("file1.ts"));
assert(afterNpmInstallProgramDiagnostics.length === 0, `afterNpmInstallProgram: program is well-formed with import.`);
}
});
it("can reuse ambient module declarations from non-modified files", () => {
const files = [
{ name: "/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") },
@ -468,7 +536,206 @@ namespace ts {
"File '/fs.jsx' does not exist.",
"======== Module name 'fs' was not resolved. ========",
], "should look for 'fs' again since node.d.ts was changed");
});
it("can reuse module resolutions from non-modified files", () => {
const files = [
{ name: "a1.ts", text: SourceText.New("", "", "let x = 1;") },
{ name: "a2.ts", text: SourceText.New("", "", "let x = 1;") },
{ name: "b1.ts", text: SourceText.New("", "export class B { x: number; }", "") },
{ name: "b2.ts", text: SourceText.New("", "export class B { x: number; }", "") },
{ name: "node_modules/@types/typerefs1/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
{ name: "node_modules/@types/typerefs2/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
{
name: "f1.ts",
text:
SourceText.New(
`/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>${newLine}/// <reference no-default-lib="true"/>`,
`import { B } from './b1';${newLine}export let BB = B;`,
"declare module './b1' { interface B { y: string; } }")
},
{
name: "f2.ts",
text: SourceText.New(
`/// <reference path="a2.ts"/>${newLine}/// <reference types="typerefs2"/>`,
`import { B } from './b2';${newLine}import { BB } from './f1';`,
"(new BB).x; (new BB).y;")
},
];
const options: CompilerOptions = { target: ScriptTarget.ES2015, traceResolution: true, moduleResolution: ModuleResolutionKind.Classic };
const program_1 = newProgram(files, files.map(f => f.name), options);
let expectedErrors = 0;
{
assert.deepEqual(program_1.host.getTrace(),
[
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs1/package.json' does not exist.",
"File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========",
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"======== Resolving module './b2' from 'f2.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b2.ts' exist - use it as a name resolution result.",
"======== Module name './b2' was successfully resolved to 'b2.ts'. ========",
"======== Resolving module './f1' from 'f2.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'f1.ts' exist - use it as a name resolution result.",
"======== Module name './f1' was successfully resolved to 'f1.ts'. ========"
],
"program_1: execute module reoslution normally.");
const program_1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("f2.ts"));
assert(program_1Diagnostics.length === expectedErrors, `initial program should be well-formed`);
}
const indexOfF1 = 6;
const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>`);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
const program_2Diagnostics = program_2.getSemanticDiagnostics(program_2.getSourceFile("f2.ts"));
assert(program_2Diagnostics.length === expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
assert.deepEqual(program_2.host.getTrace(), [
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs1/package.json' does not exist.",
"File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========",
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_2: reuse module resolutions in f2 since it is unchanged");
}
const program_3 = updateProgram(program_2, program_2.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>`);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
const program_3Diagnostics = program_3.getSemanticDiagnostics(program_3.getSourceFile("f2.ts"));
assert(program_3Diagnostics.length === expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
assert.deepEqual(program_3.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_3: reuse module resolutions in f2 since it is unchanged");
}
const program_4 = updateProgram(program_3, program_3.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
const program_4Diagnostics = program_4.getSemanticDiagnostics(program_4.getSourceFile("f2.ts"));
assert(program_4Diagnostics.length === expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
assert.deepEqual(program_4.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_4: reuse module resolutions in f2 since it is unchanged");
}
const program_5 = updateProgram(program_4, program_4.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateImportsAndExports(`import { B } from './b1';`);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
const program_5Diagnostics = program_5.getSemanticDiagnostics(program_5.getSourceFile("f2.ts"));
assert(program_5Diagnostics.length === ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
assert.deepEqual(program_5.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========"
], "program_5: exports do not affect program structure, so f2's resolutions are silently reused.");
}
const program_6 = updateProgram(program_5, program_5.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateProgram("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
const program_6Diagnostics = program_6.getSemanticDiagnostics(program_6.getSourceFile("f2.ts"));
assert(program_6Diagnostics.length === expectedErrors, `import of BB in f1 fails.`);
assert.deepEqual(program_6.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_6: reuse module resolutions in f2 since it is unchanged");
}
const program_7 = updateProgram(program_6, program_6.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateImportsAndExports("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
const program_7Diagnostics = program_7.getSemanticDiagnostics(program_7.getSourceFile("f2.ts"));
assert(program_7Diagnostics.length === expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
assert.deepEqual(program_7.host.getTrace(), [
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_7 should reuse module resolutions in f2 since it is unchanged");
}
});
});

View File

@ -3,48 +3,77 @@
namespace ts {
describe("TransformAPI", () => {
function transformsCorrectly(name: string, source: string, transformers: TransformerFactory<SourceFile>[]) {
it(name, () => {
Harness.Baseline.runBaseline(`transformApi/transformsCorrectly.${name}.js`, () => {
const transformed = transform(createSourceFile("source.ts", source, ScriptTarget.ES2015), transformers);
const printer = createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed }, {
onEmitNode: transformed.emitNodeWithNotification,
substituteNode: transformed.substituteNode
});
const result = printer.printBundle(createBundle(transformed.transformed));
transformed.dispose();
return result;
});
function replaceUndefinedWithVoid0(context: ts.TransformationContext) {
const previousOnSubstituteNode = context.onSubstituteNode;
context.enableSubstitution(SyntaxKind.Identifier);
context.onSubstituteNode = (hint, node) => {
node = previousOnSubstituteNode(hint, node);
if (hint === EmitHint.Expression && node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "undefined") {
node = createPartiallyEmittedExpression(
addSyntheticTrailingComment(
setTextRange(
createVoidZero(),
node),
SyntaxKind.MultiLineCommentTrivia, "undefined"));
}
return node;
};
return (file: ts.SourceFile) => file;
}
function replaceIdentifiersNamedOldNameWithNewName(context: ts.TransformationContext) {
const previousOnSubstituteNode = context.onSubstituteNode;
context.enableSubstitution(SyntaxKind.Identifier);
context.onSubstituteNode = (hint, node) => {
node = previousOnSubstituteNode(hint, node);
if (node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "oldName") {
node = setTextRange(createIdentifier("newName"), node);
}
return node;
};
return (file: ts.SourceFile) => file;
}
function transformSourceFile(sourceText: string, transformers: TransformerFactory<SourceFile>[]) {
const transformed = transform(createSourceFile("source.ts", sourceText, ScriptTarget.ES2015), transformers);
const printer = createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed }, {
onEmitNode: transformed.emitNodeWithNotification,
substituteNode: transformed.substituteNode
});
const result = printer.printBundle(createBundle(transformed.transformed));
transformed.dispose();
return result;
}
function testBaseline(testName: string, test: () => string) {
it(testName, () => {
Harness.Baseline.runBaseline(`transformApi/transformsCorrectly.${testName}.js`, test);
});
}
transformsCorrectly("substitution", `
var a = undefined;
`, [
context => {
const previousOnSubstituteNode = context.onSubstituteNode;
context.enableSubstitution(SyntaxKind.Identifier);
context.onSubstituteNode = (hint, node) => {
node = previousOnSubstituteNode(hint, node);
if (hint === EmitHint.Expression && node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "undefined") {
node = createPartiallyEmittedExpression(
addSyntheticTrailingComment(
setTextRange(
createVoidZero(),
node),
SyntaxKind.MultiLineCommentTrivia, "undefined"));
}
return node;
};
return file => file;
}
]);
testBaseline("substitution", () => {
return transformSourceFile(`var a = undefined;`, [replaceUndefinedWithVoid0]);
});
// https://github.com/Microsoft/TypeScript/issues/15192
transformsCorrectly("types", `let a: () => void`, [
context => file => visitNode(file, function visitor(node: Node): VisitResult<Node> {
return visitEachChild(node, visitor, context);
})
]);
testBaseline("types", () => {
return transformSourceFile(`let a: () => void`, [
context => file => visitNode(file, function visitor(node: Node): VisitResult<Node> {
return visitEachChild(node, visitor, context);
})
]);
});
testBaseline("fromTranspileModule", () => {
return ts.transpileModule(`var oldName = undefined;`, {
transformers: {
before: [replaceUndefinedWithVoid0],
after: [replaceIdentifiersNamedOldNameWithNewName]
},
compilerOptions: {
newLine: NewLineKind.CarriageReturnLineFeed
}
}).outputText;
});
});
}

View File

@ -32,17 +32,17 @@ interface Array<T> {
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an array of key, value pairs for every entry in the array
* Returns an iterable of key, value pairs for every entry in the array
*/
entries(): IterableIterator<[number, T]>;
/**
* Returns an list of keys in the array
* Returns an iterable of keys in the array
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the array
* Returns an iterable of values in the array
*/
values(): IterableIterator<T>;
}
@ -66,21 +66,21 @@ interface ArrayConstructor {
}
interface ReadonlyArray<T> {
/** Iterator */
/** Iterator of values in the array. */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an array of key, value pairs for every entry in the array
* Returns an iterable of key, value pairs for every entry in the array
*/
entries(): IterableIterator<[number, T]>;
/**
* Returns an list of keys in the array
* Returns an iterable of keys in the array
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the array
* Returns an iterable of values in the array
*/
values(): IterableIterator<T>;
}
@ -91,9 +91,42 @@ interface IArguments {
}
interface Map<K, V> {
/** Returns an iterable of entries in the map. */
[Symbol.iterator](): IterableIterator<[K, V]>;
/**
* Returns an iterable of key, value pairs for every entry in the map.
*/
entries(): IterableIterator<[K, V]>;
/**
* Returns an iterable of keys in the map
*/
keys(): IterableIterator<K>;
/**
* Returns an iterable of values in the map
*/
values(): IterableIterator<V>;
}
interface ReadonlyMap<K, V> {
/** Returns an iterable of entries in the map. */
[Symbol.iterator](): IterableIterator<[K, V]>;
/**
* Returns an iterable of key, value pairs for every entry in the map.
*/
entries(): IterableIterator<[K, V]>;
/**
* Returns an iterable of keys in the map
*/
keys(): IterableIterator<K>;
/**
* Returns an iterable of values in the map
*/
values(): IterableIterator<V>;
}
@ -108,9 +141,40 @@ interface WeakMapConstructor {
}
interface Set<T> {
/** Iterates over values in the set. */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an iterable of [v,v] pairs for every value `v` in the set.
*/
entries(): IterableIterator<[T, T]>;
/**
* Despite its name, returns an iterable of the values in the set,
*/
keys(): IterableIterator<T>;
/**
* Returns an iterable of values in the set.
*/
values(): IterableIterator<T>;
}
interface ReadonlySet<T> {
/** Iterates over values in the set. */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an iterable of [v,v] pairs for every value `v` in the set.
*/
entries(): IterableIterator<[T, T]>;
/**
* Despite its name, returns an iterable of the values in the set,
*/
keys(): IterableIterator<T>;
/**
* Returns an iterable of values in the set.
*/
values(): IterableIterator<T>;
}

View File

@ -552,7 +552,7 @@ namespace ts.server {
// bump up the version if
// - oldProgram is not set - this is a first time updateGraph is called
// - newProgram is different from the old program and structure of the old program was not reused.
if (!oldProgram || (this.program !== oldProgram && !oldProgram.structureIsReused)) {
if (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely))) {
hasChanges = true;
if (oldProgram) {
for (const f of oldProgram.getSourceFiles()) {

View File

@ -1,5 +1,13 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [
Diagnostics.Cannot_find_name_0.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";
interface ImportCodeAction extends CodeAction {
@ -113,465 +121,458 @@ namespace ts.codefix {
}
}
registerCodeFix({
errorCodes: [
Diagnostics.Cannot_find_name_0.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: (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;
function getImportCodeActions(context: CodeFixContext): ImportCodeAction[] {
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 name = token.getText();
const symbolIdActionMap = new ImportCodeActionMap();
const token = getTokenAtPosition(sourceFile, context.span.start);
const name = token.getText();
const symbolIdActionMap = new ImportCodeActionMap();
// this is a module id -> module import declaration map
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
let lastImportDeclaration: Node;
// this is a module id -> module import declaration map
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
let lastImportDeclaration: Node;
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) {
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
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) {
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
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 otherSourceFile of allSourceFiles) {
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
candidateModules.push(otherSourceFile.symbol);
for (const moduleSymbol of candidateModules) {
context.cancellationToken.throwIfCancellationRequested();
// 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) {
context.cancellationToken.throwIfCancellationRequested();
// check exports with the same name
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
}
}
// 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));
return symbolIdActionMap.getAllActions();
function getImportDeclarations(moduleSymbol: Symbol) {
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
const cached = cachedImportDeclarations[moduleSymbolId];
if (cached) {
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
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
}
function getUniqueSymbolId(symbol: Symbol) {
if (symbol.flags & SymbolFlags.Alias) {
return getSymbolId(checker.getAliasedSymbol(symbol));
}
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) {
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
const cached = cachedImportDeclarations[moduleSymbolId];
if (cached) {
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;
// It is possible that multiple import statements with the same specifier exist in the file.
// e.g.
//
// import * as ns from "foo";
// import { member1, member2 } from "foo";
//
// member3/**/ <-- cusor here
//
// in this case we should provie 2 actions:
// 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;
}
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return <ImportEqualsDeclaration>node;
else {
// 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 (symbol.flags & SymbolFlags.Alias) {
return getSymbolId(checker.getAliasedSymbol(symbol));
if (namespaceImportDeclaration) {
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
}
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);
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
/**
* If the existing import declaration already has a named import list, just
* insert the identifier into that list.
*/
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 {
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[] {
const actions: ImportCodeAction[] = [];
// It is possible that multiple import statements with the same specifier exist in the file.
// e.g.
//
// import * as ns from "foo";
// import { member1, member2 } from "foo";
//
// member3/**/ <-- cusor here
//
// in this case we should provie 2 actions:
// 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);
}
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 (namespaceImportDeclaration) {
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
}
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
/**
* If the existing import declaration already has a named import list, just
* insert the identifier into that list.
*/
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"
);
}
/**
* 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 getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
if (!lastImportDeclaration) {
// insert after any existing imports
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
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}` });
function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction {
let namespacePrefix: string;
if (declaration.kind === SyntaxKind.ImportDeclaration) {
namespacePrefix = (<NamespaceImport>declaration.importClause.namedBindings).name.getText();
}
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
// are there are already a new line seperating code and import statements.
/**
* 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.Import_0_from_1,
[name, `"${moduleSpecifierWithoutQuotes}"`],
changeTracker.getChanges(),
"NewImport",
moduleSpecifierWithoutQuotes
Diagnostics.Change_0_to_1,
[name, `${namespacePrefix}.${name}`],
createChangeTracker().replaceNode(sourceFile, token, createPropertyAccess(createIdentifier(namespacePrefix), name)).getChanges(),
"CodeChange"
);
}
}
function getModuleSpecifierForNewImport() {
const fileName = sourceFile.fileName;
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
const sourceDirectory = getDirectoryPath(fileName);
const options = context.program.getCompilerOptions();
return tryGetModuleNameFromAmbientModule() ||
tryGetModuleNameFromTypeRoots() ||
tryGetModuleNameAsNodeModule() ||
tryGetModuleNameFromBaseUrl() ||
tryGetModuleNameFromRootDirs() ||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
function tryGetModuleNameFromAmbientModule(): string {
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
return moduleSymbol.name;
}
function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
if (!lastImportDeclaration) {
// insert after any existing imports
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
const statement = sourceFile.statements[i];
if (statement.kind === SyntaxKind.ImportEqualsDeclaration || statement.kind === SyntaxKind.ImportDeclaration) {
lastImportDeclaration = statement;
break;
}
}
}
function tryGetModuleNameFromBaseUrl() {
if (!options.baseUrl) {
return undefined;
}
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 {
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
}
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
if (!relativeName) {
return undefined;
}
// 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
// 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);
relativeName = removeExtensionAndIndexPostFix(relativeName);
function getModuleSpecifierForNewImport() {
const fileName = sourceFile.fileName;
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
const sourceDirectory = getDirectoryPath(fileName);
const options = context.program.getCompilerOptions();
if (options.paths) {
for (const key in options.paths) {
for (const pattern of options.paths[key]) {
const indexOfStar = pattern.indexOf("*");
if (indexOfStar === 0 && pattern.length === 1) {
continue;
}
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 tryGetModuleNameFromAmbientModule() ||
tryGetModuleNameFromTypeRoots() ||
tryGetModuleNameAsNodeModule() ||
tryGetModuleNameFromBaseUrl() ||
tryGetModuleNameFromRootDirs() ||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
return relativeName;
function tryGetModuleNameFromAmbientModule(): string {
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
return moduleSymbol.name;
}
}
function tryGetModuleNameFromRootDirs() {
if (options.rootDirs) {
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);
}
}
function tryGetModuleNameFromBaseUrl() {
if (!options.baseUrl) {
return undefined;
}
function tryGetModuleNameFromTypeRoots() {
const typeRoots = getEffectiveTypeRoots(options, context.host);
if (typeRoots) {
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);
}
}
}
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
if (!relativeName) {
return undefined;
}
function tryGetModuleNameAsNodeModule() {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
// nothing to do here
return undefined;
}
const relativeNameWithIndex = removeFileExtension(relativeName);
relativeName = removeExtensionAndIndexPostFix(relativeName);
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
if (indexOfNodeModules < 0) {
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);
}
if (options.paths) {
for (const key in options.paths) {
for (const pattern of options.paths[key]) {
const indexOfStar = pattern.indexOf("*");
if (indexOfStar === 0 && pattern.length === 1) {
continue;
}
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;
}
}
catch (e) { }
}
return relativeFileName;
}
return relativeName;
}
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
for (const rootDir of rootDirs) {
const relativeName = getRelativePathIfInDirectory(path, rootDir);
if (relativeName !== undefined) {
return relativeName;
function tryGetModuleNameFromRootDirs() {
if (options.rootDirs) {
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;
}
function removeExtensionAndIndexPostFix(fileName: string) {
fileName = removeFileExtension(fileName);
if (endsWith(fileName, "/index")) {
fileName = fileName.substr(0, fileName.length - 6/* "/index".length */);
function tryGetModuleNameFromTypeRoots() {
const typeRoots = getEffectiveTypeRoots(options, context.host);
if (typeRoots) {
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) {
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath;
}
function tryGetModuleNameAsNodeModule() {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
// nothing to do here
return undefined;
}
function getRelativePath(path: string, directoryPath: string) {
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
if (indexOfNodeModules < 0) {
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
};
}
}
}

View File

@ -18,142 +18,150 @@ namespace ts.codefix {
switch (token.kind) {
case ts.SyntaxKind.Identifier:
switch (token.parent.kind) {
case ts.SyntaxKind.VariableDeclaration:
switch (token.parent.parent.parent.kind) {
case SyntaxKind.ForStatement:
const forStatement = <ForStatement>token.parent.parent.parent;
const forInitializer = <VariableDeclarationList>forStatement.initializer;
if (forInitializer.declarations.length === 1) {
return deleteNode(forInitializer);
}
else {
return deleteNodeInList(token.parent);
}
case SyntaxKind.ForOfStatement:
const forOfStatement = <ForOfStatement>token.parent.parent.parent;
if (forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
}
break;
case SyntaxKind.ForInStatement:
// There is no valid fix in the case of:
// for .. in
return undefined;
case SyntaxKind.CatchClause:
const catchClause = <CatchClause>token.parent.parent;
const parameter = catchClause.variableDeclaration.getChildren()[0];
return deleteNode(parameter);
default:
const variableStatement = <VariableStatement>token.parent.parent.parent;
if (variableStatement.declarationList.declarations.length === 1) {
return deleteNode(variableStatement);
}
else {
return deleteNodeInList(token.parent);
}
}
// TODO: #14885
// falls through
case SyntaxKind.TypeParameter:
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
if (typeParameters.length === 1) {
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
return deleteRange(typeParameters);
}
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
return deleteRange(typeParameters);
}
return deleteNodeRange(previousToken, nextToken);
}
else {
return deleteNodeInList(token.parent);
}
case ts.SyntaxKind.Parameter:
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
if (functionDeclaration.parameters.length === 1) {
return deleteNode(token.parent);
}
else {
return deleteNodeInList(token.parent);
}
// handle case where 'import a = A;'
case SyntaxKind.ImportEqualsDeclaration:
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
return deleteNode(importEquals);
case SyntaxKind.ImportSpecifier:
const namedImports = <NamedImports>token.parent.parent;
if (namedImports.elements.length === 1) {
// Only 1 import and it is unused. So the entire declaration should be removed.
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
return deleteNode(importSpec);
}
else {
// delete import specifier
return deleteNodeInList(token.parent);
}
// handle case where "import d, * as ns from './file'"
// or "'import {a, b as ns} from './file'"
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
const importClause = <ImportClause>token.parent;
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
// import |d,| * as ns from './file'
const start = importClause.name.getStart(sourceFile);
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
// shift first non-whitespace position after comma to the start position of the node
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
}
else {
return deleteNode(importClause.name);
}
}
case SyntaxKind.NamespaceImport:
const namespaceImport = <NamespaceImport>token.parent;
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
return deleteRange({ pos: startPosition, end: namespaceImport.end });
}
return deleteRange(namespaceImport);
}
}
break;
return deleteIdentifier();
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.NamespaceImport:
return deleteNode(token.parent);
default:
return deleteDefault();
}
if (isDeclarationName(token)) {
return deleteNode(token.parent);
function deleteDefault() {
if (isDeclarationName(token)) {
return deleteNode(token.parent);
}
else if (isLiteralComputedPropertyDeclarationName(token)) {
return deleteNode(token.parent.parent);
}
else {
return undefined;
}
}
else if (isLiteralComputedPropertyDeclarationName(token)) {
return deleteNode(token.parent.parent);
function deleteIdentifier(): CodeAction[] | undefined {
switch (token.parent.kind) {
case ts.SyntaxKind.VariableDeclaration:
return deleteVariableDeclaration(<ts.VariableDeclaration>token.parent);
case SyntaxKind.TypeParameter:
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
if (typeParameters.length === 1) {
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
return deleteRange(typeParameters);
}
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
return deleteRange(typeParameters);
}
return deleteNodeRange(previousToken, nextToken);
}
else {
return deleteNodeInList(token.parent);
}
case ts.SyntaxKind.Parameter:
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
if (functionDeclaration.parameters.length === 1) {
return deleteNode(token.parent);
}
else {
return deleteNodeInList(token.parent);
}
// handle case where 'import a = A;'
case SyntaxKind.ImportEqualsDeclaration:
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
return deleteNode(importEquals);
case SyntaxKind.ImportSpecifier:
const namedImports = <NamedImports>token.parent.parent;
if (namedImports.elements.length === 1) {
// Only 1 import and it is unused. So the entire declaration should be removed.
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
return deleteNode(importSpec);
}
else {
// delete import specifier
return deleteNodeInList(token.parent);
}
// handle case where "import d, * as ns from './file'"
// or "'import {a, b as ns} from './file'"
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
const importClause = <ImportClause>token.parent;
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
// import |d,| * as ns from './file'
const start = importClause.name.getStart(sourceFile);
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
// shift first non-whitespace position after comma to the start position of the node
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
}
else {
return deleteNode(importClause.name);
}
}
case SyntaxKind.NamespaceImport:
const namespaceImport = <NamespaceImport>token.parent;
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
return deleteRange({ pos: startPosition, end: namespaceImport.end });
}
return deleteRange(namespaceImport);
}
default:
return deleteDefault();
}
}
else {
return undefined;
// token.parent is a variableDeclaration
function deleteVariableDeclaration(varDecl: ts.VariableDeclaration): CodeAction[] | undefined {
switch (varDecl.parent.parent.kind) {
case SyntaxKind.ForStatement:
const forStatement = <ForStatement>varDecl.parent.parent;
const forInitializer = <VariableDeclarationList>forStatement.initializer;
if (forInitializer.declarations.length === 1) {
return deleteNode(forInitializer);
}
else {
return deleteNodeInList(varDecl);
}
case SyntaxKind.ForOfStatement:
const forOfStatement = <ForOfStatement>varDecl.parent.parent;
Debug.assert(forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList);
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
case SyntaxKind.ForInStatement:
// There is no valid fix in the case of:
// for .. in
return undefined;
default:
const variableStatement = <VariableStatement>varDecl.parent.parent;
if (variableStatement.declarationList.declarations.length === 1) {
return deleteNode(variableStatement);
}
else {
return deleteNodeInList(varDecl);
}
}
}
function deleteNode(n: Node) {

View File

@ -362,7 +362,7 @@ namespace ts.formatting {
sourceFile: SourceFileLike): TextChange[] {
// formatting context is used by rules provider
const formattingContext = new FormattingContext(sourceFile, requestKind);
const formattingContext = new FormattingContext(sourceFile, requestKind, options);
let previousRangeHasError: boolean;
let previousRange: TextRangeWithKind;
let previousParent: Node;

View File

@ -15,7 +15,7 @@ namespace ts.formatting {
private contextNodeBlockIsOnOneLine: boolean;
private nextNodeBlockIsOnOneLine: boolean;
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind) {
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind, public options: ts.FormatCodeSettings) {
}
public updateContext(currentRange: TextRangeWithKind, currentTokenParent: Node, nextRange: TextRangeWithKind, nextTokenParent: Node, commonParent: Node) {

View File

@ -12,12 +12,11 @@ namespace ts.formatting {
static Any: RuleOperationContext = new RuleOperationContext();
public IsAny(): boolean {
return this === RuleOperationContext.Any;
}
public InContext(context: FormattingContext): boolean {
public InContext(context: FormattingContext): boolean {
if (this.IsAny()) {
return true;
}

View File

@ -84,6 +84,7 @@ namespace ts.formatting {
public NoSpaceBeforeComma: Rule;
public SpaceAfterCertainKeywords: Rule;
public NoSpaceAfterNewKeywordOnConstructorSignature: Rule;
public SpaceAfterLetConstInVariableDeclaration: Rule;
public NoSpaceBeforeOpenParenInFuncCall: Rule;
public SpaceAfterFunctionInFuncDecl: Rule;
@ -147,6 +148,9 @@ namespace ts.formatting {
// These rules are higher in priority than user-configurable rules.
public HighPriorityCommonRules: Rule[];
// These rules are applied after high priority rules.
public UserConfigurableRules: Rule[];
// These rules are lower in priority than user-configurable rules.
public LowPriorityCommonRules: Rule[];
@ -279,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));
// 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));
// Place a space before open brace in a function declaration
@ -295,10 +301,10 @@ namespace ts.formatting {
this.SpaceBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
// Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}.
this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsBraceWrappedContext), RuleAction.Space));
this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsBraceWrappedContext), RuleAction.Space));
this.NoSpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsBraceWrappedContext), RuleAction.Space));
this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsBraceWrappedContext), RuleAction.Space));
this.NoSpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBetweenEmptyBraceBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsObjectContext), RuleAction.Delete));
// Insert new line after { and before } in multi-line contexts.
@ -331,11 +337,12 @@ namespace ts.formatting {
this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceAfterNewKeywordOnConstructorSignature = new Rule(RuleDescriptor.create1(SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsConstructorSignatureContext), RuleAction.Delete));
this.SpaceAfterLetConstInVariableDeclaration = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsStartOfVariableDeclarationList), RuleAction.Space));
this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionCallOrNewContext, Rules.IsPreviousTokenNotComma), RuleAction.Delete));
this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
this.SpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Delete));
this.SpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeFunctionParenthesis"), Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Delete));
this.SpaceAfterVoidOperator = new Rule(RuleDescriptor.create3(SyntaxKind.VoidKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsVoidOpContext), RuleAction.Space));
this.NoSpaceBetweenReturnAndSemicolon = new Rule(RuleDescriptor.create1(SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
@ -357,9 +364,8 @@ namespace ts.formatting {
// TypeScript-specific higher priority rules
// Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses
this.SpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterConstructor"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterConstructor"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Use of module as a function call. e.g.: import m2 = module("m2");
this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
@ -416,6 +422,72 @@ namespace ts.formatting {
// No space before non-null assertion operator
this.NoSpaceBeforeNonNullAssertionOperator = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.ExclamationToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonNullAssertionContext), RuleAction.Delete));
///
/// Rules controlled by user options
///
// Insert space after comma delimiter
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterCommaDelimiter"), Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete));
// Insert space before and after binary operators
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.SpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.NoSpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
this.NoSpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
// Insert space after keywords in control flow statements
this.SpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), Rules.IsControlDeclContext), RuleAction.Space));
this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), Rules.IsControlDeclContext), RuleAction.Delete));
// Open Brace braces after function
// TypeScript: Function can have return types, which can be made of tons of different token kinds
this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForFunctions"), Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after TypeScript module/class/interface
this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForFunctions"), Rules.IsTypeScriptDeclWithBlockContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after control block
this.NewLineBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), Rules.IsControlDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Insert space after semicolon in for statement
this.SpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterSemicolonInForStatements"), Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Space));
this.NoSpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty parenthesis
this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty brackets
this.SpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing template string braces
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
// No space after { and before } in JSX expression
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
// Insert space after function keyword for anonymous functions
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), Rules.IsFunctionDeclContext), RuleAction.Delete));
// No space after type assertion
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
this.SpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterTypeAssertion"), Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Space));
// These rules are higher in priority than user-configurable rules.
this.HighPriorityCommonRules = [
this.IgnoreBeforeComment, this.IgnoreAfterLineComment,
@ -462,7 +534,27 @@ namespace ts.formatting {
this.SpaceBeforeAt,
this.NoSpaceAfterAt,
this.SpaceAfterDecorator,
this.NoSpaceBeforeNonNullAssertionOperator
this.NoSpaceBeforeNonNullAssertionOperator,
this.NoSpaceAfterNewKeywordOnConstructorSignature
];
// These rules are applied after high priority rules.
this.UserConfigurableRules = [
this.SpaceAfterConstructor, this.NoSpaceAfterConstructor,
this.SpaceAfterComma, this.NoSpaceAfterComma,
this.SpaceAfterAnonymousFunctionKeyword, this.NoSpaceAfterAnonymousFunctionKeyword,
this.SpaceAfterKeywordInControl, this.NoSpaceAfterKeywordInControl,
this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen,
this.SpaceAfterOpenBracket, this.SpaceBeforeCloseBracket, this.NoSpaceBetweenBrackets, this.NoSpaceAfterOpenBracket, this.NoSpaceBeforeCloseBracket,
this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NoSpaceBetweenEmptyBraceBrackets, this.NoSpaceAfterOpenBrace, this.NoSpaceBeforeCloseBrace,
this.SpaceAfterTemplateHeadAndMiddle, this.SpaceBeforeTemplateMiddleAndTail, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail,
this.SpaceAfterOpenBraceInJsxExpression, this.SpaceBeforeCloseBraceInJsxExpression, this.NoSpaceAfterOpenBraceInJsxExpression, this.NoSpaceBeforeCloseBraceInJsxExpression,
this.SpaceAfterSemicolonInFor, this.NoSpaceAfterSemicolonInFor,
this.SpaceBeforeBinaryOperator, this.SpaceAfterBinaryOperator, this.NoSpaceBeforeBinaryOperator, this.NoSpaceAfterBinaryOperator,
this.SpaceBeforeOpenParenInFuncDecl, this.NoSpaceBeforeOpenParenInFuncDecl,
this.NewLineBeforeOpenBraceInControl,
this.NewLineBeforeOpenBraceInFunction, this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock,
this.SpaceAfterTypeAssertion, this.NoSpaceAfterTypeAssertion
];
// These rules are lower in priority than user-configurable rules.
@ -475,79 +567,28 @@ namespace ts.formatting {
this.SpaceAfterSemicolon,
this.SpaceBetweenStatements, this.SpaceAfterTryFinally
];
///
/// Rules controlled by user options
///
// Insert space after comma delimiter
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete));
// Insert space before and after binary operators
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.SpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.NoSpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
this.NoSpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
// Insert space after keywords in control flow statements
this.SpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Space));
this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Delete));
// Open Brace braces after function
// TypeScript: Function can have return types, which can be made of tons of different token kinds
this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after TypeScript module/class/interface
this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after control block
this.NewLineBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Insert space after semicolon in for statement
this.SpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Space));
this.NoSpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty parenthesis
this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty brackets
this.SpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing template string braces
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
// No space after { and before } in JSX expression
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
// Insert space after function keyword for anonymous functions
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Delete));
// No space after type assertion
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
this.SpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Space));
}
///
/// Contexts
///
static IsOptionEnabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => context.options && context.options.hasOwnProperty(optionName) && !!context.options[optionName];
}
static IsOptionDisabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => context.options && context.options.hasOwnProperty(optionName) && !context.options[optionName];
}
static IsOptionDisabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => !context.options || !context.options.hasOwnProperty(optionName) || !context.options[optionName];
}
static IsOptionEnabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => !context.options || !context.options.hasOwnProperty(optionName) || !!context.options[optionName];
}
static IsForContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.ForStatement;
}
@ -845,6 +886,10 @@ namespace ts.formatting {
return context.contextNode.kind === SyntaxKind.TypeLiteral; // && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
}
static IsConstructorSignatureContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.ConstructSignature;
}
static IsTypeArgumentOrParameterOrAssertion(token: TextRangeWithKind, parent: Node): boolean {
if (token.kind !== SyntaxKind.LessThanToken && token.kind !== SyntaxKind.GreaterThanToken) {
return false;

View File

@ -41,8 +41,7 @@ namespace ts.formatting {
}
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
const specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any &&
rule.Descriptor.RightTokenRange !== Shared.TokenRange.Any;
const specificRule = rule.Descriptor.LeftTokenRange.isSpecific() && rule.Descriptor.RightTokenRange.isSpecific();
rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => {
rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => {

View File

@ -5,11 +5,12 @@ namespace ts.formatting {
export class RulesProvider {
private globalRules: Rules;
private options: ts.FormatCodeSettings;
private activeRules: Rule[];
private rulesMap: RulesMap;
constructor() {
this.globalRules = new Rules();
const activeRules = this.globalRules.HighPriorityCommonRules.slice(0).concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
this.rulesMap = RulesMap.create(activeRules);
}
public getRuleName(rule: Rule): string {
@ -30,141 +31,8 @@ namespace ts.formatting {
public ensureUpToDate(options: ts.FormatCodeSettings) {
if (!this.options || !ts.compareDataObjects(this.options, options)) {
const activeRules = this.createActiveRules(options);
const rulesMap = RulesMap.create(activeRules);
this.activeRules = activeRules;
this.rulesMap = rulesMap;
this.options = ts.clone(options);
}
}
private createActiveRules(options: ts.FormatCodeSettings): Rule[] {
let rules = this.globalRules.HighPriorityCommonRules.slice(0);
if (options.insertSpaceAfterConstructor) {
rules.push(this.globalRules.SpaceAfterConstructor);
}
else {
rules.push(this.globalRules.NoSpaceAfterConstructor);
}
if (options.insertSpaceAfterCommaDelimiter) {
rules.push(this.globalRules.SpaceAfterComma);
}
else {
rules.push(this.globalRules.NoSpaceAfterComma);
}
if (options.insertSpaceAfterFunctionKeywordForAnonymousFunctions) {
rules.push(this.globalRules.SpaceAfterAnonymousFunctionKeyword);
}
else {
rules.push(this.globalRules.NoSpaceAfterAnonymousFunctionKeyword);
}
if (options.insertSpaceAfterKeywordsInControlFlowStatements) {
rules.push(this.globalRules.SpaceAfterKeywordInControl);
}
else {
rules.push(this.globalRules.NoSpaceAfterKeywordInControl);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) {
rules.push(this.globalRules.SpaceAfterOpenParen);
rules.push(this.globalRules.SpaceBeforeCloseParen);
rules.push(this.globalRules.NoSpaceBetweenParens);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenParen);
rules.push(this.globalRules.NoSpaceBeforeCloseParen);
rules.push(this.globalRules.NoSpaceBetweenParens);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) {
rules.push(this.globalRules.SpaceAfterOpenBracket);
rules.push(this.globalRules.SpaceBeforeCloseBracket);
rules.push(this.globalRules.NoSpaceBetweenBrackets);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenBracket);
rules.push(this.globalRules.NoSpaceBeforeCloseBracket);
rules.push(this.globalRules.NoSpaceBetweenBrackets);
}
// The default value of InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces is true
// so if the option is undefined, we should treat it as true as well
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces !== false) {
rules.push(this.globalRules.SpaceAfterOpenBrace);
rules.push(this.globalRules.SpaceBeforeCloseBrace);
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenBrace);
rules.push(this.globalRules.NoSpaceBeforeCloseBrace);
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle);
rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail);
}
else {
rules.push(this.globalRules.NoSpaceAfterTemplateHeadAndMiddle);
rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces) {
rules.push(this.globalRules.SpaceAfterOpenBraceInJsxExpression);
rules.push(this.globalRules.SpaceBeforeCloseBraceInJsxExpression);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenBraceInJsxExpression);
rules.push(this.globalRules.NoSpaceBeforeCloseBraceInJsxExpression);
}
if (options.insertSpaceAfterSemicolonInForStatements) {
rules.push(this.globalRules.SpaceAfterSemicolonInFor);
}
else {
rules.push(this.globalRules.NoSpaceAfterSemicolonInFor);
}
if (options.insertSpaceBeforeAndAfterBinaryOperators) {
rules.push(this.globalRules.SpaceBeforeBinaryOperator);
rules.push(this.globalRules.SpaceAfterBinaryOperator);
}
else {
rules.push(this.globalRules.NoSpaceBeforeBinaryOperator);
rules.push(this.globalRules.NoSpaceAfterBinaryOperator);
}
if (options.insertSpaceBeforeFunctionParenthesis) {
rules.push(this.globalRules.SpaceBeforeOpenParenInFuncDecl);
}
else {
rules.push(this.globalRules.NoSpaceBeforeOpenParenInFuncDecl);
}
if (options.placeOpenBraceOnNewLineForControlBlocks) {
rules.push(this.globalRules.NewLineBeforeOpenBraceInControl);
}
if (options.placeOpenBraceOnNewLineForFunctions) {
rules.push(this.globalRules.NewLineBeforeOpenBraceInFunction);
rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock);
}
if (options.insertSpaceAfterTypeAssertion) {
rules.push(this.globalRules.SpaceAfterTypeAssertion);
}
else {
rules.push(this.globalRules.NoSpaceAfterTypeAssertion);
}
rules = rules.concat(this.globalRules.LowPriorityCommonRules);
return rules;
}
}
}

View File

@ -6,6 +6,7 @@ namespace ts.formatting {
export interface ITokenAccess {
GetTokens(): SyntaxKind[];
Contains(token: SyntaxKind): boolean;
isSpecific(): boolean;
}
export class TokenRangeAccess implements ITokenAccess {
@ -27,6 +28,8 @@ namespace ts.formatting {
public Contains(token: SyntaxKind): boolean {
return this.tokens.indexOf(token) >= 0;
}
public isSpecific() { return true; }
}
export class TokenValuesAccess implements ITokenAccess {
@ -43,6 +46,8 @@ namespace ts.formatting {
public Contains(token: SyntaxKind): boolean {
return this.tokens.indexOf(token) >= 0;
}
public isSpecific() { return true; }
}
export class TokenSingleValueAccess implements ITokenAccess {
@ -56,15 +61,18 @@ namespace ts.formatting {
public Contains(tokenValue: SyntaxKind): boolean {
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 {
public GetTokens(): SyntaxKind[] {
const result: SyntaxKind[] = [];
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
result.push(token);
}
return result;
return allTokens;
}
public Contains(): boolean {
@ -74,6 +82,22 @@ namespace ts.formatting {
public toString(): string {
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 {
@ -92,8 +116,8 @@ namespace ts.formatting {
return new TokenRange(new TokenRangeAccess(f, to, except));
}
static AllTokens(): TokenRange {
return new TokenRange(new TokenAllAccess());
static AnyExcept(token: SyntaxKind): TokenRange {
return new TokenRange(new TokenAllExceptAccess(token));
}
public GetTokens(): SyntaxKind[] {
@ -108,8 +132,12 @@ namespace ts.formatting {
return this.tokenAccess.toString();
}
static Any: TokenRange = TokenRange.AllTokens();
static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia]));
public isSpecific() {
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 BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);

View File

@ -212,6 +212,10 @@ namespace ts.FindAllReferences {
return;
}
if (!decl.importClause) {
return;
}
const { importClause } = decl;
const { namedBindings } = importClause;

View File

@ -31,6 +31,9 @@ namespace ts {
/** The version of the language service API */
export const servicesVersion = "0.5";
/* @internal */
let ruleProvider: formatting.RulesProvider;
function createNode<TKind extends SyntaxKind>(kind: TKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject<TKind> | IdentifierObject {
const node = kind >= SyntaxKind.FirstNode ? new NodeObject(kind, pos, end) :
kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) :
@ -1041,10 +1044,9 @@ namespace ts {
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
let ruleProvider: formatting.RulesProvider;
ruleProvider = ruleProvider || new formatting.RulesProvider();
let program: Program;
let lastProjectVersion: string;
let lastTypesRootVersion = 0;
const useCaseSensitivefileNames = host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames();
@ -1073,11 +1075,6 @@ namespace ts {
}
function getRuleProvider(options: FormatCodeSettings) {
// Ensure rules are initialized and up to date wrt to formatting options
if (!ruleProvider) {
ruleProvider = new formatting.RulesProvider();
}
ruleProvider.ensureUpToDate(options);
return ruleProvider;
}

View File

@ -5,6 +5,7 @@ namespace ts {
reportDiagnostics?: boolean;
moduleName?: string;
renamedDependencies?: MapLike<string>;
transformers?: CustomTransformers;
}
export interface TranspileOutput {
@ -103,7 +104,7 @@ namespace ts {
addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
}
// Emit
program.emit();
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers);
Debug.assert(outputText !== undefined, "Output generation failed");

View File

@ -1036,6 +1036,10 @@ namespace ts {
}
export function compareDataObjects(dst: any, src: any): boolean {
if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) {
return false;
}
for (const e in dst) {
if (typeof dst[e] === "object") {
if (!compareDataObjects(dst[e], src[e])) {

View File

@ -59,7 +59,7 @@ var X;
(function (X) {
var Y;
(function (Y) {
var Point;
let Point;
(function (Point) {
Point.Origin = new Point(0, 0);
})(Point = Y.Point || (Y.Point = {}));

View 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 }) }));

View File

@ -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.

View File

@ -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.

View 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 }) }));

View File

@ -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.

View File

@ -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.

View 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 }) }));

View File

@ -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.

View File

@ -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.

View 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 }) }));

View File

@ -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.

View File

@ -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.

View File

@ -16,7 +16,7 @@ var A;
if (true) {
B.
;
var B;
var B = void 0;
(function (B) {
function baz() { }
B.baz = baz;

View File

@ -62,13 +62,13 @@ var x = 0 /* a */;
var y = 0 /* x */;
export var m1;
(function (m1) {
var e3;
let e3;
(function (e3) {
e3[e3["a"] = 0] = "a";
e3[e3["b"] = 1] = "b";
e3[e3["c"] = 2] = "c";
})(e3 = m1.e3 || (m1.e3 = {}));
var e4;
let e4;
(function (e4) {
e4[e4["x"] = 0] = "x";
e4[e4["y"] = 1] = "y";
@ -81,13 +81,13 @@ export var m1;
})(m1 || (m1 = {}));
var m2;
(function (m2) {
var e5;
let e5;
(function (e5) {
e5[e5["a"] = 0] = "a";
e5[e5["b"] = 1] = "b";
e5[e5["c"] = 2] = "c";
})(e5 = m2.e5 || (m2.e5 = {}));
var e6;
let e6;
(function (e6) {
e6[e6["x"] = 0] = "x";
e6[e6["y"] = 1] = "y";

View File

@ -62,13 +62,13 @@ var x = e1.a;
var y = e2.x;
export var m1;
(function (m1) {
var e3;
let e3;
(function (e3) {
e3[e3["a"] = 0] = "a";
e3[e3["b"] = 1] = "b";
e3[e3["c"] = 2] = "c";
})(e3 = m1.e3 || (m1.e3 = {}));
var e4;
let e4;
(function (e4) {
e4[e4["x"] = 0] = "x";
e4[e4["y"] = 1] = "y";
@ -81,13 +81,13 @@ export var m1;
})(m1 || (m1 = {}));
var m2;
(function (m2) {
var e5;
let e5;
(function (e5) {
e5[e5["a"] = 0] = "a";
e5[e5["b"] = 1] = "b";
e5[e5["c"] = 2] = "c";
})(e5 = m2.e5 || (m2.e5 = {}));
var e6;
let e6;
(function (e6) {
e6[e6["x"] = 0] = "x";
e6[e6["y"] = 1] = "y";

View File

@ -41,7 +41,7 @@ export var M;
}
M.M_C = M_C;
// instantiated module
var M_M;
let M_M;
(function (M_M) {
var x;
})(M_M = M.M_M || (M.M_M = {}));
@ -49,7 +49,7 @@ export var M;
function M_F() { }
M.M_F = M_F;
// enum
var M_E;
let M_E;
(function (M_E) {
})(M_E = M.M_E || (M.M_E = {}));
// alias

View File

@ -43,7 +43,7 @@ export var M;
}
M.M_C = M_C;
// instantiated module
var M_M;
let M_M;
(function (M_M) {
var x;
})(M_M = M.M_M || (M.M_M = {}));
@ -51,7 +51,7 @@ export var M;
function M_F() { }
M.M_F = M_F;
// enum
var M_E;
let M_E;
(function (M_E) {
})(M_E = M.M_E || (M.M_E = {}));
// alias

View File

@ -29,12 +29,12 @@ export var m1;
(function (m1) {
m1.a = 10;
var b = 10;
var innerExportedModule;
let innerExportedModule;
(function (innerExportedModule) {
innerExportedModule.k = 10;
var l = 10;
})(innerExportedModule = m1.innerExportedModule || (m1.innerExportedModule = {}));
var innerNonExportedModule;
let innerNonExportedModule;
(function (innerNonExportedModule) {
innerNonExportedModule.x = 10;
var y = 10;
@ -44,12 +44,12 @@ var m2;
(function (m2) {
m2.a = 10;
var b = 10;
var innerExportedModule;
let innerExportedModule;
(function (innerExportedModule) {
innerExportedModule.k = 10;
var l = 10;
})(innerExportedModule = m2.innerExportedModule || (m2.innerExportedModule = {}));
var innerNonExportedModule;
let innerNonExportedModule;
(function (innerNonExportedModule) {
innerNonExportedModule.x = 10;
var y = 10;

View File

@ -188,7 +188,7 @@ function f2() {
}
function f3(b) {
if (true) {
var E;
var E = void 0;
(function (E) {
E[E["A"] = 0] = "A";
E[E["B"] = 1] = "B";

View File

@ -4,4 +4,5 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(1,9): error TS7017: E
==== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts (1 errors) ====
var x = {}["hello"];
~~~~~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
var y: string = { '': 'foo' }[''];

View File

@ -1,5 +1,7 @@
//// [noImplicitAnyStringIndexerOnObject.ts]
var x = {}["hello"];
var x = {}["hello"];
var y: string = { '': 'foo' }[''];
//// [noImplicitAnyStringIndexerOnObject.js]
var x = {}["hello"];
var y = { '': 'foo' }[''];

View File

@ -0,0 +1 @@
var newName = void 0 /*undefined*/;

View File

@ -0,0 +1,19 @@
//// [file.tsx]
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P = Prop> extends React.Component<P, {}> {
internalProp: P;
}
let x = <MyComp a={10} b="hi" />
//// [file.jsx]
"use strict";
exports.__esModule = true;
var React = require("react");
var x = <MyComp a={10} b="hi"/>;

View File

@ -0,0 +1,34 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react');
>React : Symbol(React, Decl(file.tsx, 0, 0))
interface Prop {
>Prop : Symbol(Prop, Decl(file.tsx, 0, 32))
a: number,
>a : Symbol(Prop.a, Decl(file.tsx, 2, 16))
b: string
>b : Symbol(Prop.b, Decl(file.tsx, 3, 14))
}
declare class MyComp<P = Prop> extends React.Component<P, {}> {
>MyComp : Symbol(MyComp, Decl(file.tsx, 5, 1))
>P : Symbol(P, Decl(file.tsx, 7, 21))
>Prop : Symbol(Prop, Decl(file.tsx, 0, 32))
>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
>React : Symbol(React, Decl(file.tsx, 0, 0))
>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
>P : Symbol(P, Decl(file.tsx, 7, 21))
internalProp: P;
>internalProp : Symbol(MyComp.internalProp, Decl(file.tsx, 7, 63))
>P : Symbol(P, Decl(file.tsx, 7, 21))
}
let x = <MyComp a={10} b="hi" />
>x : Symbol(x, Decl(file.tsx, 11, 3))
>MyComp : Symbol(MyComp, Decl(file.tsx, 5, 1))
>a : Symbol(a, Decl(file.tsx, 11, 15))
>b : Symbol(b, Decl(file.tsx, 11, 22))

View File

@ -0,0 +1,36 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react');
>React : typeof React
interface Prop {
>Prop : Prop
a: number,
>a : number
b: string
>b : string
}
declare class MyComp<P = Prop> extends React.Component<P, {}> {
>MyComp : MyComp<P>
>P : P
>Prop : Prop
>React.Component : React.Component<P, {}>
>React : typeof React
>Component : typeof React.Component
>P : P
internalProp: P;
>internalProp : P
>P : P
}
let x = <MyComp a={10} b="hi" />
>x : JSX.Element
><MyComp a={10} b="hi" /> : JSX.Element
>MyComp : typeof MyComp
>a : number
>10 : 10
>b : string

View File

@ -0,0 +1,33 @@
tests/cases/conformance/jsx/file.tsx(13,9): error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<Prop>> & Prop & { children?: ReactNode; }'.
Type '{}' is not assignable to type 'Prop'.
Property 'a' is missing in type '{}'.
tests/cases/conformance/jsx/file.tsx(14,18): error TS2322: Type '{ a: "hi"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<Prop>> & Prop & { children?: ReactNode; }'.
Type '{ a: "hi"; }' is not assignable to type 'Prop'.
Types of property 'a' are incompatible.
Type '"hi"' is not assignable to type 'number'.
==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P = Prop> extends React.Component<P, {}> {
internalProp: P;
}
// Error
let x = <MyComp />
~~~~~~~~~~
!!! error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<Prop>> & Prop & { children?: ReactNode; }'.
!!! error TS2322: Type '{}' is not assignable to type 'Prop'.
!!! error TS2322: Property 'a' is missing in type '{}'.
let x1 = <MyComp a="hi"/>
~~~~~~
!!! error TS2322: Type '{ a: "hi"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<Prop>> & Prop & { children?: ReactNode; }'.
!!! error TS2322: Type '{ a: "hi"; }' is not assignable to type 'Prop'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type '"hi"' is not assignable to type 'number'.

View File

@ -0,0 +1,23 @@
//// [file.tsx]
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P = Prop> extends React.Component<P, {}> {
internalProp: P;
}
// Error
let x = <MyComp />
let x1 = <MyComp a="hi"/>
//// [file.jsx]
"use strict";
exports.__esModule = true;
var React = require("react");
// Error
var x = <MyComp />;
var x1 = <MyComp a="hi"/>;

View File

@ -0,0 +1,30 @@
tests/cases/conformance/jsx/file.tsx(16,17): error TS2322: Type '{ a: 10; b: "hi"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.
Property 'a' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.
tests/cases/conformance/jsx/file.tsx(17,18): error TS2322: Type '{ a: "hi"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.
Property 'a' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.
==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P extends Prop> extends React.Component<P, {}> {
internalProp: P;
}
// OK: we fille in missing type argument with empty object
let x1 = <MyComp />
// Error
let x = <MyComp a={10} b="hi" />
~~~~~~~~~~~~~
!!! error TS2322: Type '{ a: 10; b: "hi"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.
!!! error TS2322: Property 'a' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.
let x2 = <MyComp a="hi"/>
~~~~~~
!!! error TS2322: Type '{ a: "hi"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.
!!! error TS2322: Property 'a' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComp<{}>> & { children?: ReactNode; }'.

View File

@ -0,0 +1,28 @@
//// [file.tsx]
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P extends Prop> extends React.Component<P, {}> {
internalProp: P;
}
// OK: we fille in missing type argument with empty object
let x1 = <MyComp />
// Error
let x = <MyComp a={10} b="hi" />
let x2 = <MyComp a="hi"/>
//// [file.jsx]
"use strict";
exports.__esModule = true;
var React = require("react");
// OK: we fille in missing type argument with empty object
var x1 = <MyComp />;
// Error
var x = <MyComp a={10} b="hi"/>;
var x2 = <MyComp a="hi"/>;

View File

@ -0,0 +1,26 @@
//// [file.tsx]
import React = require('react')
interface MyComponentProp {
values: string;
}
function MyComponent<T = MyComponentProp>(attr: T) {
return <div>attr.values</div>
}
// OK
let i = <MyComponent values />; // We infer type arguments here
let i1 = <MyComponent values="Hello"/>;
//// [file.jsx]
define(["require", "exports", "react"], function (require, exports, React) {
"use strict";
exports.__esModule = true;
function MyComponent(attr) {
return <div>attr.values</div>;
}
// OK
var i = <MyComponent values/>; // We infer type arguments here
var i1 = <MyComponent values="Hello"/>;
});

View File

@ -0,0 +1,34 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react')
>React : Symbol(React, Decl(file.tsx, 0, 0))
interface MyComponentProp {
>MyComponentProp : Symbol(MyComponentProp, Decl(file.tsx, 0, 31))
values: string;
>values : Symbol(MyComponentProp.values, Decl(file.tsx, 2, 27))
}
function MyComponent<T = MyComponentProp>(attr: T) {
>MyComponent : Symbol(MyComponent, Decl(file.tsx, 4, 1))
>T : Symbol(T, Decl(file.tsx, 6, 21))
>MyComponentProp : Symbol(MyComponentProp, Decl(file.tsx, 0, 31))
>attr : Symbol(attr, Decl(file.tsx, 6, 42))
>T : Symbol(T, Decl(file.tsx, 6, 21))
return <div>attr.values</div>
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2399, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2399, 45))
}
// OK
let i = <MyComponent values />; // We infer type arguments here
>i : Symbol(i, Decl(file.tsx, 11, 3))
>MyComponent : Symbol(MyComponent, Decl(file.tsx, 4, 1))
>values : Symbol(values, Decl(file.tsx, 11, 20))
let i1 = <MyComponent values="Hello"/>;
>i1 : Symbol(i1, Decl(file.tsx, 12, 3))
>MyComponent : Symbol(MyComponent, Decl(file.tsx, 4, 1))
>values : Symbol(values, Decl(file.tsx, 12, 21))

View File

@ -0,0 +1,37 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react')
>React : typeof React
interface MyComponentProp {
>MyComponentProp : MyComponentProp
values: string;
>values : string
}
function MyComponent<T = MyComponentProp>(attr: T) {
>MyComponent : <T = MyComponentProp>(attr: T) => JSX.Element
>T : T
>MyComponentProp : MyComponentProp
>attr : T
>T : T
return <div>attr.values</div>
><div>attr.values</div> : JSX.Element
>div : any
>div : any
}
// OK
let i = <MyComponent values />; // We infer type arguments here
>i : JSX.Element
><MyComponent values /> : JSX.Element
>MyComponent : <T = MyComponentProp>(attr: T) => JSX.Element
>values : true
let i1 = <MyComponent values="Hello"/>;
>i1 : JSX.Element
><MyComponent values="Hello"/> : JSX.Element
>MyComponent : <T = MyComponentProp>(attr: T) => JSX.Element
>values : string

View File

@ -0,0 +1,25 @@
tests/cases/conformance/jsx/file.tsx(13,24): error TS2322: Type '{ values: 5; }' is not assignable to type 'IntrinsicAttributes & MyComponentProp'.
Type '{ values: 5; }' is not assignable to type 'MyComponentProp'.
Types of property 'values' are incompatible.
Type '5' is not assignable to type 'string'.
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
import React = require('react')
interface MyComponentProp {
values: string;
}
function MyComponent1<T extends MyComponentProp>(attr: T) {
return <div>attr.values</div>
}
// Error
let i1 = <MyComponent1 values={5}/>;
~~~~~~~~~~
!!! error TS2322: Type '{ values: 5; }' is not assignable to type 'IntrinsicAttributes & MyComponentProp'.
!!! error TS2322: Type '{ values: 5; }' is not assignable to type 'MyComponentProp'.
!!! error TS2322: Types of property 'values' are incompatible.
!!! error TS2322: Type '5' is not assignable to type 'string'.

View File

@ -0,0 +1,25 @@
//// [file.tsx]
import React = require('react')
interface MyComponentProp {
values: string;
}
function MyComponent1<T extends MyComponentProp>(attr: T) {
return <div>attr.values</div>
}
// Error
let i1 = <MyComponent1 values={5}/>;
//// [file.jsx]
define(["require", "exports", "react"], function (require, exports, React) {
"use strict";
exports.__esModule = true;
function MyComponent1(attr) {
return <div>attr.values</div>;
}
// Error
var i1 = <MyComponent1 values={5}/>;
});

View File

@ -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(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,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,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')
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 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.
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.
}

View File

@ -1,3 +1,4 @@
// @noimplicitany: true
var x = {}["hello"];
var x = {}["hello"];
var y: string = { '': 'foo' }[''];

View File

@ -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>;

View File

@ -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>;

View File

@ -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>;

View File

@ -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 })} />;

View File

@ -0,0 +1,17 @@
// @filename: file.tsx
// @jsx: preserve
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P = Prop> extends React.Component<P, {}> {
internalProp: P;
}
let x = <MyComp a={10} b="hi" />

View File

@ -0,0 +1,19 @@
// @filename: file.tsx
// @jsx: preserve
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P = Prop> extends React.Component<P, {}> {
internalProp: P;
}
// Error
let x = <MyComp />
let x1 = <MyComp a="hi"/>

View File

@ -0,0 +1,22 @@
// @filename: file.tsx
// @jsx: preserve
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
import React = require('react');
interface Prop {
a: number,
b: string
}
declare class MyComp<P extends Prop> extends React.Component<P, {}> {
internalProp: P;
}
// OK: we fille in missing type argument with empty object
let x1 = <MyComp />
// Error
let x = <MyComp a={10} b="hi" />
let x2 = <MyComp a="hi"/>

View File

@ -0,0 +1,19 @@
// @filename: file.tsx
// @jsx: preserve
// @module: amd
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
import React = require('react')
interface MyComponentProp {
values: string;
}
function MyComponent<T = MyComponentProp>(attr: T) {
return <div>attr.values</div>
}
// OK
let i = <MyComponent values />; // We infer type arguments here
let i1 = <MyComponent values="Hello"/>;

View File

@ -0,0 +1,19 @@
// @filename: file.tsx
// @jsx: preserve
// @module: amd
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
import React = require('react')
interface MyComponentProp {
values: string;
}
function MyComponent1<T extends MyComponentProp>(attr: T) {
return <div>attr.values</div>
}
// Error
let i1 = <MyComponent1 values={5}/>;

View File

@ -0,0 +1,11 @@
/// <reference path="fourslash.ts" />
// https://github.com/Microsoft/TypeScript/issues/15452
// @Filename: /a.ts
////export const [|{| "isWriteAccess": true, "isDefinition": true |}x|] = 0;
// @Filename: /b.ts
////import "./a";
verify.singleReferenceGroup("const x: 0");

View File

@ -0,0 +1,9 @@
/// <reference path="fourslash.ts"/>
////class C {
//// /*method*/async [0]() { }
////}
format.document();
goTo.marker("method");
verify.currentLineContentIs(" async [0]() { }");

View File

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
/////*1*/interface Gourai { new () {} }
/////*2*/type Stylet = { new () {} }
format.document();
goTo.marker("1");
verify.currentLineContentIs("interface Gourai { new() {} }");
goTo.marker("2");
verify.currentLineContentIs("type Stylet = { new() {} }");

View File

@ -2359,9 +2359,9 @@ declare namespace JSX {
interface ElementClass extends React.Component<any, any> {
render(): JSX.Element | null;
}
interface ElementAttributesProperty { props; }
interface ElementAttributesProperty { props: any; }
interface ElementChildrenAttribute { children; }
interface ElementChildrenAttribute { children: any; }
interface IntrinsicAttributes extends React.Attributes { }